preface

Since Android 6.0(API level 23), Android has introduced runtime permissions. Users begin to grant permissions to applications while they are running, rather than when they are installed. If a function of an application requires the use of resources protected by runtime permissions (such as cameras, locations, microphones, etc.), A SecurityException will be thrown when this function is invoked. Android 6.0 has been around for a few years now. However, the android runtime permission application process has always been very tedious, with two main steps:

1, in the need to apply for permission to check whether the permission is agreed, if agreed to directly execute, if not agree to dynamically apply for permission;

2, rewrite the Activity or fragments onRequestPermissionsResult method, according to inside grantResults array whether permission was agreed, if agreed to perform directly, if you do not agree to be a corresponding hint, If the user checks “Don’t ask again” and directs the user to the Settings screen to enable the permission, override onActivityResult to determine whether the permission is granted.

The activity Result API has been introduced in the latest Android X software, and it is not only very elegant, but also requires repetitive boilerplate code every time. With the Activity Result API, you don’t need to manage the requestCode yourself, you just need to provide the requested permissions and the callback to handle the result, which makes the permission request a little easier. However, if the user clicks reject or reject during the permission request and doesn’t ask again, So we still need to deal with these cases by ourselves, but these processing processes are the same and can be encapsulated completely. Therefore, I reconstructed a library that used no interface fragment proxy permission application to make the permission request process simpler. This paper will review the classification of permissions first. I then describe the design of PermissionHelper’s request duration, and finally note the important behavior changes related to permission requests since Android 6.0 as the system iterates.

  • PermissionHelper

Classification of permissions

All of android’s predefined permissions (excluding vendor custom ones) can be defined in the manifest.Permission static class. Android divides permissions into four categories: Common rights, signature rights, dangerous rights, and special rights are assigned a Protection Level for each type of rights, namely, Normal, Signature, Dangerous, and APPOP. The following describes the four types of rights

1. Common permissions

Common permissions are also called normal permissions, Protection Level is normal, it does not need to dynamically apply, you only need to static declaration in the androidmanifest.xml, and then the system will automatically grant the corresponding permissions when the application is installed. It can access data or operations outside the application sandbox protected by this common permission that does not leak or tamper with the user’s privacy and poses little risk to the user or other applications.

2. Signature permission

This type of permission is only available to applications with the same signature. The Protection Level is signature, and it does not need to be applied dynamically. For example, application A defines A permission in androidmanifest.xml and adds Android :protectionLevel= “signature” to the permission tag, indicating that application A has declared A signature permission. When application B wants to access the data protected by the permission from application A, it must declare the permission in androidmanifest.xml and package it with the same signature as application A. In this way, the system will automatically grant the permission to application B when application B is installed, and application B can access the data controlled by the permission after obtaining the authorization. Other applications declare this permission in androidmanifest.xml even if they know it. However, the system does not grant this permission to them at installation due to different application signatures. As a result, other applications cannot access the data protected by this permission.

In addition, some signing privileges are not available for third-party applications, but only for pre-installed applications. The Protection Level of such signing privileges is Signature and Privileged.

3. Danger permission

Dangerous permissions are also called runtime permissions. The Protection Level is dangerous. As opposed to common permissions, when an application obtains a dangerous permission, the user’s private data is at risk of being compromised or tampered with, so if you want to use data or operations protected by this permission, You must statically declare dangerous permissions in androidmanifest.xml and dynamically apply for permissions before accessing these data or operations. A permission request window will pop up asking for permission. Unless the user agrees to the permission, you cannot use the data or operations protected by the permission.

All dangerous permissions have corresponding permission groups. Android has 11 predefined permission groups (according to android 11 summary), and these 11 permission groups contain 30 dangerous permissions and several common permissions. When we dynamically apply for a dangerous permission, we apply according to the permission group. Other permissions registered in Androidmanifest.xml in the corresponding permission group will also be granted. The 11 predefined permissions of Android include the following dangerous permissions:

Permission Group Dangerous Permissions
CALENDAR READ_CALENDAR

WRITE_CALENDAR
CALL_LOG (Added Call History in Android 29) READ_CALL_LOG

WRITE_CALL_LOG

PROCESS_OUTGOING_CALLS
CAMERA CAMERA
CONTACTS: READ_CONTACTS

WRITE_CONTACTS

GET_ACCOUNTS
LOCATION information ACCESS_COARSE_LOCATION

ACCESS_FINE_LOCATION

ACCESS_BACKGROUND_LOCATION (Added in android 10)
MICROPHONE RECORD_AUDIO
Have a PHONE call READ_PHONE_NUMBERS

READ_PHONE_STATE

CALL_PHONE

ANSWER_PHONE_CALLS

ADD_VOICEMAIL

USE_SIP

