Posted by, Laurence Moroney, Developer Advocate
Along with new platform features, Android 6.0 Marshmallow has a new permissions model that streamlines the app install and auto-update process. Google Play services 8.1 is the first release to support runtime permissions on devices running Android 6.0. and will obtain all the permissions it needs to support its APIs. As a result, your apps won’t normally need to request permissions to use them. However, if you update your apps to target API level 23, they will still need to check and request runtime permissions, as necessary.
To update your Google Play services apps to handle the latest permissions model, it’s good practice to manage the user’s expectations in setting permissions that the runtime may require. Below are some best practices to help you get started.
Before you begin...
For the purposes of this post, ensure that your API level and Target SDK are set to at least 23. Additionally, ensure that, for backwards compatibility, you are using the V4 support library to verify and request permissions. If you don’t have it already, add it to your gradle file:
com.android.support:support-v4:23.0.1
You’ll also need to declare Permissions in your AndroidManifest.xml file. There’s no change here. Whatever permissions your app has always needed should be declared in your AndroidManifest.xml file with the uses-permission tag. Here’s an example:
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"/>
Documentation on maps and location, including a walkthrough on connecting may be found here.
Step 1. Manage Connections to the GoogleApiClient
Make sure that you are handling connection failures onGoogleApiClient
correctly, and that you are using the proper resolution process as outlined here. Note that if Google Play services itself is missing permissions, the user flow to fix them will be handled for you automatically if you follow this methodology.
Here’s an example:
@Override public void onConnectionFailed(ConnectionResult result) { if (mResolvingError) { // Already attempting to resolve an error. return; } else if (result.hasResolution()) { try { mResolvingError = true; result.startResolutionForResult(this, REQUEST_RESOLVE_ERROR); } catch (SendIntentException e) { // There was an error with the resolution intent. Try again. mGoogleApiClient.connect(); } } else { // Show dialog using GooglePlayServicesUtil.getErrorDialog() showErrorDialog(result.getErrorCode()); mResolvingError = true; } }
Step 2. Verify Permissions before calling APIs
It’s easy to assume that once you can connect, and you’ve declared the required permissions for APIs that you want to use in your AndroidManifest.xml file, that future calls will be fine. However, it is vital to ensure that you have the required permission before calling an API or connecting to the GoogleApiClient. This can be done using the checkSelfPermission
method of ActivityCompat
, Fragment
or ContextCompat
.
If the call returns false, i.e. the permissions aren’t granted, you’ll use requestPermissions
to request them. The response to this will be returned in a callback which you will see in the next step.
Here’s an example:
private static final int REQUEST_CODE_LOCATION = 2; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Request missing location permission. ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_LOCATION); } else { // Location permission has been granted, continue as usual. Location myLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); }
Step 3. Implement the request permission callback.
In step 2, if the permission wasn’t granted by the user, the requestPermissions
method was called to ask the user to grant them. The response from the user is captured in the onRequestPermissionsResult
callback. You need to implement this, and always check the return values because the request could be denied or cancelled. Note that you might need to request multiple permissions here -- this sample just checks for a single permission -- you may need to check for more.
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) { if (requestCode == REQUEST_CODE_LOCATION) { if(grantResults.length == 1 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { // success! Location myLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); } else { // Permission was denied or request was cancelled } }
Step 4. Show permission rationale
If the user has previously denied the permission request, your app should display an additional explanation before requesting the permission again. Indeed, if the permissions are non trivial for the core features of the app, and the user is confused as to why they are needed, it would be recommended to guide them.
In this case, before the call to requestPermissions
(step 2, above), you should call shouldShowRequestPermissionRationale
, and if it returns true, you should create some UI to display additional context for the permission.
As such your code from Step 2 might look like this:
private static final int REQUEST_CODE_LOCATION = 2; if (ActivityCompat.checkSelfPermission(this, Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) { // Check Permissions Now if (ActivityCompat.shouldShowRequestPermissionRationale(this, Manifest.permission.ACCESS_FINE_LOCATION)) { // Display UI and wait for user interaction } else { ActivityCompat.requestPermissions( this, new String[]{Manifest.permission.ACCESS_FINE_LOCATION}, REQUEST_CODE_LOCATION); } } else { // permission has been granted, continue as usual Location myLocation = LocationServices.FusedLocationApi.getLastLocation(mGoogleApiClient); }
Note that in this case your user may still deny the permissions, in which case you will need to craft your app so as not to be in a situation where a denied permission affects parts of the app where it shouldn’t. Refer to the best practices section on the Android developer’s site for more details and guidance.
If you’ve built any applications that use Google Play services, I’d recommend that you download the Google Play services 8.1 SDK, and rebuild your applications using it, testing against the most recent versions of Android 6.0, which you can download from the Android Developers site.
Useful resources:
Get started with building for Android 6.0
Android Permissions design guidelines
Google IO 2015 Session on Android M Permissions
Samples for Google Play services 8.1 with coding best practices