伙伴们应该都了解,在Android6.0之后,关于部分权限,例如Camera、读写存储权限等,都需求用户授权才能够运用,除非你的使用为体系使用,不然这些“风险权限”将不会主动颁发,那么为什么Google在Android 6.0之后会推出动态权限请求,首要是防止一些“流氓”软件在后台获取用户隐私,从而将职责从技术侧转移到用户侧,既然用户挑选了答应这些权限运用,那么职责就由用户承担了。

那么咱们在动态请求权限时,体系是怎么处理并保存这些状态,接下来咱们深入源码一看终究。

1 权限请求

假如咱们的app需求请求读写权限,那么就能够在清单文件中进行权限的声明

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

当然咱们仅仅声明并不起作用,咱们需求在页面启动时进行权限的动态声明,咱们常用的做法便是:

class MainActivity : AppCompatActivity() {
    private val REQUEST_WRITE_STORAGE_PERMISSION = 0
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
        //首先判别权限是否请求过,或许说是否现已有这个权限了
        if (ContextCompat.checkSelfPermission(
                this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE
            ) != PackageManager.PERMISSION_GRANTED
        ) {
            //假如现已拿到过了,就不需求请求了
            //没拿到过,需求动态请求
            if (ActivityCompat.shouldShowRequestPermissionRationale(
                    this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE
                )
            ) {
                Log.e("TAG", "之前回绝过权限,现在从头再次请求")
                ActivityCompat.requestPermissions(
                    this,
                    arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                    REQUEST_WRITE_STORAGE_PERMISSION
                )
                return
            }
            Log.e("TAG", "之前没有回绝过权限,现在第一次请求")
            ActivityCompat.requestPermissions(
                this,
                arrayOf(Manifest.permission.WRITE_EXTERNAL_STORAGE),
                REQUEST_WRITE_STORAGE_PERMISSION
            )
        }
    }
    override fun onRequestPermissionsResult(
        requestCode: Int,
        permissions: Array<out String>,
        grantResults: IntArray
    ) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults)
        when (requestCode) {
            REQUEST_WRITE_STORAGE_PERMISSION -> {
                Log.e("TAG", "onRequestPermissionsResult 获取到了权限")
                permissions.forEach {
                    Log.e("TAG", "permissions $it")
                }
                grantResults.forEach {
                    Log.e("TAG", "grantResults $it")
                }
            }
        }
    }
}

这是官方的写法,在请求权限之前,首先会判别 是否获取过这个权限,假如没有获取过,那么就会进行动态权限的请求。当然用户可能之前回绝过权限,所以这儿也会进行一次用户是否回绝过请求权限的操作,能够做对应的一些交互上的处理,以及权限请求的目的让用户打消顾忌。下面便是用户可能存在的居中操作,做了对应日志的打印:

用户第一次请求权限时回绝了

2023-06-17 13:43:22.111 30040-30040/com.lay.nowinandroid E/TAG: 之前没有回绝过权限,现在第一次请求
2023-06-17 13:43:31.824 30040-30040/com.lay.nowinandroid E/TAG: onRequestPermissionsResult 获取到了权限
2023-06-17 13:43:31.824 30040-30040/com.lay.nowinandroid E/TAG: permissions android.permission.WRITE_EXTERNAL_STORAGE
2023-06-17 13:43:31.824 30040-30040/com.lay.nowinandroid E/TAG: grantResults -1

用户第二次回绝了权限

经过日志发现,这时现已走到了shouldShowRequestPermissionRationale代码块中,体系是知道用户之前回绝过了权限。

2023-06-17 13:44:32.813 30868-30868/com.lay.nowinandroid E/TAG: 之前回绝过权限,现在从头再次请求
2023-06-17 13:44:49.968 30868-30868/com.lay.nowinandroid E/TAG: onRequestPermissionsResult 获取到了权限
2023-06-17 13:44:49.969 30868-30868/com.lay.nowinandroid E/TAG: permissions android.permission.WRITE_EXTERNAL_STORAGE
2023-06-17 13:44:49.969 30868-30868/com.lay.nowinandroid E/TAG: grantResults -1

用户接受了权限

2023-06-17 13:45:14.135 30868-30868/com.lay.nowinandroid E/TAG: onRequestPermissionsResult 获取到了权限
2023-06-17 13:45:14.135 30868-30868/com.lay.nowinandroid E/TAG: permissions android.permission.WRITE_EXTERNAL_STORAGE
2023-06-17 13:45:14.135 30868-30868/com.lay.nowinandroid E/TAG: grantResults 0

