android权限管理分为两种:
SDK<23:在manifest中声明,安装时赋予所有声明权限,不同意则不安装;
SDK>=23:
1、普通权限、声明即可直接赋予,不会在设置中显示给用户;
2、危险权限和特殊权限,需要声明且发送请求用户授权的intent,可自由开关每个权限;
系统中的定义权限位置在:
framework/base/core/res/AndroidManifest.xml
framework/base/data/etc/platform.xml
一、权限级别(ProtectionLevel)
1.1 Normal
android:protectionLevel="normal"
对用户隐私或者安全都不会带来影响
1.2 Dangerous
android:protectionLevel="dangerous"
需要在mainifest中声明且发送请求用户授权的intent,赋予某个组中的其中一个权限,自动赋予组内其他所有权限;
可用命令adb shell pm list permissions -d -g查看
1.3 Signature
android:protectionLevel="signature"
两类应用可使用
1.只有和定义了这个权限的apk用相同的私钥签名的应用才可以申请该权限
2.与系统签名相同的system app,即与厂商签名(厂商ROM中的系统app签名)相同的app
1.4 SignatureOrSystem
android:protectionLevel="signature|privileged"
三类应用可使用
1.只有和定义了这个权限的apk用相同的私钥签名的应用才可以申请该权限
2.与系统签名相同的app,即与厂商签名(厂商ROM中的系统app签名)相同的app
3.任意app只要标记了privileged(可暂理解为放到了/system/priv-app)就可以使用signatureOrSystem级别的权限
1.5 install权限和runtime权限
install权限:
安装时权限,是指在安装app的时候,赋予app的权限。normal和signature级别(包括SignatureOrSystem)的权限都是安装时权限。不会给用户提示界面,系统自动决定权限的赋予或拒绝。
runtime权限:
运行时权限,是指在app运行过程中,赋予app的权限。这个过程中,会显示明显的权限授予界面,让用户决定是否授予权限。dangerous权限就是运行时权限。(以下主要针对SDK<23时dangerous权限就变成了install权限)
二、app种类
1、system app (有ApplicationInfo.FLAG_SYSTEM标记)
2、privileged app (有ApplicationInfo.FLAG_SYSTEM和ApplicationInfo.PRIVATE_FLAG_PRIVILEGE两个标记)
2.1 system app
system app 定义很明了,就是在PMS初始化安装app的时候赋予了ApplicationInfo.FLAG_SYSTEM这个标记
1、特定shareUID的app
代码在PMS的构造函数中
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);
2、扫描安装特定目录的app
代码在PMS的构造函数中,扫描安装时给予PackageParser.PARSE_IS_SYSTEM标记的app,如/vendor/overlay,/system/framework,/system/priv-app,/system/app,/vendor/app,/oem/app等,给予的PackageParser.PARSE_IS_SYSTEM最终会转换为ApplicationInfo.FLAG_SYSTEM,部分代码如下
File vendorOverlayDir = new File(VENDOR_OVERLAY_DIR); scanDirTracedLI(vendorOverlayDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_TRUSTED_OVERLAY, scanFlags | SCAN_TRUSTED_OVERLAY, 0); File customFrameworkDir = new File("/custom/framework"); scanDirLI(customFrameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_NO_DEX, 0); scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags | SCAN_NO_DEX, 0);
flag转换的过程大致如下
-> scanDirLI(File dir, final int parseFlags, int scanFlags, long currentTime) -> scanPackageTracedLI(PackageParser.Package pkg, final int policyFlags, int scanFlags, long currentTime, UserHandle user) -> scanPackageLI(PackageParser.Package pkg, final int policyFlags, int scanFlags, long currentTime, UserHandle user) -> scanPackageDirtyLI(PackageParser.Package pkg, final int policyFlags, final int scanFlags, long currentTime, UserHandle user) //在scanPackageDirtyLI方法中将flag转换 // Apply policy if ((policyFlags&PackageParser.PARSE_IS_SYSTEM) != 0) { pkg.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM; ... }
2.2 privileged app
privileged app在ApplicationInfo.FLAG_SYSTEM基础上还必须有ApplicationInfo.PRIVATE_FLAG_PRIVILEGED标记
1、特定shareUID的app
特定shareUID的app有ApplicationInfo.FLAG_SYSTEM的同时都有ApplicationInfo.PRIVATE_FLAG_PRIVILEGED
2、扫描特定目录app时,给予了PackageParser.PARSE_IS_SYSTEM标记和PackageParser.PARSE_IS_PRIVILEGED标记,目录有三个:system/framework,system/priv-app,vendor/priv-app
例如:
//vender/framework目录下有PackageParser.PARSE_IS_SYSTEM没有PackageParser.PARSE_IS_PRIVILEGED标记 File vendorFrameworkDir = new File(Environment.getVendorDirectory(), "framework"); scanDirTracedLI(vendorFrameworkDir, PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR, scanFlags | SCAN_NO_DEX, 0); //system/framework目录下两个标记都有 scanDirTracedLI(frameworkDir, mDefParseFlags | PackageParser.PARSE_IS_SYSTEM | PackageParser.PARSE_IS_SYSTEM_DIR | PackageParser.PARSE_IS_PRIVILEGED, scanFlags | SCAN_NO_DEX, 0);
PackageParser.PARSE_IS_PRIVILEGED也是在scanPackageDirtyLI方法中转换的
if ((policyFlags&PackageParser.PARSE_IS_PRIVILEGED) != 0) { pkg.applicationInfo.privateFlags |= ApplicationInfo.PRIVATE_FLAG_PRIVILEGED; }
Android 12 系统存在permissioncontroller的apk,用来管理权限
涉及 PackageManagerService 和 PermissionManagerService
app源码路径为packages/modules/Permission/PermissionController/src/com/android/permissioncontroller/permission/
AppPermissions:是权限的实体类
packages\modules\Permission\PermissionController\src\com\android\permissioncontroller\permission\model\AppPermissions.java
其中:AppPermissionGroup用于管理权限的授予与收回
packages\modules\Permission\PermissionController\src\com\android\permissioncontroller\permission\model\AppPermissionGroup.java
收回权限:
public boolean revokeRuntimePermissions(boolean fixedByTheUser) { return revokeRuntimePermissions(fixedByTheUser, null); }
申请权限:
public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser) { return grantRuntimePermissions(setByTheUser, fixedByTheUser, null); }
/** * Grant permissions of the group. * * <p>This also automatically grants all app ops for permissions that have app ops. * <p>This does <u>only</u> grant permissions in {@link #mPermissions}, i.e. usually not * the background permissions. * * @param setByTheUser If the user has made the decision. This does not unset the flag * @param fixedByTheUser If the user requested that she/he does not want to be asked again * @param filterPermissions If {@code null} all permissions of the group will be granted. * Otherwise only permissions in {@code filterPermissions} will be * granted. * * @return {@code true} iff all permissions of this group could be granted. */ public boolean grantRuntimePermissions(boolean setByTheUser, boolean fixedByTheUser, String[] filterPermissions) { boolean killApp = false; boolean wasAllGranted = true; // 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; } if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) { // Skip unallowed permissions. continue; } boolean wasGranted = permission.isGrantedIncludingAppOp(); if (mAppSupportsRuntimePermissions) { // Do not touch permissions fixed by the system. if (permission.isSystemFixed()) { wasAllGranted = false; break; } // Ensure the permission app op is enabled before the permission grant. if (permission.affectsAppOp() && !permission.isAppOpAllowed()) { permission.setAppOpAllowed(true); } // Grant the permission if needed. if (!permission.isGranted()) { permission.setGranted(true); } // Update the permission flags. if (!fixedByTheUser) { if (permission.isUserFixed()) { permission.setUserFixed(false); } if (setByTheUser) { if (!permission.isUserSet()) { permission.setUserSet(true); } } } else { if (!permission.isUserFixed()) { permission.setUserFixed(true); } if (permission.isUserSet()) { permission.setUserSet(false); } } } else { // Legacy apps cannot have a not granted permission but just in case. if (!permission.isGranted()) { continue; } // If the permissions has no corresponding app op, then it is a // third-party one and we do not offer toggling of such permissions. if (permission.affectsAppOp()) { if (!permission.isAppOpAllowed()) { permission.setAppOpAllowed(true); // Legacy apps do not know that they have to retry access to a // resource due to changes in runtime permissions (app ops in this // case). Therefore, we restart them on app op change, so they // can pick up the change. killApp = true; } // Mark that the permission is not kept granted only for compatibility. if (permission.isRevokedCompat()) { permission.setRevokedCompat(false); } } // Granting a permission explicitly means the user already // reviewed it so clear the review flag on every grant. if (permission.isReviewRequired()) { permission.unsetReviewRequired(); } } // If we newly grant background access to the fine location, double-guess the user some // time later if this was really the right choice. if (!wasGranted && permission.isGrantedIncludingAppOp()) { if (permission.getName().equals(ACCESS_FINE_LOCATION)) { Permission bgPerm = permission.getBackgroundPermission(); if (bgPerm != null) { if (bgPerm.isGrantedIncludingAppOp()) { mTriggerLocationAccessCheckOnPersist = true; } } } else if (permission.getName().equals(ACCESS_BACKGROUND_LOCATION)) { ArrayList<Permission> fgPerms = permission.getForegroundPermissions(); if (fgPerms != null) { int numFgPerms = fgPerms.size(); for (int fgPermNum = 0; fgPermNum < numFgPerms; fgPermNum++) { Permission fgPerm = fgPerms.get(fgPermNum); if (fgPerm.getName().equals(ACCESS_FINE_LOCATION)) { if (fgPerm.isGrantedIncludingAppOp()) { mTriggerLocationAccessCheckOnPersist = true; } break; } } } } } } if (!mDelayChanges) { persistChanges(false); if (killApp) { killApp(KILL_REASON_APP_OP_CHANGE); } } return wasAllGranted; }
通过调用persistChanges(false);方法 来调用PMS更新权限
/** * If the changes to this group were delayed, persist them to the platform. * * @param mayKillBecauseOfAppOpsChange If the app these permissions belong to may be killed if * app ops change. If this is set to {@code false} the * caller has to make sure to kill the app if needed. * @param revokeReason If any permissions are getting revoked, the reason for revoking them. */ public void persistChanges(boolean mayKillBecauseOfAppOpsChange, String revokeReason) { int uid = mPackageInfo.applicationInfo.uid; int numPermissions = mPermissions.size(); boolean shouldKillApp = false; for (int i = 0; i < numPermissions; i++) { Permission permission = mPermissions.valueAt(i); if (!permission.isSystemFixed()) { if (permission.isGranted()) { mPackageManager.grantRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } else { boolean isCurrentlyGranted = mContext.checkPermission(permission.getName(), -1, uid) == PERMISSION_GRANTED; if (isCurrentlyGranted) { if (revokeReason == null) { mPackageManager.revokeRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle); } else { mPackageManager.revokeRuntimePermission(mPackageInfo.packageName, permission.getName(), mUserHandle, revokeReason); } } } } int flags = (permission.isUserSet() ? PackageManager.FLAG_PERMISSION_USER_SET : 0) | (permission.isUserFixed() ? PackageManager.FLAG_PERMISSION_USER_FIXED : 0) | (permission.isRevokedCompat() ? PackageManager.FLAG_PERMISSION_REVOKED_COMPAT : 0) | (permission.isPolicyFixed() ? PackageManager.FLAG_PERMISSION_POLICY_FIXED : 0) | (permission.isReviewRequired() ? PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED : 0) | (permission.isOneTime() ? PackageManager.FLAG_PERMISSION_ONE_TIME : 0); mPackageManager.updatePermissionFlags(permission.getName(), mPackageInfo.packageName, PackageManager.FLAG_PERMISSION_USER_SET | PackageManager.FLAG_PERMISSION_USER_FIXED | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT | PackageManager.FLAG_PERMISSION_POLICY_FIXED | (permission.isReviewRequired() ? 0 : PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) | PackageManager.FLAG_PERMISSION_ONE_TIME | PackageManager.FLAG_PERMISSION_AUTO_REVOKED, // clear auto revoke flags, mUserHandle); if (permission.affectsAppOp()) { if (!permission.isSystemFixed()) { // Enabling/Disabling an app op may put the app in a situation in which it has // a handle to state it shouldn't have, so we have to kill the app. This matches // the revoke runtime permission behavior. if (permission.isAppOpAllowed()) { boolean wasChanged = allowAppOp(permission, uid); shouldKillApp |= wasChanged && !mAppSupportsRuntimePermissions; } else { shouldKillApp |= disallowAppOp(permission, uid); } } } } if (mayKillBecauseOfAppOpsChange && shouldKillApp) { killApp(KILL_REASON_APP_OP_CHANGE); } if (mTriggerLocationAccessCheckOnPersist) { new LocationAccessCheck(mContext, null).checkLocationAccessSoon(); mTriggerLocationAccessCheckOnPersist = false; } String packageName = mPackageInfo.packageName; if (isOneTime() && areRuntimePermissionsGranted()) { mContext.getSystemService(PermissionManager.class) .startOneTimePermissionSession(packageName, Utils.getOneTimePermissionsTimeout(), ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_RESET_TIMER, ONE_TIME_PACKAGE_IMPORTANCE_LEVEL_TO_KEEP_SESSION_ALIVE); } else if (!Utils.hasOneTimePermissions(mContext, packageName)) { mContext.getSystemService(PermissionManager.class) .stopOneTimePermissionSession(packageName); } }
PackageManager是抽象类,实现类为PackageManagerService
frameworks\base\services\core\java\com\android\server\pm\PackageManagerService.java
// NOTE: Can't remove due to unsupported app usage @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { // Because this is accessed via the package manager service AIDL, // go through the permission manager service AIDL mContext.getSystemService(PermissionManager.class) .grantRuntimePermission(packageName, permName, UserHandle.of(userId)); }
frameworks\base\core\java\android\permission\PermissionManager.java
@RequiresPermission(android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS) //@SystemApi public void grantRuntimePermission(@NonNull String packageName, @NonNull String permissionName, @NonNull UserHandle user) { if (DEBUG_TRACE_GRANTS && shouldTraceGrant(packageName, permissionName, user.getIdentifier())) { Log.i(LOG_TAG_TRACE_GRANTS, "App " + mContext.getPackageName() + " is granting " + packageName + " " + permissionName + " for user " + user.getIdentifier(), new RuntimeException()); } try { mPermissionManager.grantRuntimePermission(packageName, permissionName, user.getIdentifier()); } catch (RemoteException e) { throw e.rethrowFromSystemServer(); } }
frameworks\base\services\core\java\com\android\server\pm\permission\PermissionManagerService.java
@Override public void grantRuntimePermission(String packageName, String permName, final int userId) { final int callingUid = Binder.getCallingUid(); final boolean overridePolicy = checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) == PackageManager.PERMISSION_GRANTED; grantRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId, mDefaultPermissionCallback); } private void grantRuntimePermissionInternal(String packageName, String permName, boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) { if (PermissionManager.DEBUG_TRACE_GRANTS && PermissionManager.shouldTraceGrant(packageName, permName, userId)) { Log.i(PermissionManager.LOG_TAG_TRACE_GRANTS, "System is granting " + packageName + " " + permName + " for user " + userId + " on behalf of uid " + callingUid + " " + mPackageManagerInt.getNameForUid(callingUid), new RuntimeException()); } 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, // requireFullPermission true, // checkShell "grantRuntimePermission"); final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); final PackageSetting ps = mPackageManagerInt.getPackageSetting(packageName); if (pkg == null || ps == null) { Log.e(TAG, "Unknown package: " + packageName); return; } if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } final boolean isRolePermission; final boolean isSoftRestrictedPermission; synchronized (mLock) { final Permission permission = mRegistry.getPermission(permName); if (permission == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } isRolePermission = permission.isRole(); isSoftRestrictedPermission = permission.isSoftRestricted(); } final boolean mayGrantRolePermission = isRolePermission && mayManageRolePermission(callingUid); final boolean mayGrantSoftRestrictedPermission = isSoftRestrictedPermission && SoftRestrictedPermissionPolicy.forPermission(mContext, pkg.toAppInfoWithoutState(), pkg, UserHandle.of(userId), permName) .mayGrantPermission(); final boolean isRuntimePermission; final boolean permissionHasGids; synchronized (mLock) { final Permission bp = mRegistry.getPermission(permName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } isRuntimePermission = bp.isRuntime(); permissionHasGids = bp.hasGids(); if (isRuntimePermission || bp.isDevelopment()) { // Good. } else if (bp.isRole()) { if (!mayGrantRolePermission) { throw new SecurityException("Permission " + permName + " is managed by role"); } } else { throw new SecurityException("Permission " + permName + " requested by " + pkg.getPackageName() + " is not a changeable permission type"); } final UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + userId); return; } if (!(uidState.hasPermissionState(permName) || pkg.getRequestedPermissions().contains(permName))) { throw new SecurityException("Package " + pkg.getPackageName() + " has not requested permission " + permName); } // 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.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) { return; } final int flags = uidState.getPermissionFlags(permName); 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() && !mayGrantSoftRestrictedPermission) { Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package " + packageName); return; } if (bp.isDevelopment() || bp.isRole()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. // TODO(zhanghai): We are breaking the behavior above by making all permission state // per-user. It isn't documented behavior and relatively rarely used anyway. if (!uidState.grantPermission(bp)) { return; } } else { if (ps.getInstantApp(userId) && !bp.isInstant()) { throw new SecurityException("Cannot grant non-ephemeral permission" + permName + " for package " + packageName); } if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) { Slog.w(TAG, "Cannot grant runtime permission to a legacy app"); return; } if (!uidState.grantPermission(bp)) { return; } } } if (isRuntimePermission) { logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName); } final int uid = UserHandle.getUid(userId, pkg.getUid()); if (callback != null) { if (isRuntimePermission) { callback.onPermissionGranted(uid, userId); } else { callback.onInstallPermissionGranted(); } if (permissionHasGids) { callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId); } } // PermissionsChanged监听 if (isRuntimePermission) { notifyRuntimePermissionStateChanged(packageName, userId); } }
private void notifyRuntimePermissionStateChanged(@NonNull String packageName, @UserIdInt int userId) { FgThread.getHandler().sendMessage(PooledLambda.obtainMessage (PermissionManagerService::doNotifyRuntimePermissionStateChanged, PermissionManagerService.this, packageName, userId)); } private void doNotifyRuntimePermissionStateChanged(@NonNull String packageName, @UserIdInt int userId) { final ArrayList<OnRuntimePermissionStateChangedListener> listeners; synchronized (mLock) { if (mRuntimePermissionStateChangedListeners.isEmpty()) { return; } listeners = new ArrayList<>(mRuntimePermissionStateChangedListeners); } final int listenerCount = listeners.size(); for (int i = 0; i < listenerCount; i++) { listeners.get(i).onRuntimePermissionStateChanged(packageName, userId); } }
最后grantRuntimePermission就是把permission存到mPermissions数据map中,再把数据跟新到/data/system/users/0/runtime-permissions.xml中