/*
|
* Copyright (C) 2017 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 static android.Manifest.permission.READ_EXTERNAL_STORAGE;
|
import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE;
|
import static android.content.pm.PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_FIXED;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_USER_SET;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM;
|
import static android.content.pm.PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE;
|
import static android.content.pm.PackageManager.MASK_PERMISSION_FLAGS_ALL;
|
import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER;
|
|
import static com.android.server.pm.PackageManagerService.DEBUG_INSTALL;
|
import static com.android.server.pm.PackageManagerService.DEBUG_PACKAGE_SCANNING;
|
import static com.android.server.pm.PackageManagerService.DEBUG_PERMISSIONS;
|
import static com.android.server.pm.PackageManagerService.DEBUG_REMOVE;
|
import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
|
import static com.android.server.pm.permission.PermissionsState.PERMISSION_OPERATION_FAILURE;
|
|
import static java.util.concurrent.TimeUnit.SECONDS;
|
|
import android.Manifest;
|
import android.annotation.NonNull;
|
import android.annotation.Nullable;
|
import android.annotation.UserIdInt;
|
import android.content.Context;
|
import android.content.pm.ApplicationInfo;
|
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager.PermissionWhitelistFlags;
|
import android.content.pm.PackageManagerInternal;
|
import android.content.pm.PackageParser;
|
import android.content.pm.PackageParser.Package;
|
import android.content.pm.PermissionGroupInfo;
|
import android.content.pm.PermissionInfo;
|
import android.metrics.LogMaker;
|
import android.os.Binder;
|
import android.os.Build;
|
import android.os.Handler;
|
import android.os.HandlerThread;
|
import android.os.Process;
|
import android.os.Trace;
|
import android.os.UserHandle;
|
import android.os.UserManager;
|
import android.os.UserManagerInternal;
|
import android.os.storage.StorageManager;
|
import android.os.storage.StorageManagerInternal;
|
import android.permission.PermissionControllerManager;
|
import android.permission.PermissionManager;
|
import android.permission.PermissionManagerInternal;
|
import android.permission.PermissionManagerInternal.OnRuntimePermissionStateChangedListener;
|
import android.text.TextUtils;
|
import android.util.ArrayMap;
|
import android.util.ArraySet;
|
import android.util.EventLog;
|
import android.util.Log;
|
import android.util.Slog;
|
import android.util.SparseArray;
|
import android.util.SparseBooleanArray;
|
|
import com.android.internal.annotations.GuardedBy;
|
import com.android.internal.logging.MetricsLogger;
|
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
|
import com.android.internal.os.RoSystemProperties;
|
import com.android.internal.util.ArrayUtils;
|
import com.android.internal.util.function.pooled.PooledLambda;
|
import com.android.server.FgThread;
|
import com.android.server.LocalServices;
|
import com.android.server.ServiceThread;
|
import com.android.server.SystemConfig;
|
import com.android.server.Watchdog;
|
import com.android.server.pm.PackageManagerServiceUtils;
|
import com.android.server.pm.PackageSetting;
|
import com.android.server.pm.SharedUserSetting;
|
import com.android.server.pm.UserManagerService;
|
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.util.ArrayList;
|
import java.util.Collection;
|
import java.util.HashMap;
|
import java.util.Iterator;
|
import java.util.List;
|
import java.util.Map;
|
import java.util.Objects;
|
import java.util.Set;
|
import java.util.concurrent.CompletableFuture;
|
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeoutException;
|
|
/**
|
* Manages all permissions and handles permissions related tasks.
|
*/
|
public class PermissionManagerService {
|
private static final String TAG = "PackageManager";
|
|
/** Permission grant: not grant the permission. */
|
private static final int GRANT_DENIED = 1;
|
/** Permission grant: grant the permission as an install permission. */
|
private static final int GRANT_INSTALL = 2;
|
/** Permission grant: grant the permission as a runtime one. */
|
private static final int GRANT_RUNTIME = 3;
|
/** Permission grant: grant as runtime a permission that was granted as an install time one. */
|
private static final int GRANT_UPGRADE = 4;
|
|
private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60);
|
|
/** Cap the size of permission trees that 3rd party apps can define; in characters of text */
|
private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768;
|
/** Empty array to avoid allocations */
|
private static final int[] EMPTY_INT_ARRAY = new int[0];
|
|
/**
|
* When these flags are set, the system should not automatically modify the permission grant
|
* state.
|
*/
|
private static final int BLOCKING_PERMISSION_FLAGS = FLAG_PERMISSION_SYSTEM_FIXED
|
| FLAG_PERMISSION_POLICY_FIXED
|
| FLAG_PERMISSION_GRANTED_BY_DEFAULT;
|
|
/** Permission flags set by the user */
|
private static final int USER_PERMISSION_FLAGS = FLAG_PERMISSION_USER_SET
|
| FLAG_PERMISSION_USER_FIXED;
|
|
/** If the permission of the value is granted, so is the key */
|
private static final Map<String, String> FULLER_PERMISSION_MAP = new HashMap<>();
|
|
static {
|
FULLER_PERMISSION_MAP.put(Manifest.permission.ACCESS_COARSE_LOCATION,
|
Manifest.permission.ACCESS_FINE_LOCATION);
|
FULLER_PERMISSION_MAP.put(Manifest.permission.INTERACT_ACROSS_USERS,
|
Manifest.permission.INTERACT_ACROSS_USERS_FULL);
|
}
|
|
/** Lock to protect internal data access */
|
private final Object mLock;
|
|
/** Internal connection to the package manager */
|
private final PackageManagerInternal mPackageManagerInt;
|
|
/** Internal connection to the user manager */
|
private final UserManagerInternal mUserManagerInt;
|
|
/** Permission controller: User space permission management */
|
private PermissionControllerManager mPermissionControllerManager;
|
|
/** Default permission policy to provide proper behaviour out-of-the-box */
|
private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy;
|
|
/**
|
* Built-in permissions. Read from system configuration files. Mapping is from
|
* UID to permission name.
|
*/
|
private final SparseArray<ArraySet<String>> mSystemPermissions;
|
|
/** Built-in group IDs given to all packages. Read from system configuration files. */
|
private final int[] mGlobalGids;
|
|
private final HandlerThread mHandlerThread;
|
private final Handler mHandler;
|
private final Context mContext;
|
private final MetricsLogger mMetricsLogger = new MetricsLogger();
|
|
/** Internal storage for permissions and related settings */
|
@GuardedBy("mLock")
|
private final PermissionSettings mSettings;
|
|
@GuardedBy("mLock")
|
private ArraySet<String> mPrivappPermissionsViolations;
|
|
@GuardedBy("mLock")
|
private boolean mSystemReady;
|
|
@GuardedBy("mLock")
|
private PermissionPolicyInternal mPermissionPolicyInternal;
|
|
/**
|
* For each foreground/background permission the mapping:
|
* Background permission -> foreground permissions
|
*/
|
@GuardedBy("mLock")
|
private ArrayMap<String, List<String>> mBackgroundPermissions;
|
|
/**
|
* A permission backup might contain apps that are not installed. In this case we delay the
|
* restoration until the app is installed.
|
*
|
* <p>This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where
|
* there is <u>no more</u> delayed backup left.
|
*/
|
@GuardedBy("mLock")
|
private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray();
|
|
/** Listeners for permission state (granting and flags) changes */
|
@GuardedBy("mLock")
|
final private ArrayList<OnRuntimePermissionStateChangedListener>
|
mRuntimePermissionStateChangedListeners = new ArrayList<>();
|
|
PermissionManagerService(Context context,
|
@NonNull Object externalLock) {
|
mContext = context;
|
mLock = externalLock;
|
mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class);
|
mUserManagerInt = LocalServices.getService(UserManagerInternal.class);
|
mSettings = new PermissionSettings(mLock);
|
|
mHandlerThread = new ServiceThread(TAG,
|
Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/);
|
mHandlerThread.start();
|
mHandler = new Handler(mHandlerThread.getLooper());
|
Watchdog.getInstance().addThread(mHandler);
|
|
mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy(
|
context, mHandlerThread.getLooper(), this);
|
SystemConfig systemConfig = SystemConfig.getInstance();
|
mSystemPermissions = systemConfig.getSystemPermissions();
|
mGlobalGids = systemConfig.getGlobalGids();
|
|
// propagate permission configuration
|
final ArrayMap<String, SystemConfig.PermissionEntry> permConfig =
|
SystemConfig.getInstance().getPermissions();
|
synchronized (mLock) {
|
for (int i=0; i<permConfig.size(); i++) {
|
final SystemConfig.PermissionEntry perm = permConfig.valueAt(i);
|
BasePermission bp = mSettings.getPermissionLocked(perm.name);
|
if (bp == null) {
|
bp = new BasePermission(perm.name, "android", BasePermission.TYPE_BUILTIN);
|
mSettings.putPermissionLocked(perm.name, bp);
|
}
|
if (perm.gids != null) {
|
bp.setGids(perm.gids, perm.perUser);
|
}
|
}
|
}
|
|
PermissionManagerServiceInternalImpl localService =
|
new PermissionManagerServiceInternalImpl();
|
LocalServices.addService(PermissionManagerServiceInternal.class, localService);
|
LocalServices.addService(PermissionManagerInternal.class, localService);
|
}
|
|
/**
|
* Creates and returns an initialized, internal service for use by other components.
|
* <p>
|
* The object returned is identical to the one returned by the LocalServices class using:
|
* {@code LocalServices.getService(PermissionManagerServiceInternal.class);}
|
* <p>
|
* NOTE: The external lock is temporary and should be removed. This needs to be a
|
* lock created by the permission manager itself.
|
*/
|
public static PermissionManagerServiceInternal create(Context context,
|
@NonNull Object externalLock) {
|
final PermissionManagerServiceInternal permMgrInt =
|
LocalServices.getService(PermissionManagerServiceInternal.class);
|
if (permMgrInt != null) {
|
return permMgrInt;
|
}
|
new PermissionManagerService(context, externalLock);
|
return LocalServices.getService(PermissionManagerServiceInternal.class);
|
}
|
|
@Nullable BasePermission getPermission(String permName) {
|
synchronized (mLock) {
|
return mSettings.getPermissionLocked(permName);
|
}
|
}
|
|
private int checkPermission(String permName, String pkgName, int callingUid, int userId) {
|
if (!mUserManagerInt.exists(userId)) {
|
return PackageManager.PERMISSION_DENIED;
|
}
|
|
final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName);
|
if (pkg != null && pkg.mExtras != null) {
|
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
|
return PackageManager.PERMISSION_DENIED;
|
}
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
final boolean instantApp = ps.getInstantApp(userId);
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
if (permissionsState.hasPermission(permName, userId)) {
|
if (instantApp) {
|
synchronized (mLock) {
|
BasePermission bp = mSettings.getPermissionLocked(permName);
|
if (bp != null && bp.isInstant()) {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
}
|
} else {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
}
|
if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
}
|
|
return PackageManager.PERMISSION_DENIED;
|
}
|
|
private int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
|
int callingUid) {
|
final int callingUserId = UserHandle.getUserId(callingUid);
|
final boolean isCallerInstantApp =
|
mPackageManagerInt.getInstantAppPackageName(callingUid) != null;
|
final boolean isUidInstantApp =
|
mPackageManagerInt.getInstantAppPackageName(uid) != null;
|
final int userId = UserHandle.getUserId(uid);
|
if (!mUserManagerInt.exists(userId)) {
|
return PackageManager.PERMISSION_DENIED;
|
}
|
|
if (pkg != null) {
|
if (pkg.mSharedUserId != null) {
|
if (isCallerInstantApp) {
|
return PackageManager.PERMISSION_DENIED;
|
}
|
} else if (mPackageManagerInt.filterAppAccess(pkg, callingUid, callingUserId)) {
|
return PackageManager.PERMISSION_DENIED;
|
}
|
final PermissionsState permissionsState =
|
((PackageSetting) pkg.mExtras).getPermissionsState();
|
if (permissionsState.hasPermission(permName, userId)) {
|
if (isUidInstantApp) {
|
if (mSettings.isPermissionInstant(permName)) {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
} else {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
}
|
if (isImpliedPermissionGranted(permissionsState, permName, userId)) {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
} else {
|
ArraySet<String> perms = mSystemPermissions.get(uid);
|
if (perms != null) {
|
if (perms.contains(permName)) {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
if (FULLER_PERMISSION_MAP.containsKey(permName)
|
&& perms.contains(FULLER_PERMISSION_MAP.get(permName))) {
|
return PackageManager.PERMISSION_GRANTED;
|
}
|
}
|
}
|
return PackageManager.PERMISSION_DENIED;
|
}
|
|
/**
|
* Get the state of the runtime permissions as xml file.
|
*
|
* <p>Can not be called on main thread.
|
*
|
* @param user The user the data should be extracted for
|
*
|
* @return The state as a xml file
|
*/
|
private @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
|
CompletableFuture<byte[]> backup = new CompletableFuture<>();
|
mPermissionControllerManager.getRuntimePermissionBackup(user, mContext.getMainExecutor(),
|
backup::complete);
|
|
try {
|
return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS);
|
} catch (InterruptedException | ExecutionException | TimeoutException e) {
|
Slog.e(TAG, "Cannot create permission backup for " + user, e);
|
return null;
|
}
|
}
|
|
/**
|
* Restore a permission state previously backed up via {@link #backupRuntimePermissions}.
|
*
|
* <p>If not all state can be restored, the un-appliable state will be delayed and can be
|
* applied via {@link #restoreDelayedRuntimePermissions}.
|
*
|
* @param backup The state as an xml file
|
* @param user The user the data should be restored for
|
*/
|
private void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
|
synchronized (mLock) {
|
mHasNoDelayedPermBackup.delete(user.getIdentifier());
|
mPermissionControllerManager.restoreRuntimePermissionBackup(backup, user);
|
}
|
}
|
|
/**
|
* Try to apply permission backup that was previously not applied.
|
*
|
* <p>Can not be called on main thread.
|
*
|
* @param packageName The package that is newly installed
|
* @param user The user the package is installed for
|
*
|
* @see #restoreRuntimePermissions
|
*/
|
private void restoreDelayedRuntimePermissions(@NonNull String packageName,
|
@NonNull UserHandle user) {
|
synchronized (mLock) {
|
if (mHasNoDelayedPermBackup.get(user.getIdentifier(), false)) {
|
return;
|
}
|
|
mPermissionControllerManager.restoreDelayedRuntimePermissionBackup(packageName, user,
|
mContext.getMainExecutor(), (hasMoreBackup) -> {
|
if (hasMoreBackup) {
|
return;
|
}
|
|
synchronized (mLock) {
|
mHasNoDelayedPermBackup.put(user.getIdentifier(), true);
|
}
|
});
|
}
|
}
|
|
private void addOnRuntimePermissionStateChangedListener(@NonNull
|
OnRuntimePermissionStateChangedListener listener) {
|
synchronized (mLock) {
|
mRuntimePermissionStateChangedListeners.add(listener);
|
}
|
}
|
|
private void removeOnRuntimePermissionStateChangedListener(@NonNull
|
OnRuntimePermissionStateChangedListener listener) {
|
synchronized (mLock) {
|
mRuntimePermissionStateChangedListeners.remove(listener);
|
}
|
}
|
|
private void notifyRuntimePermissionStateChanged(@NonNull String packageName,
|
@UserIdInt int userId) {
|
FgThread.getHandler().sendMessage(PooledLambda.obtainMessage
|
(PermissionManagerService::doNotifyRuntimePermissionStateChanged,
|
PermissionManagerService.this, packageName, userId));
|
}
|
|
private void doNotifyRuntimePermissionStateChanged(@NonNull String packageName,
|
@UserIdInt int userId) {
|
final ArrayList<OnRuntimePermissionStateChangedListener> listeners;
|
synchronized (mLock) {
|
if (mRuntimePermissionStateChangedListeners.isEmpty()) {
|
return;
|
}
|
listeners = new ArrayList<>(mRuntimePermissionStateChangedListeners);
|
}
|
final int listenerCount = listeners.size();
|
for (int i = 0; i < listenerCount; i++) {
|
listeners.get(i).onRuntimePermissionStateChanged(packageName, userId);
|
}
|
}
|
|
/**
|
* Returns {@code true} if the permission can be implied from another granted permission.
|
* <p>Some permissions, such as ACCESS_FINE_LOCATION, imply other permissions,
|
* such as ACCESS_COURSE_LOCATION. If the caller holds an umbrella permission, give
|
* it access to any implied permissions.
|
*/
|
private static boolean isImpliedPermissionGranted(PermissionsState permissionsState,
|
String permName, int userId) {
|
return FULLER_PERMISSION_MAP.containsKey(permName)
|
&& permissionsState.hasPermission(FULLER_PERMISSION_MAP.get(permName), userId);
|
}
|
|
private PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
|
int callingUid) {
|
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
|
return null;
|
}
|
synchronized (mLock) {
|
return PackageParser.generatePermissionGroupInfo(
|
mSettings.mPermissionGroups.get(groupName), flags);
|
}
|
}
|
|
private List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) {
|
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
|
return null;
|
}
|
synchronized (mLock) {
|
final int N = mSettings.mPermissionGroups.size();
|
final ArrayList<PermissionGroupInfo> out
|
= new ArrayList<PermissionGroupInfo>(N);
|
for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) {
|
out.add(PackageParser.generatePermissionGroupInfo(pg, flags));
|
}
|
return out;
|
}
|
}
|
|
private PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
|
int callingUid) {
|
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
|
return null;
|
}
|
// reader
|
synchronized (mLock) {
|
final BasePermission bp = mSettings.getPermissionLocked(permName);
|
if (bp == null) {
|
return null;
|
}
|
final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked(
|
bp.getProtectionLevel(), packageName, callingUid);
|
return bp.generatePermissionInfo(adjustedProtectionLevel, flags);
|
}
|
}
|
|
private List<PermissionInfo> getPermissionInfoByGroup(
|
String groupName, int flags, int callingUid) {
|
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
|
return null;
|
}
|
synchronized (mLock) {
|
if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) {
|
return null;
|
}
|
final ArrayList<PermissionInfo> out = new ArrayList<PermissionInfo>(10);
|
for (BasePermission bp : mSettings.mPermissions.values()) {
|
final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags);
|
if (pi != null) {
|
out.add(pi);
|
}
|
}
|
return out;
|
}
|
}
|
|
private int adjustPermissionProtectionFlagsLocked(
|
int protectionLevel, String packageName, int uid) {
|
// Signature permission flags area always reported
|
final int protectionLevelMasked = protectionLevel
|
& (PermissionInfo.PROTECTION_NORMAL
|
| PermissionInfo.PROTECTION_DANGEROUS
|
| PermissionInfo.PROTECTION_SIGNATURE);
|
if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) {
|
return protectionLevel;
|
}
|
// System sees all flags.
|
final int appId = UserHandle.getAppId(uid);
|
if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID
|
|| appId == Process.SHELL_UID) {
|
return protectionLevel;
|
}
|
// Normalize package name to handle renamed packages and static libs
|
final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
|
if (pkg == null) {
|
return protectionLevel;
|
}
|
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) {
|
return protectionLevelMasked;
|
}
|
// Apps that target O see flags for all protection levels.
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
if (ps == null) {
|
return protectionLevel;
|
}
|
if (ps.getAppId() != appId) {
|
return protectionLevel;
|
}
|
return protectionLevel;
|
}
|
|
/**
|
* We might auto-grant permissions if any permission of the group is already granted. Hence if
|
* the group of a granted permission changes we need to revoke it to avoid having permissions of
|
* the new group auto-granted.
|
*
|
* @param newPackage The new package that was installed
|
* @param oldPackage The old package that was updated
|
* @param allPackageNames All package names
|
* @param permissionCallback Callback for permission changed
|
*/
|
private void revokeRuntimePermissionsIfGroupChanged(
|
@NonNull PackageParser.Package newPackage,
|
@NonNull PackageParser.Package oldPackage,
|
@NonNull ArrayList<String> allPackageNames,
|
@NonNull PermissionCallback permissionCallback) {
|
final int numOldPackagePermissions = oldPackage.permissions.size();
|
final ArrayMap<String, String> oldPermissionNameToGroupName
|
= new ArrayMap<>(numOldPackagePermissions);
|
|
for (int i = 0; i < numOldPackagePermissions; i++) {
|
final PackageParser.Permission permission = oldPackage.permissions.get(i);
|
|
if (permission.group != null) {
|
oldPermissionNameToGroupName.put(permission.info.name,
|
permission.group.info.name);
|
}
|
}
|
|
final int numNewPackagePermissions = newPackage.permissions.size();
|
for (int newPermissionNum = 0; newPermissionNum < numNewPackagePermissions;
|
newPermissionNum++) {
|
final PackageParser.Permission newPermission =
|
newPackage.permissions.get(newPermissionNum);
|
final int newProtection = newPermission.info.getProtection();
|
|
if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) {
|
final String permissionName = newPermission.info.name;
|
final String newPermissionGroupName =
|
newPermission.group == null ? null : newPermission.group.info.name;
|
final String oldPermissionGroupName = oldPermissionNameToGroupName.get(
|
permissionName);
|
|
if (newPermissionGroupName != null
|
&& !newPermissionGroupName.equals(oldPermissionGroupName)) {
|
final int[] userIds = mUserManagerInt.getUserIds();
|
final int numUserIds = userIds.length;
|
for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
|
final int userId = userIds[userIdNum];
|
|
final int numPackages = allPackageNames.size();
|
for (int packageNum = 0; packageNum < numPackages; packageNum++) {
|
final String packageName = allPackageNames.get(packageNum);
|
|
if (checkPermission(permissionName, packageName, UserHandle.USER_SYSTEM,
|
userId) == PackageManager.PERMISSION_GRANTED) {
|
EventLog.writeEvent(0x534e4554, "72710897",
|
newPackage.applicationInfo.uid,
|
"Revoking permission " + permissionName +
|
" from package " + packageName +
|
" as the group changed from " + oldPermissionGroupName +
|
" to " + newPermissionGroupName);
|
|
try {
|
revokeRuntimePermission(permissionName, packageName, false,
|
userId, permissionCallback);
|
} catch (IllegalArgumentException e) {
|
Slog.e(TAG, "Could not revoke " + permissionName + " from "
|
+ packageName, e);
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
}
|
|
/**
|
* If permissions are upgraded to runtime, or their owner changes to the system, then any
|
* granted permissions must be revoked.
|
*
|
* @param permissionsToRevoke A list of permission names to revoke
|
* @param allPackageNames All package names
|
* @param permissionCallback Callback for permission changed
|
*/
|
private void revokeRuntimePermissionsIfPermissionDefinitionChanged(
|
@NonNull List<String> permissionsToRevoke,
|
@NonNull ArrayList<String> allPackageNames,
|
@NonNull PermissionCallback permissionCallback) {
|
|
final int[] userIds = mUserManagerInt.getUserIds();
|
final int numPermissions = permissionsToRevoke.size();
|
final int numUserIds = userIds.length;
|
final int numPackages = allPackageNames.size();
|
final int callingUid = Binder.getCallingUid();
|
|
for (int permNum = 0; permNum < numPermissions; permNum++) {
|
String permName = permissionsToRevoke.get(permNum);
|
BasePermission bp = mSettings.getPermission(permName);
|
if (bp == null || !bp.isRuntime()) {
|
continue;
|
}
|
for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
|
final int userId = userIds[userIdNum];
|
for (int packageNum = 0; packageNum < numPackages; packageNum++) {
|
final String packageName = allPackageNames.get(packageNum);
|
final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId);
|
if (uid < Process.FIRST_APPLICATION_UID) {
|
// do not revoke from system apps
|
continue;
|
}
|
final int permissionState = checkPermission(permName, packageName,
|
Binder.getCallingUid(), userId);
|
final int flags = getPermissionFlags(permName, packageName,
|
Binder.getCallingUid(), userId);
|
final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED
|
| FLAG_PERMISSION_POLICY_FIXED
|
| FLAG_PERMISSION_GRANTED_BY_DEFAULT;
|
if (permissionState == PackageManager.PERMISSION_GRANTED
|
&& (flags & flagMask) == 0) {
|
EventLog.writeEvent(0x534e4554, "154505240", uid,
|
"Revoking permission " + permName + " from package "
|
+ packageName + " due to definition change");
|
EventLog.writeEvent(0x534e4554, "168319670", uid,
|
"Revoking permission " + permName + " from package "
|
+ packageName + " due to definition change");
|
Slog.e(TAG, "Revoking permission " + permName + " from package "
|
+ packageName + " due to definition change");
|
try {
|
revokeRuntimePermission(permName, packageName,
|
false, userId, permissionCallback);
|
} catch (Exception e) {
|
Slog.e(TAG, "Could not revoke " + permName + " from "
|
+ packageName, e);
|
}
|
}
|
}
|
}
|
bp.setPermissionDefinitionChanged(false);
|
}
|
}
|
|
private List<String> addAllPermissions(PackageParser.Package pkg, boolean chatty) {
|
final int N = ArrayUtils.size(pkg.permissions);
|
ArrayList<String> definitionChangedPermissions = new ArrayList<>();
|
for (int i=0; i<N; i++) {
|
PackageParser.Permission p = pkg.permissions.get(i);
|
|
// Assume by default that we did not install this permission into the system.
|
p.info.flags &= ~PermissionInfo.FLAG_INSTALLED;
|
|
synchronized (PermissionManagerService.this.mLock) {
|
// Now that permission groups have a special meaning, we ignore permission
|
// groups for legacy apps to prevent unexpected behavior. In particular,
|
// permissions for one app being granted to someone just because they happen
|
// to be in a group defined by another app (before this had no implications).
|
if (pkg.applicationInfo.targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1) {
|
p.group = mSettings.mPermissionGroups.get(p.info.group);
|
// Warn for a permission in an unknown group.
|
if (DEBUG_PERMISSIONS
|
&& p.info.group != null && p.group == null) {
|
Slog.i(TAG, "Permission " + p.info.name + " from package "
|
+ p.info.packageName + " in an unknown group " + p.info.group);
|
}
|
}
|
|
final BasePermission bp;
|
if (p.tree) {
|
bp = BasePermission.createOrUpdate(
|
mSettings.getPermissionTreeLocked(p.info.name), p, pkg,
|
mSettings.getAllPermissionTreesLocked(), chatty);
|
mSettings.putPermissionTreeLocked(p.info.name, bp);
|
} else {
|
bp = BasePermission.createOrUpdate(
|
mSettings.getPermissionLocked(p.info.name),
|
p, pkg, mSettings.getAllPermissionTreesLocked(), chatty);
|
mSettings.putPermissionLocked(p.info.name, bp);
|
}
|
if (bp.isPermissionDefinitionChanged()) {
|
definitionChangedPermissions.add(p.info.name);
|
}
|
}
|
}
|
return definitionChangedPermissions;
|
}
|
|
private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) {
|
final int N = pkg.permissionGroups.size();
|
StringBuilder r = null;
|
for (int i=0; i<N; i++) {
|
final PackageParser.PermissionGroup pg = pkg.permissionGroups.get(i);
|
final PackageParser.PermissionGroup cur = mSettings.mPermissionGroups.get(pg.info.name);
|
final String curPackageName = (cur == null) ? null : cur.info.packageName;
|
final boolean isPackageUpdate = pg.info.packageName.equals(curPackageName);
|
if (cur == null || isPackageUpdate) {
|
mSettings.mPermissionGroups.put(pg.info.name, pg);
|
if (chatty && DEBUG_PACKAGE_SCANNING) {
|
if (r == null) {
|
r = new StringBuilder(256);
|
} else {
|
r.append(' ');
|
}
|
if (isPackageUpdate) {
|
r.append("UPD:");
|
}
|
r.append(pg.info.name);
|
}
|
} else {
|
Slog.w(TAG, "Permission group " + pg.info.name + " from package "
|
+ pg.info.packageName + " ignored: original from "
|
+ cur.info.packageName);
|
if (chatty && DEBUG_PACKAGE_SCANNING) {
|
if (r == null) {
|
r = new StringBuilder(256);
|
} else {
|
r.append(' ');
|
}
|
r.append("DUP:");
|
r.append(pg.info.name);
|
}
|
}
|
}
|
if (r != null && DEBUG_PACKAGE_SCANNING) {
|
Log.d(TAG, " Permission Groups: " + r);
|
}
|
|
}
|
|
private void removeAllPermissions(PackageParser.Package pkg, boolean chatty) {
|
synchronized (mLock) {
|
int N = pkg.permissions.size();
|
StringBuilder r = null;
|
for (int i=0; i<N; i++) {
|
PackageParser.Permission p = pkg.permissions.get(i);
|
BasePermission bp = (BasePermission) mSettings.mPermissions.get(p.info.name);
|
if (bp == null) {
|
bp = mSettings.mPermissionTrees.get(p.info.name);
|
}
|
if (bp != null && bp.isPermission(p)) {
|
bp.setPermission(null);
|
if (DEBUG_REMOVE && chatty) {
|
if (r == null) {
|
r = new StringBuilder(256);
|
} else {
|
r.append(' ');
|
}
|
r.append(p.info.name);
|
}
|
}
|
if (p.isAppOp()) {
|
ArraySet<String> appOpPkgs =
|
mSettings.mAppOpPermissionPackages.get(p.info.name);
|
if (appOpPkgs != null) {
|
appOpPkgs.remove(pkg.packageName);
|
}
|
}
|
}
|
if (r != null) {
|
if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
|
}
|
|
N = pkg.requestedPermissions.size();
|
r = null;
|
for (int i=0; i<N; i++) {
|
String perm = pkg.requestedPermissions.get(i);
|
if (mSettings.isPermissionAppOp(perm)) {
|
ArraySet<String> appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm);
|
if (appOpPkgs != null) {
|
appOpPkgs.remove(pkg.packageName);
|
if (appOpPkgs.isEmpty()) {
|
mSettings.mAppOpPermissionPackages.remove(perm);
|
}
|
}
|
}
|
}
|
if (r != null) {
|
if (DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r);
|
}
|
}
|
}
|
|
private boolean addDynamicPermission(
|
PermissionInfo info, int callingUid, PermissionCallback callback) {
|
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
|
throw new SecurityException("Instant apps can't add permissions");
|
}
|
if (info.labelRes == 0 && info.nonLocalizedLabel == null) {
|
throw new SecurityException("Label must be specified in permission");
|
}
|
final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid);
|
final boolean added;
|
final boolean changed;
|
synchronized (mLock) {
|
BasePermission bp = mSettings.getPermissionLocked(info.name);
|
added = bp == null;
|
int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel);
|
if (added) {
|
enforcePermissionCapLocked(info, tree);
|
bp = new BasePermission(info.name, tree.getSourcePackageName(),
|
BasePermission.TYPE_DYNAMIC);
|
} else if (!bp.isDynamic()) {
|
throw new SecurityException("Not allowed to modify non-dynamic permission "
|
+ info.name);
|
}
|
changed = bp.addToTree(fixedLevel, info, tree);
|
if (added) {
|
mSettings.putPermissionLocked(info.name, bp);
|
}
|
}
|
if (changed && callback != null) {
|
callback.onPermissionChanged();
|
}
|
return added;
|
}
|
|
private void removeDynamicPermission(
|
String permName, int callingUid, PermissionCallback callback) {
|
if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) {
|
throw new SecurityException("Instant applications don't have access to this method");
|
}
|
final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid);
|
synchronized (mLock) {
|
final BasePermission bp = mSettings.getPermissionLocked(permName);
|
if (bp == null) {
|
return;
|
}
|
if (bp.isDynamic()) {
|
// TODO: switch this back to SecurityException
|
Slog.wtf(TAG, "Not allowed to modify non-dynamic permission "
|
+ permName);
|
}
|
mSettings.removePermissionLocked(permName);
|
if (callback != null) {
|
callback.onPermissionRemoved();
|
}
|
}
|
}
|
|
/**
|
* Restore the permission state for a package.
|
*
|
* <ul>
|
* <li>During boot the state gets restored from the disk</li>
|
* <li>During app update the state gets restored from the last version of the app</li>
|
* </ul>
|
*
|
* <p>This restores the permission state for all users.
|
*
|
* @param pkg the package the permissions belong to
|
* @param replace if the package is getting replaced (this might change the requested
|
* permissions of this package)
|
* @param packageOfInterest If this is the name of {@code pkg} add extra logging
|
* @param callback Result call back
|
*/
|
private void restorePermissionState(@NonNull PackageParser.Package pkg, boolean replace,
|
@Nullable String packageOfInterest, @Nullable PermissionCallback callback) {
|
// IMPORTANT: There are two types of permissions: install and runtime.
|
// Install time permissions are granted when the app is installed to
|
// all device users and users added in the future. Runtime permissions
|
// are granted at runtime explicitly to specific users. Normal and signature
|
// protected permissions are install time permissions. Dangerous permissions
|
// are install permissions if the app's target SDK is Lollipop MR1 or older,
|
// otherwise they are runtime permissions. This function does not manage
|
// runtime permissions except for the case an app targeting Lollipop MR1
|
// being upgraded to target a newer SDK, in which case dangerous permissions
|
// are transformed from install time to runtime ones.
|
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
if (ps == null) {
|
return;
|
}
|
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
PermissionsState origPermissions = permissionsState;
|
|
final int[] currentUserIds = UserManagerService.getInstance().getUserIds();
|
|
boolean runtimePermissionsRevoked = false;
|
int[] updatedUserIds = EMPTY_INT_ARRAY;
|
|
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(), UserManagerService.getInstance().getUserIds());
|
if (!ArrayUtils.isEmpty(updatedUserIds)) {
|
runtimePermissionsRevoked = true;
|
}
|
}
|
}
|
}
|
|
permissionsState.setGlobalGids(mGlobalGids);
|
|
synchronized (mLock) {
|
ArraySet<String> newImplicitPermissions = new ArraySet<>();
|
|
final int N = pkg.requestedPermissions.size();
|
for (int i = 0; i < N; i++) {
|
final String permName = pkg.requestedPermissions.get(i);
|
final BasePermission bp = mSettings.getPermissionLocked(permName);
|
final boolean appSupportsRuntimePermissions =
|
pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M;
|
String upgradedActivityRecognitionPermission = null;
|
|
if (DEBUG_INSTALL) {
|
Log.i(TAG, "Package " + pkg.packageName + " checking " + permName + ": " + bp);
|
}
|
|
if (bp == null || bp.getSourcePackageSetting() == null) {
|
if (packageOfInterest == null || packageOfInterest.equals(pkg.packageName)) {
|
if (DEBUG_PERMISSIONS) {
|
Slog.i(TAG, "Unknown permission " + permName
|
+ " in package " + pkg.packageName);
|
}
|
}
|
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.implicitPermissions.contains(permName)
|
|| (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) {
|
if (pkg.implicitPermissions.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.packageName);
|
}
|
} 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.
|
int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
|
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
|
PermissionManager.SplitPermissionInfo sp =
|
PermissionManager.SPLIT_PERMISSIONS.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.packageName);
|
}
|
break;
|
}
|
}
|
}
|
}
|
|
// Limit ephemeral apps to ephemeral allowed permissions.
|
if (pkg.applicationInfo.isInstantApp() && !bp.isInstant()) {
|
if (DEBUG_PERMISSIONS) {
|
Log.i(TAG, "Denying non-ephemeral permission " + bp.getName()
|
+ " for package " + pkg.packageName);
|
}
|
continue;
|
}
|
|
if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) {
|
if (DEBUG_PERMISSIONS) {
|
Log.i(TAG, "Denying runtime-only permission " + bp.getName()
|
+ " for package " + pkg.packageName);
|
}
|
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.packageName);
|
}
|
|
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;
|
grant = GRANT_INSTALL;
|
} else {
|
// For modern apps keep runtime permissions unchanged.
|
//grant = GRANT_RUNTIME;
|
grant = GRANT_INSTALL;
|
}
|
} else if (bp.isSignature()) {
|
// For all apps signature permissions are install time ones.
|
allowedSig = grantSignaturePermission(perm, pkg, bp, origPermissions);
|
if (allowedSig) {
|
grant = GRANT_INSTALL;
|
}
|
}
|
|
if (DEBUG_PERMISSIONS) {
|
Slog.i(TAG, "Considering granting permission " + perm + " to package "
|
+ pkg.packageName);
|
}
|
|
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;
|
grant = GRANT_INSTALL;
|
}
|
}
|
}
|
|
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 : UserManagerService.getInstance().getUserIds()) {
|
if (origPermissions.getRuntimePermissionState(
|
perm, userId) != null) {
|
// Revoke the runtime permission and clear the flags.
|
origPermissions.revokeRuntimePermission(bp, userId);
|
origPermissions.updatePermissionFlags(bp, userId,
|
PackageManager.MASK_PERMISSION_FLAGS_ALL, 0);
|
// If we revoked a permission permission, we have to write.
|
updatedUserIds = ArrayUtils.appendInt(
|
updatedUserIds, userId);
|
}
|
}
|
// Grant an install permission.
|
if (permissionsState.grantInstallPermission(bp) !=
|
PERMISSION_OPERATION_FAILURE) {
|
changedInstallPermission = true;
|
}
|
} break;
|
|
case GRANT_RUNTIME: {
|
boolean hardRestricted = bp.isHardRestricted();
|
boolean softRestricted = bp.isSoftRestricted();
|
|
for (int userId : currentUserIds) {
|
// If permission policy is not ready we don't deal with restricted
|
// permissions as the policy may whitelist some permissions. Once
|
// the policy is initialized we would re-evaluate permissions.
|
final boolean permissionPolicyInitialized =
|
mPermissionPolicyInternal != null
|
&& mPermissionPolicyInternal.isInitialized(userId);
|
|
PermissionState permState = origPermissions
|
.getRuntimePermissionState(perm, userId);
|
int flags = permState != null ? permState.getFlags() : 0;
|
|
boolean wasChanged = false;
|
|
boolean restrictionExempt =
|
(origPermissions.getPermissionFlags(bp.name, userId)
|
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
|
boolean restrictionApplied = (origPermissions.getPermissionFlags(
|
bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
|
|
if (appSupportsRuntimePermissions) {
|
// If hard restricted we don't allow holding it
|
if (permissionPolicyInitialized && hardRestricted) {
|
if (!restrictionExempt) {
|
if (permState != null && permState.isGranted()
|
&& permissionsState.revokeRuntimePermission(
|
bp, userId) != PERMISSION_OPERATION_FAILURE) {
|
wasChanged = true;
|
}
|
if (!restrictionApplied) {
|
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
|
wasChanged = true;
|
}
|
}
|
// If soft restricted we allow holding in a restricted form
|
} else if (permissionPolicyInitialized && softRestricted) {
|
// Regardless if granted set the restriction flag as it
|
// may affect app treatment based on this permission.
|
if (!restrictionExempt && !restrictionApplied) {
|
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
|
wasChanged = true;
|
}
|
}
|
|
// Remove review flag as it is not necessary anymore
|
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
|
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
|
wasChanged = true;
|
}
|
|
if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
|
flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
|
wasChanged = true;
|
// Hard restricted permissions cannot be held.
|
} else if (!permissionPolicyInitialized
|
|| (!hardRestricted || restrictionExempt)) {
|
if (permState != null && permState.isGranted()) {
|
if (permissionsState.grantRuntimePermission(bp, userId)
|
== PERMISSION_OPERATION_FAILURE) {
|
wasChanged = true;
|
}
|
}
|
}
|
} else {
|
if (permState == null) {
|
// New permission
|
if (PLATFORM_PACKAGE_NAME.equals(
|
bp.getSourcePackageName())) {
|
if (!bp.isRemoved()) {
|
flags |= FLAG_PERMISSION_REVIEW_REQUIRED
|
| FLAG_PERMISSION_REVOKE_ON_UPGRADE;
|
wasChanged = true;
|
}
|
}
|
}
|
|
if (!permissionsState.hasRuntimePermission(bp.name, userId)
|
&& permissionsState.grantRuntimePermission(bp, userId)
|
!= PERMISSION_OPERATION_FAILURE) {
|
wasChanged = true;
|
}
|
|
// If legacy app always grant the permission but if restricted
|
// and not exempt take a note a restriction should be applied.
|
if (permissionPolicyInitialized
|
&& (hardRestricted || softRestricted)
|
&& !restrictionExempt && !restrictionApplied) {
|
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
|
wasChanged = true;
|
}
|
}
|
|
// If unrestricted or restriction exempt, don't apply restriction.
|
if (permissionPolicyInitialized) {
|
if (!(hardRestricted || softRestricted) || restrictionExempt) {
|
if (restrictionApplied) {
|
flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
|
// Dropping restriction on a legacy app implies a review
|
if (!appSupportsRuntimePermissions) {
|
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
|
}
|
wasChanged = true;
|
}
|
}
|
}
|
|
if (wasChanged) {
|
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
|
}
|
|
permissionsState.updatePermissionFlags(bp, userId,
|
MASK_PERMISSION_FLAGS_ALL, flags);
|
}
|
} break;
|
|
case GRANT_UPGRADE: {
|
// Upgrade from Pre-Q to Q permission model. Make all permissions
|
// runtime
|
PermissionState permState = origPermissions
|
.getInstallPermissionState(perm);
|
int flags = (permState != null) ? permState.getFlags() : 0;
|
|
BasePermission bpToRevoke =
|
upgradedActivityRecognitionPermission == null
|
? bp : mSettings.getPermissionLocked(
|
upgradedActivityRecognitionPermission);
|
// Remove install permission
|
if (origPermissions.revokeInstallPermission(bpToRevoke)
|
!= PERMISSION_OPERATION_FAILURE) {
|
origPermissions.updatePermissionFlags(bpToRevoke,
|
UserHandle.USER_ALL,
|
(MASK_PERMISSION_FLAGS_ALL
|
& ~FLAG_PERMISSION_APPLY_RESTRICTION), 0);
|
changedInstallPermission = true;
|
}
|
|
boolean hardRestricted = bp.isHardRestricted();
|
boolean softRestricted = bp.isSoftRestricted();
|
|
for (int userId : currentUserIds) {
|
// If permission policy is not ready we don't deal with restricted
|
// permissions as the policy may whitelist some permissions. Once
|
// the policy is initialized we would re-evaluate permissions.
|
final boolean permissionPolicyInitialized =
|
mPermissionPolicyInternal != null
|
&& mPermissionPolicyInternal.isInitialized(userId);
|
|
boolean wasChanged = false;
|
|
boolean restrictionExempt =
|
(origPermissions.getPermissionFlags(bp.name, userId)
|
& FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0;
|
boolean restrictionApplied = (origPermissions.getPermissionFlags(
|
bp.name, userId) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0;
|
|
if (appSupportsRuntimePermissions) {
|
// If hard restricted we don't allow holding it
|
if (permissionPolicyInitialized && hardRestricted) {
|
if (!restrictionExempt) {
|
if (permState != null && permState.isGranted()
|
&& permissionsState.revokeRuntimePermission(
|
bp, userId) != PERMISSION_OPERATION_FAILURE) {
|
wasChanged = true;
|
}
|
if (!restrictionApplied) {
|
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
|
wasChanged = true;
|
}
|
}
|
// If soft restricted we allow holding in a restricted form
|
} else if (permissionPolicyInitialized && softRestricted) {
|
// Regardless if granted set the restriction flag as it
|
// may affect app treatment based on this permission.
|
if (!restrictionExempt && !restrictionApplied) {
|
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
|
wasChanged = true;
|
}
|
}
|
|
// Remove review flag as it is not necessary anymore
|
if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
|
flags &= ~FLAG_PERMISSION_REVIEW_REQUIRED;
|
wasChanged = true;
|
}
|
|
if ((flags & FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0) {
|
flags &= ~FLAG_PERMISSION_REVOKE_ON_UPGRADE;
|
wasChanged = true;
|
// Hard restricted permissions cannot be held.
|
} else if (!permissionPolicyInitialized ||
|
(!hardRestricted || restrictionExempt)) {
|
if (permissionsState.grantRuntimePermission(bp, userId) !=
|
PERMISSION_OPERATION_FAILURE) {
|
wasChanged = true;
|
}
|
}
|
} else {
|
if (!permissionsState.hasRuntimePermission(bp.name, userId)
|
&& permissionsState.grantRuntimePermission(bp,
|
userId) != PERMISSION_OPERATION_FAILURE) {
|
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
|
wasChanged = true;
|
}
|
|
// If legacy app always grant the permission but if restricted
|
// and not exempt take a note a restriction should be applied.
|
if (permissionPolicyInitialized
|
&& (hardRestricted || softRestricted)
|
&& !restrictionExempt && !restrictionApplied) {
|
flags |= FLAG_PERMISSION_APPLY_RESTRICTION;
|
wasChanged = true;
|
}
|
}
|
|
// If unrestricted or restriction exempt, don't apply restriction.
|
if (permissionPolicyInitialized) {
|
if (!(hardRestricted || softRestricted) || restrictionExempt) {
|
if (restrictionApplied) {
|
flags &= ~FLAG_PERMISSION_APPLY_RESTRICTION;
|
// Dropping restriction on a legacy app implies a review
|
if (!appSupportsRuntimePermissions) {
|
flags |= FLAG_PERMISSION_REVIEW_REQUIRED;
|
}
|
wasChanged = true;
|
}
|
}
|
}
|
|
if (wasChanged) {
|
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
|
}
|
|
permissionsState.updatePermissionFlags(bp, userId,
|
MASK_PERMISSION_FLAGS_ALL, flags);
|
}
|
} break;
|
|
default: {
|
if (packageOfInterest == null
|
|| packageOfInterest.equals(pkg.packageName)) {
|
if (DEBUG_PERMISSIONS) {
|
Slog.i(TAG, "Not granting permission " + perm
|
+ " to package " + pkg.packageName
|
+ " 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;
|
Slog.i(TAG, "Un-granting permission " + perm
|
+ " from package " + pkg.packageName
|
+ " (protectionLevel=" + bp.getProtectionLevel()
|
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
|
+ ")");
|
} 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.packageName))) {
|
Slog.i(TAG, "Not granting permission " + perm
|
+ " to package " + pkg.packageName
|
+ " (protectionLevel=" + bp.getProtectionLevel()
|
+ " flags=0x" + Integer.toHexString(pkg.applicationInfo.flags)
|
+ ")");
|
}
|
}
|
}
|
}
|
|
if ((changedInstallPermission || replace) && !ps.areInstallPermissionsFixed() &&
|
!ps.isSystem() || ps.isUpdatedSystem()) {
|
// This is the first that we have heard about this package, so the
|
// permissions we have now selected are fixed until explicitly
|
// changed.
|
ps.setInstallPermissionsFixed(true);
|
}
|
|
updatedUserIds = revokePermissionsNoLongerImplicitLocked(permissionsState, pkg,
|
updatedUserIds);
|
updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origPermissions,
|
permissionsState, pkg, newImplicitPermissions, updatedUserIds);
|
updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, updatedUserIds);
|
}
|
|
// Persist the runtime permissions state for users with changes. If permissions
|
// were revoked because no app in the shared user declares them we have to
|
// write synchronously to avoid losing runtime permissions state.
|
if (callback != null) {
|
callback.onPermissionUpdated(updatedUserIds, runtimePermissionsRevoked);
|
}
|
|
for (int userId : updatedUserIds) {
|
notifyRuntimePermissionStateChanged(pkg.packageName, userId);
|
}
|
}
|
|
/**
|
* Revoke permissions that are not implicit anymore and that have
|
* {@link PackageManager#FLAG_PERMISSION_REVOKE_WHEN_REQUESTED} set.
|
*
|
* @param ps The state of the permissions of the package
|
* @param pkg The package that is currently looked at
|
* @param updatedUserIds a list of user ids that needs to be amended if the permission state
|
* for a user is changed.
|
*
|
* @return The updated value of the {@code updatedUserIds} parameter
|
*/
|
private @NonNull int[] revokePermissionsNoLongerImplicitLocked(
|
@NonNull PermissionsState ps, @NonNull PackageParser.Package pkg,
|
@NonNull int[] updatedUserIds) {
|
String pkgName = pkg.packageName;
|
boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
|
>= Build.VERSION_CODES.M;
|
|
int[] users = UserManagerService.getInstance().getUserIds();
|
int numUsers = users.length;
|
for (int i = 0; i < numUsers; i++) {
|
int userId = users[i];
|
|
for (String permission : ps.getPermissions(userId)) {
|
if (!pkg.implicitPermissions.contains(permission)) {
|
if (!ps.hasInstallPermission(permission)) {
|
int flags = ps.getRuntimePermissionState(permission, userId).getFlags();
|
|
if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) {
|
BasePermission bp = mSettings.getPermissionLocked(permission);
|
|
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");
|
}
|
}
|
|
flagsToRemove |= USER_PERMISSION_FLAGS;
|
}
|
|
ps.updatePermissionFlags(bp, userId, flagsToRemove, 0);
|
updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId);
|
}
|
}
|
}
|
}
|
}
|
|
return updatedUserIds;
|
}
|
|
/**
|
* {@code newPerm} is newly added; Inherit the state from {@code sourcePerms}.
|
*
|
* <p>A single new permission can be split off from several source permissions. In this case
|
* the most leniant state is inherited.
|
*
|
* <p>Warning: This does not handle foreground / background permissions
|
*
|
* @param sourcePerms The permissions to inherit from
|
* @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 PackageParser.Package pkg,
|
@UserIdInt int userId) {
|
String pkgName = pkg.packageName;
|
boolean isGranted = false;
|
int flags = 0;
|
|
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 (!isGranted) {
|
flags = 0;
|
}
|
|
isGranted = true;
|
flags |= ps.getPermissionFlags(sourcePerm, userId);
|
} else {
|
if (!isGranted) {
|
flags |= ps.getPermissionFlags(sourcePerm, userId);
|
}
|
}
|
}
|
|
if (isGranted) {
|
if (DEBUG_PERMISSIONS) {
|
Slog.i(TAG, newPerm + " inherits runtime perm grant from " + sourcePerms
|
+ " for " + pkgName);
|
}
|
|
ps.grantRuntimePermission(mSettings.getPermissionLocked(newPerm), userId);
|
}
|
|
// Add permission flags
|
ps.updatePermissionFlags(mSettings.getPermission(newPerm), userId, flags, flags);
|
}
|
|
/**
|
* When the app has requested legacy storage we might need to update
|
* {@link android.app.AppOpsManager#OP_LEGACY_STORAGE}. Hence force an update in
|
* {@link com.android.server.policy.PermissionPolicyService#synchronizePackagePermissionsAndAppOpsForUser(Context, String, int)}
|
*
|
* @param pkg The package for which the permissions are updated
|
* @param replace If the app is being replaced
|
* @param updatedUserIds The ids of the users that already changed.
|
*
|
* @return The ids of the users that are changed
|
*/
|
private @NonNull int[] checkIfLegacyStorageOpsNeedToBeUpdated(
|
@NonNull PackageParser.Package pkg, boolean replace, @NonNull int[] updatedUserIds) {
|
if (replace && pkg.applicationInfo.hasRequestedLegacyExternalStorage() && (
|
pkg.requestedPermissions.contains(READ_EXTERNAL_STORAGE)
|
|| pkg.requestedPermissions.contains(WRITE_EXTERNAL_STORAGE))) {
|
return UserManagerService.getInstance().getUserIds();
|
}
|
|
return updatedUserIds;
|
}
|
|
/**
|
* Set the state of a implicit permission that is seen for the first time.
|
*
|
* @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 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 PackageParser.Package pkg,
|
@NonNull ArraySet<String> newImplicitPermissions,
|
@NonNull int[] updatedUserIds) {
|
String pkgName = pkg.packageName;
|
ArrayMap<String, ArraySet<String>> newToSplitPerms = new ArrayMap<>();
|
|
int numSplitPerms = PermissionManager.SPLIT_PERMISSIONS.size();
|
for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
|
PermissionManager.SplitPermissionInfo spi =
|
PermissionManager.SPLIT_PERMISSIONS.get(splitPermNum);
|
|
List<String> newPerms = spi.getNewPermissions();
|
int numNewPerms = newPerms.size();
|
for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) {
|
String newPerm = newPerms.get(newPermNum);
|
|
ArraySet<String> splitPerms = newToSplitPerms.get(newPerm);
|
if (splitPerms == null) {
|
splitPerms = new ArraySet<>();
|
newToSplitPerms.put(newPerm, splitPerms);
|
}
|
|
splitPerms.add(spi.getSplitPermission());
|
}
|
}
|
|
int numNewImplicitPerms = newImplicitPermissions.size();
|
for (int newImplicitPermNum = 0; newImplicitPermNum < numNewImplicitPerms;
|
newImplicitPermNum++) {
|
String newPerm = newImplicitPermissions.valueAt(newImplicitPermNum);
|
ArraySet<String> sourcePerms = newToSplitPerms.get(newPerm);
|
|
if (sourcePerms != null) {
|
if (!ps.hasInstallPermission(newPerm)) {
|
BasePermission bp = mSettings.getPermissionLocked(newPerm);
|
|
int[] users = UserManagerService.getInstance().getUserIds();
|
int numUsers = users.length;
|
for (int userNum = 0; userNum < numUsers; userNum++) {
|
int userId = users[userNum];
|
|
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);
|
|
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");
|
}
|
|
break;
|
} else {
|
// Inherit from new install or existing runtime permissions
|
inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms,
|
newPerm, ps, pkg, userId);
|
}
|
}
|
}
|
}
|
}
|
|
return updatedUserIds;
|
}
|
|
private boolean isNewPlatformPermissionForPackage(String perm, PackageParser.Package pkg) {
|
boolean allowed = false;
|
final int NP = PackageParser.NEW_PERMISSIONS.length;
|
for (int ip=0; ip<NP; ip++) {
|
final PackageParser.NewPermissionInfo npi
|
= PackageParser.NEW_PERMISSIONS[ip];
|
if (npi.name.equals(perm)
|
&& pkg.applicationInfo.targetSdkVersion < npi.sdkVersion) {
|
allowed = true;
|
Log.i(TAG, "Auto-granting " + perm + " to old pkg "
|
+ pkg.packageName);
|
break;
|
}
|
}
|
return allowed;
|
}
|
|
/**
|
* Determines whether a package is whitelisted for a particular privapp permission.
|
*
|
* <p>Does NOT check whether the package is a privapp, just whether it's whitelisted.
|
*
|
* <p>This handles parent/child apps.
|
*/
|
private boolean hasPrivappWhitelistEntry(String perm, PackageParser.Package pkg) {
|
ArraySet<String> wlPermissions = null;
|
if (pkg.isVendor()) {
|
wlPermissions =
|
SystemConfig.getInstance().getVendorPrivAppPermissions(pkg.packageName);
|
} else if (pkg.isProduct()) {
|
wlPermissions =
|
SystemConfig.getInstance().getProductPrivAppPermissions(pkg.packageName);
|
} else if (pkg.isProductServices()) {
|
wlPermissions =
|
SystemConfig.getInstance().getProductServicesPrivAppPermissions(
|
pkg.packageName);
|
} else {
|
wlPermissions = SystemConfig.getInstance().getPrivAppPermissions(pkg.packageName);
|
}
|
// Let's check if this package is whitelisted...
|
boolean whitelisted = wlPermissions != null && wlPermissions.contains(perm);
|
// If it's not, we'll also tail-recurse to the parent.
|
return whitelisted ||
|
pkg.parentPackage != null && hasPrivappWhitelistEntry(perm, pkg.parentPackage);
|
}
|
|
private boolean grantSignaturePermission(String perm, PackageParser.Package pkg,
|
BasePermission bp, PermissionsState origPermissions) {
|
boolean oemPermission = bp.isOEM();
|
boolean vendorPrivilegedPermission = bp.isVendorPrivileged();
|
boolean privilegedPermission = bp.isPrivileged() || bp.isVendorPrivileged();
|
boolean privappPermissionsDisable =
|
RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE;
|
boolean platformPermission = PLATFORM_PACKAGE_NAME.equals(bp.getSourcePackageName());
|
boolean platformPackage = PLATFORM_PACKAGE_NAME.equals(pkg.packageName);
|
if (!privappPermissionsDisable && privilegedPermission && pkg.isPrivileged()
|
&& !platformPackage && platformPermission) {
|
if (!hasPrivappWhitelistEntry(perm, pkg)) {
|
// Only report violations for apps on system image
|
if (!mSystemReady && !pkg.isUpdatedSystemApp()) {
|
// it's only a reportable violation if the permission isn't explicitly denied
|
ArraySet<String> deniedPermissions = null;
|
if (pkg.isVendor()) {
|
deniedPermissions = SystemConfig.getInstance()
|
.getVendorPrivAppDenyPermissions(pkg.packageName);
|
} else if (pkg.isProduct()) {
|
deniedPermissions = SystemConfig.getInstance()
|
.getProductPrivAppDenyPermissions(pkg.packageName);
|
} else if (pkg.isProductServices()) {
|
deniedPermissions = SystemConfig.getInstance()
|
.getProductServicesPrivAppDenyPermissions(pkg.packageName);
|
} else {
|
deniedPermissions = SystemConfig.getInstance()
|
.getPrivAppDenyPermissions(pkg.packageName);
|
}
|
final boolean permissionViolation =
|
deniedPermissions == null || !deniedPermissions.contains(perm);
|
if (permissionViolation) {
|
Slog.w(TAG, "Privileged permission " + perm + " for package "
|
+ pkg.packageName + " - not in privapp-permissions whitelist");
|
|
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
|
if (mPrivappPermissionsViolations == null) {
|
mPrivappPermissionsViolations = new ArraySet<>();
|
}
|
mPrivappPermissionsViolations.add(pkg.packageName + ": " + perm);
|
}
|
} else {
|
return false;
|
}
|
}
|
if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) {
|
return false;
|
}
|
}
|
}
|
final String systemPackageName = mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM);
|
final PackageParser.Package systemPackage =
|
mPackageManagerInt.getPackage(systemPackageName);
|
|
// check if the package is allow to use this signature permission. A package is allowed to
|
// use a signature permission if:
|
// - it has the same set of signing certificates as the source package
|
// - or its signing certificate was rotated from the source package's certificate
|
// - or its signing certificate is a previous signing certificate of the defining
|
// package, and the defining package still trusts the old certificate for permissions
|
// - or it shares the above relationships with the system package
|
boolean allowed =
|
pkg.mSigningDetails.hasAncestorOrSelf(
|
bp.getSourcePackageSetting().getSigningDetails())
|
|| bp.getSourcePackageSetting().getSigningDetails().checkCapability(
|
pkg.mSigningDetails,
|
PackageParser.SigningDetails.CertCapabilities.PERMISSION)
|
|| pkg.mSigningDetails.hasAncestorOrSelf(systemPackage.mSigningDetails)
|
|| systemPackage.mSigningDetails.checkCapability(
|
pkg.mSigningDetails,
|
PackageParser.SigningDetails.CertCapabilities.PERMISSION);
|
if (!allowed && (privilegedPermission || oemPermission)) {
|
if (pkg.isSystem()) {
|
// For updated system applications, a privileged/oem permission
|
// is granted only if it had been defined by the original application.
|
if (pkg.isUpdatedSystemApp()) {
|
final PackageParser.Package disabledPkg =
|
mPackageManagerInt.getDisabledSystemPackage(pkg.packageName);
|
final PackageSetting disabledPs =
|
(disabledPkg != null) ? (PackageSetting) disabledPkg.mExtras : null;
|
if (disabledPs != null
|
&& disabledPs.getPermissionsState().hasInstallPermission(perm)) {
|
// If the original was granted this permission, we take
|
// that grant decision as read and propagate it to the
|
// update.
|
if ((privilegedPermission && disabledPs.isPrivileged())
|
|| (oemPermission && disabledPs.isOem()
|
&& canGrantOemPermission(disabledPs, perm))) {
|
allowed = true;
|
}
|
} else {
|
// The system apk may have been updated with an older
|
// version of the one on the data partition, but which
|
// granted a new system permission that it didn't have
|
// before. In this case we do want to allow the app to
|
// now get the new permission if the ancestral apk is
|
// privileged to get it.
|
if (disabledPs != null && disabledPkg != null
|
&& isPackageRequestingPermission(disabledPkg, perm)
|
&& ((privilegedPermission && disabledPs.isPrivileged())
|
|| (oemPermission && disabledPs.isOem()
|
&& canGrantOemPermission(disabledPs, perm)))) {
|
allowed = true;
|
}
|
// Also if a privileged parent package on the system image or any of
|
// its children requested a privileged/oem permission, the updated child
|
// packages can also get the permission.
|
if (pkg.parentPackage != null) {
|
final PackageParser.Package disabledParentPkg = mPackageManagerInt
|
.getDisabledSystemPackage(pkg.parentPackage.packageName);
|
final PackageSetting disabledParentPs = (disabledParentPkg != null)
|
? (PackageSetting) disabledParentPkg.mExtras : null;
|
if (disabledParentPkg != null
|
&& ((privilegedPermission && disabledParentPs.isPrivileged())
|
|| (oemPermission && disabledParentPs.isOem()))) {
|
if (isPackageRequestingPermission(disabledParentPkg, perm)
|
&& canGrantOemPermission(disabledParentPs, perm)) {
|
allowed = true;
|
} else if (disabledParentPkg.childPackages != null) {
|
for (PackageParser.Package disabledChildPkg
|
: disabledParentPkg.childPackages) {
|
final PackageSetting disabledChildPs =
|
(disabledChildPkg != null)
|
? (PackageSetting) disabledChildPkg.mExtras
|
: null;
|
if (isPackageRequestingPermission(disabledChildPkg, perm)
|
&& canGrantOemPermission(
|
disabledChildPs, perm)) {
|
allowed = true;
|
break;
|
}
|
}
|
}
|
}
|
}
|
}
|
} else {
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
allowed = (privilegedPermission && pkg.isPrivileged())
|
|| (oemPermission && pkg.isOem()
|
&& canGrantOemPermission(ps, perm));
|
}
|
// In any case, don't grant a privileged permission to privileged vendor apps, if
|
// the permission's protectionLevel does not have the extra 'vendorPrivileged'
|
// flag.
|
if (allowed && privilegedPermission &&
|
!vendorPrivilegedPermission && pkg.isVendor()) {
|
Slog.w(TAG, "Permission " + perm + " cannot be granted to privileged vendor apk "
|
+ pkg.packageName + " because it isn't a 'vendorPrivileged' permission.");
|
allowed = false;
|
}
|
}
|
}
|
if (!allowed) {
|
if (!allowed
|
&& bp.isPre23()
|
&& pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
|
// If this was a previously normal/dangerous permission that got moved
|
// to a system permission as part of the runtime permission redesign, then
|
// we still want to blindly grant it to old apps.
|
allowed = true;
|
}
|
// TODO (moltmann): The installer now shares the platforms signature. Hence it does not
|
// need a separate flag anymore. Hence we need to check which
|
// permissions are needed by the permission controller
|
if (!allowed && bp.isInstaller()
|
&& (pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM))
|
|| pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER,
|
UserHandle.USER_SYSTEM)))) {
|
// If this permission is to be granted to the system installer and
|
// this app is an installer, then it gets the permission.
|
allowed = true;
|
}
|
if (!allowed && bp.isVerifier()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM))) {
|
// If this permission is to be granted to the system verifier and
|
// this app is a verifier, then it gets the permission.
|
allowed = true;
|
}
|
if (!allowed && bp.isPreInstalled()
|
&& pkg.isSystem()) {
|
// Any pre-installed system app is allowed to get this permission.
|
allowed = true;
|
}
|
if (!allowed && bp.isDevelopment()) {
|
// For development permissions, a development permission
|
// is granted only if it was already granted.
|
allowed = origPermissions.hasInstallPermission(perm);
|
}
|
if (!allowed && bp.isSetup()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM))) {
|
// If this permission is to be granted to the system setup wizard and
|
// this app is a setup wizard, then it gets the permission.
|
allowed = true;
|
}
|
if (!allowed && bp.isSystemTextClassifier()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER,
|
UserHandle.USER_SYSTEM))) {
|
// Special permissions for the system default text classifier.
|
allowed = true;
|
}
|
if (!allowed && bp.isConfigurator()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_CONFIGURATOR,
|
UserHandle.USER_SYSTEM))) {
|
// Special permissions for the device configurator.
|
allowed = true;
|
}
|
if (!allowed && bp.isWellbeing()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_WELLBEING, UserHandle.USER_SYSTEM))) {
|
// Special permission granted only to the OEM specified wellbeing app
|
allowed = true;
|
}
|
if (!allowed && bp.isDocumenter()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_DOCUMENTER, UserHandle.USER_SYSTEM))) {
|
// If this permission is to be granted to the documenter and
|
// this app is the documenter, then it gets the permission.
|
allowed = true;
|
}
|
if (!allowed && bp.isIncidentReportApprover()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER,
|
UserHandle.USER_SYSTEM))) {
|
// If this permission is to be granted to the incident report approver and
|
// this app is the incident report approver, then it gets the permission.
|
allowed = true;
|
}
|
if (!allowed && bp.isAppPredictor()
|
&& pkg.packageName.equals(mPackageManagerInt.getKnownPackageName(
|
PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM))) {
|
// Special permissions for the system app predictor.
|
allowed = true;
|
}
|
}
|
return allowed;
|
}
|
|
private static boolean canGrantOemPermission(PackageSetting ps, String permission) {
|
if (!ps.isOem()) {
|
return false;
|
}
|
// all oem permissions must explicitly be granted or denied
|
final Boolean granted =
|
SystemConfig.getInstance().getOemPermissions(ps.name).get(permission);
|
if (granted == null) {
|
throw new IllegalStateException("OEM permission" + permission + " requested by package "
|
+ ps.name + " must be explicitly declared granted or not");
|
}
|
return Boolean.TRUE == granted;
|
}
|
|
private boolean isPermissionsReviewRequired(@NonNull PackageParser.Package pkg,
|
@UserIdInt int userId) {
|
// Permission review applies only to apps not supporting the new permission model.
|
if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
|
return false;
|
}
|
|
// Legacy apps have the permission and get user consent on launch.
|
if (pkg.mExtras == null) {
|
return false;
|
}
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
return permissionsState.isPermissionReviewRequired(userId);
|
}
|
|
private boolean isPackageRequestingPermission(PackageParser.Package pkg, String permission) {
|
final int permCount = pkg.requestedPermissions.size();
|
for (int j = 0; j < permCount; j++) {
|
String requestedPermission = pkg.requestedPermissions.get(j);
|
if (permission.equals(requestedPermission)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
@GuardedBy("mLock")
|
private void grantRuntimePermissionsGrantedToDisabledPackageLocked(
|
PackageParser.Package pkg, int callingUid, PermissionCallback callback) {
|
if (pkg.parentPackage == null) {
|
return;
|
}
|
if (pkg.requestedPermissions == null) {
|
return;
|
}
|
final PackageParser.Package disabledPkg =
|
mPackageManagerInt.getDisabledSystemPackage(pkg.parentPackage.packageName);
|
if (disabledPkg == null || disabledPkg.mExtras == null) {
|
return;
|
}
|
final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras;
|
if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) {
|
return;
|
}
|
final int permCount = pkg.requestedPermissions.size();
|
for (int i = 0; i < permCount; i++) {
|
String permission = pkg.requestedPermissions.get(i);
|
BasePermission bp = mSettings.getPermissionLocked(permission);
|
if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) {
|
continue;
|
}
|
for (int userId : mUserManagerInt.getUserIds()) {
|
if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) {
|
grantRuntimePermission(
|
permission, pkg.packageName, false, callingUid, userId, callback);
|
}
|
}
|
}
|
}
|
|
private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
|
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
|
for (int userId : userIds) {
|
grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid,
|
callback);
|
}
|
}
|
|
private @Nullable List<String> getWhitelistedRestrictedPermissions(
|
@NonNull PackageParser.Package pkg, @PermissionWhitelistFlags int whitelistFlags,
|
@UserIdInt int userId) {
|
final PackageSetting packageSetting = (PackageSetting) pkg.mExtras;
|
if (packageSetting == null) {
|
return null;
|
}
|
|
final PermissionsState permissionsState = packageSetting.getPermissionsState();
|
|
int queryFlags = 0;
|
if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) {
|
queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
|
}
|
if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) {
|
queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
|
}
|
if ((whitelistFlags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) {
|
queryFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
}
|
|
ArrayList<String> whitelistedPermissions = null;
|
|
final int permissionCount = pkg.requestedPermissions.size();
|
for (int i = 0; i < permissionCount; i++) {
|
final String permissionName = pkg.requestedPermissions.get(i);
|
final int currentFlags = permissionsState.getPermissionFlags(permissionName, userId);
|
if ((currentFlags & queryFlags) != 0) {
|
if (whitelistedPermissions == null) {
|
whitelistedPermissions = new ArrayList<>();
|
}
|
whitelistedPermissions.add(permissionName);
|
}
|
}
|
|
return whitelistedPermissions;
|
}
|
|
private void setWhitelistedRestrictedPermissions(@NonNull PackageParser.Package pkg,
|
@NonNull int[] userIds, @Nullable List<String> permissions, int callingUid,
|
@PackageManager.PermissionWhitelistFlags int whitelistFlags,
|
@NonNull PermissionCallback callback) {
|
for (int userId : userIds) {
|
setWhitelistedRestrictedPermissionsForUser(pkg, userId, permissions,
|
callingUid, whitelistFlags, callback);
|
}
|
}
|
|
private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId,
|
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
|
PackageSetting ps = (PackageSetting) pkg.mExtras;
|
if (ps == null) {
|
return;
|
}
|
|
PermissionsState permissionsState = ps.getPermissionsState();
|
|
final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED
|
| PackageManager.FLAG_PERMISSION_POLICY_FIXED;
|
|
final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion
|
>= Build.VERSION_CODES.M;
|
|
final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId);
|
|
for (String permission : pkg.requestedPermissions) {
|
final BasePermission bp;
|
synchronized (mLock) {
|
bp = mSettings.getPermissionLocked(permission);
|
}
|
if (bp != null && (bp.isRuntime() || bp.isDevelopment())
|
&& (!instantApp || bp.isInstant())
|
&& (supportsRuntimePermissions || !bp.isRuntimeOnly())
|
&& (grantedPermissions == null
|
|| ArrayUtils.contains(grantedPermissions, permission))) {
|
final int flags = permissionsState.getPermissionFlags(permission, userId);
|
if (supportsRuntimePermissions) {
|
// Installer cannot change immutable permissions.
|
if ((flags & immutableFlags) == 0) {
|
grantRuntimePermission(permission, pkg.packageName, false, callingUid,
|
userId, callback);
|
}
|
} else {
|
// In permission review mode we clear the review flag when we
|
// are asked to install the app with all permissions granted.
|
if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) {
|
updatePermissionFlags(permission, pkg.packageName,
|
PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid,
|
userId, false, callback);
|
}
|
}
|
}
|
}
|
}
|
|
private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy,
|
int callingUid, final int userId, PermissionCallback callback) {
|
if (!mUserManagerInt.exists(userId)) {
|
Log.e(TAG, "No such user:" + userId);
|
return;
|
}
|
|
mContext.enforceCallingOrSelfPermission(
|
android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS,
|
"grantRuntimePermission");
|
|
enforceCrossUserPermission(callingUid, userId,
|
true, // requireFullPermission
|
true, // checkShell
|
false, // requirePermissionWhenSameUser
|
"grantRuntimePermission");
|
|
final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
|
if (pkg == null || pkg.mExtras == null) {
|
throw new IllegalArgumentException("Unknown package: " + packageName);
|
}
|
final BasePermission bp;
|
synchronized(mLock) {
|
bp = mSettings.getPermissionLocked(permName);
|
}
|
if (bp == null) {
|
throw new IllegalArgumentException("Unknown permission: " + permName);
|
}
|
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
|
throw new IllegalArgumentException("Unknown package: " + packageName);
|
}
|
|
bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
|
|
// If a permission review is required for legacy apps we represent
|
// their permissions as always granted runtime ones since we need
|
// to keep the review required permission flag per user while an
|
// install permission's state is shared across all users.
|
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
|
&& bp.isRuntime()) {
|
return;
|
}
|
|
final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid);
|
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
|
final int flags = permissionsState.getPermissionFlags(permName, userId);
|
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) {
|
Log.e(TAG, "Cannot grant system fixed permission "
|
+ permName + " for package " + packageName);
|
return;
|
}
|
if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
|
Log.e(TAG, "Cannot grant policy fixed permission "
|
+ permName + " for package " + packageName);
|
return;
|
}
|
|
if (bp.isHardRestricted()
|
&& (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) {
|
Log.e(TAG, "Cannot grant hard restricted non-exempt permission "
|
+ permName + " for package " + packageName);
|
return;
|
}
|
|
if (bp.isSoftRestricted() && !SoftRestrictedPermissionPolicy.forPermission(mContext,
|
pkg.applicationInfo, UserHandle.of(userId), permName).canBeGranted()) {
|
Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package "
|
+ packageName);
|
return;
|
}
|
|
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) {
|
if (callback != null) {
|
callback.onInstallPermissionGranted();
|
}
|
}
|
return;
|
}
|
|
if (ps.getInstantApp(userId) && !bp.isInstant()) {
|
throw new SecurityException("Cannot grant non-ephemeral permission"
|
+ permName + " for package " + packageName);
|
}
|
|
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
|
Slog.w(TAG, "Cannot grant runtime permission to a legacy app");
|
return;
|
}
|
|
final int result = permissionsState.grantRuntimePermission(bp, userId);
|
switch (result) {
|
case PERMISSION_OPERATION_FAILURE: {
|
return;
|
}
|
|
case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: {
|
if (callback != null) {
|
callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId);
|
}
|
}
|
break;
|
}
|
|
if (bp.isRuntime()) {
|
logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName);
|
}
|
|
if (callback != null) {
|
callback.onPermissionGranted(uid, userId);
|
}
|
|
if (bp.isRuntime()) {
|
notifyRuntimePermissionStateChanged(packageName, userId);
|
}
|
|
// Only need to do this if user is initialized. Otherwise it's a new user
|
// and there are no processes running as the user yet and there's no need
|
// to make an expensive call to remount processes for the changed permissions.
|
if (READ_EXTERNAL_STORAGE.equals(permName)
|
|| WRITE_EXTERNAL_STORAGE.equals(permName)) {
|
final long token = Binder.clearCallingIdentity();
|
try {
|
if (mUserManagerInt.isUserInitialized(userId)) {
|
StorageManagerInternal storageManagerInternal = LocalServices.getService(
|
StorageManagerInternal.class);
|
storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName);
|
}
|
} finally {
|
Binder.restoreCallingIdentity(token);
|
}
|
}
|
|
}
|
|
private void revokeRuntimePermission(String permName, String packageName,
|
boolean overridePolicy, int userId, PermissionCallback callback) {
|
if (!mUserManagerInt.exists(userId)) {
|
Log.e(TAG, "No such user:" + userId);
|
return;
|
}
|
|
mContext.enforceCallingOrSelfPermission(
|
android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS,
|
"revokeRuntimePermission");
|
|
enforceCrossUserPermission(Binder.getCallingUid(), userId,
|
true, // requireFullPermission
|
true, // checkShell
|
false, // requirePermissionWhenSameUser
|
"revokeRuntimePermission");
|
|
final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
|
if (pkg == null || pkg.mExtras == null) {
|
throw new IllegalArgumentException("Unknown package: " + packageName);
|
}
|
if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) {
|
throw new IllegalArgumentException("Unknown package: " + packageName);
|
}
|
final BasePermission bp = mSettings.getPermissionLocked(permName);
|
if (bp == null) {
|
throw new IllegalArgumentException("Unknown permission: " + permName);
|
}
|
|
bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg);
|
|
// If a permission review is required for legacy apps we represent
|
// their permissions as always granted runtime ones since we need
|
// to keep the review required permission flag per user while an
|
// install permission's state is shared across all users.
|
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
|
&& bp.isRuntime()) {
|
return;
|
}
|
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
|
final int flags = permissionsState.getPermissionFlags(permName, userId);
|
// Only the system may revoke SYSTEM_FIXED permissions.
|
if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0
|
&& UserHandle.getCallingAppId() != Process.SYSTEM_UID) {
|
throw new SecurityException("Non-System UID cannot revoke system fixed permission "
|
+ permName + " for package " + packageName);
|
}
|
if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) {
|
throw new SecurityException("Cannot revoke policy fixed permission "
|
+ permName + " for package " + packageName);
|
}
|
|
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) {
|
if (callback != null) {
|
callback.onInstallPermissionRevoked();
|
}
|
}
|
return;
|
}
|
|
// Permission is already revoked, no need to do anything.
|
if (!permissionsState.hasRuntimePermission(permName, userId)) {
|
return;
|
}
|
|
if (permissionsState.revokeRuntimePermission(bp, userId) ==
|
PERMISSION_OPERATION_FAILURE) {
|
return;
|
}
|
|
if (bp.isRuntime()) {
|
logPermission(MetricsEvent.ACTION_PERMISSION_REVOKED, permName, packageName);
|
}
|
|
if (callback != null) {
|
callback.onPermissionRevoked(pkg.applicationInfo.uid, userId);
|
}
|
|
if (bp.isRuntime()) {
|
notifyRuntimePermissionStateChanged(packageName, userId);
|
}
|
}
|
|
private void setWhitelistedRestrictedPermissionsForUser(@NonNull PackageParser.Package pkg,
|
@UserIdInt int userId, @Nullable List<String> permissions, int callingUid,
|
@PermissionWhitelistFlags int whitelistFlags, PermissionCallback callback) {
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
if (ps == null) {
|
return;
|
}
|
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
|
ArraySet<String> oldGrantedRestrictedPermissions = null;
|
boolean updatePermissions = false;
|
|
final int permissionCount = pkg.requestedPermissions.size();
|
for (int i = 0; i < permissionCount; i++) {
|
final String permissionName = pkg.requestedPermissions.get(i);
|
|
final BasePermission bp = mSettings.getPermissionLocked(permissionName);
|
if (bp == null) {
|
Slog.w(TAG, "Cannot whitelist unknown permission: " + permissionName);
|
continue;
|
}
|
|
if (!bp.isHardOrSoftRestricted()) {
|
continue;
|
}
|
|
if (permissionsState.hasPermission(permissionName, userId)) {
|
if (oldGrantedRestrictedPermissions == null) {
|
oldGrantedRestrictedPermissions = new ArraySet<>();
|
}
|
oldGrantedRestrictedPermissions.add(permissionName);
|
}
|
|
final int oldFlags = permissionsState.getPermissionFlags(permissionName, userId);
|
|
int newFlags = oldFlags;
|
int mask = 0;
|
int whitelistFlagsCopy = whitelistFlags;
|
while (whitelistFlagsCopy != 0) {
|
final int flag = 1 << Integer.numberOfTrailingZeros(whitelistFlagsCopy);
|
whitelistFlagsCopy &= ~flag;
|
switch (flag) {
|
case FLAG_PERMISSION_WHITELIST_SYSTEM: {
|
mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
|
if (permissions != null && permissions.contains(permissionName)) {
|
newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
|
} else {
|
newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
|
}
|
} break;
|
case FLAG_PERMISSION_WHITELIST_UPGRADE: {
|
mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
|
if (permissions != null && permissions.contains(permissionName)) {
|
newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
|
} else {
|
newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
|
}
|
} break;
|
case FLAG_PERMISSION_WHITELIST_INSTALLER: {
|
mask |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
if (permissions != null && permissions.contains(permissionName)) {
|
newFlags |= PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
} else {
|
newFlags &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
}
|
} break;
|
}
|
}
|
|
if (oldFlags == newFlags) {
|
continue;
|
}
|
|
updatePermissions = true;
|
|
final boolean wasWhitelisted = (oldFlags
|
& (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
|
final boolean isWhitelisted = (newFlags
|
& (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0;
|
|
// If the permission is policy fixed as granted but it is no longer
|
// on any of the whitelists we need to clear the policy fixed flag
|
// 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);
|
if (!isWhitelisted && isGranted) {
|
mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
|
newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
|
}
|
}
|
|
// If we are whitelisting an app that does not support runtime permissions
|
// we need to make sure it goes through the permission review UI at launch.
|
if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M
|
&& !wasWhitelisted && isWhitelisted) {
|
mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
|
newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
|
}
|
|
updatePermissionFlags(permissionName, pkg.packageName, mask, newFlags,
|
callingUid, userId, false, null /*callback*/);
|
}
|
|
if (updatePermissions) {
|
// Update permission of this app to take into account the new whitelist state.
|
restorePermissionState(pkg, false, pkg.packageName, callback);
|
|
// If this resulted in losing a permission we need to kill the app.
|
if (oldGrantedRestrictedPermissions != null) {
|
final int oldGrantedCount = oldGrantedRestrictedPermissions.size();
|
for (int i = 0; i < oldGrantedCount; i++) {
|
final String permission = oldGrantedRestrictedPermissions.valueAt(i);
|
// Sometimes we create a new permission state instance during update.
|
if (!ps.getPermissionsState().hasPermission(permission, userId)) {
|
callback.onPermissionRevoked(pkg.applicationInfo.uid, userId);
|
break;
|
}
|
}
|
}
|
}
|
}
|
|
@GuardedBy("mLock")
|
private int[] revokeUnusedSharedUserPermissionsLocked(
|
SharedUserSetting suSetting, int[] allUserIds) {
|
// Collect all used permissions in the UID
|
final ArraySet<String> usedPermissions = new ArraySet<>();
|
final List<PackageParser.Package> pkgList = suSetting.getPackages();
|
if (pkgList == null || pkgList.size() == 0) {
|
return EmptyArray.INT;
|
}
|
for (PackageParser.Package pkg : pkgList) {
|
if (pkg.requestedPermissions == null) {
|
continue;
|
}
|
final int requestedPermCount = pkg.requestedPermissions.size();
|
for (int j = 0; j < requestedPermCount; j++) {
|
String permission = pkg.requestedPermissions.get(j);
|
BasePermission bp = mSettings.getPermissionLocked(permission);
|
if (bp != null) {
|
usedPermissions.add(permission);
|
}
|
}
|
}
|
|
PermissionsState permissionsState = suSetting.getPermissionsState();
|
// 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);
|
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);
|
}
|
}
|
}
|
}
|
|
return runtimePermissionChangedUserIds;
|
}
|
|
private String[] getAppOpPermissionPackages(String permName) {
|
if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) {
|
return null;
|
}
|
synchronized (mLock) {
|
final ArraySet<String> pkgs = mSettings.mAppOpPermissionPackages.get(permName);
|
if (pkgs == null) {
|
return null;
|
}
|
return pkgs.toArray(new String[pkgs.size()]);
|
}
|
}
|
|
private int getPermissionFlags(
|
String permName, String packageName, int callingUid, int userId) {
|
if (!mUserManagerInt.exists(userId)) {
|
return 0;
|
}
|
|
enforceGrantRevokeGetRuntimePermissionPermissions("getPermissionFlags");
|
|
enforceCrossUserPermission(callingUid, userId,
|
true, // requireFullPermission
|
false, // checkShell
|
false, // requirePermissionWhenSameUser
|
"getPermissionFlags");
|
|
final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
|
if (pkg == null || pkg.mExtras == null) {
|
return 0;
|
}
|
synchronized (mLock) {
|
if (mSettings.getPermissionLocked(permName) == null) {
|
return 0;
|
}
|
}
|
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
|
return 0;
|
}
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
PermissionsState permissionsState = ps.getPermissionsState();
|
return permissionsState.getPermissionFlags(permName, userId);
|
}
|
|
private static final int UPDATE_PERMISSIONS_ALL = 1<<0;
|
private static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1<<1;
|
private static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1<<2;
|
|
private void updatePermissions(String packageName, PackageParser.Package pkg,
|
boolean replaceGrant, Collection<PackageParser.Package> allPackages,
|
PermissionCallback callback) {
|
final int flags = (pkg != null ? UPDATE_PERMISSIONS_ALL : 0) |
|
(replaceGrant ? UPDATE_PERMISSIONS_REPLACE_PKG : 0);
|
updatePermissions(
|
packageName, pkg, getVolumeUuidForPackage(pkg), flags, allPackages, callback);
|
if (pkg != null && pkg.childPackages != null) {
|
for (PackageParser.Package childPkg : pkg.childPackages) {
|
updatePermissions(childPkg.packageName, childPkg,
|
getVolumeUuidForPackage(childPkg), flags, allPackages, callback);
|
}
|
}
|
}
|
|
private void updateAllPermissions(String volumeUuid, boolean sdkUpdated,
|
Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
|
final int flags = UPDATE_PERMISSIONS_ALL |
|
(sdkUpdated
|
? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL
|
: 0);
|
updatePermissions(null, null, volumeUuid, flags, allPackages, callback);
|
}
|
|
private void updatePermissions(String changingPkgName, PackageParser.Package changingPkg,
|
String replaceVolumeUuid, int flags, Collection<PackageParser.Package> allPackages,
|
PermissionCallback callback) {
|
// TODO: Most of the methods exposing BasePermission internals [source package name,
|
// etc..] shouldn't be needed. Instead, when we've parsed a permission that doesn't
|
// have package settings, we should make note of it elsewhere [map between
|
// source package name and BasePermission] and cycle through that here. Then we
|
// define a single method on BasePermission that takes a PackageSetting, changing
|
// package name and a package.
|
// NOTE: With this approach, we also don't need to tree trees differently than
|
// normal permissions. Today, we need two separate loops because these BasePermission
|
// objects are stored separately.
|
// Make sure there are no dangling permission trees.
|
flags = updatePermissionTrees(changingPkgName, changingPkg, flags);
|
|
// Make sure all dynamic permissions have been assigned to a package,
|
// and make sure there are no dangling permissions.
|
flags = updatePermissions(changingPkgName, changingPkg, flags, callback);
|
|
synchronized (mLock) {
|
if (mBackgroundPermissions == null) {
|
// Cache background -> foreground permission mapping.
|
// Only system declares background permissions, hence mapping does never change.
|
mBackgroundPermissions = new ArrayMap<>();
|
for (BasePermission bp : mSettings.getAllPermissionsLocked()) {
|
if (bp.perm != null && bp.perm.info != null
|
&& bp.perm.info.backgroundPermission != null) {
|
String fgPerm = bp.name;
|
String bgPerm = bp.perm.info.backgroundPermission;
|
|
List<String> fgPerms = mBackgroundPermissions.get(bgPerm);
|
if (fgPerms == null) {
|
fgPerms = new ArrayList<>();
|
mBackgroundPermissions.put(bgPerm, fgPerms);
|
}
|
|
fgPerms.add(fgPerm);
|
}
|
}
|
}
|
}
|
|
Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "restorePermissionState");
|
// Now update the permissions for all packages, in particular
|
// replace the granted permissions of the system packages.
|
if ((flags & UPDATE_PERMISSIONS_ALL) != 0) {
|
for (PackageParser.Package pkg : allPackages) {
|
if (pkg != changingPkg) {
|
// Only replace for packages on requested volume
|
final String volumeUuid = getVolumeUuidForPackage(pkg);
|
final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0)
|
&& Objects.equals(replaceVolumeUuid, volumeUuid);
|
restorePermissionState(pkg, replace, changingPkgName, callback);
|
}
|
}
|
}
|
|
if (changingPkg != null) {
|
// Only replace for packages on requested volume
|
final String volumeUuid = getVolumeUuidForPackage(changingPkg);
|
final boolean replace = ((flags & UPDATE_PERMISSIONS_REPLACE_PKG) != 0)
|
&& Objects.equals(replaceVolumeUuid, volumeUuid);
|
restorePermissionState(changingPkg, replace, changingPkgName, callback);
|
}
|
Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER);
|
}
|
|
private int updatePermissions(String packageName, PackageParser.Package pkg, int flags,
|
@Nullable PermissionCallback callback) {
|
Set<BasePermission> needsUpdate = null;
|
synchronized (mLock) {
|
final Iterator<BasePermission> it = mSettings.mPermissions.values().iterator();
|
while (it.hasNext()) {
|
final BasePermission bp = it.next();
|
if (bp.isDynamic()) {
|
bp.updateDynamicPermission(mSettings.mPermissionTrees.values());
|
}
|
if (bp.getSourcePackageSetting() != null) {
|
if (packageName != null && packageName.equals(bp.getSourcePackageName())
|
&& (pkg == null || !hasPermission(pkg, bp.getName()))) {
|
Slog.i(TAG, "Removing old permission tree: " + bp.getName()
|
+ " from package " + bp.getSourcePackageName());
|
if (bp.isRuntime()) {
|
final int[] userIds = mUserManagerInt.getUserIds();
|
final int numUserIds = userIds.length;
|
for (int userIdNum = 0; userIdNum < numUserIds; userIdNum++) {
|
final int userId = userIds[userIdNum];
|
|
mPackageManagerInt.forEachPackage((Package p) -> {
|
final String pName = p.packageName;
|
final ApplicationInfo appInfo =
|
mPackageManagerInt.getApplicationInfo(pName, 0,
|
Process.SYSTEM_UID, UserHandle.USER_SYSTEM);
|
if (appInfo != null
|
&& appInfo.targetSdkVersion < Build.VERSION_CODES.M) {
|
return;
|
}
|
|
final String permissionName = bp.getName();
|
if (checkPermission(permissionName, pName, Process.SYSTEM_UID,
|
userId) == PackageManager.PERMISSION_GRANTED) {
|
try {
|
revokeRuntimePermission(
|
permissionName,
|
pName,
|
false,
|
userId,
|
callback);
|
} catch (IllegalArgumentException e) {
|
Slog.e(TAG,
|
"Failed to revoke "
|
+ permissionName
|
+ " from "
|
+ pName,
|
e);
|
}
|
}
|
});
|
}
|
} else {
|
mPackageManagerInt.forEachPackage(p -> {
|
PackageSetting ps = (PackageSetting) p.mExtras;
|
if (ps == null) {
|
return;
|
}
|
PermissionsState permissionsState = ps.getPermissionsState();
|
if (permissionsState.getInstallPermissionState(bp.getName())
|
!= null) {
|
permissionsState.revokeInstallPermission(bp);
|
permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL,
|
MASK_PERMISSION_FLAGS_ALL, 0);
|
}
|
});
|
}
|
flags |= UPDATE_PERMISSIONS_ALL;
|
it.remove();
|
}
|
continue;
|
}
|
if (needsUpdate == null) {
|
needsUpdate = new ArraySet<>(mSettings.mPermissions.size());
|
}
|
needsUpdate.add(bp);
|
}
|
}
|
if (needsUpdate != null) {
|
for (final BasePermission bp : needsUpdate) {
|
final PackageParser.Package sourcePkg =
|
mPackageManagerInt.getPackage(bp.getSourcePackageName());
|
synchronized (mLock) {
|
if (sourcePkg != null && sourcePkg.mExtras != null) {
|
final PackageSetting sourcePs = (PackageSetting) sourcePkg.mExtras;
|
if (bp.getSourcePackageSetting() == null) {
|
bp.setSourcePackageSetting(sourcePs);
|
}
|
continue;
|
}
|
Slog.w(TAG, "Removing dangling permission: " + bp.getName()
|
+ " from package " + bp.getSourcePackageName());
|
mSettings.removePermissionLocked(bp.getName());
|
}
|
}
|
}
|
return flags;
|
}
|
|
private int updatePermissionTrees(String packageName, PackageParser.Package pkg,
|
int flags) {
|
Set<BasePermission> needsUpdate = null;
|
synchronized (mLock) {
|
final Iterator<BasePermission> it = mSettings.mPermissionTrees.values().iterator();
|
while (it.hasNext()) {
|
final BasePermission bp = it.next();
|
if (bp.getSourcePackageSetting() != null) {
|
if (packageName != null && packageName.equals(bp.getSourcePackageName())
|
&& (pkg == null || !hasPermission(pkg, bp.getName()))) {
|
Slog.i(TAG, "Removing old permission tree: " + bp.getName()
|
+ " from package " + bp.getSourcePackageName());
|
flags |= UPDATE_PERMISSIONS_ALL;
|
it.remove();
|
}
|
continue;
|
}
|
if (needsUpdate == null) {
|
needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size());
|
}
|
needsUpdate.add(bp);
|
}
|
}
|
if (needsUpdate != null) {
|
for (final BasePermission bp : needsUpdate) {
|
final PackageParser.Package sourcePkg =
|
mPackageManagerInt.getPackage(bp.getSourcePackageName());
|
synchronized (mLock) {
|
if (sourcePkg != null && sourcePkg.mExtras != null) {
|
final PackageSetting sourcePs = (PackageSetting) sourcePkg.mExtras;
|
if (bp.getSourcePackageSetting() == null) {
|
bp.setSourcePackageSetting(sourcePs);
|
}
|
continue;
|
}
|
Slog.w(TAG, "Removing dangling permission tree: " + bp.getName()
|
+ " from package " + bp.getSourcePackageName());
|
mSettings.removePermissionLocked(bp.getName());
|
}
|
}
|
}
|
return flags;
|
}
|
|
private void updatePermissionFlags(String permName, String packageName, int flagMask,
|
int flagValues, int callingUid, int userId, boolean overridePolicy,
|
PermissionCallback callback) {
|
if (!mUserManagerInt.exists(userId)) {
|
return;
|
}
|
|
enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags");
|
|
enforceCrossUserPermission(callingUid, userId,
|
true, // requireFullPermission
|
true, // checkShell
|
false, // requirePermissionWhenSameUser
|
"updatePermissionFlags");
|
|
if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0 && !overridePolicy) {
|
throw new SecurityException("updatePermissionFlags requires "
|
+ Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY);
|
}
|
|
// Only the system can change these flags and nothing else.
|
if (callingUid != Process.SYSTEM_UID) {
|
flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
|
flagMask &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION;
|
}
|
|
final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName);
|
if (pkg == null || pkg.mExtras == null) {
|
Log.e(TAG, "Unknown package: " + packageName);
|
return;
|
}
|
if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) {
|
throw new IllegalArgumentException("Unknown package: " + packageName);
|
}
|
|
final BasePermission bp;
|
synchronized (mLock) {
|
bp = mSettings.getPermissionLocked(permName);
|
}
|
if (bp == null) {
|
throw new IllegalArgumentException("Unknown permission: " + permName);
|
}
|
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
final PermissionsState permissionsState = ps.getPermissionsState();
|
final boolean hadState =
|
permissionsState.getRuntimePermissionState(permName, userId) != null;
|
final boolean permissionUpdated =
|
permissionsState.updatePermissionFlags(bp, userId, 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) {
|
callback.onInstallPermissionUpdated();
|
} else if (permissionsState.getRuntimePermissionState(permName, userId) != null
|
|| hadState) {
|
callback.onPermissionUpdated(new int[] { userId }, false);
|
}
|
}
|
}
|
|
private boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
|
int userId, Collection<Package> packages, PermissionCallback callback) {
|
if (!mUserManagerInt.exists(userId)) {
|
return false;
|
}
|
|
enforceGrantRevokeRuntimePermissionPermissions(
|
"updatePermissionFlagsForAllApps");
|
enforceCrossUserPermission(callingUid, userId,
|
true, // requireFullPermission
|
true, // checkShell
|
false, // requirePermissionWhenSameUser
|
"updatePermissionFlagsForAllApps");
|
|
// Only the system can change system fixed flags.
|
if (callingUid != Process.SYSTEM_UID) {
|
flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
|
flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED;
|
}
|
|
boolean changed = false;
|
for (PackageParser.Package pkg : packages) {
|
final PackageSetting ps = (PackageSetting) pkg.mExtras;
|
if (ps == null) {
|
continue;
|
}
|
PermissionsState permissionsState = ps.getPermissionsState();
|
changed |= permissionsState.updatePermissionFlagsForAllPermissions(
|
userId, flagMask, flagValues);
|
}
|
return changed;
|
}
|
|
private void enforceGrantRevokeRuntimePermissionPermissions(String message) {
|
if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
|
!= PackageManager.PERMISSION_GRANTED
|
&& mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
|
!= PackageManager.PERMISSION_GRANTED) {
|
throw new SecurityException(message + " requires "
|
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
|
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS);
|
}
|
}
|
|
private void enforceGrantRevokeGetRuntimePermissionPermissions(@NonNull String message) {
|
if (mContext.checkCallingOrSelfPermission(Manifest.permission.GET_RUNTIME_PERMISSIONS)
|
!= PackageManager.PERMISSION_GRANTED
|
&& mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS)
|
!= PackageManager.PERMISSION_GRANTED
|
&& mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS)
|
!= PackageManager.PERMISSION_GRANTED) {
|
throw new SecurityException(message + " requires "
|
+ Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or "
|
+ Manifest.permission.REVOKE_RUNTIME_PERMISSIONS + " or "
|
+ Manifest.permission.GET_RUNTIME_PERMISSIONS);
|
}
|
}
|
|
/**
|
* Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS
|
* or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller.
|
* @param checkShell whether to prevent shell from access if there's a debugging restriction
|
* @param message the message to log on security exception
|
*/
|
private void enforceCrossUserPermission(int callingUid, int userId,
|
boolean requireFullPermission, boolean checkShell,
|
boolean requirePermissionWhenSameUser, String message) {
|
if (userId < 0) {
|
throw new IllegalArgumentException("Invalid userId " + userId);
|
}
|
if (checkShell) {
|
PackageManagerServiceUtils.enforceShellRestriction(
|
UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId);
|
}
|
if (!requirePermissionWhenSameUser && userId == UserHandle.getUserId(callingUid)) return;
|
if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) {
|
if (requireFullPermission) {
|
mContext.enforceCallingOrSelfPermission(
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
|
} else {
|
try {
|
mContext.enforceCallingOrSelfPermission(
|
android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message);
|
} catch (SecurityException se) {
|
mContext.enforceCallingOrSelfPermission(
|
android.Manifest.permission.INTERACT_ACROSS_USERS, message);
|
}
|
}
|
}
|
}
|
|
@GuardedBy({"mSettings.mLock", "mLock"})
|
private int calculateCurrentPermissionFootprintLocked(BasePermission tree) {
|
int size = 0;
|
for (BasePermission perm : mSettings.mPermissions.values()) {
|
size += tree.calculateFootprint(perm);
|
}
|
return size;
|
}
|
|
@GuardedBy({"mSettings.mLock", "mLock"})
|
private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) {
|
// We calculate the max size of permissions defined by this uid and throw
|
// if that plus the size of 'info' would exceed our stated maximum.
|
if (tree.getUid() != Process.SYSTEM_UID) {
|
final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree);
|
if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) {
|
throw new SecurityException("Permission tree size cap exceeded");
|
}
|
}
|
}
|
|
private void systemReady() {
|
mSystemReady = true;
|
if (mPrivappPermissionsViolations != null) {
|
throw new IllegalStateException("Signature|privileged permissions not in "
|
+ "privapp-permissions whitelist: " + mPrivappPermissionsViolations);
|
}
|
|
mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class);
|
mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class);
|
}
|
|
private static String getVolumeUuidForPackage(PackageParser.Package pkg) {
|
if (pkg == null) {
|
return StorageManager.UUID_PRIVATE_INTERNAL;
|
}
|
if (pkg.isExternal()) {
|
if (TextUtils.isEmpty(pkg.volumeUuid)) {
|
return StorageManager.UUID_PRIMARY_PHYSICAL;
|
} else {
|
return pkg.volumeUuid;
|
}
|
} else {
|
return StorageManager.UUID_PRIVATE_INTERNAL;
|
}
|
}
|
|
private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) {
|
for (int i=pkgInfo.permissions.size()-1; i>=0; i--) {
|
if (pkgInfo.permissions.get(i).info.name.equals(permName)) {
|
return true;
|
}
|
}
|
return false;
|
}
|
|
/**
|
* Log that a permission request was granted/revoked.
|
*
|
* @param action the action performed
|
* @param name name of the permission
|
* @param packageName package permission is for
|
*/
|
private void logPermission(int action, @NonNull String name, @NonNull String packageName) {
|
final LogMaker log = new LogMaker(action);
|
log.setPackageName(packageName);
|
log.addTaggedData(MetricsEvent.FIELD_PERMISSION, name);
|
|
mMetricsLogger.write(log);
|
}
|
|
/**
|
* Get the mapping of background permissions to their foreground permissions.
|
*
|
* <p>Only initialized in the system server.
|
*
|
* @return the map <bg permission -> list<fg perm>>
|
*/
|
public @Nullable ArrayMap<String, List<String>> getBackgroundPermissions() {
|
return mBackgroundPermissions;
|
}
|
|
private class PermissionManagerServiceInternalImpl extends PermissionManagerServiceInternal {
|
@Override
|
public void systemReady() {
|
PermissionManagerService.this.systemReady();
|
}
|
@Override
|
public boolean isPermissionsReviewRequired(@NonNull Package pkg, @UserIdInt int userId) {
|
return PermissionManagerService.this.isPermissionsReviewRequired(pkg, userId);
|
}
|
@Override
|
public void revokeRuntimePermissionsIfGroupChanged(
|
@NonNull PackageParser.Package newPackage,
|
@NonNull PackageParser.Package oldPackage,
|
@NonNull ArrayList<String> allPackageNames,
|
@NonNull PermissionCallback permissionCallback) {
|
PermissionManagerService.this.revokeRuntimePermissionsIfGroupChanged(newPackage,
|
oldPackage, allPackageNames, permissionCallback);
|
}
|
|
@Override
|
public void revokeRuntimePermissionsIfPermissionDefinitionChanged(
|
@NonNull List<String> permissionsToRevoke,
|
@NonNull ArrayList<String> allPackageNames,
|
@NonNull PermissionCallback permissionCallback) {
|
PermissionManagerService.this.revokeRuntimePermissionsIfPermissionDefinitionChanged(
|
permissionsToRevoke, allPackageNames, permissionCallback);
|
}
|
|
@Override
|
public List<String> addAllPermissions(Package pkg, boolean chatty) {
|
return PermissionManagerService.this.addAllPermissions(pkg, chatty);
|
}
|
@Override
|
public void addAllPermissionGroups(Package pkg, boolean chatty) {
|
PermissionManagerService.this.addAllPermissionGroups(pkg, chatty);
|
}
|
@Override
|
public void removeAllPermissions(Package pkg, boolean chatty) {
|
PermissionManagerService.this.removeAllPermissions(pkg, chatty);
|
}
|
@Override
|
public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid,
|
PermissionCallback callback) {
|
return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback);
|
}
|
@Override
|
public void removeDynamicPermission(String permName, int callingUid,
|
PermissionCallback callback) {
|
PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback);
|
}
|
@Override
|
public void grantRuntimePermission(String permName, String packageName,
|
boolean overridePolicy, int callingUid, int userId,
|
PermissionCallback callback) {
|
PermissionManagerService.this.grantRuntimePermission(
|
permName, packageName, overridePolicy, callingUid, userId, callback);
|
}
|
@Override
|
public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds,
|
String[] grantedPermissions, int callingUid, PermissionCallback callback) {
|
PermissionManagerService.this.grantRequestedRuntimePermissions(
|
pkg, userIds, grantedPermissions, callingUid, callback);
|
}
|
@Override
|
public List<String> getWhitelistedRestrictedPermissions(PackageParser.Package pkg,
|
@PackageManager.PermissionWhitelistFlags int whitelistFlags, int userId) {
|
return PermissionManagerService.this.getWhitelistedRestrictedPermissions(pkg,
|
whitelistFlags, userId);
|
}
|
@Override
|
public void setWhitelistedRestrictedPermissions(@NonNull PackageParser.Package pkg,
|
@NonNull int[] userIds, @Nullable List<String> permissions, int callingUid,
|
@PackageManager.PermissionWhitelistFlags int whitelistFlags,
|
@NonNull PermissionCallback callback) {
|
PermissionManagerService.this.setWhitelistedRestrictedPermissions(
|
pkg, userIds, permissions, callingUid, whitelistFlags, callback);
|
}
|
@Override
|
public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg,
|
int callingUid, PermissionCallback callback) {
|
PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked(
|
pkg, callingUid, callback);
|
}
|
@Override
|
public void revokeRuntimePermission(String permName, String packageName,
|
boolean overridePolicy, int userId, PermissionCallback callback) {
|
PermissionManagerService.this.revokeRuntimePermission(permName, packageName,
|
overridePolicy, userId, callback);
|
}
|
@Override
|
public void updatePermissions(String packageName, Package pkg, boolean replaceGrant,
|
Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
|
PermissionManagerService.this.updatePermissions(
|
packageName, pkg, replaceGrant, allPackages, callback);
|
}
|
@Override
|
public void updateAllPermissions(String volumeUuid, boolean sdkUpdated,
|
Collection<PackageParser.Package> allPackages, PermissionCallback callback) {
|
PermissionManagerService.this.updateAllPermissions(
|
volumeUuid, sdkUpdated, allPackages, callback);
|
}
|
@Override
|
public String[] getAppOpPermissionPackages(String permName) {
|
return PermissionManagerService.this.getAppOpPermissionPackages(permName);
|
}
|
@Override
|
public int getPermissionFlags(String permName, String packageName, int callingUid,
|
int userId) {
|
return PermissionManagerService.this.getPermissionFlags(permName, packageName,
|
callingUid, userId);
|
}
|
@Override
|
public void updatePermissionFlags(String permName, String packageName, int flagMask,
|
int flagValues, int callingUid, int userId, boolean overridePolicy,
|
PermissionCallback callback) {
|
PermissionManagerService.this.updatePermissionFlags(
|
permName, packageName, flagMask, flagValues, callingUid, userId,
|
overridePolicy, callback);
|
}
|
@Override
|
public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid,
|
int userId, Collection<Package> packages, PermissionCallback callback) {
|
return PermissionManagerService.this.updatePermissionFlagsForAllApps(
|
flagMask, flagValues, callingUid, userId, packages, callback);
|
}
|
@Override
|
public void enforceCrossUserPermission(int callingUid, int userId,
|
boolean requireFullPermission, boolean checkShell, String message) {
|
PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
|
requireFullPermission, checkShell, false, message);
|
}
|
@Override
|
public void enforceCrossUserPermission(int callingUid, int userId,
|
boolean requireFullPermission, boolean checkShell,
|
boolean requirePermissionWhenSameUser, String message) {
|
PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId,
|
requireFullPermission, checkShell, requirePermissionWhenSameUser, message);
|
}
|
@Override
|
public void enforceGrantRevokeRuntimePermissionPermissions(String message) {
|
PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message);
|
}
|
@Override
|
public int checkPermission(String permName, String packageName, int callingUid,
|
int userId) {
|
return PermissionManagerService.this.checkPermission(
|
permName, packageName, callingUid, userId);
|
}
|
@Override
|
public int checkUidPermission(String permName, PackageParser.Package pkg, int uid,
|
int callingUid) {
|
return PermissionManagerService.this.checkUidPermission(permName, pkg, uid, callingUid);
|
}
|
@Override
|
public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags,
|
int callingUid) {
|
return PermissionManagerService.this.getPermissionGroupInfo(
|
groupName, flags, callingUid);
|
}
|
@Override
|
public List<PermissionGroupInfo> getAllPermissionGroups(int flags, int callingUid) {
|
return PermissionManagerService.this.getAllPermissionGroups(flags, callingUid);
|
}
|
@Override
|
public PermissionInfo getPermissionInfo(String permName, String packageName, int flags,
|
int callingUid) {
|
return PermissionManagerService.this.getPermissionInfo(
|
permName, packageName, flags, callingUid);
|
}
|
@Override
|
public List<PermissionInfo> getPermissionInfoByGroup(String group, int flags,
|
int callingUid) {
|
return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid);
|
}
|
@Override
|
public PermissionSettings getPermissionSettings() {
|
return mSettings;
|
}
|
@Override
|
public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() {
|
return mDefaultPermissionGrantPolicy;
|
}
|
@Override
|
public BasePermission getPermissionTEMP(String permName) {
|
synchronized (PermissionManagerService.this.mLock) {
|
return mSettings.getPermissionLocked(permName);
|
}
|
}
|
|
@Override
|
public @NonNull ArrayList<PermissionInfo> getAllPermissionWithProtectionLevel(
|
@PermissionInfo.Protection int protectionLevel) {
|
ArrayList<PermissionInfo> matchingPermissions = new ArrayList<>();
|
|
synchronized (PermissionManagerService.this.mLock) {
|
int numTotalPermissions = mSettings.mPermissions.size();
|
|
for (int i = 0; i < numTotalPermissions; i++) {
|
BasePermission bp = mSettings.mPermissions.valueAt(i);
|
|
if (bp.perm != null && bp.perm.info != null
|
&& bp.protectionLevel == protectionLevel) {
|
matchingPermissions.add(bp.perm.info);
|
}
|
}
|
}
|
|
return matchingPermissions;
|
}
|
|
@Override
|
public @Nullable byte[] backupRuntimePermissions(@NonNull UserHandle user) {
|
return PermissionManagerService.this.backupRuntimePermissions(user);
|
}
|
|
@Override
|
public void restoreRuntimePermissions(@NonNull byte[] backup, @NonNull UserHandle user) {
|
PermissionManagerService.this.restoreRuntimePermissions(backup, user);
|
}
|
|
@Override
|
public void restoreDelayedRuntimePermissions(@NonNull String packageName,
|
@NonNull UserHandle user) {
|
PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, user);
|
}
|
|
@Override
|
public void addOnRuntimePermissionStateChangedListener(
|
OnRuntimePermissionStateChangedListener listener) {
|
PermissionManagerService.this.addOnRuntimePermissionStateChangedListener(
|
listener);
|
}
|
|
@Override
|
public void removeOnRuntimePermissionStateChangedListener(
|
OnRuntimePermissionStateChangedListener listener) {
|
PermissionManagerService.this.removeOnRuntimePermissionStateChangedListener(
|
listener);
|
}
|
}
|
}
|