diff options
| author | 2020-08-25 22:15:45 -0700 | |
|---|---|---|
| committer | 2020-09-10 15:20:14 -0700 | |
| commit | 97a79bdb71ae03049c974a151db5071abfc8956d (patch) | |
| tree | 457253dd07118f2c2f5e049750cdb42456be4ef6 | |
| parent | 056630f142a730fe852155033e84a107ddbef5c1 (diff) | |
Make all permissions per-user.
This affects how we do APIs and persistence, so it has to be
accomplished first.
Although both install and runtime permissions are per-user now, we
still need to record whether a permission was granted as an install
permission or a runtime permission, so that we can act accordingly
upon install <-> runtime permission type change.
This change also moves the areInstallPermissionsFixed state into
permission, which is a per-package state.
Bug: 158736025
Test: atest CtsPermissionTestCases
Change-Id: I4aad998cddee13ff0c19c84d6d473abc7a6e7019
10 files changed, 1469 insertions, 550 deletions
diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index ac08d96ab303..050c1c4b4df5 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -239,7 +239,9 @@ <!-- Old synonym for "privileged". Deprecated in API level 23. --> <flag name="system" value="0x10" /> <!-- Additional flag from base permission type: this permission can also - (optionally) be granted to development applications. --> + (optionally) be granted to development applications. Although undocumented, the + permission state used to be shared by all users (including future users), but it is + managed per-user since API level 31. --> <flag name="development" value="0x20" /> <!-- Additional flag from base permission type: this permission is closely associated with an app op for controlling access. --> diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index c6269eba8120..a3c55da1feac 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -1848,7 +1848,7 @@ public class PackageManagerService extends IPackageManager.Stub Process.setThreadPriority(Process.THREAD_PRIORITY_DEFAULT); synchronized (mLock) { removeMessages(WRITE_PACKAGE_LIST); - mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mPermissionManager.writeStateToPackageSettingsTEMP(); mSettings.writePackageListLPr(msg.arg1); } Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND); @@ -3518,7 +3518,7 @@ public class PackageManagerService extends IPackageManager.Stub + ((SystemClock.uptimeMillis()-startTime)/1000f) + " seconds"); - mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); + mPermissionManager.readStateFromPackageSettingsTEMP(); // If the platform SDK has changed since the last time we booted, // we need to re-grant app permission to catch any new ones that // appear. This is really a hack, and means that apps can in some @@ -21846,7 +21846,7 @@ public class PackageManagerService extends IPackageManager.Stub protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) { if (!DumpUtils.checkDumpAndUsageStatsPermission(mContext, TAG, pw)) return; - mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mPermissionManager.writeStateToPackageSettingsTEMP(); DumpState dumpState = new DumpState(); boolean fullPreferred = false; @@ -23725,7 +23725,7 @@ public class PackageManagerService extends IPackageManager.Stub mDirtyUsers.remove(userId); mUserNeedsBadging.delete(userId); mPermissionManager.onUserRemoved(userId); - mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mPermissionManager.writeStateToPackageSettingsTEMP(); mSettings.removeUserLPw(userId); mPendingBroadcasts.remove(userId); mInstantAppRegistry.onUserRemovedLPw(userId); @@ -23826,9 +23826,9 @@ public class PackageManagerService extends IPackageManager.Stub boolean readPermissionStateForUser(@UserIdInt int userId) { synchronized (mPackages) { - mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mPermissionManager.writeStateToPackageSettingsTEMP(); mSettings.readPermissionStateForUserSyncLPr(userId); - mPermissionManager.readPermissionsStateFromPackageSettingsTEMP(); + mPermissionManager.readStateFromPackageSettingsTEMP(); return mPmInternal.isPermissionUpgradeNeeded(userId); } } @@ -25842,12 +25842,12 @@ public class PackageManagerService extends IPackageManager.Stub /** * Temporary method that wraps mSettings.writeLPr() and calls - * mPermissionManager.writePermissionsStateToPackageSettingsTEMP() beforehand. + * mPermissionManager.writeStateToPackageSettingsTEMP() beforehand. * * TODO(zhanghai): This should be removed once we finish migration of permission storage. */ private void writeSettingsLPrTEMP() { - mPermissionManager.writePermissionsStateToPackageSettingsTEMP(); + mPermissionManager.writeStateToPackageSettingsTEMP(); mSettings.writeLPr(); } } diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 962638b4f63c..865b8a1e97eb 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -36,6 +36,7 @@ import android.os.UserHandle; import android.util.Log; import android.util.Slog; +import com.android.internal.util.ArrayUtils; import com.android.server.pm.DumpState; import com.android.server.pm.PackageManagerService; import com.android.server.pm.PackageSettingBase; @@ -139,6 +140,10 @@ public final class BasePermission { this.perm = perm; } + public boolean hasGids() { + return !ArrayUtils.isEmpty(gids); + } + public int[] computeGids(int userId) { if (perUser) { final int[] userGids = new int[gids.length]; @@ -419,9 +424,9 @@ public final class BasePermission { } public void enforceDeclaredUsedAndRuntimeOrDevelopment(AndroidPackage pkg, - PermissionsState permsState) { + UidPermissionState uidState) { int index = pkg.getRequestedPermissions().indexOf(name); - if (!permsState.hasRequestedPermission(name) && index == -1) { + if (!uidState.hasRequestedPermission(name) && index == -1) { throw new SecurityException("Package " + pkg.getPackageName() + " has not requested permission " + name); } diff --git a/services/core/java/com/android/server/pm/permission/DevicePermissionState.java b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java new file mode 100644 index 000000000000..b9456acfced5 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/DevicePermissionState.java @@ -0,0 +1,77 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; + +/** + * Permission state for this device. + */ +public final class DevicePermissionState { + @GuardedBy("mLock") + @NonNull + private final SparseArray<UserPermissionState> mUserStates = new SparseArray<>(); + + @NonNull + private final Object mLock; + + public DevicePermissionState(@NonNull Object lock) { + mLock = lock; + } + + @Nullable + public UserPermissionState getUserState(@UserIdInt int userId) { + synchronized (mLock) { + return mUserStates.get(userId); + } + } + + @NonNull + public UserPermissionState getOrCreateUserState(@UserIdInt int userId) { + synchronized (mLock) { + UserPermissionState userState = mUserStates.get(userId); + if (userState == null) { + userState = new UserPermissionState(mLock); + mUserStates.put(userId, userState); + } + return userState; + } + } + + public void removeUserState(@UserIdInt int userId) { + synchronized (mLock) { + mUserStates.delete(userId); + } + } + + public int[] getUserIds() { + synchronized (mLock) { + final int userStatesSize = mUserStates.size(); + final int[] userIds = new int[userStatesSize]; + for (int i = 0; i < userStatesSize; i++) { + final int userId = mUserStates.keyAt(i); + userIds[i] = userId; + } + return userIds; + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index ffdcc227b7f1..71091c3f8c14 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -148,12 +148,9 @@ import com.android.server.pm.permission.PermissionManagerServiceInternal.Default import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultDialerProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.DefaultHomeProvider; import com.android.server.pm.permission.PermissionManagerServiceInternal.PermissionCallback; -import com.android.server.pm.permission.PermissionsState.PermissionState; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; -import libcore.util.EmptyArray; - import java.io.FileDescriptor; import java.io.PrintWriter; import java.lang.annotation.Retention; @@ -226,8 +223,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; - /** Maps from App ID to PermissionsState */ - private final SparseArray<PermissionsState> mAppIdStates = new SparseArray<>(); + @NonNull + private final DevicePermissionState mState; /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; @@ -395,6 +392,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); mSettings = new PermissionSettings(mLock); + mState = new DevicePermissionState(mLock); mAppOpsManager = context.getSystemService(AppOpsManager.class); mHandlerThread = new ServiceThread(TAG, @@ -681,12 +679,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return 0; } - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); return 0; } - return permissionsState.getPermissionFlags(permName, userId); + return uidState.getPermissionFlags(permName); } @Override @@ -788,14 +786,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); return; } - final boolean hadState = - permissionsState.getRuntimePermissionState(permName, userId) != null; + final boolean hadState = uidState.getPermissionState(permName) != null; if (!hadState) { boolean isRequested = false; // Fast path, the current package has requested the permission. @@ -822,20 +819,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } final boolean permissionUpdated = - permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues); + uidState.updatePermissionFlags(bp, flagMask, flagValues); if (permissionUpdated && bp.isRuntime()) { notifyRuntimePermissionStateChanged(packageName, userId); } if (permissionUpdated && callback != null) { // Install and runtime permissions are stored in different places, // so figure out what permission changed and persist the change. - if (permissionsState.getInstallPermissionState(permName) != null) { + if (!bp.isRuntime()) { int userUid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); callback.onInstallPermissionUpdatedNotifyListener(userUid); - } else if (permissionsState.getRuntimePermissionState(permName, userId) != null - || hadState) { - callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, - pkg.getUid()); + } else { + callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid()); } } } @@ -868,13 +863,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { final boolean[] changed = new boolean[1]; mPackageManagerInt.forEachPackage(pkg -> { - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); return; } - changed[0] |= permissionsState.updatePermissionFlagsForAllPermissions( - userId, effectiveFlagMask, effectiveFlagValues); + changed[0] |= uidState.updatePermissionFlagsForAllPermissions( + effectiveFlagMask, effectiveFlagValues); mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid()); }); @@ -926,19 +922,20 @@ public class PermissionManagerService extends IPermissionManager.Stub { } final int uid = UserHandle.getUid(userId, pkg.getUid()); - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); return PackageManager.PERMISSION_DENIED; } - if (checkSinglePermissionInternal(uid, permissionsState, permissionName)) { + if (checkSinglePermissionInternal(uid, uidState, permissionName)) { return PackageManager.PERMISSION_GRANTED; } final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName); if (fullerPermissionName != null - && checkSinglePermissionInternal(uid, permissionsState, fullerPermissionName)) { + && checkSinglePermissionInternal(uid, uidState, fullerPermissionName)) { return PackageManager.PERMISSION_GRANTED; } @@ -946,8 +943,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean checkSinglePermissionInternal(int uid, - @NonNull PermissionsState permissionsState, @NonNull String permissionName) { - if (!permissionsState.hasPermission(permissionName, UserHandle.getUserId(uid))) { + @NonNull UidPermissionState uidState, @NonNull String permissionName) { + if (!uidState.hasPermission(permissionName)) { return false; } @@ -1141,9 +1138,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { final long identity = Binder.clearCallingIdentity(); try { - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); return null; } @@ -1164,7 +1161,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int i = 0; i < permissionCount; i++) { final String permissionName = pkg.getRequestedPermissions().get(i); final int currentFlags = - permissionsState.getPermissionFlags(permissionName, userId); + uidState.getPermissionFlags(permissionName); if ((currentFlags & queryFlags) != 0) { if (whitelistedPermissions == null) { whitelistedPermissions = new ArrayList<>(); @@ -1453,13 +1450,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown package: " + packageName); } - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); return; } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1472,7 +1470,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { final int uid = UserHandle.getUid(userId, UserHandle.getAppId(pkg.getUid())); - final int flags = permissionsState.getPermissionFlags(permName, userId); + 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); @@ -1502,8 +1500,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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) { + // 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) != PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionGranted(); } @@ -1521,7 +1520,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final int result = permissionsState.grantRuntimePermission(bp, userId); + final int result = uidState.grantPermission(bp); switch (result) { case PERMISSION_OPERATION_FAILURE: { return; @@ -1617,13 +1616,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { throw new IllegalArgumentException("Unknown permission: " + permName); } - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); return; } - bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, permissionsState); + bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg, uidState); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need @@ -1634,7 +1634,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { return; } - final int flags = permissionsState.getPermissionFlags(permName, userId); + final int flags = uidState.getPermissionFlags(permName); // Only the system may revoke SYSTEM_FIXED permissions. if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 && UserHandle.getCallingAppId() != Process.SYSTEM_UID) { @@ -1649,8 +1649,9 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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.revokeInstallPermission(bp) - != PERMISSION_OPERATION_FAILURE) { + // 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.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) { if (callback != null) { mDefaultPermissionCallback.onInstallPermissionRevoked(); } @@ -1659,12 +1660,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Permission is already revoked, no need to do anything. - if (!permissionsState.hasRuntimePermission(permName, userId)) { + if (!uidState.hasPermission(permName)) { return; } - if (permissionsState.revokeRuntimePermission(bp, userId) - == PERMISSION_OPERATION_FAILURE) { + if (uidState.revokePermission(bp) == PERMISSION_OPERATION_FAILURE) { return; } @@ -2466,19 +2466,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void onUserRemoved(@UserIdInt int userId) { synchronized (mLock) { - final int appIdStatesSize = mAppIdStates.size(); - for (int i = 0; i < appIdStatesSize; i++) { - PermissionsState permissionsState = mAppIdStates.valueAt(i); - for (PermissionState permissionState - : permissionsState.getRuntimePermissionStates(userId)) { - BasePermission bp = mSettings.getPermission(permissionState.getName()); - if (bp != null) { - permissionsState.revokeRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - } - } - } + mState.removeUserState(userId); } } @@ -2489,19 +2477,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return Collections.emptySet(); } - final PermissionsState permissionsState = getPermissionsState(ps); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName); + final UidPermissionState uidState = getUidState(ps, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); return Collections.emptySet(); } if (!ps.getInstantApp(userId)) { - return permissionsState.getPermissions(userId); + return uidState.getPermissions(); } else { // Install permission state is shared among all users, but instant app state is // per-user, so we can only filter it here unless we make install permission state // per-user as well. - final Set<String> instantPermissions = new ArraySet<>(permissionsState.getPermissions( - userId)); + final Set<String> instantPermissions = new ArraySet<>(uidState.getPermissions()); instantPermissions.removeIf(permissionName -> { BasePermission permission = mSettings.getPermission(permissionName); if (permission == null) { @@ -2533,12 +2520,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return null; } - final PermissionsState permissionsState = getPermissionsState(ps); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName); + final UidPermissionState uidState = getUidState(ps, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); return null; } - return permissionsState.computeGids(userId); + return uidState.computeGids(userId); } /** @@ -2575,15 +2562,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (ps == null) { return; } - final PermissionsState permissionsState = getOrCreatePermissionsState(ps); final int[] userIds = getAllUserIds(); boolean runtimePermissionsRevoked = false; int[] updatedUserIds = EMPTY_INT_ARRAY; - for (int userId : userIds) { - if (permissionsState.isMissing(userId)) { + for (final int userId : userIds) { + final UserPermissionState userState = mState.getOrCreateUserState(userId); + final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId()); + + if (uidState.isMissing()) { Collection<String> requestedPermissions; int targetSdkVersion; if (!ps.isSharedUser()) { @@ -2611,222 +2600,220 @@ public class PermissionManagerService extends IPermissionManager.Stub { && permission.isRuntime() && !permission.isRemoved()) { if (permission.isHardOrSoftRestricted() || permission.isImmutablyRestricted()) { - permissionsState.updatePermissionFlags(permission, userId, + uidState.updatePermissionFlags(permission, FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT); } if (targetSdkVersion < Build.VERSION_CODES.M) { - permissionsState.updatePermissionFlags(permission, userId, + uidState.updatePermissionFlags(permission, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT); - permissionsState.grantRuntimePermission(permission, userId); + uidState.grantPermission(permission); } } } - permissionsState.setMissing(false, userId); + uidState.setMissing(false); updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - } - PermissionsState origPermissions = permissionsState; + UidPermissionState origState = uidState; - boolean changedInstallPermission = false; + 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().getPackages(), permissionsState, userIds); - if (!ArrayUtils.isEmpty(updatedUserIds)) { - runtimePermissionsRevoked = true; + if (replace) { + userState.setInstallPermissionsFixed(ps.name, false); + if (!ps.isSharedUser()) { + origState = new UidPermissionState(uidState); + uidState.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) { + if (revokeUnusedSharedUserPermissionsLocked( + ps.getSharedUser().getPackages(), uidState)) { + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + runtimePermissionsRevoked = true; + } } } } - } - permissionsState.setGlobalGids(mGlobalGids); + uidState.setGlobalGids(mGlobalGids); - ArraySet<String> newImplicitPermissions = new ArraySet<>(); + 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 BasePermission bp = mSettings.getPermission(permName); - final boolean appSupportsRuntimePermissions = - pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; - String upgradedActivityRecognitionPermission = null; + final int N = pkg.getRequestedPermissions().size(); + for (int i = 0; i < N; i++) { + final String permName = pkg.getRequestedPermissions().get(i); + final BasePermission bp = mSettings.getPermission(permName); + final boolean appSupportsRuntimePermissions = + pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; + String upgradedActivityRecognitionPermission = null; - if (DEBUG_INSTALL && bp != null) { - Log.i(TAG, "Package " + pkg.getPackageName() - + " checking " + permName + ": " + bp); - } + if (DEBUG_INSTALL && bp != null) { + Log.i(TAG, "Package " + pkg.getPackageName() + + " 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 " + pkg.getPackageName()); + if (bp == null || getSourcePackageSetting(bp) == null) { + if (packageOfInterest == null || packageOfInterest.equals( + pkg.getPackageName())) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Unknown permission " + permName + + " in package " + pkg.getPackageName()); + } } + continue; } - 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); + // Cache newImplicitPermissions before modifing permissionsState as for the shared + // uids the original and new state are the same object + if (!origState.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 " + pkg.getPackageName()); - } - } 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 " + pkg.getPackageName()); + } + } 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) + && origState.hasInstallPermission(splitPermName)) { + upgradedActivityRecognitionPermission = splitPermName; + newImplicitPermissions.add(permName); - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " - + pkg.getPackageName()); + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, permName + " is newly added for " + + pkg.getPackageName()); + } + break; } - 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 (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { -// if (DEBUG_PERMISSIONS) { -// Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() -// + " for package " + pkg.getPackageName()); -// } -// continue; -// } - if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { - if (DEBUG_PERMISSIONS) { - Log.i(TAG, "Denying runtime-only permission " + bp.getName() - + " for package " + pkg.getPackageName()); + // 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 (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { + // if (DEBUG_PERMISSIONS) { + // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() + // + " for package " + pkg.getPackageName()); + // } + // continue; + // } + + if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { + if (DEBUG_PERMISSIONS) { + Log.i(TAG, "Denying runtime-only permission " + bp.getName() + + " for package " + pkg.getPackageName()); + } + continue; } - continue; - } - - final String perm = bp.getName(); - boolean allowedSig = false; - int grant = GRANT_DENIED; - // Keep track of app op permissions. - if (bp.isAppOp()) { - mSettings.addAppOpPackage(perm, pkg.getPackageName()); - } + final String perm = bp.getName(); + boolean allowedSig = false; + int grant = GRANT_DENIED; - 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; + // Keep track of app op permissions. + if (bp.isAppOp()) { + mSettings.addAppOpPackage(perm, pkg.getPackageName()); } - } else if (bp.isSignature()) { - // For all apps signature permissions are install time ones. - allowedSig = shouldGrantSignaturePermission(perm, pkg, ps, bp, origPermissions); - if (allowedSig) { + + if (bp.isNormal()) { + // For all apps normal permissions are install time ones. grant = GRANT_INSTALL; + } else if (bp.isRuntime()) { + if (origState.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 = shouldGrantSignaturePermission(perm, pkg, ps, bp, origState); + if (allowedSig) { + grant = GRANT_INSTALL; + } } - } - 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; + if (grant != GRANT_DENIED) { + if (!ps.isSystem() && userState.areInstallPermissionsFixed(ps.name) + && !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 && !origState.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; + } } } } - } - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Considering granting permission " + perm + " to package " - + pkg.getPackageName()); - } + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Considering granting permission " + perm + " to package " + + pkg.getPackageName()); + } - synchronized (mLock) { - if (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) { + synchronized (mLock) { + if (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 + final PermissionState origPermissionState = + origState.getPermissionState(perm); + if (origPermissionState != null + && origPermissionState.isRuntime()) { // Revoke the runtime permission and clear the flags. - origPermissions.revokeRuntimePermission(bp, userId); - origPermissions.updatePermissionFlags(bp, userId, + origState.revokePermission(bp); + origState.updatePermissionFlags(bp, 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; + // Grant an install permission. + if (uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) { + changedInstallPermission = true; + } + } break; - case GRANT_RUNTIME: { - boolean hardRestricted = bp.isHardRestricted(); - boolean softRestricted = bp.isSoftRestricted(); + 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. @@ -2834,25 +2821,24 @@ public class PermissionManagerService extends IPermissionManager.Stub { mPermissionPolicyInternal != null && mPermissionPolicyInternal.isInitialized(userId); - PermissionState permState = origPermissions - .getRuntimePermissionState(perm, userId); - int flags = permState != null ? permState.getFlags() : 0; + PermissionState origPermState = origState.getPermissionState(perm); + int flags = origPermState != null ? origPermState.getFlags() : 0; boolean wasChanged = false; boolean restrictionExempt = - (origPermissions.getPermissionFlags(bp.name, userId) + (origState.getPermissionFlags(bp.name) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - boolean restrictionApplied = (origPermissions.getPermissionFlags( - bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + boolean restrictionApplied = (origState.getPermissionFlags( + bp.name) & 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) { + if (origPermState != null && origPermState.isGranted() + && uidState.revokePermission( + bp) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } if (!restrictionApplied) { @@ -2882,15 +2868,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Hard restricted permissions cannot be held. } else if (!permissionPolicyInitialized || (!hardRestricted || restrictionExempt)) { - if (permState != null && permState.isGranted()) { - if (permissionsState.grantRuntimePermission(bp, userId) + if (origPermState != null && origPermState.isGranted()) { + if (uidState.grantPermission(bp) == PERMISSION_OPERATION_FAILURE) { wasChanged = true; } } } } else { - if (permState == null) { + if (origPermState == null) { // New permission if (PLATFORM_PACKAGE_NAME.equals( bp.getSourcePackageName())) { @@ -2902,8 +2888,8 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - if (!permissionsState.hasRuntimePermission(bp.name, userId) - && permissionsState.grantRuntimePermission(bp, userId) + if (!uidState.hasPermission(bp.name) + && uidState.grantPermission(bp) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } @@ -2936,36 +2922,32 @@ public class PermissionManagerService extends IPermissionManager.Stub { 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; - } + uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, + flags); + } break; + + case GRANT_UPGRADE: { + // Upgrade from Pre-Q to Q permission model. Make all permissions + // runtime + PermissionState origPermState = origState.getPermissionState(perm); + int flags = (origPermState != null) ? origPermState.getFlags() : 0; + + BasePermission bpToRevoke = + upgradedActivityRecognitionPermission == null + ? bp : mSettings.getPermissionLocked( + upgradedActivityRecognitionPermission); + // Remove install permission + if (origState.revokePermission(bpToRevoke) + != PERMISSION_OPERATION_FAILURE) { + origState.updatePermissionFlags(bpToRevoke, + (MASK_PERMISSION_FLAGS_ALL + & ~FLAG_PERMISSION_APPLY_RESTRICTION), 0); + changedInstallPermission = true; + } - boolean hardRestricted = bp.isHardRestricted(); - boolean softRestricted = bp.isSoftRestricted(); + 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. @@ -2976,18 +2958,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { boolean wasChanged = false; boolean restrictionExempt = - (origPermissions.getPermissionFlags(bp.name, userId) + (origState.getPermissionFlags(bp.name) & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - boolean restrictionApplied = (origPermissions.getPermissionFlags( - bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; + boolean restrictionApplied = (origState.getPermissionFlags( + bp.name) & 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) { + if (origPermState != null && origPermState.isGranted() + && uidState.revokePermission( + bp) != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } if (!restrictionApplied) { @@ -3017,15 +2999,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { // Hard restricted permissions cannot be held. } else if (!permissionPolicyInitialized || (!hardRestricted || restrictionExempt)) { - if (permissionsState.grantRuntimePermission(bp, userId) != - PERMISSION_OPERATION_FAILURE) { + if (uidState.grantPermission(bp) + != PERMISSION_OPERATION_FAILURE) { wasChanged = true; } } } else { - if (!permissionsState.hasRuntimePermission(bp.name, userId) - && permissionsState.grantRuntimePermission(bp, - userId) != PERMISSION_OPERATION_FAILURE) { + if (!uidState.hasPermission(bp.name) + && uidState.grantPermission(bp) + != PERMISSION_OPERATION_FAILURE) { flags |= FLAG_PERMISSION_REVIEW_REQUIRED; wasChanged = true; } @@ -3058,72 +3040,75 @@ public class PermissionManagerService extends IPermissionManager.Stub { updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } - permissionsState.updatePermissionFlags(bp, userId, + uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, flags); - } - } break; + } break; - default: { - if (packageOfInterest == null - || packageOfInterest.equals(pkg.getPackageName())) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Not granting permission " + perm - + " to package " + pkg.getPackageName() - + " because it was previously installed without"); + default: { + if (packageOfInterest == null + || packageOfInterest.equals(pkg.getPackageName())) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Not granting permission " + perm + + " to package " + pkg.getPackageName() + + " 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 " + pkg.getPackageName() - + " (protectionLevel=" + bp.getProtectionLevel() - + " flags=0x" - + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) - + ")"); + } break; } - } 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 " + pkg.getPackageName() - + " (protectionLevel=" + bp.getProtectionLevel() - + " flags=0x" - + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, ps)) - + ")"); + } else { + if (uidState.revokePermission(bp) != PERMISSION_OPERATION_FAILURE) { + // Also drop the permission flags. + uidState.updatePermissionFlags(bp, + MASK_PERMISSION_FLAGS_ALL, 0); + changedInstallPermission = true; + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Un-granting permission " + perm + + " from package " + pkg.getPackageName() + + " (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 " + pkg.getPackageName() + + " (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); - } + if ((changedInstallPermission || replace) + && !userState.areInstallPermissionsFixed(ps.name) + && !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. + userState.setInstallPermissionsFixed(ps.name, true); + } - synchronized (mLock) { - updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg, - userIds, updatedUserIds); - updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions, - permissionsState, pkg, newImplicitPermissions, userIds, updatedUserIds); - updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, - updatedUserIds); + synchronized (mLock) { + updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg, + userId, updatedUserIds); + updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState, + uidState, pkg, newImplicitPermissions, userId, updatedUserIds); + } } + updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, + updatedUserIds); + // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important // for shared users. // Persist the runtime permissions state for users with changes. If permissions @@ -3159,40 +3144,38 @@ public class PermissionManagerService extends IPermissionManager.Stub { * * @return The updated value of the {@code updatedUserIds} parameter */ - private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull PermissionsState ps, - @NonNull AndroidPackage pkg, @NonNull int[] userIds, @NonNull int[] updatedUserIds) { + private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps, + @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) { String pkgName = pkg.getPackageName(); boolean supportsRuntimePermissions = pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; - for (int userId : userIds) { - for (String permission : ps.getPermissions(userId)) { - if (!pkg.getImplicitPermissions().contains(permission)) { - if (!ps.hasInstallPermission(permission)) { - int flags = ps.getRuntimePermissionState(permission, userId).getFlags(); + for (String permission : ps.getPermissions()) { + if (!pkg.getImplicitPermissions().contains(permission)) { + if (!ps.hasInstallPermission(permission)) { + int flags = ps.getPermissionFlags(permission); - if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { - BasePermission bp = mSettings.getPermissionLocked(permission); + if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { + BasePermission bp = mSettings.getPermissionLocked(permission); - int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; + int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; - if ((flags & BLOCKING_PERMISSION_FLAGS) == 0 - && supportsRuntimePermissions) { - int revokeResult = ps.revokeRuntimePermission(bp, userId); - if (revokeResult != PERMISSION_OPERATION_FAILURE) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " - + permission + " for " + pkgName - + " as it is now requested"); - } + if ((flags & BLOCKING_PERMISSION_FLAGS) == 0 + && supportsRuntimePermissions) { + int revokeResult = ps.revokePermission(bp); + if (revokeResult != PERMISSION_OPERATION_FAILURE) { + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, "Revoking runtime permission " + + permission + " for " + pkgName + + " as it is now requested"); } - - flagsToRemove |= USER_PERMISSION_FLAGS; } - ps.updatePermissionFlags(bp, userId, flagsToRemove, 0); - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + flagsToRemove |= USER_PERMISSION_FLAGS; } + + ps.updatePermissionFlags(bp, flagsToRemove, 0); + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); } } } @@ -3213,12 +3196,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param newPerm The permission to inherit to * @param ps The permission state of the package * @param pkg The package requesting the permissions - * @param userId The user the permission belongs to */ private void inheritPermissionStateToNewImplicitPermissionLocked( @NonNull ArraySet<String> sourcePerms, @NonNull String newPerm, - @NonNull PermissionsState ps, @NonNull AndroidPackage pkg, - @UserIdInt int userId) { + @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) { String pkgName = pkg.getPackageName(); boolean isGranted = false; int flags = 0; @@ -3226,17 +3207,16 @@ public class PermissionManagerService extends IPermissionManager.Stub { int numSourcePerm = sourcePerms.size(); for (int i = 0; i < numSourcePerm; i++) { String sourcePerm = sourcePerms.valueAt(i); - if ((ps.hasRuntimePermission(sourcePerm, userId)) - || ps.hasInstallPermission(sourcePerm)) { + if (ps.hasPermission(sourcePerm)) { if (!isGranted) { flags = 0; } isGranted = true; - flags |= ps.getPermissionFlags(sourcePerm, userId); + flags |= ps.getPermissionFlags(sourcePerm); } else { if (!isGranted) { - flags |= ps.getPermissionFlags(sourcePerm, userId); + flags |= ps.getPermissionFlags(sourcePerm); } } } @@ -3247,11 +3227,11 @@ public class PermissionManagerService extends IPermissionManager.Stub { + " for " + pkgName); } - ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId); + ps.grantPermission(mSettings.getPermissionLocked(newPerm)); } // Add permission flags - ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags); + ps.updatePermissionFlags(mSettings.getPermission(newPerm), flags, flags); } /** @@ -3283,15 +3263,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { * @param origPs The permission state of the package before the split * @param ps The new permission state * @param pkg The package the permission belongs to - * @param userIds All user IDs in the system, must be passed in because this method is locked + * @param userId The user ID * @param updatedUserIds List of users for which the permission state has already been changed * * @return List of users for which the permission state has been changed */ private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked( - @NonNull PermissionsState origPs, @NonNull PermissionsState ps, + @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg, @NonNull ArraySet<String> newImplicitPermissions, - @NonNull int[] userIds, @NonNull int[] updatedUserIds) { + @UserIdInt int userId, @NonNull int[] updatedUserIds) { String pkgName = pkg.getPackageName(); ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>(); @@ -3325,35 +3305,33 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (!ps.hasInstallPermission(newPerm)) { BasePermission bp = mSettings.getPermissionLocked(newPerm); - for (int userId : userIds) { - if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { - ps.updatePermissionFlags(bp, userId, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); - } - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); + if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { + ps.updatePermissionFlags(bp, + FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, + FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); + } + updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - boolean inheritsFromInstallPerm = false; - for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); - sourcePermNum++) { - if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) { - inheritsFromInstallPerm = true; - break; - } + boolean inheritsFromInstallPerm = false; + for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); + sourcePermNum++) { + if (ps.hasInstallPermission(sourcePerms.valueAt(sourcePermNum))) { + inheritsFromInstallPerm = true; + break; } + } - if (!origPs.hasRequestedPermission(sourcePerms) - && !inheritsFromInstallPerm) { - // Both permissions are new so nothing to inherit. - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms - + " for " + pkgName + " as split permission is also new"); - } - } else { - // Inherit from new install or existing runtime permissions - inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, - newPerm, ps, pkg, userId); + if (!origPs.hasRequestedPermission(sourcePerms) + && !inheritsFromInstallPerm) { + // Both permissions are new so nothing to inherit. + if (DEBUG_PERMISSIONS) { + Slog.i(TAG, newPerm + " does not inherit from " + sourcePerms + + " for " + pkgName + " as split permission is also new"); } + } else { + // Inherit from new install or existing runtime permissions + inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, + newPerm, ps, pkg); } } } @@ -3484,7 +3462,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } private boolean shouldGrantSignaturePermission(String perm, AndroidPackage pkg, - PackageSetting pkgSetting, BasePermission bp, PermissionsState origPermissions) { + PackageSetting pkgSetting, BasePermission bp, UidPermissionState origPermissions) { boolean oemPermission = bp.isOEM(); boolean vendorPrivilegedPermission = bp.isVendorPrivileged(); boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged(); @@ -3760,12 +3738,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { } // Legacy apps have the permission and get user consent on launch. - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); return false; } - return permissionsState.isPermissionReviewRequired(userId); + return uidState.isPermissionReviewRequired(); } private boolean isPackageRequestingPermission(AndroidPackage pkg, String permission) { @@ -3789,9 +3768,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void grantRequestedRuntimePermissionsForUser(AndroidPackage pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); return; } @@ -3816,7 +3796,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { && (supportsRuntimePermissions || !bp.isRuntimeOnly()) && (grantedPermissions == null || ArrayUtils.contains(grantedPermissions, permission))) { - final int flags = permissionsState.getPermissionFlags(permission, userId); + final int flags = uidState.getPermissionFlags(permission); if (supportsRuntimePermissions) { // Installer cannot change immutable permissions. if ((flags & immutableFlags) == 0) { @@ -3838,18 +3818,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { private void setWhitelistedRestrictedPermissionsForUsers(@NonNull AndroidPackage pkg, @UserIdInt int[] userIds, @Nullable List<String> permissions, int callingUid, @PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) { - final PermissionsState permissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); - return; - } - SparseArray<ArraySet<String>> oldGrantedRestrictedPermissions = new SparseArray<>(); boolean updatePermissions = false; final int permissionCount = pkg.getRequestedPermissions().size(); for (int i = 0; i < userIds.length; i++) { int userId = userIds[i]; + final UidPermissionState uidState = getUidState(pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " + + userId); + continue; + } + for (int j = 0; j < permissionCount; j++) { final String permissionName = pkg.getRequestedPermissions().get(j); @@ -3859,14 +3840,14 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - if (permissionsState.hasPermission(permissionName, userId)) { + if (uidState.hasPermission(permissionName)) { if (oldGrantedRestrictedPermissions.get(userId) == null) { oldGrantedRestrictedPermissions.put(userId, new ArraySet<>()); } oldGrantedRestrictedPermissions.get(userId).add(permissionName); } - final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId); + final int oldFlags = uidState.getPermissionFlags(permissionName); int newFlags = oldFlags; int mask = 0; @@ -3921,8 +3902,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { // as whitelisting trumps policy i.e. policy cannot grant a non // grantable permission. if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - final boolean isGranted = permissionsState.hasPermission(permissionName, - userId); + final boolean isGranted = uidState.hasPermission(permissionName); if (!isWhitelisted && isGranted) { mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -3958,12 +3938,13 @@ public class PermissionManagerService extends IPermissionManager.Stub { for (int j = 0; j < oldGrantedCount; j++) { final String permission = oldPermsForUser.valueAt(j); // Sometimes we create a new permission state instance during update. - final PermissionsState newPermissionsState = getPermissionsState(pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName()); + final UidPermissionState newUidState = getUidState(pkg, userId); + if (newUidState == null) { + Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + + " and user " + userId); continue; } - if (!newPermissionsState.hasPermission(permission, userId)) { + if (!newUidState.hasPermission(permission)) { callback.onPermissionRevoked(pkg.getUid(), userId, null); break; } @@ -4012,9 +3993,10 @@ public class PermissionManagerService extends IPermissionManager.Stub { continue; } - PermissionsState permissionsState = getPermissionsState(deletedPs.pkg); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName()); + UidPermissionState uidState = getUidState(deletedPs.pkg, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + deletedPs.pkg.getPackageName() + + " and user " + userId); continue; } @@ -4036,25 +4018,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - // Try to revoke as an install permission which is for all users. // The package is gone - no need to keep flags for applying policy. - permissionsState.updatePermissionFlags(bp, userId, - PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); - - if (permissionsState.revokeInstallPermission(bp) - == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - affectedUserId = UserHandle.USER_ALL; - } + uidState.updatePermissionFlags(bp, PackageManager.MASK_PERMISSION_FLAGS_ALL, 0); // Try to revoke as a runtime permission which is per user. - if (permissionsState.revokeRuntimePermission(bp, userId) + // TODO(zhanghai): This doesn't make sense. revokePermission() doesn't fail, and why are + // we only killing the uid when gids changed, instead of any permission change? + if (uidState.revokePermission(bp) == PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED) { - if (affectedUserId == UserHandle.USER_NULL) { - affectedUserId = userId; - } else if (affectedUserId != userId) { - // Multiple users affected. - affectedUserId = UserHandle.USER_ALL; - } + affectedUserId = userId; } } @@ -4062,12 +4034,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @GuardedBy("mLock") - private int[] revokeUnusedSharedUserPermissionsLocked( - List<AndroidPackage> pkgList, PermissionsState permissionsState, int[] allUserIds) { + private boolean revokeUnusedSharedUserPermissionsLocked( + List<AndroidPackage> pkgList, UidPermissionState uidState) { // Collect all used permissions in the UID final ArraySet<String> usedPermissions = new ArraySet<>(); if (pkgList == null || pkgList.size() == 0) { - return EmptyArray.INT; + return false; } for (AndroidPackage pkg : pkgList) { if (pkg.getRequestedPermissions().isEmpty()) { @@ -4083,44 +4055,27 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - // Prune install permissions - List<PermissionState> installPermStates = permissionsState.getInstallPermissionStates(); - final int installPermCount = installPermStates.size(); - for (int i = installPermCount - 1; i >= 0; i--) { - PermissionState permissionState = installPermStates.get(i); + boolean runtimePermissionChanged = false; + + // Prune permissions + final List<com.android.server.pm.permission.PermissionState> permissionStates = + uidState.getPermissionStates(); + final int permissionStatesSize = permissionStates.size(); + for (int i = permissionStatesSize - 1; i >= 0; i--) { + PermissionState permissionState = permissionStates.get(i); if (!usedPermissions.contains(permissionState.getName())) { BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); if (bp != null) { - permissionsState.revokeInstallPermission(bp); - permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, - MASK_PERMISSION_FLAGS_ALL, 0); - } - } - } - - int[] runtimePermissionChangedUserIds = EmptyArray.INT; - - // Prune runtime permissions - for (int userId : allUserIds) { - List<PermissionState> runtimePermStates = permissionsState - .getRuntimePermissionStates(userId); - final int runtimePermCount = runtimePermStates.size(); - for (int i = runtimePermCount - 1; i >= 0; i--) { - PermissionState permissionState = runtimePermStates.get(i); - if (!usedPermissions.contains(permissionState.getName())) { - BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); - if (bp != null) { - permissionsState.revokeRuntimePermission(bp, userId); - permissionsState.updatePermissionFlags(bp, userId, - MASK_PERMISSION_FLAGS_ALL, 0); - runtimePermissionChangedUserIds = ArrayUtils.appendInt( - runtimePermissionChangedUserIds, userId); + uidState.revokePermission(bp); + uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, 0); + if (permissionState.isRuntime()) { + runtimePermissionChanged = true; } } } } - return runtimePermissionChangedUserIds; + return runtimePermissionChanged; } /** @@ -4368,15 +4323,19 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } else { mPackageManagerInt.forEachPackage(p -> { - final PermissionsState permissionsState = getPermissionsState(p); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + p.getPackageName()); - return; - } - if (permissionsState.getInstallPermissionState(bp.getName()) != null) { - permissionsState.revokeInstallPermission(bp); - permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, - MASK_PERMISSION_FLAGS_ALL, 0); + final int[] userIds = mUserManagerInt.getUserIds(); + for (final int userId : userIds) { + final UidPermissionState uidState = getUidState(p, userId); + if (uidState == null) { + Slog.e(TAG, "Missing permissions state for " + + p.getPackageName() + " and user " + userId); + return; + } + if (uidState.getPermissionState(bp.getName()) != null) { + uidState.revokePermission(bp); + uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, + 0); + } } }); } @@ -4784,62 +4743,124 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Nullable - private PermissionsState getPermissionsState(@NonNull PackageSetting ps) { - return getPermissionsState(ps.getAppId()); - } - - @Nullable - private PermissionsState getPermissionsState(@NonNull AndroidPackage pkg) { - return getPermissionsState(pkg.getUid()); - } - - @Nullable - private PermissionsState getPermissionsState(int appId) { - synchronized (mLock) { - return mAppIdStates.get(appId); - } + private UidPermissionState getUidState(@NonNull PackageSetting ps, + @UserIdInt int userId) { + return getUidState(ps.getAppId(), userId); } @Nullable - private PermissionsState getOrCreatePermissionsState(@NonNull PackageSetting ps) { - return getOrCreatePermissionsState(ps.getAppId()); + private UidPermissionState getUidState(@NonNull AndroidPackage pkg, + @UserIdInt int userId) { + return getUidState(pkg.getUid(), userId); } @Nullable - private PermissionsState getOrCreatePermissionsState(int appId) { + private UidPermissionState getUidState(int appId, @UserIdInt int userId) { synchronized (mLock) { - PermissionsState state = mAppIdStates.get(appId); - if (state == null) { - state = new PermissionsState(); - mAppIdStates.put(appId, state); + final UserPermissionState userState = mState.getUserState(userId); + if (userState == null) { + return null; } - return state; + return userState.getUidState(appId); } } - private void removePermissionsState(int appId) { + private void removeAppState(int appId) { synchronized (mLock) { - mAppIdStates.remove(appId); + final int[] userIds = mState.getUserIds(); + for (final int userId : userIds) { + final UserPermissionState userState = mState.getUserState(userId); + userState.removeUidState(appId); + } } } - private void readPermissionsStateFromPackageSettings() { + private void readStateFromPackageSettings() { + final int[] userIds = getAllUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { + final int appId = ps.getAppId(); + final PermissionsState permissionsState = ps.getPermissionsState(); + synchronized (mLock) { - mAppIdStates.put(ps.getAppId(), new PermissionsState(ps.getPermissionsState())); + for (final int userId : userIds) { + final UserPermissionState userState = mState.getOrCreateUserState(userId); + + userState.setInstallPermissionsFixed(ps.name, ps.areInstallPermissionsFixed()); + final UidPermissionState uidState = userState.getOrCreateUidState(appId); + uidState.reset(); + uidState.setGlobalGids(permissionsState.getGlobalGids()); + uidState.setMissing(permissionsState.isMissing(userId)); + readStateFromPermissionStates(uidState, + permissionsState.getInstallPermissionStates(), false); + readStateFromPermissionStates(uidState, + permissionsState.getRuntimePermissionStates(userId), true); + } } }); } - private void writePermissionsStateToPackageSettings() { + private void readStateFromPermissionStates(@NonNull UidPermissionState uidState, + @NonNull List<PermissionsState.PermissionState> permissionStates, boolean isRuntime) { + final int permissionStatesSize = permissionStates.size(); + for (int i = 0; i < permissionStatesSize; i++) { + final PermissionsState.PermissionState permissionState = permissionStates.get(i); + final BasePermission permission = permissionState.getPermission(); + uidState.putPermissionState(permission, isRuntime, permissionState.isGranted(), + permissionState.getFlags()); + } + } + + private void writeStateToPackageSettings() { + final int[] userIds = mState.getUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { + ps.setInstallPermissionsFixed(false); + final PermissionsState permissionsState = ps.getPermissionsState(); + permissionsState.reset(); + final int appId = ps.getAppId(); + synchronized (mLock) { - final PermissionsState permissionsState = mAppIdStates.get(ps.getAppId()); - if (permissionsState == null) { - Slog.e(TAG, "Missing permissions state for " + ps.name); - return; + for (final int userId : userIds) { + final UserPermissionState userState = mState.getUserState(userId); + if (userState == null) { + Slog.e(TAG, "Missing user state for " + userId); + continue; + } + + if (userState.areInstallPermissionsFixed(ps.name)) { + ps.setInstallPermissionsFixed(true); + } + + final UidPermissionState uidState = userState.getUidState(appId); + if (uidState == null) { + Slog.e(TAG, "Missing permission state for " + ps.name + " and user " + + userId); + continue; + } + + permissionsState.setGlobalGids(uidState.getGlobalGids()); + permissionsState.setMissing(uidState.isMissing(), userId); + final List<PermissionState> permissionStates = uidState.getPermissionStates(); + final int permissionStatesSize = permissionStates.size(); + for (int i = 0; i < permissionStatesSize; i++) { + final PermissionState permissionState = permissionStates.get(i); + + final BasePermission permission = permissionState.getPermission(); + if (permissionState.isGranted()) { + if (permissionState.isRuntime()) { + permissionsState.grantRuntimePermission(permission, userId); + } else { + permissionsState.grantInstallPermission(permission); + } + } + final int flags = permissionState.getFlags(); + if (flags != 0) { + final int flagsUserId = permissionState.isRuntime() ? userId + : UserHandle.USER_ALL; + permissionsState.updatePermissionFlags(permission, flagsUserId, flags, + flags); + } + } } - ps.getPermissionsState().copyFrom(permissionsState); } }); } @@ -4876,12 +4897,12 @@ public class PermissionManagerService extends IPermissionManager.Stub { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } @Override - public void readPermissionsStateFromPackageSettingsTEMP() { - PermissionManagerService.this.readPermissionsStateFromPackageSettings(); + public void readStateFromPackageSettingsTEMP() { + PermissionManagerService.this.readStateFromPackageSettings(); } @Override - public void writePermissionsStateToPackageSettingsTEMP() { - PermissionManagerService.this.writePermissionsStateToPackageSettings(); + public void writeStateToPackageSettingsTEMP() { + PermissionManagerService.this.writeStateToPackageSettings(); } @Override public void onUserRemoved(@UserIdInt int userId) { @@ -4889,7 +4910,7 @@ public class PermissionManagerService extends IPermissionManager.Stub { } @Override public void removePermissionsStateTEMP(int appId) { - PermissionManagerService.this.removePermissionsState(appId); + PermissionManagerService.this.removeAppState(appId); } @Override @UserIdInt diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java index f319bf495e8b..7f6a1d4284d2 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java @@ -266,21 +266,21 @@ public abstract class PermissionManagerServiceInternal extends PermissionManager public abstract void removeAllPermissions(@NonNull AndroidPackage pkg, boolean chatty); /** - * Read {@code PermissionsState} from package settings. + * Read permission state from package settings. * * TODO(zhanghai): This is a temporary method because we should not expose * {@code PackageSetting} which is a implementation detail that permission should not know. * Instead, it should retrieve the legacy state via a defined API. */ - public abstract void readPermissionsStateFromPackageSettingsTEMP(); + public abstract void readStateFromPackageSettingsTEMP(); /** - * Write {@code PermissionsState} from to settings. + * Write permission state to package settings. * * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence * for permission. */ - public abstract void writePermissionsStateToPackageSettingsTEMP(); + public abstract void writeStateToPackageSettingsTEMP(); /** * Notify that a user has been removed and its permission state should be removed as well. diff --git a/services/core/java/com/android/server/pm/permission/PermissionState.java b/services/core/java/com/android/server/pm/permission/PermissionState.java new file mode 100644 index 000000000000..2ed9a50353d4 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/PermissionState.java @@ -0,0 +1,129 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; + +import com.android.internal.annotations.GuardedBy; + +/** + * State for a single permission. + */ +public final class PermissionState { + + @NonNull + private final BasePermission mPermission; + + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private boolean mRuntime; + + @GuardedBy("mLock") + private boolean mGranted; + + @GuardedBy("mLock") + private int mFlags; + + public PermissionState(@NonNull BasePermission permission, boolean isRuntime) { + mPermission = permission; + mRuntime = isRuntime; + } + + public PermissionState(@NonNull PermissionState other) { + this(other.mPermission, other.mRuntime); + + mGranted = other.mGranted; + mFlags = other.mFlags; + } + + @NonNull + public BasePermission getPermission() { + return mPermission; + } + + @NonNull + public String getName() { + return mPermission.getName(); + } + + @Nullable + public int[] computeGids(@UserIdInt int userId) { + return mPermission.computeGids(userId); + } + + public boolean isRuntime() { + synchronized (mLock) { + return mRuntime; + } + } + + public boolean isGranted() { + synchronized (mLock) { + return mGranted; + } + } + + public boolean grant() { + synchronized (mLock) { + if (mGranted) { + return false; + } + mGranted = true; + UidPermissionState.invalidateCache(); + return true; + } + } + + public boolean revoke() { + synchronized (mLock) { + if (!mGranted) { + return false; + } + mGranted = false; + UidPermissionState.invalidateCache(); + return true; + } + } + + public int getFlags() { + synchronized (mLock) { + return mFlags; + } + } + + public boolean updateFlags(int flagMask, int flagValues) { + synchronized (mLock) { + final int newFlags = flagValues & flagMask; + + // Okay to do before the modification because we hold the lock. + UidPermissionState.invalidateCache(); + + final int oldFlags = mFlags; + mFlags = (mFlags & ~flagMask) | newFlags; + return mFlags != oldFlags; + } + } + + public boolean isDefault() { + synchronized (mLock) { + return !mGranted && mFlags == 0; + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/PermissionsState.java b/services/core/java/com/android/server/pm/permission/PermissionsState.java index bad59cb1b567..4fb2d5fc200e 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionsState.java +++ b/services/core/java/com/android/server/pm/permission/PermissionsState.java @@ -86,6 +86,10 @@ public final class PermissionsState { copyFrom(prototype); } + public int[] getGlobalGids() { + return mGlobalGids; + } + /** * Sets the global gids, applicable to all users. * @@ -825,7 +829,7 @@ public final class PermissionsState { PermissionState userState = mUserStates.get(userId); if (userState == null) { - userState = new PermissionState(mPerm.getName()); + userState = new PermissionState(mPerm); mUserStates.put(userId, userState); } @@ -908,7 +912,7 @@ public final class PermissionsState { } return userState.mFlags != oldFlags; } else if (newFlags != 0) { - userState = new PermissionState(mPerm.getName()); + userState = new PermissionState(mPerm); userState.mFlags = newFlags; mUserStates.put(userId, userState); return true; @@ -929,16 +933,16 @@ public final class PermissionsState { } public static final class PermissionState { - private final String mName; + private final BasePermission mPermission; private boolean mGranted; private int mFlags; - public PermissionState(String name) { - mName = name; + public PermissionState(BasePermission permission) { + mPermission = permission; } public PermissionState(PermissionState other) { - mName = other.mName; + mPermission = other.mPermission; mGranted = other.mGranted; mFlags = other.mFlags; } @@ -947,8 +951,12 @@ public final class PermissionsState { return !mGranted && mFlags == 0; } + public BasePermission getPermission() { + return mPermission; + } + public String getName() { - return mName; + return mPermission.getName(); } public boolean isGranted() { diff --git a/services/core/java/com/android/server/pm/permission/UidPermissionState.java b/services/core/java/com/android/server/pm/permission/UidPermissionState.java new file mode 100644 index 000000000000..4c047ffd30e8 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/UidPermissionState.java @@ -0,0 +1,574 @@ +/* + * Copyright (C) 2015 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.PackageManager; +import android.util.ArrayMap; +import android.util.ArraySet; + +import com.android.internal.annotations.GuardedBy; +import com.android.internal.util.ArrayUtils; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + +/** + * Permission state for a UID. + * <p> + * This class is also responsible for keeping track of the Linux GIDs per + * user for a package or a shared user. The GIDs are computed as a set of + * the GIDs for all granted permissions' GIDs on a per user basis. + */ +public final class UidPermissionState { + /** The permission operation failed. */ + public static final int PERMISSION_OPERATION_FAILURE = -1; + + /** The permission operation succeeded and no gids changed. */ + public static final int PERMISSION_OPERATION_SUCCESS = 0; + + /** The permission operation succeeded and gids changed. */ + public static final int PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED = 1; + + private static final int[] NO_GIDS = {}; + + @NonNull + private final Object mLock = new Object(); + + @GuardedBy("mLock") + private ArrayMap<String, PermissionState> mPermissions; + + @NonNull + private int[] mGlobalGids = NO_GIDS; + + private boolean mMissing; + + private boolean mPermissionReviewRequired; + + public UidPermissionState() { + /* do nothing */ + } + + public UidPermissionState(@NonNull UidPermissionState prototype) { + copyFrom(prototype); + } + + /** + * Gets the global gids, applicable to all users. + */ + @NonNull + public int[] getGlobalGids() { + return mGlobalGids; + } + + /** + * Sets the global gids, applicable to all users. + * + * @param globalGids The global gids. + */ + public void setGlobalGids(@NonNull int[] globalGids) { + if (!ArrayUtils.isEmpty(globalGids)) { + mGlobalGids = Arrays.copyOf(globalGids, globalGids.length); + } + } + + static void invalidateCache() { + PackageManager.invalidatePackageInfoCache(); + } + + /** + * Initialized this instance from another one. + * + * @param other The other instance. + */ + public void copyFrom(@NonNull UidPermissionState other) { + if (other == this) { + return; + } + + synchronized (mLock) { + if (mPermissions != null) { + if (other.mPermissions == null) { + mPermissions = null; + } else { + mPermissions.clear(); + } + } + if (other.mPermissions != null) { + if (mPermissions == null) { + mPermissions = new ArrayMap<>(); + } + final int permissionCount = other.mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String name = other.mPermissions.keyAt(i); + PermissionState permissionState = other.mPermissions.valueAt(i); + mPermissions.put(name, new PermissionState(permissionState)); + } + } + } + + mGlobalGids = NO_GIDS; + if (other.mGlobalGids != NO_GIDS) { + mGlobalGids = other.mGlobalGids.clone(); + } + + mMissing = other.mMissing; + + mPermissionReviewRequired = other.mPermissionReviewRequired; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final UidPermissionState other = (UidPermissionState) obj; + + synchronized (mLock) { + if (mPermissions == null) { + if (other.mPermissions != null) { + return false; + } + } else if (!mPermissions.equals(other.mPermissions)) { + return false; + } + } + + if (mMissing != other.mMissing) { + return false; + } + + if (mPermissionReviewRequired != other.mPermissionReviewRequired) { + return false; + } + return Arrays.equals(mGlobalGids, other.mGlobalGids); + } + + /** + * Check whether the permissions state is missing for a user. This can happen if permission + * state is rolled back and we'll need to generate a reasonable default state to keep the app + * usable. + */ + public boolean isMissing() { + return mMissing; + } + + /** + * Set whether the permissions state is missing for a user. This can happen if permission state + * is rolled back and we'll need to generate a reasonable default state to keep the app usable. + */ + public void setMissing(boolean missing) { + mMissing = missing; + } + + public boolean isPermissionReviewRequired() { + return mPermissionReviewRequired; + } + + /** + * Gets whether the state has a given permission. + * + * @param name The permission name. + * @return Whether the state has the permission. + */ + public boolean hasPermission(@NonNull String name) { + synchronized (mLock) { + if (mPermissions == null) { + return false; + } + PermissionState permissionState = mPermissions.get(name); + return permissionState != null && permissionState.isGranted(); + } + } + + /** + * Gets whether the state has a given install permission. + * + * @param name The permission name. + * @return Whether the state has the install permission. + */ + public boolean hasInstallPermission(@NonNull String name) { + synchronized (mLock) { + if (mPermissions == null) { + return false; + } + PermissionState permissionState = mPermissions.get(name); + return permissionState != null && permissionState.isGranted() + && !permissionState.isRuntime(); + } + } + + /** + * Returns whether the state has any known request for the given permission name, + * whether or not it has been granted. + * + * @deprecated Not all requested permissions may be here. + */ + @Deprecated + public boolean hasRequestedPermission(@NonNull ArraySet<String> names) { + synchronized (mLock) { + if (mPermissions == null) { + return false; + } + for (int i = names.size() - 1; i >= 0; i--) { + if (mPermissions.get(names.valueAt(i)) != null) { + return true; + } + } + } + + return false; + } + + /** + * Returns whether the state has any known request for the given permission name, + * whether or not it has been granted. + * + * @deprecated Not all requested permissions may be here. + */ + @Deprecated + public boolean hasRequestedPermission(@NonNull String name) { + return mPermissions != null && (mPermissions.get(name) != null); + } + + /** + * Gets all permissions for a given device user id regardless if they + * are install time or runtime permissions. + * + * @return The permissions or an empty set. + */ + @NonNull + public Set<String> getPermissions() { + synchronized (mLock) { + if (mPermissions == null) { + return Collections.emptySet(); + } + + Set<String> permissions = new ArraySet<>(mPermissions.size()); + + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + String permission = mPermissions.keyAt(i); + + if (hasPermission(permission)) { + permissions.add(permission); + } + } + + return permissions; + } + } + + /** + * Gets the flags for a permission. + * + * @param name The permission name. + * @return The permission state or null if no such. + */ + public int getPermissionFlags(@NonNull String name) { + PermissionState permState = getPermissionState(name); + if (permState != null) { + return permState.getFlags(); + } + return 0; + } + + /** + * Update the flags associated with a given permission. + * @param permission The permission whose flags to update. + * @param flagMask Mask for which flags to change. + * @param flagValues New values for the mask flags. + * @return Whether the permission flags changed. + */ + public boolean updatePermissionFlags(@NonNull BasePermission permission, int flagMask, + int flagValues) { + if (flagMask == 0) { + return false; + } + + PermissionState permissionState = ensurePermissionState(permission); + + final int oldFlags = permissionState.getFlags(); + + synchronized (mLock) { + final boolean updated = permissionState.updateFlags(flagMask, flagValues); + if (updated) { + final int newFlags = permissionState.getFlags(); + if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0 + && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + mPermissionReviewRequired = true; + } else if ((oldFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0 + && (newFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) == 0) { + if (mPermissionReviewRequired && !hasPermissionRequiringReview()) { + mPermissionReviewRequired = false; + } + } + } + return updated; + } + } + + private boolean hasPermissionRequiringReview() { + synchronized (mLock) { + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + final PermissionState permission = mPermissions.valueAt(i); + if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + return true; + } + } + } + return false; + } + + public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) { + synchronized (mLock) { + if (mPermissions == null) { + return false; + } + boolean changed = false; + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + PermissionState permissionState = mPermissions.valueAt(i); + changed |= permissionState.updateFlags(flagMask, flagValues); + } + return changed; + } + } + + /** + * Compute the Linux gids for a given device user from the permissions + * granted to this user. Note that these are computed to avoid additional + * state as they are rarely accessed. + * + * @param userId The device user id. + * @return The gids for the device user. + */ + @NonNull + public int[] computeGids(@UserIdInt int userId) { + int[] gids = mGlobalGids; + + synchronized (mLock) { + if (mPermissions != null) { + final int permissionCount = mPermissions.size(); + for (int i = 0; i < permissionCount; i++) { + PermissionState permissionState = mPermissions.valueAt(i); + if (!permissionState.isGranted()) { + continue; + } + final int[] permGids = permissionState.computeGids(userId); + if (permGids != NO_GIDS) { + gids = appendInts(gids, permGids); + } + } + } + } + + return gids; + } + + /** + * Compute the Linux gids for all device users from the permissions + * granted to these users. + * + * @return The gids for all device users. + */ + @NonNull + public int[] computeGids(@NonNull int[] userIds) { + int[] gids = mGlobalGids; + + for (int userId : userIds) { + final int[] userGids = computeGids(userId); + gids = appendInts(gids, userGids); + } + + return gids; + } + + /** + * Resets the internal state of this object. + */ + public void reset() { + mGlobalGids = NO_GIDS; + + synchronized (mLock) { + mPermissions = null; + invalidateCache(); + } + + mMissing = false; + mPermissionReviewRequired = false; + } + + /** + * Gets the state for a permission or null if no such. + * + * @param name The permission name. + * @return The permission state. + */ + @Nullable + public PermissionState getPermissionState(@NonNull String name) { + synchronized (mLock) { + if (mPermissions == null) { + return null; + } + return mPermissions.get(name); + } + } + + /** + * Gets all permission states. + * + * @return The permission states or an empty set. + */ + @NonNull + public List<PermissionState> getPermissionStates() { + synchronized (mLock) { + if (mPermissions == null) { + return Collections.emptyList(); + } + return new ArrayList<>(mPermissions.values()); + } + } + + /** + * Put a permission state. + */ + public void putPermissionState(@NonNull BasePermission permission, boolean isRuntime, + boolean isGranted, int flags) { + synchronized (mLock) { + ensureNoPermissionState(permission.name); + PermissionState permissionState = ensurePermissionState(permission, isRuntime); + if (isGranted) { + permissionState.grant(); + } + permissionState.updateFlags(flags, flags); + if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { + mPermissionReviewRequired = true; + } + } + } + + /** + * Grant a permission. + * + * @param permission The permission to grant. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int grantPermission(@NonNull BasePermission permission) { + if (hasPermission(permission.getName())) { + return PERMISSION_OPERATION_SUCCESS; + } + + PermissionState permissionState = ensurePermissionState(permission); + + if (!permissionState.grant()) { + return PERMISSION_OPERATION_FAILURE; + } + + return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED + : PERMISSION_OPERATION_SUCCESS; + } + + /** + * Revoke a permission. + * + * @param permission The permission to revoke. + * @return The operation result which is either {@link #PERMISSION_OPERATION_SUCCESS}, + * or {@link #PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED}, or {@link + * #PERMISSION_OPERATION_FAILURE}. + */ + public int revokePermission(@NonNull BasePermission permission) { + final String permissionName = permission.getName(); + if (!hasPermission(permissionName)) { + return PERMISSION_OPERATION_SUCCESS; + } + + PermissionState permissionState; + synchronized (mLock) { + permissionState = mPermissions.get(permissionName); + } + + if (!permissionState.revoke()) { + return PERMISSION_OPERATION_FAILURE; + } + + if (permissionState.isDefault()) { + ensureNoPermissionState(permissionName); + } + + return permission.hasGids() ? PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED + : PERMISSION_OPERATION_SUCCESS; + } + + // TODO: fix this to use arraycopy and append all ints in one go + private static int[] appendInts(int[] current, int[] added) { + if (current != null && added != null) { + for (int guid : added) { + current = ArrayUtils.appendInt(current, guid); + } + } + return current; + } + + @NonNull + private PermissionState ensurePermissionState(@NonNull BasePermission permission) { + return ensurePermissionState(permission, permission.isRuntime()); + } + + @NonNull + private PermissionState ensurePermissionState(@NonNull BasePermission permission, + boolean isRuntime) { + final String permissionName = permission.getName(); + synchronized (mLock) { + if (mPermissions == null) { + mPermissions = new ArrayMap<>(); + } + PermissionState permissionState = mPermissions.get(permissionName); + if (permissionState == null) { + permissionState = new PermissionState(permission, isRuntime); + mPermissions.put(permissionName, permissionState); + } + return permissionState; + } + } + + private void ensureNoPermissionState(@NonNull String name) { + synchronized (mLock) { + if (mPermissions == null) { + return; + } + mPermissions.remove(name); + if (mPermissions.isEmpty()) { + mPermissions = null; + } + } + } +} diff --git a/services/core/java/com/android/server/pm/permission/UserPermissionState.java b/services/core/java/com/android/server/pm/permission/UserPermissionState.java new file mode 100644 index 000000000000..7f55cb161e40 --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/UserPermissionState.java @@ -0,0 +1,103 @@ +/* + * Copyright (C) 2020 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.AppIdInt; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.os.UserHandle; +import android.util.ArraySet; +import android.util.SparseArray; + +import com.android.internal.annotations.GuardedBy; + +/** + * Permission state for a user. + */ +public final class UserPermissionState { + /** + * Whether the install permissions have been granted to a package, so that no install + * permissions should be added to it unless the package is upgraded. + */ + @GuardedBy("mLock") + @NonNull + private final ArraySet<String> mInstallPermissionsFixed = new ArraySet<>(); + + /** + * Maps from app ID to {@link UidPermissionState}. + */ + @GuardedBy("mLock") + @NonNull + private final SparseArray<UidPermissionState> mUidStates = new SparseArray<>(); + + @NonNull + private final Object mLock; + + public UserPermissionState(@NonNull Object lock) { + mLock = lock; + } + + public boolean areInstallPermissionsFixed(@NonNull String packageName) { + synchronized (mLock) { + return mInstallPermissionsFixed.contains(packageName); + } + } + + public void setInstallPermissionsFixed(@NonNull String packageName, boolean fixed) { + synchronized (mLock) { + if (fixed) { + mInstallPermissionsFixed.add(packageName); + } else { + mInstallPermissionsFixed.remove(packageName); + } + } + } + + @Nullable + public UidPermissionState getUidState(@AppIdInt int appId) { + checkAppId(appId); + synchronized (mLock) { + return mUidStates.get(appId); + } + } + + @NonNull + public UidPermissionState getOrCreateUidState(@AppIdInt int appId) { + checkAppId(appId); + synchronized (mLock) { + UidPermissionState uidState = mUidStates.get(appId); + if (uidState == null) { + uidState = new UidPermissionState(); + mUidStates.put(appId, uidState); + } + return uidState; + } + } + + public void removeUidState(@AppIdInt int appId) { + checkAppId(appId); + synchronized (mLock) { + mUidStates.delete(appId); + } + } + + private void checkAppId(@AppIdInt int appId) { + if (UserHandle.getUserId(appId) != 0) { + throw new IllegalArgumentException(appId + " is not an app ID"); + } + } +} |