For those who may have missed the announcement at Google I/O: Android M permissions handling will introduce new handling and behavior. The new Android M permissions system support allows apps to be installed without requiring user review and grant of permissions. Instead, most permissions are disabled by default when an app is installed. When the user starts the app and it requires a feature which requires a permission, the user must grant it. The user can also go back at any time and either grant or deny permissions to individual apps.
While this type of functionality has been available in other AOSP derived versions of Android, such as CyanogenMod, it has not been part of the official Android open source codebase. This is quite a departure from the original permission paradigm used in Android. I’ve talked about the original Android permissions system in my previous article on Android Permissions, in case you are unfamiliar. This auto-grant/revoke with user interaction sounds like a very simple change, but trust me when I say it is not a simple or straightforward for the framework or for app developers.
This is an important step forward for the platform security wise and for the end user. Several reasons for the changes come to mind:
- The original user experience of one time grant was better than nagging the user, but was very limited due to the all-or-nothing permission grant at install time with no ability to revoke permissions
- Android M permissions support brings it closer to parity with Android’s biggest rival: iOS
- As users become more savvy, they are demanding a selective permission control type system. They are finally catching on that malware exists and want to have more refined control over what things apps can use.
Let’s take a look a look at what this means for the platform as well as end app authors.
NOTE: This article is being written fresh off Google I/O and the Android M Preview 1 release. It is possible that the API changes and behaviors described here will change between now and the anticipated 2015Q3 Android M Final Release described in the Program Overview. Be sure to consult the latest documentation as Android M previews and the final release is rolled out.
Framework Changes
Let’s start with the framework changes involved for the new Android M permissions support. This can roughly be broken down into three major categories: new API additions, compatibility and graceful failure (which is closely related to compatibility.) There is one other aspect of the Android M permissions changes which is worth noting: the system defined permissions groups have been reduced from 16 to 8 with the intent of making them easier for end users to understand.
API Additions
The Android API support for permissions are primarily internal to the framework. Prior to Android M, permissions were only ever granted to an app at install time. When the user reviewed permissions (if required) and clicked the button to install the app, every permission declared in the app’s manifest was assigned to it. This information is tracked within framework internal bookkeeping files on the /data
partition of the device. That part is conceptually still in place, but now the internal APIs allow these permissions to be selectively enabled and disabled at runtime. In fact, the selective grant and revoke APIs have been present within the framework for a long time, they were never public or intended to be used outside of install time, though.
Apps have been able to use the ContextWrapper
(the base class of Activity
and Service
) and PackageManager
to check to see if they have been granted a permission since API level 1. The new API additions extend this by allowing an app to bring up the system UI for the user to review and selectively enable/disable permissions for the app. These two things work in concert, allowing an app developer to verify it has what it needs to function. Note: there is no API for an app to magically grant itself permissions or otherwise force the functionality it needs. The most it can do is request the permission, which can cause a dialog to be presented, giving the user the ability to enable permissions for the app.
The new API is simple enough: call Activity.requestPermissions()
with an array of permission names you would like. You’ll also need to implement Activity.onRequestPermissionsResult()
in order to be notified whether the app has been granted or denied. The great part about this is the app can quickly request a permission and the system will handle the UI interaction with the user. The bad part is that the app cannot customize the UI (e.g. present a reason why the permission is needed). Additionally, the app cannot assume the user was actually asked as they may have previously selected to not be asked again. You know what that means: more work for the app author.
Compatibility Support
The Android framework generally does a great job of ensuring compatibility for apps running on different versions of the platform. This is the entire reason for the minSdkVersion
and targetSdkVersion
: the platform uses this information to enable or disable features as well as enable compatibility fallback type features, if necessary. The Android M permissions changes are no different.
For apps which are targeted at Android M, the new permissions model is used. The apps are installed without showing permissions to the users, only permissions defined as PROTECTION_NORMAL
are granted by default and the user will need to grant permissions when the feature(s) are used. The same apps installed on a pre-M device will still use the old model of granting permissions at install time. It’s worth noting that the INTERNET
permission has been re-classified as PROTECTION_NORMAL
so all apps get it without user approval.
Where it gets interesting is with apps targeted at pre-M releases. For these apps, the behavior on pre-M devices remains unchanged. However, on Android M the app permissions are shown at install time, just like on legacy versions. Where it departs is the user is still allowed to selectively turn off permissions for these apps. This obviously will have an effect on the app. Previously, once the app was installed it had all the permissions it declared, now they can be revoked at any time by the user. If you just said, “uh oh,” then you’re in good company. The one saving grace here is the system will present the user with a dialog indicating the legacy app has not been updated for the Android M permissions model and may break. But, we all know how well users pay attention to this type of things and will no doubt result in app ratings dropping.
Graceful Failures
Android’s original security architecture handled security denials in a straightforward way: exceptions. Try to use something in which you do not have permission and you will generally receive a SecurityException
. Just about every Android developer has experienced this the first time they start using a feature of the system which steps outside of the app’s sandbox and they forgot to add the permission to the app’s manifest. The heavy handed, but effective approach made it easy for app authors to develop their app and quickly realize when they needed a permission. But, this paradigm has a fundamental flaw when moving forward to dynamic permission granting: app authors have only ever had to declare a permission in the manifest then just use the feature. Based on this architecture, authors didn’t have to program defensively. After all, there should be no reason to receive a SecurityException if you have declared your permission, right?
Unfortunately, the variety of ways permissions are used make it nearly impossible to gracefully handle all protection violation situations. What has been done is the framework will sometimes gracefully fail and return negative responses/data to requests rather than SecurityException
. This is a good thing as it will most likely not break many implementations. However, the bad aspect of this is that it is not clear from the docs which APIs behave this way vs. just throwing a SecurityException
.
Developer Impact
While the Android M permissions compatibility support in the framework goes a long way into making app author’s lives easier when dealing with the change, devs do have some things to be concerned about:
- Not all APIs will report graceful failures.
SecurityException
can still be thrown. Regardless, defensive programming for any feature outside of the app sandbox is required. - Good UX is essential when dealing with failures, otherwise users are more likely to leave a bad review or uninstall the app
- Some APIs are only available in Android M, so apps may need to have API specific code to handle showing the user security related issues
Defensive Programming
Hands down, the biggest impacts of the Android M permissions model changes is the need for defensive programming around protected calls. As I mentioned before, the original permissions model was just a “declare and use” paradigm, so there was no need to deal with SecurityException
once you had developed your app. App authors should already be checking for bad/null/invalid data returns from calls, but it’s been my experience that nobody bothers to catch and handle the SecurityException
. This has to change on Android M. But, more importantly, it needs to change even on legacy apps which are not specifically targeting M. In essence, the code is going to look something like this in various locations throughout the app:
public class MyActivity extends Activity { private static final int REQ_CAM_PERM_CODE = 10001; ... @Override protected void onCreate(Bundle savedInstance) { ... if (checkSelfPermission(Manifest.permission.CAMERA) == PackageManager.PERMISSION_DENIED) { // We do not have this essential permission, ask for it requestPermissions(new String[] {Manifest.permission.CAMERA}, REQ_CAM_PERM_CODE); } } @Override public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { switch (requestCode) { case REQ_CAM_PERM_CODE: if (grantResults[0] == PackageManager.PERMISSION_DENIED) { // Notify the user of the reduction in functionality // and possibly exit (app dependent) ... } else { // Continue startup or move to a running state ... } ... } } ... }
But, it’s really not as simple as catching the exception and moving on. App authors must now take an additional step to verify the app actually has been granted the permission before trying to use a protected API. So, the defensive programming needed is a combination of good error handling and proactive permissions checking. What the app does after that point is largely up to the app. More on that below.
Good UX
Good UX is essential apps and as the mobile market has expanded we have seen this landscape change. Your design team may really need to go to town to consider how to deal with Anroid M’s security changes. Every app’s needs and purpose are different, so I can’t give specific recommendations. However, I can boil it down to several very basic ones:
- If permissions are needed for functionality which is non-essential to the app, selectively disable that feature if the permission has not been granted.
- If permissions are needed, but not essential, only notify the user the first time the app detects the condition. Constant pop-ups are annoying and a sure fire way to upset your users and get uninstalled.
- If permissions are essential for the app to work, notify the user of the need and offer to take them to the Settings page to alter the permissions.
Wrapping Up
The Android framework and security teams have made a much needed change to Android M permissions: dynamic permission settings for apps which are controlled by the user. This puts Android on-par with the other major smartphone OS, but also gives users the ability to use apps while denying them some capabilities. But, this comes at a cost to app authors. We as developers have to gracefully deal with this paradigm shift or else our users will be quite unhappy and ultimately cause our apps to get poor rankings. I expect we’ll also see some fallout for apps which have been in the market for a long time and are not updated for Android M. In any case, I’m excited about this change and think it is going to be a great stride forward for Android and its users.
What are your thoughts and concerns? Do you see these changes as a good or a bad thing?
Very interesting round-up! One question though: Is targeting the user to the app settings page via the method mentioned here[0] the “right place” where he can change the permissions per app or is there a more specific Intent available in M? I can see that the “Permissions” list item leads to a separate screen…
[0] http://stackoverflow.com/a/4421650
Great question, Thomas! You could go this route and force the user into the Settings application to manipulate the app’s settings. You would use an
Intent
with the action set toACTION_APPLICATION_DETAIL_SETTINGS
and an extra of the package name to bring up the app specific settings page. The M preview 1 didn’t add anything specific for adjusting just the permissions of an app. This may be something which gets added later. I took a quick glance at the Settings app code in theandroid-m-preview
branch of AOSP and it doesn’t appear to create a newActivity
directly.Requesting the permission using
Activity.requestPermissions()
would be the first step – if this fails (and you cannot tell if the user was actually presented a dialog), then from a UX perspective it would be best to present them with a dialog which explains why the permission is needed and give them the option to change the settings, which fires thisIntent