Android system实战 — Android R(11) 第三方apk权限
/**
* Restore the permission state for a package.
*
* <ul>
* <li>During boot the state gets restored from the disk</li>
* <li>During app update the state gets restored from the last version of the app</li>
* </ul>
*
* <p>This restores the permission state for all users.
*
* @param pkg the package the permissions belong to
* @param replace if the package is getting replaced (this might change the requested
* permissions of this package)
* @param packageOfInterest If this is the name of {@code pkg} add extra logging
* @param callback Result call back
*/
private void restorePermissionState(@NonNull AndroidPackage pkg, boolean replace,
@Nullable String packageOfInterest, @Nullable PermissionCallback callback) {
// IMPORTANT: There are two types of permissions: install and runtime.
// Install time permissions are granted when the app is installed to
// all device users and users added in the future. Runtime permissions
// are granted at runtime explicitly to specific users. Normal and signature
// protected permissions are install time permissions. Dangerous permissions
// are install permissions if the app's target SDK is Lollipop MR1 or older,
// otherwise they are runtime permissions. This function does not manage
// runtime permissions except for the case an app targeting Lollipop MR1
// being upgraded to target a newer SDK, in which case dangerous permissions
// are transformed from install time to runtime ones.
final PackageSetting ps = (PackageSetting) mPackageManagerInt.getPackageSetting(
pkg.getPackageName());
if (ps == null) {
return;
}
final PermissionsState permissionsState = ps.getPermissionsState();
final int[] userIds = getAllUserIds();
boolean runtimePermissionsRevoked = false;
int[] updatedUserIds = EMPTY_INT_ARRAY;
for (int userId : userIds) {
if (permissionsState.isMissing(userId)) {
Collection<String> requestedPermissions;
int targetSdkVersion;
if (!ps.isSharedUser()) {
requestedPermissions = pkg.getRequestedPermissions();
targetSdkVersion = pkg.getTargetSdkVersion();
} else {
requestedPermissions = new ArraySet<>();
targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT;
List<AndroidPackage> packages = ps.getSharedUser().getPackages();
int packagesSize = packages.size();
for (int i = 0; i < packagesSize; i++) {
AndroidPackage sharedUserPackage = packages.get(i);
requestedPermissions.addAll(sharedUserPackage.getRequestedPermissions());
targetSdkVersion = Math.min(targetSdkVersion,
sharedUserPackage.getTargetSdkVersion());
}
}
for (String permissionName : requestedPermissions) {
BasePermission permission = mSettings.getPermission(permissionName);
if (permission == null) {
continue;
}
if (Objects.equals(permission.getSourcePackageName(), PLATFORM_PACKAGE_NAME)
&& permission.isRuntime() && !permission.isRemoved()) {
if (permission.isHardOrSoftRestricted()
|| permission.isImmutablyRestricted()) {
permissionsState.updatePermissionFlags(permission, userId,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT,
FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT);
}
if (targetSdkVersion < Build.VERSION_CODES.M) {
permissionsState.updatePermissionFlags(permission, userId,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT,
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED
| PackageManager.FLAG_PERMISSION_REVOKED_COMPAT);
permissionsState.grantRuntimePermission(permission, userId);
}
}
}
permissionsState.setMissing(false, userId);
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
}
PermissionsState origPermissions = permissionsState;
boolean changedInstallPermission = false;
if (replace) {
ps.setInstallPermissionsFixed(false);
if (!ps.isSharedUser()) {
origPermissions = new PermissionsState(permissionsState);
permissionsState.reset();
} else {
// We need to know only about runtime permission changes since the
// calling code always writes the install permissions state but
// the runtime ones are written only if changed. The only cases of
// changed runtime permissions here are promotion of an install to
// runtime and revocation of a runtime from a shared user.
synchronized (mLock) {
updatedUserIds = revokeUnusedSharedUserPermissionsLocked(
ps.getSharedUser(), userIds);
if (!ArrayUtils.isEmpty(updatedUserIds)) {
runtimePermissionsRevoked = true;
}
}
}
}
permissionsState.setGlobalGids(mGlobalGids);
synchronized (mLock) {
ArraySet<String> newImplicitPermissions = new ArraySet<>();
final int N = pkg.getRequestedPermissions().size();
for (int i = 0; i < N; i++) {
final String permName = pkg.getRequestedPermissions().get(i);
final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")";
final BasePermission bp = mSettings.getPermissionLocked(permName);
final boolean appSupportsRuntimePermissions =
pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M;
String upgradedActivityRecognitionPermission = null;
if (DEBUG_INSTALL && bp != null) {
Log.i(TAG, "Package " + friendlyName
+ " checking " + permName + ": " + bp);
}
if (bp == null || getSourcePackageSetting(bp) == null) {
if (packageOfInterest == null || packageOfInterest.equals(
pkg.getPackageName())) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Unknown permission " + permName
+ " in package " + friendlyName);
}
}
continue;
}
// Cache newImplicitPermissions before modifing permissionsState as for the shared
// uids the original and new state are the same object
if (!origPermissions.hasRequestedPermission(permName)
&& (pkg.getImplicitPermissions().contains(permName)
|| (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
if (pkg.getImplicitPermissions().contains(permName)) {
// If permName is an implicit permission, try to auto-grant
newImplicitPermissions.add(permName);
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, permName + " is newly added for " + friendlyName);
}
} else {
// Special case for Activity Recognition permission. Even if AR permission
// is not an implicit permission we want to add it to the list (try to
// auto-grant it) if the app was installed on a device before AR permission
// was split, regardless of if the app now requests the new AR permission
// or has updated its target SDK and AR is no longer implicit to it.
// This is a compatibility workaround for apps when AR permission was
// split in Q.
final List<SplitPermissionInfoParcelable> permissionList =
getSplitPermissions();
int numSplitPerms = permissionList.size();
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
SplitPermissionInfoParcelable sp = permissionList.get(splitPermNum);
String splitPermName = sp.getSplitPermission();
if (sp.getNewPermissions().contains(permName)
&& origPermissions.hasInstallPermission(splitPermName)) {
upgradedActivityRecognitionPermission = splitPermName;
newImplicitPermissions.add(permName);
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, permName + " is newly added for " + friendlyName);
}
break;
}
}
}
}
// TODO(b/140256621): The package instant app method has been removed
// as part of work in b/135203078, so this has been commented out in the meantime
// Limit ephemeral apps to ephemeral allowed permissions.
// if (/*()*/ false && !()) {
// if (DEBUG_PERMISSIONS) {
// (TAG, "Denying non-ephemeral permission " + ()
// + " for package " + friendlyName);
// }
// continue;
// }
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
if (DEBUG_PERMISSIONS) {
Log.i(TAG, "Denying runtime-only permission " + bp.getName()
+ " for package " + friendlyName);
}
continue;
}
final String perm = bp.getName();
boolean allowedSig = false;
int grant = GRANT_DENIED;
// 检查permission是否特殊,针对不同级别permission赋予不同的grant
// Keep track of app op permissions.
if (bp.isAppOp()) {
mSettings.addAppOpPackage(perm, pkg.getPackageName());
}
if (bp.isNormal()) {
// For all apps normal permissions are install time ones.
grant = GRANT_INSTALL;
} else if (bp.isRuntime()) {
if (origPermissions.hasInstallPermission(bp.getName())
|| upgradedActivityRecognitionPermission != null) {
// Before Q we represented some runtime permissions as install permissions,
// in Q we cannot do this anymore. Hence upgrade them all.
grant = GRANT_UPGRADE;
} else {
// For modern apps keep runtime permissions unchanged.
grant = GRANT_RUNTIME;
}
} else if (bp.isSignature()) {
// For all apps signature permissions are install time ones.
allowedSig = grantSignaturePermission(perm, pkg, ps, bp, origPermissions);
if (allowedSig) {
grant = GRANT_INSTALL;
}
}
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Considering granting permission " + perm + " to package "
+ friendlyName + "grant " + grant);
}
if (grant != GRANT_DENIED) {
if (!ps.isSystem() && ps.areInstallPermissionsFixed() && !bp.isRuntime()) {
// If this is an existing, non-system package, then
// we can't add any new permissions to it. Runtime
// permissions can be added any time - they ad dynamic.
if (!allowedSig && !origPermissions.hasInstallPermission(perm)) {
// Except... if this is a permission that was added
// to the platform (note: need to only do this when
// updating the platform).
if (!isNewPlatformPermissionForPackage(perm, pkg)) {
grant = GRANT_DENIED;
}
}
}
switch (grant) {
case GRANT_INSTALL: {
// Revoke this as runtime permission to handle the case of
// a runtime permission being downgraded to an install one.
// Also in permission review mode we keep dangerous permissions
// for legacy apps
for (int userId : userIds) {
if (origPermissions.getRuntimePermissionState(
perm, userId) != null) {
// Revoke the runtime permission and clear the flags.
origPermissions.revokeRuntimePermission(bp, userId);
origPermissions.updatePermissionFlags(bp, userId,
PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
// If we revoked a permission permission, we have to write.
updatedUserIds = ArrayUtils.appendInt(
updatedUserIds, userId);
}
}
// Grant an install permission.
if (permissionsState.grantInstallPermission(bp) !=
PERMISSION_OPERATION_FAILURE) {
changedInstallPermission = true;
}
} break;
case GRANT_RUNTIME: {
boolean hardRestricted = bp.isHardRestricted();
boolean softRestricted = bp.isSoftRestricted();
for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
final boolean permissionPolicyInitialized =
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
PermissionState permState = origPermissions
.getRuntimePermissionState(perm, userId);
int flags = permState != null ? permState.getFlags() : 0;
boolean wasChanged = false;
boolean restrictionExempt =
(origPermissions.getPermissionFlags(bp.name, userId)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
boolean restrictionApplied = (origPermissions.getPermissionFlags(
bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
if (permState != null && permState.isGranted()
&& permissionsState.revokeRuntimePermission(
bp, userId) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If soft restricted we allow holding in a restricted form
} else if (permissionPolicyInitialized && softRestricted) {
// Regardless if granted set the restriction flag as it
// may affect app treatment based on this permission.
if (!restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// Remove review flag as it is not necessary anymore
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized
|| (!hardRestricted || restrictionExempt)) {
if (permState != null && permState.isGranted()) {
if (permissionsState.grantRuntimePermission(bp, userId)
== PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
}
} else {
if (permState == null) {
// New permission
if (PLATFORM_PACKAGE_NAME.equals(
bp.getSourcePackageName())) {
if (!bp.isRemoved()) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED
| FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
}
}
}
if (!permissionsState.hasRuntimePermission(bp.name, userId)
&& permissionsState.grantRuntimePermission(bp, userId)
!= PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
// If legacy app always grant the permission but if restricted
// and not exempt take a note a restriction should be applied.
if (permissionPolicyInitialized
&& (hardRestricted || softRestricted)
&& !restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If unrestricted or restriction exempt, don't apply restriction.
if (permissionPolicyInitialized) {
if (!(hardRestricted || softRestricted) || restrictionExempt) {
if (restrictionApplied) {
flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
// Dropping restriction on a legacy app implies a review
if (!appSupportsRuntimePermissions) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
}
wasChanged = true;
}
}
}
if (wasChanged) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
permissionsState.updatePermissionFlags(bp, userId,
MASK_PERMISSION_FLAGS_ALL, flags);
}
} break;
case GRANT_UPGRADE: {
// Upgrade from Pre-Q to Q permission model. Make all permissions
// runtime
PermissionState permState = origPermissions
.getInstallPermissionState(perm);
int flags = (permState != null) ? permState.getFlags() : 0;
BasePermission bpToRevoke =
upgradedActivityRecognitionPermission == null
? bp : mSettings.getPermissionLocked(
upgradedActivityRecognitionPermission);
// Remove install permission
if (origPermissions.revokeInstallPermission(bpToRevoke)
!= PERMISSION_OPERATION_FAILURE) {
origPermissions.updatePermissionFlags(bpToRevoke,
UserHandle.USER_ALL,
(MASK_PERMISSION_FLAGS_ALL
& ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
changedInstallPermission = true;
}
boolean hardRestricted = bp.isHardRestricted();
boolean softRestricted = bp.isSoftRestricted();
for (int userId : userIds) {
// If permission policy is not ready we don't deal with restricted
// permissions as the policy may whitelist some permissions. Once
// the policy is initialized we would re-evaluate permissions.
final boolean permissionPolicyInitialized =
mPermissionPolicyInternal != null
&& mPermissionPolicyInternal.isInitialized(userId);
boolean wasChanged = false;
boolean restrictionExempt =
(origPermissions.getPermissionFlags(bp.name, userId)
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
boolean restrictionApplied = (origPermissions.getPermissionFlags(
bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
if (appSupportsRuntimePermissions) {
// If hard restricted we don't allow holding it
if (permissionPolicyInitialized && hardRestricted) {
if (!restrictionExempt) {
if (permState != null && permState.isGranted()
&& permissionsState.revokeRuntimePermission(
bp, userId) != PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
if (!restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If soft restricted we allow holding in a restricted form
} else if (permissionPolicyInitialized && softRestricted) {
// Regardless if granted set the restriction flag as it
// may affect app treatment based on this permission.
if (!restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// Remove review flag as it is not necessary anymore
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
if ((flags & FLAG_PERMISSION_REVOKED_COMPAT) != 0) {
flags &= ~FLAG_PERMISSION_REVOKED_COMPAT;
wasChanged = true;
// Hard restricted permissions cannot be held.
} else if (!permissionPolicyInitialized ||
(!hardRestricted || restrictionExempt)) {
if (permissionsState.grantRuntimePermission(bp, userId) !=
PERMISSION_OPERATION_FAILURE) {
wasChanged = true;
}
}
} else {
if (!permissionsState.hasRuntimePermission(bp.name, userId)
&& permissionsState.grantRuntimePermission(bp,
userId) != PERMISSION_OPERATION_FAILURE) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
wasChanged = true;
}
// If legacy app always grant the permission but if restricted
// and not exempt take a note a restriction should be applied.
if (permissionPolicyInitialized
&& (hardRestricted || softRestricted)
&& !restrictionExempt && !restrictionApplied) {
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
wasChanged = true;
}
}
// If unrestricted or restriction exempt, don't apply restriction.
if (permissionPolicyInitialized) {
if (!(hardRestricted || softRestricted) || restrictionExempt) {
if (restrictionApplied) {
flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
// Dropping restriction on a legacy app implies a review
if (!appSupportsRuntimePermissions) {
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
}
wasChanged = true;
}
}
}
if (wasChanged) {
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
}
permissionsState.updatePermissionFlags(bp, userId,
MASK_PERMISSION_FLAGS_ALL, flags);
}
} break;
default: {
if (packageOfInterest == null
|| packageOfInterest.equals(pkg.getPackageName())) {
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + friendlyName
+ " because it was previously installed without");
}
}
} break;
}
} else {
if (permissionsState.revokeInstallPermission(bp) !=
PERMISSION_OPERATION_FAILURE) {
// Also drop the permission flags.
permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
MASK_PERMISSION_FLAGS_ALL, 0);
changedInstallPermission = true;
if (DEBUG_PERMISSIONS) {
Slog.i(TAG, "Un-granting permission " + perm
+ " from package " + friendlyName
+ " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x"
+ Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
+ ")");
}
} else if (bp.isAppOp()) {
// Don't print warning for app op permissions, since it is fine for them
// not to be granted, there is a UI for the user to decide.
if (DEBUG_PERMISSIONS
&& (packageOfInterest == null
|| packageOfInterest.equals(pkg.getPackageName()))) {
Slog.i(TAG, "Not granting permission " + perm
+ " to package " + friendlyName
+ " (protectionLevel=" + bp.getProtectionLevel()
+ " flags=0x"
+ Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps))
+ ")");
}
}
}
}
if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
!ps.isSystem() || ps.getPkgState().isUpdatedSystemApp()) {
// This is the first that we have heard about this package, so the
// permissions we have now selected are fixed until explicitly
// changed.
ps.setInstallPermissionsFixed(true);
}
updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
updatedUserIds);
updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
permissionsState, pkg, newImplicitPermissions, updatedUserIds);
updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, updatedUserIds);
}
// Persist the runtime permissions state for users with changes. If permissions
// were revoked because no app in the shared user declares them we have to
// write synchronously to avoid losing runtime permissions state.
if (callback != null) {
callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked);
}
for (int userId : updatedUserIds) {
notifyRuntimePermissionStateChanged(pkg.getPackageName(), userId);
}
}