一、授予方式

1.动态授权

从Android M开始,虽然之后的版本会有部分调整,但对危险权限的处理方式是一致的。无论是亲自编码实现,还是引用第三方框架,技术都已经很成熟了,在此不做过多的论述。

2.特定shareUID属性

这里以android.uid.system属性为例。不同于通过install方式安装的App,将该属性添加到AndroidManifest.xml中后,需要先用System Signature进行签名,再像System App一样集成到Rom之中,这样便可以直接使用在AndroidManifest.xml中声明的所有权限。

3.default permission配置

对于那些既不想申请运行时权限,也不想添加android.uid.system属性,即使放置在system/priv-app目录下,依然不能使用某些在AndroidManifest.xml中声明的权限的System App。在system/etc/permissions目录下,做好default permission配置后,便可直接声明、使用那些配置过的权限。

二、查看授权

adb shell dumpsys package com.xxx.xxx

未授权:

已授权:

三、原理简析

1.动态授权

ActivityCompat.requestPermissions(...):实际是通过PMS的grantRuntimePermission(...)方法,来完成运行时权限的动态分配;最终存放到data/system/users/0/runtime-permissions.xml配置文件中,而安装时权限都是存放在data/system/packages.xml配置文件中的。

不同的Android系统版本,代码结构有一定的差异。下面是Android Q中的部分源码:

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.java@Override
public void grantRuntimePermission(String packageName, String permName, final int userId) {boolean overridePolicy = (checkUidPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, Binder.getCallingUid())== PackageManager.PERMISSION_GRANTED);mPermissionManager.grantRuntimePermission(permName, packageName, overridePolicy,getCallingUid(), userId, mPermissionCallback);
}
//frameworks/base/services/core/java/com/android/server/pm/permission/PermissionManagerService.javaprivate void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,int callingUid, final int userId, PermissionCallback callback) {if (!mUserManagerInt.exists(userId)) {Log.e(TAG, "No such user:" + userId);return;}mContext.enforceCallingOrSelfPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,"grantRuntimePermission");enforceCrossUserPermission(callingUid, userId,true,  // requireFullPermissiontrue,  // checkShellfalse, // requirePermissionWhenSameUser"grantRuntimePermission");final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);if (pkg == null || pkg.mExtras == null) {throw new IllegalArgumentException("Unknown package: " + packageName);}final BasePermission bp;synchronized(mLock) {bp = mSettings.getPermissionLocked(permName);}if (bp == null) {throw new IllegalArgumentException("Unknown permission: " + permName);}if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {throw new IllegalArgumentException("Unknown package: " + packageName);}bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);// If a permission review is required for legacy apps we represent// their permissions as always granted runtime ones since we need// to keep the review required permission flag per user while an// install permission's state is shared across all users.if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M&& bp.isRuntime()) {return;}final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);final PackageSetting ps = (PackageSetting) pkg.mExtras;final PermissionsState permissionsState = ps.getPermissionsState();final int flags = permissionsState.getPermissionFlags(permName, userId);if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {Log.e(TAG, "Cannot grant system fixed permission "+ permName + " for package " + packageName);return;}if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {Log.e(TAG, "Cannot grant policy fixed permission "+ permName + " for package " + packageName);return;}if (bp.isHardRestricted()&& (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {Log.e(TAG, "Cannot grant hard restricted non-exempt permission "+ permName + " for package " + packageName);return;}if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,pkg.applicationInfo, UserHandle.of(userId), permName).canBeGranted()) {Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "+ packageName);return;}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) !=PERMISSION_OPERATION_FAILURE) {if (callback != null) {callback.onInstallPermissionGranted();}}return;}if (ps.getInstantApp(userId) && !bp.isInstant()) {throw new SecurityException("Cannot grant non-ephemeral permission"+ permName + " for package " + packageName);}if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {Slog.w(TAG, "Cannot grant runtime permission to a legacy app");return;}final int result = permissionsState.grantRuntimePermission(bp, userId);switch (result) {case PERMISSION_OPERATION_FAILURE: {return;}case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {if (callback != null) {callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);}}break;}if (bp.isRuntime()) {logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName);}if (callback != null) {callback.onPermissionGranted(uid, userId);}if (bp.isRuntime()) {notifyRuntimePermissionStateChanged(packageName, userId);}// 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(permName)|| WRITE_EXTERNAL_STORAGE.equals(permName)) {final long token = Binder.clearCallingIdentity();try {if (mUserManagerInt.isUserInitialized(userId)) {StorageManagerInternal storageManagerInternal = LocalServices.getService(StorageManagerInternal.class);storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);}} finally {Binder.restoreCallingIdentity(token);}}}