咱们看到,当咱们请求权限时,其实在onRequestPermissionsResult回调中,无论是赞同仍是回绝都会有对应的输出,假如没有赞同权限,那么此时grantResults的值就为-1,假如接受了权限,那么grantResults的值就为0,能够看下面对应的介绍。

/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has been granted to the given package.
 */
public static final int PERMISSION_GRANTED = 0;
/**
 * Permission check result: this is returned by {@link #checkPermission}
 * if the permission has not been granted to the given package.
 */
public static final int PERMISSION_DENIED = -1;

咱们看到这儿其实成果为一个数组,由于咱们在请求权限时,能够以组为单位,与permissions是一一对应的,咱们能够知道,究竟哪些权限咱们拿到了,哪些没有拿到。

2 requestPermissions源码剖析

前面在介绍权限请求时,咱们调用的是ActivityCompat的requestPermissions办法,接下来咱们跟随源码,看体系是怎么完结权限请求的。

ActivityCompat # requestPermissions

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;
   }
   for (String permission : permissions) {
       if (TextUtils.isEmpty(permission)) {
           throw new IllegalArgumentException("Permission request for permissions "
                   + Arrays.toString(permissions) + " must not contain null or empty values");
       }
   }
   if (Build.VERSION.SDK_INT >= 23) {
       if (activity instanceof RequestPermissionsRequestCodeValidator) {
           ((RequestPermissionsRequestCodeValidator) activity)
                   .validateRequestPermissionsRequestCode(requestCode);
       }
       Api23Impl.requestPermissions(activity, 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);
           }
       });
   }
}

首先在requestPermissions办法中,判别了SDK的版别,假如大于等于23,也便是包含Android 6.0以上的版别,会履行对应的逻辑,在Android 6.0版别以下,则是直接经过PKMS来查看权限,并做onRequestPermissionsResult的回调,咱们重视的要点不在这儿,咱们要点看Android 6.0以上的版别。

Api23Impl # requestPermissions

@DoNotInline
static void requestPermissions(Activity activity, String[] permissions, int requestCode) {
    activity.requestPermissions(permissions, requestCode);
}

经过源码咱们看到是调用了Api23Impl的requestPermissions办法,在这个办法内部,直接调用了Activity的requestPermissions办法。

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 request 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;
    }
    if (!getAttributionSource().getRenouncedPermissions().isEmpty()) {
        final int permissionCount = permissions.length;
        for (int i = 0; i < permissionCount; i++) {
            if (getAttributionSource().getRenouncedPermissions().contains(permissions[i])) {
                throw new IllegalArgumentException("Cannot request renounced permission: "
                        + permissions[i]);
            }
        }
    }
    final Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions);
    startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null);
    mHasCurrentPermissionsRequest = true;
}

在这个办法中,前面首要做了一些判别,包含requestCode的校验、并发的处理(同一时间只能进行一次权限请求)等,最终经过PackManager创立一个Intent目标,由于需求回调状态到onRequestPermissionsResult,所以经过startActivityForResult方式启动了一个Activity。

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;
}

这个办法十分简单,便是创立了一个Intent目标,可是这个Intent目标的详细装备咱们需求看一下。

public static final String ACTION_REQUEST_PERMISSIONS =
        "android.content.pm.action.REQUEST_PERMISSIONS";
public static final String EXTRA_REQUEST_PERMISSIONS_NAMES =
        "android.content.pm.extra.REQUEST_PERMISSIONS_NAMES";

在创立一个Intent目标之后,将ACTION_REQUEST_PERMISSIONS作为参数传递到Intent构造函数中,意味着这儿是创立了一个隐式目的,这儿会启动一个体系的页面

由于通常咱们在启动一个Activity的时分,通常是一个显现目的,经过setClass将目的地声明在此,而buildRequestPermissionsIntent中则是创立了一个隐式目的,这儿的Activity便是咱们看到的那个弹窗页面。那么这些Activity是存在哪里呢,其实便是隐式装置器PackageInstaller提供的。

PackageInstaller

这儿我简单提一下,PackageInstaller其实也是一个体系使用,看姓名应该知道它的作用是用来装置或许卸载使用程序的,除此之外,还能够管理使用程序的权限,在装置使用程序时,它会向用户显现使用程序要求的权限,并答应用户对这些权限进行管理和操控。

所以咱们在装置或许卸载使用时,体系出现的弹窗,都是在PackageInstaller app中提供的,前面咱们提到的android.content.pm.action.REQUEST_PERMISSIONS这个action,在PackageInstaller中就对应一个页面,咱们能够去看下PackageInstaller的清单文件。