ACCEPT_HANDOVER (Added in android 9)
SENSORS body SENSORS BODY_SENSORS
SMS READ_SMS

RECEIVE_WAP_PUSH

RECEIVE_SMS

RECEIVE_MMS

SEND_SMS
STORAGE space READ_EXTERNAL_STORAGE

WRITE_EXTERNAL_STORAGE

ACCESS_MEDIA_LOCATION (Added in android 10)
ACTIVITY_RECOGNITION (Physical Activity, Added in Android 10) ACTIVITY_RECOGNITION (Added in android 10)

4. Special permissions

Special permissions are used to protect certain application operations. The Protection Level is apPOP, which also needs to be declared statically in androidmanifest.xml before use. It also needs to be applied dynamically, but it is different from dangerous permissions. The dangerous permission application will pop up a dialog box asking whether you agree, while the special permission application needs to jump to the specified setting interface and let you manually click the toggle button to confirm whether you agree or not. Up to Android 11, THE five commonly used special permissions I have learned are as follows:

  • SYSTEM_ALERT_WINDOW: allows applications to draw hover Windows on top of other applications. This permission is required if you create a hover window of type TYPE_APPLICATION_OVERLAY.
  • WRITE_SETTINGS: Allows applications to modify System Settings. You need to apply for this permission when you need to modify System parameters, such as screen brightness.
  • REQUEST_INSTALL_PACKAGES: allows applications to install applications from unknown sources. After Android 8.0, you need to apply for this permission when you install third-party applications in your application. Otherwise, you will not be redirected to the installation screen.
  • PACKAGE_USAGE_STATS: Allows an application to collect usage information about other applications. This permission is required when you use the UsageStatsManager Api to collect usage information about other applications.
  • MANAGE_EXTERNAL_STORAGE(Added in Android 11) : Allows applications to access external storage in scoped storage. Android 11 will force newly installed applications to use scoped storage, but for applications like file manager that need to manage files on the entire SD card, Therefore, for these special applications, you can apply for this permission to obtain read and write permission on the entire SD card. When the application grants this permission, it can access the real path of the file. Note that this permission is very dangerous and may need to be reviewed before claiming this permission for the shelf application.

In addition to special permissions, LOCATION permissions in the LOCATION group are also a little special. It should be noted that LOCATION information is not only dependent on the dynamic application for LOCATION permissions but also on the system LOCATION switch. If you apply for LOCATION permissions without turning on the LOCATION switch, then even if the user agrees to grant LOCATION permissions, The application cannot obtain Location information through the location-related Api. Therefore, before applying for Location permission, it is best to determine whether to turn on the Location switch by using the LocationManager#isProviderEnabled method. If the positioning switch is not turned on, it is necessary to jump to the setting interface and turn on the positioning switch. The pseudo-code is as follows:

val locationManager = this.getSystemService(Context.LOCATION_SERVICE) as LocationManager
if (locationManager.isProviderEnabled(LocationManager.GPS_PROVIDER) or locationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
    // Request location permissions
} else {
    // Jump to the location where the location is enabled
    Toast.makeText(this."Location service not enabled detected. Please enable it.", Toast.LENGTH_SHORT).show()
    val intent = Intent().apply {
        action = Settings.ACTION_LOCATION_SOURCE_SETTINGS
    }
    startActivityForResult(intent, REQUEST_CODE_LOCATION_PROVIDER)
}
Copy the code

However, the PermissionHelper has already wrapped all the dangerous permissions and special permissions for you. You just need to declare the permissions statically in androidmanifest.xml and then apply them dynamically in your code as usual. I refer to both dangerous and special permissions as dynamic permissions, because they need to be requested dynamically.

Dynamic permission application design

Dynamic permission application has different behavior according to different Android version and application targetSdkVersion, there are two main processing, as follows:

  • Android version <= 5.1 or application targetSdkVersion <= 22: When a user agrees to install an application, the system requires the user to grant all permissions declared by the application, including dynamic permissions. If the user does not agree to grant permissions, the user can only refuse to install the application. If the user agrees to grant all permissions, the only way to revoke permissions is to uninstall the application.
  • Android version >= 6.0 and application targetSdkVersion >= 23: When the user agrees to install applications, the system is no longer mandatory users must be authorized to dynamic permissions, the system will only authorized application common permissions, except dynamic permissions dynamically access need to apply the dynamic application to the relevant function, when apply for dynamic permissions, the user can choose to grant or deny each jurisdiction, even if the user agrees to authorize access, Users can also go to Settings to change dynamic permissions at any time, so you need to apply for dynamic permissions every time you use them, because users can turn them off again in Settings.

