Android权限管理原理(含6.0-4.3)

人生处万类,知识最为贤。这篇文章主要讲述Android权限管理原理(含6.0-4.3)相关的知识,希望能为你提供帮助。
android 4.3-5.1 AppOpsManager动态权限管理( 官方不成熟的权限管理) AppOpsManager 是Google在Android4.3-Android5.0引入的动态权限管理方式, 但是又与Google觉得不成熟, 所以在每个发行版的时候, 总是会将这个功能给屏蔽掉。国内一些早期版本的权限动态管理的表现类似, 这里用CyanogenMod12里面的实现讲述一下, 国内的ROM源码拿不到, 不过从表现来看, 实现应该类似。
在一开始, 其实Google将权限的动态管理放在每个服务内部, 类似于登记的策略, 比如, 如果一个App要申请Cammera权限, android6.0之前是不要显示的动态申请的, 只需要在Manifest中声明即可, 在需要使用Camera的时候, 如果是第一, App一定没有在Camera服务中登记过, 这时候, Camera服务就会调用AppOpsManager访问AppOpsService, 发起权限申请请求, 并弹出统一的权限申请对话框, 并做好登记, 如果获得了权限, 就开启所请求的服务, 如果被拒绝, 就拒绝向APP提供服务。这个时机是在AppOpsManager鉴定的时候, 类似于对没有获得权限的APP进行服务拦截, 如果在Setting里设置了响应的权限, 也会去更新相应的权限操作持久化文件/data/system/appops.xml, 下次再次申请服务的时候, 服务会再次鉴定权限。
举个栗子( 定位服务LocationManagerService)