<activity android:name="com.android.packageinstaller.permission.ui.GrantPermissionsActivity"
        android:configChanges="keyboardHidden|screenSize"
        android:excludeFromRecents="true"
        android:theme="@style/GrantPermissions"
        android:visibleToInstantApps="true"
        android:inheritShowWhenLocked="true">
    <intent-filter android:priority="1">
        <action android:name="android.content.pm.action.REQUEST_PERMISSIONS" />
        <category android:name="android.intent.category.DEFAULT" />
    </intent-filter>
</activity>

所以当咱们请求权限时弹出的体系弹窗,便是GrantPermissionsActivity;除此之外,在构建隐式目的时,还把需求请求的权限组作为参数传进去了,所以在GrantPermissionsActivity中,会接收这些数据。

GrantPermissionsActivity

接下来咱们剖析GrantPermissionsActivity中处理逻辑,这儿我把体系源码中GrantPermissionsActivity拷贝了一份,方便剖析权限请求的流程。

public class GrantPermissionsActivity extends Activity
        implements GrantPermissionsViewHandler.ResultListener {
    private static final String LOG_TAG = "GrantPermissionsActivity";
    private static final String KEY_REQUEST_ID = GrantPermissionsActivity.class.getName()
            + "_REQUEST_ID";
    public static int NUM_BUTTONS = 5;
    public static int LABEL_ALLOW_BUTTON = 0;
    public static int LABEL_ALLOW_ALWAYS_BUTTON = 1;
    public static int LABEL_ALLOW_FOREGROUND_BUTTON = 2;
    public static int LABEL_DENY_BUTTON = 3;
    public static int LABEL_DENY_AND_DONT_ASK_AGAIN_BUTTON = 4;
    /** Unique Id of a request */
    private long mRequestId;
    private String[] mRequestedPermissions;
    private CharSequence[] mButtonLabels;
    private ArrayMap<Pair<String, Boolean>, GroupState> mRequestGrantPermissionGroups =
            new ArrayMap<>();
    private GrantPermissionsViewHandler mViewHandler;
    private AppPermissions mAppPermissions;
    boolean mResultSet;
    /**
     * Listens for changes to the permission of the app the permissions are currently getting
     * granted to. {@code null} when unregistered.
     */
    private @Nullable PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
    /**
     * Listens for changes to the app the permissions are currently getting granted to. {@code null}
     * when unregistered.
     */
    private @Nullable PackageRemovalMonitor mPackageRemovalMonitor;
    // .....
    /**
     * Report the result of a grant of a permission.
     *
     * @param permission The permission that was granted or denied
     * @param result The permission grant result
     */
    private void reportRequestResult(@NonNull String permission, int result) {
        boolean isImplicit = !ArrayUtils.contains(mRequestedPermissions, permission);
        Log.v(LOG_TAG,
                "Permission grant result requestId=" + mRequestId + " callingUid=" + mCallingUid
                        + " callingPackage=" + mCallingPackage + " permission=" + permission
                        + " isImplicit=" + isImplicit + " result=" + result);
        PermissionControllerStatsLog.write(
                PermissionControllerStatsLog.PERMISSION_GRANT_REQUEST_RESULT_REPORTED, mRequestId,
                mCallingUid, mCallingPackage, permission, isImplicit, result);
    }
    /**
     * Report the result of a grant of a permission.
     *
     * @param permissions The permissions that were granted or denied
     * @param result The permission grant result
     */
    private void reportRequestResult(@NonNull String[] permissions, int result) {
        for (String permission : permissions) {
            reportRequestResult(permission, result);
        }
    }
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        // ......
        mRequestedPermissions = getIntent().getStringArrayExtra(
                PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
        if (mRequestedPermissions == null) {
            mRequestedPermissions = new String[0];
        }
        //......
        //这儿会经过判别硬件设备类型,展现不同的UI
        if (DeviceUtils.isTelevision(this)) {
            mViewHandler = new com.android.packageinstaller.permission.ui.television
                    .GrantPermissionsViewHandlerImpl(this,
                    mCallingPackage).setResultListener(this);
        } else if (DeviceUtils.isWear(this)) {
            mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
        } else if (DeviceUtils.isAuto(this)) {
            mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage, userHandle)
                    .setResultListener(this);
        } else {
            mViewHandler = new com.android.packageinstaller.permission.ui.handheld
                    .GrantPermissionsViewHandlerImpl(this, mCallingPackage, userHandle)
                    .setResultListener(this);
        }
        // ......
        setContentView(mViewHandler.createView());
        // ......
    }
    //......
    @Override
    public void onPermissionGrantResult(String name,
            @GrantPermissionsViewHandler.Result int result) {
        logGrantPermissionActivityButtons(name, result);
        GroupState foregroundGroupState = getForegroundGroupState(name);
        GroupState backgroundGroupState = getBackgroundGroupState(name);
        if (result == GRANTED_ALWAYS || result == GRANTED_FOREGROUND_ONLY
                || result == DENIED_DO_NOT_ASK_AGAIN) {
            KeyguardManager kgm = getSystemService(KeyguardManager.class);
            if (kgm.isDeviceLocked()) {
                kgm.requestDismissKeyguard(this, new KeyguardManager.KeyguardDismissCallback() {
                            @Override
                            public void onDismissError() {
                                Log.e(LOG_TAG, "Cannot dismiss keyguard perm=" + name + " result="
                                        + result);
                            }
                            @Override
                            public void onDismissCancelled() {
                                // do nothing (i.e. stay at the current permission group)
                            }
                            @Override
                            public void onDismissSucceeded() {
                                // Now the keyguard is dismissed, hence the device is not locked
                                // anymore
                                onPermissionGrantResult(name, result);
                            }
                        });
                return;
            }
        }
        switch (result) {
            case GRANTED_ALWAYS :
                if (foregroundGroupState != null) {
                    onPermissionGrantResultSingleState(foregroundGroupState, true, false);
                }
                if (backgroundGroupState != null) {
                    onPermissionGrantResultSingleState(backgroundGroupState, true, false);
                }
                break;
            case GRANTED_FOREGROUND_ONLY :
                if (foregroundGroupState != null) {
                    onPermissionGrantResultSingleState(foregroundGroupState, true, false);
                }
                if (backgroundGroupState != null) {
                    onPermissionGrantResultSingleState(backgroundGroupState, false, false);
                }
                break;
            case DENIED :
                if (foregroundGroupState != null) {
                    onPermissionGrantResultSingleState(foregroundGroupState, false, false);
                }
                if (backgroundGroupState != null) {
                    onPermissionGrantResultSingleState(backgroundGroupState, false, false);
                }
                break;
            case DENIED_DO_NOT_ASK_AGAIN :
                if (foregroundGroupState != null) {
                    onPermissionGrantResultSingleState(foregroundGroupState, false, true);
                }
                if (backgroundGroupState != null) {
                    onPermissionGrantResultSingleState(backgroundGroupState, false, true);
                }
                break;
        }
        if (!showNextPermissionGroupGrantRequest()) {
            setResultAndFinish();
        }
    }
    /**
     * Grants or revoked the affected permissions for a single {@link groupState}.
     *
     * @param groupState The group state with the permissions to grant/revoke
     * @param granted {@code true} if the permissions should be granted, {@code false} if they
     *        should be revoked
     * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask
     *        again for the same permissions?
     */
    private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted,
            boolean doNotAskAgain) {
        if (groupState != null && groupState.mGroup != null
                && groupState.mState == GroupState.STATE_UNKNOWN) {
            if (granted) {
                groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
                        groupState.affectedPermissions);
                groupState.mState = GroupState.STATE_ALLOWED;
                reportRequestResult(groupState.affectedPermissions,
                        PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_GRANTED);
            } else {
                groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
                        groupState.affectedPermissions);
                groupState.mState = GroupState.STATE_DENIED;
                reportRequestResult(groupState.affectedPermissions, doNotAskAgain
                        ?
                        PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED_WITH_PREJUDICE
                        : PERMISSION_GRANT_REQUEST_RESULT_REPORTED__RESULT__USER_DENIED);
            }
        }
    }
    // ......
    private void setResultIfNeeded(int resultCode) {
        if (!mResultSet) {
            mResultSet = true;
            logRequestedPermissionGroups();
            Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
            result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
            PackageManager pm = getPackageManager();
            int numRequestedPermissions = mRequestedPermissions.length;
            int[] grantResults = new int[numRequestedPermissions];
            for (int i = 0; i < numRequestedPermissions; i++) {
                grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage);
            }
            result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults);
            setResult(resultCode, result);
        }
    }
    private void setResultAndFinish() {
        setResultIfNeeded(RESULT_OK);
        finish();
    }
    // ......
    private static final class GroupState {
        static final int STATE_UNKNOWN = 0;
        static final int STATE_ALLOWED = 1;
        static final int STATE_DENIED = 2;
        static final int STATE_SKIPPED = 3;
        final AppPermissionGroup mGroup;
        int mState = STATE_UNKNOWN;
        /** Permissions of this group that need to be granted, null == no permissions of group */
        String[] affectedPermissions;
        GroupState(AppPermissionGroup group) {
            mGroup = group;
        }
    }
    private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
        final int mCallingPackageUid;
        PermissionChangeListener() throws NameNotFoundException {
            mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0);
        }
        @Override
        public void onPermissionsChanged(int uid) {
            if (uid == mCallingPackageUid) {
                updateIfPermissionsWereGranted();
            }
        }
    }
}

