Complete permission parsing during Android 6.0 Runtime

Source: Internet
Author: User

Complete permission parsing during Android 6.0 Runtime
I. Overview

With the release and popularization of Android 6.0, some changes brought about by the new version of the SDK should be addressed by developers. The first concern is the changes in the permission mechanism. For several major changes in version 6.0, refer to the article titled "website Permissions.

OK. One of the purposes of this article is to introduce the permission processing during runtime and some knowledge about the current permission-related libraries.


This article is also based on the above understanding, experiment and encapsulation.

Ii. Changes and features of runtime Permissions

For permissions lower than 6.0 and during installation, a permission list is generated according to the permission statement. The user can complete the installation of the app only after consent, resulting in the desire to use an app, it is necessary to put aside unnecessary permissions (for example, an app must access the address book, text message, and so on ). After 6.0, we can directly install the app. When the app requires an inappropriate permission, we can reject it (for example, the single-host chess game, requesting access to any permissions, I disagree ). Of course, you can also view the permissions of each app on the settings page, and authorize or revoke the permissions of a single app.

The new permission mechanism better protects users' privacy. Google classifies Permissions into two categories: Normal Permissions, which generally does not involve user privacy and does not require user authorization, for example, mobile phone vibration and network access. The other type is Dangerous Permission, which generally involves user privacy and requires user authorization, such as reading sdcard and accessing address book.

Normal Permissions:
ACCESS_LOCATION_EXTRA_COMMANDSACCESS_NETWORK_STATEACCESS_NOTIFICATION_POLICYACCESS_WIFI_STATEBLUETOOTHBLUETOOTH_ADMINBROADCAST_STICKYCHANGE_NETWORK_STATECHANGE_WIFI_MULTICAST_STATECHANGE_WIFI_STATEDISABLE_KEYGUARDEXPAND_STATUS_BARGET_PACKAGE_SIZEINSTALL_SHORTCUTINTERNETKILL_BACKGROUND_PROCESSESMODIFY_AUDIO_SETTINGSNFCREAD_SYNC_SETTINGSREAD_SYNC_STATSRECEIVE_BOOT_COMPLETEDREORDER_TASKSREQUEST_INSTALL_PACKAGESSET_ALARMSET_TIME_ZONESET_WALLPAPERSET_WALLPAPER_HINTSTRANSMIT_IRUNINSTALL_SHORTCUTUSE_FINGERPRINTVIBRATEWAKE_LOCKWRITE_SYNC_SETTINGS
Dangerous Permissions:
group:android.permission-group.CONTACTS  permission:android.permission.WRITE_CONTACTS  permission:android.permission.GET_ACCOUNTS  permission:android.permission.READ_CONTACTSgroup:android.permission-group.PHONE  permission:android.permission.READ_CALL_LOG  permission:android.permission.READ_PHONE_STATE  permission:android.permission.CALL_PHONE  permission:android.permission.WRITE_CALL_LOG  permission:android.permission.USE_SIP  permission:android.permission.PROCESS_OUTGOING_CALLS  permission:com.android.voicemail.permission.ADD_VOICEMAILgroup:android.permission-group.CALENDAR  permission:android.permission.READ_CALENDAR  permission:android.permission.WRITE_CALENDARgroup:android.permission-group.CAMERA  permission:android.permission.CAMERAgroup:android.permission-group.SENSORS  permission:android.permission.BODY_SENSORSgroup:android.permission-group.LOCATION  permission:android.permission.ACCESS_FINE_LOCATION  permission:android.permission.ACCESS_COARSE_LOCATIONgroup:android.permission-group.STORAGE  permission:android.permission.READ_EXTERNAL_STORAGE  permission:android.permission.WRITE_EXTERNAL_STORAGEgroup:android.permission-group.MICROPHONE  permission:android.permission.RECORD_AUDIOgroup:android.permission-group.SMS  permission:android.permission.READ_SMS  permission:android.permission.RECEIVE_WAP_PUSH  permission:android.permission.RECEIVE_MMS  permission:android.permission.RECEIVE_SMS  permission:android.permission.SEND_SMS  permission:android.permission.READ_CELL_BROADCASTS

You can useadb shell pm list permissions -d -g.

