Allow callers with QUARANTINE_APPS permission to quarantine apps.
Bug: 301109246
Test: atest tests/tests/content/src/android/content/pm/cts/PackageManagerTest.java
Change-Id: I4e33009a2328554c093d3939fc9a183aaa75d68c
diff --git a/core/api/system-current.txt b/core/api/system-current.txt
index 346d62b..e24b290 100644
--- a/core/api/system-current.txt
+++ b/core/api/system-current.txt
@@ -3937,7 +3937,7 @@
method @RequiresPermission(android.Manifest.permission.SET_HARMFUL_APP_WARNINGS) public void setHarmfulAppWarning(@NonNull String, @Nullable CharSequence);
method @Deprecated @Nullable @RequiresPermission(android.Manifest.permission.SUSPEND_APPS) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable String);
method @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo);
- method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(value=android.Manifest.permission.SUSPEND_APPS, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
+ method @FlaggedApi("android.content.pm.quarantined_enabled") @Nullable @RequiresPermission(anyOf={android.Manifest.permission.SUSPEND_APPS, android.Manifest.permission.QUARANTINE_APPS}, conditional=true) public String[] setPackagesSuspended(@Nullable String[], boolean, @Nullable android.os.PersistableBundle, @Nullable android.os.PersistableBundle, @Nullable android.content.pm.SuspendDialogInfo, int);
method @RequiresPermission(value=android.Manifest.permission.CHANGE_COMPONENT_ENABLED_STATE, conditional=true) public void setSyntheticAppDetailsActivityEnabled(@NonNull String, boolean);
method public void setSystemAppState(@NonNull String, int);
method @RequiresPermission(android.Manifest.permission.INSTALL_PACKAGES) public abstract void setUpdateAvailable(@NonNull String, boolean);
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index e2a5747..0aadd60 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -9781,7 +9781,8 @@
* launcher to support customization that they might need to handle the suspended state.
*
* <p>The caller must hold {@link Manifest.permission#SUSPEND_APPS} to use this API except for
- * device owner and profile owner.
+ * device owner and profile owner or the {@link Manifest.permission#QUARANTINE_APPS} if the
+ * caller is using {@link #FLAG_SUSPEND_QUARANTINED}.
*
* @param packageNames The names of the packages to set the suspended status.
* @param suspended If set to {@code true}, the packages will be suspended, if set to
@@ -9809,7 +9810,10 @@
*/
@SystemApi
@FlaggedApi(android.content.pm.Flags.FLAG_QUARANTINED_ENABLED)
- @RequiresPermission(value=Manifest.permission.SUSPEND_APPS, conditional=true)
+ @RequiresPermission(anyOf = {
+ Manifest.permission.SUSPEND_APPS,
+ Manifest.permission.QUARANTINE_APPS
+ }, conditional = true)
@SuppressLint("NullableCollection")
@Nullable
public String[] setPackagesSuspended(@Nullable String[] packageNames, boolean suspended,
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index ec302e7..e5dac6f 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -2365,7 +2365,7 @@
them from running without explicit user action.
-->
<permission android:name="android.permission.QUARANTINE_APPS"
- android:protectionLevel="internal|verifier" />
+ android:protectionLevel="signature|verifier" />
<!-- Allows applications to discover and pair bluetooth devices.
<p>Protection level: normal
diff --git a/packages/Shell/AndroidManifest.xml b/packages/Shell/AndroidManifest.xml
index 10d04d3..ab11242 100644
--- a/packages/Shell/AndroidManifest.xml
+++ b/packages/Shell/AndroidManifest.xml
@@ -325,6 +325,7 @@
<uses-permission android:name="android.permission.CONTROL_KEYGUARD" />
<uses-permission android:name="android.permission.CONTROL_REMOTE_APP_TRANSITION_ANIMATIONS" />
<uses-permission android:name="android.permission.SUSPEND_APPS" />
+ <uses-permission android:name="android.permission.QUARANTINE_APPS" />
<uses-permission android:name="android.permission.OBSERVE_APP_USAGE" />
<uses-permission android:name="android.permission.READ_CLIPBOARD_IN_BACKGROUND" />
<!-- Permission needed to wipe the device for Test Harness Mode -->
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index e5f7962..fd09fea 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -3105,7 +3105,8 @@
}
private void enforceCanSetPackagesSuspendedAsUser(@NonNull Computer snapshot,
- String callingPackage, int callingUid, int userId, String callingMethod) {
+ boolean quarantined, String callingPackage, int callingUid, int userId,
+ String callingMethod) {
if (callingUid == Process.ROOT_UID
// Need to compare app-id to allow system dialogs access on secondary users
|| UserHandle.getAppId(callingUid) == Process.SYSTEM_UID) {
@@ -3120,8 +3121,20 @@
}
}
- mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
- callingMethod);
+ if (quarantined) {
+ final boolean hasQuarantineAppsPerm = mContext.checkCallingOrSelfPermission(
+ android.Manifest.permission.QUARANTINE_APPS) == PERMISSION_GRANTED;
+ // TODO: b/305256093 - In order to facilitate testing, temporarily allowing apps
+ // with SUSPEND_APPS permission to quarantine apps. Remove this once the testing
+ // is done and this is no longer needed.
+ if (!hasQuarantineAppsPerm) {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ callingMethod);
+ }
+ } else {
+ mContext.enforceCallingOrSelfPermission(android.Manifest.permission.SUSPEND_APPS,
+ callingMethod);
+ }
final int packageUid = snapshot.getPackageUid(callingPackage, 0, userId);
final boolean allowedPackageUid = packageUid == callingUid;
@@ -6136,9 +6149,6 @@
PersistableBundle appExtras, PersistableBundle launcherExtras,
SuspendDialogInfo dialogInfo, int flags, String callingPackage, int userId) {
final int callingUid = Binder.getCallingUid();
- final Computer snapshot = snapshotComputer();
- enforceCanSetPackagesSuspendedAsUser(snapshot, callingPackage, callingUid, userId,
- "setPackagesSuspendedAsUser");
boolean quarantined = false;
if (Flags.quarantinedEnabled()) {
if ((flags & PackageManager.FLAG_SUSPEND_QUARANTINED) != 0) {
@@ -6149,6 +6159,9 @@
quarantined = callingPackage.equals(wellbeingPkg);
}
}
+ final Computer snapshot = snapshotComputer();
+ enforceCanSetPackagesSuspendedAsUser(snapshot, quarantined, callingPackage, callingUid,
+ userId, "setPackagesSuspendedAsUser");
return mSuspendPackageHelper.setPackagesSuspended(snapshot, packageNames, suspended,
appExtras, launcherExtras, dialogInfo, callingPackage, userId, callingUid,
quarantined);