There are a lot of things you can do to create a kid-friendly browsing experience. In this first version we focused on making the browser simpler by hiding complex or kid-unfriendly features and utilizing the parental controls of the Android system: Restricted profiles.
What are restricted profiles?
Restricted Profiles have been introduced in Android 4.3. The device administrator can create these profiles and restrict access to apps and features on the device. In addition to that restrictions inside an app can be configured if supported by the app.
|Configuring which apps the restricted profile can acccess.|
A unique feature of restricted profiles is that they share the Google account of the device owner. It does not allow full-access to everything connected to the account but it allows the restricted user to watch content (e.g. movies and music) bought with that account or use paid applications. Of course only if the device owner explicitly allowed this.
Unfortunately Restricted Profiles are only supported on tablets so far. It is a pity because in the meantime Google allowed to create full-featured and guest profiles on phones too.
The following DevBytes episodes gives a good overview about the Restricted Profiles APIs:
Being in control
Our final list of restrictable features for Firefox 42 contains 10 items:
- Disable add-on installation
- Disable 'Import from Android' (Bookmark import)
- Disable developer tools
- Disable Home customization (Home panels)
- Disable Private Browsing
- Disable Location Services (Contributing to Mozilla's Location Service)
- Disable Display settings
- Disable 'Clear browsing history'
- Disable master password
- Disable Guest Browsing
Limiting access to features is a very personal decision. Not every parent wants to control every aspect of the browsing experience. That's why we decided to make these restrictions configurable by the parent. By implementing a broadcast receiver that listens to ACTION_GET_RESTRICTION_ENTRIES actions it's possible to send a list of restrictions to the system and so they will show up in the admin interface:
|Configuring restrictions of an application.|
User restrictions vs. application restrictions
There are two kinds of restrictions. User restrictions are imposed on the user by the system and application restrictions are added by an application via the broadcast mechanism mentioned above. An application can query the UserManager only for its own and global user restrictions.
Detecting restricted profiles
One of the first things you might want to do in your app is to detect if the current user is using a restricted or a normal profile. There's no API method to do that and the video linked above suggests to query the user restrictions from the UserManager and if the returned bundle is not empty then you are in a restricted profile.
This worked fine until we deployed the application to a phone running an Android M preview build. On this phone - that doesn't even support restricted profiles - the UserManager always returned a restriction. Whoops, suddenly everyone with Android M on their phones had a very limited Firefox Nightly. We then switched to iterating over the returned Bundles (for application and user restrictions) and only assuming we are in a restricted profile if at least one restriction in those bundles is enabled (getBoolean() returns true).
In general it is a better approach to never detect whether you are in a restricted profile or not but instead always check whether a specific (application) restriction is enabled or not.
A resource qualifier would be nice
Hiding features and UI in restricted profiles will add a lot of if statements to the code base. It would have been nice to have a resource qualifier for restricted profiles to load different layouts, drawables and other configurations. Besides that this would also solve the "detect restricted profile" problem quite elegant.
Strict mode: Disk read violation
At runtime we noticed that we have been triggering a lot of disk read violations. Looking at Android's source code it turns out that UserManager.getApplicationRestrictions() reads and parses an XML file on every call. Without caching anything like SharedPreferences do. We worked around that by implementing our own memory cache and refreshing the list of restrictions whenever the application is resumed. To update restrictions the user will always have to switch to the admin profile and therefore leave (and later resume) the application.
Android Marshmallow (6.0) introduced a new Easter egg system restriction: UserManager.DISALLOW_FUN - Specifies if the user is not allowed to have fun. In some cases, the device owner may wish to prevent the user from experiencing amusement or joy while using the device. The default value is false.
|Simplified browser UI with custom theme using a restricted profile (Firefox 42).|
With the current set of restrictions parents can create a kid-friendly and simplified browsing experience. Of course there are a lot of more possible features around parental controls that come to mind like block lists and restricting Web APIs (Microphone, Webcam). Some of these ideas have already been filed (Bug 1125710) and in addition to that we just started planning features for the next version (Bug 1205615). More ideas are definitely welcome!
Testing and feedback
At the time of this writing support for restricted profiles is available in Aurora (42.0) and Nightly (43.0) builds of Firefox. Restricted Profiles serve a very specific use case and therefore do not get as much usage coverage like other browser features. If you do have a tablet and are interested in restricted profiles then help us testing it! :)
Firefox for Android is open-source software and contributors are very welcome! You can find me on IRC (irc.mozilla.org) in #mobile (my nickname is "sebastian"), on Twitter and Google+. Get involved with Firefox for Android.
More resources about Project KidFox
- Mozilla Wiki: Project KinderFox
- Mozilla Wiki: KidFox proposal
- Mozilla wiki: KidFox brief
- Generic meta bug
- Bugzilla meta bug for v1
- Bugzilla meta bug for v2