GrantPermissionsActivity # onPermissionGrantResult

当用户点击按钮时,会调用onPermissionGrantResult办法,在这个办法中,会判别用户行为,一般会有以下几种:始终答应、仅答应一次、制止、制止并且不需求再提醒,针对每种成果,都会调用onPermissionGrantResultSingleState办法来详细的实施。

在onPermissionGrantResultSingleState中,会判别granted参数,也便是否答应权限,调用AppPermissionGroup的grantRuntimePermissions办法做详细的运行时权限请求。

AppPermissionGroup # grantRuntimePermissions

留意这个办法中的参数,fixedByTheUser其实对应的便是dontAskAgain,是否需求再次问询,假如用户挑选回绝权限并且不再问询,那么就只能去设置中打开权限。

public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
    boolean killApp = false;
    boolean wasAllGranted = true;
    // We toggle permissions only to apps that support runtime
    // permissions, otherwise we toggle the app op corresponding
    // to the permission if the permission is granted to the app.
    for (Permission permission : mPermissions.values()) {
        if (filterPermissions != null
                && !ArrayUtils.contains(filterPermissions, permission.getName())) {
            continue;
        }
        if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) {
            // Skip unallowed permissions.
            continue;
        }
        boolean wasGranted = permission.isGrantedIncludingAppOp();
        if (mAppSupportsRuntimePermissions) {
            // Do not touch permissions fixed by the system.
            if (permission.isSystemFixed()) {
                wasAllGranted = false;
                break;
            }
            // Ensure the permission app op enabled before the permission grant.
            if (permission.affectsAppOp() && !permission.isAppOpAllowed()) {
                permission.setAppOpAllowed(true);
            }
            // Grant the permission if needed.
            if (!permission.isGranted()) {
                permission.setGranted(true);
            }
            // Update the permission flags.
            if (!fixedByTheUser) {
                // Now the apps can ask for the permission as the user
                // no longer has it fixed in a denied state.
                if (permission.isUserFixed() || permission.isUserSet()) {
                    permission.setUserFixed(false);
                    permission.setUserSet(false);
                }
            }
        } else {
            // Legacy apps cannot have a not granted permission but just in case.
            if (!permission.isGranted()) {
                continue;
            }
            // If the permissions has no corresponding app op, then it is a
            // third-party one and we do not offer toggling of such permissions.
            if (permission.affectsAppOp()) {
                if (!permission.isAppOpAllowed()) {
                    permission.setAppOpAllowed(true);
                    // Legacy apps do not know that they have to retry access to a
                    // resource due to changes in runtime permissions (app ops in this
                    // case). Therefore, we restart them on app op change, so they
                    // can pick up the change.
                    killApp = true;
                }
                // Mark that the permission should not be be granted on upgrade
                // when the app begins supporting runtime permissions.
                if (permission.shouldRevokeOnUpgrade()) {
                    permission.setRevokeOnUpgrade(false);
                }
            }
            // Granting a permission explicitly means the user already
            // reviewed it so clear the review flag on every grant.
            if (permission.isReviewRequired()) {
                permission.unsetReviewRequired();
            }
        }
        // If we newly grant background access to the fine location, double-guess the user some
        // time later if this was really the right choice.
        if (!wasGranted && permission.isGrantedIncludingAppOp()) {
            if (permission.getName().equals(ACCESS_FINE_LOCATION)) {
                Permission bgPerm = permission.getBackgroundPermission();
                if (bgPerm != null) {
                    if (bgPerm.isGrantedIncludingAppOp()) {
                        mTriggerLocationAccessCheckOnPersist = true;
                    }
                }
            } else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) {
                ArrayList<Permission> fgPerms = permission.getForegroundPermissions();
                if (fgPerms != null) {
                    int numFgPerms = fgPerms.size();
                    for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {
                        Permission fgPerm = fgPerms.get(fgPermNum);
                        if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) {
                            if (fgPerm.isGrantedIncludingAppOp()) {
                                mTriggerLocationAccessCheckOnPersist = true;
                            }
                            break;
                        }
                    }
                }
            }
        }
    }
    //默认 mDelayChanges = false,这儿真实进行权限请求
    if (!mDelayChanges) {
        persistChanges(false);
        if (killApp) {
            killApp(KILL_REASON_APP_OP_CHANGE);
        }
    }
    return wasAllGranted;
}

