diff options
17 files changed, 216 insertions, 264 deletions
diff --git a/api/current.txt b/api/current.txt index 0ff2eacce9b1..9ab8dbb78a67 100644 --- a/api/current.txt +++ b/api/current.txt @@ -287,7 +287,6 @@ package android { field public static final int allowBackup = 16843392; // 0x1010280 field public static final int allowClearUserData = 16842757; // 0x1010005 field public static final int allowEmbedded = 16843765; // 0x10103f5 - field public static final int allowExternalStorageSandbox = 16844201; // 0x10105a9 field public static final int allowParallelSyncs = 16843570; // 0x1010332 field public static final int allowSingleTap = 16843353; // 0x1010259 field public static final int allowTaskReparenting = 16843268; // 0x1010204 @@ -1125,6 +1124,7 @@ package android { field public static final int reqKeyboardType = 16843304; // 0x1010228 field public static final int reqNavigation = 16843306; // 0x101022a field public static final int reqTouchScreen = 16843303; // 0x1010227 + field public static final int requestLegacyExternalStorage = 16844201; // 0x10105a9 field public static final int requireDeviceUnlock = 16843756; // 0x10103ec field public static final int required = 16843406; // 0x101028e field public static final int requiredAccountType = 16843734; // 0x10103d6 @@ -11859,6 +11859,7 @@ package android.content.pm { field @NonNull public static final android.os.Parcelable.Creator<android.content.pm.PermissionInfo> CREATOR; field public static final int FLAG_COSTS_MONEY = 1; // 0x1 field public static final int FLAG_HARD_RESTRICTED = 4; // 0x4 + field public static final int FLAG_IMMUTABLY_RESTRICTED = 16; // 0x10 field public static final int FLAG_INSTALLED = 1073741824; // 0x40000000 field public static final int FLAG_SOFT_RESTRICTED = 8; // 0x8 field public static final int PROTECTION_DANGEROUS = 1; // 0x1 diff --git a/core/java/android/app/AppOpsManager.java b/core/java/android/app/AppOpsManager.java index 6f9224481eba..8eb0a03671a5 100644 --- a/core/java/android/app/AppOpsManager.java +++ b/core/java/android/app/AppOpsManager.java @@ -1752,21 +1752,21 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // VIBRATE AppOpsManager.MODE_ALLOWED, // READ_CONTACTS AppOpsManager.MODE_ALLOWED, // WRITE_CONTACTS - AppOpsManager.MODE_DEFAULT, // READ_CALL_LOG - AppOpsManager.MODE_DEFAULT, // WRITE_CALL_LOG + AppOpsManager.MODE_ALLOWED, // READ_CALL_LOG + AppOpsManager.MODE_ALLOWED, // WRITE_CALL_LOG AppOpsManager.MODE_ALLOWED, // READ_CALENDAR AppOpsManager.MODE_ALLOWED, // WRITE_CALENDAR AppOpsManager.MODE_ALLOWED, // WIFI_SCAN AppOpsManager.MODE_ALLOWED, // POST_NOTIFICATION AppOpsManager.MODE_ALLOWED, // NEIGHBORING_CELLS AppOpsManager.MODE_ALLOWED, // CALL_PHONE - AppOpsManager.MODE_DEFAULT, // READ_SMS + AppOpsManager.MODE_ALLOWED, // READ_SMS AppOpsManager.MODE_IGNORED, // WRITE_SMS - AppOpsManager.MODE_DEFAULT, // RECEIVE_SMS + AppOpsManager.MODE_ALLOWED, // RECEIVE_SMS AppOpsManager.MODE_ALLOWED, // RECEIVE_EMERGENCY_BROADCAST - AppOpsManager.MODE_DEFAULT, // RECEIVE_MMS - AppOpsManager.MODE_DEFAULT, // RECEIVE_WAP_PUSH - AppOpsManager.MODE_DEFAULT, // SEND_SMS + AppOpsManager.MODE_ALLOWED, // RECEIVE_MMS + AppOpsManager.MODE_ALLOWED, // RECEIVE_WAP_PUSH + AppOpsManager.MODE_ALLOWED, // SEND_SMS AppOpsManager.MODE_ALLOWED, // READ_ICC_SMS AppOpsManager.MODE_ALLOWED, // WRITE_ICC_SMS AppOpsManager.MODE_DEFAULT, // WRITE_SETTINGS @@ -1800,10 +1800,10 @@ public class AppOpsManager { AppOpsManager.MODE_ALLOWED, // READ_PHONE_STATE AppOpsManager.MODE_ALLOWED, // ADD_VOICEMAIL AppOpsManager.MODE_ALLOWED, // USE_SIP - AppOpsManager.MODE_DEFAULT, // PROCESS_OUTGOING_CALLS + AppOpsManager.MODE_ALLOWED, // PROCESS_OUTGOING_CALLS AppOpsManager.MODE_ALLOWED, // USE_FINGERPRINT AppOpsManager.MODE_ALLOWED, // BODY_SENSORS - AppOpsManager.MODE_DEFAULT, // READ_CELL_BROADCASTS + AppOpsManager.MODE_ALLOWED, // READ_CELL_BROADCASTS AppOpsManager.MODE_ERRORED, // MOCK_LOCATION AppOpsManager.MODE_ALLOWED, // READ_EXTERNAL_STORAGE AppOpsManager.MODE_ALLOWED, // WRITE_EXTERNAL_STORAGE diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java index deb181f4fd0a..9bc5f8055617 100644 --- a/core/java/android/content/pm/ApplicationInfo.java +++ b/core/java/android/content/pm/ApplicationInfo.java @@ -679,13 +679,13 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { public static final int PRIVATE_FLAG_IS_RESOURCE_OVERLAY = 1 << 28; /** - * Value for {@link #privateFlags}: If {@code true} this app allows - * shared/external storage media to be a sandboxed view that only contains - * files owned by the app. + * Value for {@link #privateFlags}: If {@code true} this app requests + * full external storage access. The request may not be honored due to + * policy or other reasons. * * @hide */ - public static final int PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX = 1 << 29; + public static final int PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE = 1 << 29; /** * Value for {@link #privateFlags}: whether this app is pre-installed on the @@ -723,7 +723,7 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { PRIVATE_FLAG_HAS_FRAGILE_USER_DATA, PRIVATE_FLAG_ALLOW_CLEAR_USER_DATA_ON_FAILED_RESTORE, PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, - PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, + PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE, PRIVATE_FLAG_ODM, }) @Retention(RetentionPolicy.SOURCE) @@ -1858,13 +1858,12 @@ public class ApplicationInfo extends PackageItemInfo implements Parcelable { } /** - * If {@code true} this app allows shared/external storage media to be a - * sandboxed view that only contains files owned by the app. + * If {@code true} this app requested to run in the legacy storage mode. * * @hide */ - public boolean isExternalStorageSandboxAllowed() { - return (privateFlags & PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX) != 0; + public boolean hasRequestedLegacyExternalStorage() { + return (privateFlags & PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE) != 0; } private boolean isAllowedToUseHiddenApis() { diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 6ce682843ed5..d2f0fb3cc768 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -1505,13 +1505,14 @@ public class PackageInstaller { * allows the app to hold that permission and whitelisting a soft restricted * permission allows the app to hold the permission in its full, unrestricted form. * - * <p>The whitelisted permissions would be applied as the {@link - * PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist}. + * <p> Permissions can also be immutably restricted which means that the whitelist + * state of the permission can be determined only at install time and cannot be + * changed on updated or at a later point via the package manager APIs. * - * @param permissions The restricted permissions to whitelist. Pass - * {@link #RESTRICTED_PERMISSIONS_ALL} to whitelist all permissions and - * <code>null</code> to clear. If you want to whitelist some permissions - * (not all) the list must contain at least one permission. + * <p>The whitelisted non-immutably restricted permissions would be added to + * the {@link PackageManager#FLAG_PERMISSION_WHITELIST_INSTALLER installer whitelist} + * while the immutably restricted permissions would be added to the {@link + * PackageManager#FLAG_PERMISSION_WHITELIST_SYSTEM system whitelist} * * @see PackageManager#addWhitelistedRestrictedPermission(String, String, int) * @see PackageManager#removeWhitelistedRestrictedPermission(String, String, int) diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java index bdd80e325c4b..0ea5200d202a 100644 --- a/core/java/android/content/pm/PackageParser.java +++ b/core/java/android/content/pm/PackageParser.java @@ -3700,9 +3700,9 @@ public class PackageParser { } if (sa.getBoolean( - R.styleable.AndroidManifestApplication_allowExternalStorageSandbox, - owner.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.Q)) { - ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX; + R.styleable.AndroidManifestApplication_requestLegacyExternalStorage, + owner.applicationInfo.targetSdkVersion < Build.VERSION_CODES.Q)) { + ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE; } ai.maxAspectRatio = sa.getFloat(R.styleable.AndroidManifestApplication_maxAspectRatio, 0); diff --git a/core/java/android/content/pm/PermissionInfo.java b/core/java/android/content/pm/PermissionInfo.java index f838900d43d7..14340fe788f7 100644 --- a/core/java/android/content/pm/PermissionInfo.java +++ b/core/java/android/content/pm/PermissionInfo.java @@ -341,6 +341,17 @@ public class PermissionInfo extends PackageItemInfo implements Parcelable { public static final int FLAG_SOFT_RESTRICTED = 1<<3; /** + * Flag for {@link #flags}, corresponding to <code>immutablyRestricted</code> + * value of {@link android.R.attr#permissionFlags}. + * + * <p>This permission is restricted immutably which means that its + * restriction state may be specified only on the first install of + * the app and will stay in this initial whitelist state until + * the app is uninstalled. + */ + public static final int FLAG_IMMUTABLY_RESTRICTED = 1<<4; + + /** * Flag for {@link #flags}, indicating that this permission has been * installed into the system's globally defined permissions. */ diff --git a/core/java/android/os/Environment.java b/core/java/android/os/Environment.java index ed5c1b1e2277..dde1e6a7f5f7 100644 --- a/core/java/android/os/Environment.java +++ b/core/java/android/os/Environment.java @@ -1148,15 +1148,9 @@ public class Environment { final Context context = AppGlobals.getInitialApplication(); final AppOpsManager appOps = context.getSystemService(AppOpsManager.class); - final boolean hasLegacy = appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, + return appOps.checkOpNoThrow(AppOpsManager.OP_LEGACY_STORAGE, context.getApplicationInfo().uid, - context.getOpPackageName()) == AppOpsManager.MODE_ALLOWED; - - // STOPSHIP: only use app-op once permission model has fully landed - final boolean requestedLegacy = !AppGlobals.getInitialApplication().getApplicationInfo() - .isExternalStorageSandboxAllowed(); - - return !(hasLegacy || requestedLegacy); + context.getOpPackageName()) != AppOpsManager.MODE_ALLOWED; } static File getDirectory(String variableName, String defaultPath) { diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml index cc3b3a4c7ccb..8714bf2505bb 100644 --- a/core/res/AndroidManifest.xml +++ b/core/res/AndroidManifest.xml @@ -828,6 +828,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardRead" android:description="@string/permdesc_sdcardRead" + android:permissionFlags="softRestricted|immutablyRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to write to external storage. @@ -848,6 +849,7 @@ android:permissionGroup="android.permission-group.UNDEFINED" android:label="@string/permlab_sdcardWrite" android:description="@string/permdesc_sdcardWrite" + android:permissionFlags="softRestricted|immutablyRestricted" android:protectionLevel="dangerous" /> <!-- Allows an application to access any geographic locations persisted in the diff --git a/core/res/res/values/attrs_manifest.xml b/core/res/res/values/attrs_manifest.xml index bfa57e46984b..bbc784a10db3 100644 --- a/core/res/res/values/attrs_manifest.xml +++ b/core/res/res/values/attrs_manifest.xml @@ -328,6 +328,12 @@ would be granted. The weak grant depends on the permission. --> <flag name="softRestricted" value="0x8" /> + <!-- This permission is restricted immutably which means that its + restriction state may be specified only on the first install of + the app and will stay in this initial whitelist state until + the app is uninstalled. + --> + <flag name="immutablyRestricted" value="0x10" /> </attr> <!-- Specified the name of a group that this permission is associated @@ -1707,17 +1713,18 @@ See {@link android.media.AudioPlaybackCaptureConfiguration} for more detail. --> <attr name="allowAudioPlaybackCapture" format="boolean" /> - <!-- If {@code true} this app allows shared/external storage media to be - a sandboxed view that only contains files owned by the app. - <p> - Sandboxed apps can continue to discover and read media belonging to other - apps via {@code MediaStore}. + <!-- If {@code true} this app would like to run under the legacy storage + model. Note that this may not always be respected due to policy or + backwards compatibility reasons. + + <p>Apps not requesting legacy storage can continue to discover and + read media belonging to other apps via {@code MediaStore}. <p> The default value is: - - {@code true} for apps with targetSdkVersion >= 29 (Q). - - {@code false} for apps with targetSdkVersion < 29. + - {@code false} for apps with targetSdkVersion >= 29 (Q). + - {@code true} for apps with targetSdkVersion < 29. --> - <attr name="allowExternalStorageSandbox" format="boolean" /> + <attr name="requestLegacyExternalStorage" format="boolean" /> </declare-styleable> <!-- The <code>permission</code> tag declares a security permission that can be used to control access from other packages to specific components or diff --git a/core/res/res/values/public.xml b/core/res/res/values/public.xml index b7d61c8f8d39..a7af144ab9ed 100644 --- a/core/res/res/values/public.xml +++ b/core/res/res/values/public.xml @@ -2942,7 +2942,7 @@ <public name="allowClearUserDataOnFailedRestore"/> <public name="allowAudioPlaybackCapture"/> <public name="secureElementName" /> - <public name="allowExternalStorageSandbox"/> + <public name="requestLegacyExternalStorage"/> <public name="ensuringStatusBarContrastWhenTransparent" /> <public name="ensuringNavigationBarContrastWhenTransparent" /> <public name="identifier" /> diff --git a/services/core/java/com/android/server/StorageManagerService.java b/services/core/java/com/android/server/StorageManagerService.java index 382fdec94f08..cdc7b269a2a2 100644 --- a/services/core/java/com/android/server/StorageManagerService.java +++ b/services/core/java/com/android/server/StorageManagerService.java @@ -293,7 +293,6 @@ class StorageManagerService extends IStorageManager.Stub private static final String TAG_VOLUMES = "volumes"; private static final String ATTR_VERSION = "version"; private static final String ATTR_PRIMARY_STORAGE_UUID = "primaryStorageUuid"; - private static final String ATTR_ISOLATED_STORAGE = "isolatedStorage"; private static final String TAG_VOLUME = "volume"; private static final String ATTR_TYPE = "type"; private static final String ATTR_FS_UUID = "fsUuid"; @@ -349,10 +348,6 @@ class StorageManagerService extends IStorageManager.Stub @GuardedBy("mLock") private String mPrimaryStorageUuid; - /** Flag indicating isolated storage state of last boot */ - @GuardedBy("mLock") - private boolean mLastIsolatedStorage = false; - /** Map from disk ID to latches */ @GuardedBy("mLock") private ArrayMap<String, CountDownLatch> mDiskScanLatches = new ArrayMap<>(); @@ -1681,63 +1676,6 @@ class StorageManagerService extends IStorageManager.Stub mIAppOpsService.startWatchingMode(OP_REQUEST_INSTALL_PACKAGES, null, mAppOpsCallback); } catch (RemoteException e) { } - - synchronized (mLock) { - final boolean thisIsolatedStorage = StorageManager.hasIsolatedStorage(); - if (mLastIsolatedStorage != thisIsolatedStorage) { - if (thisIsolatedStorage) { - // This boot enables isolated storage; apply legacy behavior - applyLegacyStorage(); - } - - // Always remember the new state we just booted with - writeSettingsLocked(); - } - } - } - - /** - * If we're enabling isolated storage, we need to remember which existing - * apps have already been using shared storage, and grant them legacy access - * to keep them running smoothly. - * - * @see com.android.server.pm.permission.PermissionManagerService - * #applyLegacyStoragePermissionModel - */ - private void applyLegacyStorage() { - final AppOpsManager appOps = mContext.getSystemService(AppOpsManager.class); - final UserManagerInternal um = LocalServices.getService(UserManagerInternal.class); - for (int userId : um.getUserIds()) { - final UserHandle user = UserHandle.of(userId); - final PackageManager pm; - try { - pm = mContext.createPackageContextAsUser(mContext.getPackageName(), 0, - user).getPackageManager(); - } catch (PackageManager.NameNotFoundException e) { - throw new RuntimeException(e); - } - - final List<PackageInfo> pkgs = pm.getPackagesHoldingPermissions( - ALL_STORAGE_PERMISSIONS, - MATCH_UNINSTALLED_PACKAGES | MATCH_DIRECT_BOOT_AWARE | MATCH_DIRECT_BOOT_UNAWARE - | GET_PERMISSIONS); - for (PackageInfo pkg : pkgs) { - final int uid = pkg.applicationInfo.uid; - final String packageName = pkg.applicationInfo.packageName; - - final long lastAccess = getLastAccessTime(appOps, uid, packageName, new int[] { - AppOpsManager.OP_READ_EXTERNAL_STORAGE, - AppOpsManager.OP_WRITE_EXTERNAL_STORAGE, - }); - - Log.d(TAG, "Found " + uid + " " + packageName - + " with granted storage access, last accessed " + lastAccess); - if (lastAccess > 0) { - appOps.setUidMode(AppOpsManager.OP_LEGACY_STORAGE, uid, - AppOpsManager.MODE_ALLOWED); - } - } - } } private static long getLastAccessTime(AppOpsManager manager, @@ -1783,7 +1721,6 @@ class StorageManagerService extends IStorageManager.Stub private void readSettingsLocked() { mRecords.clear(); mPrimaryStorageUuid = getDefaultPrimaryStorageUuid(); - mLastIsolatedStorage = false; FileInputStream fis = null; try { @@ -1805,9 +1742,6 @@ class StorageManagerService extends IStorageManager.Stub mPrimaryStorageUuid = readStringAttribute(in, ATTR_PRIMARY_STORAGE_UUID); } - mLastIsolatedStorage = readBooleanAttribute(in, - ATTR_ISOLATED_STORAGE, false); - } else if (TAG_VOLUME.equals(tag)) { final VolumeRecord rec = readVolumeRecord(in); mRecords.put(rec.fsUuid, rec); @@ -1837,7 +1771,6 @@ class StorageManagerService extends IStorageManager.Stub out.startTag(null, TAG_VOLUMES); writeIntAttribute(out, ATTR_VERSION, VERSION_FIX_PRIMARY); writeStringAttribute(out, ATTR_PRIMARY_STORAGE_UUID, mPrimaryStorageUuid); - writeBooleanAttribute(out, ATTR_ISOLATED_STORAGE, StorageManager.hasIsolatedStorage()); final int size = mRecords.size(); for (int i = 0; i < size; i++) { final VolumeRecord rec = mRecords.valueAt(i); @@ -3800,11 +3733,7 @@ class StorageManagerService extends IStorageManager.Stub // they hold the runtime permission final boolean hasLegacy = mIAppOpsService.checkOperation(OP_LEGACY_STORAGE, uid, packageName) == MODE_ALLOWED; - // STOPSHIP: only use app-op once permission model has fully landed - final boolean requestedLegacy = !mIPackageManager - .getApplicationInfo(packageName, 0, UserHandle.getUserId(uid)) - .isExternalStorageSandboxAllowed(); - if ((hasLegacy || requestedLegacy) && hasStorage) { + if (hasLegacy && hasStorage) { return Zygote.MOUNT_EXTERNAL_LEGACY; } else { return Zygote.MOUNT_EXTERNAL_WRITE; diff --git a/services/core/java/com/android/server/appop/AppOpsService.java b/services/core/java/com/android/server/appop/AppOpsService.java index 873cadb5a9d9..cd8167ae4653 100644 --- a/services/core/java/com/android/server/appop/AppOpsService.java +++ b/services/core/java/com/android/server/appop/AppOpsService.java @@ -3097,6 +3097,7 @@ public class AppOpsService extends IAppOpsService.Stub { int nonpackageUid; final static Binder sBinder = new Binder(); IBinder mToken; + boolean targetsUid; Shell(IAppOpsService iface, AppOpsService internal) { mInterface = iface; @@ -3190,6 +3191,8 @@ public class AppOpsService extends IAppOpsService.Stub { for (String argument; (argument = getNextArg()) != null;) { if ("--user".equals(argument)) { userId = UserHandle.parseUserArg(getNextArgRequired()); + } else if ("--uid".equals(argument)) { + targetsUid = true; } else { if (packageName == null) { packageName = argument; @@ -3288,7 +3291,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" Starts a given operation for a particular application."); pw.println(" stop [--user <USER_ID>] <PACKAGE | UID> <OP> "); pw.println(" Stops a given operation for a particular application."); - pw.println(" set [--user <USER_ID>] <PACKAGE | UID> <OP> <MODE>"); + pw.println(" set [--user <USER_ID>] <--uid PACKAGE | PACKAGE | UID> <OP> <MODE>"); pw.println(" Set the mode for a particular application and operation."); pw.println(" get [--user <USER_ID>] <PACKAGE | UID> [<OP>]"); pw.println(" Return the mode for a particular application and optional operation."); @@ -3306,6 +3309,7 @@ public class AppOpsService extends IAppOpsService.Stub { pw.println(" <MODE> one of allow, ignore, deny, or default"); pw.println(" <USER_ID> the user id under which the package is installed. If --user is not"); pw.println(" specified, the current user is assumed."); + pw.println(" --uid PACKAGE refer to the UID of the package"); } static int onShellCommand(Shell shell, String cmd) { @@ -3332,9 +3336,17 @@ public class AppOpsService extends IAppOpsService.Stub { return -1; } - if (shell.packageName != null) { + if (!shell.targetsUid && shell.packageName != null) { shell.mInterface.setMode(shell.op, shell.packageUid, shell.packageName, mode); + } else if (shell.targetsUid && shell.packageName != null) { + try { + final int uid = shell.mInternal.mContext.getPackageManager() + .getPackageUid(shell.packageName, shell.userId); + shell.mInterface.setUidMode(shell.op, uid, mode); + } catch (PackageManager.NameNotFoundException e) { + return -1; + } } else { shell.mInterface.setUidMode(shell.op, shell.nonpackageUid, mode); } diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 7b418b51957c..e231eed3e2b6 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -5733,6 +5733,8 @@ public class PackageManagerService extends IPackageManager.Stub "getWhitelistedRestrictedPermissions for user " + userId); } + final PackageParser.Package pkg; + synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { @@ -5740,6 +5742,8 @@ public class PackageManagerService extends IPackageManager.Stub return null; } + pkg = packageSetting.pkg; + final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; @@ -5767,14 +5771,14 @@ public class PackageManagerService extends IPackageManager.Stub UserHandle.getCallingUserId())) { return null; } + } - final long identity = Binder.clearCallingIdentity(); - try { - return mPermissionManager.getWhitelistedRestrictedPermissions( - packageSetting.pkg, whitelistFlags, userId); - } finally { - Binder.restoreCallingIdentity(identity); - } + final long identity = Binder.clearCallingIdentity(); + try { + return mPermissionManager.getWhitelistedRestrictedPermissions( + pkg, whitelistFlags, userId); + } finally { + Binder.restoreCallingIdentity(identity); } } @@ -5785,6 +5789,10 @@ public class PackageManagerService extends IPackageManager.Stub // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); + if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { + return false; + } + List<String> permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions == null) { @@ -5792,13 +5800,31 @@ public class PackageManagerService extends IPackageManager.Stub } if (permissions.indexOf(permission) < 0) { permissions.add(permission); - setWhitelistedRestrictedPermissions(packageName, permissions, + return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); - return true; } return false; } + private boolean checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission( + @NonNull String permission) { + synchronized (mPackages) { + final BasePermission bp = mPermissionManager.getPermissionTEMP(permission); + if (bp == null) { + Slog.w(TAG, "No such permissions: " + permission); + return false; + } + if (bp.isHardOrSoftRestricted() && bp.isImmutablyRestricted() + && mContext.checkCallingOrSelfPermission( + Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) + != PackageManager.PERMISSION_GRANTED) { + throw new SecurityException("Cannot modify whitelisting of an immutably " + + "restricted permission: " + permission); + } + return true; + } + } + @Override public boolean removeWhitelistedRestrictedPermission(@NonNull String packageName, @NonNull String permission, @PermissionWhitelistFlags int whitelistFlags, @@ -5806,17 +5832,20 @@ public class PackageManagerService extends IPackageManager.Stub // Other argument checks are done in get/setWhitelistedRestrictedPermissions Preconditions.checkNotNull(permission); + if (!checkExistsAndEnforceCannotModifyImmutablyRestrictedPermission(permission)) { + return false; + } + final List<String> permissions = getWhitelistedRestrictedPermissions(packageName, whitelistFlags, userId); if (permissions != null && permissions.remove(permission)) { - setWhitelistedRestrictedPermissions(packageName, permissions, + return setWhitelistedRestrictedPermissions(packageName, permissions, whitelistFlags, userId); - return true; } return false; } - private void setWhitelistedRestrictedPermissions(@NonNull String packageName, + private boolean setWhitelistedRestrictedPermissions(@NonNull String packageName, @Nullable List<String> permissions, @PermissionWhitelistFlags int whitelistFlag, @UserIdInt int userId) { Preconditions.checkNotNull(packageName); @@ -5833,13 +5862,17 @@ public class PackageManagerService extends IPackageManager.Stub "setWhitelistedRestrictedPermissions for user " + userId); } + final PackageParser.Package pkg; + synchronized (mPackages) { final PackageSetting packageSetting = mSettings.mPackages.get(packageName); if (packageSetting == null) { Slog.w(TAG, "Unknown package: " + packageName); - return; + return false; } + pkg = packageSetting.pkg; + final boolean isCallerPrivileged = mContext.checkCallingOrSelfPermission( Manifest.permission.WHITELIST_RESTRICTED_PERMISSIONS) == PackageManager.PERMISSION_GRANTED; @@ -5864,7 +5897,7 @@ public class PackageManagerService extends IPackageManager.Stub packageName, whitelistFlag, userId); if (permissions == null || permissions.isEmpty()) { if (whitelistedPermissions == null || whitelistedPermissions.isEmpty()) { - return; + return true; } } else { // Only the system can add and remove while the installer can only remove. @@ -5890,18 +5923,20 @@ public class PackageManagerService extends IPackageManager.Stub if (filterAppAccessLPr(packageSetting, Binder.getCallingUid(), UserHandle.getCallingUserId())) { - return; + return false; } + } - final long identity = Binder.clearCallingIdentity(); - try { - mPermissionManager.setWhitelistedRestrictedPermissions(packageSetting.pkg, - new int[]{userId}, permissions, Process.myUid(), whitelistFlag, - mPermissionCallback); - } finally { - Binder.restoreCallingIdentity(identity); - } + final long identity = Binder.clearCallingIdentity(); + try { + mPermissionManager.setWhitelistedRestrictedPermissions(pkg, + new int[]{userId}, permissions, Process.myUid(), whitelistFlag, + mPermissionCallback); + } finally { + Binder.restoreCallingIdentity(identity); } + + return true; } @Override diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 41a8a776e3f7..5baeaf6595e0 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -4413,7 +4413,7 @@ public final class Settings { ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_RESIZEABLE_VIA_SDK_VERSION", ApplicationInfo.PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE, "PRIVATE_FLAG_ACTIVITIES_RESIZE_MODE_UNRESIZEABLE", ApplicationInfo.PRIVATE_FLAG_ALLOW_AUDIO_PLAYBACK_CAPTURE, "ALLOW_AUDIO_PLAYBACK_CAPTURE", - ApplicationInfo.PRIVATE_FLAG_ALLOW_EXTERNAL_STORAGE_SANDBOX, "ALLOW_EXTERNAL_STORAGE_SANDBOX", + ApplicationInfo.PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE, "PRIVATE_FLAG_REQUEST_LEGACY_EXTERNAL_STORAGE", ApplicationInfo.PRIVATE_FLAG_BACKUP_IN_FOREGROUND, "BACKUP_IN_FOREGROUND", ApplicationInfo.PRIVATE_FLAG_CANT_SAVE_STATE, "CANT_SAVE_STATE", ApplicationInfo.PRIVATE_FLAG_DEFAULT_TO_DEVICE_PROTECTED_STORAGE, "DEFAULT_TO_DEVICE_PROTECTED_STORAGE", @@ -4737,7 +4737,7 @@ public final class Settings { } pw.print(prefix); pw.print(" "); pw.print(perm); final BasePermission bp = mPermissions.getPermission(perm); - if (bp != null && bp.isRestricted()) { + if (bp != null && bp.isHardOrSoftRestricted()) { pw.println(": restricted=true"); } else { pw.println(); diff --git a/services/core/java/com/android/server/pm/permission/BasePermission.java b/services/core/java/com/android/server/pm/permission/BasePermission.java index 490c647c22b1..6d22faa7032e 100644 --- a/services/core/java/com/android/server/pm/permission/BasePermission.java +++ b/services/core/java/com/android/server/pm/permission/BasePermission.java @@ -203,12 +203,17 @@ public final class BasePermission { && (perm.info.flags & PermissionInfo.FLAG_HARD_RESTRICTED) != 0; } - public boolean isRestricted() { + public boolean isHardOrSoftRestricted() { return perm != null && perm.info != null && (perm.info.flags & (PermissionInfo.FLAG_HARD_RESTRICTED | PermissionInfo.FLAG_SOFT_RESTRICTED)) != 0; } + public boolean isImmutablyRestricted() { + return perm != null && perm.info != null + && (perm.info.flags & PermissionInfo.FLAG_IMMUTABLY_RESTRICTED) != 0; + } + public boolean isSignature() { return (protectionLevel & PermissionInfo.PROTECTION_MASK_BASE) == PermissionInfo.PROTECTION_SIGNATURE; 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 dd63e3ca290e..9ede263284a1 100644 --- a/services/core/java/com/android/server/pm/permission/PermissionManagerService.java +++ b/services/core/java/com/android/server/pm/permission/PermissionManagerService.java @@ -2059,7 +2059,7 @@ public class PermissionManagerService { return; } - if (RESTRICTED_PERMISSIONS_ENABLED && bp.isRestricted() + if (RESTRICTED_PERMISSIONS_ENABLED && bp.isHardOrSoftRestricted() && (flags & PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT) == 0) { Log.e(TAG, "Cannot grant restricted non-exempt permission " + permName + " for package " + packageName); @@ -2241,7 +2241,7 @@ public class PermissionManagerService { continue; } - if (!bp.isRestricted()) { + if (!bp.isHardOrSoftRestricted()) { continue; } @@ -2294,13 +2294,16 @@ public class PermissionManagerService { updatePermissions = true; + final boolean wasWhitelisted = (oldFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + final boolean isWhitelisted = (newFlags + & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; + // If the permission is policy fixed as granted but it is no longer // on any of the whitelists we need to clear the policy fixed flag // as whitelisting trumps policy i.e. policy cannot grant a non // grantable permission. if ((oldFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0) { - final boolean isWhitelisted = (newFlags - & (PackageManager.FLAGS_PERMISSION_RESTRICTION_ANY_EXEMPT)) != 0; final boolean isGranted = permissionsState.hasPermission(permissionName, userId); if (!isWhitelisted && isGranted) { mask |= PackageManager.FLAG_PERMISSION_POLICY_FIXED; @@ -2308,6 +2311,14 @@ public class PermissionManagerService { } } + // If we are whitelisting an app that does not support runtime permissions + // we need to make sure it goes through the permission review UI at launch. + if (pkg.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M + && !wasWhitelisted && isWhitelisted) { + mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + newFlags |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED; + } + updatePermissionFlags(permissionName, pkg.packageName, mask, newFlags, callingUid, userId, false, null /*callback*/); } diff --git a/services/core/java/com/android/server/policy/PermissionPolicyService.java b/services/core/java/com/android/server/policy/PermissionPolicyService.java index 1fd8b711d348..a280d83fac27 100644 --- a/services/core/java/com/android/server/policy/PermissionPolicyService.java +++ b/services/core/java/com/android/server/policy/PermissionPolicyService.java @@ -19,6 +19,7 @@ package com.android.server.policy; import static android.content.pm.PackageManager.FLAG_PERMISSION_APPLY_RESTRICTION; import static android.content.pm.PackageManager.GET_PERMISSIONS; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; @@ -36,9 +37,7 @@ import android.os.Process; import android.os.UserHandle; import android.permission.PermissionControllerManager; import android.permission.PermissionManagerInternal; -import android.util.ArraySet; import android.util.Slog; -import android.util.SparseArray; import android.util.SparseIntArray; import com.android.server.FgThread; @@ -46,7 +45,6 @@ import com.android.server.LocalServices; import com.android.server.SystemService; import java.util.ArrayList; -import java.util.List; import java.util.concurrent.CountDownLatch; /** @@ -57,17 +55,10 @@ import java.util.concurrent.CountDownLatch; * and app ops - and vise versa. */ public final class PermissionPolicyService extends SystemService { - private static final String PLATFORM_PACKAGE = "android"; - private static final String LOG_TAG = PermissionPolicyService.class.getSimpleName(); - // No need to lock as this is populated on boot when the OS is - // single threaded and is never mutated until a reboot. - private static final ArraySet<String> sAllRestrictedPermissions = new ArraySet<>(); - public PermissionPolicyService(@NonNull Context context) { super(context); - cacheAllRestrictedPermissions(context); } @Override @@ -101,20 +92,6 @@ public final class PermissionPolicyService extends SystemService { startWatchingRuntimePermissionChanges(getContext(), userId); } - private static void cacheAllRestrictedPermissions(@NonNull Context context) { - try { - final PackageInfo packageInfo = context.getPackageManager() - .getPackageInfo(PLATFORM_PACKAGE, PackageManager.GET_PERMISSIONS); - for (PermissionInfo permissionInfo : packageInfo.permissions) { - if (permissionInfo.isRestricted()) { - sAllRestrictedPermissions.add(permissionInfo.name); - } - } - } catch (NameNotFoundException impossible) { - /* cannot happen */ - } - } - private static void grantOrUpgradeDefaultRuntimePermissionsInNeeded(@NonNull Context context, @UserIdInt int userId) { final PackageManagerInternal packageManagerInternal = LocalServices.getService( @@ -162,7 +139,7 @@ public final class PermissionPolicyService extends SystemService { } private static @Nullable Context getUserContext(@NonNull Context context, - @NonNull UserHandle user) { + @Nullable UserHandle user) { if (context.getUser().equals(user)) { return context; } else { @@ -230,18 +207,31 @@ public final class PermissionPolicyService extends SystemService { private final @NonNull SparseIntArray mAllUids = new SparseIntArray(); /** - * All ops that need to be restricted + * All ops that need to be set to default + * + * Currently, only used by the restricted permissions logic. * * @see #syncRestrictedOps */ - private final @NonNull ArrayList<OpToRestrict> mOpsToRestrict = new ArrayList<>(); + private final @NonNull ArrayList<OpToRestrict> mOpsToDefault = new ArrayList<>(); /** - * All ops that need to be unrestricted + * All ops that need to be flipped to allow if default. + * + * Currently, only used by the restricted permissions logic. * * @see #syncRestrictedOps */ - private final @NonNull ArrayList<OpToUnrestrict> mOpsToUnrestrict = new ArrayList<>(); + private final @NonNull ArrayList<OpToUnrestrict> mOpsToAllow = new ArrayList<>(); + + /** + * All ops that need to be flipped to ignore if default. + * + * Currently, only used by the restricted permissions logic. + * + * @see #syncRestrictedOps + */ + private final @NonNull ArrayList<OpToUnrestrict> mOpsToIgnore = new ArrayList<>(); /** * All foreground permissions @@ -262,89 +252,20 @@ public final class PermissionPolicyService extends SystemService { * <p>This processes ops previously added by {@link #addOpIfRestricted} */ private void syncRestrictedOps() { - final SparseIntArray unprocessedUids = mAllUids.clone(); - - // TRICKY: we set the app op for a restricted permission to allow if the app - // requesting the permission is whitelisted and to deny if the app requesting - // the permission is not whitelisted. However, there is another case where an - // app in a shared user can access a component in another app in the same shared - // user due to being in the same shared user and not by having the permission - // that guards the component form the rest of the world. We need to handle this. - // The way we do this is by setting app ops corresponding to non requested - // restricted permissions to allow as this would allow the shared uid access - // case and be okay for other apps as they would not have the permission and - // would fail on the permission checks before reaching the app op check. - final SparseArray<List<String>> unrequestedRestrictedPermissionsForUid = - new SparseArray<>(); - - final int unrestrictCount = mOpsToUnrestrict.size(); - for (int i = 0; i < unrestrictCount; i++) { - final OpToUnrestrict op = mOpsToUnrestrict.get(i); - setUidModeAllowed(op.code, op.uid, op.packageName); - - // Keep track this permission was requested by the UID. - List<String> unrequestedRestrictedPermissions = - unrequestedRestrictedPermissionsForUid.get(op.uid); - if (unrequestedRestrictedPermissions == null) { - unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions); - unrequestedRestrictedPermissionsForUid.put(op.uid, - unrequestedRestrictedPermissions); - } - unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code)); - - unprocessedUids.delete(op.uid); + final int allowCount = mOpsToAllow.size(); + for (int i = 0; i < allowCount; i++) { + final OpToUnrestrict op = mOpsToAllow.get(i); + setUidModeAllowedIfDefault(op.code, op.uid, op.packageName); } - final int restrictCount = mOpsToRestrict.size(); - for (int i = 0; i < restrictCount; i++) { - final OpToRestrict op = mOpsToRestrict.get(i); - setUidModeDefault(op.code, op.uid); - - // Keep track this permission was requested by the UID. - List<String> unrequestedRestrictedPermissions = - unrequestedRestrictedPermissionsForUid.get(op.uid); - if (unrequestedRestrictedPermissions == null) { - unrequestedRestrictedPermissions = new ArrayList<>(sAllRestrictedPermissions); - unrequestedRestrictedPermissionsForUid.put(op.uid, - unrequestedRestrictedPermissions); - } - unrequestedRestrictedPermissions.remove(AppOpsManager.opToPermission(op.code)); - - unprocessedUids.delete(op.uid); - } - - // Give root access - unprocessedUids.put(Process.ROOT_UID, Process.ROOT_UID); - - // Add records for UIDs that don't use any restricted permissions. - final int uidCount = unprocessedUids.size(); - for (int i = 0; i < uidCount; i++) { - final int uid = unprocessedUids.keyAt(i); - unrequestedRestrictedPermissionsForUid.put(uid, - new ArrayList<>(sAllRestrictedPermissions)); + final int ignoreCount = mOpsToIgnore.size(); + for (int i = 0; i < ignoreCount; i++) { + final OpToUnrestrict op = mOpsToIgnore.get(i); + setUidModeIgnoredIfDefault(op.code, op.uid, op.packageName); } - - // Flip ops for all unrequested restricted permission for the UIDs. - final int unrequestedUidCount = unrequestedRestrictedPermissionsForUid.size(); - for (int i = 0; i < unrequestedUidCount; i++) { - final List<String> unrequestedRestrictedPermissions = - unrequestedRestrictedPermissionsForUid.valueAt(i); - if (unrequestedRestrictedPermissions != null) { - final int uid = unrequestedRestrictedPermissionsForUid.keyAt(i); - final String[] packageNames = (uid != Process.ROOT_UID) - ? mPackageManager.getPackagesForUid(uid) - : new String[] {"root"}; - if (packageNames == null) { - continue; - } - final int permissionCount = unrequestedRestrictedPermissions.size(); - for (int j = 0; j < permissionCount; j++) { - final String permission = unrequestedRestrictedPermissions.get(j); - for (String packageName : packageNames) { - setUidModeAllowed(AppOpsManager.permissionToOpCode(permission), uid, - packageName); - } - } - } + final int defaultCount = mOpsToDefault.size(); + for (int i = 0; i < defaultCount; i++) { + final OpToRestrict op = mOpsToDefault.get(i); + setUidModeDefault(op.code, op.uid); } } @@ -411,12 +332,27 @@ public final class PermissionPolicyService extends SystemService { if (permissionInfo.isHardRestricted()) { if (applyRestriction) { - mOpsToRestrict.add(new OpToRestrict(uid, opCode)); + mOpsToDefault.add(new OpToRestrict(uid, opCode)); } else { - mOpsToUnrestrict.add(new OpToUnrestrict(uid, pkg.packageName, opCode)); + mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, opCode)); } } else if (permissionInfo.isSoftRestricted()) { - //TODO: Implement soft restrictions like storage here. + // Storage uses a special app op to decide the mount state and + // supports soft restriction where the restricted state allows + // the permission but only for accessing the medial collections. + if (Manifest.permission.READ_EXTERNAL_STORAGE.equals(permission) + || Manifest.permission.WRITE_EXTERNAL_STORAGE.equals(permission)) { + if (applyRestriction) { + mOpsToDefault.add(new OpToRestrict(uid, + AppOpsManager.OP_LEGACY_STORAGE)); + } else if (pkg.applicationInfo.hasRequestedLegacyExternalStorage()) { + mOpsToAllow.add(new OpToUnrestrict(uid, pkg.packageName, + AppOpsManager.OP_LEGACY_STORAGE)); + } else { + mOpsToIgnore.add(new OpToUnrestrict(uid, pkg.packageName, + AppOpsManager.OP_LEGACY_STORAGE)); + } + } } } @@ -474,11 +410,20 @@ public final class PermissionPolicyService extends SystemService { } } - private void setUidModeAllowed(int opCode, int uid, @NonNull String packageName) { + private void setUidModeAllowedIfDefault(int opCode, int uid, @NonNull String packageName) { + setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_ALLOWED, packageName); + } + + private void setUidModeIgnoredIfDefault(int opCode, int uid, @NonNull String packageName) { + setUidModeIfDefault(opCode, uid, AppOpsManager.MODE_IGNORED, packageName); + } + + private void setUidModeIfDefault(int opCode, int uid, int mode, + @NonNull String packageName) { final int currentMode = mAppOpsManager.unsafeCheckOpRaw(AppOpsManager .opToPublicName(opCode), uid, packageName); if (currentMode == AppOpsManager.MODE_DEFAULT) { - mAppOpsManager.setUidMode(opCode, uid, AppOpsManager.MODE_ALLOWED); + mAppOpsManager.setUidMode(opCode, uid, mode); } } |