2.特定shareUID属性

在PMS的构造方法中,对sharedUserId和UID进行了映射,最终通过PMS来完成:解析应用信息、各组件信息、权限信息,分配UID、记录组件信息,更新、授予、保存权限信息。

//frameworks/base/services/core/java/com/android/server/pm/PackageManagerService.javapublic PackageManagerService(Context context, Installer installer,boolean factoryTest, boolean onlyCore) {// 省略部分代码mSettings.addSharedUserLPw("android.uid.system", Process.SYSTEM_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.phone", RADIO_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.log", LOG_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.nfc", NFC_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.bluetooth", BLUETOOTH_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.shell", SHELL_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.se", SE_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);mSettings.addSharedUserLPw("android.uid.networkstack", NETWORKSTACK_UID,ApplicationInfo.FLAG_SYSTEM, ApplicationInfo.PRIVATE_FLAG_PRIVILEGED);// 省略部分代码
}

3.default permission配置

DefaultPermissionGrantPolicy.grantDefaultPermissions(...):最终也是通过PMS来完成默认权限的管理,在经过一些列判断后,调用关键方法grantRuntimePermission(...)和updatePermissionFlags(...)。

//frameworks/base/services/core/java/com/android/server/pm/permission/DefaultPermissionGrantPolicy.javapublic void grantDefaultPermissions(int userId) {grantPermissionsToSysComponentsAndPrivApps(userId);grantDefaultSystemHandlerPermissions(userId);grantDefaultPermissionExceptions(userId);synchronized (mLock) {mDefaultPermissionsGrantedUsers.put(userId, userId);}
}private void grantRuntimePermissions(PackageInfo pkg, Set<String> permissionsWithoutSplits,boolean systemFixed, boolean ignoreSystemPackage,boolean whitelistRestrictedPermissions, int userId) {UserHandle user = UserHandle.of(userId);if (pkg == null) {return;}String[] requestedPermissions = pkg.requestedPermissions;if (ArrayUtils.isEmpty(requestedPermissions)) {return;}// Intersect the requestedPermissions for a factory image with that of its current update// in case the latter one removed a <uses-permission>String[] requestedByNonSystemPackage = getPackageInfo(pkg.packageName).requestedPermissions;int size = requestedPermissions.length;for (int i = 0; i < size; i++) {if (!ArrayUtils.contains(requestedByNonSystemPackage, requestedPermissions[i])) {requestedPermissions[i] = null;}}requestedPermissions = ArrayUtils.filterNotNull(requestedPermissions, String[]::new);PackageManager pm;try {pm = mContext.createPackageContextAsUser(mContext.getPackageName(), 0,user).getPackageManager();} catch (NameNotFoundException doesNotHappen) {throw new IllegalStateException(doesNotHappen);}final ArraySet<String> permissions = new ArraySet<>(permissionsWithoutSplits);ApplicationInfo applicationInfo = pkg.applicationInfo;int newFlags = PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;if (systemFixed) {newFlags |= PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;}// Automatically attempt to grant split permissions to older APKsfinal List<PermissionManager.SplitPermissionInfo> splitPermissions =mContext.getSystemService(PermissionManager.class).getSplitPermissions();final int numSplitPerms = splitPermissions.size();for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {final PermissionManager.SplitPermissionInfo splitPerm =splitPermissions.get(splitPermNum);if (applicationInfo != null&& applicationInfo.targetSdkVersion < splitPerm.getTargetSdk()&& permissionsWithoutSplits.contains(splitPerm.getSplitPermission())) {permissions.addAll(splitPerm.getNewPermissions());}}Set<String> grantablePermissions = null;// In some cases, like for the Phone or SMS app, we grant permissions regardless// of if the version on the system image declares the permission as used since// selecting the app as the default for that function the user makes a deliberate// choice to grant this app the permissions needed to function. For all other// apps, (default grants on first boot and user creation) we don't grant default// permissions if the version on the system image does not declare them.if (!ignoreSystemPackage&& applicationInfo != null&& applicationInfo.isUpdatedSystemApp()) {final PackageInfo disabledPkg = getSystemPackageInfo(mServiceInternal.getDisabledSystemPackageName(pkg.packageName));if (disabledPkg != null) {if (ArrayUtils.isEmpty(disabledPkg.requestedPermissions)) {return;}if (!Arrays.equals(requestedPermissions, disabledPkg.requestedPermissions)) {grantablePermissions = new ArraySet<>(Arrays.asList(requestedPermissions));requestedPermissions = disabledPkg.requestedPermissions;}}}final int numRequestedPermissions = requestedPermissions.length;// Sort requested permissions so that all permissions that are a foreground permission (i.e.// permissions that have a background permission) are before their background permissions.final String[] sortedRequestedPermissions = new String[numRequestedPermissions];int numForeground = 0;int numOther = 0;for (int i = 0; i < numRequestedPermissions; i++) {String permission = requestedPermissions[i];if (getBackgroundPermission(permission) != null) {sortedRequestedPermissions[numForeground] = permission;numForeground++;} else {sortedRequestedPermissions[numRequestedPermissions - 1 - numOther] =permission;numOther++;}}for (int requestedPermissionNum = 0; requestedPermissionNum < numRequestedPermissions;requestedPermissionNum++) {String permission = requestedPermissions[requestedPermissionNum];// If there is a disabled system app it may request a permission the updated// version ot the data partition doesn't, In this case skip the permission.if (grantablePermissions != null && !grantablePermissions.contains(permission)) {continue;}if (permissions.contains(permission)) {final int flags = mContext.getPackageManager().getPermissionFlags(permission, pkg.packageName, user);// If we are trying to grant as system fixed and already system fixed// then the system can change the system fixed grant state.final boolean changingGrantForSystemFixed = systemFixed&& (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;// Certain flags imply that the permission's current state by the system or// device/profile owner or the user. In these cases we do not want to clobber the// current state.//// Unless the caller wants to override user choices. The override is// to make sure we can grant the needed permission to the default// sms and phone apps after the user chooses this in the UI.if (!isFixedOrUserSet(flags) || ignoreSystemPackage|| changingGrantForSystemFixed) {// Never clobber policy fixed permissions.// We must allow the grant of a system-fixed permission because// system-fixed is sticky, but the permission itself may be revoked.if ((flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {continue;}// Preserve whitelisting flags.newFlags |= (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT);// If we are whitelisting the permission, update the exempt flag before grant.if (whitelistRestrictedPermissions && isPermissionRestricted(permission)) {mContext.getPackageManager().updatePermissionFlags(permission,pkg.packageName,PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT,PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT, user);}// If the system tries to change a system fixed permission from one fixed// state to another we need to drop the fixed flag to allow the grant.if (changingGrantForSystemFixed) {mContext.getPackageManager().updatePermissionFlags(permission,pkg.packageName, flags,flags & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, user);}if (pm.checkPermission(permission, pkg.packageName)!= PackageManager.PERMISSION_GRANTED) {mContext.getPackageManager().grantRuntimePermission(pkg.packageName, permission, user);}mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,newFlags, newFlags, user);int uid = UserHandle.getUid(userId,UserHandle.getAppId(pkg.applicationInfo.uid));List<String> fgPerms = mPermissionManager.getBackgroundPermissions().get(permission);if (fgPerms != null) {int numFgPerms = fgPerms.size();for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) {String fgPerm = fgPerms.get(fgPermNum);if (pm.checkPermission(fgPerm, pkg.packageName)== PackageManager.PERMISSION_GRANTED) {// Upgrade the app-op state of the fg permission to allow bg access// TODO: Dont' call app ops from package manager code.mContext.getSystemService(AppOpsManager.class).setUidMode(AppOpsManager.permissionToOp(fgPerm), uid,AppOpsManager.MODE_ALLOWED);break;}}}String bgPerm = getBackgroundPermission(permission);String op = AppOpsManager.permissionToOp(permission);if (bgPerm == null) {if (op != null) {// TODO: Dont' call app ops from package manager code.mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid,AppOpsManager.MODE_ALLOWED);}} else {int mode;if (pm.checkPermission(bgPerm, pkg.packageName)== PackageManager.PERMISSION_GRANTED) {mode = AppOpsManager.MODE_ALLOWED;} else {mode = AppOpsManager.MODE_FOREGROUND;}mContext.getSystemService(AppOpsManager.class).setUidMode(op, uid, mode);}if (DEBUG) {Log.i(TAG, "Granted " + (systemFixed ? "fixed " : "not fixed ")+ permission + " to default handler " + pkg);int appOp = AppOpsManager.permissionToOpCode(permission);if (appOp != AppOpsManager.OP_NONE&& AppOpsManager.opToDefaultMode(appOp)!= AppOpsManager.MODE_ALLOWED) {// Permission has a corresponding appop which is not allowed by default// We must allow it as well, as it's usually checked alongside the// permissionif (DEBUG) {Log.i(TAG, "Granting OP_" + AppOpsManager.opToName(appOp)+ " to " + pkg.packageName);}mContext.getSystemService(AppOpsManager.class).setUidMode(appOp, pkg.applicationInfo.uid, AppOpsManager.MODE_ALLOWED);}}}// If a component gets a permission for being the default handler A// and also default handler B, we grant the weaker grant form.if ((flags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0&& (flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0&& !systemFixed) {if (DEBUG) {Log.i(TAG, "Granted not fixed " + permission + " to default handler "+ pkg);}mContext.getPackageManager().updatePermissionFlags(permission, pkg.packageName,PackageManager.FLAG_PERMISSION_SYSTEM_FIXED, 0, user);}}}
}