CM12 源码
在调用LocationManager的requestLocationUpdates函数获取定位信息时候, 其实是通过Binder请求LocationManagerService去定位。
LocationManager
private void requestLocationUpdates(LocationRequest request, LocationListener listener, Looper looper, PendingIntent intent) {String packageName = mContext.getPackageName(); // wrap the listener class ListenerTransport transport = wrapListener(listener, looper); try { mService.requestLocationUpdates(request, transport, intent, packageName); } catch (RemoteException e) { Log.e(TAG, " RemoteException" , e); } }

看一下LocationManagerService中关键代码
LocationManagerService
@ Override public void requestLocationUpdates(LocationRequest request, ILocationListener listener, PendingIntent intent, String packageName) { if (request = = null) request = DEFAULT_LOCATION_REQUEST; checkPackageName(packageName); < !--关键函数 1 , 查询Manifest文件, 是否进行了权限声明 --> int allowedResolutionLevel = getCallerAllowedResolutionLevel(); checkResolutionLevelIsSufficientForProviderUse(allowedResolutionLevel, request.getProvider()); 。。。 < !--获取调用app的pid跟uid--> final int pid = Binder.getCallingPid(); final int uid = Binder.getCallingUid(); // providers may use public location API' s, need to clear identity long identity = Binder.clearCallingIdentity(); try { < !--关键函数 2 检查是否动态授权了权限, 或者拒绝了权限--> checkLocationAccess(uid, packageName, allowedResolutionLevel); synchronized (mLock) { Receiver receiver = checkListenerOrIntentLocked(listener, intent, pid, uid, packageName, workSource, hideFromAppOps); if (receiver != null) { requestLocationUpdatesLocked(sanitizedRequest, receiver, pid, uid, packageName); } } } finally { Binder.restoreCallingIdentity(identity); } }

getCallerAllowedResolutionLevel主要通过调用getAllowedResolutionLevel, 通过mContext.checkPermission查询APP是否在Manifest中声明了权限
private int getCallerAllowedResolutionLevel() { return getAllowedResolutionLevel(Binder.getCallingPid(), Binder.getCallingUid()); }private int getAllowedResolutionLevel(int pid, int uid) { if (mContext.checkPermission(android.Manifest.permission.ACCESS_FINE_LOCATION, pid, uid) = = PackageManager.PERMISSION_GRANTED) { return RESOLUTION_LEVEL_FINE; } else if (mContext.checkPermission(android.Manifest.permission.ACCESS_COARSE_LOCATION, pid, uid) = = PackageManager.PERMISSION_GRANTED) { return RESOLUTION_LEVEL_COARSE; } else { return RESOLUTION_LEVEL_NONE; } }

checkLocationAccess这里才是动态鉴权的入口, 在checkLocationAccess函数中, 会调用mAppOps.checkOp去鉴权, mAppOps就是AppOpsManager实例,
boolean checkLocationAccess(int uid, String packageName, int allowedResolutionLevel) { int op = resolutionLevelToOp(allowedResolutionLevel); if (op > = 0) { int mode = mAppOps.checkOp(op, uid, packageName); if (mode != AppOpsManager.MODE_ALLOWED & & mode != AppOpsManager.MODE_ASK ) { return false; } } return true; }

进而通过Binder向AppOpsService服务发送鉴权请求
public int noteOp(int op, int uid, String packageName) { try { int mode = mService.noteOperation(op, uid, packageName); if (mode = = MODE_ERRORED) { throw new SecurityException(buildSecurityExceptionMsg(op, uid, packageName)); } return mode; } catch (RemoteException e) { } return MODE_IGNORED; }

AppOpsService负责动态权限的鉴定跟更新, 去看noteOperation代码
@ Override public int noteOperation(int code, int uid, String packageName) { final Result userDialogResult; verifyIncomingUid(uid); verifyIncomingOp(code); synchronized (this) { Ops ops = getOpsLocked(uid, packageName, true); ... < !--关键点 1--> if (switchOp.mode = = AppOpsManager.MODE_IGNORED || switchOp.mode = = AppOpsManager.MODE_ERRORED) {op.rejectTime = System.currentTimeMillis(); op.ignoredCount+ + ; return switchOp.mode; < !--关键点 2--> } else if(switchOp.mode = = AppOpsManager.MODE_ALLOWED) {op.time = System.currentTimeMillis(); op.rejectTime = 0; op.allowedCount+ + ; return AppOpsManager.MODE_ALLOWED; } else { op.noteOpCount+ + ; < !--关键函数 3--> userDialogResult = askOperationLocked(code, uid, packageName, switchOp); } } return userDialogResult.get(); }

在上面的代码里面, 1、2是对已经处理过的场景直接返回已授权, 或者已经拒绝, 而3 就是我们常见授权入口对话框, 这里是统一在AppOpsServie中进行授权处理的。askOperationLocked会显示一个系统对话框, 用户选择授权或者拒绝后, AppOpsServie会将选择记录在案, 并通知申请服务提供或者拒绝服务。askOperationLocked通过mHandler发送鉴权Message, 看一下实现其实就是新建了一个PermissionDialog授权对话框, 并且将AppOpsService的引用传了进去, 授权后会通过mService.notifyOperation通知授权结果。
mHandler = new Handler() { public void handleMessage(Message msg) { switch (msg.what) { case SHOW_PERMISSION_DIALOG: { HashMap< String, Object> data = (HashMap< String, Object> ) msg.obj; synchronized (this) { Op op = (Op) data.get(" op" ); Result res = (Result) data.get(" result" ); op.dialogResult.register(res); if(op.dialogResult.mDialog = = null) { Integer code = (Integer) data.get(" code" ); Integer uid= (Integer) data.get(" uid" ); String packageName = (String) data.get(" packageName" ); Dialog d = new PermissionDialog(mContext, AppOpsService.this, code, uid, packageName); op.dialogResult.mDialog = (PermissionDialog)d; d.show(); } } }break; } } };

Android权限管理原理(含6.0-4.3)

文章图片

Android发行版源码对于动态权限管理的支持( 几乎为零) 虽然App可以获得AppOpsManager的实例, 但是真正操作动态权限的接口setMode函数是被隐藏, 如下
/** @ hide */ public void setMode(int code, int uid, String packageName, int mode) { try { mService.setMode(code, uid, packageName, mode); } catch (RemoteException e) { } }

在Android4.3-5.0之间, setMode也是不对外开放的, 看源码也只有NotificationManagerService这个系统应用使用了setMode, 也就是说发行版, 只有通知是通过系统的通知管理进行动态管理的。
public void setNotificationsEnabledForPackage(String pkg, int uid, boolean enabled) { checkCallerIsSystem(); Slog.v(TAG, (enabled?" en" :" dis" ) + " abling notifications for " + pkg); mAppOps.setMode(AppOpsManager.OP_POST_NOTIFICATION, uid, pkg, enabled ? AppOpsManager.MODE_ALLOWED : AppOpsManager.MODE_IGNORED); // Now, cancel any outstanding notifications that are part of a just-disabled app if (ENABLE_BLOCKED_NOTIFICATIONS & & !enabled) { cancelAllNotificationsInt(pkg, 0, 0, true, UserHandle.getUserId(uid)); } }

对于6.0以下的手机权限如何检测 对于Android6.0以下的手机, 不需要关心targetVersion。先说个自己验证的结果: 基本没法检测, 同时也不需要检测, 就算检测出来也没有多大意义, 因为, 触发时机是在真正的调用服务时候。对于4.3到6.0之前的国产ROM, 虽然采用AppopsManagerService, 但是并未按照Google的模型对所有权限进行适配, 在这个模型下, 也就适配了两个权限, (悬浮窗还不一定有)
  • 通知权限 public static final int OP_POST_NOTIFICATION = 11;
  • 悬浮窗权限 public static final int OP_SYSTEM_ALERT_WINDOW = 24;
Google发行版的APPOpsService, 基本是把整个鉴权逻辑给屏蔽了, 通过CM的源码, 课对这部分代码窥探一斑, 如果整个权限都采用4.3权限管理模型, 在拒绝一项权限的时候, 这个操作会被持久化到appops.xml中去, 但是具体看下去, 其实并不是如此, 这种机制只对以上两个权限生效:
< pkg n= " com.xxx" > < uid n= " 10988" > < !--关键点1--> < op n= " 11" m= " 1" t= " 1513145979969" r= " 1521550658067" /> < op n= " 12" t= " 1521550651593" /> < op n= " 29" t= " 1521550682769" /> < pkg n= " com.wandoujia.phoenix2.usbproxy" > < uid n= " 10969" > < op n= " 4" t= " 1517279031173" /> < !--关键点2--> < op n= " 11" m= " 1" t= " 1510889291834" r= " 1517279030708" /> < op n= " 14" t= " 1517293452801" /> < !--关键点3--> < op n= " 24" m= " 1" /> < op n= " 40" t= " 1513599239364" d= " 600011" />

国产rom中, 假如你拒绝授权位置权限, 按照AppOpsService模型, 该操作应该被持久化到appops.xml中去, 但是, 结果并非如此, 也就是说, 对于其他权限, 国产ROM应该是自己糊弄了一套持久管理, 持久化Android系统API无法访问的地方, 仅仅为自身ROM可见。appops.xml真正被系统使用时从Android6.0开始, 其实Android6.0是有两套权限管理的, 这其实很混乱, 不知道Google怎么想的, 不过6.0似乎也有漏洞: 权限的授予跟回收权限好像并不配对。
那么这就带来了一个问题, 在Android4.3到Android6.0之间的版本, 并没有同一个API来检测是否获取了某种权限, 因为你动态更新的权限并未持久化到appops.xml中去。对于Android6.0之前的ROM, 虽然不能检测, 但完全可以直接用服务, 不会崩溃, 因为如果真需要鉴权, 它的鉴权时机其实是在服务使用的时候。AppopsManager在6.0之前, 只能用来检测通知, 可能还有悬浮窗。
Android 6.0权限管理原理 Android6.0的动态权限管理让用户在任何时候都可以取消授权, 因此, 每次在使用系统服务的时候, 都要动态查询是否获取了相应的权限, 如果没有获取, 就需要动态去申请。
Android6.0权限查询 V4兼容包里面提供了一个工具类PermissionChecker, 可以用来检查权限获取情况。
PermissionChecker
public static int checkPermission(@ NonNull Context context, @ NonNull String permission, int pid, int uid, String packageName) { if (context.checkPermission(permission, pid, uid) = = PackageManager.PERMISSION_DENIED) { return PERMISSION_DENIED; }String op = AppOpsManagerCompat.permissionToOp(permission); if (op = = null) { return PERMISSION_GRANTED; }if (packageName = = null) { String[] packageNames = context.getPackageManager().getPackagesForUid(uid); if (packageNames = = null || packageNames.length < = 0) { return PERMISSION_DENIED; } packageName = packageNames[0]; }if (AppOpsManagerCompat.noteProxyOp(context, op, packageName) != AppOpsManagerCompat.MODE_ALLOWED) { return PERMISSION_DENIED_APP_OP; }return PERMISSION_GRANTED; }

这里我们只关心context.checkPermission, 从上面对于4.3-5.1的APPOpsManager的分析, 我们知道AppOpsManagerCompat本身的一些操作对于权限管理并没有实际意义, 只是用来做一些标记, 最多就是对于统治权限有些用。接下来看checkPermission:
ContextImple.java
/** @ hide */ @ Override public int checkPermission(String permission, int pid, int uid, IBinder callerToken) { if (permission = = null) { throw new IllegalArgumentException(" permission is null" ); } try { return ActivityManagerNative.getDefault().checkPermissionWithToken( permission, pid, uid, callerToken); } catch (RemoteException e) { return PackageManager.PERMISSION_DENIED; } }

接着往下看
ActivityManagerNative.java
public int checkPermission(String permission, int pid, int uid) throws RemoteException { Parcel data = Parcel.obtain(); Parcel reply = Parcel.obtain(); data.writeInterfaceToken(IActivityManager.descriptor); data.writeString(permission); data.writeInt(pid); data.writeInt(uid); mRemote.transact(CHECK_PERMISSION_TRANSACTION, data, reply, 0); reply.readException(); int res = reply.readInt(); data.recycle(); reply.recycle(); return res; }

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

进而调用ActivityManager.checkComponentPermission, 调用AppGlobals.getPackageManager().checkUidPermission(permission, uid);
ActivityManager.java
/** @ hide */ public static int checkComponentPermission(String permission, int uid, int owningUid, boolean exported) { // Root, system server get to do everything.< !--root及System能获取所有权限--> if (uid = = 0 || uid = = Process.SYSTEM_UID) { return PackageManager.PERMISSION_GRANTED; } 。。。 < !--普通的权限查询--> try { return AppGlobals.getPackageManager() .checkUidPermission(permission, uid); } catch (RemoteException e) { // Should never happen, but if it does... deny! Slog.e(TAG, " PackageManager is dead?!?" , e); } return PackageManager.PERMISSION_DENIED; }

最终调用PackageManagerService.java去查看是否有权限。到这里, 我们只需要知道权限的查询其实是通过PKMS来进行的。心里先有个底, 权限的更新, 持久化, 恢复都是通过PKMS来进行的。
PKMS不同版本的权限查询 Android5.0的checkUidPermission
public int checkUidPermission(String permName, int uid) { final boolean enforcedDefault = isPermissionEnforcedDefault(permName); synchronized (mPackages) { < !--PackageManagerService.Setting.mUserIds数组中, 根据uid查找uid( 也就是package) 的权限列表--> Object obj = mSettings.getUserIdLPr(UserHandle.getAppId(uid)); if (obj != null) { GrantedPermissions gp = (GrantedPermissions)obj; if (gp.grantedPermissions.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } } else { < !--mSystemPermissions记录一些系统级的应用的 uid 对应的 permission-> HashSet< String> perms = mSystemPermissions.get(uid); if (perms != null & & perms.contains(permName)) { return PackageManager.PERMISSION_GRANTED; } } if (!isPermissionEnforcedLocked(permName, enforcedDefault)) { return PackageManager.PERMISSION_GRANTED; } } return PackageManager.PERMISSION_DENIED; }

Android6.0+ 的checkUidPermission
@ Override public int checkUidPermission(String permName, int uid) { 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) { final SettingBase ps = (SettingBase) obj; final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { 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; }

可以看到Android6.0之后, 对权限的操作是PermissionsState
PermissionsState.java (android-6.0\\frameworks\\base\\services\\core\\java\\com\\android\\server\\pm)
public boolean hasPermission(String name, int userId) { enforceValidUserId(userId); if (mPermissions = = null) { return false; }PermissionData permissionData = mPermissions.get(name); return permissionData != null & & permissionData.isGranted(userId); }

从上面的代码可以很清晰看出, 6.0之后, 除了声明了权限之外, 还必须是授权了的。前一篇文章, 已经说明了运行时权限跟install权限区别, 对于install权限isGranted一直返回是True。这里先不必深究PermissionsState是怎么存进内存, 你先记住, 后面会将讲。
Android权限管理原理(含6.0-4.3)

文章图片

Android6.0动态申请权限 申请权限可以通过V4包里面的ActivityCompat, 它已经对不同版本做了兼容
ActivityCompat.java
public static void requestPermissions(final @ NonNull Activity activity, final @ NonNull String[] permissions, final int requestCode) { if (Build.VERSION.SDK_INT > = 23) { ActivityCompatApi23.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); } }); } }

可以看到, 如果是6.0以下, 直接通过PKMS查询是否在Manifest里面申请了权限, 并把查询结果通过onRequestPermissionsResult回调传给Activity或者Fragment。其实这里只要在Manifest中声明了, 就会默认是Granted。接着往下看: ActivityCompatApi23最终会调用activity.requestPermissions去请求权限。
Activity
public final void requestPermissions(@ NonNull String[] permissions, int requestCode) { Intent intent = getPackageManager().buildRequestPermissionsIntent(permissions); startActivityForResult(REQUEST_PERMISSIONS_WHO_PREFIX, intent, requestCode, null); }

Intent其实是通过PackageManager( ApplicationPackageManager实现类) 获取的Intent
public Intent buildRequestPermissionsIntent(@ NonNull String[] permissions) { if (ArrayUtils.isEmpty(permissions)) { throw new NullPointerException(" 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; }

这里首先是隐式的获取授权Activity组件相关信息( GrantPermissionsActivity) , 其实就是对话框样式的授权Activity, 它是PackageInstaller系统应用里面的一个Activity。这里的getPermissionControllerPackageName其实就是获取相应的包名,
ApplicationPackageManager.java (android-6.0\\frameworks\\base\\core\\java\\android\\app)
@ Override public String getPermissionControllerPackageName() { synchronized (mLock) { if (mPermissionsControllerPackageName = = null) { try { mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName(); } catch (RemoteException e) { throw new RuntimeException(" Package manager has died" , e); } } return mPermissionsControllerPackageName; } }

最终通过PackageManagerService获取包名
PackageManagerService.java (android-6.0\\frameworks\\base\\services\\core\\java\\com\\android\\server\\pm)
@ Override public String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredInstallerPackage; } }

mRequiredInstallerPackage这个变量具体赋值是在PMS的构造器中: 对于原生Android 6.0, 权限管理的APP跟安装器是同一个
mRequiredInstallerPackage = getRequiredInstallerLPr();

这里会得到PackageInstaller应用的相关信息, PackageInstaller负责应用的安装与卸载, 里面还包含了对授权管理的一些逻辑。startActivityForResult启动的就是PackageInstaller中的GrantPermissionsActivity, 该Activity主要负责权限的授予工作。
< activity android:name= " .permission.ui.GrantPermissionsActivity" android:configChanges= " orientation|keyboardHidden|screenSize" android:excludeFromRecents= " true" android:theme= " @ style/GrantPermissions" > < intent-filter> < action android:name= " android.content.pm.action.REQUEST_PERMISSIONS" /> < category android:name= " android.intent.category.DEFAULT" /> < /intent-filter> < /activity>

这是一个类似于对话框的悬浮窗样式的Activity
< style name= " GrantPermissions" parent= " Settings" > < item name= " android:windowIsFloating" > true< /item> < item name= " android:windowElevation" > @ dimen/action_dialog_z< /item> < item name= " android:windowSwipeToDismiss" > false< /item> < /style>

之后就是动态更新权限流程:
Android权限管理原理(含6.0-4.3)

文章图片

如何动态更新RuntimePermission 通过上面的流程, 我们进入了GrantPermissionsActivity
GrantPermissionsActivity
GrantPermissionsActivity其实是利用GroupState对象与PKMS通信, 远程更新权限的。
public class GrantPermissionsActivity extends OverlayTouchActivity implements GrantPermissionsViewHandler.ResultListener {private LinkedHashMap< String, GroupState> mRequestGrantPermissionGroups = new LinkedHashMap< > (); ....@ Override 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.mState = GroupState.STATE_ALLOWED; } else { groupState.mGroup.revokeRuntimePermissions(doNotAskAgain); groupState.mState = GroupState.STATE_DENIED; } updateGrantResults(groupState.mGroup); } if (!showNextPermissionGroupGrantRequest()) { setResultAndFinish(); } }

