Marshmallow Permissions: Should I, Will I, Could I?

Marshmallow Permissions: Should I, Will I, Could I?

With Marshmallow, there are lots of changes that affect Android users and developers. One of the biggest changes is the major overhaul of app permissions. This change mandates that every app using Location permissions (Beacons included) targeting Marshmallow and above needs to programmatically ask for runtime location permissions. For any user that has an Android device running API version 22 and below, don’t worry, Android’s old permission model will remain in effect.

Should I do a runtime permission check?

   Yes. You should check for runtime permissions if they are classified as “dangerous” for API version 23 and above.

Will I be able to skip the runtime permission check?

    Maybe. Yes if you are targeting your app for an API level lower than 23. But you cannot skip it if the app is targeted for an API level of 23 and above.

Could I add permissions in the manifest without doing the runtime permissions check?

    No. If you are targeting your app for API level 23 and above you cannot just add it in the manifest, you will have to specifically ask for permissions during runtime.

Not all permissions requested by an app require the users to accept them to be able to use it. Some permissions fall in the category of “normal” permissions that will be granted without asking for the users permissions. You can see the list of normal permissions here. Some of the key aspects of Android’s location permissions are:

    • Declaring the permissions in the manifest: this was required before Marshmallow and will continue to be required moving forward.

    • Permissions categories, “Normal” vs “Dangerous:” these categories are outlined clearly here and we strongly recommend reading these guidelines closely. Google categorizes permissions that impact a user’s privacy such as accessing their location, contacts, or permissions that are related to actions that cost money such as telephony and SMS as “dangerous” permissions. “Normal” permissions such as Internet are granted right away without any user intervention.

    • Permission Groups: the permissions are grouped together based on their functionality. When permission for a group is granted, other permissions for the same group do not need to be agreed to or requested. For example, if the permission for android.permission.ACCESS_FINE_LOCATION is granted, there is no need to ask for android.permission.ACCESS_COARSE_LOCATION.

Apps built for API level 22 and below will not need any additional logic for handling the permission, as they will still continue to work when running on Android Marshmallow. Apps designed for Marshmallow need to handle the permissions in the new way if they are accessing “dangerous” permissions.

One of the important things that Android developers should consider when supporting Marshmallow is, users have the option to change the access of app permissions at any time. Even though they have granted or denied the permissions through the system dialog inside the app, they can change it later if they change their mind. One must go a few menu levels deep to get to the screen where the permissions can be changed, but it is possible. Handling permission rejection should be done gracefully.

Marshmallow Pic 1&2 Revised

Considering all of these scenarios, Gimbal is working actively to make sure a situation does not arise where the application does not work properly. However, the onus is on the app developer using the Gimbal SDK to make sure requests for runtime permissions are made and develop contingencies should a user revoke the permissions from the “Settings” menu.

So what do I need to do?

The Gimbal SDK uses permissions categorized as “dangerous” for Geofencing and Beacon detection. What? Really for beacons too? Yes, in addition to Bluetooth permissions (android.permission.BLUETOOTH and android.permission.BLUETOOTH_ADMIN) we need either android.permission.ACCESS_FINE_LOCATION or android.permission.ACCESS_COARSE_LOCATION. Bluetooth permissions let you scan beacons in the foreground without location permissions, but for background scanning you will need location permissions turned on.

With all things said, if your app is using the Gimbal SDK (either our geofencing capabilities or the beacon detection feature) and targeting Android 6.0 devices, you will need to make some changes to your app. There is no workaround for this and it is an absolute must.

Remember: Adding these permissions sets in the Manifest only is not enough. You have to prompt the user to grant the “dangerous” permission since it follows the new model after the app is launched. If you fail to do so, your instance Gimbal’s PlaceManager and BeaconManager instances cannot detect geofences or beacons.

To help out we have developed a checklist to make sure you don’t miss anything.

You will need to check off the following items on this list:
Did you add the following uses permission tags in the Android Manifest xml file?

•  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION”/>
•  <uses-permission android:name="android.permission.BLUETOOTH" />
•  <uses-permission android:name="android.permission.BLUETOOTH_ADMIN" />

 

Did you add the following code to your Activity to check for runtime permissions?

The below Sample Activity below shows the usage of a helper class which checks for the runtime permissions. The method: checkAndRequestPermission, if location is not enabled it will first prompt the user and explain the rationale for why location permission is needed. Remember, you can explain the rationale after the user has denied the first time and it’s totally up to the app developer’s flow and use case scenario. This is done using an AlertDialog after checking the Android API shouldShowRequestPermissionRationale. The text in the AlertDialog needs to give a good explanation on why location permission is needed. We encourage the app developers to come up with a very compelling and nonintrusive message so that users understand the advantages that come from granting this permission. Note: This dialog is not compulsory but it’s highly recommended. This dialog can render before asking for permission or after a user has denied permissions to explain the benefits of this feature and what they would be missing out on by not accepting.