前面首要是进行一系列的装备,persistChanges办法中会向PKMS发起进程间通讯,调用PKMS的grantRuntimePermission办法。


void persistChanges(boolean mayKillBecauseOfAppOpsChange) {
        int uid = mPackageInfo.applicationInfo.uid;
        int numPermissions = mPermissions.size();
        boolean shouldKillApp = false;
        for (int i = 0; i < numPermissions; i++) {
            Permission permission = mPermissions.valueAt(i);
            if (!permission.isSystemFixed()) {
                if (permission.isGranted()) {
                    mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
                            permission.getName(), mUserHandle);
                } else {
                    boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1,
                            uid) == PERMISSION_GRANTED;
                    if (isCurrentlyGranted) {
                        mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
                                permission.getName(), mUserHandle);
                    }
                }
            }
            int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0)
                    | (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0)
                    | (permission.shouldRevokeOnUpgrade()
                    ? PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE : 0)
                    | (permission.isPolicyFixed() ? PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0)
                    | (permission.isReviewRequired()
                    ? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0);
            mPackageManager.updatePermissionFlags(permission.getName(),
                    mPackageInfo.packageName,
                    PackageManager.FLAG_PERMISSION_USER_SET
                            | PackageManager.FLAG_PERMISSION_USER_FIXED
                            | PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE
                            | PackageManager.FLAG_PERMISSION_POLICY_FIXED
                            | PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
                    flags, mUserHandle);
            if (permission.affectsAppOp()) {
                if (!permission.isSystemFixed()) {
                    // Enabling/Disabling an app op may put the app in a situation in which it has
                    // a handle to state it shouldn't have, so we have to kill the app. This matches
                    // the revoke runtime permission behavior.
                    if (permission.isAppOpAllowed()) {
                        shouldKillApp |= allowAppOp(permission, uid);
                    } else {
                        shouldKillApp |= disallowAppOp(permission, uid);
                    }
                }
            }
        }
        if (mayKillBecauseOfAppOpsChange && shouldKillApp) {
            killApp(KILL_REASON_APP_OP_CHANGE);
        }
        if (mTriggerLocationAccessCheckOnPersist) {
            new LocationAccessCheck(mContext, null).checkLocationAccessSoon();
            mTriggerLocationAccessCheckOnPersist = false;
        }
    }

