Android6.0動(dòng)態(tài)運(yùn)行時(shí)權(quán)限源碼分析

一.前言

由于政策和應(yīng)用市場(chǎng)的原因,我們項(xiàng)目需要將targetSdkVersion從19升級(jí)到26,這樣導(dǎo)致一些API,webview高低版本 https&http混合加載,通知欄顯示,敏感權(quán)限動(dòng)態(tài)申請(qǐng),應(yīng)用內(nèi)升級(jí)以及高低版本崩潰等一些問(wèn)題。今天重點(diǎn)來(lái)說(shuō)說(shuō)Android動(dòng)態(tài)運(yùn)行時(shí)權(quán)限,我們知道android從targetSdkVersion23就引入了動(dòng)態(tài)權(quán)限,主要應(yīng)該是處于敏感權(quán)限的安全考慮。本文基于api 28代碼分析。


zw_overview.png

二.權(quán)限組與敏感權(quán)限

普通權(quán)限當(dāng)在AndroidMainfest申請(qǐng)注冊(cè)了,并且權(quán)限組里其中某個(gè)權(quán)限被授權(quán)了,其他同組權(quán)限也授權(quán)了,但是對(duì)于敏感權(quán)限,用戶有可能隨時(shí)關(guān)閉某個(gè)app的敏感權(quán)限,所以應(yīng)用必須需要?jiǎng)討B(tài)去授權(quán)。如下為權(quán)限組與敏感權(quán)限:


zw_permission.png

三.權(quán)限授權(quán)過(guò)程

以存儲(chǔ)權(quán)限動(dòng)態(tài)授權(quán)步驟為例:

