/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.server.pm.permission; import static android.Manifest.permission.READ_EXTERNAL_STORAGE; import static android.Manifest.permission.WRITE_EXTERNAL_STORAGE; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.AppOpsManager; import android.content.Context; import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.pm.PermissionGroupInfo; import android.content.pm.PermissionInfo; import android.content.pm.PackageParser.Package; import android.os.Binder; import android.os.Build; import android.os.Handler; import android.os.HandlerThread; import android.os.Process; import android.os.UserHandle; import android.os.UserManager; import android.os.UserManagerInternal; import android.os.storage.StorageManagerInternal; import android.util.ArrayMap; import android.util.ArraySet; import android.util.Log; import android.util.Slog; import com.android.internal.R; import com.android.internal.logging.MetricsLogger; import com.android.internal.logging.nano.MetricsProto.MetricsEvent; import com.android.internal.util.ArrayUtils; 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.PackageManagerService; import com.android.server.pm.PackageManagerServiceUtils; import com.android.server.pm.PackageSetting; import com.android.server.pm.ProcessLoggingHandler; import com.android.server.pm.SharedUserSetting; import com.android.server.pm.permission.DefaultPermissionGrantPolicy.DefaultPermissionGrantedCallback; import com.android.server.pm.permission.PermissionManagerInternal.PermissionCallback; import com.android.server.pm.permission.PermissionsState.PermissionState; import libcore.util.EmptyArray; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.Set; /** * Manages all permissions and handles permissions related tasks. */ public class PermissionManagerService { private static final String TAG = "PackageManager"; /** All dangerous permission names in the same order as the events in MetricsEvent */ private static final List ALL_DANGEROUS_PERMISSIONS = Arrays.asList( Manifest.permission.READ_CALENDAR, Manifest.permission.WRITE_CALENDAR, Manifest.permission.CAMERA, Manifest.permission.READ_CONTACTS, Manifest.permission.WRITE_CONTACTS, Manifest.permission.GET_ACCOUNTS, Manifest.permission.ACCESS_FINE_LOCATION, Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.RECORD_AUDIO, Manifest.permission.READ_PHONE_STATE, Manifest.permission.CALL_PHONE, Manifest.permission.READ_CALL_LOG, Manifest.permission.WRITE_CALL_LOG, Manifest.permission.ADD_VOICEMAIL, Manifest.permission.USE_SIP, Manifest.permission.PROCESS_OUTGOING_CALLS, Manifest.permission.READ_CELL_BROADCASTS, Manifest.permission.BODY_SENSORS, Manifest.permission.SEND_SMS, Manifest.permission.RECEIVE_SMS, Manifest.permission.READ_SMS, Manifest.permission.RECEIVE_WAP_PUSH, Manifest.permission.RECEIVE_MMS, Manifest.permission.READ_EXTERNAL_STORAGE, Manifest.permission.WRITE_EXTERNAL_STORAGE, Manifest.permission.READ_PHONE_NUMBERS, Manifest.permission.ANSWER_PHONE_CALLS); /** Cap the size of permission trees that 3rd party apps can define */ private static final int MAX_PERMISSION_TREE_FOOTPRINT = 32768; // characters of text /** Lock to protect internal data access */ private final Object mLock; /** Internal connection to the package manager */ private final PackageManagerInternal mPackageManagerInt; /** Internal connection to the user manager */ private final UserManagerInternal mUserManagerInt; /** Default permission policy to provide proper behaviour out-of-the-box */ private final DefaultPermissionGrantPolicy mDefaultPermissionGrantPolicy; /** Internal storage for permissions and related settings */ private final PermissionSettings mSettings; private final HandlerThread mHandlerThread; private final Handler mHandler; private final Context mContext; PermissionManagerService(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { mContext = context; mLock = externalLock; mPackageManagerInt = LocalServices.getService(PackageManagerInternal.class); mUserManagerInt = LocalServices.getService(UserManagerInternal.class); mSettings = new PermissionSettings(context, mLock); mHandlerThread = new ServiceThread(TAG, Process.THREAD_PRIORITY_BACKGROUND, true /*allowIo*/); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); Watchdog.getInstance().addThread(mHandler); mDefaultPermissionGrantPolicy = new DefaultPermissionGrantPolicy( context, mHandlerThread.getLooper(), defaultGrantCallback, this); // 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(PermissionManagerInternal.class);} *

* NOTE: The external lock is temporary and should be removed. This needs to be a * lock created by the permission manager itself. */ public static PermissionManagerInternal create(Context context, @Nullable DefaultPermissionGrantedCallback defaultGrantCallback, @NonNull Object externalLock) { final PermissionManagerInternal permMgrInt = LocalServices.getService(PermissionManagerInternal.class); if (permMgrInt != null) { return permMgrInt; } new PermissionManagerService(context, defaultGrantCallback, externalLock); return LocalServices.getService(PermissionManagerInternal.class); } @Nullable BasePermission getPermission(String permName) { synchronized (mLock) { return mSettings.getPermissionLocked(permName); } } private int checkPermission(String permName, String pkgName, int callingUid, int userId) { if (!mUserManagerInt.exists(userId)) { return PackageManager.PERMISSION_DENIED; } final PackageParser.Package pkg = mPackageManagerInt.getPackage(pkgName); if (pkg != null && pkg.mExtras != null) { if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return PackageManager.PERMISSION_DENIED; } final PackageSetting ps = (PackageSetting) pkg.mExtras; final boolean instantApp = ps.getInstantApp(userId); final PermissionsState permissionsState = ps.getPermissionsState(); if (permissionsState.hasPermission(permName, userId)) { if (instantApp) { synchronized (mLock) { BasePermission bp = mSettings.getPermissionLocked(permName); if (bp != null && bp.isInstant()) { return PackageManager.PERMISSION_GRANTED; } } } else { return PackageManager.PERMISSION_GRANTED; } } // Special case: ACCESS_FINE_LOCATION permission includes ACCESS_COARSE_LOCATION if (Manifest.permission.ACCESS_COARSE_LOCATION.equals(permName) && permissionsState .hasPermission(Manifest.permission.ACCESS_FINE_LOCATION, userId)) { return PackageManager.PERMISSION_GRANTED; } } return PackageManager.PERMISSION_DENIED; } private PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags, int callingUid) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mLock) { return PackageParser.generatePermissionGroupInfo( mSettings.mPermissionGroups.get(groupName), flags); } } private List getAllPermissionGroups(int flags, int callingUid) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mLock) { final int N = mSettings.mPermissionGroups.size(); final ArrayList out = new ArrayList(N); for (PackageParser.PermissionGroup pg : mSettings.mPermissionGroups.values()) { out.add(PackageParser.generatePermissionGroupInfo(pg, flags)); } return out; } } private PermissionInfo getPermissionInfo(String permName, String packageName, int flags, int callingUid) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } // reader synchronized (mLock) { final BasePermission bp = mSettings.getPermissionLocked(permName); if (bp == null) { return null; } final int adjustedProtectionLevel = adjustPermissionProtectionFlagsLocked( bp.getProtectionLevel(), packageName, callingUid); return bp.generatePermissionInfo(adjustedProtectionLevel, flags); } } private List getPermissionInfoByGroup( String groupName, int flags, int callingUid) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { return null; } synchronized (mLock) { if (groupName != null && !mSettings.mPermissionGroups.containsKey(groupName)) { return null; } final ArrayList out = new ArrayList(10); for (BasePermission bp : mSettings.mPermissions.values()) { final PermissionInfo pi = bp.generatePermissionInfo(groupName, flags); if (pi != null) { out.add(pi); } } return out; } } private int adjustPermissionProtectionFlagsLocked( int protectionLevel, String packageName, int uid) { // Signature permission flags area always reported final int protectionLevelMasked = protectionLevel & (PermissionInfo.PROTECTION_NORMAL | PermissionInfo.PROTECTION_DANGEROUS | PermissionInfo.PROTECTION_SIGNATURE); if (protectionLevelMasked == PermissionInfo.PROTECTION_SIGNATURE) { return protectionLevel; } // System sees all flags. final int appId = UserHandle.getAppId(uid); if (appId == Process.SYSTEM_UID || appId == Process.ROOT_UID || appId == Process.SHELL_UID) { return protectionLevel; } // Normalize package name to handle renamed packages and static libs final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null) { return protectionLevel; } if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.O) { return protectionLevelMasked; } // Apps that target O see flags for all protection levels. final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return protectionLevel; } if (ps.getAppId() != appId) { return protectionLevel; } return protectionLevel; } private void addAllPermissions(PackageParser.Package pkg, boolean chatty) { final int N = pkg.permissions.size(); for (int i=0; i Build.VERSION_CODES.LOLLIPOP_MR1) { p.group = mSettings.mPermissionGroups.get(p.info.group); // Warn for a permission in an unknown group. if (PackageManagerService.DEBUG_PERMISSIONS && p.info.group != null && p.group == null) { Slog.i(TAG, "Permission " + p.info.name + " from package " + p.info.packageName + " in an unknown group " + p.info.group); } } if (p.tree) { final BasePermission bp = BasePermission.createOrUpdate( mSettings.getPermissionTreeLocked(p.info.name), p, pkg, mSettings.getAllPermissionTreesLocked(), chatty); mSettings.putPermissionTreeLocked(p.info.name, bp); } else { final BasePermission bp = BasePermission.createOrUpdate( mSettings.getPermissionLocked(p.info.name), p, pkg, mSettings.getAllPermissionTreesLocked(), chatty); mSettings.putPermissionLocked(p.info.name, bp); } } } } private void addAllPermissionGroups(PackageParser.Package pkg, boolean chatty) { final int N = pkg.permissionGroups.size(); StringBuilder r = null; for (int i=0; i appOpPkgs = mSettings.mAppOpPermissionPackages.get(p.info.name); if (appOpPkgs != null) { appOpPkgs.remove(pkg.packageName); } } } if (r != null) { if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); } N = pkg.requestedPermissions.size(); r = null; for (int i=0; i appOpPkgs = mSettings.mAppOpPermissionPackages.get(perm); if (appOpPkgs != null) { appOpPkgs.remove(pkg.packageName); if (appOpPkgs.isEmpty()) { mSettings.mAppOpPermissionPackages.remove(perm); } } } } if (r != null) { if (PackageManagerService.DEBUG_REMOVE) Log.d(TAG, " Permissions: " + r); } } } private boolean addDynamicPermission( PermissionInfo info, int callingUid, PermissionCallback callback) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant apps can't add permissions"); } if (info.labelRes == 0 && info.nonLocalizedLabel == null) { throw new SecurityException("Label must be specified in permission"); } final BasePermission tree = mSettings.enforcePermissionTree(info.name, callingUid); final boolean added; final boolean changed; synchronized (mLock) { BasePermission bp = mSettings.getPermissionLocked(info.name); added = bp == null; int fixedLevel = PermissionInfo.fixProtectionLevel(info.protectionLevel); if (added) { enforcePermissionCapLocked(info, tree); bp = new BasePermission(info.name, tree.getSourcePackageName(), BasePermission.TYPE_DYNAMIC); } else if (bp.isDynamic()) { // TODO: switch this back to SecurityException Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + info.name); } changed = bp.addToTree(fixedLevel, info, tree); if (added) { mSettings.putPermissionLocked(info.name, bp); } } if (changed && callback != null) { callback.onPermissionChanged(); } return added; } private void removeDynamicPermission( String permName, int callingUid, PermissionCallback callback) { if (mPackageManagerInt.getInstantAppPackageName(callingUid) != null) { throw new SecurityException("Instant applications don't have access to this method"); } final BasePermission tree = mSettings.enforcePermissionTree(permName, callingUid); synchronized (mLock) { final BasePermission bp = mSettings.getPermissionLocked(permName); if (bp == null) { return; } if (bp.isDynamic()) { // TODO: switch this back to SecurityException Slog.wtf(TAG, "Not allowed to modify non-dynamic permission " + permName); } mSettings.removePermissionLocked(permName); if (callback != null) { callback.onPermissionRemoved(); } } } private void grantRuntimePermissionsGrantedToDisabledPackageLocked( PackageParser.Package pkg, int callingUid, PermissionCallback callback) { if (pkg.parentPackage == null) { return; } if (pkg.requestedPermissions == null) { return; } final PackageParser.Package disabledPkg = mPackageManagerInt.getDisabledPackage(pkg.parentPackage.packageName); if (disabledPkg == null || disabledPkg.mExtras == null) { return; } final PackageSetting disabledPs = (PackageSetting) disabledPkg.mExtras; if (!disabledPs.isPrivileged() || disabledPs.hasChildPackages()) { return; } final int permCount = pkg.requestedPermissions.size(); for (int i = 0; i < permCount; i++) { String permission = pkg.requestedPermissions.get(i); BasePermission bp = mSettings.getPermissionLocked(permission); if (bp == null || !(bp.isRuntime() || bp.isDevelopment())) { continue; } for (int userId : mUserManagerInt.getUserIds()) { if (disabledPs.getPermissionsState().hasRuntimePermission(permission, userId)) { grantRuntimePermission( permission, pkg.packageName, false, callingUid, userId, callback); } } } } private void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds, String[] grantedPermissions, int callingUid, PermissionCallback callback) { for (int userId : userIds) { grantRequestedRuntimePermissionsForUser(pkg, userId, grantedPermissions, callingUid, callback); } } private void grantRequestedRuntimePermissionsForUser(PackageParser.Package pkg, int userId, String[] grantedPermissions, int callingUid, PermissionCallback callback) { PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { return; } PermissionsState permissionsState = ps.getPermissionsState(); final int immutableFlags = PackageManager.FLAG_PERMISSION_SYSTEM_FIXED | PackageManager.FLAG_PERMISSION_POLICY_FIXED; final boolean supportsRuntimePermissions = pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M; final boolean instantApp = mPackageManagerInt.isInstantApp(pkg.packageName, userId); for (String permission : pkg.requestedPermissions) { final BasePermission bp; synchronized (mLock) { bp = mSettings.getPermissionLocked(permission); } if (bp != null && (bp.isRuntime() || bp.isDevelopment()) && (!instantApp || bp.isInstant()) && (supportsRuntimePermissions || !bp.isRuntimeOnly()) && (grantedPermissions == null || ArrayUtils.contains(grantedPermissions, permission))) { final int flags = permissionsState.getPermissionFlags(permission, userId); if (supportsRuntimePermissions) { // Installer cannot change immutable permissions. if ((flags & immutableFlags) == 0) { grantRuntimePermission(permission, pkg.packageName, false, callingUid, userId, callback); } } else if (mSettings.mPermissionReviewRequired) { // In permission review mode we clear the review flag when we // are asked to install the app with all permissions granted. if ((flags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0) { updatePermissionFlags(permission, pkg.packageName, PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED, 0, callingUid, userId, callback); } } } } } private void grantRuntimePermission(String permName, String packageName, boolean overridePolicy, int callingUid, final int userId, PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { Log.e(TAG, "No such user:" + userId); return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.GRANT_RUNTIME_PERMISSIONS, "grantRuntimePermission"); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "grantRuntimePermission"); final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null || pkg.mExtras == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } final BasePermission bp; synchronized(mLock) { bp = mSettings.getPermissionLocked(permName); } if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. if (mSettings.mPermissionReviewRequired && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { return; } final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); final PackageSetting ps = (PackageSetting) pkg.mExtras; final PermissionsState permissionsState = ps.getPermissionsState(); final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { throw new SecurityException("Cannot grant system fixed permission " + permName + " for package " + packageName); } if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { throw new SecurityException("Cannot grant policy fixed permission " + permName + " for package " + packageName); } if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.grantInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionGranted(); } } return; } if (ps.getInstantApp(userId) && !bp.isInstant()) { throw new SecurityException("Cannot grant non-ephemeral permission" + permName + " for package " + packageName); } if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) { Slog.w(TAG, "Cannot grant runtime permission to a legacy app"); return; } final int result = permissionsState.grantRuntimePermission(bp, userId); switch (result) { case PermissionsState.PERMISSION_OPERATION_FAILURE: { return; } case PermissionsState.PERMISSION_OPERATION_SUCCESS_GIDS_CHANGED: { if (callback != null) { callback.onGidsChanged(UserHandle.getAppId(pkg.applicationInfo.uid), userId); } } break; } if (bp.isRuntime()) { logPermissionGranted(mContext, permName, packageName); } if (callback != null) { callback.onPermissionGranted(uid, userId); } // Only need to do this if user is initialized. Otherwise it's a new user // and there are no processes running as the user yet and there's no need // to make an expensive call to remount processes for the changed permissions. if (READ_EXTERNAL_STORAGE.equals(permName) || WRITE_EXTERNAL_STORAGE.equals(permName)) { final long token = Binder.clearCallingIdentity(); try { if (mUserManagerInt.isUserInitialized(userId)) { StorageManagerInternal storageManagerInternal = LocalServices.getService( StorageManagerInternal.class); storageManagerInternal.onExternalStoragePolicyChanged(uid, packageName); } } finally { Binder.restoreCallingIdentity(token); } } } private void revokeRuntimePermission(String permName, String packageName, boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { Log.e(TAG, "No such user:" + userId); return; } mContext.enforceCallingOrSelfPermission( android.Manifest.permission.REVOKE_RUNTIME_PERMISSIONS, "revokeRuntimePermission"); enforceCrossUserPermission(Binder.getCallingUid(), userId, true /* requireFullPermission */, true /* checkShell */, "revokeRuntimePermission"); final int appId; final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null || pkg.mExtras == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } if (mPackageManagerInt.filterAppAccess(pkg, Binder.getCallingUid(), userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } final BasePermission bp = mSettings.getPermissionLocked(permName); if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } bp.enforceDeclaredUsedAndRuntimeOrDevelopment(pkg); // If a permission review is required for legacy apps we represent // their permissions as always granted runtime ones since we need // to keep the review required permission flag per user while an // install permission's state is shared across all users. if (mSettings.mPermissionReviewRequired && pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M && bp.isRuntime()) { return; } final PackageSetting ps = (PackageSetting) pkg.mExtras; final PermissionsState permissionsState = ps.getPermissionsState(); final int flags = permissionsState.getPermissionFlags(permName, userId); if ((flags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0) { throw new SecurityException("Cannot revoke system fixed permission " + permName + " for package " + packageName); } if (!overridePolicy && (flags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { throw new SecurityException("Cannot revoke policy fixed permission " + permName + " for package " + packageName); } if (bp.isDevelopment()) { // Development permissions must be handled specially, since they are not // normal runtime permissions. For now they apply to all users. if (permissionsState.revokeInstallPermission(bp) != PermissionsState.PERMISSION_OPERATION_FAILURE) { if (callback != null) { callback.onInstallPermissionRevoked(); } } return; } if (permissionsState.revokeRuntimePermission(bp, userId) == PermissionsState.PERMISSION_OPERATION_FAILURE) { return; } if (bp.isRuntime()) { logPermissionRevoked(mContext, permName, packageName); } if (callback != null) { final int uid = UserHandle.getUid(userId, pkg.applicationInfo.uid); callback.onPermissionRevoked(pkg.applicationInfo.uid, userId); } } private int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) { // Collect all used permissions in the UID final ArraySet usedPermissions = new ArraySet<>(); final List pkgList = suSetting.getPackages(); if (pkgList == null || pkgList.size() == 0) { return EmptyArray.INT; } for (PackageParser.Package pkg : pkgList) { final int requestedPermCount = pkg.requestedPermissions.size(); for (int j = 0; j < requestedPermCount; j++) { String permission = pkg.requestedPermissions.get(j); BasePermission bp = mSettings.getPermissionLocked(permission); if (bp != null) { usedPermissions.add(permission); } } } PermissionsState permissionsState = suSetting.getPermissionsState(); // Prune install permissions List installPermStates = permissionsState.getInstallPermissionStates(); final int installPermCount = installPermStates.size(); for (int i = installPermCount - 1; i >= 0; i--) { PermissionState permissionState = installPermStates.get(i); if (!usedPermissions.contains(permissionState.getName())) { BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); if (bp != null) { permissionsState.revokeInstallPermission(bp); permissionsState.updatePermissionFlags(bp, UserHandle.USER_ALL, PackageManager.MASK_PERMISSION_FLAGS, 0); } } } int[] runtimePermissionChangedUserIds = EmptyArray.INT; // Prune runtime permissions for (int userId : allUserIds) { List runtimePermStates = permissionsState .getRuntimePermissionStates(userId); final int runtimePermCount = runtimePermStates.size(); for (int i = runtimePermCount - 1; i >= 0; i--) { PermissionState permissionState = runtimePermStates.get(i); if (!usedPermissions.contains(permissionState.getName())) { BasePermission bp = mSettings.getPermissionLocked(permissionState.getName()); if (bp != null) { permissionsState.revokeRuntimePermission(bp, userId); permissionsState.updatePermissionFlags(bp, userId, PackageManager.MASK_PERMISSION_FLAGS, 0); runtimePermissionChangedUserIds = ArrayUtils.appendInt( runtimePermissionChangedUserIds, userId); } } } } return runtimePermissionChangedUserIds; } private String[] getAppOpPermissionPackages(String permName) { if (mPackageManagerInt.getInstantAppPackageName(Binder.getCallingUid()) != null) { return null; } synchronized (mLock) { final ArraySet pkgs = mSettings.mAppOpPermissionPackages.get(permName); if (pkgs == null) { return null; } return pkgs.toArray(new String[pkgs.size()]); } } private int getPermissionFlags( String permName, String packageName, int callingUid, int userId) { if (!mUserManagerInt.exists(userId)) { return 0; } enforceGrantRevokeRuntimePermissionPermissions("getPermissionFlags"); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, false /* checkShell */, "getPermissionFlags"); final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null || pkg.mExtras == null) { return 0; } synchronized (mLock) { if (mSettings.getPermissionLocked(permName) == null) { return 0; } } if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { return 0; } final PackageSetting ps = (PackageSetting) pkg.mExtras; PermissionsState permissionsState = ps.getPermissionsState(); return permissionsState.getPermissionFlags(permName, userId); } private int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) { Set needsUpdate = null; synchronized (mLock) { final Iterator it = mSettings.mPermissions.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); if (bp.isDynamic()) { bp.updateDynamicPermission(mSettings.mPermissionTrees.values()); } if (bp.getSourcePackageSetting() != null) { if (packageName != null && packageName.equals(bp.getSourcePackageName()) && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) { Slog.i(TAG, "Removing old permission tree: " + bp.getName() + " from package " + bp.getSourcePackageName()); flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL; it.remove(); } continue; } if (needsUpdate == null) { needsUpdate = new ArraySet<>(mSettings.mPermissions.size()); } needsUpdate.add(bp); } } if (needsUpdate != null) { for (final BasePermission bp : needsUpdate) { final PackageParser.Package pkg = mPackageManagerInt.getPackage(bp.getSourcePackageName()); synchronized (mLock) { if (pkg != null && pkg.mExtras != null) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (bp.getSourcePackageSetting() == null) { bp.setSourcePackageSetting(ps); } continue; } Slog.w(TAG, "Removing dangling permission: " + bp.getName() + " from package " + bp.getSourcePackageName()); mSettings.removePermissionLocked(bp.getName()); } } } return flags; } private int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo, int flags) { Set needsUpdate = null; synchronized (mLock) { final Iterator it = mSettings.mPermissionTrees.values().iterator(); while (it.hasNext()) { final BasePermission bp = it.next(); if (bp.getSourcePackageSetting() != null) { if (packageName != null && packageName.equals(bp.getSourcePackageName()) && (pkgInfo == null || !hasPermission(pkgInfo, bp.getName()))) { Slog.i(TAG, "Removing old permission tree: " + bp.getName() + " from package " + bp.getSourcePackageName()); flags |= PackageManagerService.UPDATE_PERMISSIONS_ALL; it.remove(); } continue; } if (needsUpdate == null) { needsUpdate = new ArraySet<>(mSettings.mPermissionTrees.size()); } needsUpdate.add(bp); } } if (needsUpdate != null) { for (final BasePermission bp : needsUpdate) { final PackageParser.Package pkg = mPackageManagerInt.getPackage(bp.getSourcePackageName()); synchronized (mLock) { if (pkg != null && pkg.mExtras != null) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (bp.getSourcePackageSetting() == null) { bp.setSourcePackageSetting(ps); } continue; } Slog.w(TAG, "Removing dangling permission tree: " + bp.getName() + " from package " + bp.getSourcePackageName()); mSettings.removePermissionLocked(bp.getName()); } } } return flags; } private void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, int callingUid, int userId, PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { return; } enforceGrantRevokeRuntimePermissionPermissions("updatePermissionFlags"); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "updatePermissionFlags"); // 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; } final PackageParser.Package pkg = mPackageManagerInt.getPackage(packageName); if (pkg == null || pkg.mExtras == null) { throw new IllegalArgumentException("Unknown package: " + packageName); } if (mPackageManagerInt.filterAppAccess(pkg, callingUid, userId)) { throw new IllegalArgumentException("Unknown package: " + packageName); } final BasePermission bp; synchronized (mLock) { bp = mSettings.getPermissionLocked(permName); } if (bp == null) { throw new IllegalArgumentException("Unknown permission: " + permName); } final PackageSetting ps = (PackageSetting) pkg.mExtras; final PermissionsState permissionsState = ps.getPermissionsState(); final boolean hadState = permissionsState.getRuntimePermissionState(permName, userId) != null; final boolean permissionUpdated = permissionsState.updatePermissionFlags(bp, userId, flagMask, flagValues); if (permissionUpdated && callback != null) { // Install and runtime permissions are stored in different places, // so figure out what permission changed and persist the change. if (permissionsState.getInstallPermissionState(permName) != null) { callback.onInstallPermissionUpdated(); } else if (permissionsState.getRuntimePermissionState(permName, userId) != null || hadState) { callback.onPermissionUpdated(userId); } } } private boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid, int userId, Collection packages, PermissionCallback callback) { if (!mUserManagerInt.exists(userId)) { return false; } enforceGrantRevokeRuntimePermissionPermissions( "updatePermissionFlagsForAllApps"); enforceCrossUserPermission(callingUid, userId, true /* requireFullPermission */, true /* checkShell */, "updatePermissionFlagsForAllApps"); // Only the system can change system fixed flags. if (callingUid != Process.SYSTEM_UID) { flagMask &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; flagValues &= ~PackageManager.FLAG_PERMISSION_SYSTEM_FIXED; } boolean changed = false; for (PackageParser.Package pkg : packages) { final PackageSetting ps = (PackageSetting) pkg.mExtras; if (ps == null) { continue; } PermissionsState permissionsState = ps.getPermissionsState(); changed |= permissionsState.updatePermissionFlagsForAllPermissions( userId, flagMask, flagValues); } return changed; } private void enforceGrantRevokeRuntimePermissionPermissions(String message) { if (mContext.checkCallingOrSelfPermission(Manifest.permission.GRANT_RUNTIME_PERMISSIONS) != PackageManager.PERMISSION_GRANTED && mContext.checkCallingOrSelfPermission(Manifest.permission.REVOKE_RUNTIME_PERMISSIONS) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException(message + " requires " + Manifest.permission.GRANT_RUNTIME_PERMISSIONS + " or " + Manifest.permission.REVOKE_RUNTIME_PERMISSIONS); } } /** * Checks if the request is from the system or an app that has INTERACT_ACROSS_USERS * or INTERACT_ACROSS_USERS_FULL permissions, if the userid is not for the caller. * @param checkShell whether to prevent shell from access if there's a debugging restriction * @param message the message to log on security exception */ private void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, String message) { if (userId < 0) { throw new IllegalArgumentException("Invalid userId " + userId); } if (checkShell) { PackageManagerServiceUtils.enforceShellRestriction( UserManager.DISALLOW_DEBUGGING_FEATURES, callingUid, userId); } if (userId == UserHandle.getUserId(callingUid)) return; if (callingUid != Process.SYSTEM_UID && callingUid != 0) { if (requireFullPermission) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); } else { try { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS_FULL, message); } catch (SecurityException se) { mContext.enforceCallingOrSelfPermission( android.Manifest.permission.INTERACT_ACROSS_USERS, message); } } } } private int calculateCurrentPermissionFootprintLocked(BasePermission tree) { int size = 0; for (BasePermission perm : mSettings.mPermissions.values()) { size += tree.calculateFootprint(perm); } return size; } private void enforcePermissionCapLocked(PermissionInfo info, BasePermission tree) { // We calculate the max size of permissions defined by this uid and throw // if that plus the size of 'info' would exceed our stated maximum. if (tree.getUid() != Process.SYSTEM_UID) { final int curTreeSize = calculateCurrentPermissionFootprintLocked(tree); if (curTreeSize + info.calculateFootprint() > MAX_PERMISSION_TREE_FOOTPRINT) { throw new SecurityException("Permission tree size cap exceeded"); } } } private static boolean hasPermission(PackageParser.Package pkgInfo, String permName) { for (int i=pkgInfo.permissions.size()-1; i>=0; i--) { if (pkgInfo.permissions.get(i).info.name.equals(permName)) { return true; } } return false; } /** * Get the first event id for the permission. * *

There are four events for each permission:

    *
  • Request permission: first id + 0
  • *
  • Grant permission: first id + 1
  • *
  • Request for permission denied: first id + 2
  • *
  • Revoke permission: first id + 3
  • *

* * @param name name of the permission * * @return The first event id for the permission */ private static int getBaseEventId(@NonNull String name) { int eventIdIndex = ALL_DANGEROUS_PERMISSIONS.indexOf(name); if (eventIdIndex == -1) { if (AppOpsManager.permissionToOpCode(name) == AppOpsManager.OP_NONE || Build.IS_USER) { Log.i(TAG, "Unknown permission " + name); return MetricsEvent.ACTION_PERMISSION_REQUEST_UNKNOWN; } else { // Most likely #ALL_DANGEROUS_PERMISSIONS needs to be updated. // // Also update // - EventLogger#ALL_DANGEROUS_PERMISSIONS // - metrics_constants.proto throw new IllegalStateException("Unknown permission " + name); } } return MetricsEvent.ACTION_PERMISSION_REQUEST_READ_CALENDAR + eventIdIndex * 4; } /** * Log that a permission was revoked. * * @param context Context of the caller * @param name name of the permission * @param packageName package permission if for */ private static void logPermissionRevoked(@NonNull Context context, @NonNull String name, @NonNull String packageName) { MetricsLogger.action(context, getBaseEventId(name) + 3, packageName); } /** * Log that a permission request was granted. * * @param context Context of the caller * @param name name of the permission * @param packageName package permission if for */ private static void logPermissionGranted(@NonNull Context context, @NonNull String name, @NonNull String packageName) { MetricsLogger.action(context, getBaseEventId(name) + 1, packageName); } private class PermissionManagerInternalImpl extends PermissionManagerInternal { @Override public void addAllPermissions(Package pkg, boolean chatty) { PermissionManagerService.this.addAllPermissions(pkg, chatty); } @Override public void addAllPermissionGroups(Package pkg, boolean chatty) { PermissionManagerService.this.addAllPermissionGroups(pkg, chatty); } @Override public void removeAllPermissions(Package pkg, boolean chatty) { PermissionManagerService.this.removeAllPermissions(pkg, chatty); } @Override public boolean addDynamicPermission(PermissionInfo info, boolean async, int callingUid, PermissionCallback callback) { return PermissionManagerService.this.addDynamicPermission(info, callingUid, callback); } @Override public void removeDynamicPermission(String permName, int callingUid, PermissionCallback callback) { PermissionManagerService.this.removeDynamicPermission(permName, callingUid, callback); } @Override public void grantRuntimePermission(String permName, String packageName, boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) { PermissionManagerService.this.grantRuntimePermission( permName, packageName, overridePolicy, callingUid, userId, callback); } @Override public void grantRequestedRuntimePermissions(PackageParser.Package pkg, int[] userIds, String[] grantedPermissions, int callingUid, PermissionCallback callback) { PermissionManagerService.this.grantRequestedRuntimePermissions( pkg, userIds, grantedPermissions, callingUid, callback); } @Override public void grantRuntimePermissionsGrantedToDisabledPackage(PackageParser.Package pkg, int callingUid, PermissionCallback callback) { PermissionManagerService.this.grantRuntimePermissionsGrantedToDisabledPackageLocked( pkg, callingUid, callback); } @Override public void revokeRuntimePermission(String permName, String packageName, boolean overridePolicy, int callingUid, int userId, PermissionCallback callback) { PermissionManagerService.this.revokeRuntimePermission(permName, packageName, overridePolicy, callingUid, userId, callback); } @Override public int[] revokeUnusedSharedUserPermissions(SharedUserSetting suSetting, int[] allUserIds) { return PermissionManagerService.this.revokeUnusedSharedUserPermissions( (SharedUserSetting) suSetting, allUserIds); } @Override public String[] getAppOpPermissionPackages(String permName) { return PermissionManagerService.this.getAppOpPermissionPackages(permName); } @Override public int getPermissionFlags(String permName, String packageName, int callingUid, int userId) { return PermissionManagerService.this.getPermissionFlags(permName, packageName, callingUid, userId); } @Override public int updatePermissions(String packageName, PackageParser.Package pkgInfo, int flags) { return PermissionManagerService.this.updatePermissions(packageName, pkgInfo, flags); } @Override public int updatePermissionTrees(String packageName, PackageParser.Package pkgInfo, int flags) { return PermissionManagerService.this.updatePermissionTrees(packageName, pkgInfo, flags); } @Override public void updatePermissionFlags(String permName, String packageName, int flagMask, int flagValues, int callingUid, int userId, PermissionCallback callback) { PermissionManagerService.this.updatePermissionFlags( permName, packageName, flagMask, flagValues, callingUid, userId, callback); } @Override public boolean updatePermissionFlagsForAllApps(int flagMask, int flagValues, int callingUid, int userId, Collection packages, PermissionCallback callback) { return PermissionManagerService.this.updatePermissionFlagsForAllApps( flagMask, flagValues, callingUid, userId, packages, callback); } @Override public void enforceCrossUserPermission(int callingUid, int userId, boolean requireFullPermission, boolean checkShell, String message) { PermissionManagerService.this.enforceCrossUserPermission(callingUid, userId, requireFullPermission, checkShell, message); } @Override public void enforceGrantRevokeRuntimePermissionPermissions(String message) { PermissionManagerService.this.enforceGrantRevokeRuntimePermissionPermissions(message); } @Override public int checkPermission(String permName, String packageName, int callingUid, int userId) { return PermissionManagerService.this.checkPermission( permName, packageName, callingUid, userId); } @Override public PermissionGroupInfo getPermissionGroupInfo(String groupName, int flags, int callingUid) { return PermissionManagerService.this.getPermissionGroupInfo( groupName, flags, callingUid); } @Override public List getAllPermissionGroups(int flags, int callingUid) { return PermissionManagerService.this.getAllPermissionGroups(flags, callingUid); } @Override public PermissionInfo getPermissionInfo(String permName, String packageName, int flags, int callingUid) { return PermissionManagerService.this.getPermissionInfo( permName, packageName, flags, callingUid); } @Override public List getPermissionInfoByGroup(String group, int flags, int callingUid) { return PermissionManagerService.this.getPermissionInfoByGroup(group, flags, callingUid); } @Override public PermissionSettings getPermissionSettings() { return mSettings; } @Override public DefaultPermissionGrantPolicy getDefaultPermissionGrantPolicy() { return mDefaultPermissionGrantPolicy; } @Override public BasePermission getPermissionTEMP(String permName) { synchronized (PermissionManagerService.this.mLock) { return mSettings.getPermissionLocked(permName); } } @Override public void putPermissionTEMP(String permName, BasePermission permission) { synchronized (PermissionManagerService.this.mLock) { mSettings.putPermissionLocked(permName, (BasePermission) permission); } } @Override public Iterator getPermissionIteratorTEMP() { synchronized (PermissionManagerService.this.mLock) { return mSettings.getAllPermissionsLocked().iterator(); } } } }