具体更新流程:
public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) { final int uid = mPackageInfo.applicationInfo.uid; // 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; } ... < !--一些关键点--> // Grant the permission if needed. if (!permission.isGranted()) { permission.setGranted(true); mPackageManager.grantRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } // 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(true); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_FIXED | PackageManager.FLAG_PERMISSION_USER_SET, 0, mUserHandle);

可以看到最终还是调用PackageManager去更新App的运行时权限,最终走进PackageManagerService服务,
PackageManagerService
@ 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 /* requireFullPermission */, true /* checkShell */, " grantRuntimePermission" ); 。。。。。 ... 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 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); }

private static void enforceDeclaredAsUsedAndRuntimeOrDevelopmentPermission(PackageParser.Package pkg, BasePermission bp) { int index = pkg.requestedPermissions.indexOf(bp.name); if (index = = -1) { throw new SecurityException(" Package " + pkg.packageName + " has not requested permission " + bp.name); } if (!bp.isRuntime() & & !bp.isDevelopment()) { throw new SecurityException(" Permission " + bp.name + " is not a changeable permission type" ); } }

首先要更新内存中的权限授予情况
PermissionsState.java
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; }

private PermissionData ensurePermissionData(BasePermission permission) { if (mPermissions = = null) { mPermissions = new ArrayMap< > (); } PermissionData permissionData = mPermissions.get(permission.name); if (permissionData = = null) { permissionData = new PermissionData(permission); mPermissions.put(permission.name, permissionData); } return permissionData; }