参考:

Android 权限的一些细节

深入浅析Android动态权限的机制

Android O 默认授予预制应用运行时权限方法

PackageManagerService处理应用权限流程

Android App 授权机制简析相关推荐

  1. Android开机启动流程简析

    Android开机启动流程简析 (一) 文章目录 Android开机启动流程简析 (一) 前言 一.开机启动的流程概述 二.Android的启动过程分析 (1).总体流程 init简述 Zygote简 ...

  2. 关于php车服务论文,「PHP」行车服务app后端代码简析

    之前发布了一篇关于我的 行车服务 app iOS 端代码简析的文章:文章地址. 此篇是对这个项目后端 iOS端代码地址: iOS代码,PHP代码.如果你觉得有帮助,希望能够点个 Star ,感谢~ 笔 ...

  3. Linux I2C设备regmap机制简析

    在Linu 3.1开始,Linux引入了regmap来同意管理内核的I2C, SPI等总线,将I2C, SPI驱动做了一次重构,把I/O读写的重复逻辑在regmap中实现. 用一个I2C设备为例,在3 ...

  4. 超千万注册用户APP技术实现简析-android《洋葱数学》

    洋葱数学 零,前言 洋葱数学完成超1.2亿元C轮融资 这个产品今年年初刚融资一个多亿,估值几亿人民币,因此想看看他们程序员水平怎么样,学习借鉴一下 一,通过反编译apk以及使用chalres抓包工具, ...

  5. Android热修复-Tinker简析

    一.简介 日常工作工作中难免会遇到项目上线后出现bug问题,如果紧急发版往往由于渠道审核时间问题,导致bug修复不及时,影响用户体验.这时我们需要引入热修复,免去发版审核烦恼. 热更新优势: 让应用能 ...

  6. android app唤醒机制,Android平台下APP唤醒机制

    方案一可以使用以下1.2两种方法实现,方案二可以使用以下方法3实现.(部分转载) 1.使用隐匿intent的方式调用,比较简单 首先在自己应用的Mainifest.xml中加入如下代码: androi ...

  7. android camera 拍照流程,Android -- Camera源码简析,启动流程

    com.android.camera.Camera.java,主要的实现Activity,继承于ActivityBase. ActivityBase 在ActivityBase中执行流程: onCre ...

  8. android之descendantFocusability用法简析

    2019独角兽企业重金招聘Python工程师标准>>> listView的Item被抢焦点,这是开发中很常见的一个问题,项目中的listview不仅仅是简单的文字,常常需要自己定义l ...

  9. Android短信数据库简析

    如果想跳过数据库介绍,直接看数据库操作代码的话,请点击这里: 读取Android短信 -------------– Android短信数据库: 读取Android系统所有短信 读取Android短信会 ...

  10. 《传送门》游戏中传送机制简析

    相信大家对于传送门应该在电影或者游戏看过很多了,无缝传送门这个机制也不是Value公司第一次在游戏中实现,早已经有很多游戏实现过,比如<影子武士>.<雷神之锤>等等都使用了各种 ...

