blob: 3ef4e46e5b5c8291ecc717417d887cccd1dc2d92 [file] [log] [blame]
/*
* 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 android.util.IntArray;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
/**
* Permission state for a UID.
*/
public final class UidPermissionState {
private boolean mMissing;
@Nullable
private ArrayMap<String, PermissionState> mPermissions;
public UidPermissionState() {}
public UidPermissionState(@NonNull UidPermissionState other) {
mMissing = other.mMissing;
if (other.mPermissions != null) {
mPermissions = new ArrayMap<>();
final int permissionsSize = other.mPermissions.size();
for (int i = 0; i < permissionsSize; i++) {
final String name = other.mPermissions.keyAt(i);
final PermissionState permissionState = other.mPermissions.valueAt(i);
mPermissions.put(name, new PermissionState(permissionState));
}
}
}
/**
* Reset the internal state of this object.
*/
public void reset() {
mMissing = false;
mPermissions = null;
invalidateCache();
}
/**
* 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;
}
/**
* Get whether there is a permission state for a permission.
*
* @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
@Deprecated
public boolean hasPermissionState(@NonNull String name) {
return mPermissions != null && mPermissions.containsKey(name);
}
/**
* Get whether there is a permission state for any of the permissions.
*
* @deprecated This used to be named hasRequestedPermission() and its usage is confusing
*/
@Deprecated
public boolean hasPermissionState(@NonNull ArraySet<String> names) {
if (mPermissions == null) {
return false;
}
final int namesSize = names.size();
for (int i = 0; i < namesSize; i++) {
final String name = names.valueAt(i);
if (mPermissions.containsKey(name)) {
return true;
}
}
return false;
}
/**
* Gets the state for a permission or null if none.
*
* @param name the permission name
* @return the permission state
*/
@Nullable
public PermissionState getPermissionState(@NonNull String name) {
if (mPermissions == null) {
return null;
}
return mPermissions.get(name);
}
@NonNull
private PermissionState getOrCreatePermissionState(@NonNull Permission permission) {
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
}
final String name = permission.getName();
PermissionState permissionState = mPermissions.get(name);
if (permissionState == null) {
permissionState = new PermissionState(permission);
mPermissions.put(name, permissionState);
}
return permissionState;
}
/**
* Get all permission states.
*
* @return the permission states
*/
@NonNull
public List<PermissionState> getPermissionStates() {
if (mPermissions == null) {
return Collections.emptyList();
}
return new ArrayList<>(mPermissions.values());
}
/**
* Put a permission state.
*
* @param permission the permission
* @param granted whether the permission is granted
* @param flags the permission flags
*/
public void putPermissionState(@NonNull Permission permission, boolean granted, int flags) {
final String name = permission.getName();
if (mPermissions == null) {
mPermissions = new ArrayMap<>();
} else {
mPermissions.remove(name);
}
final PermissionState permissionState = new PermissionState(permission);
if (granted) {
permissionState.grant();
}
permissionState.updateFlags(flags, flags);
mPermissions.put(name, permissionState);
}
/**
* Remove a permission state.
*
* @param name the permission name
* @return whether the permission state changed
*/
public boolean removePermissionState(@NonNull String name) {
if (mPermissions == null) {
return false;
}
final boolean changed = mPermissions.remove(name) != null;
if (changed && mPermissions.isEmpty()) {
mPermissions = null;
}
return changed;
}
/**
* Get whether a permission is granted.
*
* @param name the permission name
* @return whether the permission is granted
*/
public boolean isPermissionGranted(@NonNull String name) {
final PermissionState permissionState = getPermissionState(name);
return permissionState != null && permissionState.isGranted();
}
/**
* Get all the granted permissions.
*
* @return the granted permissions
*/
@NonNull
public Set<String> getGrantedPermissions() {
if (mPermissions == null) {
return Collections.emptySet();
}
final Set<String> permissions = new ArraySet<>(mPermissions.size());
final int permissionsSize = mPermissions.size();
for (int i = 0; i < permissionsSize; i++) {
final PermissionState permissionState = mPermissions.valueAt(i);
if (permissionState.isGranted()) {
permissions.add(permissionState.getName());
}
}
return permissions;
}
/**
* Grant a permission.
*
* @param permission the permission to grant
* @return whether the permission grant state changed
*/
public boolean grantPermission(@NonNull Permission permission) {
final PermissionState permissionState = getOrCreatePermissionState(permission);
return permissionState.grant();
}
/**
* Revoke a permission.
*
* @param permission the permission to revoke
* @return whether the permission grant state changed
*/
public boolean revokePermission(@NonNull Permission permission) {
final String name = permission.getName();
final PermissionState permissionState = getPermissionState(name);
if (permissionState == null) {
return false;
}
final boolean changed = permissionState.revoke();
if (changed && permissionState.isDefault()) {
removePermissionState(name);
}
return changed;
}
/**
* Get the flags for a permission.
*
* @param name the permission name
* @return the permission flags
*/
public int getPermissionFlags(@NonNull String name) {
final PermissionState permissionState = getPermissionState(name);
if (permissionState == null) {
return 0;
}
return permissionState.getFlags();
}
/**
* Update the flags for a permission.
*
* @param permission the permission name
* @param flagMask the mask for the flags
* @param flagValues the new values for the masked flags
* @return whether the permission flags changed
*/
public boolean updatePermissionFlags(@NonNull Permission permission, int flagMask,
int flagValues) {
if (flagMask == 0) {
return false;
}
final PermissionState permissionState = getOrCreatePermissionState(permission);
final boolean changed = permissionState.updateFlags(flagMask, flagValues);
if (changed && permissionState.isDefault()) {
removePermissionState(permission.getName());
}
return changed;
}
public boolean updatePermissionFlagsForAllPermissions(int flagMask, int flagValues) {
if (flagMask == 0) {
return false;
}
if (mPermissions == null) {
return false;
}
boolean anyChanged = false;
for (int i = mPermissions.size() - 1; i >= 0; i--) {
final PermissionState permissionState = mPermissions.valueAt(i);
final boolean changed = permissionState.updateFlags(flagMask, flagValues);
if (changed && permissionState.isDefault()) {
mPermissions.removeAt(i);
}
anyChanged |= changed;
}
return anyChanged;
}
public boolean isPermissionsReviewRequired() {
if (mPermissions == null) {
return false;
}
final int permissionsSize = mPermissions.size();
for (int i = 0; i < permissionsSize; i++) {
final PermissionState permission = mPermissions.valueAt(i);
if ((permission.getFlags() & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
return true;
}
}
return false;
}
/**
* Compute the Linux GIDs from the permissions granted to a user.
*
* @param userId the user ID
* @return the GIDs for the user
*/
@NonNull
public int[] computeGids(@NonNull int[] globalGids, @UserIdInt int userId) {
IntArray gids = IntArray.wrap(globalGids);
if (mPermissions == null) {
return gids.toArray();
}
final int permissionsSize = mPermissions.size();
for (int i = 0; i < permissionsSize; i++) {
PermissionState permissionState = mPermissions.valueAt(i);
if (!permissionState.isGranted()) {
continue;
}
final int[] permissionGids = permissionState.computeGids(userId);
if (permissionGids.length != 0) {
gids.addAll(permissionGids);
}
}
return gids.toArray();
}
static void invalidateCache() {
PackageManager.invalidatePackageInfoCache();
}
}