下一步, 要将更新的权限持久化到文件中去 mSettings.writeRuntimePermissionsForUserLPr
RuntimePermission持久化 Settings.java
public void writeRuntimePermissionsForUserLPr(int userId, boolean sync) { if (sync) { mRuntimePermissionsPersistence.writePermissionsForUserSyncLPr(userId); } else { mRuntimePermissionsPersistence.writePermissionsForUserAsyncLPr(userId); } }

Settings.getPackageLPw这个方法, 这是在安装应用扫描的时候scanPackageDirtyLI方法调用的, 里面可以看到Settings类中的mUserIds、mPackages里面存的value还有PackageManagerService中的mPackages.pkg. mExtras都是同一个玩意奏是个PackageSetting。
private File getUserRuntimePermissionsFile(int userId) { // TODO: Implement a cleaner solution when adding tests. // This instead of Environment.getUserSystemDirectory(userId) to support testing. File userDir = new File(new File(mSystemDir, " users" ), Integer.toString(userId)); return new File(userDir, RUNTIME_PERMISSIONS_FILE_NAME); }

在目录data/system/0/runtime-permissions.xml存放需要运行时申请的权限, Android6.0以上才有
< pkg name= " com.snail.labaffinity" > < item name= " android.permission.CALL_PHONE" granted= " true" flags= " 0" /> < item name= " android.permission.CAMERA" granted= " false" flags= " 1" /> < /pkg>

RuntimePermission 恢复( 其实这里也包含普通权限) 这些持久化的数据会在手机启动的时候由PMS读取,开机启动, PKMS扫描Apk, 并更新package信息, 检查/data/system/packages.xml是否存在, 这个文件是在解析apk时由writeLP()创建的, 里面记录了系统的permissions, 以及每个apk的name,codePath,flags,ts,version,uesrid等信息, 这些信息主要通过apk的AndroidManifest.xml解析获取, 解析完apk后将更新信息写入这个文件并保存到flash, 下次开机直接从里面读取相关信息添加到内存相关列表中, 当有apk升级, 安装或删除时会更新这个文件, packages.xml放的只包括installpermission, runtimepermissiono由runtime-permissions.xml存放。
public PackageManagerService(Context context, Installer installer, boolean factoryTest, boolean onlyCore) { .... mSettings = new Settings(mPackages); //汇总并更新和Permission相关的信息updatePermissionsLPw(null, null, true,regrantPermissions,regrantPermissions); //将信息写到package.xml、package.list及package-stopped.xml文件中 mSettings.writeLPr(); .... mFirstBoot = !mSettings.readLPw(sUserManager.getUsers(false)); Settings(File dataDir, Object lock) {mRuntimePermissionsPersistence = new RuntimePermissionPersistence(mLock); < !--加载package信息-->

根据SettingsFile或者BackupSettingsFile读取相应的设置信息 生成PackageSetting对象, 里面有权限列表字段protected final PermissionsState mPermissionsState; , 之后再运行中, 动态权限的操作都是针对这个对象
boolean readLPw(@ NonNull List< UserInfo> users) { FileInputStream str = null; if (mBackupSettingsFilename.exists()) { try { str = new FileInputStream(mBackupSettingsFilename); mReadMessages.append(" Reading from backup settings file\\n" ); ... while ((type = parser.next()) != XmlPullParser.END_DOCUMENT & & (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {String tagName = parser.getName(); if (tagName.equals(" package" )) {!--读取package信息, 包括install权限信息( 对于Android6.0package.xml) --> readPackageLPw(parser); ...< !--读取runtime权限信息--> for (UserInfo user : users) { mRuntimePermissionsPersistence.readStateForUserSyncLPr(user.id); } }private void readPackageLPw(XmlPullParser parser) throws XmlPullParserException, IOException { String name = null; ... (tagName.equals(TAG_PERMISSIONS)) { readInstallPermissionsLPr(parser, packageSetting.getPermissionsState());

之后就可以checkpermission了
@ Override public int checkUidPermission(String permName, int uid) { 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) { final SettingBase ps = (SettingBase) obj; final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { return PackageManager.PERMISSION_GRANTED; }

Android权限管理原理(含6.0-4.3)

文章图片

原来的权限存放位置在哪? 不会都从Android Manifest清单去读取, 只会在启动时读取一次。Android6.0之前会吧所有的权限都放置在data/system/packages.xml文件中。Android6.0之后, 分为运行时权限跟普通权限, 普通权限还是放在data/system/packages.xml中, 运行时权限防止在data/system/users/0/runtime-permissions.xml文件中。根据运行时是否动态申请去更新权限。
Android权限管理的关键节点, 在哪里? 关键节点并不是查询是否具有该权限, Android6.0之前的 权限查询是不会触发权限申请与授权的, 只有在请求系统服务的时候, 由系统服务调用AppopsManager去查询是否赋予了该权限, 第一次未操作肯定是null, 未赋予就可能会触发权限申请逻辑, 这个点在各个系统服务内部, 由AppOpsService服务统一管理, 不过对于官方的Release版本, 其实只有系统通知APP才有动态权限管理的能力, 其他都没有操作能力。
参考文档 【Android权限管理原理(含6.0-4.3)】1、Android 安全機制概述 Permission
2、android permission权限与安全机制解析
3、android6.0权限管理原理
4、深入理解 PackageManagerService
5、Android 4.3 隐藏功能 App Ops 分析
6、Android 权限机制, 你真的了解吗?
7、Android原生权限管理: AppOps
8、 Android 5.1 AppOps总结
9、[CM12源码]

    推荐阅读