Android Permission System And Case Study
How to work with Android Permission System in Android M
To protect the android system and the user’s privacy, Android system runs each application in a limited access sandbox. If the application wants to use resources or information outside of its sandbox, the application has to explicitly request permission. Depending on the type of permission the application requests, the system may grant the permission automatically, or the system may ask the user to grant the permission. Android depends on two types of permission to decide to grant or request permission, Normal and Dangerous Permissions.
Declaring Permission:
You need to declare required permissions in Application manifest file.
Depending on the sensitiveness of the permission, the system might grant the permission automatically, or the device user might have to grant the request. For example, if your application requests permission to turn on the device’s flashlight, the system grants that permission automatically. But if your application needs to read the user’s contacts, the system asks the user to approve that permission. Depending on the platform version, the user grants the permission either when they install the application (on Android 5.1 and lower) or while running the application (on Android 6.0 and higher).
Add Permissions to Manifest:
To declare that your application needs a permission, put a <uses-permission> element in your application manifest file, as a child of the top-level <manifest> element. For example, an application that needs to send SMS messages would have this line in the manifest:
[code]
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.example.ttnd">
<uses-permission android:name="android.permission.SEND_SMS"/>
<application …>
…
</application>
</manifest>
[/code]
Requesting Permissions at Runtime:
The Beginning in Android 6.0 (API level 23), users grant permissions to applications while the application is running, not when they install the application. This approach streamlines the application install process, since the user does not need to grant permissions when they install or update the application. It also gives the user more control over the application’s functionality; for example, a user could choose to give a camera application access to the camera but not to the device location. The user can revoke the permissions at any time, by going to the application’s Settings screen.
Android System permissions are divided into two categories, Normal and Dangerous permission:
The Normal permissions do not directly risk the user’s privacy. If your application lists a normal permission in its manifest, the system grants the permission automatically. The Dangerous permissions can give the application access to the user’s confidential data. If you list a dangerous permission, the user has to explicitly give approval to your application.
- If the device is running Android 5.1 or lower, or your application’s target SDK is 22 or lower: If you list a dangerous permission in your manifest, the user has to grant the permission when they install the application; if they do not grant the permission, the system does not install the application at all.
- If the device is running Android 6.0 or higher, and your application’s target SDK is 23 or higher: The application has to list the permissions in the manifest, and it must request each dangerous permission it needs while the application is running. The user can grant or deny each permission, and the application can continue to run with limited capabilities even if the user denies a permission request.
Checks for Permissions:
if your application needs a dangerous permission, you must check whether you have that permission every time to perform an operation that requires the permission. The user is always free to deny the permission, so even if the application used the camera yesterday, it can’t assume it still has that permission today.
To check if you have a permission, call the ContextCompat.checkSelfPermission() method. For example, Code snippet below shows how to check if the activity has permission to write to the calendar:
[code]
// Assume thisActivity is the current activity
int permissionCheck = ContextCompat.checkSelfPermission(thisActivity, Manifest.permission.WRITE_CALENDAR);
[/code]
If the application has the permission, the method returns PackageManager.PERMISSION_GRANTED, and the application can proceed with the operation. If the application does not have the permission, the method returns returns PackageManager .PERMISSION_DENIED, and the application has to explicitly ask the user for permission.
Request for Permission:
If your application doesn’t already have the permission it needs, the application must call requestPermissions() methods to request the appropriate permissions. Your application passes the permissions it wants, and also 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 dialog box, the system calls the application’s callback method with the results, passing the same request code that the application passed to requestPermissions().
There may be a situation, where the user deny the requested permission, that probably shows that the user doesn’t understand why the application needs the permission to provide that functionality. In a situation like that, it’s probably a good idea to show an explanation.
So when the user again interact with the feature that have been rejected by the user earlier, Android provides a utility method, shouldShowRequestPermissionRationale(). This method returns true if the application has been requested this permission previously and the user denied the request.
If the user turned down the permission request in the past and chose the Don’t ask again option in the permission request system dialog, this method returns false. The method also returns false if a device policy prohibits the application from having that permission.
The following code checks if the application has permission to read the user’s contacts, and requests the permission if necessary:
[code]
// Here, thisActivity is the current activity
if (ContextCompat.checkSelfPermission(thisActivity,
Manifest.permission.READ_CONTACTS)
!= PackageManager.PERMISSION_GRANTED) {
// Should we show an explanation?
if (ActivityCompat.shouldShowRequestPermissionRationale(thisActivity,
Manifest.permission.READ_CONTACTS)) {
// Show an expanation to the user *asynchronously* — don’t block
// this thread waiting for the user’s response! After the user
// sees the explanation, try again to request the permission.
} else {
// No explanation needed, we can request the permission.
ActivityCompat.requestPermissions(thisActivity,
new String[]{Manifest.permission.READ_CONTACTS},
MY_PERMISSIONS_REQUEST_READ_CONTACTS);
// MY_PERMISSIONS_REQUEST_READ_CONTACTS is an
// app-defined int constant. The callback method gets the
// result of the request.
}
}[/code]
When your application calls requestPermissions(), the system shows a standard dialog box to the user. Your application cannot configure or alter that dialog box. If you need to provide any information or explanation to the user, you should do that before you call requestPermissions().
Handle the permissions request response:
When your application requests permissions, the system shows a dialog box to the user. When the user responds, the system invokes your application’s onRequestPermissionsResult() method, passing it the user response. Your application has to override that method. The callback is passed the same request code you passed to requestPermissions(). For example, if an application requests READ_CONTACT access it might have the following callback method:
[code]
@Override
public void onRequestPermissionsResult(int requestCode,
String permissions[], int[] grantResults) {
switch (requestCode) {
case MY_PERMISSIONS_REQUEST_READ_CONTACTS: {
// If request is cancelled, the result arrays are empty.
if (grantResults.length > 0
&& grantResults[0] == PackageManager.PERMISSION_GRANTED) {
// permission was granted
// contacts-related task you need to do.
} else {
// permission denied, Disable the
// functionality that depends on this permission.
}
return;
}
// other ‘case’ lines to check for other
// permissions this application might request
}
}
[/code]
The dialog box shown by the system describes the permission group your application 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 application needs access to the device’s contacts. The user only needs to grant permission once for each permission group. If your application requests any other permissions in that group (that are listed in your application manifest), the system automatically grants them. When you request the permission, the system calls your onRequestPermissionsResult() callback method and passes PERMISSION_GRANTED, the same way it would if the user had explicitly granted your request through the system dialog box.
How Android system ask for Permission:
Here is a flow diagram that represent the general flow of events that will happen when you need to use a feature that requires a permission.
We can check if a permission has been previously granted by calling checkSelfPermission before using the particular feature that will require that permission. This method returns an int value based on whether that permission is granted
or not.
If the value of int is equal to PackageManager.PERMISSION_GRANTED that means you can continue as expected, Otherwise You have to request for the permission to grant using RequestPermission. You just need to pass a integer request code and String permission array.
Here is the code snippet:
[code]
int hasLocationPermission = checkSelfPermission( Manifest.permission.ACCESS_FINE_LOCATION );
int hasSMSPermission = checkSelfPermission( Manifest.permission.SEND_SMS );
List<String> permissions = new ArrayList<String>();
if( hasLocationPermission != PackageManager.PERMISSION_GRANTED ) {
permissions.add( Manifest.permission.ACCESS_FINE_LOCATION );
}
if( hasSMSPermission != PackageManager.PERMISSION_GRANTED ) {
permissions.add( Manifest.permission.SEND_SMS );
}
if( !permissions.isEmpty() ) {
requestPermissions( permissions.toArray( new String[permissions.size()] ), REQUEST_CODE_SOME_FEATURES_PERMISSIONS );
}
[/code]
when requestPermissions is called, a dialog is presented to the user for each permission group that your application is asking permission for. It is best practice to only request permissions as needed, rather than in bulk when your user first starts the application.
After interacting with dialog, onRequestPermissionsResult is called and can be accessed in your Activity.
Code snippets will show, weather a permission has been granted or denied. If the user denied the required permission than you should disable the affected feature and let the user know about that.
[code]
@Override
public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
switch ( requestCode ) {
case REQUEST_CODE_SOME_FEATURES_PERMISSIONS: {
for( int i = 0; i < permissions.length; i++ ) {
if( grantResults[i] == PackageManager.PERMISSION_GRANTED ) {
Log.d( "Permissions", "Permission Granted: " + permissions[i] );
} else if( grantResults[i] == PackageManager.PERMISSION_DENIED ) {
Log.d( "Permissions", "Permission Denied: " + permissions[i] );
}
}
}
break;
default: {
super.onRequestPermissionsResult(requestCode, permissions, grantResults);
}
}
}[/code]
I also prepared a presentation on Android Permission System, You can check it out here.
Case Study:
I was doing some research work on android permission system and I found some surprising results with Android M device. I am trying to share it with this case study. Please read the below points thoroughly.
- When I set the targetSDKVersion to 23, The application was crashed in Android M device, due to permission denied, Because I did not handle the permission request as pr Android M guide lines.
- But when I changed the targetSDKVersion to 21 (or less than 23), then the application was working fine in Android M device. When I denied the permission manually from application settings, in the application that build by targetSDKVersion 21, the application did not crash, but particular functionality was not working.
- For instance: I was reading contact from Android M device, I build the application with targetSDKVersion 23, I did not handle the permission request (READ_CONTACT) for android M as android guidelines. The application was crashed, due to permission denied. When I changed the targetSDKVersion to 21 (or less than 23), the application worked fine, the application was able to read contact. When I manually denied the READ_CONTACT permission, The application did not crash, but the application was unable to read contacts.
Conclusion: I think, the conclusion is, When we target the application to 23 that means, You have tested your application till API level 23 (Android M), So android system would not take care of your application for API version 23 or less. But if you are targeting your application with API version 21 (or less than 23), that means android system will take care of your application above API version 21. So as conclusion targetSDKVersion=23 means you have tested your application till API level 23.
If the device is running Android 5.1 or lower, or your application’s target SDK is 22 or lower: If you list a dangerous permission in your manifest, the user has to grant the permission when they install the application; if they do not grant the permission, the system does not install the application at all.
So what if user revokes the permission for a legacy app targeting SDK 21 . so is there any way to check for permission. ?
Android Permission system works after application installed on Android device, So there is no provision of grant or reject permission before installation. After installation, Application will ask for permission, If you reject the permission first time than android system again ask for permittion when you again try to access any dangerous permission. For more detailed information See the diagram “How Android system ask for Permission”.