Runtime Permissions - StansAssets/com.stansassets.android-native GitHub Wiki

Every Android app runs in a limited-access sandbox. If an app needs to use resources or information outside of its own sandbox, the app has to request the appropriate permission. You declare that your app needs permission by listing the permission in the app manifest and then requesting that the user approve each permission at runtime (on Android 6.0 and higher).

Android Native permissions API is enabled by default and will not add any additional dependencies to your application. However, if you want to skip permission's request dialog on application start you need to enable set Startup Permissions Dialog as Yes.

Also please note that starting from Unity 2018.3.1 permissions dialogs will never be shown on application start.

Add permissions to the manifest

There a number way how to add permission you need in your Android Manifest when using Unity. You can use any of it, and you can also use the built-in Android Native Manifest Manager window to handle your manifest permissions manually. Please note that this UI only displays the manifest located at /Assets/Plugins/Android/AndroidManifest.xml

Runtime Permissions2

The system's behavior after you declare a permission depends on how sensitive the permission is. Some permissions are considered "normal" so the system immediately grants them upon installation. Other permissions are considered "dangerous" so the user must explicitly grant your app access. For more information about the different kinds of permissions, see Protection levels.

Check for permissions

If your app needs a dangerous permission, you must check whether you have that permission every time you perform an operation that requires that permission. Beginning with Android 6.0 (API level 23), users can revoke permissions from any app at any time, even if the app targets a lower API level. So even if the app used the camera yesterday, it can't assume it still has that permission today.

To check if you have a permission, call the AN_PermissionsManager.CheckSelfPermission method. For example, this snippet shows how to check if the activity has permission to write to read contacts:

using SA.Android.Manifest;
using SA.Android.Content.Pm;
using SA.Android.SupportV4.App;
...

var state = AN_PermissionsManager.CheckSelfPermission(AMM_ManifestPermission.READ_CONTACTS);
switch (state) {
    case AN_PackageManager.PermissionState.Granted:
        Debug.Log("READ_CONTACTS Permission Granted");
        break;
    case AN_PackageManager.PermissionState.Denied:
        Debug.Log("READ_CONTACTS Permission Denied");
        break;
}

If the app has the permission, the method returns Granted and the app can proceed with the operation. If the app does not have the permission, the method returns Denied, and the app has to explicitly ask the user for permission.

Explain why the app needs permissions

In some circumstances, you want to help the user understand why your app needs a permission. For example, if a user launches a photography app, the user probably won't be surprised that the app asks for permission to use the camera, but the user might not understand why the app wants access to the user's location or contacts. Before your app requests a permission, you should consider providing an explanation to the user. Keep in mind that you don't want to overwhelm the user with explanations; if you provide too many explanations, the user might find the app frustrating and remove it.

One approach you might use is to provide an explanation only if the user has already denied that permission request. Android provides a utility method, ShouldShowRequestPermissionRationale, that returns true if the user has previously denied the request, and returns false if a user has denied a permission and selected the Don't ask again option in the permission request dialog, or if a device policy prohibits the permission.

using SA.Android.Manifest;
using SA.Android.Content.Pm;
using SA.Android.SupportV4.App;
...

bool result = AN_PermissionsManager.ShouldShowRequestPermissionRationale(AMM_ManifestPermission.READ_CONTACTS);
Debug.Log("ShouldShowRequestPermissionRationale: " + result.ToString());

If a user keeps trying to use functionality that requires a permission, but keeps denying the permission request, that probably means the user doesn't understand why the app needs permission to provide that functionality. In a situation like that, it's probably a good idea to show an explanation.

More advice about how to create a good user experience when asking for permission is provided in App Permissions Best Practices.

Request the permissions you need

If your app doesn't already have the permission it needs, the app must call one of the RequestPermissions methods to request the appropriate permissions. Your app passes the permissions it wants and an integer request code that you specify to identify this permission request. This method functions asynchronously. It returns right away, and after the user responds to the prompt, the system calls the app's callback method with the results, passing the same request code that the app passed to RequestPermissions.

The following code checks if the app has permission to read the user's contacts. If it does not have permission it checks if it should show an explanation for needing the permission, and if no explanation is needed, it requests the permission:

using SA.Android.Manifest;
using SA.Android.Content.Pm;
using SA.Android.SupportV4.App;
...

List<AMM_ManifestPermission> permissions = new List<AMM_ManifestPermission>();

permissions.Add(AMM_ManifestPermission.READ_CONTACTS);
permissions.Add(AMM_ManifestPermission.WRITE_CONTACTS);

AN_PermissionsManager.RequestPermissions(permissions.ToArray(), (result) => {
    foreach(var responce in result.GrantResults) {
        Debug.Log("RequestPermissions:");
        Debug.Log(responce.Permission.ToString() + " / " + responce.GrantResult.ToString());
    }
});

The dialog box shown by the system describes the permission group your app needs access to; it does not list the specific permission. For example, if you request the READ_CONTACTS permission, the system dialog box just says your app needs access to the device's contacts. The user only needs to grant permission once for each permission group. If your app requests any other permissions in that group (that are listed in your app manifest), the system automatically grants them.

Note: Your app still needs to explicitly request every permission it needs, even if the user has already granted another permission in the same group. In addition, the grouping of permissions into groups may change in future Android releases. Your code should not rely on the assumption that particular permissions are or are not in the same group.

For example, suppose you list both READ_CONTACTS and WRITE_CONTACTS in your app manifest. If you request READ_CONTACTS and the user grants the permission, and you then request WRITE_CONTACTS, the system immediately grants you that permission without interacting with the user.

If the user denies a permission request, your app should take appropriate action. For example, your app might show a dialog explaining why it could not perform the user's requested action that needs that permission.

When the system asks the user to grant a permission, the user has the option of telling the system not to ask for that permission again. In that case, any time an app uses RequestPermissions to ask for that permission again, the system immediately denies the request. The system calls your callback method and passes Denied, the same way it would if the user had explicitly rejected your request again. The method also returns false if a device policy prohibits the app from having that permission. This means that when you call RequestPermissions , you cannot assume that any direct interaction with the user has taken place.

Simplified API

The plugin also provides the simplified API for work with permissions.

using SA.Android.App;
...

AN_PermissionsUtility.TryToResolvePermission(AMM_ManifestPermission.READ_CONTACTS, (granted) => {
    if(granted) {
        //authorized
    } else {
       //declined
    }
});

AN_PermissionsUtility.TryToResolvePermission(
new [] { AMM_ManifestPermission.READ_EXTERNAL_STORAGE, AMM_ManifestPermission.WRITE_EXTERNAL_STORAGE },
(granted) => {
    if(granted) {
         //authorized
    } else {
        //declined
    }
});

To provide the best user experience when asking for app permissions, also see App Permissions Best Practices.