public class YourActivity extends Activity {
    LocationPermissions permissions;


    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.optin);
        enablePlaceMonitoring();
    }
    private void enablePlaceMonitoring() {
        permissions = new LocationPermissions(this);
        permissions.checkAndRequestPermission();
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        this.permissions.onRequestPermissionResult(requestCode, permissions, grantResults);
    }
    ......

 }

public class LocationPermissions implements DialogInterface.OnClickListener {
    private static final String TAG = "LocationPermissions";
    public static final int LOCATION_PERMISSION_REQUEST_CODE = 100;
    static private Activity activity;
    public LocationPermissions(Activity activity) {
        this.activity = activity;
    }
    private PlaceManager placeManager;
    private PlaceEventListener placeEventListener;
    private ArrayAdapter<String> listAdapter;

    public void checkAndRequestPermission(ArrayAdapter<String> listAdapter) {
        this.listAdapter = listAdapter;
        if (isLocationPermissionEnabled()) {
            enablePlaceMonitoring();
        }
        else {
            requestLocationPermission();
        }
    }

    public boolean isLocationPermissionEnabled() {
        return ContextCompat.checkSelfPermission(this.activity, Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED;
    }

    public void requestLocationPermission() {
        if (!ActivityCompat.shouldShowRequestPermissionRationale(this.activity, Manifest.permission.ACCESS_FINE_LOCATION)) {
            showMessageOKCancel("Gimbal SDK requires location permission. Please grant location permission.", this.activity, this, this);
            return;
        }
        activityRequestPermission();
    }

    private static void showMessageOKCancel(String message, Activity activity, DialogInterface.OnClickListener okListener,
            DialogInterface.OnClickListener cancelListener) {
        new AlertDialog.Builder(activity).setMessage(message).setPositiveButton("OK", okListener).create().show();
    }

    @Override
    public void onClick(DialogInterface dialog, int which) {
        if (which == DialogInterface.BUTTON_POSITIVE) {
            ActivityCompat.requestPermissions(this.activity, new String[] { Manifest.permission.ACCESS_FINE_LOCATION,
                    Manifest.permission.ACCESS_COARSE_LOCATION }, LOCATION_PERMISSION_REQUEST_CODE);
        }
        else if (which == DialogInterface.BUTTON_NEGATIVE) {
            Log.e(TAG, "Application was denied permission!");

        }

    }

    private void activityRequestPermission() {
        ActivityCompat.requestPermissions(this.activity, new String[] { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION },
                LOCATION_PERMISSION_REQUEST_CODE);
    }

    public void onRequestPermissionResult(int requestCode, String[] permissions, int[] grantResults) {
        if (requestCode == LOCATION_PERMISSION_REQUEST_CODE) {
            if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                enablePlaceMonitoring();
            }else{
                Log.e(TAG, "Application was denied permission!");
            }

        }
    }

    private void enablePlaceMonitoring() {
        placeEventListener = new PlaceEventListener() {
            @Override
            public void onVisitStart(Visit visit) {
                listAdapter.add(String.format("Start Visit for %s", visit.getPlace().getName()));
                listAdapter.notifyDataSetChanged();
            }

            @Override
            public void onVisitEnd(Visit visit) {
                listAdapter.add(String.format("End Visit for %s", visit.getPlace().getName()));
                listAdapter.notifyDataSetChanged();
            }
        };

        placeManager = PlaceManager.getInstance();
        placeManager.addListener(placeEventListener);
        placeManager.startMonitoring();

        CommunicationManager.getInstance().startReceivingCommunications();

    }

    }
Marshmallow Permissions

This dialog can render before asking for permission or after a user has denied permissions to explain the benefits of this feature and what they would be missing out on by not accepting.

Once the dialog above is presented we call upon the requestPermissions method that actually does the prompting for which the users have to Accept or Deny. After presenting this dialog, the system then calls the new Android requestPermissions method that actually does the prompting.

ActivityCompat.requestPermissions(thisActivity, new String[] { Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION }, MY_LOCATION_PERMISSION_REQUEST_CODE);

Marshmallow Permissions

The dialog screen shot above shows what the user actually sees.

The dialog screen shot above shows what the user actually sees. Once the user makes their choice, Android provides a callback to the app telling you the result of what the user decided (see the onRequestPermissionResult callback). Again, it’s very important to remember that users can change their mind later. I cannot stress this enough. Leave contingencies to handle this scenario. Please note if the user changes the permission using Settings the callback method will not be invoked.

In conclusion, if you want your app to be Marshmallow compliant with Gimbal, you will need to add the code changes in your activity to avoid any unexpected issues. Also, there is sample code that you can take a look at in github which does place monitoring using Gimbal API’s and showcases the requesting of permissions.

Share This