if (Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
            //檢查權(quán)限
            if (ContextCompat.checkSelfPermission(context,
                    Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
            //是否需要?jiǎng)討B(tài)授權(quán)
                if (ActivityCompat.shouldShowRequestPermissionRationale(
                        (Activity) context,
                        Manifest.permission.READ_EXTERNAL_STORAGE)) {
             //動(dòng)態(tài)授權(quán)
                    ActivityCompat.requestPermissions((Activity) context,
                            new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                            PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
                } else {
                    ActivityCompat
                            .requestPermissions(
                                    (Activity) context,
                                    new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                                    PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
                }
                return false;
            } else {
                return true;
            }

        } else {
            return true;
        }

動(dòng)態(tài)授權(quán)后,系統(tǒng)會(huì)彈出權(quán)限界面,操作結(jié)果會(huì)回調(diào)給對(duì)應(yīng)的Activity重載onRequestPermisionResult方法:

@Override
    public void onRequestPermissionsResult(int requestCode, String[] permissions, int[] grantResults) {
        switch (requestCode) {
            case AndroidPermissionUtils.PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE:
                if (grantResults[0] == PackageManager.PERMISSION_GRANTED) {
                    // todo 用戶在授權(quán)框同意授權(quán)
                } else {
                    //todo 用戶在授權(quán)框禁止授權(quán)
                }
                break;
            default:
                super.onRequestPermissionsResult(requestCode, permissions,
                        grantResults);
        }
    }

小結(jié):
1.檢查權(quán)限。
2.提示用戶是否需要?jiǎng)討B(tài)授權(quán)該權(quán)限。
3.進(jìn)行動(dòng)態(tài)授權(quán)。
4.處理用戶授權(quán)回調(diào)。

下面結(jié)合上面的授權(quán)步驟來(lái)具體分析下源碼調(diào)用的過(guò)程:

1.檢查權(quán)限ContextCompat.checkSelfPermission源碼調(diào)用過(guò)程分析

首先會(huì)調(diào)用到ContextCompat的checkSelfPermission方法

public static int checkSelfPermission(@NonNull Context context, @NonNull String permission) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        return context.checkPermission(permission, android.os.Process.myPid(), Process.myUid());
    }

看最后一行代碼context.checkPermission,checkPermission其實(shí)是一個(gè)抽象方法,看到context這個(gè)實(shí)例具體實(shí)現(xiàn)肯定實(shí)在ContextImpl#checkPermission方法里處理的。ContextImpl是Context的實(shí)現(xiàn)類,具體實(shí)現(xiàn)如下:

 @Override
    public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            throw new IllegalArgumentException("permission is null");
        }

        final IActivityManager am = ActivityManager.getService();
        if (am == null) {
            // Well this is super awkward; we somehow don't have an active
            // ActivityManager instance. If we're testing a root or system
            // UID, then they totally have whatever permission this is.
            final int appId = UserHandle.getAppId(uid);
            if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
                Slog.w(TAG, "Missing ActivityManager; assuming " + uid + " holds " + permission);
                return PackageManager.PERMISSION_GRANTED;
            }
        }

        try {
            return am.checkPermission(permission, pid, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

1.當(dāng)permission為空就拋出狀態(tài)異常。反之進(jìn)入第二步。
2.獲取IActivityManager am實(shí)例,IActivityManager是一個(gè)Binder。當(dāng)前應(yīng)用appId是rootUid或者是systemUid,也就是是用root權(quán)限或者是系統(tǒng)應(yīng)用。則直接返回已授權(quán)。
3.am不為空則通過(guò)Binder跨進(jìn)程調(diào)到AMS中,進(jìn)入AMS#checkPermission方法中。
com.android.server.am.ActivityManagerService#checkPermission

public int checkPermission(String permission, int pid, int uid) {
        if (permission == null) {
            return PackageManager.PERMISSION_DENIED;
        }
        return checkComponentPermission(permission, pid, uid, -1, true);
    }

判斷permission是否為空,為空就直接拒絕授權(quán),反之調(diào)用checkComponentPermission進(jìn)行檢查權(quán)限。

int checkComponentPermission(String permission, int pid, int uid,
            int owningUid, boolean exported) {
        if (pid == MY_PID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        return ActivityManager.checkComponentPermission(permission, uid,
                owningUid, exported);
    }

我們看到pid == MY_PID,意思是說(shuō)當(dāng)我們的appid如果和AMS是一個(gè)進(jìn)程的話,就直接返回允許授權(quán)。否則進(jìn)入ActivityManager中checkComponentPermission方法繼續(xù)檢查是否授權(quán)。

public static int checkComponentPermission(String permission, int uid,
            int owningUid, boolean exported) {
        // Root, system server get to do everything.
        final int appId = UserHandle.getAppId(uid);
        if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // Isolated processes don't get any permissions.
        if (UserHandle.isIsolated(uid)) {
            return PackageManager.PERMISSION_DENIED;
        }
        // If there is a uid that owns whatever is being accessed, it has
        // blanket access to it regardless of the permissions it requires.
        if (owningUid >= 0 && UserHandle.isSameApp(uid, owningUid)) {
            return PackageManager.PERMISSION_GRANTED;
        }
        // If the target is not exported, then nobody else can get to it.
        if (!exported) {
            /*
            RuntimeException here = new RuntimeException("here");
            here.fillInStackTrace();
            Slog.w(TAG, "Permission denied: checkComponentPermission() owningUid=" + owningUid,
                    here);
            */
            return PackageManager.PERMISSION_DENIED;
        }
        if (permission == null) {
            return PackageManager.PERMISSION_GRANTED;
        }
        try {
            return AppGlobals.getPackageManager()
                    .checkUidPermission(permission, uid);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

1.通過(guò)uid獲取到appid,判斷如果是root和系統(tǒng)應(yīng)用直接返回已授權(quán)。
2.通過(guò)UserHandle.isIsolated(uid)判斷是否是隔離進(jìn)程,那什么是隔離進(jìn)程呢?所謂隔離進(jìn)程在android系統(tǒng)里有進(jìn)程范圍的,就是被拉入黑名單中的應(yīng)用。那就直接返回沒(méi)有授權(quán)。
3.UserHandle.isSameApp(uid, owningUid),這個(gè)是什么意思呢?看方法名稱我們應(yīng)該可以猜到,通過(guò)判斷當(dāng)前id和擁有的id是否相等,說(shuō)明是同一個(gè)app。意思是說(shuō),當(dāng)前權(quán)限是我們自己app在AndroidMainfest通過(guò)permission標(biāo)簽申請(qǐng)的權(quán)限當(dāng)前進(jìn)程可以用就直接返回已授權(quán)。
4.當(dāng)exported為false的時(shí)候直接返回未授權(quán)。exported是在AndroidMainfest注冊(cè)四大組件的時(shí)候用到的標(biāo)簽。
接著調(diào)用AppGlobals.getPackageManager().checkUidPermission(permission, uid)方法:

public int checkUidPermission(String permName, int uid) {
        final int callingUid = Binder.getCallingUid();
        final int callingUserId = UserHandle.getUserId(callingUid);
        final boolean isCallerInstantApp = getInstantAppPackageName(callingUid) != null;
        final boolean isUidInstantApp = getInstantAppPackageName(uid) != null;
        final int userId = UserHandle.getUserId(uid);
        if (!sUserManager.exists(userId)) {
            return PackageManager.PERMISSION_DENIED;
        }

        synchronized (mPackages) {
            Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid));
            if (obj != null) {
                if (obj instanceof SharedUserSetting) {
                    if (isCallerInstantApp) {
                        return PackageManager.PERMISSION_DENIED;
                    }
                } else if (obj instanceof PackageSetting) {
                    final PackageSetting ps = (PackageSetting) obj;
                    if (filterAppAccessLPr(ps, callingUid, callingUserId)) {
                        return PackageManager.PERMISSION_DENIED;
                    }
                }
                final SettingBase settingBase = (SettingBase) obj;
                final PermissionsState permissionsState = settingBase.getPermissionsState();
                if (permissionsState.hasPermission(permName, userId)) {
                    if (isUidInstantApp) {
                        BasePermission bp = mSettings.mPermissions.get(permName);
                        if (bp != null && bp.isInstant()) {
                            return PackageManager.PERMISSION_GRANTED;
                        }
                    } else {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                }
                // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION
                if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState
                        .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) {
                    return PackageManager.PERMISSION_GRANTED;
                }
            } else {
                ArraySet<String> perms = mSystemPermissions.get(uid);
                if (perms != null) {
                    if (perms.contains(permName)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                    if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && perms
                            .contains(Manifest.permission.ACCESS_FINE_LOCATION)) {
                        return PackageManager.PERMISSION_GRANTED;
                    }
                }
            }
        }

        return PackageManager.PERMISSION_DENIED;
    }

AppGlobals.getPackageManager()獲取的是一個(gè)IPackageManager Binder實(shí)例。最終調(diào)到PMS PackageManagerService#checkUidPermission方法中。

checkSelfPermission調(diào)用流程:
zw_checkUidPermission.png

2.是否需要?jiǎng)討B(tài)授權(quán)提示ActivityCompat.shouldShowRequestPermissionRationale源碼調(diào)用過(guò)程分析

是否需要展示給用戶確認(rèn)授權(quán)提示,我們從上面存儲(chǔ)動(dòng)態(tài)授權(quán)知道,每次動(dòng)態(tài)授權(quán)會(huì)彈出一個(gè)系統(tǒng)的GrantPermissionsActivity提示需要授權(quán),這將會(huì)在動(dòng)態(tài)授權(quán)的步驟中會(huì)提到。

public static boolean shouldShowRequestPermissionRationale(@NonNull Activity activity,
            @NonNull String permission) {
        if (Build.VERSION.SDK_INT >= 23) {
            return activity.shouldShowRequestPermissionRationale(permission);
        }
        return false;
    }

Build.VERSION.SDK_INT >= 23我們看出當(dāng)api大于等于23就會(huì)有確認(rèn)授權(quán)提示,就是說(shuō)android6.0才會(huì)又動(dòng)態(tài)授權(quán)提示界面。android6.0以下是沒(méi)有動(dòng)態(tài)授權(quán)的概念的。接著會(huì)流轉(zhuǎn)到Activity#shouldShowRequestPermissionRationale方法,然后再流轉(zhuǎn)到PackageManager#shouldShowRequestPermissionRationale方法,PackageManager類中shouldShowRequestPermissionRationale是一個(gè)抽象方法,具體實(shí)現(xiàn)是在 ApplicationPackageManager#shouldShowRequestPermissionRationale方法中:

public boolean shouldShowRequestPermissionRationale(String permission) {
        try {
            return mPM.shouldShowRequestPermissionRationale(permission,
                    mContext.getPackageName(), mContext.getUserId());
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
        }
    }

mPM這個(gè)實(shí)例是Binder類型的實(shí)例,這里跨進(jìn)程最終流轉(zhuǎn)到PackageManagerService#shouldShowRequestPermissionRationale方法中。

public boolean shouldShowRequestPermissionRationale(String permissionName,
            String packageName, int userId) {
        if (UserHandle.getCallingUserId() != userId) {
            mContext.enforceCallingPermission(
                    android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
                    "canShowRequestPermissionRationale for user " + userId);
        }

        final int uid = getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId);
        if (UserHandle.getAppId(getCallingUid()) != UserHandle.getAppId(uid)) {
            return false;
        }

        if (checkPermission(permissionName, packageName, userId)
                == PackageManager.PERMISSION_GRANTED) {
            return false;
        }

        final int flags;

        final long identity = Binder.clearCallingIdentity();
        try {
            flags = getPermissionFlags(permissionName,
                    packageName, userId);
        } finally {
            Binder.restoreCallingIdentity(identity);
        }

        final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
                | PackageManager.FLAG_PERMISSION_POLICY_FIXED
                | PackageManager.FLAG_PERMISSION_USER_FIXED;

        if ((flags & fixedFlags) != 0) {
            return false;
        }

        return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
    }

1.當(dāng)Binder IPC跨進(jìn)程調(diào)用方的UID不等于現(xiàn)在的UID就直接返回false表示不需要彈出授權(quán)提示,反之需要。
2.checkPermission 通過(guò)權(quán)限名稱,包名,uid去檢查當(dāng)前是否又權(quán)限,如果有權(quán)限則不需要彈出授權(quán)提示,反之需要。
3.當(dāng)flags為系統(tǒng)固定設(shè)置,權(quán)限策略固定,用戶主動(dòng)設(shè)置不允許修改的則不需要彈出授權(quán)提示,反之需要。
是否展示授權(quán)提示流程:


zw_shouldShowRequestPermissionRationale.png

3.動(dòng)態(tài)請(qǐng)求權(quán)限ActivityCompat.requestPermissions源碼調(diào)用過(guò)程分析

我們通過(guò)上面第二步ActivityCompat.shouldShowRequestPermissionRationale方法知道是否需要進(jìn)行動(dòng)態(tài)授權(quán),通過(guò)ActivityCompat#requestPermissions方法可以實(shí)現(xiàn)動(dòng)態(tài)授權(quán):

 public static void requestPermissions(final @NonNull Activity activity,
            final @NonNull String[] permissions, final @IntRange(from = 0) int requestCode) {
        if (sDelegate != null
                && sDelegate.requestPermissions(activity, permissions, requestCode)) {
            // Delegate has handled the permission request.
            return;
        }

        if (Build.VERSION.SDK_INT >= 23) {
            if (activity instanceof RequestPermissionsRequestCodeValidator) {
                ((RequestPermissionsRequestCodeValidator) activity)
                        .validateRequestPermissionsRequestCode(requestCode);
            }
            activity.requestPermissions(permissions, requestCode);
        } else if (activity instanceof OnRequestPermissionsResultCallback) {
            Handler handler = new Handler(Looper.getMainLooper());
            handler.post(new Runnable() {
                @Override
                public void run() {
                    final int[] grantResults = new int[permissions.length];

                    PackageManager packageManager = activity.getPackageManager();
                    String packageName = activity.getPackageName();

                    final int permissionCount = permissions.length;
                    for (int i = 0; i < permissionCount; i++) {
                        grantResults[i] = packageManager.checkPermission(
                                permissions[i], packageName);
                    }

                    ((OnRequestPermissionsResultCallback) activity).onRequestPermissionsResult(
                            requestCode, permissions, grantResults);
                }
            });
        }
    }

通過(guò)上面我們可以看出api 6.0以下系統(tǒng)授權(quán)過(guò)程,通過(guò)PackageManager去檢查在Androidmanifest注冊(cè)的權(quán)限,只要有就默認(rèn)grant并放入grantResults數(shù)組里,然后通過(guò)onRequestPermissionsResult回調(diào)到Activity中。所以只要在manifest有注冊(cè)。接下來(lái)我們重點(diǎn)分析api 6.0以上的動(dòng)態(tài)授權(quán)過(guò)程
ActivityCompat#requestPermissions中流轉(zhuǎn)到Activity#requestPermissions方法:

public final void requestPermissions(@NonNull String[] permissions, int requestCode) {
        if (requestCode < 0) {
            throw new IllegalArgumentException("requestCode should be >= 0");
        }
        if (mHasCurrentPermissionsRequest) {
            Log.w(TAG, "Can reqeust only one set of permissions at a time");
            // Dispatch the callback with empty arrays which means a cancellation.
            onRequestPermissionsResult(requestCode, new String[0], new int[0]);
            return;
        }
        Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
        startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
        mHasCurrentPermissionsRequest = true;
    }

PackageManager#buildRequestPermissionsIntent方法:

public Intent buildRequestPermissionsIntent(@NonNull String[] permissions) {
        if (ArrayUtils.isEmpty(permissions)) {
           throw new IllegalArgumentException("permission cannot be null or empty");
        }
        Intent intent = new Intent(ACTION_REQUEST_PERMISSIONS);
        intent.putExtra(EXTRA_REQUEST_PERMISSIONS_NAMES, permissions);
        intent.setPackage(getPermissionControllerPackageName());
        return intent;
    }

當(dāng)mHasCurrentPermissionsRequest為true的時(shí)候我們就直接回調(diào)到對(duì)應(yīng)的Activity的重載方法onRequestPermissionsResult中。mHasCurrentPermissionsRequest為true表示我們已經(jīng)發(fā)起過(guò)動(dòng)態(tài)授權(quán),我們看到最后一行代碼mHasCurrentPermissionsRequest 設(shè)置為 true。否則就通過(guò)buildRequestPermissionsIntent構(gòu)建一個(gè)Intent啟動(dòng)一個(gè)名叫GrantPermissionsActivity的Activity。沒(méi)錯(cuò)這個(gè)GrantPermissionsActivity就是我們經(jīng)??吹降南到y(tǒng)授權(quán)彈窗。
GrantPermissionsActivity源碼(https://android.googlesource.com/platform/packages/apps/PackageInstaller/+/refs/tags/android-vts-8.0_r12/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
):
包路徑:packages/apps/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
我們從上面PackageManager#buildRequestPermissionsIntent方法看出Intent帶EXTRA_REQUEST_PERMISSIONS_NAMES為key,value為permissions權(quán)限數(shù)組傳給GrantPermissionsActivity彈窗界面。下面重點(diǎn)介紹下授權(quán)界面交互的幾部分:

1.GrantPermissionsActivity通過(guò)Intent拿到權(quán)限數(shù)組。
mRequestedPermissions = getIntent().getStringArrayExtra(
                PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
        if (mRequestedPermissions == null) {
            mRequestedPermissions = new String[0];
        }
。。。。
for (String requestedPermission : mRequestedPermissions)
 AppPermissionGroup group = null;
            for (AppPermissionGroup nextGroup : mAppPermissions.getPermissionGroups()) {
                if (nextGroup.hasPermission(requestedPermission)) {
                    group = nextGroup;
                    break;
                }
            }
            if (group == null) {
                continue;
            }
。。。

進(jìn)行一波權(quán)限判斷,接著往下看。

2. GrantPermissionsViewHandler
@Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
       。。。
        if (DeviceUtils.isTelevision(this)) {
            mViewHandler = new com.android.packageinstaller.permission.ui.television
                    .GrantPermissionsViewHandlerImpl(this,
                    getCallingPackage()).setResultListener(this);
        } else if (DeviceUtils.isWear(this)) {
            mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
        } else if (DeviceUtils.isAuto(this)) {
            mViewHandler = new GrantPermissionsAutoViewHandler(this, getCallingPackage())
                    .setResultListener(this);
        } else {
            mViewHandler = new com.android.packageinstaller.permission.ui.handheld
                    .GrantPermissionsViewHandlerImpl(this, getCallingPackage())
                    .setResultListener(this);
        }
。。。
setContentView(mViewHandler.createView());

GrantPermissionsViewHandler#mViewHandler是一個(gè)接口,我們通過(guò)上面mViewHandler.createView()已經(jīng)設(shè)置result回調(diào)應(yīng)該可以猜到是處理UI相關(guān)的。

3. onPermissionGrantResult: GrantPermissionsViewHandler#ResultListener

GrantPermissionsViewHandler#ResultListener是一個(gè)接口,回調(diào)如下方法:

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.affectedPermissions);
                groupState.mState = GroupState.STATE_ALLOWED;
            } else {
                groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
                        groupState.affectedPermissions);
                groupState.mState = GroupState.STATE_DENIED;
                int numRequestedPermissions = mRequestedPermissions.length;
                for (int i = 0; i < numRequestedPermissions; i++) {
                    String permission = mRequestedPermissions[i];
                    if (groupState.mGroup.hasPermission(permission)) {
                        EventLogger.logPermissionDenied(this, permission,
                                mAppPermissions.getPackageInfo().packageName);
                    }
                }
            }
            updateGrantResults(groupState.mGroup);
        }
        if (!showNextPermissionGroupGrantRequest()) {
            setResultAndFinish();
        }
    }