When you see the preceding dangerous permissions, you will find a problem. It seems that dangerous permissions are all in a group,

So there is a question: Does grouping affect our permission mechanism?

It does have an impact. If the app runs on Android 6. x, the authorization mechanism is like this. If you apply for a dangerous permission, assume that your app has already been authorized by the user.Same GroupThe system will immediately authorize a dangerous permission, instead of clicking authorize. For exampleREAD_CONTACTSAuthorized. When your app appliesWRITE_CONTACTSThe system will directly authorize the pass. In addition, the text description above dialog displayed during the application is also a description of the entire permission group, rather than a single permission (ps: This dialog cannot be customized ).

However, do not rely too much on the permission group. Try to apply for normal processes for every dangerous permission, because the permission group may change in later versions.

Iii. Related APIs

Fortunately, the related APIs at runtime are also relatively simple, so it is not very painful to adapt.

The procedure for applying for permissions is as follows:

Add the required permissions to the AndroidManifest file.

This step has nothing to do with our previous development. Trying to apply for a permission without a declaration may cause the program to crash.

Check permissions

if (ContextCompat.checkSelfPermission(thisActivity,                Manifest.permission.READ_CONTACTS)        != PackageManager.PERMISSION_GRANTED) {}else{    //}

This involves an API,ContextCompat.checkSelfPermissionIs used to check whether a permission has been granted. The return value of the method isPackageManager.PERMISSION_DENIEDOrPackageManager.PERMISSION_GRANTED. When DENIED is returned, you need to apply for authorization.

Apply for authorization

 ActivityCompat.requestPermissions(thisActivity,                new String[]{Manifest.permission.READ_CONTACTS},                MY_PERMISSIONS_REQUEST_READ_CONTACTS);

This method is asynchronous. The first parameter is Context, the second parameter is a string array of permissions to be applied for, and the third parameter is requestCode, which is mainly used for callback detection. From the method namerequestPermissionsThe second parameter indicates that Multiple permissions can be applied for at one time.One by oneAsk whether the user is authorized.

Process permission application callback

@Overridepublic 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, yay! Do the                // contacts-related task you need to do.            } else {                // permission denied, boo! Disable the                // functionality that depends on this permission.            }            return;        }    }}

OK. For the permission application result, first verify that requestCode locates your application, and then verify that grantResults corresponds to the application result. The array here corresponds to the second permission string array at the time of application. If you apply for two permissions at the same time, the length of grantResults is 2, which records the application results of the two permissions respectively. If the application is successful, you can do your work ~

Of course, our permission application does not go here. The basic introduction is as above. However, there is another API worth mentioning:

// 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.}

This API is mainly used to give users an explanation of the permission application. This method is used only when the user has rejected your permission application in the previous time. That is to say, the user has rejected the request once. In the authorization box, you need to explain to the user why the authorization is required.

The preceding steps are combined as follows:

// Here, thisActivity is the current activityif (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.    }}
Iv. Simple examples

Here is a simple example for running permissions. I believe that when we first came into contact with Android, we used Intent to experiment with phone calls, text messages, and other functions.

Let's take a look at how to handle the permission to call Android 6. x directly.

Of course, the code is very simple:

package com.zhy.android160217;import android.Manifest;import android.content.Intent;import android.content.pm.PackageManager;import android.net.Uri;import android.os.Bundle;import android.support.v4.app.ActivityCompat;import android.support.v4.content.ContextCompat;import android.support.v7.app.AppCompatActivity;import android.view.View;import android.widget.Toast;public class MainActivity extends AppCompatActivity{    private static final int MY_PERMISSIONS_REQUEST_CALL_PHONE = 1;    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);    }    public void testCall(View view)    {        if (ContextCompat.checkSelfPermission(this,                Manifest.permission.CALL_PHONE)                != PackageManager.PERMISSION_GRANTED)        {            ActivityCompat.requestPermissions(this,                    new String[]{Manifest.permission.CALL_PHONE},                    MY_PERMISSIONS_REQUEST_CALL_PHONE);        } else        {            callPhone();        }    }    public void callPhone()    {        Intent intent = new Intent(Intent.ACTION_CALL);        Uri data = Uri.parse("tel:" + "10086");        intent.setData(data);        startActivity(intent);    }    @Override    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)    {        if (requestCode == MY_PERMISSIONS_REQUEST_CALL_PHONE)        {            if (grantResults[0] == PackageManager.PERMISSION_GRANTED)            {                callPhone();            } else            {                // Permission Denied                Toast.makeText(MainActivity.this, "Permission Denied", Toast.LENGTH_SHORT).show();            }            return;        }        super.onRequestPermissionsResult(requestCode, permissions, grantResults);    }}

Run the command on Android 6. x. Click testCall. The authorization window is displayed. You can call Allow directly. If Denied is set, "Permission Denied" is displayed ".

The example is very simple, but it should be noted that for Intent, in many casesNoYou do not need to grant or even grant permissions. For example, you do not need to apply for permissions when you go to the dial-up interface instead of calling the phone directly. Open the system image library and select a photo; call the system Camera app to license the app. For more information, see Consider Using an Intent
.

Of course, the above example shows that not all Intent methods do not need to apply for permissions. Generally, you open another app through Intent and ask the user to do something through the app. You only pay attention to the result (onActivityResult), so the permission does not need to be processed, it is handled by the opened app.

5. encapsulation (1) Permission Application

Although permission processing is not complex, you need to write a lot of repeated code, so there are also a lot of libraries to encapsulate the usage, you can search on the github homepage:android permission, Compares the usage of several databases, and finds that.

The encapsulated code is very simple. As you can see from the preceding permission Code, there is no particular complexity.

The original code for permission application is:

if (ContextCompat.checkSelfPermission(this,                Manifest.permission.CALL_PHONE)                != PackageManager.PERMISSION_GRANTED){    ActivityCompat.requestPermissions(this,            new String[]{Manifest.permission.CALL_PHONE},            MY_PERMISSIONS_REQUEST_CALL_PHONE);} else{    callPhone();}

As you can see, the logic of the Application for other permissions is similar. The only difference is the parameter. For the above Code, we need three parameters:Activity|Fragment,Permission String Array,Int type application code.

That is to say, we only need to write a method to accept these parameters, and then the logic is unified.

public static void needPermission(Fragment fragment, int requestCode, String[] permissions){    requestPermissions(fragment, requestCode, permissions);}@TargetApi(value = Build.VERSION_CODES.M)private static void requestPermissions(Object object, int requestCode, String[] permissions){    if (!Utils.isOverMarshmallow())    {        doExecuteSuccess(object, requestCode);        return;    }    List
  
    deniedPermissions = Utils.findDeniedPermissions(getActivity(object), permissions);    if (deniedPermissions.size() > 0)    {        if (object instanceof Activity)        {            ((Activity) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);        } else if (object instanceof Fragment)        {            ((Fragment) object).requestPermissions(deniedPermissions.toArray(new String[deniedPermissions.size()]), requestCode);        } else        {            throw new IllegalArgumentException(object.getClass().getName() + " is not supported");        }    } else    {        doExecuteSuccess(object, requestCode);    }}
  

Utils.findDeniedPermissionsCheck is not authorized.

#Utils@TargetApi(value = Build.VERSION_CODES.M)public static List
  
    findDeniedPermissions(Activity activity, String... permission){    List
   
     denyPermissions = new ArrayList<>();    for (String value : permission)    {        if (activity.checkSelfPermission(value) != PackageManager.PERMISSION_GRANTED)        {            denyPermissions.add(value);        }    }    return denyPermissions;}
   
  

The above logic is clear. The three required parameters are passed in. The applied permissions are removed first, and then the permissions are applied.

OK. I believe that the above Code can be understood at a glance.

(2) process permission callback

For callback, many frameworks also need a series of code or are coupled with the previous application code to execute different tasks based on the authorization or not. However, this framework is quite convenient, which is why I chose it to explain it.

Main tasks of callback:

Check whether the authorization is successful. Callback based on authorization.

The logic for the first line is the same; for the second line, because two branches are involved, each branch executes different methods.

For Article 2, many frameworks choose to register the methods of the two branches when applying for permissions, and then perform matching execution based on requestCode In the callback, however, this requires object management for each application.

However, this framework has taken a very interesting approach. It uses annotations to determine the method to be executed. There are two Annotations:

@PermissionSuccess(requestCode = 100)@PermissionFail(requestCode = 100)

You can use reflection to find the annotation method based on the authorization + requestCode, and then execute it directly.

The rough code is:

@Override public void onRequestPermissionsResult(int requestCode, String[] permissions,      int[] grantResults) {    PermissionGen.onRequestPermissionsResult(this, requestCode, permissions, grantResults);}private static void requestResult(Object obj, int requestCode, String[] permissions,                                  int[] grantResults){    List
  
    deniedPermissions = new ArrayList<>();    for (int i = 0; i < grantResults.length; i++)    {        if (grantResults[i] != PackageManager.PERMISSION_GRANTED)        {            deniedPermissions.add(permissions[i]);        }    }    if (deniedPermissions.size() > 0)    {        doExecuteFail(obj, requestCode);    } else    {        doExecuteSuccess(obj, requestCode);    }}
  

First, judge whether the request succeeds or fails Based on grantResults. If the request succeeds:

private static void doExecuteSuccess(Object activity, int requestCode){    Method executeMethod = Utils.findMethodWithRequestCode(activity.getClass(),            PermissionSuccess.class, requestCode);    executeMethod(activity, executeMethod);}#Utilspublic static  Method findMethodWithRequestCode(Class clazz,  Class annotation, int requestCode){    for (Method method : clazz.getDeclaredMethods())    {        if (method.isAnnotationPresent(annotation))        {            if (isEqualRequestCodeFromAnntation(method, annotation, requestCode))            {                return method;            }        }    }    return null;}

Locate the method based on the annotation and requestCode, and then execute the method through reflection. The failure logic is similar, and no code is pasted.

OK. At this point, we have finished introducing the changes, features, handling, and encapsulation of the runtime permissions compared with the previous version.

However, some people may say that runtime reflection will affect the efficiency of the Library mentioned above, but I have changed the runtime Annotation to the Annotation Processor method based on the above Code, that is, annotations during compilation. In this way, there will be no reflection loss efficiency problems. The fork was prepared for modification, and then PR was written. The change was too large. It is estimated that PR could not be passed. So we started another project and made it easier for us to perform some extension and maintenance later.

Vi. MPermissions usage

The external interface is basically the same as PermissionGen, because the application only requires three parameters. Instead of using the singleton method of the original class library, you can directly use a few static methods, which is simple, clean, and violent.

Paste a usage:

public class MainActivity extends AppCompatActivity{    private Button mBtnSdcard;    private static final int REQUECT_CODE_SDCARD = 2;    @Override    protected void onCreate(Bundle savedInstanceState)    {        super.onCreate(savedInstanceState);        setContentView(R.layout.activity_main);        mBtnSdcard = (Button) findViewById(R.id.id_btn_sdcard);        mBtnSdcard.setOnClickListener(new View.OnClickListener()        {            @Override            public void onClick(View v)            {                MPermissions.requestPermissions(MainActivity.this, REQUECT_CODE_SDCARD, Manifest.permission.WRITE_EXTERNAL_STORAGE);            }        });    }    @Override    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults)    {        MPermissions.onRequestPermissionsResult(this, requestCode, permissions, grantResults);        super.onRequestPermissionsResult(requestCode, permissions, grantResults);    }    @PermissionGrant(REQUECT_CODE_SDCARD)    public void requestSdcardSuccess()    {        Toast.makeText(this, "GRANT ACCESS SDCARD!", Toast.LENGTH_SHORT).show();    }    @PermissionDenied(REQUECT_CODE_SDCARD)    public void requestSdcardFailed()    {        Toast.makeText(this, "DENY ACCESS SDCARD!", Toast.LENGTH_SHORT).show();    }}

Is it simple and clear ~~ All the activities of onRequestPermissionsResult are consistent, so they can be placed in BaseActivity. In addition, the methods used in Fragment are the same. For details, see the demo.

See database: https://github.com/hongyangAndroid/MPermissions.

The source code of MPermission is not directly introduced, because it mainly involves Annotation Processor, next, I will discuss the compilation annotation practices and API usage in a single blog post.

Last thanksPermissionGenKu, wish you a happy Lantern Festival ~!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.