PKMS # grantRuntimePermission

@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
    // Because this is accessed via the package manager service AIDL,
    // go through the permission manager service AIDL
    mContext.getSystemService(PermissionManager.class)
            .grantRuntimePermission(packageName, permName, UserHandle.of(userId));
}

这儿咱们看到,是拿到PermissionManager服务目标,调用PMMS的grantRuntimePermission办法,所以咱们需求去PermissionManagerService中查找。

@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {
    final int callingUid = Binder.getCallingUid();
    final boolean overridePolicy =
            checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY)
                    == PackageManager.PERMISSION_GRANTED;
    grantRuntimePermissionInternal(packageName, permName, overridePolicy,
            callingUid, userId, mDefaultPermissionCallback);
}
private void grantRuntimePermissionInternal(String packageName, String permName,
        boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) {
    // ..... 
    final int uid = UserHandle.getUid(userId, pkg.getUid());
    if (callback != null) {
        if (isRuntimePermission) {
            callback.onPermissionGranted(uid, userId);
        } else {
            callback.onInstallPermissionGranted();
        }
        if (permissionHasGids) {
            callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId);
        }
    }
    if (isRuntimePermission) {
        notifyRuntimePermissionStateChanged(packageName, userId);
    }
}

其实在PermissionManagerService的grantRuntimePermissionInternal前面所有的判别,都是在判别当时这个权限是不是现已获取到了,假如获取到了就退出;假如没有获取到,那么就会经过PermissionCallback回调,并判别是否为运行时权限,假如是运行时权限,会回调onPermissionGranted办法,非运行时权限会回调onInstallPermissionGranted办法。