When android version <= 5.1 or application targetSdkVersion <= 22, the system uses AppOps for permission management, which is a set of application operation permission management introduced in Android 4.4. AppOps manages all operations that may involve user privacy and security, such as Access Notification, Keep Weak Lock, display toast, etc., while the runtime permission management emerged in Android 6.0, which is based on AppOps. Dynamic request encapsulation and explicit specification, and when targetSdkVersion <= 22 is running on android >= 6.0, dynamic permission can be turned off in the “Settings” screen. When relevant functions are used in the running process of the application, it will crash due to lack of permissions. In this case, you can only use the checkOp method of AppOps to check whether the corresponding permissions are authorized, and jump to the “Settings” interface if there are no permissions. Considering that Android 6.0 has been launched for a long time, The app store also does not allow applications with targetSdkVersion < 23 to be listed, so in order to reduce the complexity of the framework, the dynamic permission application design does not consider AppOps compatible permission management operations. So when you use PermissionHelper, apply targetSdkVersion >= 23.

PermissionHelper support dangerous permissions and special permission to apply for, need only one line of code can access request, has a life cycle induction ability, to initiate the request and only visible at the interface between the correction results, at the same time when the system configuration changes such as screen after rotation to restore access application process before, don’t interrupt access application process, With high flexibility, you can set the callback before and after the request is rejected, suspend the permission application process when the callback occurs, and then decide whether to continue the permission application process according to the user’s wishes. The whole application process is shown in the following figure:

PermissionHelper can use callbacks to call back requested permissions and denied permissions before permission requests start and after permission requests are denied. In callbacks, you can explain to the user that the requested permissions are necessary for the application through a pop-up window, and instruct the user to continue or reauthorize the application. PermissionHelper does not customize the popover UI. The popover UI is customized by the developer. The developer simply calls the corresponding method of the Process instance in the callback after the user agrees or rejects the request to resume the suspended permission request Process and then processes the result in the final result callback. About the popup explaining the reason of permission application to the user, the popup content is suggested as follows:

1. A description of the list of permissions to be authorized;

2. Include a confirm button that users can click to re-authorize or jump to “Settings”;

3, including the cancel button, users can click the cancel button to abandon authorization.

If the user does not grant this permission, the application will not be able to continue. Consider canceling the cancel button in step 3. You cannot cancel the popover, but must either authorize again or go to “Settings” to authorize.

The design of PermissionHelper refers to okHTTP’s interceptor mode. Through the responsibility Chain mode, the dangerous permission request, special permission request, pre-processing and post-processing are divided into nodes, and then the nodes are connected through the Chain. Each node is only responsible for the corresponding content, as follows:

val originalRequest = Request()    
val interceptors = listOf(
    StartRequestNode(),
    RequestLocationNode(),
    RequestNormalNode(),
    RequestSpecialNode(),
    PostRequestNode(),
    FinishRequestNode()
)
DefaultChain(originalRequest, interceptors).process(originalRequest)
Copy the code

Lifecycle+LiveData PermissionHelper implements Lifecycle sensing capabilities using Lifecycle+LiveData, both of which are officially supported for operations that need to respond to Lifecycle sensing. More lightweight and maintainable code can be written to avoid memory leaks after interface destruction. For data recovery after system configuration changes, ViewModel component is used, which is officially supported to save data that needs to be restored after configuration changes, such as some UI-related data. Through this three-piece + responsibility chain pattern, an easy-to-use permission application framework can be seen in the code repository for more detailed usage and implementation details.

Permission application related changes

Since the introduction of dynamic permission application in Android 6.0, some application behaviors have also changed with the iteration of the system, in order to better protect users’ privacy and make permission application aware to users:

After Android 8.0 and the application’s targetSdkVersion >= 28, the application applies for a dangerous permission authorization. After the user agrees, the system will not mistakenly grant the application other permissions registered in androidmanifest.xml in the permission group corresponding to the dangerous permission. However, once a user application agrees to grant a dangerous permission, subsequent requests in the dangerous permission group will be automatically approved without prompting the user. For example, if an application registers the READ_EXTERNAL_STORAGE and WRITE_EXTERNAL_STORAGE permissions in androidmanifest.xml, the application applies for the READ_EXTERNAL_STORAGE permission and the user agrees. Prior to Android 8.0, the system also granted WRITE_EXTERNAL_STORAGE permission with user consent because it is the same permission group as READ_EXTERNAL_STORAGE and is registered in androidmanifest.xml. However, after Android 8.0 and the application’s targetSdkVersion >= 28, the system will only grant READ_EXTERNAL_STORAGE permission with the user’s consent, but if the application later applies for WRITE_EXTERNAL_STORAGE permission, In other words, if only the external storage space read permission, under the earlier version (Android < 8.0) there is no problem using write operations on the external storage space. However, in higher versions (Android >= 8.0&& targetSdkVersion >= 28), there is a problem. The solution is to apply for both read and write permissions.

