在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的流程就走完了,整理一下:
- PackageManagerService中通过mPackage得到目标pkg ;
- 通过mSettings.mPermissions获取到目标权限bp ;
- 通过pkg得到一个PermissionsState对象permissionsState;
- 使用permissionsState对象来对pkg的bp进行赋予权限的操作;
- 通过ensurePermissionData方法获取一个PermissionData对象pd;
- 使用pd来赋予权限
其实到这里,一直没有看到进行持久化操作。这部分内容在PackageManagerService的grantRuntimePermission的末尾,在上面源码的76行,发现没带上pkg和bp这些参数。继续跟了一下,发现这个方法是异步的写xml,而且是直接更新mPackage所有内容。。。这难道不会有性能问题么。。虽然有异步并且移除旧任务的操作。
至此,运行时权限的内容就分析完了。想想还有安装时,默认权限的赋予没有写,后续看情况再添加把。
大家若看到有不对的地方,希望及时指出,多多交流,我也好及时修正,避免误导他人。