最新文章

  1. Merge Sorted Array
  2. jquery post 同步异步总结
  3. getbean方法找不到bean_iphone手机静音找不到怎么办 iphone静音找不到解决方法【图文】...
  4. Java异常处理001:Maven clean package时Failed to clean project: Failed to delete
  5. Bootstrap框架学习
  6. css媒体查询和居中
  7. C# Datatable的Select方法
  8. C语言程序设计:图书管理系统(附代码)
  9. windows系统上PrtSc,ScroLk,Pause等三个功能按钮的作用
  10. cad隐藏图层命令快捷键_CAD中,有没有快速隐藏其他图层的快捷键啊? 新上
  11. MessagingTimeout: Timed out waiting for a reply to message ID
  12. mcrpg职业系统服务器,我的世界MC-梦想大陆RPG服务器1.7.10
  13. CTS测试中testCameraOrientationAlignedWithDevice项
  14. 通俗解释Docker是什么
  15. 深度学习中梯度消失和梯度爆炸的根本原因及其缓解方法
  16. 手把手教你实战开发黑白棋实时对战游戏
  17. 双11阿里数据中心,把机器当“媳妇”的守夜者
  18. 十六届全向组硬件开源
  19. 英伟达斥巨资“聘礼”收购ARM,半导体行业将变天?
  20. 什么是内存屏障?为什么需要内存屏障?

热门文章

  1. 缓解疲劳、舒缓全身放松,游养乐分享养生小秘籍
  2. python 小说爬虫_初次尝试python爬虫,爬取小说网站的小说。
  3. 谷歌colab平台简单使用及读取自己的数据集
  4. [日常] NOI2019 退役记
  5. android 访问win10共享文件夹,win10系统用手机es文件浏览器访问共享文件的操作方法...
  6. 战略规划,要这么做才对!
  7. 【原创】Python 使用jmpy模块加密|加固 python代码
  8. 三大语言(C/Java/Python)基本数据类型大小 / 内置容器 总结
  9. python基础语法结构图(简单明了)
  10. 【GDB】__stack_chk_fail 栈溢出问题定位