Android 9.0 added CALL_LOG and moved READ_CALL_LOG, WRITE_CALL_LOG, and PROCESS_OUTGOING_CALLS from PHONE to CALL_LOG. Android 9.0 added CALL_LOG and moved READ_CALL_LOG, WRITE_CALL_LOG, and PROCESS_OUTGOING_CALLS from PHONE. The CALL_LOG permission group gives users more control over applications that need access to sensitive information about phone call records, such as reading call records and identifying phone numbers.

Android 10 introduces a number of privacy changes, including new ACTIVITY_RECOGNITION permissions and permissions groups that allow apps to detect a user’s steps or categorize a user’s physical activity such as walking or cycling. In addition, Android 10 introduces scoped storage. When scoped storage is enabled for an application, the WRITE_EXTERNAL_STORAGE permission is invalid. Application for the WRITE_EXTERNAL_STORAGE permission does not affect the application’s storage access permission. And WRITE_EXTERNAL_STORAGE will be deprecated in the future, because the purpose of scoped storage is not to allow applications to modify external storage outside the application sandbox. The ACCESS_BACKGROUND_LOCATION permission is added, which belongs to the LOCATION permission group and is used for background applications to access user LOCATION. Separate from the foreground location permissions such as ACCESS_FINE_LOCATION and ACCESS_COARSE_LOCATION. When your app is targetSdkVersion >= 29 and running above Android 10, The application needs to dynamically apply for background location permission when accessing and locating in the background. When you apply for background location permission and foreground location permission together, the popup authorization box will have two options: Always allow allowed and used only in the application process, click the always allow said authorized background location permissions and location permissions reception at the same time, click in the process of application using only allows only authorized at the front desk said location permissions, and then apply for alone will only when applying for again next time background location permissions, and there will be 2 allows options, And click always allow to allow the background location permission application. When your application targetSdkVersion < 29 runs on Android 10 or more, the application will grant the background location permission to the application when applying for foreground location permission. Android 10 also added the ACCESS_MEDIA_LOCATION permission, which belongs to the STORAGE permission group. After Android 10, due to privacy issues, the location of the image is no longer provided by default. To obtain this information need to apply to the user ACCESS_MEDIA_LOCATION permissions, and use the MediaStore. SetRequireOriginal update file Uri () interface.

Android 11 also introduces a number of privacy changes. Android 11 forces newly installed apps (targetSdkVersion >= 30) to enable scoped storage, Add MANAGE_EXTERNAL_STORAGE to replace the WRITE_EXTERNAL_STORAGE permission and provide the MANAGE_EXTERNAL_STORAGE permission for applications that need to manage files on the SD card, such as mobile phone managers and file managers. In Android 11, when a user returns to an application after enabling the “Install apps from unknown sources” permission, the application will be killed and restarted. This behavior is related to forced partition storage. After Android 11, if the application repeatedly clicks to reject a permission, the system will directly reject the next request for the permission without even popping up the authorization popup. This behavior is equivalent to checking “Don’t ask again” before Android 11. Android 11 has also added one-time permissions and auto-reset. These changes do not require additional adaptation as long as you correctly perform runtime permissions requests; At the same time, after Android 11, when the application with targetSdkVersion < 30 applies for background positioning permission and foreground positioning permission together, the “always allow” option in the popup authorization box will no longer be displayed. Only this permit and only in the application use process will be allowed. In android 11, the ACCESS_BACKGROUND_LOCATION permission of targetSdkVersion >= 30 should be applied independently, not together with the ACCESS_BACKGROUND_LOCATION permission. If you apply for permission with the foreground, the system will directly reject the authorization popup without popping up. The system recommends incremental request for permission, which is more user-friendly. At the same time, users must first agree with the foreground permission before entering the background to locate permission applications.

It can be seen that since the introduction of ACCESS_BACKGROUND_LOCATION from Android 10, the application of background location permission has been very special. It can be applied together with the foreground location permission in Android 10, but not together with the foreground location permission in Android 11. In this special case, when applying for background positioning permission, do as follows:

  • 1, first request front positioning permission, and then request background positioning permission;
  • 2, separate request background location permission, do not request with other permissions.

The above PermissionHelper has already been processed, so you just need to pass in the background and foreground location permissions.

conclusion

This article mainly to let everybody to access application process have further understanding of, then you can through the encapsulation of dynamic permissions, will detect dynamic permissions, dynamic permissions request, permissions jump, to monitor the permissions setting isolates processing and business functions, such as business after dynamic permissions can very quick access to support, improve the development efficiency.

That’s all for this article!

References:

Request app permissions

Android permissions, do you really know that?

Android 6.0 run permission parsing