summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Hai Zhang <zhanghai@google.com> 2020-08-25 22:15:45 -0700
committer Hai Zhang <zhanghai@google.com> 2020-09-10 15:20:14 -0700
commit97a79bdb71ae03049c974a151db5071abfc8956d (patch)
tree457253dd07118f2c2f5e049750cdb42456be4ef6
parent056630f142a730fe852155033e84a107ddbef5c1 (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
-rw-r--r--core/res/res/values/attrs_manifest.xml4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java16
-rw-r--r--services/core/java/com/android/server/pm/permission/BasePermission.java9
-rw-r--r--services/core/java/com/android/server/pm/permission/DevicePermissionState.java77
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerService.java1077
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionManagerServiceInternal.java8
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionState.java129
-rw-r--r--services/core/java/com/android/server/pm/permission/PermissionsState.java22
-rw-r--r--services/core/java/com/android/server/pm/permission/UidPermissionState.java574
-rw-r--r--services/core/java/com/android/server/pm/permission/UserPermissionState.java103
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");
+ }
+ }
+}