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 f319bf495e8b8..7f6a1d4284d20 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 0000000000000..2ed9a50353d47
--- /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 bad59cb1b5676..4fb2d5fc200e4 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 0000000000000..4c047ffd30e8a
--- /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.
+ *
+ * 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 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 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 getPermissions() {
+ synchronized (mLock) {
+ if (mPermissions == null) {
+ return Collections.emptySet();
+ }
+
+ Set 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 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 0000000000000..7f55cb161e405
--- /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 mInstallPermissionsFixed = new ArraySet<>();
+
+ /**
+ * Maps from app ID to {@link UidPermissionState}.
+ */
+ @GuardedBy("mLock")
+ @NonNull
+ private final SparseArray 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");
+ }
+ }
+}