private final PermissionCallback mDefaultPermissionCallback = new PermissionCallback() {
    @Override
    public void onGidsChanged(int appId, int userId) {
        mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED));
    }
    @Override
    public void onPermissionGranted(int uid, int userId) {
        mOnPermissionChangeListeners.onPermissionsChanged(uid);
        // Not critical; if this is lost, the application has to request again.
        mPackageManagerInt.writeSettings(true);
    }
    @Override
    public void onInstallPermissionGranted() {
        mPackageManagerInt.writeSettings(true);
    }
    @Override
    public void onPermissionRevoked(int uid, int userId, String reason) {
        mOnPermissionChangeListeners.onPermissionsChanged(uid);
        // Critical; after this call the application should never have the permission
        mPackageManagerInt.writeSettings(false);
        final int appId = UserHandle.getAppId(uid);
        if (reason == null) {
            mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED));
        } else {
            mHandler.post(() -> killUid(appId, userId, reason));
        }
    }
    @Override
    public void onInstallPermissionRevoked() {
        mPackageManagerInt.writeSettings(true);
    }
    @Override
    public void onPermissionUpdated(int[] userIds, boolean sync) {
        mPackageManagerInt.writePermissionSettings(userIds, !sync);
    }
    @Override
    public void onInstallPermissionUpdated() {
        mPackageManagerInt.writeSettings(true);
    }
    @Override
    public void onPermissionRemoved() {
        mPackageManagerInt.writeSettings(false);
    }
    public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync,
            int uid) {
        onPermissionUpdated(updatedUserIds, sync);
        for (int i = 0; i < updatedUserIds.length; i++) {
            int userUid = UserHandle.getUid(updatedUserIds[i], UserHandle.getAppId(uid));
            mOnPermissionChangeListeners.onPermissionsChanged(userUid);
        }
    }
    public void onInstallPermissionUpdatedNotifyListener(int uid) {
        onInstallPermissionUpdated();
        mOnPermissionChangeListeners.onPermissionsChanged(uid);
    }
};

在onPermissionGranted回调办法中,会调用PackageManagerInternal的writeSettings办法,将权限信息写入到xml文件中。

PackageManagerInternal # writeSettings

@Override
public void writeSettings(boolean async) {
    synchronized (mLock) {
        if (async) {
            scheduleWriteSettingsLocked();
        } else {
            writeSettingsLPrTEMP();
        }
    }
}

这儿是能够挑选同步或许异步,由于涉及到了IO操作,所以这儿传入的是true。

case WRITE_SETTINGS: {
    Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT);
    synchronized (mLock) {
        removeMessages(WRITE_SETTINGS);
        removeMessages(WRITE_PACKAGE_RESTRICTIONS);
        writeSettingsLPrTEMP();
        mDirtyUsers.clear();
    }
    Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
} 
break;

最终是调用writeLPr办法,创立xml文件

