Android M权限管理(续)

来源:互联网 发布:金蝶erp软件二次开发 编辑:程序博客网 时间:2024/06/07 23:57

在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所有内容。。。这难道不会有性能问题么。。虽然有异步并且移除旧任务的操作。

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

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

0 0
原创粉丝点击