Framework基礎:系統源碼理解6.0的運行時權限

Android 6.0 引入了運行時權限。就是權限是在程序運行的時候賦予的,而不是安裝的時候賦予的。6.0以前權限的賦予都是在安裝的時候列出一堆權限,而後用戶點擊肯定後賦予的。下面對比圖,有種你敢看。android

Android L.pngshell

Android M.png數據庫

大致說一下##

今天來講下6.0權限賦予的一個過程吧,先不上代碼,先用天然語言說一下。
1.並非全部權限都要運行時才申請的。有些是安裝的時候天然就給你的,安裝時候自動給你的都是一些小權限啦,沒啥危險的。執行 adb shell pm dump com.example.rubbishdemo |find "permission" 能夠看到有哪些權限是這個com.example.rubbishdemo安裝的時候就被賦予的。這個能夠叫作「安裝時權限」,哈哈,名字是我亂起的。數據結構

Paste_Image.pngide

2.運行時權限的賦予是給整個權限組賦予權限,啥意思啊,是這樣的,Android裏面的權限都是分組的,便於管理,例如寫sd卡,和讀sd卡就是同一個權限組android.permission-group.STORAGE的。這些在frameworks/base/core/res/AndroidManifest.xml中定義this

權限組定義.pngspa

權限組下的兩個權限.pngcode

3.權限賦予後,會記錄到設置數據庫中。於是重啓也不會失效。xml

代碼走一下##

下面就進入代碼時間
首先是應用部分,在onCreate裏面申請發送信息的權限SEND_SMS。ci

protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        this.requestPermissions(
                new String[]{Manifest.permission.SEND_SMS},
                2);
    }

AndroidManifest定義要獲取的權限

<uses-permission android:name="android.permission.SEND_SMS" />

上節咱們說到運行時權限那個彈框是有應用安裝器彈出的,所在的Activity是GrantPermissionsActivity。因此,過一下這個Activity的代碼。

public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
       //mRequestedPermissions是要獲取的權限,這裏要獲取的是android.permission.SEND_SMS
        mRequestedPermissions = getIntent().getStringArrayExtra(
                PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
        if (mRequestedPermissions == null) {
            mRequestedPermissions = new String[0];
        }
     
        final int requestedPermCount = mRequestedPermissions.length;
        mGrantResults = new int[requestedPermCount];  //用來保存權限獲取結果
        //要獲取權限的應用的包名
        PackageInfo callingPackageInfo = getCallingPackageInfo();
        //設備管理器
        DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
        //權限策略
        final int permissionPolicy = devicePolicyManager.getPermissionPolicy(null);
        //獲取應用中全部的權限,這個返回值是AndroidManifest裏面定義的全部權限,本例子就是下面一個,only one
    /*
    <uses-permission android:name="android.permission.SEND_SMS" />
     */
       //把這些權限保存到一個數據結構AppPermissions中去
        mAppPermissions = new AppPermissions(this, callingPackageInfo, null, false,
                new Runnable() {
                    @Override
                    public void run() {
                        setResultAndFinish();
                    }
                });

        for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
           //遍歷應用的權限組groud
            boolean groupHasRequestedPermission = false;
            for (String requestedPermission : mRequestedPermissions) {
                if (group.hasPermission(requestedPermission)) {
                   //權限組已經有權限了,就不用再次賦予權限了
                    groupHasRequestedPermission = true;
                    break;
                }
            }
            if (!groupHasRequestedPermission) {
                continue;
            }
            // We allow the user to choose only non-fixed permissions. A permission
            // is fixed either by device policy or the user denying with prejudice.
            //根據權限策略賦予權限,正常這兩個策略不知足
            if (!group.isUserFixed() && !group.isPolicyFixed()) {
                switch (permissionPolicy) {
                    case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
                        if (!group.areRuntimePermissionsGranted()) {
                            group.grantRuntimePermissions(false);
                        }
                        group.setPolicyFixed();
                    } break;

                    case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: {
                        if (group.areRuntimePermissionsGranted()) {
                            group.revokeRuntimePermissions(false);
                        }
                        group.setPolicyFixed();
                    } break;

                    default: {
                        if (!group.areRuntimePermissionsGranted()) {
                            //把要申請權限的權限組groud放在數據結構mRequestGrantPermissionGroups
                            mRequestGrantPermissionGroups.put(group.getName(),
                                    new GroupState(group));
                        } else {
                            group.grantRuntimePermissions(false);
                            updateGrantResults(group);
                        }
                    } break;
                }
            } else {
                // if the permission is fixed, ensure that we return the right request result
                updateGrantResults(group);
            }
        }
       //彈出彈框
        mViewHandler = new GrantPermissionsDefaultViewHandler(this).setResultListener(this);
        setContentView(mViewHandler.createView());
    }

先看一下上面的註釋,大概步驟是
1.獲取APK AndroidManifest中定義的全部權限,並保存在結構AppPermissions。
本例子只定義了一個權限android.permission.SEND_SMS,因此AppPermissions結構中只包含一個Permission,若是定義了多個Permission,則AppPermissions結構中包含多個Permission。

2.獲取APK的權限組AppPermissionGroup ,本例子只定義了一個權限,因此權限組也只有一個。android.permission.SEND_SMS所在的權限組是android.permission-group.SMS。

3.查看要獲取的權限所在的權限組是否以前已經獲取到權限,若是已經獲取到,再也不獲取。直接結束,不然會彈個框,嚇嚇你。

4.彈框經過GrantPermissionsDefaultViewHandler建立。

彈框嚇嚇你.png

下面進入GrantPermissionsDefaultViewHandler看看

//建立了彈框,只關注贊成按鈕
    public View createView() {
        mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);
        mDenyButton = (Button) mRootView.findViewById(R.id.permission_deny_button);
        return mRootView;
    }
  //看點擊容許是怎麼處理
    public void onClick(View view) {
        switch (view.getId()) {
            case R.id.permission_allow_button:
                if (mResultListener != null) {
                    view.clearAccessibilityFocus();
                    mResultListener.onPermissionGrantResult(mGroupName, true, false);  
                    //這裏的mResultListener就是GrantPermissionsActivity
                }
                break;
        }
    }

    //GrantPermissionsActivity中
    public void onPermissionGrantResult(String name, boolean granted, boolean doNotAskAgain) {
        GroupState groupState = mRequestGrantPermissionGroups.get(name);
        if (groupState.mGroup != null) {
            if (granted) {
                groupState.mGroup.grantRuntimePermissions(doNotAskAgain);  //這裏會往設置數據庫寫一個值,完成權限賦予
                groupState.mState = GroupState.STATE_ALLOWED;
            }
    }

能夠看到,步驟也很簡單
1.彈框建立了容許與拒絕的按鈕,給按鈕設置監聽。
2.按下容許按鍵後,會給權限組賦予權限,最後調用PackageManagerService系統服務將結果寫入設置數據庫。groupState.mGroup就是權限組。

總結##

1.6.0的權限賦予是給整個權限組賦予權限。
2.權限賦予的入口是應用安裝器。
3.有些權限是安裝時賦予的,是沒啥危險的權限。危險的權限經過運行時賦予。至於危險的定義也在frameworks/base/core/res/AndroidManifest.xml中,截個圖你看

危險.png

不危險.png

做者:九九叔 連接:https://www.jianshu.com/p/d31ea81f75a3 來源:簡書 著做權歸做者全部。商業轉載請聯繫做者得到受權,非商業轉載請註明出處。

相關文章
相關標籤/搜索