Android M权限管理(续)

在Android M权限管理这篇文章里,我大致的介绍了Android的动态权限管理,同时简单梳理了一下权限的检查和申请的流程。

在上篇文章的末尾,我们停在了PackageInstaller这个包的grantRuntimePermission这个方法。在这个方法里我们看到,PackageInstaller也是保存了不少的权限的状态,但真正对权限进行“操作”的还是在PackageManagerService,这部分内容很多,我就单独拿出来写这篇续了。

先贴上grantRuntimePermission的源码:

@Override
    public void grantRuntimePermission(String packageName, String name, final int userId) {
        if (!sUserManager.exists(userId)) {
            Log.e(TAG, "No such user:" + userId);
            return;
        }

        mContext.enforceCallingOrSelfPermission(
                android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
                "grantRuntimePermission");

        enforceCrossUserPermission(Binder.getCallingUid(), userId, true, false,
                "grantRuntimePermission");

        final int uid;
        final SettingBase sb;

        synchronized (mPackages) {
            final PackageParser.Package pkg = mPackages.get(packageName);
            if (pkg == null) {
                throw new IllegalArgumentException("Unknown package: " + packageName);
            }

            final BasePermission bp = mSettings.mPermissions.get(name);
            if (bp == null) {
                throw new IllegalArgumentException("Unknown permission: " + name);
            }

            enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(pkg, bp);

            uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
            sb = (SettingBase) pkg.mExtras;
            if (sb == null) {
                throw new IllegalArgumentException("Unknown package: " + packageName);
            }

            final PermissionsState permissionsState = sb.getPermissionsState();

            final int flags = permissionsState.getPermissionFlags(name, userId);
            if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
                throw new SecurityException("Cannot grant system fixed permission: "
                        + name + " for package: " + packageName);
            }

            if (bp.isDevelopment()) {
                // Development permissions must be handled specially, since they are not
                // normal runtime permissions.  For now they apply to all users.
                if (permissionsState.grantInstallPermission(bp) !=
                        PermissionsState.PERMISSION_OPERATION_FAILURE) {
                    scheduleWriteSettingsLocked();
                }
                return;
            }

            final int result = permissionsState.grantRuntimePermission(bp, userId);
            switch (result) {
                case PermissionsState.PERMISSION_OPERATION_FAILURE: {
                    return;
                }

                case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
                    final int appId = UserHandle.getAppId(pkg.applicationInfo.uid);
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED);
                        }
                    });
                }
                break;
            }

            mOnPermissionChangeListeners.onPermissionsChanged(uid);

            // Not critical if that is lost - app has to request again.
            mSettings.writeRuntimePermissionsForUserLPr(userId, false);
        }

        // Only need to do this if user is initialized. Otherwise it's a new user
        // and there are no processes running as the user yet and there's no need
        // to make an expensive call to remount processes for the changed permissions.
        if (READ_EXTERNAL_STORAGE.equals(name)
                || WRITE_EXTERNAL_STORAGE.equals(name)) {
            final long token = Binder.clearCallingIdentity();
            try {
                if (sUserManager.isInitialized(userId)) {
                    MountServiceInternal mountServiceInternal = LocalServices.getService(
                            MountServiceInternal.class);
                    mountServiceInternal.onExternalStoragePolicyChanged(uid, packageName);
                }
            } finally {
                Binder.restoreCallingIdentity(token);
            }
        }
    }

在19行,获取了一个Package对象pkg,这个对象中包含了你能想到的一个应用的各种信息,非常详尽。在32行,从pkg中获取了SettingBase对象sb,这个SettingBase中有一个PermissionsState对象,我们最终权限的操作就是调用的PermissionsState的方法,具体在55行。这里我们看到另一个参数bp,这是一个BaseSetting对象,这个对象包含了一项权限的基本信息。对55行做个小翻译就是:赋予userId的应用的bp这个权限以运行时权限。

其实从PermissionsState的名字我们也可以猜出来这个类就是保存的pkg的所有的权限状态,各种操作和它肯定是拖不了关系的。

private int grantPermission(BasePermission permission, int userId) {
        if (hasPermission(permission.name, userId)) {
            return PERMISSION_OPERATION_FAILURE;
        }

        final boolean hasGids = !ArrayUtils.isEmpty(permission.computeGids(userId));
        final int[] oldGids = hasGids ? computeGids(userId) : NO_GIDS;

        PermissionData permissionData = ensurePermissionData(permission);

        if (!permissionData.grant(userId)) {
            return PERMISSION_OPERATION_FAILURE;
        }

        if (hasGids) {
            final int[] newGids = computeGids(userId);
            if (oldGids.length != newGids.length) {
                return PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED;
            }
        }

        return PERMISSION_OPERATION_SUCCESS;
    }

在第9行,调用ensurePermissionData获取一个PermissionData对象。若是第一次对该权限操作,会新建一个PermissionData对象,并存入mPermissions这个Map中。

然后我们就用PermissionData来grant了。

public boolean grant(int userId) {
            if (!isCompatibleUserId(userId)) {
                return false;
            }

            if (isGranted(userId)) {
                return false;
            }

            PermissionState userState = mUserStates.get(userId);
            if (userState == null) {
                userState = new PermissionState(mPerm.name);
                mUserStates.put(userId, userState);
            }

            userState.mGranted = true;

            return true;
        }

这里的逻辑挺清楚的,userId不对或者已经有权限,返回失败;若之前没有处理过该权限,先新建一个PermissionState对象,然后将其存入mUserStates中,将mGranted设为true。

走到这里grant的流程就走完了,整理一下:

  1. PackageManagerService中通过mPackage得到目标pkg ;
  2. 通过mSettings.mPermissions获取到目标权限bp ;
  3. 通过pkg得到一个PermissionsState对象permissionsState;
  4. 使用permissionsState对象来对pkg的bp进行赋予权限的操作;
  5. 通过ensurePermissionData方法获取一个PermissionData对象pd;
  6. 使用pd来赋予权限

其实到这里,一直没有看到进行持久化操作。这部分内容在PackageManagerService的grantRuntimePermission的末尾,在上面源码的76行,发现没带上pkg和bp这些参数。继续跟了一下,发现这个方法是异步的写xml,而且是直接更新mPackage所有内容。。。这难道不会有性能问题么。。虽然有异步并且移除旧任务的操作。

至此,运行时权限的内容就分析完了。想想还有安装时,默认权限的赋予没有写,后续看情况再添加把。

大家若看到有不对的地方,希望及时指出,多多交流,我也好及时修正,避免误导他人。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值