通過(guò)上面處理是否允許授權(quán)邏輯。通過(guò)GroupState最終跨進(jìn)程調(diào)用到PMS中遠(yuǎn)程更新權(quán)限。在PMS中做一些檢查,授權(quán),持久化等相關(guān)的事情,具體可以自己去看看源碼。
這里提下持久化,持久化會(huì)將permissions存入到xml文件中,權(quán)限會(huì)按包名持久化相應(yīng)的permissions,xml文件位置:data/system/0/runtime-permissions.xml。
以上是動(dòng)態(tài)授權(quán)私密權(quán)限,普通權(quán)限會(huì)和api23以下一樣存到data/system/packages.xml中,api23以下所有的權(quán)限都存放在packages.xml文件中。應(yīng)用一起來(lái)會(huì)從這里面加載權(quán)限。
小結(jié):
GrantPermissionsActivity中主要是用戶獲取哪些權(quán)限以及處理授權(quán)界面監(jiān)聽事件,最終會(huì)流轉(zhuǎn)回PMS中進(jìn)行授權(quán)。最終授權(quán)在PMS中完成。
授權(quán)流程:


zw_requestPermissions.png

4.處理權(quán)限onRequestPermissionsResult回調(diào)過(guò)程

api23以上會(huì)通過(guò)GrantPermissionsActivity界面中通過(guò)用戶操作跨進(jìn)程至PMS回調(diào)具體onRequestPermissionsResult方法。api23以下通過(guò)PMS檢查權(quán)限是否在manifest文件中注冊(cè),然后回調(diào)onRequestPermissionsResult方法到對(duì)應(yīng)的Activity中。這里就不贅述了,具體可以看源碼。

四.總結(jié)

以上為運(yùn)行時(shí)授權(quán)的過(guò)程:
1.檢查是否有需要權(quán)限。
2.判斷是否需要?jiǎng)討B(tài)授權(quán)提示。
3.進(jìn)行動(dòng)態(tài)授權(quán)。
4.處理用戶授權(quán)回調(diào)。

建議

通過(guò)這次權(quán)限頻繁出現(xiàn)問(wèn)題,在google官網(wǎng)發(fā)現(xiàn)其實(shí)每個(gè)版本跟新都會(huì)有相應(yīng)的changelist。我們平時(shí)可以關(guān)注Android新版本更新哪些功能,做了哪些限制,優(yōu)化了哪些功能都可以在這官方文檔上可以查看到。
Android 8.0 changes
google官方每個(gè)版本更新說(shuō)明

Other

平時(shí)看查看源碼由于Android的Hide標(biāo)注使得看源碼非常不方便跳轉(zhuǎn),順便分享平時(shí)我是怎樣看源碼的,其實(shí)就是去掉源碼Hide標(biāo)注,建議下載去掉hide方法的android.jar包,方便在IDE中跳轉(zhuǎn)。
android-hidden-api

That's All

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容