diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java index 4830691d472aa..0958bcb6cc148 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -16,12 +16,9 @@ package com.android.server.pm.permission; -import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY; import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; -import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.RECORD_AUDIO; import static android.Manifest.permission.UPDATE_APP_OPS_STATS; -import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; import static android.app.AppOpsManager.ATTRIBUTION_FLAGS_NONE; import static android.app.AppOpsManager.MODE_ALLOWED; @@ -29,43 +26,11 @@ import static android.app.AppOpsManager.MODE_ERRORED; import static android.app.AppOpsManager.MODE_IGNORED; import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED; import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; -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_GRANTED_BY_ROLE; -import static android.content.pm.PackageManager.FLAG_PERMISSION_ONE_TIME; -import static android.content.pm.PackageManager.FLAG_PERMISSION_POLICY_FIXED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; -import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; -import static android.content.pm.PackageManager.FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; -import static android.content.pm.PackageManager.FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; -import static android.content.pm.PackageManager.FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY; -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.content.pm.PackageManager.MATCH_DEBUG_TRIAGED_MISSING; -import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; -import static android.permission.PermissionManager.KILL_APP_REASON_GIDS_CHANGED; -import static android.permission.PermissionManager.KILL_APP_REASON_PERMISSIONS_REVOKED; -import static com.android.server.pm.ApexManager.MATCH_ACTIVE_PACKAGE; -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 java.util.concurrent.TimeUnit.SECONDS; - import android.Manifest; import android.annotation.AppIdInt; -import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -73,119 +38,49 @@ import android.app.ActivityManager; import android.app.AppOpsManager; import android.app.AppOpsManager.AttributionFlags; import android.app.IActivityManager; -import android.app.admin.DevicePolicyManagerInternal; -import android.compat.annotation.ChangeId; -import android.compat.annotation.EnabledAfter; import android.content.AttributionSource; import android.content.AttributionSourceState; import android.content.Context; import android.content.PermissionChecker; -import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.PermissionGroupInfoFlags; -import android.content.pm.PackageManager.PermissionInfoFlags; -import android.content.pm.PackageManager.PermissionWhitelistFlags; import android.content.pm.PackageManagerInternal; import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; -import android.content.pm.SigningDetails; -import android.content.pm.parsing.component.ComponentMutateUtils; -import android.content.pm.parsing.component.ParsedPermission; -import android.content.pm.parsing.component.ParsedPermissionGroup; -import android.content.pm.parsing.component.ParsedPermissionUtils; -import android.content.pm.permission.CompatibilityPermissionInfo; import android.content.pm.permission.SplitPermissionInfoParcelable; -import android.metrics.LogMaker; -import android.os.AsyncTask; import android.os.Binder; -import android.os.Build; -import android.os.Debug; -import android.os.Handler; -import android.os.HandlerThread; import android.os.IBinder; -import android.os.Looper; -import android.os.Message; import android.os.Process; -import android.os.RemoteCallbackList; import android.os.RemoteException; import android.os.ServiceManager; -import android.os.Trace; import android.os.UserHandle; -import android.os.UserManager; -import android.os.storage.StorageManager; import android.permission.IOnPermissionsChangeListener; import android.permission.IPermissionChecker; import android.permission.IPermissionManager; import android.permission.PermissionCheckerManager; -import android.permission.PermissionControllerManager; import android.permission.PermissionManager; import android.permission.PermissionManagerInternal; -import android.text.TextUtils; import android.util.ArrayMap; -import android.util.ArraySet; -import android.util.DebugUtils; -import android.util.EventLog; -import android.util.IntArray; -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.compat.IPlatformCompat; -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.CollectionUtils; -import com.android.internal.util.DumpUtils; -import com.android.internal.util.IntPair; -import com.android.internal.util.Preconditions; import com.android.internal.util.function.TriFunction; -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.ApexManager; -import com.android.server.pm.PackageSetting; import com.android.server.pm.UserManagerInternal; -import com.android.server.pm.UserManagerService; -import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; -import com.android.server.pm.parsing.pkg.AndroidPackageUtils; import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener; -import com.android.server.pm.pkg.PackageStateInternal; -import com.android.server.policy.PermissionPolicyInternal; -import com.android.server.policy.SoftRestrictedPermissionPolicy; -import libcore.util.EmptyArray; - -import java.io.FileDescriptor; -import java.io.PrintWriter; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; import java.util.Collections; -import java.util.HashMap; -import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; import java.util.WeakHashMap; -import java.util.concurrent.CompletableFuture; import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.function.BiFunction; @@ -194,68 +89,12 @@ import java.util.function.BiFunction; * Manages all permissions and handles permissions related tasks. */ public class PermissionManagerService extends IPermissionManager.Stub { - private static final String TAG = "PackageManager"; private static final String LOG_TAG = PermissionManagerService.class.getSimpleName(); - private static final long BACKUP_TIMEOUT_MILLIS = SECONDS.toMillis(60); - - // For automotive products, CarService enforces allow-listing of the privileged permissions - // com.android.car is the package name which declares auto specific permissions - private static final String CAR_PACKAGE_NAME = "com.android.car"; - - /** 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; - - /** All storage permissions */ - private static final List STORAGE_PERMISSIONS = new ArrayList<>(); - /** All nearby devices permissions */ - private static final List NEARBY_DEVICES_PERMISSIONS = new ArrayList<>(); - - /** - * All permissions that should be granted with the REVOKE_WHEN_REQUESTED flag, if they are - * implicitly added to a package - */ - private static final List IMPLICIT_GRANTED_PERMISSIONS = new ArrayList<>(); - - /** If the permission of the value is granted, so is the key */ - private static final Map FULLER_PERMISSION_MAP = new HashMap<>(); - /** Map of IBinder -> Running AttributionSource */ private static final ConcurrentHashMap sRunningAttributionSources = new ConcurrentHashMap<>(); - 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); - STORAGE_PERMISSIONS.add(Manifest.permission.READ_EXTERNAL_STORAGE); - STORAGE_PERMISSIONS.add(Manifest.permission.WRITE_EXTERNAL_STORAGE); - STORAGE_PERMISSIONS.add(Manifest.permission.ACCESS_MEDIA_LOCATION); - NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_ADVERTISE); - NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_CONNECT); - NEARBY_DEVICES_PERMISSIONS.add(Manifest.permission.BLUETOOTH_SCAN); - IMPLICIT_GRANTED_PERMISSIONS.add(Manifest.permission.POST_NOTIFICATIONS); - } - - /** Set of source package names for Privileged Permission Allowlist */ - private final ArraySet mPrivilegedPermissionAllowlistSourcePackageNames = - new ArraySet<>(); - /** Lock to protect internal data access */ private final Object mLock = new Object(); @@ -265,13 +104,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; - @GuardedBy("mLock") - @NonNull - private final DevicePermissionState mState = new DevicePermissionState(); - - /** Permission controller: User space permission management */ - private PermissionControllerManager mPermissionControllerManager; - /** Map of OneTimePermissionUserManagers keyed by userId */ @GuardedBy("mLock") @NonNull @@ -281,130 +113,18 @@ public class PermissionManagerService extends IPermissionManager.Stub { /** App ops manager */ private final AppOpsManager mAppOpsManager; - /** - * Built-in permissions. Read from system configuration files. Mapping is from - * UID to permission name. - */ - private final SparseArray> mSystemPermissions; - - /** Built-in group IDs given to all packages. Read from system configuration files. */ - @NonNull - private final int[] mGlobalGids; - - private final HandlerThread mHandlerThread; - private final Handler mHandler; private final Context mContext; - private final MetricsLogger mMetricsLogger = new MetricsLogger(); - private final IPlatformCompat mPlatformCompat = IPlatformCompat.Stub.asInterface( - ServiceManager.getService(Context.PLATFORM_COMPAT_SERVICE)); - - /** Internal storage for permissions and related settings */ - @GuardedBy("mLock") - @NonNull - private final PermissionRegistry mRegistry = new PermissionRegistry(); + private final PermissionManagerServiceImpl mPermissionManagerServiceImpl; @NonNull private final AttributionSourceRegistry mAttributionSourceRegistry; - @GuardedBy("mLock") - @Nullable - private ArraySet mPrivappPermissionsViolations; - - @GuardedBy("mLock") - private boolean mSystemReady; - - @GuardedBy("mLock") - private PermissionPolicyInternal mPermissionPolicyInternal; - - /** - * A permission backup might contain apps that are not installed. In this case we delay the - * restoration until the app is installed. - * - *

This array ({@code userId -> noDelayedBackupLeft}) is {@code true} for all the users where - * there is no more delayed backup left. - */ - @GuardedBy("mLock") - private final SparseBooleanArray mHasNoDelayedPermBackup = new SparseBooleanArray(); - - /** Listeners for permission state (granting and flags) changes */ - @GuardedBy("mLock") - final private ArrayList - mRuntimePermissionStateChangedListeners = new ArrayList<>(); - @GuardedBy("mLock") private CheckPermissionDelegate mCheckPermissionDelegate; - @NonNull - private final OnPermissionChangeListeners mOnPermissionChangeListeners; - @Nullable private HotwordDetectionServiceProvider mHotwordDetectionServiceProvider; - // TODO: Take a look at the methods defined in the callback. - // The callback was initially created to support the split between permission - // manager and the package manager. However, it's started to be used for other - // purposes. It may make sense to keep as an abstraction, but, the methods - // necessary to be overridden may be different than what was initially needed - // for the split. - private final PermissionCallback mDefaultPermissionCallback = new PermissionCallback() { - @Override - public void onGidsChanged(int appId, int userId) { - mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_GIDS_CHANGED)); - } - @Override - public void onPermissionGranted(int uid, int userId) { - mOnPermissionChangeListeners.onPermissionsChanged(uid); - - // Not critical; if this is lost, the application has to request again. - mPackageManagerInt.writeSettings(true); - } - @Override - public void onInstallPermissionGranted() { - mPackageManagerInt.writeSettings(true); - } - @Override - public void onPermissionRevoked(int uid, int userId, String reason) { - mOnPermissionChangeListeners.onPermissionsChanged(uid); - - // Critical; after this call the application should never have the permission - mPackageManagerInt.writeSettings(false); - final int appId = UserHandle.getAppId(uid); - if (reason == null) { - mHandler.post(() -> killUid(appId, userId, KILL_APP_REASON_PERMISSIONS_REVOKED)); - } else { - mHandler.post(() -> killUid(appId, userId, reason)); - } - } - @Override - public void onInstallPermissionRevoked() { - mPackageManagerInt.writeSettings(true); - } - @Override - public void onPermissionUpdated(int[] userIds, boolean sync) { - mPackageManagerInt.writePermissionSettings(userIds, !sync); - } - @Override - public void onInstallPermissionUpdated() { - mPackageManagerInt.writeSettings(true); - } - @Override - public void onPermissionRemoved() { - mPackageManagerInt.writeSettings(false); - } - public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, - int uid) { - onPermissionUpdated(updatedUserIds, sync); - for (int i = 0; i < updatedUserIds.length; i++) { - int userUid = UserHandle.getUid(updatedUserIds[i], UserHandle.getAppId(uid)); - mOnPermissionChangeListeners.onPermissionsChanged(userUid); - } - } - public void onInstallPermissionUpdatedNotifyListener(int uid) { - onInstallPermissionUpdated(); - mOnPermissionChangeListeners.onPermissionsChanged(uid); - } - }; - PermissionManagerService(@NonNull Context context, @NonNull ArrayMap availableFeatures) { // The package info cache is the cache for package and permission information. @@ -418,55 +138,15 @@ public class PermissionManagerService extends IPermissionManager.Stub { mUserManagerInt = LocalServices.getService(UserManagerInternal.class); mAppOpsManager = context.getSystemService(AppOpsManager.class); - mPrivilegedPermissionAllowlistSourcePackageNames.add(PLATFORM_PACKAGE_NAME); - // PackageManager.hasSystemFeature() is not used here because PackageManagerService - // isn't ready yet. - if (availableFeatures.containsKey(PackageManager.FEATURE_AUTOMOTIVE)) { - mPrivilegedPermissionAllowlistSourcePackageNames.add(CAR_PACKAGE_NAME); - } - - mHandlerThread = new ServiceThread(TAG, - Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); - mHandlerThread.start(); - mHandler = new Handler(mHandlerThread.getLooper()); - Watchdog.getInstance().addThread(mHandler); - - SystemConfig systemConfig = SystemConfig.getInstance(); - mSystemPermissions = systemConfig.getSystemPermissions(); - mGlobalGids = systemConfig.getGlobalGids(); - mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper()); mAttributionSourceRegistry = new AttributionSourceRegistry(context); - // propagate permission configuration - final ArrayMap permConfig = - SystemConfig.getInstance().getPermissions(); - synchronized (mLock) { - for (int i=0; i @@ -519,511 +202,22 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - @NonNull - private String[] getAppOpPermissionPackagesInternal(@NonNull String permissionName) { - synchronized (mLock) { - final ArraySet packageNames = mRegistry.getAppOpPermissionPackages( - permissionName); - if (packageNames == null) { - return EmptyArray.STRING; - } - return packageNames.toArray(new String[0]); - } - } - - @Override - @NonNull - public ParceledListSlice getAllPermissionGroups( - @PermissionGroupInfoFlags int flags) { - final int callingUid = getCallingUid(); - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return ParceledListSlice.emptyList(); - } - - final List out = new ArrayList<>(); - synchronized (mLock) { - for (ParsedPermissionGroup pg : mRegistry.getPermissionGroups()) { - out.add(PackageInfoUtils.generatePermissionGroupInfo(pg, flags)); - } - } - - final int callingUserId = UserHandle.getUserId(callingUid); - out.removeIf(it -> mPackageManagerInt.filterAppAccess(it.packageName, callingUid, - callingUserId)); - return new ParceledListSlice<>(out); - } - - - @Override - @Nullable - public PermissionGroupInfo getPermissionGroupInfo(String groupName, - @PermissionGroupInfoFlags int flags) { - final int callingUid = getCallingUid(); - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return null; - } - - final PermissionGroupInfo permissionGroupInfo; - synchronized (mLock) { - final ParsedPermissionGroup permissionGroup = mRegistry.getPermissionGroup(groupName); - if (permissionGroup == null) { - return null; - } - permissionGroupInfo = PackageInfoUtils.generatePermissionGroupInfo(permissionGroup, - flags); - } - - final int callingUserId = UserHandle.getUserId(callingUid); - if (mPackageManagerInt.filterAppAccess(permissionGroupInfo.packageName, callingUid, - callingUserId)) { - EventLog.writeEvent(0x534e4554, "186113473", callingUid, groupName); - return null; - } - return permissionGroupInfo; - } - - - @Override - @Nullable - public PermissionInfo getPermissionInfo(@NonNull String permName, @NonNull String opPackageName, - @PermissionInfoFlags int flags) { - final int callingUid = getCallingUid(); - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return null; - } - - final AndroidPackage opPackage = mPackageManagerInt.getPackage(opPackageName); - final int targetSdkVersion = getPermissionInfoCallingTargetSdkVersion(opPackage, - callingUid); - final PermissionInfo permissionInfo; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permName); - if (bp == null) { - return null; - } - permissionInfo = bp.generatePermissionInfo(flags, targetSdkVersion); - } - - final int callingUserId = UserHandle.getUserId(callingUid); - if (mPackageManagerInt.filterAppAccess(permissionInfo.packageName, callingUid, - callingUserId)) { - EventLog.writeEvent(0x534e4554, "183122164", callingUid, permName); - return null; - } - return permissionInfo; - } - - private int getPermissionInfoCallingTargetSdkVersion(@Nullable AndroidPackage pkg, int uid) { - final int appId = UserHandle.getAppId(uid); - if (appId == Process.ROOT_UID || appId == Process.SYSTEM_UID - || appId == Process.SHELL_UID) { - // System sees all flags. - return Build.VERSION_CODES.CUR_DEVELOPMENT; - } - if (pkg == null) { - return Build.VERSION_CODES.CUR_DEVELOPMENT; - } - return pkg.getTargetSdkVersion(); - } - - @Override - @Nullable - public ParceledListSlice queryPermissionsByGroup(String groupName, - @PermissionInfoFlags int flags) { - final int callingUid = getCallingUid(); - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return null; - } - - final List out = new ArrayList<>(10); - synchronized (mLock) { - if (groupName != null && mRegistry.getPermissionGroup(groupName) == null) { - return null; - } - for (Permission bp : mRegistry.getPermissions()) { - if (Objects.equals(bp.getGroup(), groupName)) { - out.add(bp.generatePermissionInfo(flags)); - } - } - } - - final int callingUserId = UserHandle.getUserId(callingUid); - out.removeIf(it -> mPackageManagerInt.filterAppAccess(it.packageName, callingUid, - callingUserId)); - return new ParceledListSlice<>(out); - } - - @Override - public boolean addPermission(PermissionInfo info, boolean async) { - final int callingUid = getCallingUid(); - 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 boolean added; - final boolean changed; - synchronized (mLock) { - final Permission tree = mRegistry.enforcePermissionTree(info.name, callingUid); - Permission bp = mRegistry.getPermission(info.name); - added = bp == null; - int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); - if (added) { - enforcePermissionCapLocked(info, tree); - bp = new Permission(info.name, tree.getPackageName(), Permission.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) { - mRegistry.addPermission(bp); - } - } - if (changed) { - mPackageManagerInt.writeSettings(async); - } - return added; - } - - @Override - public void removePermission(String permName) { - final int callingUid = getCallingUid(); - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - throw new SecurityException("Instant applications don't have access to this method"); - } - synchronized (mLock) { - mRegistry.enforcePermissionTree(permName, callingUid); - final Permission bp = mRegistry.getPermission(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); - } - mRegistry.removePermission(permName); - } - mPackageManagerInt.writeSettings(false); - } - - @Override - public int getPermissionFlags(String packageName, String permName, int userId) { - final int callingUid = getCallingUid(); - return getPermissionFlagsInternal(packageName, permName, callingUid, userId); - } - - private int getPermissionFlagsInternal( - String packageName, String permName, int callingUid, int userId) { - if (!mUserManagerInt.exists(userId)) { - return 0; - } - - enforceGrantRevokeGetRuntimePermissionPermissions("getPermissionFlags"); - enforceCrossUserPermission(callingUid, userId, - true, // requireFullPermission - false, // checkShell - "getPermissionFlags"); - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - if (pkg == null) { - return 0; - } - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { - return 0; - } - - synchronized (mLock) { - if (mRegistry.getPermission(permName) == null) { - return 0; - } - - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); - return 0; - } - - return uidState.getPermissionFlags(permName); - } - } - - @Override - public void updatePermissionFlags(String packageName, String permName, int flagMask, - int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { - final int callingUid = getCallingUid(); - boolean overridePolicy = false; - - if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { - final long callingIdentity = Binder.clearCallingIdentity(); - try { - if ((flagMask & FLAG_PERMISSION_POLICY_FIXED) != 0) { - if (checkAdjustPolicyFlagPermission) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, - "Need " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY - + " to change policy flags"); - } else if (mPackageManagerInt.getUidTargetSdkVersion(callingUid) - >= Build.VERSION_CODES.Q) { - throw new IllegalArgumentException( - Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY + " needs " - + " to be checked for packages targeting " - + Build.VERSION_CODES.Q + " or later when changing policy " - + "flags"); - } - overridePolicy = true; - } - } finally { - Binder.restoreCallingIdentity(callingIdentity); - } - } - - updatePermissionFlagsInternal( - packageName, permName, flagMask, flagValues, callingUid, userId, - overridePolicy, mDefaultPermissionCallback); - } - - private void updatePermissionFlagsInternal(String packageName, String permName, int flagMask, - int flagValues, int callingUid, int userId, boolean overridePolicy, - PermissionCallback callback) { - if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES - && PermissionManager.shouldTraceGrant(packageName, permName, userId)) { - Log.i(TAG, "System is updating flags for " + packageName + " " - + permName + " for user " + userId + " " - + DebugUtils.flagsToString( - PackageManager.class, "FLAG_PERMISSION_", flagMask) - + " := " - + DebugUtils.flagsToString( - PackageManager.class, "FLAG_PERMISSION_", flagValues) - + " on behalf of uid " + callingUid - + " " + mPackageManagerInt.getNameForUid(callingUid), - new RuntimeException()); - } - - if (!mUserManagerInt.exists(userId)) { - return; - } - - enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags"); - - enforceCrossUserPermission(callingUid, userId, - true, // requireFullPermission - true, // checkShell - "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 &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - flagValues &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - flagValues &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - flagValues &= ~PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; - } - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - if (pkg == null) { - Log.e(TAG, "Unknown package: " + packageName); - return; - } - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - boolean isRequested = false; - // Fast path, the current package has requested the permission. - if (pkg.getRequestedPermissions().contains(permName)) { - isRequested = true; - } - if (!isRequested) { - // Slow path, go through all shared user packages. - String[] sharedUserPackageNames = - mPackageManagerInt.getSharedUserPackagesForPackage(packageName, userId); - for (String sharedUserPackageName : sharedUserPackageNames) { - AndroidPackage sharedUserPkg = mPackageManagerInt.getPackage( - sharedUserPackageName); - if (sharedUserPkg != null - && sharedUserPkg.getRequestedPermissions().contains(permName)) { - isRequested = true; - break; - } - } - } - - final boolean isRuntimePermission; - final boolean permissionUpdated; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permName); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - - isRuntimePermission = bp.isRuntime(); - - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); - return; - } - - if (!uidState.hasPermissionState(permName) && !isRequested) { - Log.e(TAG, "Permission " + permName + " isn't requested by package " + packageName); - return; - } - - permissionUpdated = uidState.updatePermissionFlags(bp, flagMask, flagValues); - } - - if (permissionUpdated && isRuntimePermission) { - 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 (!isRuntimePermission) { - int userUid = UserHandle.getUid(userId, pkg.getUid()); - callback.onInstallPermissionUpdatedNotifyListener(userUid); - } else { - callback.onPermissionUpdatedNotifyListener(new int[]{userId}, false, pkg.getUid()); - } - } - } - - /** - * Update the permission flags for all packages and runtime permissions of a user in order - * to allow device or profile owner to remove POLICY_FIXED. - */ - @Override - public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, - final int userId) { - final int callingUid = getCallingUid(); - if (!mUserManagerInt.exists(userId)) { - return; - } - - enforceGrantRevokeRuntimePermissionPermissions( - "updatePermissionFlagsForAllApps"); - enforceCrossUserPermission(callingUid, userId, - true, // requireFullPermission - true, // checkShell - "updatePermissionFlagsForAllApps"); - - // Only the system can change system fixed flags. - final int effectiveFlagMask = (callingUid != Process.SYSTEM_UID) - ? flagMask : flagMask & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - final int effectiveFlagValues = (callingUid != Process.SYSTEM_UID) - ? flagValues : flagValues & ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; - - final boolean[] changed = new boolean[1]; - mPackageManagerInt.forEachPackage(pkg -> { - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, - "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - return; - } - changed[0] |= uidState.updatePermissionFlagsForAllPermissions( - effectiveFlagMask, effectiveFlagValues); - } - mOnPermissionChangeListeners.onPermissionsChanged(pkg.getUid()); - }); - - if (changed[0]) { - mPackageManagerInt.writePermissionSettings(new int[] { userId }, true); - } - } - private int checkPermission(String pkgName, String permName, @UserIdInt int userId) { // Not using Objects.requireNonNull() here for compatibility reasons. if (pkgName == null || permName == null) { return PackageManager.PERMISSION_DENIED; } - if (!mUserManagerInt.exists(userId)) { - return PackageManager.PERMISSION_DENIED; - } final CheckPermissionDelegate checkPermissionDelegate; synchronized (mLock) { checkPermissionDelegate = mCheckPermissionDelegate; } + if (checkPermissionDelegate == null) { - return checkPermissionImpl(pkgName, permName, userId); + return mPermissionManagerServiceImpl.checkPermission(pkgName, permName, userId); } return checkPermissionDelegate.checkPermission(pkgName, permName, userId, - this::checkPermissionImpl); - } - - private int checkPermissionImpl(String pkgName, String permName, int userId) { - final AndroidPackage pkg = mPackageManagerInt.getPackage(pkgName); - if (pkg == null) { - return PackageManager.PERMISSION_DENIED; - } - return checkPermissionInternal(pkg, true, permName, userId); - } - - private int checkPermissionInternal(@NonNull AndroidPackage pkg, boolean isPackageExplicit, - @NonNull String permissionName, @UserIdInt int userId) { - final int callingUid = getCallingUid(); - if (isPackageExplicit || pkg.getSharedUserId() == null) { - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { - return PackageManager.PERMISSION_DENIED; - } - } else { - if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return PackageManager.PERMISSION_DENIED; - } - } - - final int uid = UserHandle.getUid(userId, pkg.getUid()); - final boolean isInstantApp = mPackageManagerInt.getInstantAppPackageName(uid) != null; - - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - return PackageManager.PERMISSION_DENIED; - } - - if (checkSinglePermissionInternalLocked(uidState, permissionName, isInstantApp)) { - return PackageManager.PERMISSION_GRANTED; - } - - final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName); - if (fullerPermissionName != null && checkSinglePermissionInternalLocked(uidState, - fullerPermissionName, isInstantApp)) { - return PackageManager.PERMISSION_GRANTED; - } - } - - return PackageManager.PERMISSION_DENIED; - } - - @GuardedBy("mLock") - private boolean checkSinglePermissionInternalLocked(@NonNull UidPermissionState uidState, - @NonNull String permissionName, boolean isInstantApp) { - if (!uidState.isPermissionGranted(permissionName)) { - return false; - } - - if (isInstantApp) { - final Permission permission = mRegistry.getPermission(permissionName); - return permission != null && permission.isInstant(); - } - - return true; + mPermissionManagerServiceImpl::checkPermission); } private int checkUidPermission(int uid, String permName) { @@ -1031,332 +225,17 @@ public class PermissionManagerService extends IPermissionManager.Stub { if (permName == null) { return PackageManager.PERMISSION_DENIED; } - final int userId = UserHandle.getUserId(uid); - if (!mUserManagerInt.exists(userId)) { - return PackageManager.PERMISSION_DENIED; - } final CheckPermissionDelegate checkPermissionDelegate; synchronized (mLock) { checkPermissionDelegate = mCheckPermissionDelegate; } + if (checkPermissionDelegate == null) { - return checkUidPermissionImpl(uid, permName); + return mPermissionManagerServiceImpl.checkUidPermission(uid, permName); } return checkPermissionDelegate.checkUidPermission(uid, permName, - this::checkUidPermissionImpl); - } - - private int checkUidPermissionImpl(int uid, String permName) { - final AndroidPackage pkg = mPackageManagerInt.getPackage(uid); - return checkUidPermissionInternal(pkg, uid, permName); - } - - /** - * Checks whether or not the given package has been granted the specified - * permission. If the given package is {@code null}, we instead check the - * system permissions for the given UID. - * - * @see SystemConfig#getSystemPermissions() - */ - private int checkUidPermissionInternal(@Nullable AndroidPackage pkg, int uid, - @NonNull String permissionName) { - if (pkg != null) { - final int userId = UserHandle.getUserId(uid); - return checkPermissionInternal(pkg, false, permissionName, userId); - } - - synchronized (mLock) { - if (checkSingleUidPermissionInternalLocked(uid, permissionName)) { - return PackageManager.PERMISSION_GRANTED; - } - - final String fullerPermissionName = FULLER_PERMISSION_MAP.get(permissionName); - if (fullerPermissionName != null - && checkSingleUidPermissionInternalLocked(uid, fullerPermissionName)) { - return PackageManager.PERMISSION_GRANTED; - } - } - - return PackageManager.PERMISSION_DENIED; - } - - @GuardedBy("mLock") - private boolean checkSingleUidPermissionInternalLocked(int uid, - @NonNull String permissionName) { - ArraySet permissions = mSystemPermissions.get(uid); - return permissions != null && permissions.contains(permissionName); - } - - @Override - public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.OBSERVE_GRANT_REVOKE_PERMISSIONS, - "addOnPermissionsChangeListener"); - - mOnPermissionChangeListeners.addListener(listener); - } - - @Override - public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { - if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) { - throw new SecurityException("Instant applications don't have access to this method"); - } - mOnPermissionChangeListeners.removeListener(listener); - } - - @Nullable - @Override - public List getAllowlistedRestrictedPermissions(@NonNull String packageName, - @PermissionWhitelistFlags int flags, @UserIdInt int userId) { - Objects.requireNonNull(packageName); - Preconditions.checkFlagsArgument(flags, - PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE - | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM - | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); - Preconditions.checkArgumentNonNegative(userId, null); - - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS, - "getAllowlistedRestrictedPermissions for user " + userId); - } - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - if (pkg == null) { - return null; - } - - final int callingUid = Binder.getCallingUid(); - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, UserHandle.getCallingUserId())) { - return null; - } - final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( - Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) - == PackageManager.PERMISSION_GRANTED; - final boolean isCallerInstallerOnRecord = - mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid); - - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 - && !isCallerPrivileged) { - throw new SecurityException("Querying system allowlist requires " - + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); - } - - if ((flags & (PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE - | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER)) != 0) { - if (!isCallerPrivileged && !isCallerInstallerOnRecord) { - throw new SecurityException("Querying upgrade or installer allowlist" - + " requires being installer on record or " - + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); - } - } - - final long identity = Binder.clearCallingIdentity(); - try { - return getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - @Nullable - private List getAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, - @PermissionWhitelistFlags int flags, @UserIdInt int userId) { - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - return null; - } - - int queryFlags = 0; - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { - queryFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } - - ArrayList allowlistedPermissions = null; - - final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions()); - for (int i = 0; i < permissionCount; i++) { - final String permissionName = pkg.getRequestedPermissions().get(i); - final int currentFlags = - uidState.getPermissionFlags(permissionName); - if ((currentFlags & queryFlags) != 0) { - if (allowlistedPermissions == null) { - allowlistedPermissions = new ArrayList<>(); - } - allowlistedPermissions.add(permissionName); - } - } - - return allowlistedPermissions; - } - } - - @Override - public boolean addAllowlistedRestrictedPermission(@NonNull String packageName, - @NonNull String permName, @PermissionWhitelistFlags int flags, - @UserIdInt int userId) { - // Other argument checks are done in get/setAllowlistedRestrictedPermissions - Objects.requireNonNull(permName); - - if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permName)) { - return false; - } - - List permissions = - getAllowlistedRestrictedPermissions(packageName, flags, userId); - if (permissions == null) { - permissions = new ArrayList<>(1); - } - if (permissions.indexOf(permName) < 0) { - permissions.add(permName); - return setAllowlistedRestrictedPermissions(packageName, permissions, - flags, userId); - } - return false; - } - - private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( - @NonNull String permName) { - final String permissionPackageName; - final boolean isImmutablyRestrictedPermission; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permName); - if (bp == null) { - Slog.w(TAG, "No such permissions: " + permName); - return false; - } - permissionPackageName = bp.getPackageName(); - isImmutablyRestrictedPermission = bp.isHardOrSoftRestricted() - && bp.isImmutablyRestricted(); - } - - final int callingUid = getCallingUid(); - final int callingUserId = UserHandle.getUserId(callingUid); - if (mPackageManagerInt.filterAppAccess(permissionPackageName, callingUid, callingUserId)) { - EventLog.writeEvent(0x534e4554, "186404356", callingUid, permName); - return false; - } - - if (isImmutablyRestrictedPermission && mContext.checkCallingOrSelfPermission( - Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot modify allowlisting of an immutably " - + "restricted permission: " + permName); - } - - return true; - } - - @Override - public boolean removeAllowlistedRestrictedPermission(@NonNull String packageName, - @NonNull String permName, @PermissionWhitelistFlags int flags, - @UserIdInt int userId) { - // Other argument checks are done in get/setAllowlistedRestrictedPermissions - Objects.requireNonNull(permName); - - if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permName)) { - return false; - } - - final List permissions = - getAllowlistedRestrictedPermissions(packageName, flags, userId); - if (permissions != null && permissions.remove(permName)) { - return setAllowlistedRestrictedPermissions(packageName, permissions, - flags, userId); - } - return false; - } - - private boolean setAllowlistedRestrictedPermissions(@NonNull String packageName, - @Nullable List permissions, @PermissionWhitelistFlags int flags, - @UserIdInt int userId) { - Objects.requireNonNull(packageName); - Preconditions.checkFlagsArgument(flags, - PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE - | PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM - | PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER); - Preconditions.checkArgument(Integer.bitCount(flags) == 1); - Preconditions.checkArgumentNonNegative(userId, null); - - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingOrSelfPermission( - Manifest.permission.INTERACT_ACROSS_USERS, - "setAllowlistedRestrictedPermissions for user " + userId); - } - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - if (pkg == null) { - return false; - } - - final int callingUid = Binder.getCallingUid(); - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, UserHandle.getCallingUserId())) { - return false; - } - - final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( - Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) - == PackageManager.PERMISSION_GRANTED; - final boolean isCallerInstallerOnRecord = - mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid); - - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_SYSTEM) != 0 && !isCallerPrivileged) { - throw new SecurityException("Modifying system allowlist requires " - + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); - } - - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE) != 0) { - if (!isCallerPrivileged && !isCallerInstallerOnRecord) { - throw new SecurityException("Modifying upgrade allowlist requires" - + " being installer on record or " - + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); - } - final List allowlistedPermissions = - getAllowlistedRestrictedPermissions(pkg.getPackageName(), flags, userId); - if (permissions == null || permissions.isEmpty()) { - if (allowlistedPermissions == null || allowlistedPermissions.isEmpty()) { - return true; - } - } else { - // Only the system can add and remove while the installer can only remove. - final int permissionCount = permissions.size(); - for (int i = 0; i < permissionCount; i++) { - if ((allowlistedPermissions == null - || !allowlistedPermissions.contains(permissions.get(i))) - && !isCallerPrivileged) { - throw new SecurityException("Adding to upgrade allowlist requires" - + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); - } - } - } - - if ((flags & PackageManager.FLAG_PERMISSION_WHITELIST_INSTALLER) != 0) { - if (!isCallerPrivileged && !isCallerInstallerOnRecord) { - throw new SecurityException("Modifying installer allowlist requires" - + " being installer on record or " - + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS); - } - } - } - - final long identity = Binder.clearCallingIdentity(); - try { - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - - return true; + mPermissionManagerServiceImpl::checkUidPermission); } @Override @@ -1438,744 +317,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { } } - @Override - public void grantRuntimePermission(String packageName, String permName, final int userId) { - final int callingUid = Binder.getCallingUid(); - final boolean overridePolicy = - checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) - == PackageManager.PERMISSION_GRANTED; - - grantRuntimePermissionInternal(packageName, permName, overridePolicy, - callingUid, userId, mDefaultPermissionCallback); - } - - private void grantRuntimePermissionInternal(String packageName, String permName, - boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) { - if (PermissionManager.DEBUG_TRACE_GRANTS - && PermissionManager.shouldTraceGrant(packageName, permName, userId)) { - Log.i(PermissionManager.LOG_TAG_TRACE_GRANTS, "System is granting " + packageName + " " - + permName + " for user " + userId + " on behalf of uid " + callingUid - + " " + mPackageManagerInt.getNameForUid(callingUid), - new RuntimeException()); - } - 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 - "grantRuntimePermission"); - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final PackageStateInternal ps = mPackageManagerInt.getPackageStateInternal(packageName); - if (pkg == null || ps == null) { - Log.e(TAG, "Unknown package: " + packageName); - return; - } - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - final boolean isRolePermission; - final boolean isSoftRestrictedPermission; - synchronized (mLock) { - final Permission permission = mRegistry.getPermission(permName); - if (permission == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - isRolePermission = permission.isRole(); - isSoftRestrictedPermission = permission.isSoftRestricted(); - } - final boolean mayGrantRolePermission = isRolePermission - && mayManageRolePermission(callingUid); - final boolean mayGrantSoftRestrictedPermission = isSoftRestrictedPermission - && SoftRestrictedPermissionPolicy.forPermission(mContext, - AndroidPackageUtils.generateAppInfoWithoutState(pkg), pkg, - UserHandle.of(userId), permName) - .mayGrantPermission(); - - final boolean isRuntimePermission; - final boolean permissionHasGids; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permName); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - - isRuntimePermission = bp.isRuntime(); - permissionHasGids = bp.hasGids(); - if (isRuntimePermission || bp.isDevelopment()) { - // Good. - } else if (bp.isRole()) { - if (!mayGrantRolePermission) { - throw new SecurityException("Permission " + permName + " is managed by role"); - } - } else { - throw new SecurityException("Permission " + permName + " requested by " - + pkg.getPackageName() + " is not a changeable permission type"); - } - - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - return; - } - - if (!(uidState.hasPermissionState(permName) - || pkg.getRequestedPermissions().contains(permName))) { - throw new SecurityException("Package " + pkg.getPackageName() - + " has not requested permission " + permName); - } - - // 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.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) { - return; - } - - final int flags = uidState.getPermissionFlags(permName); - if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { - Log.e(TAG, "Cannot grant system fixed permission " - + permName + " for package " + packageName); - 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() && !mayGrantSoftRestrictedPermission) { - Log.e(TAG, "Cannot grant soft restricted permission " + permName + " for package " - + packageName); - return; - } - - if (bp.isDevelopment() || bp.isRole()) { - // Development permissions must be handled specially, since they are not - // normal runtime permissions. For now they apply to all users. - // TODO(zhanghai): We are breaking the behavior above by making all permission state - // per-user. It isn't documented behavior and relatively rarely used anyway. - if (!uidState.grantPermission(bp)) { - return; - } - } else { - if (ps.getUserStateOrDefault(userId).isInstantApp() && !bp.isInstant()) { - throw new SecurityException("Cannot grant non-ephemeral permission" + permName - + " for package " + packageName); - } - - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M) { - Slog.w(TAG, "Cannot grant runtime permission to a legacy app"); - return; - } - - if (!uidState.grantPermission(bp)) { - return; - } - } - } - - if (isRuntimePermission) { - logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName); - } - - final int uid = UserHandle.getUid(userId, pkg.getUid()); - if (callback != null) { - if (isRuntimePermission) { - callback.onPermissionGranted(uid, userId); - } else { - callback.onInstallPermissionGranted(); - } - if (permissionHasGids) { - callback.onGidsChanged(UserHandle.getAppId(pkg.getUid()), userId); - } - } - - if (isRuntimePermission) { - notifyRuntimePermissionStateChanged(packageName, userId); - } - } - - @Override - public void revokeRuntimePermission(String packageName, String permName, int userId, - String reason) { - final int callingUid = Binder.getCallingUid(); - final boolean overridePolicy = - checkUidPermission(callingUid, ADJUST_RUNTIME_PERMISSIONS_POLICY) - == PackageManager.PERMISSION_GRANTED; - - revokeRuntimePermissionInternal(packageName, permName, overridePolicy, callingUid, userId, - reason, mDefaultPermissionCallback); - } - - private void revokeRuntimePermissionInternal(String packageName, String permName, - boolean overridePolicy, int callingUid, final int userId, String reason, - PermissionCallback callback) { - if (PermissionManager.DEBUG_TRACE_PERMISSION_UPDATES - && PermissionManager.shouldTraceGrant(packageName, permName, userId)) { - Log.i(TAG, "System is revoking " + packageName + " " - + permName + " for user " + userId + " on behalf of uid " + callingUid - + " " + mPackageManagerInt.getNameForUid(callingUid), - new RuntimeException()); - } - if (!mUserManagerInt.exists(userId)) { - Log.e(TAG, "No such user:" + userId); - return; - } - - mContext.enforceCallingOrSelfPermission( - android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, - "revokeRuntimePermission"); - - enforceCrossUserPermission(callingUid, userId, - true, // requireFullPermission - true, // checkShell - "revokeRuntimePermission"); - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - if (pkg == null) { - Log.e(TAG, "Unknown package: " + packageName); - return; - } - if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { - throw new IllegalArgumentException("Unknown package: " + packageName); - } - - final boolean isRolePermission; - synchronized (mLock) { - final Permission permission = mRegistry.getPermission(permName); - if (permission == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - isRolePermission = permission.isRole(); - } - final boolean mayRevokeRolePermission = isRolePermission - // Allow ourselves to revoke role permissions due to definition changes. - && (callingUid == Process.myUid() || mayManageRolePermission(callingUid)); - - final boolean isRuntimePermission; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permName); - if (bp == null) { - throw new IllegalArgumentException("Unknown permission: " + permName); - } - - isRuntimePermission = bp.isRuntime(); - if (isRuntimePermission || bp.isDevelopment()) { - // Good. - } else if (bp.isRole()) { - if (!mayRevokeRolePermission) { - throw new SecurityException("Permission " + permName + " is managed by role"); - } - } else { - throw new SecurityException("Permission " + permName + " requested by " - + pkg.getPackageName() + " is not a changeable permission type"); - } - - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - return; - } - - if (!(uidState.hasPermissionState(permName) - || pkg.getRequestedPermissions().contains(permName))) { - throw new SecurityException("Package " + pkg.getPackageName() - + " has not requested permission " + permName); - } - - // 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.getTargetSdkVersion() < Build.VERSION_CODES.M && bp.isRuntime()) { - return; - } - - final int flags = uidState.getPermissionFlags(permName); - // Only the system may revoke SYSTEM_FIXED permissions. - if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0 - && UserHandle.getCallingAppId() != Process.SYSTEM_UID) { - 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); - } - - // Development permissions must be handled specially, since they are not - // normal runtime permissions. For now they apply to all users. - // TODO(zhanghai): We are breaking the behavior above by making all permission state - // per-user. It isn't documented behavior and relatively rarely used anyway. - if (!uidState.revokePermission(bp)) { - return; - } - } - - if (isRuntimePermission) { - logPermission(MetricsEvent.ACTION_PERMISSION_REVOKED, permName, packageName); - } - - if (callback != null) { - if (isRuntimePermission) { - callback.onPermissionRevoked(UserHandle.getUid(userId, pkg.getUid()), userId, - reason); - } else { - mDefaultPermissionCallback.onInstallPermissionRevoked(); - } - } - - if (isRuntimePermission) { - notifyRuntimePermissionStateChanged(packageName, userId); - } - } - - private boolean mayManageRolePermission(int uid) { - final PackageManager packageManager = mContext.getPackageManager(); - final String[] packageNames = packageManager.getPackagesForUid(uid); - if (packageNames == null) { - return false; - } - final String permissionControllerPackageName = - packageManager.getPermissionControllerPackageName(); - return Arrays.asList(packageNames).contains(permissionControllerPackageName); - } - - /** - * Reverts user permission state changes (permissions and flags). - * - * @param pkg The package for which to reset. - * @param userId The device user for which to do a reset. - */ - private void resetRuntimePermissionsInternal(@NonNull AndroidPackage pkg, - @UserIdInt int userId) { - final String packageName = pkg.getPackageName(); - - // These are flags that can change base on user actions. - final int userSettableMask = FLAG_PERMISSION_USER_SET - | FLAG_PERMISSION_USER_FIXED - | FLAG_PERMISSION_REVOKED_COMPAT - | FLAG_PERMISSION_REVIEW_REQUIRED - | FLAG_PERMISSION_ONE_TIME - | FLAG_PERMISSION_SELECTED_LOCATION_ACCURACY; - - final int policyOrSystemFlags = FLAG_PERMISSION_SYSTEM_FIXED - | FLAG_PERMISSION_POLICY_FIXED; - - // Delay and combine non-async permission callbacks - final int permissionCount = ArrayUtils.size(pkg.getRequestedPermissions()); - final boolean[] permissionRemoved = new boolean[1]; - final ArraySet revokedPermissions = new ArraySet<>(); - final IntArray syncUpdatedUsers = new IntArray(permissionCount); - final IntArray asyncUpdatedUsers = new IntArray(permissionCount); - - PermissionCallback delayingPermCallback = new PermissionCallback() { - public void onGidsChanged(int appId, int userId) { - mDefaultPermissionCallback.onGidsChanged(appId, userId); - } - - public void onPermissionChanged() { - mDefaultPermissionCallback.onPermissionChanged(); - } - - public void onPermissionGranted(int uid, int userId) { - mDefaultPermissionCallback.onPermissionGranted(uid, userId); - } - - public void onInstallPermissionGranted() { - mDefaultPermissionCallback.onInstallPermissionGranted(); - } - - public void onPermissionRevoked(int uid, int userId, String reason) { - revokedPermissions.add(IntPair.of(uid, userId)); - - syncUpdatedUsers.add(userId); - } - - public void onInstallPermissionRevoked() { - mDefaultPermissionCallback.onInstallPermissionRevoked(); - } - - public void onPermissionUpdated(int[] updatedUserIds, boolean sync) { - for (int userId : updatedUserIds) { - if (sync) { - syncUpdatedUsers.add(userId); - asyncUpdatedUsers.remove(userId); - } else { - // Don't override sync=true by sync=false - if (syncUpdatedUsers.indexOf(userId) == -1) { - asyncUpdatedUsers.add(userId); - } - } - } - } - - public void onPermissionRemoved() { - permissionRemoved[0] = true; - } - - public void onInstallPermissionUpdated() { - mDefaultPermissionCallback.onInstallPermissionUpdated(); - } - - public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, - boolean sync, int uid) { - onPermissionUpdated(updatedUserIds, sync); - mOnPermissionChangeListeners.onPermissionsChanged(uid); - } - - public void onInstallPermissionUpdatedNotifyListener(int uid) { - mDefaultPermissionCallback.onInstallPermissionUpdatedNotifyListener(uid); - } - }; - - for (int i = 0; i < permissionCount; i++) { - final String permName = pkg.getRequestedPermissions().get(i); - - final boolean isRuntimePermission; - synchronized (mLock) { - final Permission permission = mRegistry.getPermission(permName); - if (permission == null) { - continue; - } - - if (permission.isRemoved()) { - continue; - } - isRuntimePermission = permission.isRuntime(); - } - - // If shared user we just reset the state to which only this app contributed. - final String[] pkgNames = mPackageManagerInt.getSharedUserPackagesForPackage( - pkg.getPackageName(), userId); - if (pkgNames.length > 0) { - boolean used = false; - for (String sharedPkgName : pkgNames) { - final AndroidPackage sharedPkg = - mPackageManagerInt.getPackage(sharedPkgName); - if (sharedPkg != null && !sharedPkg.getPackageName().equals(packageName) - && sharedPkg.getRequestedPermissions().contains(permName)) { - used = true; - break; - } - } - if (used) { - continue; - } - } - - final int oldFlags = - getPermissionFlagsInternal(packageName, permName, Process.SYSTEM_UID, userId); - - // Always clear the user settable flags. - // If permission review is enabled and this is a legacy app, mark the - // permission as requiring a review as this is the initial state. - final int uid = mPackageManagerInt.getPackageUid(packageName, 0, userId); - final int targetSdk = mPackageManagerInt.getUidTargetSdkVersion(uid); - final int flags = (targetSdk < Build.VERSION_CODES.M && isRuntimePermission) - ? FLAG_PERMISSION_REVIEW_REQUIRED | FLAG_PERMISSION_REVOKED_COMPAT - : 0; - - updatePermissionFlagsInternal( - packageName, permName, userSettableMask, flags, Process.SYSTEM_UID, userId, - false, delayingPermCallback); - - // Below is only runtime permission handling. - if (!isRuntimePermission) { - continue; - } - - // Never clobber system or policy. - if ((oldFlags & policyOrSystemFlags) != 0) { - continue; - } - - // If this permission was granted by default or role, make sure it is. - if ((oldFlags & FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0 - || (oldFlags & FLAG_PERMISSION_GRANTED_BY_ROLE) != 0) { - // PermissionPolicyService will handle the app op for runtime permissions later. - grantRuntimePermissionInternal(packageName, permName, false, - Process.SYSTEM_UID, userId, delayingPermCallback); - // In certain cases we should leave the state unchanged: - // -- If permission review is enabled the permissions for a legacy apps - // are represented as constantly granted runtime ones - // -- If the permission was split from a non-runtime permission - } else if ((flags & FLAG_PERMISSION_REVIEW_REQUIRED) == 0 - && !isPermissionSplitFromNonRuntime(permName, targetSdk)) { - // Otherwise, reset the permission. - revokeRuntimePermissionInternal(packageName, permName, false, Process.SYSTEM_UID, - userId, null, delayingPermCallback); - } - } - - // Execute delayed callbacks - if (permissionRemoved[0]) { - mDefaultPermissionCallback.onPermissionRemoved(); - } - - // Slight variation on the code in mPermissionCallback.onPermissionRevoked() as we cannot - // kill uid while holding mPackages-lock - if (!revokedPermissions.isEmpty()) { - int numRevokedPermissions = revokedPermissions.size(); - for (int i = 0; i < numRevokedPermissions; i++) { - int revocationUID = IntPair.first(revokedPermissions.valueAt(i)); - int revocationUserId = IntPair.second(revokedPermissions.valueAt(i)); - - mOnPermissionChangeListeners.onPermissionsChanged(revocationUID); - - // Kill app later as we are holding mPackages - mHandler.post(() -> killUid(UserHandle.getAppId(revocationUID), revocationUserId, - KILL_APP_REASON_PERMISSIONS_REVOKED)); - } - } - - mPackageManagerInt.writePermissionSettings(syncUpdatedUsers.toArray(), false); - mPackageManagerInt.writePermissionSettings(asyncUpdatedUsers.toArray(), true); - } - - /** - * Determine if the given permission should be treated as split from a - * non-runtime permission for an application targeting the given SDK level. - */ - private boolean isPermissionSplitFromNonRuntime(String permName, int targetSdk) { - final List splitPerms = getSplitPermissionInfos(); - final int size = splitPerms.size(); - for (int i = 0; i < size; i++) { - final PermissionManager.SplitPermissionInfo splitPerm = splitPerms.get(i); - if (targetSdk < splitPerm.getTargetSdk() - && splitPerm.getNewPermissions().contains(permName)) { - synchronized (mLock) { - final Permission perm = - mRegistry.getPermission(splitPerm.getSplitPermission()); - return perm != null && !perm.isRuntime(); - } - } - } - return false; - } - - /** - * This change makes it so that apps are told to show rationale for asking for background - * location access every time they request. - */ - @ChangeId - @EnabledAfter(targetSdkVersion = Build.VERSION_CODES.Q) - private static final long BACKGROUND_RATIONALE_CHANGE_ID = 147316723L; - - @Override - public boolean shouldShowRequestPermissionRationale(String packageName, String permName, - @UserIdInt int userId) { - final int callingUid = Binder.getCallingUid(); - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "canShowRequestPermissionRationale for user " + userId); - } - - final int uid = - mPackageManagerInt.getPackageUid(packageName, MATCH_DEBUG_TRIAGED_MISSING, userId); - if (UserHandle.getAppId(callingUid) != UserHandle.getAppId(uid)) { - return false; - } - - if (checkPermission(packageName, permName, userId) - == PackageManager.PERMISSION_GRANTED) { - return false; - } - - final int flags; - - final long identity = Binder.clearCallingIdentity(); - try { - flags = getPermissionFlagsInternal(packageName, permName, callingUid, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - - final int fixedFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED - | PackageManager.FLAG_PERMISSION_POLICY_FIXED - | PackageManager.FLAG_PERMISSION_USER_FIXED; - - if ((flags & fixedFlags) != 0) { - return false; - } - - synchronized (mLock) { - final Permission permission = mRegistry.getPermission(permName); - if (permission == null) { - return false; - } - if (permission.isHardRestricted() - && (flags & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { - return false; - } - } - - final long token = Binder.clearCallingIdentity(); - try { - if (permName.equals(Manifest.permission.ACCESS_BACKGROUND_LOCATION) - && mPlatformCompat.isChangeEnabledByPackageName(BACKGROUND_RATIONALE_CHANGE_ID, - packageName, userId)) { - return true; - } - } catch (RemoteException e) { - Log.e(TAG, "Unable to check if compatibility change is enabled.", e); - } finally { - Binder.restoreCallingIdentity(token); - } - - return (flags & PackageManager.FLAG_PERMISSION_USER_SET) != 0; - } - - @Override - public boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId) { - if (UserHandle.getCallingUserId() != userId) { - mContext.enforceCallingPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, - "isPermissionRevokedByPolicy for user " + userId); - } - - if (checkPermission(packageName, permName, userId) == PackageManager.PERMISSION_GRANTED) { - return false; - } - - final int callingUid = Binder.getCallingUid(); - if (mPackageManagerInt.filterAppAccess(packageName, callingUid, userId)) { - return false; - } - - final long identity = Binder.clearCallingIdentity(); - try { - final int flags = getPermissionFlagsInternal(packageName, permName, callingUid, userId); - return (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - - /** - * Get the state of the runtime permissions as xml file. - * - *

Can not be called on main thread. - * - * @param userId The user ID the data should be extracted for - * - * @return The state as a xml file - */ - @Nullable - private byte[] backupRuntimePermissions(@UserIdInt int userId) { - CompletableFuture backup = new CompletableFuture<>(); - mPermissionControllerManager.getRuntimePermissionBackup(UserHandle.of(userId), - 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 " + userId, e); - return null; - } - } - - /** - * Restore a permission state previously backed up via {@link #backupRuntimePermissions}. - * - *

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 userId The user ID the data should be restored for - */ - private void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { - synchronized (mLock) { - mHasNoDelayedPermBackup.delete(userId); - } - mPermissionControllerManager.stageAndApplyRuntimePermissionsBackup(backup, - UserHandle.of(userId)); - } - - /** - * Try to apply permission backup that was previously not applied. - * - *

Can not be called on main thread. - * - * @param packageName The package that is newly installed - * @param userId The user ID the package is installed for - * - * @see #restoreRuntimePermissions - */ - private void restoreDelayedRuntimePermissions(@NonNull String packageName, - @UserIdInt int userId) { - synchronized (mLock) { - if (mHasNoDelayedPermBackup.get(userId, false)) { - return; - } - } - mPermissionControllerManager.applyStagedRuntimePermissionBackup(packageName, - UserHandle.of(userId), mContext.getMainExecutor(), (hasMoreBackup) -> { - if (hasMoreBackup) { - return; - } - synchronized (mLock) { - mHasNoDelayedPermBackup.put(userId, 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 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); - } - } - private void startShellPermissionIdentityDelegationInternal(int uid, @NonNull String packageName, @Nullable List permissionNames) { synchronized (mLock) { @@ -2212,1150 +353,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { mCheckPermissionDelegate = delegate; } - /** - * If the app is updated, and has scoped storage permissions, then it is possible that the - * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. - * @param newPackage The new package that was installed - * @param oldPackage The old package that was updated - */ - private void revokeStoragePermissionsIfScopeExpandedInternal( - @NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage) { - boolean downgradedSdk = oldPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q - && newPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q; - boolean upgradedSdk = oldPackage.getTargetSdkVersion() < Build.VERSION_CODES.Q - && newPackage.getTargetSdkVersion() >= Build.VERSION_CODES.Q; - boolean newlyRequestsLegacy = !upgradedSdk && !oldPackage.isRequestLegacyExternalStorage() - && newPackage.isRequestLegacyExternalStorage(); - - if (!newlyRequestsLegacy && !downgradedSdk) { - return; - } - - final int callingUid = Binder.getCallingUid(); - for (int userId: getAllUserIds()) { - int numRequestedPermissions = newPackage.getRequestedPermissions().size(); - for (int i = 0; i < numRequestedPermissions; i++) { - PermissionInfo permInfo = getPermissionInfo( - newPackage.getRequestedPermissions().get(i), - newPackage.getPackageName(), 0); - if (permInfo == null || !STORAGE_PERMISSIONS.contains(permInfo.name)) { - continue; - } - - EventLog.writeEvent(0x534e4554, "171430330", newPackage.getUid(), - "Revoking permission " + permInfo.name + " from package " - + newPackage.getPackageName() + " as either the sdk downgraded " - + downgradedSdk + " or newly requested legacy full storage " - + newlyRequestsLegacy); - - try { - revokeRuntimePermissionInternal(newPackage.getPackageName(), permInfo.name, - false, callingUid, userId, null, mDefaultPermissionCallback); - } catch (IllegalStateException | SecurityException e) { - Log.e(TAG, "unable to revoke " + permInfo.name + " for " - + newPackage.getPackageName() + " user " + userId, e); - } - } - } - - } - - /** - * 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 - */ - private void revokeRuntimePermissionsIfGroupChangedInternal(@NonNull AndroidPackage newPackage, - @NonNull AndroidPackage oldPackage) { - final int numOldPackagePermissions = ArrayUtils.size(oldPackage.getPermissions()); - final ArrayMap oldPermissionNameToGroupName - = new ArrayMap<>(numOldPackagePermissions); - - for (int i = 0; i < numOldPackagePermissions; i++) { - final ParsedPermission permission = oldPackage.getPermissions().get(i); - - if (permission.getParsedPermissionGroup() != null) { - oldPermissionNameToGroupName.put(permission.getName(), - permission.getParsedPermissionGroup().getName()); - } - } - - final int callingUid = Binder.getCallingUid(); - final int numNewPackagePermissions = ArrayUtils.size(newPackage.getPermissions()); - for (int newPermissionNum = 0; newPermissionNum < numNewPackagePermissions; - newPermissionNum++) { - final ParsedPermission newPermission = - newPackage.getPermissions().get(newPermissionNum); - final int newProtection = ParsedPermissionUtils.getProtection(newPermission); - - if ((newProtection & PermissionInfo.PROTECTION_DANGEROUS) != 0) { - final String permissionName = newPermission.getName(); - final String newPermissionGroupName = - newPermission.getParsedPermissionGroup() == null - ? null : newPermission.getParsedPermissionGroup().getName(); - final String oldPermissionGroupName = oldPermissionNameToGroupName.get( - permissionName); - - if (newPermissionGroupName != null - && !newPermissionGroupName.equals(oldPermissionGroupName)) { - final int[] userIds = mUserManagerInt.getUserIds(); - mPackageManagerInt.forEachPackage(pkg -> { - final String packageName = pkg.getPackageName(); - for (final int userId : userIds) { - final int permissionState = checkPermission(packageName, permissionName, - userId); - if (permissionState == PackageManager.PERMISSION_GRANTED) { - EventLog.writeEvent(0x534e4554, "72710897", - newPackage.getUid(), - "Revoking permission " + permissionName + - " from package " + packageName + - " as the group changed from " + oldPermissionGroupName + - " to " + newPermissionGroupName); - - try { - revokeRuntimePermissionInternal(packageName, permissionName, - false, callingUid, userId, null, - mDefaultPermissionCallback); - } 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 - */ - private void revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( - @NonNull List permissionsToRevoke) { - final int[] userIds = mUserManagerInt.getUserIds(); - final int numPermissions = permissionsToRevoke.size(); - final int callingUid = Binder.getCallingUid(); - - for (int permNum = 0; permNum < numPermissions; permNum++) { - final String permName = permissionsToRevoke.get(permNum); - final boolean isInternalPermission; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permName); - if (bp == null || !(bp.isInternal() || bp.isRuntime())) { - continue; - } - isInternalPermission = bp.isInternal(); - } - mPackageManagerInt.forEachPackage(pkg -> { - final String packageName = pkg.getPackageName(); - final int appId = pkg.getUid(); - if (appId < Process.FIRST_APPLICATION_UID) { - // do not revoke from system apps - return; - } - for (final int userId : userIds) { - final int permissionState = checkPermissionImpl(packageName, permName, - userId); - final int flags = getPermissionFlags(packageName, permName, userId); - final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED - | FLAG_PERMISSION_POLICY_FIXED - | FLAG_PERMISSION_GRANTED_BY_DEFAULT - | FLAG_PERMISSION_GRANTED_BY_ROLE; - if (permissionState == PackageManager.PERMISSION_GRANTED - && (flags & flagMask) == 0) { - final int uid = UserHandle.getUid(userId, appId); - if (isInternalPermission) { - EventLog.writeEvent(0x534e4554, "195338390", uid, - "Revoking permission " + permName + " from package " - + packageName + " due to definition change"); - } else { - 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 { - revokeRuntimePermissionInternal(packageName, permName, - false, callingUid, userId, null, mDefaultPermissionCallback); - } catch (Exception e) { - Slog.e(TAG, "Could not revoke " + permName + " from " - + packageName, e); - } - } - } - }); - } - } - - private List addAllPermissionsInternal(@NonNull AndroidPackage pkg) { - final int N = ArrayUtils.size(pkg.getPermissions()); - ArrayList definitionChangedPermissions = new ArrayList<>(); - for (int i=0; i Build.VERSION_CODES.LOLLIPOP_MR1) { - ComponentMutateUtils.setParsedPermissionGroup(p, - mRegistry.getPermissionGroup(p.getGroup())); - // Warn for a permission in an unknown group. - if (DEBUG_PERMISSIONS - && p.getGroup() != null && p.getParsedPermissionGroup() == null) { - Slog.i(TAG, "Permission " + p.getName() + " from package " - + p.getPackageName() + " in an unknown group " + p.getGroup()); - } - } - - permissionInfo = PackageInfoUtils.generatePermissionInfo(p, - PackageManager.GET_META_DATA); - oldPermission = p.isTree() ? mRegistry.getPermissionTree(p.getName()) - : mRegistry.getPermission(p.getName()); - } - // TODO(zhanghai): Maybe we should store whether a permission is owned by system inside - // itself. - final boolean isOverridingSystemPermission = Permission.isOverridingSystemPermission( - oldPermission, permissionInfo, mPackageManagerInt); - synchronized (mLock) { - final Permission permission = Permission.createOrUpdate(oldPermission, - permissionInfo, pkg, mRegistry.getPermissionTrees(), - isOverridingSystemPermission); - if (p.isTree()) { - mRegistry.addPermissionTree(permission); - } else { - mRegistry.addPermission(permission); - } - if (permission.isInstalled()) { - ComponentMutateUtils.setExactFlags(p, - p.getFlags() | PermissionInfo.FLAG_INSTALLED); - } - if (permission.isDefinitionChanged()) { - definitionChangedPermissions.add(p.getName()); - permission.setDefinitionChanged(false); - } - } - } - return definitionChangedPermissions; - } - - private void addAllPermissionGroupsInternal(@NonNull AndroidPackage pkg) { - synchronized (mLock) { - final int N = ArrayUtils.size(pkg.getPermissionGroups()); - StringBuilder r = null; - for (int i = 0; i < N; i++) { - final ParsedPermissionGroup pg = pkg.getPermissionGroups().get(i); - final ParsedPermissionGroup cur = mRegistry.getPermissionGroup(pg.getName()); - final String curPackageName = (cur == null) ? null : cur.getPackageName(); - final boolean isPackageUpdate = pg.getPackageName().equals(curPackageName); - if (cur == null || isPackageUpdate) { - mRegistry.addPermissionGroup(pg); - if (DEBUG_PACKAGE_SCANNING) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - if (isPackageUpdate) { - r.append("UPD:"); - } - r.append(pg.getName()); - } - } else { - Slog.w(TAG, "Permission group " + pg.getName() + " from package " - + pg.getPackageName() + " ignored: original from " - + cur.getPackageName()); - if (DEBUG_PACKAGE_SCANNING) { - if (r == null) { - r = new StringBuilder(256); - } else { - r.append(' '); - } - r.append("DUP:"); - r.append(pg.getName()); - } - } - } - if (r != null && DEBUG_PACKAGE_SCANNING) { - Log.d(TAG, " Permission Groups: " + r); - } - } - } - - private void removeAllPermissionsInternal(@NonNull AndroidPackage pkg) { - synchronized (mLock) { - int N = ArrayUtils.size(pkg.getPermissions()); - StringBuilder r = null; - for (int i=0; i getGrantedPermissionsInternal(@NonNull String packageName, - @UserIdInt int userId) { - final PackageStateInternal ps = mPackageManagerInt.getPackageStateInternal(packageName); - if (ps == null) { - return Collections.emptySet(); - } - - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(ps, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + packageName + " and user " + userId); - return Collections.emptySet(); - } - if (!ps.getUserStateOrDefault(userId).isInstantApp()) { - return uidState.getGrantedPermissions(); - } else { - // Install permission state is shared among all users, but instant app state is - // per-user, so we can only filter it here unless we make install permission state - // per-user as well. - final Set instantPermissions = - new ArraySet<>(uidState.getGrantedPermissions()); - instantPermissions.removeIf(permissionName -> { - Permission permission = mRegistry.getPermission(permissionName); - if (permission == null) { - return true; - } - if (!permission.isInstant()) { - EventLog.writeEvent(0x534e4554, "140256621", UserHandle.getUid(userId, - ps.getAppId()), permissionName); - return true; - } - return false; - }); - return instantPermissions; - } - } - } - - @NonNull - private int[] getPermissionGidsInternal(@NonNull String permissionName, @UserIdInt int userId) { - synchronized (mLock) { - Permission permission = mRegistry.getPermission(permissionName); - if (permission == null) { - return EmptyArray.INT; - } - return permission.computeGids(userId); - } - } - - /** - * Restore the permission state for a package. - * - *

    - *
  • During boot the state gets restored from the disk
  • - *
  • During app update the state gets restored from the last version of the app
  • - *
- * - * @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 - * @param filterUserId If not {@link UserHandle.USER_ALL}, only restore the permission state for - * this particular user - */ - private void restorePermissionState(@NonNull AndroidPackage pkg, boolean replace, - @Nullable String packageOfInterest, @Nullable PermissionCallback callback, - @UserIdInt int filterUserId) { - // 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 PackageStateInternal ps = - mPackageManagerInt.getPackageStateInternal(pkg.getPackageName()); - if (ps == null) { - return; - } - - final int[] userIds = filterUserId == UserHandle.USER_ALL ? getAllUserIds() - : new int[] { filterUserId }; - - boolean runtimePermissionsRevoked = false; - int[] updatedUserIds = EMPTY_INT_ARRAY; - - ArraySet isPrivilegedPermissionAllowlisted = null; - ArraySet shouldGrantSignaturePermission = null; - ArraySet shouldGrantInternalPermission = null; - ArraySet shouldGrantPrivilegedPermissionIfWasGranted = new ArraySet<>(); - final List requestedPermissions = pkg.getRequestedPermissions(); - final int requestedPermissionsSize = requestedPermissions.size(); - for (int i = 0; i < requestedPermissionsSize; i++) { - final String permissionName = pkg.getRequestedPermissions().get(i); - - final Permission permission; - synchronized (mLock) { - permission = mRegistry.getPermission(permissionName); - } - if (permission == null) { - continue; - } - if (permission.isPrivileged() - && checkPrivilegedPermissionAllowlist(pkg, ps, permission)) { - if (isPrivilegedPermissionAllowlisted == null) { - isPrivilegedPermissionAllowlisted = new ArraySet<>(); - } - isPrivilegedPermissionAllowlisted.add(permissionName); - } - if (permission.isSignature() && (shouldGrantPermissionBySignature(pkg, permission) - || shouldGrantPermissionByProtectionFlags(pkg, ps, permission, - shouldGrantPrivilegedPermissionIfWasGranted))) { - if (shouldGrantSignaturePermission == null) { - shouldGrantSignaturePermission = new ArraySet<>(); - } - shouldGrantSignaturePermission.add(permissionName); - } - if (permission.isInternal() - && shouldGrantPermissionByProtectionFlags(pkg, ps, permission, - shouldGrantPrivilegedPermissionIfWasGranted)) { - if (shouldGrantInternalPermission == null) { - shouldGrantInternalPermission = new ArraySet<>(); - } - shouldGrantInternalPermission.add(permissionName); - } - } - - final SparseBooleanArray isPermissionPolicyInitialized = new SparseBooleanArray(); - if (mPermissionPolicyInternal != null) { - for (final int userId : userIds) { - if (mPermissionPolicyInternal.isInitialized(userId)) { - isPermissionPolicyInitialized.put(userId, true); - } - } - } - - synchronized (mLock) { - for (final int userId : userIds) { - final UserPermissionState userState = mState.getOrCreateUserState(userId); - final UidPermissionState uidState = userState.getOrCreateUidState(ps.getAppId()); - - if (uidState.isMissing()) { - Collection uidRequestedPermissions; - int targetSdkVersion; - if (ps.getSharedUser() == null) { - uidRequestedPermissions = pkg.getRequestedPermissions(); - targetSdkVersion = pkg.getTargetSdkVersion(); - } else { - uidRequestedPermissions = new ArraySet<>(); - targetSdkVersion = Build.VERSION_CODES.CUR_DEVELOPMENT; - List packages = ps.getSharedUser().getPackages(); - int packagesSize = packages.size(); - for (int i = 0; i < packagesSize; i++) { - AndroidPackage sharedUserPackage = packages.get(i); - uidRequestedPermissions.addAll( - sharedUserPackage.getRequestedPermissions()); - targetSdkVersion = Math.min(targetSdkVersion, - sharedUserPackage.getTargetSdkVersion()); - } - } - - for (String permissionName : uidRequestedPermissions) { - Permission permission = mRegistry.getPermission(permissionName); - if (permission == null) { - continue; - } - if (Objects.equals(permission.getPackageName(), PLATFORM_PACKAGE_NAME) - && permission.isRuntime() && !permission.isRemoved()) { - if (permission.isHardOrSoftRestricted() - || permission.isImmutablyRestricted()) { - uidState.updatePermissionFlags(permission, - FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT, - FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT); - } - if (targetSdkVersion < Build.VERSION_CODES.M) { - uidState.updatePermissionFlags(permission, - PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED - | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT, - PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED - | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT); - uidState.grantPermission(permission); - } - } - } - - uidState.setMissing(false); - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - } - - UidPermissionState origState = uidState; - - boolean changedInstallPermission = false; - - if (replace) { - userState.setInstallPermissionsFixed(ps.getPackageName(), false); - if (ps.getSharedUser() == null) { - origState = new UidPermissionState(uidState); - uidState.reset(); - } else { - // We need to know only about runtime permission changes since the - // calling code always writes the install permissions state but - // the runtime ones are written only if changed. The only cases of - // changed runtime permissions here are promotion of an install to - // runtime and revocation of a runtime from a shared user. - if (revokeUnusedSharedUserPermissionsLocked( - ps.getSharedUser().getPackages(), uidState)) { - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - runtimePermissionsRevoked = true; - } - } - } - - ArraySet newImplicitPermissions = new ArraySet<>(); - final String friendlyName = pkg.getPackageName() + "(" + pkg.getUid() + ")"; - - for (int i = 0; i < requestedPermissionsSize; i++) { - final String permName = requestedPermissions.get(i); - - final Permission bp = mRegistry.getPermission(permName); - final boolean appSupportsRuntimePermissions = - pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M; - String legacyActivityRecognitionPermission = null; - - if (DEBUG_INSTALL && bp != null) { - Log.i(TAG, "Package " + friendlyName - + " checking " + permName + ": " + bp); - } - - // TODO(zhanghai): I don't think we need to check source package setting if - // permission is present, because otherwise the permission should have been - // removed. - if (bp == null /*|| getSourcePackageSetting(bp) == null*/) { - if (packageOfInterest == null || packageOfInterest.equals( - pkg.getPackageName())) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Unknown permission " + permName - + " in package " + friendlyName); - } - } - continue; - } - - // Cache newImplicitPermissions before modifing permissionsState as for the - // shared uids the original and new state are the same object - if (!origState.hasPermissionState(permName) - && (pkg.getImplicitPermissions().contains(permName) - || (permName.equals(Manifest.permission.ACTIVITY_RECOGNITION)))) { - if (pkg.getImplicitPermissions().contains(permName)) { - // If permName is an implicit permission, try to auto-grant - newImplicitPermissions.add(permName); - - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " + friendlyName); - } - } 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. - // TODO(zhanghai): This calls into SystemConfig, which generally - // shouldn't cause deadlock, but maybe we should keep a cache of the - // split permission list and just eliminate the possibility. - final List permissionList = - getSplitPermissionInfos(); - int numSplitPerms = permissionList.size(); - for (int splitPermNum = 0; splitPermNum < numSplitPerms; - splitPermNum++) { - PermissionManager.SplitPermissionInfo sp = permissionList.get( - splitPermNum); - String splitPermName = sp.getSplitPermission(); - if (sp.getNewPermissions().contains(permName) - && origState.isPermissionGranted(splitPermName)) { - legacyActivityRecognitionPermission = splitPermName; - newImplicitPermissions.add(permName); - - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, permName + " is newly added for " - + friendlyName); - } - break; - } - } - } - } - - // TODO(b/140256621): The package instant app method has been removed - // as part of work in b/135203078, so this has been commented out in the - // meantime - // Limit ephemeral apps to ephemeral allowed permissions. - // if (/*pkg.isInstantApp()*/ false && !bp.isInstant()) { - // if (DEBUG_PERMISSIONS) { - // Log.i(TAG, "Denying non-ephemeral permission " + bp.getName() - // + " for package " + pkg.getPackageName()); - // } - // continue; - // } - - if (bp.isRuntimeOnly() && !appSupportsRuntimePermissions) { - if (DEBUG_PERMISSIONS) { - Log.i(TAG, "Denying runtime-only permission " + bp.getName() - + " for package " + friendlyName); - } - continue; - } - - final String perm = bp.getName(); - - // Keep track of app op permissions. - if (bp.isAppOp()) { - mRegistry.addAppOpPermissionPackage(perm, pkg.getPackageName()); - } - - boolean shouldGrantNormalPermission = true; - if (bp.isNormal() && !origState.isPermissionGranted(perm)) { - // 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 are dynamic. - if (!ps.isSystem() && userState.areInstallPermissionsFixed( - ps.getPackageName())) { - // Except... if this is a permission that was added - // to the platform (note: need to only do this when - // updating the platform). - if (!isCompatPlatformPermissionForPackage(perm, pkg)) { - shouldGrantNormalPermission = false; - } - } - } - - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Considering granting permission " + perm + " to package " - + pkg.getPackageName()); - } - - if ((bp.isNormal() && shouldGrantNormalPermission) - || (bp.isSignature() - && (!bp.isPrivileged() || CollectionUtils.contains( - isPrivilegedPermissionAllowlisted, permName)) - && (CollectionUtils.contains(shouldGrantSignaturePermission, - permName) - || (((bp.isPrivileged() && CollectionUtils.contains( - shouldGrantPrivilegedPermissionIfWasGranted, - permName)) || bp.isDevelopment() || bp.isRole()) - && origState.isPermissionGranted(permName)))) - || (bp.isInternal() - && (!bp.isPrivileged() || CollectionUtils.contains( - isPrivilegedPermissionAllowlisted, permName)) - && (CollectionUtils.contains(shouldGrantInternalPermission, - permName) - || (((bp.isPrivileged() && CollectionUtils.contains( - shouldGrantPrivilegedPermissionIfWasGranted, - permName)) || bp.isDevelopment() || bp.isRole()) - && origState.isPermissionGranted(permName))))) { - // Grant an install permission. - if (uidState.grantPermission(bp)) { - changedInstallPermission = true; - } - } else if (bp.isRuntime()) { - boolean hardRestricted = bp.isHardRestricted(); - boolean softRestricted = bp.isSoftRestricted(); - - // If permission policy is not ready we don't deal with restricted - // permissions as the policy may allowlist some permissions. Once - // the policy is initialized we would re-evaluate permissions. - final boolean permissionPolicyInitialized = - isPermissionPolicyInitialized.get(userId); - - PermissionState origPermState = origState.getPermissionState(perm); - int flags = origPermState != null ? origPermState.getFlags() : 0; - - boolean wasChanged = false; - - boolean restrictionExempt = - (origState.getPermissionFlags(bp.getName()) - & FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) != 0; - boolean restrictionApplied = (origState.getPermissionFlags( - bp.getName()) & FLAG_PERMISSION_APPLY_RESTRICTION) != 0; - - if (appSupportsRuntimePermissions) { - // If hard restricted we don't allow holding it - if (permissionPolicyInitialized && hardRestricted) { - if (!restrictionExempt) { - if (origPermState != null && origPermState.isGranted() - && uidState.revokePermission(bp)) { - 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_REVOKED_COMPAT) != 0 - && !isPermissionSplitFromNonRuntime(permName, - pkg.getTargetSdkVersion())) { - flags &= ~FLAG_PERMISSION_REVOKED_COMPAT; - wasChanged = true; - // Hard restricted permissions cannot be held. - } else if (!permissionPolicyInitialized - || (!hardRestricted || restrictionExempt)) { - if ((origPermState != null && origPermState.isGranted()) - || legacyActivityRecognitionPermission != null) { - if (!uidState.grantPermission(bp)) { - wasChanged = true; - } - } - } - } else { - if (origPermState == null) { - // New permission - if (PLATFORM_PACKAGE_NAME.equals( - bp.getPackageName())) { - if (!bp.isRemoved()) { - flags |= FLAG_PERMISSION_REVIEW_REQUIRED - | FLAG_PERMISSION_REVOKED_COMPAT; - wasChanged = true; - } - } - } - - if (!uidState.isPermissionGranted(bp.getName()) - && uidState.grantPermission(bp)) { - 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); - } - - uidState.updatePermissionFlags(bp, MASK_PERMISSION_FLAGS_ALL, - flags); - } else { - if (DEBUG_PERMISSIONS) { - boolean wasGranted = uidState.isPermissionGranted(bp.getName()); - if (wasGranted || bp.isAppOp()) { - Slog.i(TAG, (wasGranted ? "Un-granting" : "Not granting") - + " permission " + perm - + " from package " + friendlyName - + " (protectionLevel=" + bp.getProtectionLevel() - + " flags=0x" - + Integer.toHexString(PackageInfoUtils.appInfoFlags(pkg, - ps)) - + ")"); - } - } - if (uidState.removePermissionState(bp.getName())) { - changedInstallPermission = true; - } - } - } - - if ((changedInstallPermission || replace) - && !userState.areInstallPermissionsFixed(ps.getPackageName()) - && !ps.isSystem() || ps.getTransientState().isUpdatedSystemApp()) { - // This is the first that we have heard about this package, so the - // permissions we have now selected are fixed until explicitly - // changed. - userState.setInstallPermissionsFixed(ps.getPackageName(), true); - } - - updatedUserIds = revokePermissionsNoLongerImplicitLocked(uidState, pkg, - userId, updatedUserIds); - updatedUserIds = setInitialGrantForNewImplicitPermissionsLocked(origState, - uidState, pkg, newImplicitPermissions, userId, updatedUserIds); - } - } - - updatedUserIds = checkIfLegacyStorageOpsNeedToBeUpdated(pkg, replace, userIds, - updatedUserIds); - - // TODO: Kill UIDs whose GIDs or runtime permissions changed. This might be more important - // for shared users. - // Persist the runtime permissions state for users with changes. If permissions - // 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.getPackageName(), userId); - } - } - - /** - * Returns all relevant user ids. This list include the current set of created user ids as well - * as pre-created user ids. - * @return user ids for created users and pre-created users - */ - private int[] getAllUserIds() { - return UserManagerService.getInstance().getUserIdsIncludingPreCreated(); - } - - /** - * 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 userIds All user IDs in the system, must be passed in because this method is locked - * @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 - */ - @GuardedBy("mLock") - private @NonNull int[] revokePermissionsNoLongerImplicitLocked(@NonNull UidPermissionState ps, - @NonNull AndroidPackage pkg, int userId, @NonNull int[] updatedUserIds) { - String pkgName = pkg.getPackageName(); - boolean supportsRuntimePermissions = pkg.getTargetSdkVersion() - >= Build.VERSION_CODES.M; - - for (String permission : ps.getGrantedPermissions()) { - if (pkg.getRequestedPermissions().contains(permission) - && !pkg.getImplicitPermissions().contains(permission)) { - Permission bp = mRegistry.getPermission(permission); - if (bp != null && bp.isRuntime()) { - int flags = ps.getPermissionFlags(permission); - if ((flags & FLAG_PERMISSION_REVOKE_WHEN_REQUESTED) != 0) { - int flagsToRemove = FLAG_PERMISSION_REVOKE_WHEN_REQUESTED; - - // We're willing to preserve an implicit "Nearby devices" - // permission grant if this app was already able to interact - // with nearby devices via background location access - boolean preserveGrant = false; - if (ArrayUtils.contains(NEARBY_DEVICES_PERMISSIONS, permission) - && ps.isPermissionGranted( - android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) - && (ps.getPermissionFlags( - android.Manifest.permission.ACCESS_BACKGROUND_LOCATION) - & (FLAG_PERMISSION_REVOKE_WHEN_REQUESTED - | FLAG_PERMISSION_REVOKED_COMPAT)) == 0) { - preserveGrant = true; - } - - if ((flags & BLOCKING_PERMISSION_FLAGS) == 0 - && supportsRuntimePermissions - && !preserveGrant) { - if (ps.revokePermission(bp)) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, "Revoking runtime permission " - + permission + " for " + pkgName - + " as it is now requested"); - } - } - - flagsToRemove |= USER_PERMISSION_FLAGS; - } - - ps.updatePermissionFlags(bp, flagsToRemove, 0); - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - } - } - } - } - - return updatedUserIds; - } - - /** - * {@code newPerm} is newly added; Inherit the state from {@code sourcePerms}. - * - *

A single new permission can be split off from several source permissions. In this case - * the most leniant state is inherited. - * - *

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 - */ - @GuardedBy("mLock") - private void inheritPermissionStateToNewImplicitPermissionLocked( - @NonNull ArraySet sourcePerms, @NonNull String newPerm, - @NonNull UidPermissionState ps, @NonNull AndroidPackage pkg) { - String pkgName = pkg.getPackageName(); - boolean isGranted = false; - int flags = 0; - - int numSourcePerm = sourcePerms.size(); - for (int i = 0; i < numSourcePerm; i++) { - String sourcePerm = sourcePerms.valueAt(i); - if (ps.isPermissionGranted(sourcePerm)) { - if (!isGranted) { - flags = 0; - } - - isGranted = true; - flags |= ps.getPermissionFlags(sourcePerm); - } else { - if (!isGranted) { - flags |= ps.getPermissionFlags(sourcePerm); - } - } - } - - if (isGranted) { - if (DEBUG_PERMISSIONS) { - Slog.i(TAG, newPerm + " inherits runtime perm grant from " + sourcePerms - + " for " + pkgName); - } - - ps.grantPermission(mRegistry.getPermission(newPerm)); - } - - // Add permission flags - ps.updatePermissionFlags(mRegistry.getPermission(newPerm), 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 userIds All user IDs in the system, must be passed in because this method is locked - * @param updatedUserIds The ids of the users that already changed. - * - * @return The ids of the users that are changed - */ - private @NonNull int[] checkIfLegacyStorageOpsNeedToBeUpdated(@NonNull AndroidPackage pkg, - boolean replace, @NonNull int[] userIds, @NonNull int[] updatedUserIds) { - if (replace && pkg.isRequestLegacyExternalStorage() && ( - pkg.getRequestedPermissions().contains(READ_EXTERNAL_STORAGE) - || pkg.getRequestedPermissions().contains(WRITE_EXTERNAL_STORAGE))) { - return userIds.clone(); - } - - 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 userId The user ID - * @param updatedUserIds List of users for which the permission state has already been changed - * - * @return List of users for which the permission state has been changed - */ - @GuardedBy("mLock") - private @NonNull int[] setInitialGrantForNewImplicitPermissionsLocked( - @NonNull UidPermissionState origPs, @NonNull UidPermissionState ps, - @NonNull AndroidPackage pkg, @NonNull ArraySet newImplicitPermissions, - @UserIdInt int userId, @NonNull int[] updatedUserIds) { - String pkgName = pkg.getPackageName(); - ArrayMap> newToSplitPerms = new ArrayMap<>(); - - final List permissionList = - getSplitPermissionInfos(); - int numSplitPerms = permissionList.size(); - for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) { - PermissionManager.SplitPermissionInfo spi = permissionList.get(splitPermNum); - - List newPerms = spi.getNewPermissions(); - int numNewPerms = newPerms.size(); - for (int newPermNum = 0; newPermNum < numNewPerms; newPermNum++) { - String newPerm = newPerms.get(newPermNum); - - ArraySet 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 sourcePerms = newToSplitPerms.get(newPerm); - - if (sourcePerms != null) { - Permission bp = mRegistry.getPermission(newPerm); - if (bp == null) { - throw new IllegalStateException("Unknown new permission in split permission: " - + newPerm); - } - if (bp.isRuntime()) { - - if (!newPerm.equals(Manifest.permission.ACTIVITY_RECOGNITION)) { - ps.updatePermissionFlags(bp, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); - } - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - - if (!origPs.hasPermissionState(sourcePerms)) { - boolean inheritsFromInstallPerm = false; - for (int sourcePermNum = 0; sourcePermNum < sourcePerms.size(); - sourcePermNum++) { - final String sourcePerm = sourcePerms.valueAt(sourcePermNum); - Permission sourceBp = mRegistry.getPermission(sourcePerm); - if (sourceBp == null) { - throw new IllegalStateException("Unknown source permission in split" - + " permission: " + sourcePerm); - } - if (!sourceBp.isRuntime()) { - inheritsFromInstallPerm = true; - break; - } - } - - if (!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"); - } - continue; - } - } - - // Inherit from new install or existing runtime permissions - inheritPermissionStateToNewImplicitPermissionLocked(sourcePerms, newPerm, ps, - pkg); - } - } else if (IMPLICIT_GRANTED_PERMISSIONS.contains(newPerm) - && !origPs.hasPermissionState(newPerm)) { - Permission bp = mRegistry.getPermission(newPerm); - if (bp == null) { - throw new IllegalStateException("Unknown new permission " + newPerm); - } - if ((ps.getPermissionState(newPerm).getFlags() - & FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { - // No need to grant if review is required - continue; - } - updatedUserIds = ArrayUtils.appendInt(updatedUserIds, userId); - ps.updatePermissionFlags(bp, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED, - FLAG_PERMISSION_REVOKE_WHEN_REQUESTED); - ps.grantPermission(bp); - } - } - - return updatedUserIds; - } - - @NonNull - @Override - public List getSplitPermissions() { - return PermissionManager.splitPermissionInfoListToParcelableList(getSplitPermissionInfos()); - } - - @NonNull - private List getSplitPermissionInfos() { - return SystemConfig.getInstance().getSplitPermissions(); - } - @NonNull private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) { OneTimePermissionUserManager oneTimePermissionUserManager; @@ -3452,1675 +449,125 @@ public class PermissionManagerService extends IPermissionManager.Stub { return result; } - private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) { - boolean allowed = false; - for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) { - final CompatibilityPermissionInfo info = CompatibilityPermissionInfo.COMPAT_PERMS[i]; - if (info.getName().equals(perm) - && pkg.getTargetSdkVersion() < info.getSdkVersion()) { - allowed = true; - Log.i(TAG, "Auto-granting " + perm + " to old pkg " - + pkg.getPackageName()); - break; - } - } - return allowed; + /* Start of delegate methods to PermissionManagerServiceInterface */ + + @Override + public ParceledListSlice getAllPermissionGroups(int flags) { + return new ParceledListSlice<>(mPermissionManagerServiceImpl.getAllPermissionGroups(flags)); } - private boolean checkPrivilegedPermissionAllowlist(@NonNull AndroidPackage pkg, - @NonNull PackageStateInternal packageSetting, @NonNull Permission permission) { - if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_DISABLE) { - return true; - } - final String packageName = pkg.getPackageName(); - if (Objects.equals(packageName, PLATFORM_PACKAGE_NAME)) { - return true; - } - if (!pkg.isPrivileged()) { - return true; - } - if (!mPrivilegedPermissionAllowlistSourcePackageNames - .contains(permission.getPackageName())) { - return true; - } - final String permissionName = permission.getName(); - final ApexManager apexManager = ApexManager.getInstance(); - final String containingApexPackageName = - apexManager.getActiveApexPackageNameContainingPackage(packageName); - if (isInSystemConfigPrivAppPermissions(pkg, permissionName, - containingApexPackageName)) { - return true; - } - if (isInSystemConfigPrivAppDenyPermissions(pkg, permissionName, - containingApexPackageName)) { - return false; - } - // Updated system apps do not need to be allowlisted - if (packageSetting.getTransientState().isUpdatedSystemApp()) { - // Let shouldGrantPermissionByProtectionFlags() decide whether the privileged permission - // can be granted, because an updated system app may be in a shared UID, and in case a - // new privileged permission is requested by the updated system app but not the factory - // app, although this app and permission combination isn't in the allowlist and can't - // get the permission this way, other apps in the shared UID may still get it. A proper - // fix for this would be to perform the reconciliation by UID, but for now let's keep - // the old workaround working, which is to keep granted privileged permissions still - // granted. - return true; - } - // Only enforce the allowlist on boot - if (!mSystemReady) { - final boolean isInUpdatedApex = containingApexPackageName != null - && !apexManager.isFactory(apexManager.getPackageInfo(containingApexPackageName, - MATCH_ACTIVE_PACKAGE)); - // Apps that are in updated apexs' do not need to be allowlisted - if (!isInUpdatedApex) { - Slog.w(TAG, "Privileged permission " + permissionName + " for package " - + packageName + " (" + pkg.getPath() - + ") not in privapp-permissions allowlist"); - if (RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE) { - synchronized (mLock) { - if (mPrivappPermissionsViolations == null) { - mPrivappPermissionsViolations = new ArraySet<>(); - } - mPrivappPermissionsViolations.add(packageName + " (" + pkg.getPath() + "): " - + permissionName); - } - } - } - } - return !RoSystemProperties.CONTROL_PRIVAPP_PERMISSIONS_ENFORCE; + @Override + public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags) { + return mPermissionManagerServiceImpl.getPermissionGroupInfo(groupName, flags); } - private boolean isInSystemConfigPrivAppPermissions(@NonNull AndroidPackage pkg, - @NonNull String permission, String containingApexPackageName) { - final SystemConfig systemConfig = SystemConfig.getInstance(); - final Set permissions; - if (pkg.isVendor()) { - permissions = systemConfig.getVendorPrivAppPermissions(pkg.getPackageName()); - } else if (pkg.isProduct()) { - permissions = systemConfig.getProductPrivAppPermissions(pkg.getPackageName()); - } else if (pkg.isSystemExt()) { - permissions = systemConfig.getSystemExtPrivAppPermissions(pkg.getPackageName()); - } else if (containingApexPackageName != null) { - final Set privAppPermissions = systemConfig.getPrivAppPermissions( - pkg.getPackageName()); - final Set apexPermissions = systemConfig.getApexPrivAppPermissions( - containingApexPackageName, pkg.getPackageName()); - if (privAppPermissions != null) { - // TODO(andreionea): Remove check as soon as all apk-in-apex - // permission allowlists are migrated. - Slog.w(TAG, "Package " + pkg.getPackageName() + " is an APK in APEX," - + " but has permission allowlist on the system image. Please bundle the" - + " allowlist in the " + containingApexPackageName + " APEX instead."); - if (apexPermissions != null) { - permissions = new ArraySet<>(privAppPermissions); - permissions.addAll(apexPermissions); - } else { - permissions = privAppPermissions; - } - } else { - permissions = apexPermissions; - } - } else { - permissions = systemConfig.getPrivAppPermissions(pkg.getPackageName()); - } - return CollectionUtils.contains(permissions, permission); + @Override + public PermissionInfo getPermissionInfo(String permissionName, String packageName, int flags) { + return mPermissionManagerServiceImpl.getPermissionInfo(permissionName, packageName, flags); } - private boolean isInSystemConfigPrivAppDenyPermissions(@NonNull AndroidPackage pkg, - @NonNull String permission, String containingApexPackageName) { - final SystemConfig systemConfig = SystemConfig.getInstance(); - final Set permissions; - if (pkg.isVendor()) { - permissions = systemConfig.getVendorPrivAppDenyPermissions(pkg.getPackageName()); - } else if (pkg.isProduct()) { - permissions = systemConfig.getProductPrivAppDenyPermissions(pkg.getPackageName()); - } else if (pkg.isSystemExt()) { - permissions = systemConfig.getSystemExtPrivAppDenyPermissions(pkg.getPackageName()); - } else if (containingApexPackageName != null) { - permissions = systemConfig.getApexPrivAppDenyPermissions(containingApexPackageName, - pkg.getPackageName()); - } else { - permissions = systemConfig.getPrivAppDenyPermissions(pkg.getPackageName()); - } - return CollectionUtils.contains(permissions, permission); - } - - private boolean shouldGrantPermissionBySignature(@NonNull AndroidPackage pkg, - @NonNull Permission bp) { - // expect single system package - String systemPackageName = ArrayUtils.firstOrNull(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_SYSTEM, UserHandle.USER_SYSTEM)); - final AndroidPackage 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 a common signing certificate in its lineage with the defining package, - // and the defining package still trusts the old certificate for permissions - // - or it shares the above relationships with the system package - final SigningDetails sourceSigningDetails = - getSourcePackageSigningDetails(bp); - return sourceSigningDetails.hasCommonSignerWithCapability( - pkg.getSigningDetails(), - SigningDetails.CertCapabilities.PERMISSION) - || pkg.getSigningDetails().hasAncestorOrSelf(systemPackage.getSigningDetails()) - || systemPackage.getSigningDetails().checkCapability( - pkg.getSigningDetails(), - SigningDetails.CertCapabilities.PERMISSION); - } - - private boolean shouldGrantPermissionByProtectionFlags(@NonNull AndroidPackage pkg, - @NonNull PackageStateInternal pkgSetting, @NonNull Permission bp, - @NonNull ArraySet shouldGrantPrivilegedPermissionIfWasGranted) { - boolean allowed = false; - final boolean isPrivilegedPermission = bp.isPrivileged(); - final boolean isOemPermission = bp.isOem(); - if (!allowed && (isPrivilegedPermission || isOemPermission) && pkg.isSystem()) { - final String permissionName = bp.getName(); - // For updated system applications, a privileged/oem permission - // is granted only if it had been defined by the original application. - if (pkgSetting.getTransientState().isUpdatedSystemApp()) { - final PackageSetting disabledPs = mPackageManagerInt - .getDisabledSystemPackage(pkg.getPackageName()); - final AndroidPackage disabledPkg = disabledPs == null ? null : disabledPs.getPkg(); - if (disabledPkg != null - && ((isPrivilegedPermission && disabledPkg.isPrivileged()) - || (isOemPermission && canGrantOemPermission(disabledPkg, - permissionName)))) { - if (disabledPkg.getRequestedPermissions().contains(permissionName)) { - allowed = true; - } else { - // If the original was granted this permission, we take - // that grant decision as read and propagate it to the - // update. - shouldGrantPrivilegedPermissionIfWasGranted.add(permissionName); - } - } - } else { - allowed = (isPrivilegedPermission && pkg.isPrivileged()) - || (isOemPermission && canGrantOemPermission(pkg, permissionName)); - } - // 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 && isPrivilegedPermission && !bp.isVendorPrivileged() && pkg.isVendor()) { - Slog.w(TAG, "Permission " + permissionName - + " cannot be granted to privileged vendor apk " + pkg.getPackageName() - + " because it isn't a 'vendorPrivileged' permission."); - allowed = false; - } - } - if (!allowed && bp.isPre23() && pkg.getTargetSdkVersion() < 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() - && (ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_INSTALLER, UserHandle.USER_SYSTEM), - pkg.getPackageName()) || ArrayUtils.contains( - mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_PERMISSION_CONTROLLER, - UserHandle.USER_SYSTEM), pkg.getPackageName()))) { - // 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() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_VERIFIER, UserHandle.USER_SYSTEM), - pkg.getPackageName())) { - // 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.isKnownSigner()) { - // If the permission is to be granted to a known signer then check if any of this - // app's signing certificates are in the trusted certificate digest Set. - allowed = pkg.getSigningDetails().hasAncestorOrSelfWithDigest(bp.getKnownCerts()); - } - // Deferred to be checked under permission data lock inside restorePermissionState(). - //if (!allowed && bp.isDevelopment()) { - // // For development permissions, a development permission - // // is granted only if it was already granted. - // allowed = origPermissions.isPermissionGranted(permissionName); - //} - if (!allowed && bp.isSetup() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_SETUP_WIZARD, UserHandle.USER_SYSTEM), - pkg.getPackageName())) { - // 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() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_SYSTEM_TEXT_CLASSIFIER, - UserHandle.USER_SYSTEM), pkg.getPackageName())) { - // Special permissions for the system default text classifier. - allowed = true; - } - if (!allowed && bp.isConfigurator() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_CONFIGURATOR, - UserHandle.USER_SYSTEM), pkg.getPackageName())) { - // Special permissions for the device configurator. - allowed = true; - } - if (!allowed && bp.isIncidentReportApprover() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_INCIDENT_REPORT_APPROVER, - UserHandle.USER_SYSTEM), pkg.getPackageName())) { - // 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() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_APP_PREDICTOR, UserHandle.USER_SYSTEM), - pkg.getPackageName())) { - // Special permissions for the system app predictor. - allowed = true; - } - if (!allowed && bp.isCompanion() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_COMPANION, UserHandle.USER_SYSTEM), - pkg.getPackageName())) { - // Special permissions for the system companion device manager. - allowed = true; - } - if (!allowed && bp.isRetailDemo() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_RETAIL_DEMO, UserHandle.USER_SYSTEM), - pkg.getPackageName()) && isProfileOwner(pkg.getUid())) { - // Special permission granted only to the OEM specified retail demo app - allowed = true; - } - if (!allowed && bp.isRecents() - && ArrayUtils.contains(mPackageManagerInt.getKnownPackageNames( - PackageManagerInternal.PACKAGE_RECENTS, UserHandle.USER_SYSTEM), - pkg.getPackageName())) { - // Special permission for the recents app. - allowed = true; - } - return allowed; - } - - @NonNull - private SigningDetails getSourcePackageSigningDetails( - @NonNull Permission bp) { - final PackageStateInternal ps = getSourcePackageSetting(bp); - if (ps == null) { - return SigningDetails.UNKNOWN; - } - return ps.getSigningDetails(); - } - - @Nullable - private PackageStateInternal getSourcePackageSetting(@NonNull Permission bp) { - final String sourcePackageName = bp.getPackageName(); - return mPackageManagerInt.getPackageStateInternal(sourcePackageName); - } - - private static boolean canGrantOemPermission(AndroidPackage pkg, String permission) { - if (!pkg.isOem()) { - return false; - } - // all oem permissions must explicitly be granted or denied - final Boolean granted = - SystemConfig.getInstance().getOemPermissions(pkg.getPackageName()).get(permission); - if (granted == null) { - throw new IllegalStateException("OEM permission" + permission + " requested by package " - + pkg.getPackageName() + " must be explicitly declared granted or not"); - } - return Boolean.TRUE == granted; - } - - private static boolean isProfileOwner(int uid) { - DevicePolicyManagerInternal dpmInternal = - LocalServices.getService(DevicePolicyManagerInternal.class); - //TODO(b/169395065) Figure out if this flow makes sense in Device Owner mode. - if (dpmInternal != null) { - return dpmInternal.isActiveProfileOwner(uid) || dpmInternal.isActiveDeviceOwner(uid); - } - return false; - } - - private boolean isPermissionsReviewRequiredInternal(@NonNull String packageName, - @UserIdInt int userId) { - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - if (pkg == null) { - return false; - } - - // Permission review applies only to apps not supporting the new permission model. - if (pkg.getTargetSdkVersion() >= Build.VERSION_CODES.M) { - return false; - } - - // Legacy apps have the permission and get user consent on launch. - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() + " and user " - + userId); - return false; - } - return uidState.isPermissionsReviewRequired(); - } - } - - private void grantRequestedRuntimePermissionsInternal(@NonNull AndroidPackage pkg, - @Nullable List permissions, int userId) { - final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED - | PackageManager.FLAG_PERMISSION_POLICY_FIXED; - - final int compatFlags = PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED - | PackageManager.FLAG_PERMISSION_REVOKED_COMPAT; - - final boolean supportsRuntimePermissions = pkg.getTargetSdkVersion() - >= Build.VERSION_CODES.M; - - final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.getPackageName(), userId); - - final int myUid = Process.myUid(); - - for (String permission : pkg.getRequestedPermissions()) { - final boolean shouldGrantPermission; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permission); - shouldGrantPermission = bp != null && (bp.isRuntime() || bp.isDevelopment()) - && (!instantApp || bp.isInstant()) - && (supportsRuntimePermissions || !bp.isRuntimeOnly()) - && (permissions == null || permissions.contains(permission)); - } - if (shouldGrantPermission) { - final int flags = getPermissionFlagsInternal(pkg.getPackageName(), permission, - myUid, userId); - if (supportsRuntimePermissions) { - // Installer cannot change immutable permissions. - if ((flags & immutableFlags) == 0) { - grantRuntimePermissionInternal(pkg.getPackageName(), permission, false, - myUid, userId, mDefaultPermissionCallback); - } - } else { - // In permission review mode we clear the review flag and the revoked compat - // flag when we are asked to install the app with all permissions granted. - if ((flags & compatFlags) != 0) { - updatePermissionFlagsInternal(pkg.getPackageName(), permission, compatFlags, - 0, myUid, userId, false, mDefaultPermissionCallback); - } - } - } - } - } - - private void setAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, - @Nullable List permissions, @PermissionWhitelistFlags int allowlistFlags, - @UserIdInt int userId) { - ArraySet oldGrantedRestrictedPermissions = null; - boolean updatePermissions = false; - final int permissionCount = pkg.getRequestedPermissions().size(); - final int myUid = Process.myUid(); - - for (int j = 0; j < permissionCount; j++) { - final String permissionName = pkg.getRequestedPermissions().get(j); - - final boolean isGranted; - synchronized (mLock) { - final Permission bp = mRegistry.getPermission(permissionName); - if (bp == null || !bp.isHardOrSoftRestricted()) { - continue; - } - - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - isGranted = uidState.isPermissionGranted(permissionName); - } - - if (isGranted) { - if (oldGrantedRestrictedPermissions == null) { - oldGrantedRestrictedPermissions = new ArraySet<>(); - } - oldGrantedRestrictedPermissions.add(permissionName); - } - - final int oldFlags = getPermissionFlagsInternal(pkg.getPackageName(), permissionName, - myUid, userId); - - int newFlags = oldFlags; - int mask = 0; - int allowlistFlagsCopy = allowlistFlags; - while (allowlistFlagsCopy != 0) { - final int flag = 1 << Integer.numberOfTrailingZeros(allowlistFlagsCopy); - allowlistFlagsCopy &= ~flag; - switch (flag) { - case FLAG_PERMISSION_WHITELIST_SYSTEM: { - mask |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_SYSTEM_EXEMPT; - } - } - break; - case FLAG_PERMISSION_WHITELIST_UPGRADE: { - mask |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_UPGRADE_EXEMPT; - } - } - break; - case FLAG_PERMISSION_WHITELIST_INSTALLER: { - mask |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - if (permissions != null && permissions.contains(permissionName)) { - newFlags |= FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } else { - newFlags &= ~FLAG_PERMISSION_RESTRICTION_INSTALLER_EXEMPT; - } - } - break; - } - } - - if (oldFlags == newFlags) { - continue; - } - - updatePermissions = true; - - final boolean wasAllowlisted = (oldFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; - final boolean isAllowlisted = (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 allowlists we need to clear the policy fixed flag - // as allowlisting trumps policy i.e. policy cannot grant a non - // grantable permission. - if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - if (!isAllowlisted && isGranted) { - mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; - newFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED; - } - } - - // If we are allowlisting an app that does not support runtime permissions - // we need to make sure it goes through the permission review UI at launch. - if (pkg.getTargetSdkVersion() < Build.VERSION_CODES.M - && !wasAllowlisted && isAllowlisted) { - mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; - } - - updatePermissionFlagsInternal(pkg.getPackageName(), permissionName, mask, newFlags, - myUid, userId, false, null /*callback*/); - } - - if (updatePermissions) { - // Update permission of this app to take into account the new allowlist state. - restorePermissionState(pkg, false, pkg.getPackageName(), mDefaultPermissionCallback, - userId); - - // If this resulted in losing a permission we need to kill the app. - if (oldGrantedRestrictedPermissions == null) { - return; - } - - final int oldGrantedCount = oldGrantedRestrictedPermissions.size(); - for (int j = 0; j < oldGrantedCount; j++) { - final String permissionName = oldGrantedRestrictedPermissions.valueAt(j); - // Sometimes we create a new permission state instance during update. - final boolean isGranted; - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(pkg, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - isGranted = uidState.isPermissionGranted(permissionName); - } - if (!isGranted) { - mDefaultPermissionCallback.onPermissionRevoked(pkg.getUid(), userId, null); - break; - } - } - } - } - - private void revokeSharedUserPermissionsForLeavingPackageInternal( - @Nullable AndroidPackage pkg, int appId, @NonNull List sharedUserPkgs, - @UserIdInt int userId) { - if (pkg == null) { - Slog.i(TAG, "Trying to update info for null package. Just ignoring"); - return; - } - - // No shared user packages - if (sharedUserPkgs.isEmpty()) { - return; - } - - PackageSetting disabledPs = mPackageManagerInt.getDisabledSystemPackage( - pkg.getPackageName()); - boolean isShadowingSystemPkg = disabledPs != null && disabledPs.getAppId() == pkg.getUid(); - - boolean shouldKillUid = false; - // Update permissions - for (String eachPerm : pkg.getRequestedPermissions()) { - // Check if another package in the shared user needs the permission. - boolean used = false; - for (AndroidPackage sharedUserpkg : sharedUserPkgs) { - if (sharedUserpkg != null - && !sharedUserpkg.getPackageName().equals(pkg.getPackageName()) - && sharedUserpkg.getRequestedPermissions().contains(eachPerm)) { - used = true; - break; - } - } - if (used) { - continue; - } - - // If the package is shadowing a disabled system package, - // do not drop permissions that the shadowed package requests. - if (isShadowingSystemPkg - && disabledPs.getPkg().getRequestedPermissions().contains(eachPerm)) { - continue; - } - - synchronized (mLock) { - UidPermissionState uidState = getUidStateLocked(appId, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " + pkg.getPackageName() - + " and user " + userId); - continue; - } - - Permission bp = mRegistry.getPermission(eachPerm); - if (bp == null) { - continue; - } - - // TODO(zhanghai): Why are we only killing the UID when GIDs changed, instead of any - // permission change? - if (uidState.removePermissionState(bp.getName()) && bp.hasGids()) { - shouldKillUid = true; - } - } - } - - // If gids changed, kill all affected packages. - if (shouldKillUid) { - mHandler.post(() -> { - // This has to happen with no lock held. - killUid(appId, UserHandle.USER_ALL, KILL_APP_REASON_GIDS_CHANGED); - }); - } - } - - @GuardedBy("mLock") - private boolean revokeUnusedSharedUserPermissionsLocked( - List pkgList, UidPermissionState uidState) { - // Collect all used permissions in the UID - final ArraySet usedPermissions = new ArraySet<>(); - if (pkgList == null || pkgList.size() == 0) { - return false; - } - for (AndroidPackage pkg : pkgList) { - if (pkg.getRequestedPermissions().isEmpty()) { - continue; - } - final int requestedPermCount = pkg.getRequestedPermissions().size(); - for (int j = 0; j < requestedPermCount; j++) { - String permission = pkg.getRequestedPermissions().get(j); - Permission bp = mRegistry.getPermission(permission); - if (bp != null) { - usedPermissions.add(permission); - } - } - } - - boolean runtimePermissionChanged = false; - - // Prune permissions - final List permissionStates = uidState.getPermissionStates(); - final int permissionStatesSize = permissionStates.size(); - for (int i = permissionStatesSize - 1; i >= 0; i--) { - PermissionState permissionState = permissionStates.get(i); - if (!usedPermissions.contains(permissionState.getName())) { - Permission bp = mRegistry.getPermission(permissionState.getName()); - if (bp != null) { - if (uidState.removePermissionState(bp.getName()) && bp.isRuntime()) { - runtimePermissionChanged = true; - } - } - } - } - - return runtimePermissionChanged; - } - - /** - * Update permissions when a package changed. - * - *

    - *
  1. Reconsider the ownership of permission
  2. - *
  3. Update the state (grant, flags) of the permissions
  4. - *
- * - * @param packageName The package that is updated - * @param pkg The package that is updated, or {@code null} if package is deleted - */ - private void updatePermissions(@NonNull String packageName, @Nullable AndroidPackage pkg) { - // If the package is being deleted, update the permissions of all the apps - final int flags = - (pkg == null ? UPDATE_PERMISSIONS_ALL | UPDATE_PERMISSIONS_REPLACE_PKG - : UPDATE_PERMISSIONS_REPLACE_PKG); - updatePermissions( - packageName, pkg, getVolumeUuidForPackage(pkg), flags, mDefaultPermissionCallback); - } - - /** - * Update all permissions for all apps. - * - *

    - *
  1. Reconsider the ownership of permission
  2. - *
  3. Update the state (grant, flags) of the permissions
  4. - *
- * - * @param volumeUuid The volume UUID of the packages to be updated - * @param fingerprintChanged whether the current build fingerprint is different from what it was - * when this volume was last mounted - */ - private void updateAllPermissions(@NonNull String volumeUuid, boolean fingerprintChanged) { - PackageManager.corkPackageInfoCache(); // Prevent invalidation storm - try { - final int flags = UPDATE_PERMISSIONS_ALL | - (fingerprintChanged - ? UPDATE_PERMISSIONS_REPLACE_PKG | UPDATE_PERMISSIONS_REPLACE_ALL - : 0); - updatePermissions(null, null, volumeUuid, flags, mDefaultPermissionCallback); - } finally { - PackageManager.uncorkPackageInfoCache(); - } - } - - /** - * Update all packages on the volume, beside the changing package. If the changing - * package is set too, all packages are updated. - */ - private static final int UPDATE_PERMISSIONS_ALL = 1 << 0; - /** The changing package is replaced. Requires the changing package to be set */ - private static final int UPDATE_PERMISSIONS_REPLACE_PKG = 1 << 1; - /** - * Schedule all packages beside the changing package for replacement. Requires - * UPDATE_PERMISSIONS_ALL to be set - */ - private static final int UPDATE_PERMISSIONS_REPLACE_ALL = 1 << 2; - - @IntDef(flag = true, prefix = { "UPDATE_PERMISSIONS_" }, value = { - UPDATE_PERMISSIONS_ALL, UPDATE_PERMISSIONS_REPLACE_PKG, - UPDATE_PERMISSIONS_REPLACE_ALL }) - @Retention(RetentionPolicy.SOURCE) - private @interface UpdatePermissionFlags {} - - /** - * Update permissions when packages changed. - * - *

    - *
  1. Reconsider the ownership of permission
  2. - *
  3. Update the state (grant, flags) of the permissions
  4. - *
- * - *

Meaning of combination of package parameters: - * - * - * - * - *
changingPkgName != nullchangingPkgName == null
changingPkg != nullpackage is updatedinvalid
changingPkg == nullpackage is deletedall packages are - * updated
- * - * @param changingPkgName The package that is updated, or {@code null} if all packages should be - * updated - * @param changingPkg The package that is updated, or {@code null} if all packages should be - * updated or package is deleted - * @param replaceVolumeUuid The volume of the packages to be updated are on, {@code null} for - * all volumes - * @param flags Control permission for which apps should be updated - * @param callback Callback to call after permission changes - */ - private void updatePermissions(final @Nullable String changingPkgName, - final @Nullable AndroidPackage changingPkg, - final @Nullable String replaceVolumeUuid, - @UpdatePermissionFlags int flags, - final @Nullable 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. - boolean permissionTreesSourcePackageChanged = updatePermissionTreeSourcePackage( - changingPkgName, changingPkg); - // Make sure all dynamic permissions have been assigned to a package, - // and make sure there are no dangling permissions. - boolean permissionSourcePackageChanged = updatePermissionSourcePackage(changingPkgName, - callback); - - if (permissionTreesSourcePackageChanged | permissionSourcePackageChanged) { - // Permission ownership has changed. This e.g. changes which packages can get signature - // permissions - Slog.i(TAG, "Permission ownership changed. Updating all permissions."); - flags |= UPDATE_PERMISSIONS_ALL; - } - - Trace.traceBegin(TRACE_TAG_PACKAGE_MANAGER, "restorePermissionState"); - // Now update the permissions for all packages. - if ((flags & UPDATE_PERMISSIONS_ALL) != 0) { - final boolean replaceAll = ((flags & UPDATE_PERMISSIONS_REPLACE_ALL) != 0); - mPackageManagerInt.forEachPackage((AndroidPackage pkg) -> { - if (pkg == changingPkg) { - return; - } - // Only replace for packages on requested volume - final String volumeUuid = getVolumeUuidForPackage(pkg); - final boolean replace = replaceAll && Objects.equals(replaceVolumeUuid, volumeUuid); - restorePermissionState(pkg, replace, changingPkgName, callback, - UserHandle.USER_ALL); - }); - } - - 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, - UserHandle.USER_ALL); - } - Trace.traceEnd(TRACE_TAG_PACKAGE_MANAGER); - } - - /** - * Update which app declares a permission. - * - * @param packageName The package that is updated, or {@code null} if all packages should be - * updated - * - * @return {@code true} if a permission source package might have changed - */ - private boolean updatePermissionSourcePackage(@Nullable String packageName, - final @Nullable PermissionCallback callback) { - // Always need update if packageName is null - if (packageName == null) { - return true; - } - - boolean changed = false; - Set needsUpdate = null; - synchronized (mLock) { - for (final Permission bp : mRegistry.getPermissions()) { - if (bp.isDynamic()) { - bp.updateDynamicPermission(mRegistry.getPermissionTrees()); - } - if (!packageName.equals(bp.getPackageName())) { - // Not checking sourcePackageSetting because it can be null when - // the permission source package is the target package and the target package is - // being uninstalled, - continue; - } - // The target package is the source of the current permission - // Set to changed for either install or uninstall - changed = true; - if (needsUpdate == null) { - needsUpdate = new ArraySet<>(); - } - needsUpdate.add(bp); - } - } - if (needsUpdate != null) { - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - for (final Permission bp : needsUpdate) { - // If the target package is being uninstalled, we need to revoke this permission - // From all other packages - if (pkg == null || !hasPermission(pkg, bp.getName())) { - if (!isPermissionDeclaredByDisabledSystemPkg(bp)) { - Slog.i(TAG, "Removing permission " + bp.getName() - + " that used to be declared by " + bp.getPackageName()); - 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((AndroidPackage p) -> - revokePermissionFromPackageForUser(p.getPackageName(), - bp.getName(), true, userId, callback)); - } - } else { - mPackageManagerInt.forEachPackage(p -> { - final int[] userIds = mUserManagerInt.getUserIds(); - synchronized (mLock) { - for (final int userId : userIds) { - final UidPermissionState uidState = getUidStateLocked(p, - userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for " - + p.getPackageName() + " and user " + userId); - continue; - } - uidState.removePermissionState(bp.getName()); - } - } - }); - } - } - synchronized (mLock) { - mRegistry.removePermission(bp.getName()); - } - continue; - } - final AndroidPackage sourcePkg = - mPackageManagerInt.getPackage(bp.getPackageName()); - final PackageStateInternal sourcePs = - mPackageManagerInt.getPackageStateInternal(bp.getPackageName()); - synchronized (mLock) { - if (sourcePkg != null && sourcePs != null) { - continue; - } - Slog.w(TAG, "Removing dangling permission: " + bp.getName() - + " from package " + bp.getPackageName()); - mRegistry.removePermission(bp.getName()); - } - } - } - return changed; - } - - private boolean isPermissionDeclaredByDisabledSystemPkg(@NonNull Permission permission) { - final PackageSetting disabledSourcePs = mPackageManagerInt.getDisabledSystemPackage( - permission.getPackageName()); - if (disabledSourcePs != null && disabledSourcePs.getPkg() != null) { - final String permissionName = permission.getName(); - final List sourcePerms = disabledSourcePs.getPkg().getPermissions(); - for (ParsedPermission sourcePerm : sourcePerms) { - if (TextUtils.equals(permissionName, sourcePerm.getName()) - && permission.getProtectionLevel() == sourcePerm.getProtectionLevel()) { - return true; - } - } - } - return false; - } - - /** - * Revoke a runtime permission from a package for a given user ID. - */ - private void revokePermissionFromPackageForUser(@NonNull String pName, - @NonNull String permissionName, boolean overridePolicy, int userId, - @Nullable PermissionCallback callback) { - final ApplicationInfo appInfo = - mPackageManagerInt.getApplicationInfo(pName, 0, - Process.SYSTEM_UID, UserHandle.USER_SYSTEM); - if (appInfo != null - && appInfo.targetSdkVersion < Build.VERSION_CODES.M) { - return; - } - - if (checkPermissionImpl(pName, permissionName, userId) - == PackageManager.PERMISSION_GRANTED) { - try { - revokeRuntimePermissionInternal( - pName, permissionName, - overridePolicy, - Process.SYSTEM_UID, - userId, - null, callback); - } catch (IllegalArgumentException e) { - Slog.e(TAG, - "Failed to revoke " - + permissionName - + " from " - + pName, - e); - } - } - } - /** - * Update which app owns a permission trees. - * - *

Possible parameter combinations - * - * - * - * - *
packageName != nullpackageName == null
pkg != nullpackage is updatedinvalid
pkg == nullpackage is deletedall packages are updated
- * - * @param packageName The package that is updated, or {@code null} if all packages should be - * updated - * @param pkg The package that is updated, or {@code null} if all packages should be updated or - * package is deleted - * - * @return {@code true} if a permission tree ownership might have changed - */ - private boolean updatePermissionTreeSourcePackage(@Nullable String packageName, - @Nullable AndroidPackage pkg) { - // Always need update if packageName is null - if (packageName == null) { - return true; - } - boolean changed = false; - - Set needsUpdate = null; - synchronized (mLock) { - final Iterator it = mRegistry.getPermissionTrees().iterator(); - while (it.hasNext()) { - final Permission bp = it.next(); - if (!packageName.equals(bp.getPackageName())) { - // Not checking sourcePackageSetting because it can be null when - // the permission source package is the target package and the target package is - // being uninstalled, - continue; - } - // The target package is the source of the current permission tree - // Set to changed for either install or uninstall - changed = true; - if (pkg == null || !hasPermission(pkg, bp.getName())) { - Slog.i(TAG, "Removing permission tree " + bp.getName() - + " that used to be declared by " + bp.getPackageName()); - it.remove(); - } - if (needsUpdate == null) { - needsUpdate = new ArraySet<>(); - } - needsUpdate.add(bp); - } - } - if (needsUpdate != null) { - for (final Permission bp : needsUpdate) { - final AndroidPackage sourcePkg = - mPackageManagerInt.getPackage(bp.getPackageName()); - final PackageStateInternal sourcePs = - mPackageManagerInt.getPackageStateInternal(bp.getPackageName()); - synchronized (mLock) { - if (sourcePkg != null && sourcePs != null) { - continue; - } - Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() - + " from package " + bp.getPackageName()); - mRegistry.removePermission(bp.getName()); - } - } - } - 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); - } - } - - /** - * Enforces the request is from the system or an app that has INTERACT_ACROSS_USERS - * or INTERACT_ACROSS_USERS_FULL permissions, if the {@code 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, @UserIdInt int userId, - boolean requireFullPermission, boolean checkShell, @Nullable String message) { - if (userId < 0) { - throw new IllegalArgumentException("Invalid userId " + userId); - } - if (checkShell) { - enforceShellRestriction(UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); - } - final int callingUserId = UserHandle.getUserId(callingUid); - if (checkCrossUserPermission(callingUid, callingUserId, userId, requireFullPermission)) { - return; - } - String errorMessage = buildInvalidCrossUserPermissionMessage( - callingUid, userId, message, requireFullPermission); - Slog.w(TAG, errorMessage); - throw new SecurityException(errorMessage); - } - - /** - * Enforces that if the caller is shell, it does not have the provided user restriction. - */ - private void enforceShellRestriction(@NonNull String restriction, int callingUid, - @UserIdInt int userId) { - if (callingUid == Process.SHELL_UID) { - if (userId >= 0 && mUserManagerInt.hasUserRestriction(restriction, userId)) { - throw new SecurityException("Shell does not have permission to access user " - + userId); - } else if (userId < 0) { - Slog.e(LOG_TAG, "Unable to check shell permission for user " - + userId + "\n\t" + Debug.getCallers(3)); - } - } - } - - private boolean checkCrossUserPermission(int callingUid, @UserIdInt int callingUserId, - @UserIdInt int userId, boolean requireFullPermission) { - if (userId == callingUserId) { - return true; - } - if (callingUid == Process.SYSTEM_UID || callingUid == Process.ROOT_UID) { - return true; - } - if (requireFullPermission) { - return checkCallingOrSelfPermission( - android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - } - return checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL) - || checkCallingOrSelfPermission(android.Manifest.permission.INTERACT_ACROSS_USERS); - } - - private boolean checkCallingOrSelfPermission(String permission) { - return mContext.checkCallingOrSelfPermission(permission) - == PackageManager.PERMISSION_GRANTED; - } - - @NonNull - private static String buildInvalidCrossUserPermissionMessage(int callingUid, - @UserIdInt int userId, @Nullable String message, boolean requireFullPermission) { - StringBuilder builder = new StringBuilder(); - if (message != null) { - builder.append(message); - builder.append(": "); - } - builder.append("UID "); - builder.append(callingUid); - builder.append(" requires "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS_FULL); - if (!requireFullPermission) { - builder.append(" or "); - builder.append(android.Manifest.permission.INTERACT_ACROSS_USERS); - } - builder.append(" to access user "); - builder.append(userId); - builder.append("."); - return builder.toString(); - } - - @GuardedBy("mLock") - private int calculateCurrentPermissionFootprintLocked(@NonNull Permission permissionTree) { - int size = 0; - for (final Permission permission : mRegistry.getPermissions()) { - size += permissionTree.calculateFootprint(permission); - } - return size; - } - - @GuardedBy("mLock") - private void enforcePermissionCapLocked(PermissionInfo info, Permission 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() { - // Now that we've scanned all packages, and granted any default - // permissions, ensure permissions are updated. Beware of dragons if you - // try optimizing this. - updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false); - - final PermissionPolicyInternal permissionPolicyInternal = LocalServices.getService( - PermissionPolicyInternal.class); - permissionPolicyInternal.setOnInitializedCallback(userId -> - // The SDK updated case is already handled when we run during the ctor. - updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, false) - ); - - mSystemReady = true; - - synchronized (mLock) { - if (mPrivappPermissionsViolations != null) { - throw new IllegalStateException("Signature|privileged permissions not in " - + "privapp-permissions allowlist: " + mPrivappPermissionsViolations); - } - } - - mPermissionControllerManager = mContext.getSystemService(PermissionControllerManager.class); - mPermissionPolicyInternal = LocalServices.getService(PermissionPolicyInternal.class); - } - - private static String getVolumeUuidForPackage(AndroidPackage pkg) { - if (pkg == null) { - return StorageManager.UUID_PRIVATE_INTERNAL; - } - if (pkg.isExternalStorage()) { - if (TextUtils.isEmpty(pkg.getVolumeUuid())) { - return StorageManager.UUID_PRIMARY_PHYSICAL; - } else { - return pkg.getVolumeUuid(); - } - } else { - return StorageManager.UUID_PRIVATE_INTERNAL; - } - } - - private static boolean hasPermission(AndroidPackage pkg, String permName) { - if (pkg.getPermissions().isEmpty()) { - return false; - } - - for (int i = pkg.getPermissions().size() - 1; i >= 0; i--) { - if (pkg.getPermissions().get(i).getName().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); - } - - @GuardedBy("mLock") - @Nullable - private UidPermissionState getUidStateLocked(@NonNull PackageStateInternal ps, - @UserIdInt int userId) { - return getUidStateLocked(ps.getAppId(), userId); - } - - @GuardedBy("mLock") - @Nullable - private UidPermissionState getUidStateLocked(@NonNull AndroidPackage pkg, - @UserIdInt int userId) { - return getUidStateLocked(pkg.getUid(), userId); - } - - @GuardedBy("mLock") - @Nullable - private UidPermissionState getUidStateLocked(@AppIdInt int appId, @UserIdInt int userId) { - final UserPermissionState userState = mState.getUserState(userId); - if (userState == null) { + @Override + public ParceledListSlice queryPermissionsByGroup(String groupName, int flags) { + List permissionInfo = + mPermissionManagerServiceImpl.queryPermissionsByGroup(groupName, flags); + if (permissionInfo == null) { return null; } - return userState.getUidState(appId); + + return new ParceledListSlice<>(permissionInfo); } - private void removeUidStateAndResetPackageInstallPermissionsFixed(@AppIdInt int appId, - @NonNull String packageName, @UserIdInt int userId) { - synchronized (mLock) { - final UserPermissionState userState = mState.getUserState(userId); - if (userState == null) { - return; - } - userState.removeUidState(appId); - userState.setInstallPermissionsFixed(packageName, false); - } + @Override + public boolean addPermission(PermissionInfo permissionInfo, boolean async) { + return mPermissionManagerServiceImpl.addPermission(permissionInfo, async); } - private void readLegacyPermissionState() { - final int[] userIds = getAllUserIds(); - mPackageManagerInt.forEachPackageSetting(ps -> { - final int appId = ps.getAppId(); - final LegacyPermissionState legacyState = ps.getLegacyPermissionState(); - - synchronized (mLock) { - for (final int userId : userIds) { - final UserPermissionState userState = mState.getOrCreateUserState(userId); - - userState.setInstallPermissionsFixed(ps.getPackageName(), - ps.isInstallPermissionsFixed()); - final UidPermissionState uidState = userState.getOrCreateUidState(appId); - uidState.reset(); - uidState.setMissing(legacyState.isMissing(userId)); - readLegacyPermissionStatesLocked(uidState, - legacyState.getPermissionStates(userId)); - } - } - }); + @Override + public void removePermission(String permissionName) { + mPermissionManagerServiceImpl.removePermission(permissionName); } - @GuardedBy("mLock") - private void readLegacyPermissionStatesLocked(@NonNull UidPermissionState uidState, - @NonNull Collection permissionStates) { - for (final LegacyPermissionState.PermissionState permissionState : permissionStates) { - final String permissionName = permissionState.getName(); - final Permission permission = mRegistry.getPermission(permissionName); - if (permission == null) { - Slog.w(TAG, "Unknown permission: " + permissionName); - continue; - } - uidState.putPermissionState(permission, permissionState.isGranted(), - permissionState.getFlags()); - } + @Override + public int getPermissionFlags(String packageName, String permissionName, int userId) { + return mPermissionManagerServiceImpl + .getPermissionFlags(packageName, permissionName, userId); } - private void writeLegacyPermissionState() { - final int[] userIds; - synchronized (mLock) { - userIds = mState.getUserIds(); - } - mPackageManagerInt.forEachPackageSetting(ps -> { - ps.setInstallPermissionsFixed(false); - final LegacyPermissionState legacyState = ps.getLegacyPermissionState(); - legacyState.reset(); - final int appId = ps.getAppId(); - - synchronized (mLock) { - for (final int userId : userIds) { - final UserPermissionState userState = mState.getUserState(userId); - if (userState == null) { - Slog.e(TAG, "Missing user state for " + userId); - continue; - } - - if (userState.areInstallPermissionsFixed(ps.getPackageName())) { - ps.setInstallPermissionsFixed(true); - } - - final UidPermissionState uidState = userState.getUidState(appId); - if (uidState == null) { - Slog.e(TAG, "Missing permission state for " + ps.getPackageName() - + " and user " + userId); - continue; - } - - legacyState.setMissing(uidState.isMissing(), userId); - final List permissionStates = uidState.getPermissionStates(); - final int permissionStatesSize = permissionStates.size(); - for (int i = 0; i < permissionStatesSize; i++) { - final PermissionState permissionState = permissionStates.get(i); - - final LegacyPermissionState.PermissionState legacyPermissionState = - new LegacyPermissionState.PermissionState(permissionState.getName(), - permissionState.getPermission().isRuntime(), - permissionState.isGranted(), permissionState.getFlags()); - legacyState.putPermissionState(legacyPermissionState, userId); - } - } - } - }); + @Override + public void updatePermissionFlags(String packageName, String permissionName, int flagMask, + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { + mPermissionManagerServiceImpl.updatePermissionFlags(packageName, permissionName, flagMask, + flagValues, checkAdjustPolicyFlagPermission, userId); } - private void readLegacyPermissions(@NonNull LegacyPermissionSettings legacyPermissionSettings) { - for (int readPermissionOrPermissionTree = 0; readPermissionOrPermissionTree < 2; - readPermissionOrPermissionTree++) { - final List legacyPermissions = readPermissionOrPermissionTree == 0 - ? legacyPermissionSettings.getPermissions() - : legacyPermissionSettings.getPermissionTrees(); - synchronized (mLock) { - final int legacyPermissionsSize = legacyPermissions.size(); - for (int i = 0; i < legacyPermissionsSize; i++) { - final LegacyPermission legacyPermission = legacyPermissions.get(i); - final Permission permission = new Permission( - legacyPermission.getPermissionInfo(), legacyPermission.getType()); - if (readPermissionOrPermissionTree == 0) { - // Config permissions are currently read in PermissionManagerService - // constructor. The old behavior was to add other attributes to the config - // permission in LegacyPermission.read(), so equivalently we can add the - // GIDs to the new permissions here, since config permissions created in - // PermissionManagerService constructor get only their names and GIDs there. - final Permission configPermission = mRegistry.getPermission( - permission.getName()); - if (configPermission != null - && configPermission.getType() == Permission.TYPE_CONFIG) { - permission.setGids(configPermission.getRawGids(), - configPermission.areGidsPerUser()); - } - mRegistry.addPermission(permission); - } else { - mRegistry.addPermissionTree(permission); - } - } - } - } + @Override + public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId) { + mPermissionManagerServiceImpl.updatePermissionFlagsForAllApps(flagMask, flagValues, userId); } - private void writeLegacyPermissions( - @NonNull LegacyPermissionSettings legacyPermissionSettings) { - for (int writePermissionOrPermissionTree = 0; writePermissionOrPermissionTree < 2; - writePermissionOrPermissionTree++) { - final List legacyPermissions = new ArrayList<>(); - synchronized (mLock) { - final Collection permissions = writePermissionOrPermissionTree == 0 - ? mRegistry.getPermissions() : mRegistry.getPermissionTrees(); - for (final Permission permission : permissions) { - // We don't need to provide UID and GIDs, which are only retrieved when dumping. - final LegacyPermission legacyPermission = new LegacyPermission( - permission.getPermissionInfo(), permission.getType(), 0, - EmptyArray.INT); - legacyPermissions.add(legacyPermission); - } - } - if (writePermissionOrPermissionTree == 0) { - legacyPermissionSettings.replacePermissions(legacyPermissions); - } else { - legacyPermissionSettings.replacePermissionTrees(legacyPermissions); - } - } + @Override + public void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { + mPermissionManagerServiceImpl.addOnPermissionsChangeListener(listener); } - private void onPackageAddedInternal(@NonNull AndroidPackage pkg, boolean isInstantApp, - @Nullable AndroidPackage oldPkg) { - if (!pkg.getAdoptPermissions().isEmpty()) { - // This package wants to adopt ownership of permissions from - // another package. - for (int i = pkg.getAdoptPermissions().size() - 1; i >= 0; i--) { - final String origName = pkg.getAdoptPermissions().get(i); - if (canAdoptPermissionsInternal(origName, pkg)) { - Slog.i(TAG, "Adopting permissions from " + origName + " to " - + pkg.getPackageName()); - synchronized (mLock) { - mRegistry.transferPermissions(origName, pkg.getPackageName()); - } - } - } - } - - // Don't allow ephemeral applications to define new permissions groups. - if (isInstantApp) { - Slog.w(TAG, "Permission groups from package " + pkg.getPackageName() - + " ignored: instant apps cannot define new permission groups."); - } else { - addAllPermissionGroupsInternal(pkg); - } - - // If a permission has had its defining app changed, or it has had its protection - // upgraded, we need to revoke apps that hold it - final List permissionsWithChangedDefinition; - // Don't allow ephemeral applications to define new permissions. - if (isInstantApp) { - permissionsWithChangedDefinition = null; - Slog.w(TAG, "Permissions from package " + pkg.getPackageName() - + " ignored: instant apps cannot define new permissions."); - } else { - permissionsWithChangedDefinition = addAllPermissionsInternal(pkg); - } - - boolean hasOldPkg = oldPkg != null; - boolean hasPermissionDefinitionChanges = - !CollectionUtils.isEmpty(permissionsWithChangedDefinition); - if (hasOldPkg || hasPermissionDefinitionChanges) { - // We need to call revokeRuntimePermissionsIfGroupChanged async as permission - // revoke callbacks from this method might need to kill apps which need the - // mPackages lock on a different thread. This would dead lock. - AsyncTask.execute(() -> { - if (hasOldPkg) { - revokeRuntimePermissionsIfGroupChangedInternal(pkg, oldPkg); - revokeStoragePermissionsIfScopeExpandedInternal(pkg, oldPkg); - } - if (hasPermissionDefinitionChanges) { - revokeRuntimePermissionsIfPermissionDefinitionChangedInternal( - permissionsWithChangedDefinition); - } - }); - } + @Override + public void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener) { + mPermissionManagerServiceImpl.removeOnPermissionsChangeListener(listener); } - private boolean canAdoptPermissionsInternal(@NonNull String oldPackageName, - @NonNull AndroidPackage newPkg) { - final PackageStateInternal oldPs = - mPackageManagerInt.getPackageStateInternal(oldPackageName); - if (oldPs == null) { - return false; - } - if (!oldPs.isSystem()) { - Slog.w(TAG, "Unable to update from " + oldPs.getPackageName() - + " to " + newPkg.getPackageName() - + ": old package not in system partition"); - return false; - } - if (mPackageManagerInt.getPackage(oldPs.getPackageName()) != null) { - Slog.w(TAG, "Unable to update from " + oldPs.getPackageName() - + " to " + newPkg.getPackageName() - + ": old package still exists"); - return false; - } - return true; + @Override + public List getAllowlistedRestrictedPermissions(String packageName, + int flags, int userId) { + return mPermissionManagerServiceImpl.getAllowlistedRestrictedPermissions(packageName, + flags, userId); } - private void onPackageInstalledInternal(@NonNull AndroidPackage pkg, int previousAppId, - @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, - @UserIdInt int[] userIds) { - // If previousAppId is not Process.INVALID_UID, the package is performing a migration out - // of a shared user group. Operations we need to do before calling updatePermissions(): - // - Retrieve the original uid permission state and create a copy of it as the new app's - // uid state. The new permission state will be properly updated in updatePermissions(). - // - Remove the app from the original shared user group. Other apps in the shared - // user group will perceive as if the original app is uninstalled. - if (previousAppId != Process.INVALID_UID) { - final PackageStateInternal ps = - mPackageManagerInt.getPackageStateInternal(pkg.getPackageName()); - final List origSharedUserPackages = - mPackageManagerInt.getPackagesForAppId(previousAppId); - - synchronized (mLock) { - // All users are affected - for (final int userId : getAllUserIds()) { - // Retrieve the original uid state - final UserPermissionState userState = mState.getUserState(userId); - if (userState == null) { - continue; - } - final UidPermissionState prevUidState = userState.getUidState(previousAppId); - if (prevUidState == null) { - continue; - } - - // Insert new uid state by cloning the original one - userState.createUidStateWithExisting(ps.getAppId(), prevUidState); - - // Remove original app ID from original shared user group - // Should match the implementation of onPackageUninstalledInternal(...) - if (origSharedUserPackages.isEmpty()) { - removeUidStateAndResetPackageInstallPermissionsFixed( - previousAppId, pkg.getPackageName(), userId); - } else { - revokeSharedUserPermissionsForLeavingPackageInternal(pkg, previousAppId, - origSharedUserPackages, userId); - } - } - } - } - updatePermissions(pkg.getPackageName(), pkg); - for (final int userId : userIds) { - addAllowlistedRestrictedPermissionsInternal(pkg, - params.getAllowlistedRestrictedPermissions(), - FLAG_PERMISSION_WHITELIST_INSTALLER, userId); - final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode(); - if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED - || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { - setAutoRevokeExemptedInternal(pkg, - autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); - } - grantRequestedRuntimePermissionsInternal(pkg, params.getGrantedPermissions(), userId); - } + @Override + public boolean addAllowlistedRestrictedPermission(String packageName, String permissionName, + int flags, int userId) { + return mPermissionManagerServiceImpl.addAllowlistedRestrictedPermission(packageName, + permissionName, flags, userId); } - private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, - @NonNull List allowlistedRestrictedPermissions, - @PermissionWhitelistFlags int flags, @UserIdInt int userId) { - List permissions = getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); - if (permissions != null) { - ArraySet permissionSet = new ArraySet<>(permissions); - permissionSet.addAll(allowlistedRestrictedPermissions); - permissions = new ArrayList<>(permissionSet); - } else { - permissions = allowlistedRestrictedPermissions; - } - setAllowlistedRestrictedPermissionsInternal(pkg, permissions, flags, userId); + @Override + public boolean removeAllowlistedRestrictedPermission(String packageName, String permissionName, + int flags, int userId) { + return mPermissionManagerServiceImpl.removeAllowlistedRestrictedPermission(packageName, + permissionName, flags, userId); } - private void onPackageRemovedInternal(@NonNull AndroidPackage pkg) { - removeAllPermissionsInternal(pkg); + @Override + public void grantRuntimePermission(String packageName, String permissionName, int userId) { + mPermissionManagerServiceImpl.grantRuntimePermission(packageName, permissionName, userId); } - private void onPackageUninstalledInternal(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List sharedUserPkgs, - @UserIdInt int[] userIds) { - // TODO: Handle the case when a system app upgrade is uninstalled and need to rejoin - // a shared UID permission state. - - // TODO: Move these checks to check PackageState to be more reliable. - // System packages should always have an available APK. - if (pkg != null && pkg.isSystem() - // We may be fully removing invalid system packages during boot, and in that case we - // do want to remove their permission state. So make sure that the package is only - // being marked as uninstalled instead of fully removed. - && mPackageManagerInt.getPackage(packageName) != null) { - // If we are only marking a system package as uninstalled, we need to keep its - // pregranted permission state so that it still works once it gets reinstalled, thus - // only reset the user modifications to its permission state. - for (final int userId : userIds) { - resetRuntimePermissionsInternal(pkg, userId); - } - return; - } - updatePermissions(packageName, null); - for (final int userId : userIds) { - if (sharedUserPkgs.isEmpty()) { - removeUidStateAndResetPackageInstallPermissionsFixed(appId, packageName, userId); - } else { - // Remove permissions associated with package. Since runtime - // permissions are per user we have to kill the removed package - // or packages running under the shared user of the removed - // package if revoking the permissions requested only by the removed - // package is successful and this causes a change in gids. - revokeSharedUserPermissionsForLeavingPackageInternal(pkg, appId, - sharedUserPkgs, userId); - } - } + @Override + public void revokeRuntimePermission(String packageName, String permissionName, int userId, + String reason) { + mPermissionManagerServiceImpl.revokeRuntimePermission(packageName, permissionName, userId, + reason); } - @NonNull - private List getLegacyPermissions() { - synchronized (mLock) { - final List legacyPermissions = new ArrayList<>(); - for (final Permission permission : mRegistry.getPermissions()) { - final LegacyPermission legacyPermission = new LegacyPermission( - permission.getPermissionInfo(), permission.getType(), permission.getUid(), - permission.getRawGids()); - legacyPermissions.add(legacyPermission); - } - return legacyPermissions; - } + @Override + public boolean shouldShowRequestPermissionRationale(String packageName, String permissionName, + int userId) { + return mPermissionManagerServiceImpl.shouldShowRequestPermissionRationale(packageName, + permissionName, userId); } - @NonNull - private Map> getAllAppOpPermissionPackages() { - synchronized (mLock) { - final ArrayMap> appOpPermissionPackages = - mRegistry.getAllAppOpPermissionPackages(); - final Map> deepClone = new ArrayMap<>(); - final int appOpPermissionPackagesSize = appOpPermissionPackages.size(); - for (int i = 0; i < appOpPermissionPackagesSize; i++) { - final String appOpPermission = appOpPermissionPackages.keyAt(i); - final ArraySet packageNames = appOpPermissionPackages.valueAt(i); - deepClone.put(appOpPermission, new ArraySet<>(packageNames)); - } - return deepClone; - } + @Override + public boolean isPermissionRevokedByPolicy(String packageName, String permissionName, + int userId) { + return mPermissionManagerServiceImpl + .isPermissionRevokedByPolicy(packageName, permissionName, userId); } - @NonNull - private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { - final LegacyPermissionState legacyState = new LegacyPermissionState(); - synchronized (mLock) { - final int[] userIds = mState.getUserIds(); - for (final int userId : userIds) { - final UidPermissionState uidState = getUidStateLocked(appId, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " - + userId); - continue; - } - - final List permissionStates = uidState.getPermissionStates(); - final int permissionStatesSize = permissionStates.size(); - for (int i = 0; i < permissionStatesSize; i++) { - final PermissionState permissionState = permissionStates.get(i); - - final LegacyPermissionState.PermissionState legacyPermissionState = - new LegacyPermissionState.PermissionState(permissionState.getName(), - permissionState.getPermission().isRuntime(), - permissionState.isGranted(), permissionState.getFlags()); - legacyState.putPermissionState(legacyPermissionState, userId); - } - } - } - return legacyState; + @Override + public List getSplitPermissions() { + return mPermissionManagerServiceImpl.getSplitPermissions(); } - @NonNull - private int[] getGidsForUid(int uid) { - final int appId = UserHandle.getAppId(uid); - final int userId = UserHandle.getUserId(uid); - synchronized (mLock) { - final UidPermissionState uidState = getUidStateLocked(appId, userId); - if (uidState == null) { - Slog.e(TAG, "Missing permissions state for app ID " + appId + " and user ID " - + userId); - return EMPTY_INT_ARRAY; - } - return uidState.computeGids(mGlobalGids, userId); - } - } + /* End of delegate methods to PermissionManagerServiceInterface */ private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal { @Override @@ -5135,140 +582,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return PermissionManagerService.this.checkUidPermission(uid, permissionName); } - @Override - public void onSystemReady() { - PermissionManagerService.this.systemReady(); - } - - @Override - public boolean isPermissionsReviewRequired(@NonNull String packageName, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - // TODO(b/173235285): Some caller may pass USER_ALL as userId. - //Preconditions.checkArgumentNonnegative(userId, "userId"); - return isPermissionsReviewRequiredInternal(packageName, userId); - } - - @Override - public void readLegacyPermissionStateTEMP() { - PermissionManagerService.this.readLegacyPermissionState(); - } - @Override - public void writeLegacyPermissionStateTEMP() { - PermissionManagerService.this.writeLegacyPermissionState(); - } - @Override - public void onUserRemoved(@UserIdInt int userId) { - Preconditions.checkArgumentNonNegative(userId, "userId"); - PermissionManagerService.this.onUserRemoved(userId); - } - @NonNull - @Override - public Set getGrantedPermissions(@NonNull String packageName, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - return getGrantedPermissionsInternal(packageName, userId); - } - @NonNull - @Override - public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { - Objects.requireNonNull(permissionName, "permissionName"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - return getPermissionGidsInternal(permissionName, userId); - } - @NonNull - @Override - public String[] getAppOpPermissionPackages(@NonNull String permissionName) { - Objects.requireNonNull(permissionName, "permissionName"); - return PermissionManagerService.this.getAppOpPermissionPackagesInternal(permissionName); - } - @Override - public void onStorageVolumeMounted(@Nullable String volumeUuid, boolean fingerprintChanged) { - updateAllPermissions(volumeUuid, fingerprintChanged); - } - @Override - public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) { - Objects.requireNonNull(pkg, "pkg"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - resetRuntimePermissionsInternal(pkg, userId); - } - - @Override - public Permission getPermissionTEMP(String permName) { - synchronized (mLock) { - return mRegistry.getPermission(permName); - } - } - - @Override - public @NonNull ArrayList getAllPermissionsWithProtection( - @PermissionInfo.Protection int protection) { - ArrayList matchingPermissions = new ArrayList<>(); - - synchronized (mLock) { - for (final Permission permission : mRegistry.getPermissions()) { - if (permission.getProtection() == protection) { - matchingPermissions.add(permission.generatePermissionInfo(0)); - } - } - } - - return matchingPermissions; - } - - @Override - public @NonNull ArrayList getAllPermissionsWithProtectionFlags( - @PermissionInfo.ProtectionFlags int protectionFlags) { - ArrayList matchingPermissions = new ArrayList<>(); - - synchronized (mLock) { - for (final Permission permission : mRegistry.getPermissions()) { - if ((permission.getProtectionFlags() & protectionFlags) == protectionFlags) { - matchingPermissions.add(permission.generatePermissionInfo(0)); - } - } - } - - return matchingPermissions; - } - - @Nullable - @Override - public byte[] backupRuntimePermissions(@UserIdInt int userId) { - Preconditions.checkArgumentNonNegative(userId, "userId"); - return PermissionManagerService.this.backupRuntimePermissions(userId); - } - - @Override - public void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { - Objects.requireNonNull(backup, "backup"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - PermissionManagerService.this.restoreRuntimePermissions(backup, userId); - } - - @Override - public void restoreDelayedRuntimePermissions(@NonNull String packageName, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - PermissionManagerService.this.restoreDelayedRuntimePermissions(packageName, userId); - } - - @Override - public void addOnRuntimePermissionStateChangedListener( - OnRuntimePermissionStateChangedListener listener) { - PermissionManagerService.this.addOnRuntimePermissionStateChangedListener( - listener); - } - - @Override - public void removeOnRuntimePermissionStateChangedListener( - OnRuntimePermissionStateChangedListener listener) { - PermissionManagerService.this.removeOnRuntimePermissionStateChangedListener( - listener); - } - @Override public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName, @Nullable List permissionNames) { @@ -5287,87 +600,6 @@ public class PermissionManagerService extends IPermissionManager.Stub { return getDelegatedShellPermissionsInternal(); } - @Override - public void onUserCreated(@UserIdInt int userId) { - Preconditions.checkArgumentNonNegative(userId, "userId"); - // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG - updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true); - } - - @Override - public void readLegacyPermissionsTEMP( - @NonNull LegacyPermissionSettings legacyPermissionSettings) { - PermissionManagerService.this.readLegacyPermissions(legacyPermissionSettings); - } - - @Override - public void writeLegacyPermissionsTEMP( - @NonNull LegacyPermissionSettings legacyPermissionSettings) { - PermissionManagerService.this.writeLegacyPermissions(legacyPermissionSettings); - } - - @Override - public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, - @Nullable AndroidPackage oldPkg) { - Objects.requireNonNull(pkg); - onPackageAddedInternal(pkg, isInstantApp, oldPkg); - } - - @Override - public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, - @NonNull PackageInstalledParams params, @UserIdInt int userId) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(params, "params"); - Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM - || userId == UserHandle.USER_ALL, "userId"); - final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds() - : new int[] { userId }; - onPackageInstalledInternal(pkg, previousAppId, params, userIds); - } - - @Override - public void onPackageRemoved(@NonNull AndroidPackage pkg) { - Objects.requireNonNull(pkg); - onPackageRemovedInternal(pkg); - } - - @Override - public void onPackageUninstalled(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List sharedUserPkgs, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs"); - Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM - || userId == UserHandle.USER_ALL, "userId"); - final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds() - : new int[] { userId }; - onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds); - } - - @NonNull - @Override - public List getLegacyPermissions() { - return PermissionManagerService.this.getLegacyPermissions(); - } - - @NonNull - @Override - public Map> getAllAppOpPermissionPackages() { - return PermissionManagerService.this.getAllAppOpPermissionPackages(); - } - - @NonNull - @Override - public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { - return PermissionManagerService.this.getLegacyPermissionState(appId); - } - - @NonNull - @Override - public int[] getGidsForUid(int uid) { - return PermissionManagerService.this.getGidsForUid(uid); - } - @Override public void setHotwordDetectionServiceProvider(HotwordDetectionServiceProvider provider) { mHotwordDetectionServiceProvider = provider; @@ -5377,84 +609,174 @@ public class PermissionManagerService extends IPermissionManager.Stub { public HotwordDetectionServiceProvider getHotwordDetectionServiceProvider() { return mHotwordDetectionServiceProvider; } - } - /** - * Callbacks invoked when interesting actions have been taken on a permission. - *

- * NOTE: The current arguments are merely to support the existing use cases. This - * needs to be properly thought out with appropriate arguments for each of the - * callback methods. - */ - private static class PermissionCallback { - public void onGidsChanged(@AppIdInt int appId, @UserIdInt int userId) {} - public void onPermissionChanged() {} - public void onPermissionGranted(int uid, @UserIdInt int userId) {} - public void onInstallPermissionGranted() {} - public void onPermissionRevoked(int uid, @UserIdInt int userId, String reason) {} - public void onInstallPermissionRevoked() {} - public void onPermissionUpdated(@UserIdInt int[] updatedUserIds, boolean sync) {} - public void onPermissionUpdatedNotifyListener(@UserIdInt int[] updatedUserIds, boolean sync, - int uid) { - onPermissionUpdated(updatedUserIds, sync); + /* Start of delegate methods to PermissionManagerServiceInterface */ + + @NonNull + @Override + public int[] getGidsForUid(int uid) { + return mPermissionManagerServiceImpl.getGidsForUid(uid); } - public void onPermissionRemoved() {} - public void onInstallPermissionUpdated() {} - public void onInstallPermissionUpdatedNotifyListener(int uid) { - onInstallPermissionUpdated(); - } - } - private static final class OnPermissionChangeListeners extends Handler { - private static final int MSG_ON_PERMISSIONS_CHANGED = 1; - - private final RemoteCallbackList mPermissionListeners = - new RemoteCallbackList<>(); - - OnPermissionChangeListeners(Looper looper) { - super(looper); + @NonNull + @Override + public Map> getAllAppOpPermissionPackages() { + return mPermissionManagerServiceImpl.getAllAppOpPermissionPackages(); } @Override - public void handleMessage(Message msg) { - switch (msg.what) { - case MSG_ON_PERMISSIONS_CHANGED: { - final int uid = msg.arg1; - handleOnPermissionsChanged(uid); - } break; - } + public void onUserCreated(@UserIdInt int userId) { + mPermissionManagerServiceImpl.onUserCreated(userId); } - public void addListener(IOnPermissionsChangeListener listener) { - mPermissionListeners.register(listener); + @NonNull + @Override + public List getLegacyPermissions() { + return mPermissionManagerServiceImpl.getLegacyPermissions(); } - public void removeListener(IOnPermissionsChangeListener listener) { - mPermissionListeners.unregister(listener); + @NonNull + @Override + public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { + return mPermissionManagerServiceImpl.getLegacyPermissionState(appId); } - public void onPermissionsChanged(int uid) { - if (mPermissionListeners.getRegisteredCallbackCount() > 0) { - obtainMessage(MSG_ON_PERMISSIONS_CHANGED, uid, 0).sendToTarget(); - } + @Nullable + @Override + public byte[] backupRuntimePermissions(@UserIdInt int userId) { + return mPermissionManagerServiceImpl.backupRuntimePermissions(userId); } - private void handleOnPermissionsChanged(int uid) { - final int count = mPermissionListeners.beginBroadcast(); - try { - for (int i = 0; i < count; i++) { - IOnPermissionsChangeListener callback = mPermissionListeners - .getBroadcastItem(i); - try { - callback.onPermissionsChanged(uid); - } catch (RemoteException e) { - Log.e(TAG, "Permission listener is dead", e); - } - } - } finally { - mPermissionListeners.finishBroadcast(); - } + @Override + public void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { + mPermissionManagerServiceImpl.restoreRuntimePermissions(backup, userId); } + + @Override + public void restoreDelayedRuntimePermissions(@NonNull String packageName, + @UserIdInt int userId) { + mPermissionManagerServiceImpl.restoreDelayedRuntimePermissions(packageName, userId); + } + + @Override + public void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + mPermissionManagerServiceImpl.readLegacyPermissionsTEMP(legacyPermissionSettings); + } + + @Override + public void writeLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { + mPermissionManagerServiceImpl.writeLegacyPermissionsTEMP(legacyPermissionSettings); + } + + @Override + public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg) { + mPermissionManagerServiceImpl.onPackageAdded(pkg, isInstantApp, oldPkg); + } + + @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, + @NonNull PackageInstalledParams params, @UserIdInt int userId) { + mPermissionManagerServiceImpl.onPackageInstalled(pkg, previousAppId, params, userId); + } + + @Override + public void onPackageRemoved(@NonNull AndroidPackage pkg) { + mPermissionManagerServiceImpl.onPackageRemoved(pkg); + } + + @Override + public void onPackageUninstalled(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List sharedUserPkgs, + @UserIdInt int userId) { + mPermissionManagerServiceImpl.onPackageUninstalled(packageName, appId, + pkg, sharedUserPkgs, userId); + } + + @Override + public void addOnRuntimePermissionStateChangedListener( + OnRuntimePermissionStateChangedListener listener) { + mPermissionManagerServiceImpl.addOnRuntimePermissionStateChangedListener(listener); + } + + @Override + public void removeOnRuntimePermissionStateChangedListener( + OnRuntimePermissionStateChangedListener listener) { + mPermissionManagerServiceImpl.removeOnRuntimePermissionStateChangedListener(listener); + } + + @Override + public void onSystemReady() { + mPermissionManagerServiceImpl.onSystemReady(); + } + + @Override + public boolean isPermissionsReviewRequired(@NonNull String packageName, + @UserIdInt int userId) { + return mPermissionManagerServiceImpl.isPermissionsReviewRequired(packageName, userId); + } + + @Override + public void readLegacyPermissionStateTEMP() { + mPermissionManagerServiceImpl.readLegacyPermissionStateTEMP(); + } + @Override + public void writeLegacyPermissionStateTEMP() { + mPermissionManagerServiceImpl.writeLegacyPermissionStateTEMP(); + } + @Override + public void onUserRemoved(@UserIdInt int userId) { + mPermissionManagerServiceImpl.onUserRemoved(userId); + } + @NonNull + @Override + public Set getGrantedPermissions(@NonNull String packageName, + @UserIdInt int userId) { + return mPermissionManagerServiceImpl.getGrantedPermissions(packageName, userId); + } + @NonNull + @Override + public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { + return mPermissionManagerServiceImpl.getPermissionGids(permissionName, userId); + } + @NonNull + @Override + public String[] getAppOpPermissionPackages(@NonNull String permissionName) { + return mPermissionManagerServiceImpl.getAppOpPermissionPackages(permissionName); + } + @Override + public void onStorageVolumeMounted(@Nullable String volumeUuid, + boolean fingerprintChanged) { + mPermissionManagerServiceImpl.onStorageVolumeMounted(volumeUuid, fingerprintChanged); + } + @Override + public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) { + mPermissionManagerServiceImpl.resetRuntimePermissions(pkg, userId); + } + + @Override + public Permission getPermissionTEMP(String permName) { + return mPermissionManagerServiceImpl.getPermissionTEMP(permName); + } + + @NonNull + @Override + public ArrayList getAllPermissionsWithProtection( + @PermissionInfo.Protection int protection) { + return mPermissionManagerServiceImpl.getAllPermissionsWithProtection(protection); + } + + @NonNull + @Override + public ArrayList getAllPermissionsWithProtectionFlags( + @PermissionInfo.ProtectionFlags int protectionFlags) { + return mPermissionManagerServiceImpl + .getAllPermissionsWithProtectionFlags(protectionFlags); + } + + /* End of delegate methods to PermissionManagerServiceInterface */ } /** diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java index e6ac0b5e49de4..d1b99380b0932 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceImpl.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2017 The Android Open Source Project + * Copyright (C) 2021 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. @@ -17,18 +17,10 @@ package com.android.server.pm.permission; import static android.Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY; -import static android.Manifest.permission.CAPTURE_AUDIO_HOTWORD; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; -import static android.Manifest.permission.RECORD_AUDIO; -import static android.Manifest.permission.UPDATE_APP_OPS_STATS; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; -import static android.app.AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; -import static android.app.AppOpsManager.ATTRIBUTION_FLAGS_NONE; import static android.app.AppOpsManager.MODE_ALLOWED; -import static android.app.AppOpsManager.MODE_ERRORED; import static android.app.AppOpsManager.MODE_IGNORED; -import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISALLOWED; -import static android.content.pm.ApplicationInfo.AUTO_REVOKE_DISCOURAGED; 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; @@ -71,23 +63,15 @@ import android.annotation.Nullable; import android.annotation.UserIdInt; import android.app.ActivityManager; import android.app.AppOpsManager; -import android.app.AppOpsManager.AttributionFlags; import android.app.IActivityManager; import android.app.admin.DevicePolicyManagerInternal; import android.compat.annotation.ChangeId; import android.compat.annotation.EnabledAfter; -import android.content.AttributionSource; -import android.content.AttributionSourceState; import android.content.Context; -import android.content.PermissionChecker; import android.content.pm.ApplicationInfo; import android.content.pm.FeatureInfo; import android.content.pm.PackageManager; -import android.content.pm.PackageManager.PermissionGroupInfoFlags; -import android.content.pm.PackageManager.PermissionInfoFlags; -import android.content.pm.PackageManager.PermissionWhitelistFlags; import android.content.pm.PackageManagerInternal; -import android.content.pm.ParceledListSlice; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.SigningDetails; @@ -104,7 +88,6 @@ import android.os.Build; import android.os.Debug; import android.os.Handler; import android.os.HandlerThread; -import android.os.IBinder; import android.os.Looper; import android.os.Message; import android.os.Process; @@ -116,12 +99,8 @@ import android.os.UserHandle; import android.os.UserManager; import android.os.storage.StorageManager; import android.permission.IOnPermissionsChangeListener; -import android.permission.IPermissionChecker; -import android.permission.IPermissionManager; -import android.permission.PermissionCheckerManager; import android.permission.PermissionControllerManager; import android.permission.PermissionManager; -import android.permission.PermissionManagerInternal; import android.text.TextUtils; import android.util.ArrayMap; import android.util.ArraySet; @@ -136,14 +115,13 @@ import android.util.SparseBooleanArray; import com.android.internal.annotations.GuardedBy; import com.android.internal.compat.IPlatformCompat; import com.android.internal.logging.MetricsLogger; -import com.android.internal.logging.nano.MetricsProto.MetricsEvent; +import com.android.internal.logging.nano.MetricsProto; import com.android.internal.os.RoSystemProperties; import com.android.internal.util.ArrayUtils; import com.android.internal.util.CollectionUtils; import com.android.internal.util.DumpUtils; import com.android.internal.util.IntPair; import com.android.internal.util.Preconditions; -import com.android.internal.util.function.TriFunction; import com.android.internal.util.function.pooled.PooledLambda; import com.android.server.FgThread; import com.android.server.LocalServices; @@ -157,8 +135,6 @@ import com.android.server.pm.UserManagerService; import com.android.server.pm.parsing.PackageInfoUtils; import com.android.server.pm.parsing.pkg.AndroidPackage; import com.android.server.pm.parsing.pkg.AndroidPackageUtils; -import com.android.server.pm.permission.PermissionManagerServiceInternal.HotwordDetectionServiceProvider; -import com.android.server.pm.permission.PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener; import com.android.server.pm.pkg.PackageStateInternal; import com.android.server.policy.PermissionPolicyInternal; import com.android.server.policy.SoftRestrictedPermissionPolicy; @@ -177,23 +153,18 @@ import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.NoSuchElementException; import java.util.Objects; import java.util.Set; -import java.util.WeakHashMap; import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ExecutionException; import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeoutException; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.BiFunction; /** - * Manages all permissions and handles permissions related tasks. + * PermissionManagerServiceImpl. */ -public class PermissionManagerServiceImpl extends IPermissionManager.Stub { +public class PermissionManagerServiceImpl implements PermissionManagerServiceInterface { + private static final String TAG = "PackageManager"; private static final String LOG_TAG = PermissionManagerServiceImpl.class.getSimpleName(); @@ -234,10 +205,6 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { /** If the permission of the value is granted, so is the key */ private static final Map FULLER_PERMISSION_MAP = new HashMap<>(); - /** Map of IBinder -> Running AttributionSource */ - private static final ConcurrentHashMap - sRunningAttributionSources = new ConcurrentHashMap<>(); - static { FULLER_PERMISSION_MAP.put(Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION); @@ -272,12 +239,6 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { /** Permission controller: User space permission management */ private PermissionControllerManager mPermissionControllerManager; - /** Map of OneTimePermissionUserManagers keyed by userId */ - @GuardedBy("mLock") - @NonNull - private final SparseArray mOneTimePermissionUserManagers = - new SparseArray<>(); - /** App ops manager */ private final AppOpsManager mAppOpsManager; @@ -303,9 +264,6 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @NonNull private final PermissionRegistry mRegistry = new PermissionRegistry(); - @NonNull - private final AttributionSourceRegistry mAttributionSourceRegistry; - @GuardedBy("mLock") @Nullable private ArraySet mPrivappPermissionsViolations; @@ -328,18 +286,13 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { /** Listeners for permission state (granting and flags) changes */ @GuardedBy("mLock") - final private ArrayList + private final ArrayList mRuntimePermissionStateChangedListeners = new ArrayList<>(); - @GuardedBy("mLock") - private CheckPermissionDelegate mCheckPermissionDelegate; - @NonNull private final OnPermissionChangeListeners mOnPermissionChangeListeners; - @Nullable - private HotwordDetectionServiceProvider mHotwordDetectionServiceProvider; - // TODO: Take a look at the methods defined in the callback. // The callback was initially created to support the split between permission // manager and the package manager. However, it's started to be used for other @@ -405,7 +358,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } }; - PermissionManagerServiceImpl(@NonNull Context context, + public PermissionManagerServiceImpl(@NonNull Context context, @NonNull ArrayMap availableFeatures) { // The package info cache is the cache for package and permission information. // Disable the package info and package permission caches locally but leave the @@ -435,13 +388,12 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { mSystemPermissions = systemConfig.getSystemPermissions(); mGlobalGids = systemConfig.getGlobalGids(); mOnPermissionChangeListeners = new OnPermissionChangeListeners(FgThread.get().getLooper()); - mAttributionSourceRegistry = new AttributionSourceRegistry(context); // propagate permission configuration final ArrayMap permConfig = SystemConfig.getInstance().getPermissions(); synchronized (mLock) { - for (int i=0; i - * The object returned is identical to the one returned by the LocalServices class using: - * {@code LocalServices.getService(PermissionManagerServiceInternal.class);} - *

- * NOTE: The external lock is temporary and should be removed. This needs to be a - * lock created by the permission manager itself. - */ - @NonNull - public static PermissionManagerServiceInternal create(@NonNull Context context, - ArrayMap availableFeatures) { - final PermissionManagerServiceInternal permMgrInt = - LocalServices.getService(PermissionManagerServiceInternal.class); - if (permMgrInt != null) { - return permMgrInt; - } - PermissionManagerServiceImpl permissionService = - (PermissionManagerServiceImpl) ServiceManager.getService("permissionmgr"); - if (permissionService == null) { - permissionService = new PermissionManagerServiceImpl(context, availableFeatures); - ServiceManager.addService("permissionmgr", permissionService); - ServiceManager.addService("permission_checker", new PermissionCheckerService(context)); - } - return LocalServices.getService(PermissionManagerServiceInternal.class); - } - /** * This method should typically only be used when granting or revoking * permissions, since the app may immediately restart after this call. @@ -503,7 +423,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { * If you're doing surgery on app code/data, use {@link PackageFreezer} to * guard your work against the app being relaunched. */ - public static void killUid(int appId, int userId, String reason) { + private static void killUid(int appId, int userId, String reason) { final long identity = Binder.clearCallingIdentity(); try { IActivityManager am = ActivityManager.getService(); @@ -533,11 +453,11 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override @NonNull - public ParceledListSlice getAllPermissionGroups( - @PermissionGroupInfoFlags int flags) { - final int callingUid = getCallingUid(); + public List getAllPermissionGroups( + @PackageManager.PermissionGroupInfoFlags int flags) { + final int callingUid = Binder.getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { - return ParceledListSlice.emptyList(); + return Collections.emptyList(); } final List out = new ArrayList<>(); @@ -550,15 +470,14 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { final int callingUserId = UserHandle.getUserId(callingUid); out.removeIf(it -> mPackageManagerInt.filterAppAccess(it.packageName, callingUid, callingUserId)); - return new ParceledListSlice<>(out); + return out; } - @Override @Nullable public PermissionGroupInfo getPermissionGroupInfo(String groupName, - @PermissionGroupInfoFlags int flags) { - final int callingUid = getCallingUid(); + @PackageManager.PermissionGroupInfoFlags int flags) { + final int callingUid = Binder.getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } @@ -582,12 +501,11 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return permissionGroupInfo; } - @Override @Nullable public PermissionInfo getPermissionInfo(@NonNull String permName, @NonNull String opPackageName, - @PermissionInfoFlags int flags) { - final int callingUid = getCallingUid(); + @PackageManager.PermissionInfoFlags int flags) { + final int callingUid = Binder.getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } @@ -628,9 +546,9 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override @Nullable - public ParceledListSlice queryPermissionsByGroup(String groupName, - @PermissionInfoFlags int flags) { - final int callingUid = getCallingUid(); + public List queryPermissionsByGroup(String groupName, + @PackageManager.PermissionInfoFlags int flags) { + final int callingUid = Binder.getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } @@ -650,12 +568,12 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { final int callingUserId = UserHandle.getUserId(callingUid); out.removeIf(it -> mPackageManagerInt.filterAppAccess(it.packageName, callingUid, callingUserId)); - return new ParceledListSlice<>(out); + return out; } @Override public boolean addPermission(PermissionInfo info, boolean async) { - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant apps can't add permissions"); } @@ -689,7 +607,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override public void removePermission(String permName) { - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } @@ -711,7 +629,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override public int getPermissionFlags(String packageName, String permName, int userId) { - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); return getPermissionFlagsInternal(packageName, permName, callingUid, userId); } @@ -753,7 +671,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override public void updatePermissionFlags(String packageName, String permName, int flagMask, int flagValues, boolean checkAdjustPolicyFlagPermission, int userId) { - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); boolean overridePolicy = false; if (callingUid != Process.SYSTEM_UID && callingUid != Process.ROOT_UID) { @@ -906,7 +824,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override public void updatePermissionFlagsForAllApps(int flagMask, int flagValues, final int userId) { - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); if (!mUserManagerInt.exists(userId)) { return; } @@ -945,27 +863,12 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private int checkPermission(String pkgName, String permName, @UserIdInt int userId) { - // Not using Objects.requireNonNull() here for compatibility reasons. - if (pkgName == null || permName == null) { - return PackageManager.PERMISSION_DENIED; - } + @Override + public int checkPermission(String pkgName, String permName, int userId) { if (!mUserManagerInt.exists(userId)) { return PackageManager.PERMISSION_DENIED; } - final CheckPermissionDelegate checkPermissionDelegate; - synchronized (mLock) { - checkPermissionDelegate = mCheckPermissionDelegate; - } - if (checkPermissionDelegate == null) { - return checkPermissionImpl(pkgName, permName, userId); - } - return checkPermissionDelegate.checkPermission(pkgName, permName, userId, - this::checkPermissionImpl); - } - - private int checkPermissionImpl(String pkgName, String permName, int userId) { final AndroidPackage pkg = mPackageManagerInt.getPackage(pkgName); if (pkg == null) { return PackageManager.PERMISSION_DENIED; @@ -975,7 +878,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { private int checkPermissionInternal(@NonNull AndroidPackage pkg, boolean isPackageExplicit, @NonNull String permissionName, @UserIdInt int userId) { - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); if (isPackageExplicit || pkg.getSharedUserId() == null) { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return PackageManager.PERMISSION_DENIED; @@ -1026,28 +929,13 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return true; } - private int checkUidPermission(int uid, String permName) { - // Not using Objects.requireNonNull() here for compatibility reasons. - if (permName == null) { - return PackageManager.PERMISSION_DENIED; - } + @Override + public int checkUidPermission(int uid, String permName) { final int userId = UserHandle.getUserId(uid); if (!mUserManagerInt.exists(userId)) { return PackageManager.PERMISSION_DENIED; } - final CheckPermissionDelegate checkPermissionDelegate; - synchronized (mLock) { - checkPermissionDelegate = mCheckPermissionDelegate; - } - if (checkPermissionDelegate == null) { - return checkUidPermissionImpl(uid, permName); - } - return checkPermissionDelegate.checkUidPermission(uid, permName, - this::checkUidPermissionImpl); - } - - private int checkUidPermissionImpl(int uid, String permName) { final AndroidPackage pkg = mPackageManagerInt.getPackage(uid); return checkUidPermissionInternal(pkg, uid, permName); } @@ -1108,7 +996,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Nullable @Override public List getAllowlistedRestrictedPermissions(@NonNull String packageName, - @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId) { Objects.requireNonNull(packageName); Preconditions.checkFlagsArgument(flags, PackageManager.FLAG_PERMISSION_WHITELIST_UPGRADE @@ -1162,7 +1050,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Nullable private List getAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, - @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId) { synchronized (mLock) { final UidPermissionState uidState = getUidStateLocked(pkg, userId); if (uidState == null) { @@ -1203,7 +1091,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override public boolean addAllowlistedRestrictedPermission(@NonNull String packageName, - @NonNull String permName, @PermissionWhitelistFlags int flags, + @NonNull String permName, @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId) { // Other argument checks are done in get/setAllowlistedRestrictedPermissions Objects.requireNonNull(permName); @@ -1240,7 +1128,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { && bp.isImmutablyRestricted(); } - final int callingUid = getCallingUid(); + final int callingUid = Binder.getCallingUid(); final int callingUserId = UserHandle.getUserId(callingUid); if (mPackageManagerInt.filterAppAccess(permissionPackageName, callingUid, callingUserId)) { EventLog.writeEvent(0x534e4554, "186404356", callingUid, permName); @@ -1259,7 +1147,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { @Override public boolean removeAllowlistedRestrictedPermission(@NonNull String packageName, - @NonNull String permName, @PermissionWhitelistFlags int flags, + @NonNull String permName, @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId) { // Other argument checks are done in get/setAllowlistedRestrictedPermissions Objects.requireNonNull(permName); @@ -1278,7 +1166,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } private boolean setAllowlistedRestrictedPermissions(@NonNull String packageName, - @Nullable List permissions, @PermissionWhitelistFlags int flags, + @Nullable List permissions, @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId) { Objects.requireNonNull(packageName); Preconditions.checkFlagsArgument(flags, @@ -1359,85 +1247,6 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return true; } - @Override - public boolean setAutoRevokeExempted( - @NonNull String packageName, boolean exempted, int userId) { - Objects.requireNonNull(packageName); - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final int callingUid = Binder.getCallingUid(); - - if (!checkAutoRevokeAccess(pkg, callingUid)) { - return false; - } - - return setAutoRevokeExemptedInternal(pkg, exempted, userId); - } - - private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, - @UserIdInt int userId) { - final int packageUid = UserHandle.getUid(userId, pkg.getUid()); - if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, - packageUid, pkg.getPackageName()) != MODE_ALLOWED) { - // Allowlist user set - don't override - return false; - } - - final long identity = Binder.clearCallingIdentity(); - try { - mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid, - pkg.getPackageName(), exempted ? MODE_IGNORED : MODE_ALLOWED); - } finally { - Binder.restoreCallingIdentity(identity); - } - return true; - } - - private boolean checkAutoRevokeAccess(AndroidPackage pkg, int callingUid) { - if (pkg == null) { - return false; - } - - final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( - Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS) - == PackageManager.PERMISSION_GRANTED; - final boolean isCallerInstallerOnRecord = - mPackageManagerInt.isCallerInstallerOfRecord(pkg, callingUid); - - if (!isCallerPrivileged && !isCallerInstallerOnRecord) { - throw new SecurityException("Caller must either hold " - + Manifest.permission.WHITELIST_AUTO_REVOKE_PERMISSIONS - + " or be the installer on record"); - } - return true; - } - - @Override - public boolean isAutoRevokeExempted(@NonNull String packageName, int userId) { - Objects.requireNonNull(packageName); - - final AndroidPackage pkg = mPackageManagerInt.getPackage(packageName); - final int callingUid = Binder.getCallingUid(); - if (mPackageManagerInt.filterAppAccess(packageName, callingUid, userId)) { - return false; - } - - if (!checkAutoRevokeAccess(pkg, callingUid)) { - return false; - } - - final int packageUid = UserHandle.getUid(userId, pkg.getUid()); - - final long identity = Binder.clearCallingIdentity(); - try { - return mAppOpsManager.checkOpNoThrow( - AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid, packageName) - == MODE_IGNORED; - } finally { - Binder.restoreCallingIdentity(identity); - } - } - @Override public void grantRuntimePermission(String packageName, String permName, final int userId) { final int callingUid = Binder.getCallingUid(); @@ -1593,7 +1402,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } if (isRuntimePermission) { - logPermission(MetricsEvent.ACTION_PERMISSION_GRANTED, permName, packageName); + logPermission(MetricsProto.MetricsEvent.ACTION_PERMISSION_GRANTED, + permName, packageName); } final int uid = UserHandle.getUid(userId, pkg.getUid()); @@ -1732,7 +1542,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } if (isRuntimePermission) { - logPermission(MetricsEvent.ACTION_PERMISSION_REVOKED, permName, packageName); + logPermission(MetricsProto.MetricsEvent.ACTION_PERMISSION_REVOKED, + permName, packageName); } if (callback != null) { @@ -1997,8 +1808,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return false; } - if (checkPermission(packageName, permName, userId) - == PackageManager.PERMISSION_GRANTED) { + if (checkPermission(packageName, permName, userId) == PackageManager.PERMISSION_GRANTED) { return false; } @@ -2082,14 +1892,16 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { * @return The state as a xml file */ @Nullable - private byte[] backupRuntimePermissions(@UserIdInt int userId) { + @Override + public byte[] backupRuntimePermissions(@UserIdInt int userId) { + Preconditions.checkArgumentNonNegative(userId, "userId"); CompletableFuture backup = new CompletableFuture<>(); mPermissionControllerManager.getRuntimePermissionBackup(UserHandle.of(userId), mContext.getMainExecutor(), backup::complete); try { return backup.get(BACKUP_TIMEOUT_MILLIS, TimeUnit.MILLISECONDS); - } catch (InterruptedException | ExecutionException | TimeoutException e) { + } catch (InterruptedException | ExecutionException | TimeoutException e) { Slog.e(TAG, "Cannot create permission backup for user " + userId, e); return null; } @@ -2104,7 +1916,10 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { * @param backup The state as an xml file * @param userId The user ID the data should be restored for */ - private void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { + @Override + public void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { + Objects.requireNonNull(backup, "backup"); + Preconditions.checkArgumentNonNegative(userId, "userId"); synchronized (mLock) { mHasNoDelayedPermBackup.delete(userId); } @@ -2122,8 +1937,11 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { * * @see #restoreRuntimePermissions */ - private void restoreDelayedRuntimePermissions(@NonNull String packageName, + @Override + public void restoreDelayedRuntimePermissions(@NonNull String packageName, @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + Preconditions.checkArgumentNonNegative(userId, "userId"); synchronized (mLock) { if (mHasNoDelayedPermBackup.get(userId, false)) { return; @@ -2140,15 +1958,17 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { }); } - private void addOnRuntimePermissionStateChangedListener(@NonNull - OnRuntimePermissionStateChangedListener listener) { + @Override + public void addOnRuntimePermissionStateChangedListener( + PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener listener) { synchronized (mLock) { mRuntimePermissionStateChangedListeners.add(listener); } } - private void removeOnRuntimePermissionStateChangedListener(@NonNull - OnRuntimePermissionStateChangedListener listener) { + @Override + public void removeOnRuntimePermissionStateChangedListener( + PermissionManagerServiceInternal.OnRuntimePermissionStateChangedListener listener) { synchronized (mLock) { mRuntimePermissionStateChangedListeners.remove(listener); } @@ -2156,14 +1976,15 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { private void notifyRuntimePermissionStateChanged(@NonNull String packageName, @UserIdInt int userId) { - FgThread.getHandler().sendMessage(PooledLambda.obtainMessage - (PermissionManagerServiceImpl::doNotifyRuntimePermissionStateChanged, - PermissionManagerServiceImpl.this, packageName, userId)); + FgThread.getHandler().sendMessage(PooledLambda.obtainMessage( + PermissionManagerServiceImpl::doNotifyRuntimePermissionStateChanged, + PermissionManagerServiceImpl.this, packageName, userId)); } private void doNotifyRuntimePermissionStateChanged(@NonNull String packageName, @UserIdInt int userId) { - final ArrayList listeners; + final ArrayList + listeners; synchronized (mLock) { if (mRuntimePermissionStateChangedListeners.isEmpty()) { return; @@ -2176,42 +1997,6 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private void startShellPermissionIdentityDelegationInternal(int uid, - @NonNull String packageName, @Nullable List permissionNames) { - synchronized (mLock) { - final CheckPermissionDelegate oldDelegate = mCheckPermissionDelegate; - if (oldDelegate != null && oldDelegate.getDelegatedUid() != uid) { - throw new SecurityException( - "Shell can delegate permissions only to one UID at a time"); - } - final ShellDelegate delegate = new ShellDelegate(uid, packageName, permissionNames); - setCheckPermissionDelegateLocked(delegate); - } - } - - private void stopShellPermissionIdentityDelegationInternal() { - synchronized (mLock) { - setCheckPermissionDelegateLocked(null); - } - } - - @Nullable - private List getDelegatedShellPermissionsInternal() { - synchronized (mLock) { - if (mCheckPermissionDelegate == null) { - return Collections.EMPTY_LIST; - } - return mCheckPermissionDelegate.getDelegatedPermissionNames(); - } - } - - private void setCheckPermissionDelegateLocked(@Nullable CheckPermissionDelegate delegate) { - if (delegate != null || mCheckPermissionDelegate != null) { - PackageManager.invalidatePackageInfoCache(); - } - mCheckPermissionDelegate = delegate; - } - /** * If the app is updated, and has scoped storage permissions, then it is possible that the * app updated in an attempt to get unscoped storage. If so, revoke all storage permissions. @@ -2272,8 +2057,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { private void revokeRuntimePermissionsIfGroupChangedInternal(@NonNull AndroidPackage newPackage, @NonNull AndroidPackage oldPackage) { final int numOldPackagePermissions = ArrayUtils.size(oldPackage.getPermissions()); - final ArrayMap oldPermissionNameToGroupName - = new ArrayMap<>(numOldPackagePermissions); + final ArrayMap oldPermissionNameToGroupName = + new ArrayMap<>(numOldPackagePermissions); for (int i = 0; i < numOldPackagePermissions; i++) { final ParsedPermission permission = oldPackage.getPermissions().get(i); @@ -2306,8 +2091,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { mPackageManagerInt.forEachPackage(pkg -> { final String packageName = pkg.getPackageName(); for (final int userId : userIds) { - final int permissionState = checkPermission(packageName, permissionName, - userId); + final int permissionState = + checkPermission(packageName, permissionName, userId); if (permissionState == PackageManager.PERMISSION_GRANTED) { EventLog.writeEvent(0x534e4554, "72710897", newPackage.getUid(), @@ -2362,7 +2147,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return; } for (final int userId : userIds) { - final int permissionState = checkPermissionImpl(packageName, permName, + final int permissionState = checkPermission(packageName, permName, userId); final int flags = getPermissionFlags(packageName, permName, userId); final int flagMask = FLAG_PERMISSION_SYSTEM_FIXED @@ -2502,9 +2287,9 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { private void removeAllPermissionsInternal(@NonNull AndroidPackage pkg) { synchronized (mLock) { - int N = ArrayUtils.size(pkg.getPermissions()); + int n = ArrayUtils.size(pkg.getPermissions()); StringBuilder r = null; - for (int i=0; i newImplicitPermissions, @UserIdInt int userId, @NonNull int[] updatedUserIds) { @@ -3356,102 +3145,6 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return SystemConfig.getInstance().getSplitPermissions(); } - @NonNull - private OneTimePermissionUserManager getOneTimePermissionUserManager(@UserIdInt int userId) { - OneTimePermissionUserManager oneTimePermissionUserManager; - synchronized (mLock) { - oneTimePermissionUserManager = mOneTimePermissionUserManagers.get(userId); - if (oneTimePermissionUserManager != null) { - return oneTimePermissionUserManager; - } - } - // We cannot create a new instance of OneTimePermissionUserManager while holding our own - // lock, which may lead to a deadlock with the package manager lock. So we do it in a - // retry-like way, and just discard the newly created instance if someone else managed to be - // a little bit faster than us when we dropped our own lock. - final OneTimePermissionUserManager newOneTimePermissionUserManager = - new OneTimePermissionUserManager(mContext.createContextAsUser(UserHandle.of(userId), - /*flags*/ 0)); - synchronized (mLock) { - oneTimePermissionUserManager = mOneTimePermissionUserManagers.get(userId); - if (oneTimePermissionUserManager != null) { - return oneTimePermissionUserManager; - } - oneTimePermissionUserManager = newOneTimePermissionUserManager; - mOneTimePermissionUserManagers.put(userId, oneTimePermissionUserManager); - } - oneTimePermissionUserManager.registerUninstallListener(); - return oneTimePermissionUserManager; - } - - @Override - public void startOneTimePermissionSession(String packageName, @UserIdInt int userId, - long timeoutMillis, int importanceToResetTimer, int importanceToKeepSessionAlive) { - mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS, - "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS - + " to register permissions as one time."); - Objects.requireNonNull(packageName); - - final long token = Binder.clearCallingIdentity(); - try { - getOneTimePermissionUserManager(userId).startPackageOneTimeSession(packageName, - timeoutMillis, importanceToResetTimer, importanceToKeepSessionAlive); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void stopOneTimePermissionSession(String packageName, @UserIdInt int userId) { - mContext.enforceCallingPermission(Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS, - "Must hold " + Manifest.permission.MANAGE_ONE_TIME_PERMISSION_SESSIONS - + " to remove permissions as one time."); - Objects.requireNonNull(packageName); - - final long token = Binder.clearCallingIdentity(); - try { - getOneTimePermissionUserManager(userId).stopPackageOneTimeSession(packageName); - } finally { - Binder.restoreCallingIdentity(token); - } - } - - @Override - public void registerAttributionSource(@NonNull AttributionSourceState source) { - mAttributionSourceRegistry - .registerAttributionSource(new AttributionSource(source)); - } - - @Override - public boolean isRegisteredAttributionSource(@NonNull AttributionSourceState source) { - return mAttributionSourceRegistry - .isRegisteredAttributionSource(new AttributionSource(source)); - } - - @Override - public List getAutoRevokeExemptionRequestedPackages(int userId) { - return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISCOURAGED, userId); - } - - @Override - public List getAutoRevokeExemptionGrantedPackages(int userId) { - return getPackagesWithAutoRevokePolicy(AUTO_REVOKE_DISALLOWED, userId); - } - - @NonNull - private List getPackagesWithAutoRevokePolicy(int autoRevokePolicy, int userId) { - mContext.enforceCallingPermission(Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY, - "Must hold " + Manifest.permission.ADJUST_RUNTIME_PERMISSIONS_POLICY); - - List result = new ArrayList<>(); - mPackageManagerInt.forEachInstalledPackage(pkg -> { - if (pkg.getAutoRevokePermissions() == autoRevokePolicy) { - result.add(pkg.getPackageName()); - } - }, userId); - return result; - } - private static boolean isCompatPlatformPermissionForPackage(String perm, AndroidPackage pkg) { boolean allowed = false; for (int i = 0, size = CompatibilityPermissionInfo.COMPAT_PERMS.length; i < size; i++) { @@ -3869,7 +3562,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } private void setAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, - @Nullable List permissions, @PermissionWhitelistFlags int allowlistFlags, + @Nullable List permissions, + @PackageManager.PermissionWhitelistFlags int allowlistFlags, @UserIdInt int userId) { ArraySet oldGrantedRestrictedPermissions = null; boolean updatePermissions = false; @@ -4391,7 +4085,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { return; } - if (checkPermissionImpl(pName, permissionName, userId) + if (checkPermission(pName, permissionName, userId) == PackageManager.PERMISSION_GRANTED) { try { revokeRuntimePermissionInternal( @@ -4410,6 +4104,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } } + /** * Update which app owns a permission trees. * @@ -4609,7 +4304,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private void systemReady() { + @Override + public void onSystemReady() { // Now that we've scanned all packages, and granted any default // permissions, ensure permissions are updated. Beware of dragons if you // try optimizing this. @@ -4673,7 +4369,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { 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); + log.addTaggedData(MetricsProto.MetricsEvent.FIELD_PERMISSION, name); mMetricsLogger.write(log); } @@ -4714,7 +4410,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private void readLegacyPermissionState() { + @Override + public void readLegacyPermissionStateTEMP() { final int[] userIds = getAllUserIds(); mPackageManagerInt.forEachPackageSetting(ps -> { final int appId = ps.getAppId(); @@ -4751,7 +4448,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private void writeLegacyPermissionState() { + @Override + public void writeLegacyPermissionStateTEMP() { final int[] userIds; synchronized (mLock) { userIds = mState.getUserIds(); @@ -4798,7 +4496,9 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { }); } - private void readLegacyPermissions(@NonNull LegacyPermissionSettings legacyPermissionSettings) { + @Override + public void readLegacyPermissionsTEMP( + @NonNull LegacyPermissionSettings legacyPermissionSettings) { for (int readPermissionOrPermissionTree = 0; readPermissionOrPermissionTree < 2; readPermissionOrPermissionTree++) { final List legacyPermissions = readPermissionOrPermissionTree == 0 @@ -4832,7 +4532,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private void writeLegacyPermissions( + @Override + public void writeLegacyPermissionsTEMP( @NonNull LegacyPermissionSettings legacyPermissionSettings) { for (int writePermissionOrPermissionTree = 0; writePermissionOrPermissionTree < 2; writePermissionOrPermissionTree++) { @@ -4986,6 +4687,9 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { final int autoRevokePermissionsMode = params.getAutoRevokePermissionsMode(); if (autoRevokePermissionsMode == AppOpsManager.MODE_ALLOWED || autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED) { + // TODO: theianchen Bug: 182523293 + // We should move this portion of code that's calling + // setAutoRevokeExemptedInternal() into the old PMS setAutoRevokeExemptedInternal(pkg, autoRevokePermissionsMode == AppOpsManager.MODE_IGNORED, userId); } @@ -4995,7 +4699,7 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { private void addAllowlistedRestrictedPermissionsInternal(@NonNull AndroidPackage pkg, @NonNull List allowlistedRestrictedPermissions, - @PermissionWhitelistFlags int flags, @UserIdInt int userId) { + @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId) { List permissions = getAllowlistedRestrictedPermissionsInternal(pkg, flags, userId); if (permissions != null) { ArraySet permissionSet = new ArraySet<>(permissions); @@ -5049,7 +4753,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } @NonNull - private List getLegacyPermissions() { + @Override + public List getLegacyPermissions() { synchronized (mLock) { final List legacyPermissions = new ArrayList<>(); for (final Permission permission : mRegistry.getPermissions()) { @@ -5062,8 +4767,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - @NonNull - private Map> getAllAppOpPermissionPackages() { + @Override + public Map> getAllAppOpPermissionPackages() { synchronized (mLock) { final ArrayMap> appOpPermissionPackages = mRegistry.getAllAppOpPermissionPackages(); @@ -5079,7 +4784,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } @NonNull - private LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { + @Override + public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { final LegacyPermissionState legacyState = new LegacyPermissionState(); synchronized (mLock) { final int[] userIds = mState.getUserIds(); @@ -5108,7 +4814,8 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } @NonNull - private int[] getGidsForUid(int uid) { + @Override + public int[] getGidsForUid(int uid) { final int appId = UserHandle.getAppId(uid); final int userId = UserHandle.getUserId(uid); synchronized (mLock) { @@ -5122,261 +4829,155 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } - private class PermissionManagerServiceInternalImpl implements PermissionManagerServiceInternal { - @Override - public int checkPermission(@NonNull String packageName, @NonNull String permissionName, - @UserIdInt int userId) { - return PermissionManagerServiceImpl.this.checkPermission(packageName, permissionName, - userId); - } + @Override + public boolean isPermissionsReviewRequired(@NonNull String packageName, + @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + // TODO(b/173235285): Some caller may pass USER_ALL as userId. + //Preconditions.checkArgumentNonnegative(userId, "userId"); + return isPermissionsReviewRequiredInternal(packageName, userId); + } - @Override - public int checkUidPermission(int uid, @NonNull String permissionName) { - return PermissionManagerServiceImpl.this.checkUidPermission(uid, permissionName); - } + @NonNull + @Override + public Set getGrantedPermissions(@NonNull String packageName, + @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + return getGrantedPermissionsInternal(packageName, userId); + } - @Override - public void onSystemReady() { - PermissionManagerServiceImpl.this.systemReady(); - } + @NonNull + @Override + public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { + Objects.requireNonNull(permissionName, "permissionName"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + return getPermissionGidsInternal(permissionName, userId); + } - @Override - public boolean isPermissionsReviewRequired(@NonNull String packageName, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - // TODO(b/173235285): Some caller may pass USER_ALL as userId. - //Preconditions.checkArgumentNonnegative(userId, "userId"); - return isPermissionsReviewRequiredInternal(packageName, userId); - } + @NonNull + @Override + public String[] getAppOpPermissionPackages(@NonNull String permissionName) { + Objects.requireNonNull(permissionName, "permissionName"); + return PermissionManagerServiceImpl.this.getAppOpPermissionPackagesInternal(permissionName); + } - @Override - public void readLegacyPermissionStateTEMP() { - PermissionManagerServiceImpl.this.readLegacyPermissionState(); - } - @Override - public void writeLegacyPermissionStateTEMP() { - PermissionManagerServiceImpl.this.writeLegacyPermissionState(); - } - @Override - public void onUserRemoved(@UserIdInt int userId) { - Preconditions.checkArgumentNonNegative(userId, "userId"); - PermissionManagerServiceImpl.this.onUserRemoved(userId); - } - @NonNull - @Override - public Set getGrantedPermissions(@NonNull String packageName, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - return getGrantedPermissionsInternal(packageName, userId); - } - @NonNull - @Override - public int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId) { - Objects.requireNonNull(permissionName, "permissionName"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - return getPermissionGidsInternal(permissionName, userId); - } - @NonNull - @Override - public String[] getAppOpPermissionPackages(@NonNull String permissionName) { - Objects.requireNonNull(permissionName, "permissionName"); - return PermissionManagerServiceImpl.this.getAppOpPermissionPackagesInternal(permissionName); - } - @Override - public void onStorageVolumeMounted(@Nullable String volumeUuid, boolean fingerprintChanged) { - updateAllPermissions(volumeUuid, fingerprintChanged); - } - @Override - public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) { - Objects.requireNonNull(pkg, "pkg"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - resetRuntimePermissionsInternal(pkg, userId); - } + @Override + public void onStorageVolumeMounted(@Nullable String volumeUuid, boolean fingerprintChanged) { + updateAllPermissions(volumeUuid, fingerprintChanged); + } - @Override - public Permission getPermissionTEMP(String permName) { - synchronized (mLock) { - return mRegistry.getPermission(permName); - } + @Override + public void resetRuntimePermissions(@NonNull AndroidPackage pkg, @UserIdInt int userId) { + Objects.requireNonNull(pkg, "pkg"); + Preconditions.checkArgumentNonNegative(userId, "userId"); + resetRuntimePermissionsInternal(pkg, userId); + } + + @Override + public Permission getPermissionTEMP(String permName) { + synchronized (mLock) { + return mRegistry.getPermission(permName); } + } - @Override - public @NonNull ArrayList getAllPermissionsWithProtection( - @PermissionInfo.Protection int protection) { - ArrayList matchingPermissions = new ArrayList<>(); + @NonNull + @Override + public ArrayList getAllPermissionsWithProtection( + @PermissionInfo.Protection int protection) { + ArrayList matchingPermissions = new ArrayList<>(); - synchronized (mLock) { - for (final Permission permission : mRegistry.getPermissions()) { - if (permission.getProtection() == protection) { - matchingPermissions.add(permission.generatePermissionInfo(0)); - } + synchronized (mLock) { + for (final Permission permission : mRegistry.getPermissions()) { + if (permission.getProtection() == protection) { + matchingPermissions.add(permission.generatePermissionInfo(0)); } } - - return matchingPermissions; } - @Override - public @NonNull ArrayList getAllPermissionsWithProtectionFlags( - @PermissionInfo.ProtectionFlags int protectionFlags) { - ArrayList matchingPermissions = new ArrayList<>(); + return matchingPermissions; + } - synchronized (mLock) { - for (final Permission permission : mRegistry.getPermissions()) { - if ((permission.getProtectionFlags() & protectionFlags) == protectionFlags) { - matchingPermissions.add(permission.generatePermissionInfo(0)); - } + @NonNull + @Override + public ArrayList getAllPermissionsWithProtectionFlags( + @PermissionInfo.ProtectionFlags int protectionFlags) { + ArrayList matchingPermissions = new ArrayList<>(); + + synchronized (mLock) { + for (final Permission permission : mRegistry.getPermissions()) { + if ((permission.getProtectionFlags() & protectionFlags) == protectionFlags) { + matchingPermissions.add(permission.generatePermissionInfo(0)); } } - - return matchingPermissions; } - @Nullable - @Override - public byte[] backupRuntimePermissions(@UserIdInt int userId) { - Preconditions.checkArgumentNonNegative(userId, "userId"); - return PermissionManagerServiceImpl.this.backupRuntimePermissions(userId); + return matchingPermissions; + } + + @Override + public void onUserCreated(@UserIdInt int userId) { + Preconditions.checkArgumentNonNegative(userId, "userId"); + // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG + updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true); + } + + @Override + public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg) { + Objects.requireNonNull(pkg); + onPackageAddedInternal(pkg, isInstantApp, oldPkg); + } + + @Override + public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, + @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, + @UserIdInt int userId) { + Objects.requireNonNull(pkg, "pkg"); + Objects.requireNonNull(params, "params"); + Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM + || userId == UserHandle.USER_ALL, "userId"); + final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds() + : new int[] { userId }; + onPackageInstalledInternal(pkg, previousAppId, params, userIds); + } + + @Override + public void onPackageRemoved(@NonNull AndroidPackage pkg) { + Objects.requireNonNull(pkg); + onPackageRemovedInternal(pkg); + } + + @Override + public void onPackageUninstalled(@NonNull String packageName, int appId, + @Nullable AndroidPackage pkg, @NonNull List sharedUserPkgs, + @UserIdInt int userId) { + Objects.requireNonNull(packageName, "packageName"); + Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs"); + Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM + || userId == UserHandle.USER_ALL, "userId"); + final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds() + : new int[] { userId }; + onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds); + } + + private boolean setAutoRevokeExemptedInternal(@NonNull AndroidPackage pkg, boolean exempted, + @UserIdInt int userId) { + final int packageUid = UserHandle.getUid(userId, pkg.getUid()); + if (mAppOpsManager.checkOpNoThrow(AppOpsManager.OP_AUTO_REVOKE_MANAGED_BY_INSTALLER, + packageUid, pkg.getPackageName()) != MODE_ALLOWED) { + // Allowlist user set - don't override + return false; } - @Override - public void restoreRuntimePermissions(@NonNull byte[] backup, @UserIdInt int userId) { - Objects.requireNonNull(backup, "backup"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - PermissionManagerServiceImpl.this.restoreRuntimePermissions(backup, userId); - } - - @Override - public void restoreDelayedRuntimePermissions(@NonNull String packageName, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - Preconditions.checkArgumentNonNegative(userId, "userId"); - PermissionManagerServiceImpl.this.restoreDelayedRuntimePermissions(packageName, userId); - } - - @Override - public void addOnRuntimePermissionStateChangedListener( - OnRuntimePermissionStateChangedListener listener) { - PermissionManagerServiceImpl.this.addOnRuntimePermissionStateChangedListener( - listener); - } - - @Override - public void removeOnRuntimePermissionStateChangedListener( - OnRuntimePermissionStateChangedListener listener) { - PermissionManagerServiceImpl.this.removeOnRuntimePermissionStateChangedListener( - listener); - } - - @Override - public void startShellPermissionIdentityDelegation(int uid, @NonNull String packageName, - @Nullable List permissionNames) { - Objects.requireNonNull(packageName, "packageName"); - startShellPermissionIdentityDelegationInternal(uid, packageName, permissionNames); - } - - @Override - public void stopShellPermissionIdentityDelegation() { - stopShellPermissionIdentityDelegationInternal(); - } - - @Override - @NonNull - public List getDelegatedShellPermissions() { - return getDelegatedShellPermissionsInternal(); - } - - @Override - public void onUserCreated(@UserIdInt int userId) { - Preconditions.checkArgumentNonNegative(userId, "userId"); - // NOTE: This adds UPDATE_PERMISSIONS_REPLACE_PKG - updateAllPermissions(StorageManager.UUID_PRIVATE_INTERNAL, true); - } - - @Override - public void readLegacyPermissionsTEMP( - @NonNull LegacyPermissionSettings legacyPermissionSettings) { - PermissionManagerServiceImpl.this.readLegacyPermissions(legacyPermissionSettings); - } - - @Override - public void writeLegacyPermissionsTEMP( - @NonNull LegacyPermissionSettings legacyPermissionSettings) { - PermissionManagerServiceImpl.this.writeLegacyPermissions(legacyPermissionSettings); - } - - @Override - public void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, - @Nullable AndroidPackage oldPkg) { - Objects.requireNonNull(pkg); - onPackageAddedInternal(pkg, isInstantApp, oldPkg); - } - - @Override - public void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, - @NonNull PackageInstalledParams params, @UserIdInt int userId) { - Objects.requireNonNull(pkg, "pkg"); - Objects.requireNonNull(params, "params"); - Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM - || userId == UserHandle.USER_ALL, "userId"); - final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds() - : new int[] { userId }; - onPackageInstalledInternal(pkg, previousAppId, params, userIds); - } - - @Override - public void onPackageRemoved(@NonNull AndroidPackage pkg) { - Objects.requireNonNull(pkg); - onPackageRemovedInternal(pkg); - } - - @Override - public void onPackageUninstalled(@NonNull String packageName, int appId, - @Nullable AndroidPackage pkg, @NonNull List sharedUserPkgs, - @UserIdInt int userId) { - Objects.requireNonNull(packageName, "packageName"); - Objects.requireNonNull(sharedUserPkgs, "sharedUserPkgs"); - Preconditions.checkArgument(userId >= UserHandle.USER_SYSTEM - || userId == UserHandle.USER_ALL, "userId"); - final int[] userIds = userId == UserHandle.USER_ALL ? getAllUserIds() - : new int[] { userId }; - onPackageUninstalledInternal(packageName, appId, pkg, sharedUserPkgs, userIds); - } - - @NonNull - @Override - public List getLegacyPermissions() { - return PermissionManagerServiceImpl.this.getLegacyPermissions(); - } - - @NonNull - @Override - public Map> getAllAppOpPermissionPackages() { - return PermissionManagerServiceImpl.this.getAllAppOpPermissionPackages(); - } - - @NonNull - @Override - public LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId) { - return PermissionManagerServiceImpl.this.getLegacyPermissionState(appId); - } - - @NonNull - @Override - public int[] getGidsForUid(int uid) { - return PermissionManagerServiceImpl.this.getGidsForUid(uid); - } - - @Override - public void setHotwordDetectionServiceProvider(HotwordDetectionServiceProvider provider) { - mHotwordDetectionServiceProvider = provider; - } - - @Override - public HotwordDetectionServiceProvider getHotwordDetectionServiceProvider() { - return mHotwordDetectionServiceProvider; + final long identity = Binder.clearCallingIdentity(); + try { + mAppOpsManager.setMode(AppOpsManager.OP_AUTO_REVOKE_PERMISSIONS_IF_UNUSED, packageUid, + pkg.getPackageName(), exempted ? MODE_IGNORED : MODE_ALLOWED); + } finally { + Binder.restoreCallingIdentity(identity); } + return true; } /** @@ -5456,921 +5057,4 @@ public class PermissionManagerServiceImpl extends IPermissionManager.Stub { } } } - - /** - * Interface to intercept permission checks and optionally pass through to the original - * implementation. - */ - private interface CheckPermissionDelegate { - /** - * Get the UID whose permission checks is being delegated. - * - * @return the UID - */ - int getDelegatedUid(); - - /** - * Check whether the given package has been granted the specified permission. - * - * @param packageName the name of the package to be checked - * @param permissionName the name of the permission to be checked - * @param userId the user ID - * @param superImpl the original implementation that can be delegated to - * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has - * the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise - * - * @see android.content.pm.PackageManager#checkPermission(String, String) - */ - int checkPermission(@NonNull String packageName, @NonNull String permissionName, - @UserIdInt int userId, - @NonNull TriFunction superImpl); - - /** - * Check whether the given UID has been granted the specified permission. - * - * @param uid the UID to be checked - * @param permissionName the name of the permission to be checked - * @param superImpl the original implementation that can be delegated to - * @return {@link android.content.pm.PackageManager#PERMISSION_GRANTED} if the package has - * the permission, or {@link android.content.pm.PackageManager#PERMISSION_DENIED} otherwise - */ - int checkUidPermission(int uid, @NonNull String permissionName, - BiFunction superImpl); - - /** - * @return list of delegated permissions - */ - List getDelegatedPermissionNames(); - } - - private class ShellDelegate implements CheckPermissionDelegate { - private final int mDelegatedUid; - @NonNull - private final String mDelegatedPackageName; - @Nullable - private final List mDelegatedPermissionNames; - - public ShellDelegate(int delegatedUid, @NonNull String delegatedPackageName, - @Nullable List delegatedPermissionNames) { - mDelegatedUid = delegatedUid; - mDelegatedPackageName = delegatedPackageName; - mDelegatedPermissionNames = delegatedPermissionNames; - } - - @Override - public int getDelegatedUid() { - return mDelegatedUid; - } - - @Override - public int checkPermission(@NonNull String packageName, @NonNull String permissionName, - int userId, @NonNull TriFunction superImpl) { - if (mDelegatedPackageName.equals(packageName) - && isDelegatedPermission(permissionName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply("com.android.shell", permissionName, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - return superImpl.apply(packageName, permissionName, userId); - } - - @Override - public int checkUidPermission(int uid, @NonNull String permissionName, - @NonNull BiFunction superImpl) { - if (uid == mDelegatedUid && isDelegatedPermission(permissionName)) { - final long identity = Binder.clearCallingIdentity(); - try { - return superImpl.apply(Process.SHELL_UID, permissionName); - } finally { - Binder.restoreCallingIdentity(identity); - } - } - return superImpl.apply(uid, permissionName); - } - - @Override - public List getDelegatedPermissionNames() { - return mDelegatedPermissionNames == null - ? null - : new ArrayList<>(mDelegatedPermissionNames); - } - - private boolean isDelegatedPermission(@NonNull String permissionName) { - // null permissions means all permissions are targeted - return mDelegatedPermissionNames == null - || mDelegatedPermissionNames.contains(permissionName); - } - } - - private static final class AttributionSourceRegistry { - private final Object mLock = new Object(); - - private final Context mContext; - - AttributionSourceRegistry(@NonNull Context context) { - mContext = context; - } - - private final WeakHashMap mAttributions = new WeakHashMap<>(); - - public void registerAttributionSource(@NonNull AttributionSource source) { - // Here we keep track of attribution sources that were created by an app - // from an attribution chain that called into the app and the apps's - // own attribution source. An app can register an attribution chain up - // to itself inclusive if and only if it is adding a node for itself which - // optionally points to an attribution chain that was created by each - // preceding app recursively up to the beginning of the chain. - // The only special case is when the first app in the attribution chain - // creates a source that points to another app (not a chain of apps). We - // allow this even if the source the app points to is not registered since - // in app ops we allow every app to blame every other app (untrusted if not - // holding a special permission). - // This technique ensures that a bad actor in the middle of the attribution - // chain can neither prepend nor append an invalid attribution sequence, i.e. - // a sequence that is not constructed by trusted sources up to the that bad - // actor's app. - // Note that passing your attribution source to another app means you allow - // it to blame private data access on your app. This can be mediated by the OS - // in, which case security is already enforced; by other app's code running in - // your process calling into the other app, in which case it can already access - // the private data in your process; or by you explicitly calling to another - // app passing the source, in which case you must trust the other side; - - final int callingUid = Binder.getCallingUid(); - if (source.getUid() != callingUid && mContext.checkPermission( - Manifest.permission.UPDATE_APP_OPS_STATS, /*pid*/ -1, callingUid) - != PackageManager.PERMISSION_GRANTED) { - throw new SecurityException("Cannot register attribution source for uid:" - + source.getUid() + " from uid:" + callingUid); - } - - final PackageManagerInternal packageManagerInternal = LocalServices.getService( - PackageManagerInternal.class); - if (packageManagerInternal.getPackageUid(source.getPackageName(), 0, - UserHandle.getUserId(callingUid)) != source.getUid()) { - throw new SecurityException("Cannot register attribution source for package:" - + source.getPackageName() + " from uid:" + callingUid); - } - - final AttributionSource next = source.getNext(); - if (next != null && next.getNext() != null - && !isRegisteredAttributionSource(next)) { - throw new SecurityException("Cannot register forged attribution source:" - + source); - } - - synchronized (mLock) { - mAttributions.put(source.getToken(), source); - } - } - - public boolean isRegisteredAttributionSource(@NonNull AttributionSource source) { - synchronized (mLock) { - final AttributionSource cachedSource = mAttributions.get(source.getToken()); - if (cachedSource != null) { - return cachedSource.equals(source); - } - return false; - } - } - } - - /** - * TODO: We need to consolidate these APIs either on PermissionManager or an extension - * object or a separate PermissionChecker service in context. The impartant part is to - * keep a single impl that is exposed to Java and native. We are not sure about the - * API shape so let is soak a bit. - */ - private static final class PermissionCheckerService extends IPermissionChecker.Stub { - // Cache for platform defined runtime permissions to avoid multi lookup (name -> info) - private static final ConcurrentHashMap sPlatformPermissions - = new ConcurrentHashMap<>(); - - private static final AtomicInteger sAttributionChainIds = new AtomicInteger(0); - - private final @NonNull Context mContext; - private final @NonNull AppOpsManager mAppOpsManager; - private final @NonNull PermissionManagerServiceInternal mPermissionManagerServiceInternal; - - PermissionCheckerService(@NonNull Context context) { - mContext = context; - mAppOpsManager = mContext.getSystemService(AppOpsManager.class); - mPermissionManagerServiceInternal = - LocalServices.getService(PermissionManagerServiceInternal.class); - } - - @Override - @PermissionCheckerManager.PermissionResult - public int checkPermission(@NonNull String permission, - @NonNull AttributionSourceState attributionSourceState, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery, boolean fromDatasource, - int attributedOp) { - Objects.requireNonNull(permission); - Objects.requireNonNull(attributionSourceState); - final AttributionSource attributionSource = new AttributionSource( - attributionSourceState); - final int result = checkPermission(mContext, mPermissionManagerServiceInternal, - permission, attributionSource, message, forDataDelivery, startDataDelivery, - fromDatasource, attributedOp); - // Finish any started op if some step in the attribution chain failed. - if (startDataDelivery && result != PermissionChecker.PERMISSION_GRANTED - && result != PermissionChecker.PERMISSION_SOFT_DENIED) { - if (attributedOp == AppOpsManager.OP_NONE) { - finishDataDelivery(AppOpsManager.permissionToOpCode(permission), - attributionSource.asState(), fromDatasource); - } else { - finishDataDelivery(attributedOp, attributionSource.asState(), fromDatasource); - } - } - return result; - } - - @Override - public void finishDataDelivery(int op, - @NonNull AttributionSourceState attributionSourceState, boolean fromDataSource) { - finishDataDelivery(mContext, op, attributionSourceState, - fromDataSource); - } - - private static void finishDataDelivery(Context context, int op, - @NonNull AttributionSourceState attributionSourceState, boolean fromDatasource) { - Objects.requireNonNull(attributionSourceState); - AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - - if (op == AppOpsManager.OP_NONE) { - return; - } - - AttributionSource current = new AttributionSource(attributionSourceState); - AttributionSource next = null; - - while (true) { - final boolean skipCurrentFinish = (fromDatasource || next != null); - - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (!(fromDatasource && current.asState() == attributionSourceState) - && next != null && !current.isTrusted(context)) { - return; - } - - // The access is for oneself if this is the single receiver of data - // after the data source or if this is the single attribution source - // in the chain if not from a datasource. - final boolean singleReceiverFromDatasource = (fromDatasource - && current.asState() == attributionSourceState && next != null - && next.getNext() == null); - final boolean selfAccess = singleReceiverFromDatasource || next == null; - - final AttributionSource accessorSource = (!singleReceiverFromDatasource) - ? current : next; - - if (selfAccess) { - final String resolvedPackageName = resolvePackageName(context, accessorSource); - if (resolvedPackageName == null) { - return; - } - appOpsManager.finishOp(accessorSource.getToken(), op, - accessorSource.getUid(), resolvedPackageName, - accessorSource.getAttributionTag()); - } else { - final AttributionSource resolvedAttributionSource = - resolveAttributionSource(context, accessorSource); - if (resolvedAttributionSource.getPackageName() == null) { - return; - } - appOpsManager.finishProxyOp(AppOpsManager.opToPublicName(op), - resolvedAttributionSource, skipCurrentFinish); - } - - if (next == null || next.getNext() == null) { - return; - } - - RegisteredAttribution registered = - sRunningAttributionSources.remove(current.getToken()); - if (registered != null) { - registered.unregister(); - } - current = next; - } - } - - @Override - @PermissionCheckerManager.PermissionResult - public int checkOp(int op, AttributionSourceState attributionSource, - String message, boolean forDataDelivery, boolean startDataDelivery) { - int result = checkOp(mContext, op, mPermissionManagerServiceInternal, - new AttributionSource(attributionSource), message, forDataDelivery, - startDataDelivery); - if (result != PermissionChecker.PERMISSION_GRANTED && startDataDelivery) { - // Finish any started op if some step in the attribution chain failed. - finishDataDelivery(op, attributionSource, /*fromDatasource*/ false); - } - return result; - } - - @PermissionCheckerManager.PermissionResult - private static int checkPermission(@NonNull Context context, - @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, - @NonNull String permission, @NonNull AttributionSource attributionSource, - @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, - boolean fromDatasource, int attributedOp) { - PermissionInfo permissionInfo = sPlatformPermissions.get(permission); - - if (permissionInfo == null) { - try { - permissionInfo = context.getPackageManager().getPermissionInfo(permission, 0); - if (PLATFORM_PACKAGE_NAME.equals(permissionInfo.packageName)) { - // Double addition due to concurrency is fine - the backing - // store is concurrent. - sPlatformPermissions.put(permission, permissionInfo); - } - } catch (PackageManager.NameNotFoundException ignored) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - } - - if (permissionInfo.isAppOp()) { - return checkAppOpPermission(context, permissionManagerServiceInt, permission, - attributionSource, message, forDataDelivery, fromDatasource); - } - if (permissionInfo.isRuntime()) { - return checkRuntimePermission(context, permissionManagerServiceInt, permission, - attributionSource, message, forDataDelivery, startDataDelivery, - fromDatasource, attributedOp); - } - - if (!fromDatasource && !checkPermission(context, permissionManagerServiceInt, - permission, attributionSource.getUid(), - attributionSource.getRenouncedPermissions())) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - if (attributionSource.getNext() != null) { - return checkPermission(context, permissionManagerServiceInt, permission, - attributionSource.getNext(), message, forDataDelivery, startDataDelivery, - /*fromDatasource*/ false, attributedOp); - } - - return PermissionChecker.PERMISSION_GRANTED; - } - - @PermissionCheckerManager.PermissionResult - private static int checkAppOpPermission(@NonNull Context context, - @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, - @NonNull String permission, @NonNull AttributionSource attributionSource, - @Nullable String message, boolean forDataDelivery, boolean fromDatasource) { - final int op = AppOpsManager.permissionToOpCode(permission); - if (op < 0) { - Slog.wtf(LOG_TAG, "Appop permission " + permission + " with no app op defined!"); - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - AttributionSource current = attributionSource; - AttributionSource next = null; - - while (true) { - final boolean skipCurrentChecks = (fromDatasource || next != null); - - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (!(fromDatasource && current.equals(attributionSource)) - && next != null && !current.isTrusted(context)) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - // The access is for oneself if this is the single receiver of data - // after the data source or if this is the single attribution source - // in the chain if not from a datasource. - final boolean singleReceiverFromDatasource = (fromDatasource - && current.equals(attributionSource) && next != null - && next.getNext() == null); - final boolean selfAccess = singleReceiverFromDatasource || next == null; - - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, /*startDataDelivery*/ false, skipCurrentChecks, - selfAccess, singleReceiverFromDatasource, AppOpsManager.OP_NONE, - AppOpsManager.ATTRIBUTION_FLAGS_NONE, AppOpsManager.ATTRIBUTION_FLAGS_NONE, - AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE); - - switch (opMode) { - case AppOpsManager.MODE_IGNORED: - case AppOpsManager.MODE_ERRORED: { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - case AppOpsManager.MODE_DEFAULT: { - if (!skipCurrentChecks && !checkPermission(context, - permissionManagerServiceInt, permission, attributionSource.getUid(), - attributionSource.getRenouncedPermissions())) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - if (next != null && !checkPermission(context, permissionManagerServiceInt, - permission, next.getUid(), next.getRenouncedPermissions())) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - } - } - - if (next == null || next.getNext() == null) { - return PermissionChecker.PERMISSION_GRANTED; - } - - current = next; - } - } - - private static int checkRuntimePermission(@NonNull Context context, - @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, - @NonNull String permission, @NonNull AttributionSource attributionSource, - @Nullable String message, boolean forDataDelivery, boolean startDataDelivery, - boolean fromDatasource, int attributedOp) { - // Now let's check the identity chain... - final int op = AppOpsManager.permissionToOpCode(permission); - final int attributionChainId = - getAttributionChainId(startDataDelivery, attributionSource); - final boolean hasChain = attributionChainId != ATTRIBUTION_CHAIN_ID_NONE; - AttributionSource current = attributionSource; - AttributionSource next = null; - // We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and - // every attributionSource in the chain is registered with the system. - final boolean isChainStartTrusted = !hasChain || checkPermission(context, - permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(), - current.getRenouncedPermissions()); - - while (true) { - final boolean skipCurrentChecks = (fromDatasource || next != null); - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (!(fromDatasource && current.equals(attributionSource)) - && next != null && !current.isTrusted(context)) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - // If we already checked the permission for this one, skip the work - if (!skipCurrentChecks && !checkPermission(context, permissionManagerServiceInt, - permission, current.getUid(), current.getRenouncedPermissions())) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - if (next != null && !checkPermission(context, permissionManagerServiceInt, - permission, next.getUid(), next.getRenouncedPermissions())) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - if (op < 0) { - // Bg location is one-off runtime modifier permission and has no app op - if (sPlatformPermissions.contains(permission) - && !Manifest.permission.ACCESS_BACKGROUND_LOCATION.equals(permission)) { - Slog.wtf(LOG_TAG, "Platform runtime permission " + permission - + " with no app op defined!"); - } - if (next == null) { - return PermissionChecker.PERMISSION_GRANTED; - } - current = next; - continue; - } - - // The access is for oneself if this is the single receiver of data - // after the data source or if this is the single attribution source - // in the chain if not from a datasource. - final boolean singleReceiverFromDatasource = (fromDatasource - && current.equals(attributionSource) - && next != null && next.getNext() == null); - final boolean selfAccess = singleReceiverFromDatasource || next == null; - final boolean isLinkTrusted = isChainStartTrusted - && (current.isTrusted(context) || current.equals(attributionSource)) - && (next == null || next.isTrusted(context)); - - final int proxyAttributionFlags = (!skipCurrentChecks && hasChain) - ? resolveProxyAttributionFlags(attributionSource, current, fromDatasource, - startDataDelivery, selfAccess, isLinkTrusted) - : ATTRIBUTION_FLAGS_NONE; - final int proxiedAttributionFlags = hasChain ? resolveProxiedAttributionFlags( - attributionSource, next, fromDatasource, startDataDelivery, selfAccess, - isLinkTrusted) : ATTRIBUTION_FLAGS_NONE; - - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, - singleReceiverFromDatasource, attributedOp, proxyAttributionFlags, - proxiedAttributionFlags, attributionChainId); - - switch (opMode) { - case AppOpsManager.MODE_ERRORED: { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - case AppOpsManager.MODE_IGNORED: { - return PermissionChecker.PERMISSION_SOFT_DENIED; - } - } - - if (startDataDelivery) { - RegisteredAttribution registered = new RegisteredAttribution(context, op, - current, fromDatasource); - sRunningAttributionSources.put(current.getToken(), registered); - } - - if (next == null || next.getNext() == null) { - return PermissionChecker.PERMISSION_GRANTED; - } - - current = next; - } - } - - private static boolean checkPermission(@NonNull Context context, - @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, - @NonNull String permission, int uid, @NonNull Set renouncedPermissions) { - boolean permissionGranted = context.checkPermission(permission, /*pid*/ -1, - uid) == PackageManager.PERMISSION_GRANTED; - - // Override certain permissions checks for the HotwordDetectionService. This service is - // an isolated service, which ordinarily cannot hold permissions. - // There's probably a cleaner, more generalizable way to do this. For now, this is - // the only use case for this, so simply override here. - if (!permissionGranted - && Process.isIsolated(uid) // simple check which fails-fast for the common case - && (permission.equals(RECORD_AUDIO) - || permission.equals(CAPTURE_AUDIO_HOTWORD))) { - HotwordDetectionServiceProvider hotwordServiceProvider = - permissionManagerServiceInt.getHotwordDetectionServiceProvider(); - permissionGranted = hotwordServiceProvider != null - && uid == hotwordServiceProvider.getUid(); - } - - if (permissionGranted && renouncedPermissions.contains(permission) - && context.checkPermission(Manifest.permission.RENOUNCE_PERMISSIONS, - /*pid*/ -1, uid) == PackageManager.PERMISSION_GRANTED) { - return false; - } - return permissionGranted; - } - - private static @AttributionFlags int resolveProxyAttributionFlags( - @NonNull AttributionSource attributionChain, - @NonNull AttributionSource current, boolean fromDatasource, - boolean startDataDelivery, boolean selfAccess, boolean isTrusted) { - return resolveAttributionFlags(attributionChain, current, fromDatasource, - startDataDelivery, selfAccess, isTrusted, /*flagsForProxy*/ true); - } - - private static @AttributionFlags int resolveProxiedAttributionFlags( - @NonNull AttributionSource attributionChain, - @NonNull AttributionSource current, boolean fromDatasource, - boolean startDataDelivery, boolean selfAccess, boolean isTrusted) { - return resolveAttributionFlags(attributionChain, current, fromDatasource, - startDataDelivery, selfAccess, isTrusted, /*flagsForProxy*/ false); - } - - private static @AttributionFlags int resolveAttributionFlags( - @NonNull AttributionSource attributionChain, - @NonNull AttributionSource current, boolean fromDatasource, - boolean startDataDelivery, boolean selfAccess, boolean isTrusted, - boolean flagsForProxy) { - if (current == null || !startDataDelivery) { - return AppOpsManager.ATTRIBUTION_FLAGS_NONE; - } - int trustedFlag = isTrusted - ? AppOpsManager.ATTRIBUTION_FLAG_TRUSTED : AppOpsManager.ATTRIBUTION_FLAGS_NONE; - if (flagsForProxy) { - if (selfAccess) { - return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR; - } else if (!fromDatasource && current.equals(attributionChain)) { - return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR; - } - } else { - if (selfAccess) { - return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_RECEIVER; - } else if (fromDatasource && current.equals(attributionChain.getNext())) { - return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_ACCESSOR; - } else if (current.getNext() == null) { - return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_RECEIVER; - } - } - if (fromDatasource && current.equals(attributionChain)) { - return AppOpsManager.ATTRIBUTION_FLAGS_NONE; - } - return trustedFlag | AppOpsManager.ATTRIBUTION_FLAG_INTERMEDIARY; - } - - private static int checkOp(@NonNull Context context, @NonNull int op, - @NonNull PermissionManagerServiceInternal permissionManagerServiceInt, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery) { - if (op < 0 || attributionSource.getPackageName() == null) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - final int attributionChainId = - getAttributionChainId(startDataDelivery, attributionSource); - final boolean hasChain = attributionChainId != ATTRIBUTION_CHAIN_ID_NONE; - - AttributionSource current = attributionSource; - AttributionSource next = null; - - // We consider the chain trusted if the start node has UPDATE_APP_OPS_STATS, and - // every attributionSource in the chain is registered with the system. - final boolean isChainStartTrusted = !hasChain || checkPermission(context, - permissionManagerServiceInt, UPDATE_APP_OPS_STATS, current.getUid(), - current.getRenouncedPermissions()); - - while (true) { - final boolean skipCurrentChecks = (next != null); - next = current.getNext(); - - // If the call is from a datasource we need to vet only the chain before it. This - // way we can avoid the datasource creating an attribution context for every call. - if (next != null && !current.isTrusted(context)) { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - - // The access is for oneself if this is the single attribution source in the chain. - final boolean selfAccess = (next == null); - final boolean isLinkTrusted = isChainStartTrusted - && (current.isTrusted(context) || current.equals(attributionSource)) - && (next == null || next.isTrusted(context)); - - final int proxyAttributionFlags = (!skipCurrentChecks && hasChain) - ? resolveProxyAttributionFlags(attributionSource, current, - /*fromDatasource*/ false, startDataDelivery, selfAccess, - isLinkTrusted) : ATTRIBUTION_FLAGS_NONE; - final int proxiedAttributionFlags = hasChain ? resolveProxiedAttributionFlags( - attributionSource, next, /*fromDatasource*/ false, startDataDelivery, - selfAccess, isLinkTrusted) : ATTRIBUTION_FLAGS_NONE; - - final int opMode = performOpTransaction(context, op, current, message, - forDataDelivery, startDataDelivery, skipCurrentChecks, selfAccess, - /*fromDatasource*/ false, AppOpsManager.OP_NONE, proxyAttributionFlags, - proxiedAttributionFlags, attributionChainId); - - switch (opMode) { - case AppOpsManager.MODE_ERRORED: { - return PermissionChecker.PERMISSION_HARD_DENIED; - } - case AppOpsManager.MODE_IGNORED: { - return PermissionChecker.PERMISSION_SOFT_DENIED; - } - } - - if (next == null || next.getNext() == null) { - return PermissionChecker.PERMISSION_GRANTED; - } - - current = next; - } - } - - @SuppressWarnings("ConstantConditions") - private static int performOpTransaction(@NonNull Context context, int op, - @NonNull AttributionSource attributionSource, @Nullable String message, - boolean forDataDelivery, boolean startDataDelivery, boolean skipProxyOperation, - boolean selfAccess, boolean singleReceiverFromDatasource, int attributedOp, - @AttributionFlags int proxyAttributionFlags, - @AttributionFlags int proxiedAttributionFlags, int attributionChainId) { - // We cannot perform app ops transactions without a package name. In all relevant - // places we pass the package name but just in case there is a bug somewhere we - // do a best effort to resolve the package from the UID (pick first without a loss - // of generality - they are in the same security sandbox). - final AppOpsManager appOpsManager = context.getSystemService(AppOpsManager.class); - final AttributionSource accessorSource = (!singleReceiverFromDatasource) - ? attributionSource : attributionSource.getNext(); - if (!forDataDelivery) { - final String resolvedAccessorPackageName = resolvePackageName(context, - accessorSource); - if (resolvedAccessorPackageName == null) { - return AppOpsManager.MODE_ERRORED; - } - final int opMode = appOpsManager.unsafeCheckOpRawNoThrow(op, - accessorSource.getUid(), resolvedAccessorPackageName); - final AttributionSource next = accessorSource.getNext(); - if (!selfAccess && opMode == AppOpsManager.MODE_ALLOWED && next != null) { - final String resolvedNextPackageName = resolvePackageName(context, next); - if (resolvedNextPackageName == null) { - return AppOpsManager.MODE_ERRORED; - } - return appOpsManager.unsafeCheckOpRawNoThrow(op, next.getUid(), - resolvedNextPackageName); - } - return opMode; - } else if (startDataDelivery) { - final AttributionSource resolvedAttributionSource = resolveAttributionSource( - context, accessorSource); - if (resolvedAttributionSource.getPackageName() == null) { - return AppOpsManager.MODE_ERRORED; - } - // If the datasource is not in a trusted platform component then in would not - // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that - // an app is exposing runtime permission protected data but cannot blame others - // in a trusted way which would not properly show in permission usage UIs. - // As a fallback we note a proxy op that blames the app and the datasource. - int startedOp = op; - int checkedOpResult = MODE_ALLOWED; - int startedOpResult; - - // If the datasource wants to attribute to another app op we need to - // make sure the op for the permission and the attributed ops allow - // the operation. We return the less permissive of the two and check - // the permission op while start the attributed op. - if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) { - checkedOpResult = appOpsManager.checkOpNoThrow(op, - resolvedAttributionSource.getUid(), resolvedAttributionSource - .getPackageName()); - if (checkedOpResult == MODE_ERRORED) { - return checkedOpResult; - } - startedOp = attributedOp; - } - if (selfAccess) { - try { - startedOpResult = appOpsManager.startOpNoThrow( - resolvedAttributionSource.getToken(), startedOp, - resolvedAttributionSource.getUid(), - resolvedAttributionSource.getPackageName(), - /*startIfModeDefault*/ false, - resolvedAttributionSource.getAttributionTag(), - message, proxyAttributionFlags, attributionChainId); - } catch (SecurityException e) { - Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with" - + " platform defined runtime permission " - + AppOpsManager.opToPermission(op) + " while not having " - + Manifest.permission.UPDATE_APP_OPS_STATS); - startedOpResult = appOpsManager.startProxyOpNoThrow(attributedOp, - attributionSource, message, skipProxyOperation, - proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); - } - } else { - try { - startedOpResult = appOpsManager.startProxyOpNoThrow(startedOp, - resolvedAttributionSource, message, skipProxyOperation, - proxyAttributionFlags, proxiedAttributionFlags, attributionChainId); - } catch (SecurityException e) { - //TODO 195339480: remove - String msg = "Security exception for op " + startedOp + " with source " - + attributionSource.getUid() + ":" - + attributionSource.getPackageName() + ", " - + attributionSource.getNextUid() + ":" - + attributionSource.getNextPackageName(); - if (attributionSource.getNext() != null) { - AttributionSource next = attributionSource.getNext(); - msg = msg + ", " + next.getNextPackageName() + ":" + next.getNextUid(); - } - throw new SecurityException(msg + ":" + e.getMessage()); - } - } - return Math.max(checkedOpResult, startedOpResult); - } else { - final AttributionSource resolvedAttributionSource = resolveAttributionSource( - context, accessorSource); - if (resolvedAttributionSource.getPackageName() == null) { - return AppOpsManager.MODE_ERRORED; - } - int notedOp = op; - int checkedOpResult = MODE_ALLOWED; - int notedOpResult; - - // If the datasource wants to attribute to another app op we need to - // make sure the op for the permission and the attributed ops allow - // the operation. We return the less permissive of the two and check - // the permission op while start the attributed op. - if (attributedOp != AppOpsManager.OP_NONE && attributedOp != op) { - checkedOpResult = appOpsManager.checkOpNoThrow(op, - resolvedAttributionSource.getUid(), resolvedAttributionSource - .getPackageName()); - if (checkedOpResult == MODE_ERRORED) { - return checkedOpResult; - } - notedOp = attributedOp; - } - if (selfAccess) { - // If the datasource is not in a trusted platform component then in would not - // have UPDATE_APP_OPS_STATS and the call below would fail. The problem is that - // an app is exposing runtime permission protected data but cannot blame others - // in a trusted way which would not properly show in permission usage UIs. - // As a fallback we note a proxy op that blames the app and the datasource. - try { - notedOpResult = appOpsManager.noteOpNoThrow(notedOp, - resolvedAttributionSource.getUid(), - resolvedAttributionSource.getPackageName(), - resolvedAttributionSource.getAttributionTag(), - message); - } catch (SecurityException e) { - Slog.w(LOG_TAG, "Datasource " + attributionSource + " protecting data with" - + " platform defined runtime permission " - + AppOpsManager.opToPermission(op) + " while not having " - + Manifest.permission.UPDATE_APP_OPS_STATS); - notedOpResult = appOpsManager.noteProxyOpNoThrow(notedOp, attributionSource, - message, skipProxyOperation); - } - } else { - try { - notedOpResult = appOpsManager.noteProxyOpNoThrow(notedOp, - resolvedAttributionSource, message, skipProxyOperation); - } catch (SecurityException e) { - //TODO 195339480: remove - String msg = "Security exception for op " + notedOp + " with source " - + attributionSource.getUid() + ":" - + attributionSource.getPackageName() + ", " - + attributionSource.getNextUid() + ":" - + attributionSource.getNextPackageName(); - if (attributionSource.getNext() != null) { - AttributionSource next = attributionSource.getNext(); - msg = msg + ", " + next.getNextPackageName() + ":" + next.getNextUid(); - } - throw new SecurityException(msg + ":" + e.getMessage()); - } - } - return Math.max(checkedOpResult, notedOpResult); - } - } - - private static int getAttributionChainId(boolean startDataDelivery, - AttributionSource source) { - if (source == null || source.getNext() == null || !startDataDelivery) { - return AppOpsManager.ATTRIBUTION_CHAIN_ID_NONE; - } - int attributionChainId = sAttributionChainIds.incrementAndGet(); - - // handle overflow - if (attributionChainId < 0) { - attributionChainId = 0; - sAttributionChainIds.set(0); - } - return attributionChainId; - } - - private static @Nullable String resolvePackageName(@NonNull Context context, - @NonNull AttributionSource attributionSource) { - if (attributionSource.getPackageName() != null) { - return attributionSource.getPackageName(); - } - final String[] packageNames = context.getPackageManager().getPackagesForUid( - attributionSource.getUid()); - if (packageNames != null) { - // This is best effort if the caller doesn't pass a package. The security - // sandbox is UID, therefore we pick an arbitrary package. - return packageNames[0]; - } - // Last resort to handle special UIDs like root, etc. - return AppOpsManager.resolvePackageName(attributionSource.getUid(), - attributionSource.getPackageName()); - } - - private static @NonNull AttributionSource resolveAttributionSource( - @NonNull Context context, @NonNull AttributionSource attributionSource) { - if (attributionSource.getPackageName() != null) { - return attributionSource; - } - return attributionSource.withPackageName(resolvePackageName(context, - attributionSource)); - } - } - - private static final class RegisteredAttribution { - private final DeathRecipient mDeathRecipient; - private final IBinder mToken; - private final AtomicBoolean mFinished; - - RegisteredAttribution(Context context, int op, AttributionSource source, - boolean fromDatasource) { - mFinished = new AtomicBoolean(false); - mDeathRecipient = () -> { - if (unregister()) { - PermissionCheckerService - .finishDataDelivery(context, op, source.asState(), fromDatasource); - } - }; - mToken = source.getToken(); - if (mToken != null) { - try { - mToken.linkToDeath(mDeathRecipient, 0); - } catch (RemoteException e) { - mDeathRecipient.binderDied(); - } - } - } - - public boolean unregister() { - if (mFinished.compareAndSet(false, true)) { - try { - if (mToken != null) { - mToken.unlinkToDeath(mDeathRecipient, 0); - } - } catch (NoSuchElementException e) { - // do nothing - } - return true; - } - return false; - } - } } diff --git a/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java new file mode 100644 index 0000000000000..d2018f26d04ad --- /dev/null +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerServiceInterface.java @@ -0,0 +1,597 @@ +/* + * Copyright (C) 2021 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.server.pm.permission; + +import android.annotation.AppIdInt; +import android.annotation.NonNull; +import android.annotation.Nullable; +import android.annotation.UserIdInt; +import android.content.pm.PackageManager; +import android.content.pm.PermissionGroupInfo; +import android.content.pm.PermissionInfo; +import android.content.pm.permission.SplitPermissionInfoParcelable; +import android.permission.IOnPermissionsChangeListener; +import android.permission.PermissionManagerInternal; + +import com.android.server.pm.parsing.pkg.AndroidPackage; + +import java.io.FileDescriptor; +import java.io.PrintWriter; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; + +/** + * Interface for managing all permissions and handling permissions related tasks. + */ +public interface PermissionManagerServiceInterface extends PermissionManagerInternal { + /** + * Dump. + */ + void dump(FileDescriptor fd, PrintWriter pw, String[] args); + + /** + * Retrieve all of the known permission groups in the system. + * + * @param flags additional option flags to modify the data returned + * @return a list of {@link PermissionGroupInfo} containing information about all of the known + * permission groups + */ + List getAllPermissionGroups( + @PackageManager.PermissionGroupInfoFlags int flags); + + /** + * Retrieve all of the information we know about a particular group of permissions. + * + * @param groupName the fully qualified name (e.g. com.android.permission_group.APPS) of the + * permission you are interested in + * @param flags additional option flags to modify the data returned + * @return a {@link PermissionGroupInfo} containing information about the permission, or + * {@code null} if not found + */ + PermissionGroupInfo getPermissionGroupInfo(String groupName, + @PackageManager.PermissionGroupInfoFlags int flags); + + /** + * Retrieve all of the information we know about a particular permission. + * + * @param permName the fully qualified name (e.g. com.android.permission.LOGIN) of the + * permission you are interested in + * @param flags additional option flags to modify the data returned + * @return a {@link PermissionInfo} containing information about the permission, or {@code null} + * if not found + */ + PermissionInfo getPermissionInfo(@NonNull String permName, @NonNull String opPackageName, + @PackageManager.PermissionInfoFlags int flags); + + /** + * Query for all of the permissions associated with a particular group. + * + * @param groupName the fully qualified name (e.g. com.android.permission.LOGIN) of the + * permission group you are interested in. Use {@code null} to find all of the + * permissions not associated with a group + * @param flags additional option flags to modify the data returned + * @return a list of {@link PermissionInfo} containing information about all of the permissions + * in the given group, or {@code null} if the group is not found + */ + List queryPermissionsByGroup(String groupName, + @PackageManager.PermissionInfoFlags int flags); + + /** + * Add a new dynamic permission to the system. For this to work, your package must have defined + * a permission tree through the + * {@link android.R.styleable#AndroidManifestPermissionTree <permission-tree>} tag in its + * manifest. A package can only add permissions to trees that were defined by either its own + * package or another with the same user id; a permission is in a tree if it matches the name of + * the permission tree + ".": for example, "com.foo.bar" is a member of the permission tree + * "com.foo". + *

+ * It is good to make your permission tree name descriptive, because you are taking possession + * of that entire set of permission names. Thus, it must be under a domain you control, with a + * suffix that will not match any normal permissions that may be declared in any applications + * that are part of that domain. + *

+ * New permissions must be added before any .apks are installed that use those permissions. + * Permissions you add through this method are remembered across reboots of the device. If the + * given permission already exists, the info you supply here will be used to update it. + * + * @param info description of the permission to be added + * @param async whether the persistence of the permission should be asynchronous, allowing it to + * return quicker and batch a series of adds, at the expense of no guarantee the + * added permission will be retained if the device is rebooted before it is + * written. + * @return {@code true} if a new permission was created, {@code false} if an existing one was + * updated + * @throws SecurityException if you are not allowed to add the given permission name + * + * @see #removePermission(String) + */ + boolean addPermission(PermissionInfo info, boolean async); + + /** + * Removes a permission that was previously added with + * {@link #addPermission(PermissionInfo, boolean)}. The same ownership rules apply -- you are + * only allowed to remove permissions that you are allowed to add. + * + * @param permName the name of the permission to remove + * @throws SecurityException if you are not allowed to remove the given permission name + * + * @see #addPermission(PermissionInfo, boolean) + */ + void removePermission(String permName); + + /** + * Gets the state flags associated with a permission. + * + * @param packageName the package name for which to get the flags + * @param permName the permission for which to get the flags + * @param userId the user for which to get permission flags + * @return the permission flags + */ + int getPermissionFlags(String packageName, String permName, int userId); + + /** + * Updates the flags associated with a permission by replacing the flags in the specified mask + * with the provided flag values. + * + * @param packageName The package name for which to update the flags + * @param permName The permission for which to update the flags + * @param flagMask The flags which to replace + * @param flagValues The flags with which to replace + * @param userId The user for which to update the permission flags + */ + void updatePermissionFlags(String packageName, String permName, int flagMask, + int flagValues, boolean checkAdjustPolicyFlagPermission, int userId); + + /** + * Update the permission flags for all packages and runtime permissions of a user in order + * to allow device or profile owner to remove POLICY_FIXED. + */ + void updatePermissionFlagsForAllApps(int flagMask, int flagValues, int userId); + + /** + * TODO: theianchen We should get rid of the IBinder interface which is an implementation detail + * + * Add a listener for permission changes for installed packages. + * @param listener the listener to add + */ + void addOnPermissionsChangeListener(IOnPermissionsChangeListener listener); + + /** + * Remove a listener for permission changes for installed packages. + * @param listener the listener to remove + */ + void removeOnPermissionsChangeListener(IOnPermissionsChangeListener listener); + + /** + * addAllowlistedRestrictedPermission. TODO: theianchen add doc + */ + boolean addAllowlistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, @PackageManager.PermissionWhitelistFlags int flags, + @UserIdInt int userId); + + /** + * Gets the restricted permissions that have been allowlisted and the app is allowed to have + * them granted in their full form. + *

+ * Permissions can be hard restricted which means that the app cannot hold them or soft + * restricted where the app can hold the permission but in a weaker form. Whether a permission + * is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard restricted} or + * {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} depends on the permission + * declaration. Allowlisting a hard restricted permission allows for the to hold that permission + * and allowlisting a soft restricted permission allows the app to hold the permission in its + * full, unrestricted form. + *

+ * There are four allowlists: + *

    + *
  1. + * One for cases where the system permission policy allowlists a permission. This list + * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM} flag. Can only be + * accessed by pre-installed holders of a dedicated permission. + *
  2. + * One for cases where the system allowlists the permission when upgrading from an OS version in + * which the permission was not restricted to an OS version in which the permission is + * restricted. This list corresponds to the + * {@link PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by + * pre-installed holders of a dedicated permission or the installer on record. + *
  3. + * One for cases where the installer of the package allowlists a permission. This list + * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be + * accessed by pre-installed holders of a dedicated permission or the installer on record. + *
+ * + * @param packageName the app for which to get allowlisted permissions + * @param flags the flag to determine which allowlist to query. Only one flag can be + * passed. + * @return the allowlisted permissions that are on any of the allowlists you query for + * @throws SecurityException if you try to access a allowlist that you have no access to + * + * @see #addAllowlistedRestrictedPermission(String, String, int) + * @see #removeAllowlistedRestrictedPermission(String, String, int) + * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM + * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE + * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER + */ + List getAllowlistedRestrictedPermissions(@NonNull String packageName, + @PackageManager.PermissionWhitelistFlags int flags, @UserIdInt int userId); + + /** + * Removes a allowlisted restricted permission for an app. + *

+ * Permissions can be hard restricted which means that the app cannot hold them or soft + * restricted where the app can hold the permission but in a weaker form. Whether a permission + * is {@link PermissionInfo#FLAG_HARD_RESTRICTED hard restricted} or + * {@link PermissionInfo#FLAG_SOFT_RESTRICTED soft restricted} depends on the permission + * declaration. Allowlisting a hard restricted permission allows for the to hold that permission + * and allowlisting a soft restricted permission allows the app to hold the permission in its + * full, unrestricted form. + *

There are four allowlists: + *

    + *
  1. + * One for cases where the system permission policy allowlists a permission. This list + * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM} flag. Can only be + * accessed by pre-installed holders of a dedicated permission. + *
  2. + * One for cases where the system allowlists the permission when upgrading from an OS version in + * which the permission was not restricted to an OS version in which the permission is + * restricted. This list corresponds to the + * {@link PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE} flag. Can be accessed by + * pre-installed holders of a dedicated permission or the installer on record. + *
  3. + * One for cases where the installer of the package allowlists a permission. This list + * corresponds to the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER} flag. Can be + * accessed by pre-installed holders of a dedicated permission or the installer on record. + *
+ *

+ * You need to specify the allowlists for which to set the allowlisted permissions which will + * clear the previous allowlisted permissions and replace them with the provided ones. + * + * @param packageName the app for which to get allowlisted permissions + * @param permName the allowlisted permission to remove + * @param flags the allowlists from which to remove. Passing multiple flags updates all + * specified allowlists. + * @return whether the permission was removed from the allowlist + * @throws SecurityException if you try to modify a allowlist that you have no access to. + * + * @see #getAllowlistedRestrictedPermissions(String, int) + * @see #addAllowlistedRestrictedPermission(String, String, int) + * @see PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM + * @see PackageManager#FLAG_PERMISSION_WHITELIST_UPGRADE + * @see PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER + */ + boolean removeAllowlistedRestrictedPermission(@NonNull String packageName, + @NonNull String permName, @PackageManager.PermissionWhitelistFlags int flags, + @UserIdInt int userId); + + /** + * Grant a runtime permission to an application which the application does not already have. The + * permission must have been requested by the application. If the application is not allowed to + * hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or + * permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown. + *

+ * Note: Using this API requires holding + * {@code android.permission.GRANT_RUNTIME_PERMISSIONS} and if the user ID is not the current + * user {@code android.permission.INTERACT_ACROSS_USERS_FULL}. + * + * @param packageName the package to which to grant the permission + * @param permName the permission name to grant + * @param userId the user for which to grant the permission + * + * @see #revokeRuntimePermission(String, String, android.os.UserHandle, String) + */ + void grantRuntimePermission(String packageName, String permName, int userId); + + /** + * Revoke a runtime permission that was previously granted by + * {@link #grantRuntimePermission(String, String, android.os.UserHandle)}. The permission must + * have been requested by and granted to the application. If the application is not allowed to + * hold the permission, a {@link java.lang.SecurityException} is thrown. If the package or + * permission is invalid, a {@link java.lang.IllegalArgumentException} is thrown. + *

+ * Note: Using this API requires holding + * {@code android.permission.REVOKE_RUNTIME_PERMISSIONS} and if the user ID is not the current + * user {@code android.permission.INTERACT_ACROSS_USERS_FULL}. + * + * @param packageName the package from which to revoke the permission + * @param permName the permission name to revoke + * @param userId the user for which to revoke the permission + * @param reason the reason for the revoke, or {@code null} for unspecified + * + * @see #grantRuntimePermission(String, String, android.os.UserHandle) + */ + void revokeRuntimePermission(String packageName, String permName, int userId, + String reason); + + /** + * Get whether you should show UI with rationale for requesting a permission. You should do this + * only if you do not have the permission and the context in which the permission is requested + * does not clearly communicate to the user what would be the benefit from grating this + * permission. + * + * @param permName a permission your app wants to request + * @return whether you can show permission rationale UI + */ + boolean shouldShowRequestPermissionRationale(String packageName, String permName, + @UserIdInt int userId); + + /** + * Checks whether a particular permissions has been revoked for a package by policy. Typically + * the device owner or the profile owner may apply such a policy. The user cannot grant policy + * revoked permissions, hence the only way for an app to get such a permission is by a policy + * change. + * + * @param packageName the name of the package you are checking against + * @param permName the name of the permission you are checking for + * + * @return whether the permission is restricted by policy + */ + boolean isPermissionRevokedByPolicy(String packageName, String permName, int userId); + + /** + * Get set of permissions that have been split into more granular or dependent permissions. + * + *

E.g. before {@link android.os.Build.VERSION_CODES#Q} an app that was granted + * {@link Manifest.permission#ACCESS_COARSE_LOCATION} could access the location while it was in + * foreground and background. On platforms after {@link android.os.Build.VERSION_CODES#Q} + * the location permission only grants location access while the app is in foreground. This + * would break apps that target before {@link android.os.Build.VERSION_CODES#Q}. Hence whenever + * such an old app asks for a location permission (i.e. the + * {@link PermissionManager.SplitPermissionInfo#getSplitPermission()}), then the + * {@link Manifest.permission#ACCESS_BACKGROUND_LOCATION} permission (inside + * {@link PermissionManager.SplitPermissionInfo#getNewPermissions}) is added. + * + *

Note: Regular apps do not have to worry about this. The platform and permission controller + * automatically add the new permissions where needed. + * + * @return All permissions that are split. + */ + List getSplitPermissions(); + + /** + * TODO:theianchen add doc describing this is the old checkPermissionImpl + */ + int checkPermission(String pkgName, String permName, int userId); + + /** + * TODO:theianchen add doc describing this is the old checkUidPermissionImpl + */ + int checkUidPermission(int uid, String permName); + + /** + * Adds a listener for runtime permission state (permissions or flags) changes. + * + * @param listener The listener. + */ + void addOnRuntimePermissionStateChangedListener( + @NonNull PermissionManagerServiceInternal + .OnRuntimePermissionStateChangedListener listener); + + /** + * Removes a listener for runtime permission state (permissions or flags) changes. + * + * @param listener The listener. + */ + void removeOnRuntimePermissionStateChangedListener( + @NonNull PermissionManagerServiceInternal + .OnRuntimePermissionStateChangedListener listener); + + /** + * Get all the package names requesting app op permissions. + * + * @return a map of app op permission names to package names requesting them + */ + Map> getAllAppOpPermissionPackages(); + + /** + * Get whether permission review is required for a package. + * + * @param packageName the name of the package + * @param userId the user ID + * @return whether permission review is required + */ + boolean isPermissionsReviewRequired(@NonNull String packageName, + @UserIdInt int userId); + + /** + * Reset the runtime permission state changes for a package. + * + * TODO(zhanghai): Turn this into package change callback? + * + * @param pkg the package + * @param userId the user ID + */ + void resetRuntimePermissions(@NonNull AndroidPackage pkg, + @UserIdInt int userId); + + /** + * Read legacy permission state from package settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code PackageSetting} which is a implementation detail that permission should not know. + * Instead, it should retrieve the legacy state via a defined API. + */ + void readLegacyPermissionStateTEMP(); + + /** + * Write legacy permission state to package settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + void writeLegacyPermissionStateTEMP(); + + /** + * Get all the permissions granted to a package. + * + * @param packageName the name of the package + * @param userId the user ID + * @return the names of the granted permissions + */ + @NonNull + Set getGrantedPermissions(@NonNull String packageName, @UserIdInt int userId); + + /** + * Get the GIDs of a permission. + * + * @param permissionName the name of the permission + * @param userId the user ID + * @return the GIDs of the permission + */ + @NonNull + int[] getPermissionGids(@NonNull String permissionName, @UserIdInt int userId); + + /** + * Get the packages that have requested an app op permission. + * + * @param permissionName the name of the app op permission + * @return the names of the packages that have requested the app op permission + */ + @NonNull + String[] getAppOpPermissionPackages(@NonNull String permissionName); + + /** HACK HACK methods to allow for partial migration of data to the PermissionManager class */ + @Nullable + Permission getPermissionTEMP(@NonNull String permName); + + /** Get all permissions that have a certain protection */ + @NonNull + ArrayList getAllPermissionsWithProtection( + @PermissionInfo.Protection int protection); + + /** Get all permissions that have certain protection flags */ + @NonNull ArrayList getAllPermissionsWithProtectionFlags( + @PermissionInfo.ProtectionFlags int protectionFlags); + + /** + * Get all the legacy permissions currently registered in the system. + * + * @return the legacy permissions + */ + @NonNull + List getLegacyPermissions(); + + /** + * Get the legacy permission state of an app ID, either a package or a shared user. + * + * @param appId the app ID + * @return the legacy permission state + */ + @NonNull + LegacyPermissionState getLegacyPermissionState(@AppIdInt int appId); + + /** + * Read legacy permissions from legacy permission settings. + * + * TODO(zhanghai): This is a temporary method because we should not expose + * {@code LegacyPermissionSettings} which is a implementation detail that permission should not + * know. Instead, it should retrieve the legacy permissions via a defined API. + */ + void readLegacyPermissionsTEMP(@NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Write legacy permissions to legacy permission settings. + * + * TODO(zhanghai): This is a temporary method and should be removed once we migrated persistence + * for permission. + */ + void writeLegacyPermissionsTEMP(@NonNull LegacyPermissionSettings legacyPermissionSettings); + + /** + * Callback when the system is ready. + */ + void onSystemReady(); + + /** + * Callback when a storage volume is mounted, so that all packages on it become available. + * + * @param volumeUuid the UUID of the storage volume + * @param fingerprintChanged whether the current build fingerprint is different from what it was + * when this volume was last mounted + */ + void onStorageVolumeMounted(@NonNull String volumeUuid, boolean fingerprintChanged); + + /** + * Get the GIDs computed from the permission state of a UID, either a package or a shared user. + * + * @param uid the UID + * @return the GIDs for the UID + */ + @NonNull + int[] getGidsForUid(int uid); + + /** + * Callback when a user has been created. + * + * @param userId the created user ID + */ + void onUserCreated(@UserIdInt int userId); + + /** + * Callback when a user has been removed. + * + * @param userId the removed user ID + */ + void onUserRemoved(@UserIdInt int userId); + + /** + * Callback when a package has been added. + * + * @param pkg the added package + * @param isInstantApp whether the added package is an instant app + * @param oldPkg the old package, or {@code null} if none + */ + void onPackageAdded(@NonNull AndroidPackage pkg, boolean isInstantApp, + @Nullable AndroidPackage oldPkg); + + /** + * Callback when a package has been installed for a user. + * + * @param pkg the installed package + * @param previousAppId the previous app ID if the package is leaving a shared UID, + * or Process.INVALID_UID + * @param params the parameters passed in for package installation + * @param userId the user ID this package is installed for + */ + void onPackageInstalled(@NonNull AndroidPackage pkg, int previousAppId, + @NonNull PermissionManagerServiceInternal.PackageInstalledParams params, + @UserIdInt int userId); + + /** + * Callback when a package has been removed. + * + * @param pkg the removed package + */ + void onPackageRemoved(@NonNull AndroidPackage pkg); + + /** + * Callback when a package has been uninstalled. + *

+ * The package may have been fully removed from the system, or only marked as uninstalled for + * this user but still instlaled for other users. + * + * TODO: Pass PackageState instead. + * + * @param packageName the name of the uninstalled package + * @param appId the app ID of the uninstalled package + * @param pkg the uninstalled package, or {@code null} if unavailable + * @param sharedUserPkgs the packages that are in the same shared user + * @param userId the user ID the package is uninstalled for + */ + void onPackageUninstalled(@NonNull String packageName, int appId, @Nullable AndroidPackage pkg, + @NonNull List sharedUserPkgs, @UserIdInt int userId); +}