Tackling The New Android App Permissions Model Android 6.0 Marshmallow brings a host of new capabilities to the table. One interesting change is the new Android app permissions model. Android 6.0 Marshmallow brings a host of new capabilities to the table, especially for developers. One interesting change is the new Android app permissions model introduced by Android Marshmallow. The old Android app permissions model Every Android application is isolated from the other applications and the system by running in a sandbox. They have limited access to data outside of it. In order to use resources and information out of its sandbox, an application needs to request the appropriate Android permissions. This is done by including one or more <uses-permission> tags in the application’s manifest file: <uses-permission android_name="android.permission.CAMERA"/> All these Android app permissions are granted during installation and the only way to revoke them is to uninstall the respective app. Why its great for developers. Their applications do not need extra logic for handling Android app permissions (outside of declaring them). As long as the app is installed, it does not have to worry about its access to a protected resource being revoked. This old model is fairly straightforward, but it does have some issues. Limitations of the permissions model. First users have no control over app permissions. They either install an app and grant it all of the required Android permissions or just skip the app entirely. Another pressing issue is the lack of clarity. Why does a flashlight app need access to the user’s contacts or the internet? The old model does not provide effective means to inform the users why an app exactly needs a specific permission. Users can only review the required Android permissions and the general purpose. The third big problem with the old model is: It can be abused by apps, because users often do not bother to review the permissions. This leads to the emergence of flashlight apps that have access to location, wifi, contacts and other user data — posing a serious security concern. Normal and dangerous Android Marshmallow permissions Android 6.0 addresses these problems with the introduction of a new Android app permissions model. Android Marshmallow permissions are divided into protection levels — normal, dangerous, signature and signatureOrSystem. The two most common are normal and dangerous. Normal permissions are deemed by Google to not pose a threat for the system, other apps or the user’s privacy. That is why they are automatically granted by the system upon installation of the app. They can be reviewed at any point by the user, but they cannot be revoked. It is safe to assume that they are no different than the permissions in the old model. A normal permission, for instance, is VIBRATE — it does not pose serious risks to users. Here is the full list of normal permissions: Normal Permissions. Now let’s talk about the dangerous permissions. Unlike the normal ones, these permissions can give an app access to the user’s private data or affect the system/other apps. That is why an explicit approval from the user at runtime is required for these permissions. Furthermore, these permissions can be revoked by the user at any point. This means that every time an app performs an operation, which requires a dangerous permission, a check to see whether this permission is granted must be made. This can be a hassle for developers to implement but is ideal for users ultimately giving them more control. An example of a dangerous permission is READ_CONTACTS, which can potentially harm the user’s privacy. Android M permission groups As of Marshmallow, permissions are logically coupled in permission groups. For instance, there is the CALENDAR group, which contains the READ_CALENDAR and WRITE_CALENDAR permissions. Dangerous permissions require explicit approval from the user at runtime. When that happens, the approval applies to the whole group granted the permission. So if an user grants the READ_CALENDAR permission to an app, the WRITE_CALENDAR is automatically granted permission as well. It should be noted, that there are special kinds of permissions, that have behavior unlike the normal and dangerous permissions. For instance the SYSTEM ALERT WINDOW is particularly sensitive and needs to be requested. public void requestManageOverlayPermission() { //first check whether the permission is granted //NB! this is only for API level 23 if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { //construct an intent Intent intent = new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION, Uri.parse("package:" + getPackageName())); //request the permission startActivityForResult(intent, REQUEST_OVERLAY_PERMISSION); } } } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { if (requestCode == REQUEST_OVERLAY_PERMISSION) { //check whether permission was granted if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (!Settings.canDrawOverlays(this)) { // SYSTEM_ALERT_WINDOW permission not granted... } else { //SYSTEM_ALERT_WINDOW permission granted } } } } The new Android permissions model and compatibility We have the following possible situations: 1. Installing an app with targetSdkVersion <23 on a device running 6.0 — In this case, all the permissions will be granted upon installation. The user, however, still has the ability to revoke the dangerous permissions. The difference here is that a warning will be shown to the user informing him that this could potentially break the app. 2. Installing an app on a device running 5.1 or below — The old permission model is used regardless of the targetSdkVersion of the app, i.e. all permissions are granted upon installation. Handling permission in apps with Android M So how does the new model change the way we handle permissions in the apps? All permissions (normal and dangerous) are declared in the application’s manifest file. There are no changes to the process here. No extra logic or code is required for the normal permissions, but the dangerous types need to be requested at runtime. To do this, Android introduces a couple of new methods. All the new methods are available in the support library and the usage of their support counterparts is strongly recommended. As we already know, every time we need to perform an operation related to a dangerous permission, a check must be made. But what happens if we skip the check and the respective permission was not granted? In this case a SecurityException is thrown, informing us which permissions are required for the specific operation. For instance, if we want to read the contacts the exception will have the following message: Permission Denial: opening provider com.android.providers.contacts.ContactsProvider2 from ProcessRecord{} (pid, uid) requires android.permission.READ_CONTACTS or android.permission.WRITE_CONTACTS Checking for permissions with Android M As mentioned above, access is granted for all the permissions in a group. In order for the app to read contact information, the READ_CONTACTS OR WRITE_CONTACTS permission is required. Though, it’s not a good idea to rely on this and skip permission checks. To check whether a permission is granted, the ContextCompat.checkSelfPermission() method is used. The method is also available in ActivityCompat and has two arguments — context and the required permission. All permissions are available at Manifest.permission. The method returns either PackageManager.PERMISSION_GRANTED (the permission is available and the operations can proceed) or PackageManager.PERMISSION_DENIED (the app needs to request the permission from the user). If an app has targetSdkVersion >=23 but is installed on a device with Android version <6.0, the method will always return true. Requesting permissions in Android M When requesting permissions, Android provides the requestPermissions() method, which functions asynchronously. When the method returns, a predefined system dialog is shown and a callback is triggered after the user responds to the dialog. It should be noted that the provided system dialog CANNOT be styled. It has three arguments : Activity, permissions in the form of string array and a request code (in the same vein as startActivityForResult()). Let’s say we have an app which shows all contacts and has the ability to add a new contact to existing ones. To request the corresponding permissions, we must do something like this: private static String[] PERMISSIONS_CONTACT = {Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS }; private static final int REQUEST_CONTACTS = 1; private void checkForContactsPermissions() { // Verify that all required contact permissions have been granted. if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) { //given that Manifest.permission.READ_CONTACTS and Manifest.permission.WRITE_CONTACTS //are from the same permission group , on theory it is enough to check for only one of them //yet permission groups are subject to change therefore it is s good idea to check //for both permissions // Contacts permissions have not been granted. Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions."); ActivityCompat.requestPermissions(MainActivity.this, PERMISSIONS_CONTACT, REQUEST_CONTACTS); } else { // Contact permissions have been granted. Show the contacts fragment. Log.i(TAG, "Contact permissions have already been granted. Displaying contact details."); //logic for contacts goes here } } Yet, these two permissions are from the same group. What happens if we want several permissions from different groups? Android handles this by showing a sequence of dialogs. If the system dialog for a particular permission does not show up, this usually means that the permission was denied at some point by the user and the ‘Never ask again’ option was used. If an app tries to request a permission that was not declared in its manifest file, nothing will happen. Handling the callback in Android M When the user is done interacting with the system dialog, the system invokes the app’s onRequestPermissionsResult() method with the results. It can be found in the AppCompatActivity and Fragment and must be overridden. /** * Callback received when a permissions request has been completed. */ @Override public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) { if (requestCode == REQUEST_CONTACTS) { Log.i(TAG, "Received response for contact permissions request."); // response for contact permissions received // both Manifest.permission.READ_CONTACTS and Manifest.permission.WRITE_CONTACTS were //requested therefore we need to check if both were granted if (verifyPermissions(grantResults)) { // all required permissions have been granted, proceed with dangerous operations } else { //contact permissions not granted, disable functionality or show message } } else { super.onRequestPermissionsResult(requestCode, permissions, grantResults); } } private boolean verifyPermissions(int[] grantResults) { // At least one result must be checked. if(grantResults.length < 1){ return false; } // Verify that each required permission has been granted, otherwise return false. for (int result : grantResults) { if (result != PackageManager.PERMISSION_GRANTED) { return false; } } return true; } An interesting question is raised when we have an activity with a fragment, and they both implement the callback method. Where will the result be delivered? The answer depends on where requestPermissions() was invoked. Therefore, if the activity called requestPermissions(), the results will be delivered to its callback and vice versa. Providing explanation Clarity is one of the focuses of the new permission model. Sometimes it is not apparent why an app needs a specific permission. Why does a flashlight app need the READ_CONTACTS permission? A good practice is to provide an explanation before requesting something, yet be careful not to go overboard. Too much information in the form of dialogs/snackbars can lead to bad user experience. The suggestion from Google themselves is to present extra information only when needed – for example when the user has declined a request once, yet tries to use functionality that requires the specific permission. This usually means that it is not clear to the user why the app need the required permission. That is why a short and informative explanation before showing the system dialog is a great course of action. Here’s where the new shouldShowRequestPermissionRationale() method comes into use. It returns true when a specific permission was requested before, but the user has denied the request. It is good to know that this method always returns false in case the ‘Never ask again’ option was selected! Having this method in mind, we can update our previous example to: private void checkForContactsPermissions() { // Verify that all required contact permissions have been granted. if (ActivityCompat.checkSelfPermission(this, Manifest.permission.READ_CONTACTS) != PackageManager.PERMISSION_GRANTED || ActivityCompat.checkSelfPermission(this, Manifest.permission.WRITE_CONTACTS) != PackageManager.PERMISSION_GRANTED) { //given that Manifest.permission.READ_CONTACTS and Manifest.permission.WRITE_CONTACTS //are from the same permission group , on theory it is enough to check for only one of them //yet permission groups are subject to change therefore it is s good idea to check //for both permissions // Contacts permissions have not been granted. Log.i(TAG, "Contact permissions has NOT been granted. Requesting permissions."); requestContactsPermissions(); } else { // Contact permissions have been granted. Show the contacts fragment. Log.i(TAG, "Contact permissions have already been granted. Displaying contact details."); //logic for contacts goes here } } /** * Requests the Contacts permissions. * If the permission has been denied previously, a SnackBar will prompt the user to grant the * permission, otherwise it is requested directly. */ private void requestContactsPermissions() { //check whether the user has previously denied the request if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.READ_CONTACTS) || ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.WRITE_CONTACTS)) { //request was previously denied //provide additional information to the user and request the permission again //keep in mind that the default system dialog can not be tweaked } else { // we are yet to ask for the contacts permissions //do not show additional information yet, instead ask directly ActivityCompat.requestPermissions(this, PERMISSIONS_CONTACT, REQUEST_CONTACTS); } } Revoking permission when an app is running As we already mentioned, dangerous permissions can be revoked at any time by the user. What happens if an app starts an operation, requiring such a permission, but the user turns it off from the management screen and returns to our app ? Well in this case, every method, which is tied to the permission in question, returns null, 0 or throws a SecurityException. This holds true for apps with targetSdkVersion <23 as well. Best Practices Consider the benefits and drawbacks. First of all many dangerous operations can be handled by other apps. For example, when taking a picture with the camera we can have our app send an intent and have another app do the work and just return the result. The later definitely has its perks – things like permissions, UI, UX, etc. are handled by other apps, which arguably can also be a drawback. An app should never ask for permissions it does not need. Requiring lots of permissions also means issuing quite a bit of requests, which can be annoying to the user. Remember that starting with Marshmallow users can disable dangerous permissions at any time, so useless or misleading permissions could be turned off at any time. Do consider using intents to bring down the number of required dangerous permissions. Remember to test for both permission models. According to the latest Android distribution chart, less than 0.1% of all the devices run 6.0 yet this number will definitely continue to grow therefore it is important for an app to be tested for both models. Considering everything, dealing with the new permissions model sounds like a lot of work. But in the long run, permissions ought to be requested when they are needed or the user might be overwhelmed with all the prompts and become frustrated with the app. While we talk about requesting permissions, it is a great time to mention again that the system provided dialogs only provide information about the permission itself, which can often times be insufficient. That’s why it’s important to explain at the right time why an app needs a particular permission. Conclusion In conclusion, be mindful of the new permissions model – while it is a great for the users, it can be a real pain for developers. Spend some time on figuring out how it will impact your apps, as it is a significant change and may require more time and resources to implement than you thought. Share Share on Facebook Share on LinkedIn Share on Twitter