保存到桌面加入收藏设为首页
IOS开发
当前位置:首页 > IOS开发

Android权限管理理(含6.04.3)-安度博客

时间:2019-01-11 19:16:27   作者:   来源:   阅读:152   评论:0
内容摘要:nk" target="_blank">Android 4.3-5.1 AppOpsManager动态权限管理(官方不成熟的权限管理)AppOpsManager 是Google在nk" target="_blank"......
  • nk" target="_blank">Android 4.3-5.1 AppOpsManager动态权限管理(官方不成熟的权限管理)

    AppOpsManager 是Google在nk" target="_blank">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

    @Overridepublic 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&#39;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代码

    @Overridepublic 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(&#39;op&#39;); Result res = (Result) data.get(&#39;result&#39;); op.dialogResult.register(res); if(op.dialogResult.mDialog == null) { Integer code = (Integer) data.get(&#39;code&#39;); Integer uid = (Integer) data.get(&#39;uid&#39;); String packageName = (String) data.get(&#39;packageName&#39;); Dialog d = new PermissionDialog(mContext AppOpsService.this code uid packageName); op.dialogResult.mDialog = (PermissionDialog)d; d.show(); } } }break; } } };

    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?&#39;en&#39;:&#39;dis&#39;) + &#39;abling notifications for &#39; + 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)); }}

    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 */@Overridepublic int checkPermission(String permission int pid int uid IBinder callerToken) { if (permission == null) { throw new IllegalArgumentException(&#39;permission is null&#39;); } 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 &#39;PackageManager is dead?!?&#39; 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 rameworksaseservicescorejavacomandroidserverpm)

    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是怎么存进内存,你先记住,后面会将讲。

    权限恢复流程

    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(&#39;permission cannot be null or empty&#39;); } 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 rameworksasecorejavaandroidapp)

    @Overridepublic String getPermissionControllerPackageName() { synchronized (mLock) { if (mPermissionsControllerPackageName == null) { try { mPermissionsControllerPackageName = mPM.getPermissionControllerPackageName(); } catch (RemoteException e) { throw new RuntimeException(&#39;Package manager has died&#39; e); } } return mPermissionsControllerPackageName; }}

    最终通过PackageManagerService获取包名

    PackageManagerService.java (android-6.0 rameworksaseservicescorejavacomandroidserverpm)

    @Overridepublic String getPermissionControllerPackageName() { synchronized (mPackages) { return mRequiredInstallerPackage; }}

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

    mRequiredInstallerPackage = getRequiredInstallerLPr();

    这里会得到PackageInstaller应用的相关信息,PackageInstaller负责应用的安装与卸载,里面还包含了对授权管理的一些逻辑。startActivityForResult启动的就是PackageInstaller中的GrantPermissionsActivity,该Activity主要负责权限的授予工作。

     <activity android:name=&#39;.permission.ui.GrantPermissionsActivity&#39; android:configChanges=&#39;orientation|keyboardHidden|screenSize&#39; android:excludeFromRecents=&#39;true&#39; android:theme=&#39;@style/GrantPermissions&#39;> <intent-filter> <action android:name=&#39;android.content.pm.action.REQUEST_PERMISSIONS&#39; /> <category android:name=&#39;android.intent.category.DEFAULT&#39; /> </intent-filter> </activity>

    这是一个类似于对话框的悬浮窗样式的Activity

    <style name=&#39;GrantPermissions&#39; parent=&#39;Settings&#39;> <item name=&#39;android:windowIsFloating&#39;>true</item> <item name=&#39;android:windowElevation&#39;>@dimen/action_dialog_z</item> <item name=&#39;android:windowSwipeToDismiss&#39;>false</item></style>

    之后就是动态更新权限流程:

    权限恢复流程

    如何动态更新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 &#39;No such user:&#39; + userId); return; } ...一些检查 mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS &#39;grantRuntimePermission&#39;); enforceCrossUserPermission(Binder.getCallingUid() userId true /* requireFullPermission */ true /* checkShell */ &#39;grantRuntimePermission&#39;); 。。。。。 ... uid = UserHandle.getUid(userId pkg.applicationInfo.uid); sb = (Settingbase) pkg.mExtras; if (sb == null) { throw new IllegalArgumentException(&#39;Unknown package: &#39; + 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(&#39;Package &#39; + pkg.packageName + &#39; has not requested permission &#39; + bp.name); } if (!bp.isRuntime() && !bp.isDevelopment()) { throw new SecurityException(&#39;Permission &#39; + bp.name + &#39; is not a changeable permission type&#39;); }}

    首先要更新内存中的权限授予情况

    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 &#39;users&#39;) Integer.toString(userId)); return new File(userDir RUNTIME_PERMISSIONS_FILE_NAME);}

    在目录data/system/0/runtime-permissions.xml存放需要运行时申请的权限,Android6.0以上才有

     <pkg name=&#39;com.snail.labaffinity&#39;> <item name=&#39;android.permission.CALL_PHONE&#39; granted=&#39;true&#39; flags=&#39;0&#39; /> <item name=&#39;android.permission.CAMERA&#39; granted=&#39;false&#39; flags=&#39;1&#39; /> </pkg>

    RuntimePermission 恢复(其实这里也包含普通权限)

    这些持久化的数据会在手机启动的时候由PMS读取开机启动,PKMS扫描Apk,并更新package信息,检查/data/system/packages.xml是否存在,这个文件是在解析apk时由writeLP()创建的,里面记录了系统的permissions,以及每个apk的namecodePathflagstsversionuesrid等信息,这些信息主要通过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 regrantPermissionsregrantPermissions); //将信息写到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(&#39;Reading from backup settings file&#39;); ... while ((type = parser.next()) != xmlPullParser.END_DOCUMENT && (type != xmlPullParser.END_TAG || parser.getDepth() > outerDepth)) { String tagName = parser.getName(); if (tagName.equals(&#39;package&#39;)) { !--读取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 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才有动态权限管理的能力,其他都没有操作能力。

     


本站所有站内信息仅供娱乐参考,不作任何商业用途,不以营利为目的,专注分享快乐,欢迎收藏本站!
所有信息均来自:百度一下 (威尼斯人官网)