void writeLPr() {
    //Debug.startMethodTracing("/data/system/packageprof", 8 * 1024 * 1024);
    final long startTime = SystemClock.uptimeMillis();
    // Whenever package manager changes something on the system, it writes out whatever it
    // changed in the form of a settings object change, and it does so under its internal
    // lock --- so if we invalidate the package cache here, we end up invalidating at the
    // right time.
    invalidatePackageCache();
    // Keep the old settings around until we know the new ones have
    // been successfully written.
    if (mSettingsFilename.exists()) {
        // Presence of backup settings file indicates that we failed
        // to persist settings earlier. So preserve the older
        // backup for future reference since the current settings
        // might have been corrupted.
        if (!mBackupSettingsFilename.exists()) {
            if (!mSettingsFilename.renameTo(mBackupSettingsFilename)) {
                Slog.wtf(PackageManagerService.TAG,
                        "Unable to backup package manager settings, "
                        + " current changes will be lost at reboot");
                return;
            }
        } else {
            mSettingsFilename.delete();
            Slog.w(PackageManagerService.TAG, "Preserving older settings backup");
        }
    }
    mPastSignatures.clear();
    try {
        final FileOutputStream fstr = new FileOutputStream(mSettingsFilename);
        final TypedXmlSerializer serializer = Xml.resolveSerializer(fstr);
        serializer.startDocument(null, true);
        serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
        serializer.startTag(null, "packages");
        for (int i = 0; i < mVersion.size(); i++) {
            final String volumeUuid = mVersion.keyAt(i);
            final VersionInfo ver = mVersion.valueAt(i);
            serializer.startTag(null, TAG_VERSION);
            XmlUtils.writeStringAttribute(serializer, ATTR_VOLUME_UUID, volumeUuid);
            serializer.attributeInt(null, ATTR_SDK_VERSION, ver.sdkVersion);
            serializer.attributeInt(null, ATTR_DATABASE_VERSION, ver.databaseVersion);
            XmlUtils.writeStringAttribute(serializer, ATTR_FINGERPRINT, ver.fingerprint);
            serializer.endTag(null, TAG_VERSION);
        }
        if (mVerifierDeviceIdentity != null) {
            serializer.startTag(null, "verifier");
            serializer.attribute(null, "device", mVerifierDeviceIdentity.toString());
            serializer.endTag(null, "verifier");
        }
        serializer.startTag(null, "permission-trees");
        mPermissions.writePermissionTrees(serializer);
        serializer.endTag(null, "permission-trees");
        serializer.startTag(null, "permissions");
        mPermissions.writePermissions(serializer);
        serializer.endTag(null, "permissions");
        for (final PackageSetting pkg : mPackages.values()) {
            writePackageLPr(serializer, pkg);
        }
        for (final PackageSetting pkg : mDisabledSysPackages.values()) {
            writeDisabledSysPackageLPr(serializer, pkg);
        }
        for (final SharedUserSetting usr : mSharedUsers.values()) {
            serializer.startTag(null, "shared-user");
            serializer.attribute(null, ATTR_NAME, usr.name);
            serializer.attributeInt(null, "userId", usr.userId);
            usr.signatures.writeXml(serializer, "sigs", mPastSignatures.untrackedStorage());
            serializer.endTag(null, "shared-user");
        }
        if (mRenamedPackages.size() > 0) {
            for (Map.Entry<String, String> e : mRenamedPackages.entrySet()) {
                serializer.startTag(null, "renamed-package");
                serializer.attribute(null, "new", e.getKey());
                serializer.attribute(null, "old", e.getValue());
                serializer.endTag(null, "renamed-package");
            }
        }
        mDomainVerificationManager.writeSettings(serializer, false /* includeSignatures */,
                UserHandle.USER_ALL);
        mKeySetManagerService.writeKeySetManagerServiceLPr(serializer);
        serializer.endTag(null, "packages");
        serializer.endDocument();
        fstr.flush();
        FileUtils.sync(fstr);
        fstr.close();
        // New settings successfully written, old ones are no longer
        // needed.
        mBackupSettingsFilename.delete();
        FileUtils.setPermissions(mSettingsFilename.toString(),
                FileUtils.S_IRUSR|FileUtils.S_IWUSR
                |FileUtils.S_IRGRP|FileUtils.S_IWGRP,
                -1, -1);
        writeKernelMappingLPr();
        writePackageListLPr();
        writeAllUsersPackageRestrictionsLPr();
        writeAllRuntimePermissionsLPr();
        com.android.internal.logging.EventLogTags.writeCommitSysConfigFile(
                "package", SystemClock.uptimeMillis() - startTime);
        return;
    } catch(java.io.IOException e) {
        Slog.wtf(PackageManagerService.TAG, "Unable to write package manager settings, "
                + "current changes will be lost at reboot", e);
    }
    // Clean up partially written files
    if (mSettingsFilename.exists()) {
        if (!mSettingsFilename.delete()) {
            Slog.wtf(PackageManagerService.TAG, "Failed to clean up mangled file: "
                    + mSettingsFilename);
        }
    }
    //Debug.stopMethodTracing();
}

详细文件为:

mSettingsFilename = new File(mSystemDir, "packages.xml");

也便是在data/system/packages.xml文件中永久保存,假如使用卸载那么就会清除权限.

<package name="com.lay.nowinandroid" codePath="/data/app/~~Z3KlFzqUZ3vXhgizpl-R4Q==/com.lay.nowinandroid-NcpT91M9gQfcPkeJJhPoxA==" nativeLibraryPath="/data/app/~~Z3KlFzqUZ3vXhgizpl-R4Q==/com.lay.nowinandroid-NcpT91M9gQfcPkeJJhPoxA==/lib" publicFlags="810073926" privateFlags="-1400893440" ft="188c89ab490" it="188c7e11f01" ut="188c89ab614" version="1" userId="10131">
    <sigs count="1" schemeVersion="2">
        <cert index="9" key="/>
    </sigs>
    <perms>
        <item name="android.permission.INTERNET" granted="true" flags="0" />
    </perms>
    <proper-signing-keyset identifier="10" />
</package>

咱们能够看到,在xml文件中的perms标签下有对应权限的声明,以及granted的参数,也便是说下次再进来之后,会查看这个xml文件中咱们要请求的这个权限是不是现已获取到了,假如granted = true || flags = 0,那么就不会再弹窗了。