Wire call to suspend a package
Adds APIs in DevicePolicyManager and PackageManager for allowing
a device admin to suspend a package. PackageManagerService sets
or unsets a new PackageUserState 'suspended' setting. Terminal
command to suspend/unsuspend has been added via
PackageManagerShellCommand (as root).
Next steps:
* use the new 'suspended' setting for denying access to start app
(probably in ActivityStackSupervisor)
* broadcast a PACKAGE_(UN)SUSPENDED intent for launchers to pick up
* remove app from recents (go further and kill it if it is running)
* erase existing notifications for this app
Bug: 22776576
Change-Id: I718b3498f6a53cc0c6fdfb6d15031e53ddca4353
diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index 460e68c..77721e6 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -1858,6 +1858,16 @@
}
@Override
+ public boolean setPackageSuspendedAsUser(String packageName, boolean suspended, int userId) {
+ try {
+ return mPM.setPackageSuspendedAsUser(packageName, suspended, userId);
+ } catch (RemoteException e) {
+ // Should never happen!
+ }
+ return false;
+ }
+
+ @Override
public void getPackageSizeInfo(String packageName, int userHandle,
IPackageStatsObserver observer) {
try {
diff --git a/core/java/android/app/admin/DevicePolicyManager.java b/core/java/android/app/admin/DevicePolicyManager.java
index 08b7d62..660ce3b 100644
--- a/core/java/android/app/admin/DevicePolicyManager.java
+++ b/core/java/android/app/admin/DevicePolicyManager.java
@@ -3092,6 +3092,48 @@
}
/**
+ * Called by device or profile owners for setting the package suspended for this user.
+ * A suspended package will not be started by the package manager, its notifications will
+ * be hidden and it will not show up in recents. The package must already be installed.
+ *
+ * @param admin The name of the admin component to check.
+ * @param packageName The package name of the app to suspend or unsuspend.
+ * @param suspended If set to {@code true} than the package will be suspended, if set to
+ * {@code false} the package will be unsuspended.
+ * @return boolean {@code true} if the operation was successfully performed, {@code false}
+ * otherwise.
+ */
+ public boolean setPackageSuspended(@NonNull ComponentName admin, String packageName,
+ boolean suspended) {
+ if (mService != null) {
+ try {
+ return mService.setPackageSuspended(admin, packageName, suspended);
+ } catch (RemoteException re) {
+ Log.w(TAG, "Failed talking with device policy service", re);
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Called by device or profile owners to determine if a package is suspended.
+ *
+ * @param admin Which {@link DeviceAdminReceiver} this request is associated with.
+ * @param packageName The name of the package to retrieve the suspended status of.
+ * @return boolean {@code true} if the package is suspended, {@code false} otherwise.
+ */
+ public boolean getPackageSuspended(@NonNull ComponentName admin, String packageName) {
+ if (mService != null) {
+ try {
+ return mService.getPackageSuspended(admin, packageName);
+ } catch (RemoteException e) {
+ Log.w(TAG, "Failed talking with device policy service", e);
+ }
+ }
+ return false;
+ }
+
+ /**
* Sets the enabled state of the profile. A profile should be enabled only once it is ready to
* be used. Only the profile owner can call this.
*
diff --git a/core/java/android/app/admin/IDevicePolicyManager.aidl b/core/java/android/app/admin/IDevicePolicyManager.aidl
index 1708ee3..d8cc2ea 100644
--- a/core/java/android/app/admin/IDevicePolicyManager.aidl
+++ b/core/java/android/app/admin/IDevicePolicyManager.aidl
@@ -130,6 +130,9 @@
boolean setDeviceOwnerLockScreenInfo(in ComponentName who, String deviceOwnerInfo);
String getDeviceOwnerLockScreenInfo();
+ boolean setPackageSuspended(in ComponentName admin, String packageName, boolean suspended);
+ boolean getPackageSuspended(in ComponentName admin, String packageName);
+
boolean installCaCert(in ComponentName admin, in byte[] certBuffer);
void uninstallCaCerts(in ComponentName admin, in String[] aliases);
void enforceCanManageCaCerts(in ComponentName admin);
diff --git a/core/java/android/content/pm/ApplicationInfo.java b/core/java/android/content/pm/ApplicationInfo.java
index 1eeace1..807c0a2 100644
--- a/core/java/android/content/pm/ApplicationInfo.java
+++ b/core/java/android/content/pm/ApplicationInfo.java
@@ -382,6 +382,12 @@
public static final int FLAG_HARDWARE_ACCELERATED = 1<<29;
/**
+ * Value for {@link #flags}: true if this application's package is in
+ * the suspended state.
+ */
+ public static final int FLAG_SUSPENDED = 1<<30;
+
+ /**
* Value for {@link #flags}: true if code from this application will need to be
* loaded into other applications' processes. On devices that support multiple
* instruction sets, this implies the code might be loaded into a process that's
diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl
index b947a2b..4d0d146 100644
--- a/core/java/android/content/pm/IPackageManager.aidl
+++ b/core/java/android/content/pm/IPackageManager.aidl
@@ -285,6 +285,8 @@
void clearCrossProfileIntentFilters(int sourceUserId, String ownerPackage);
+ boolean setPackageSuspendedAsUser(String packageName, boolean suspended, int userId);
+
/**
* Backup/restore support - only the system uid may use these.
*/
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index c40c8a6..3e7deb9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -4684,6 +4684,21 @@
throw new UnsupportedOperationException();
}
+ /**
+ * Puts the package in a suspended state, making the package un-runnable,
+ * but it doesn't remove the data or the actual package file. The application notifications
+ * will be hidden and also the application will not show up in recents.
+ *
+ * @param packageName The name of the package to set the suspended status.
+ * @param suspended If set to {@code true} than the package will be suspended, if set to
+ * {@code false} the package will be unsuspended.
+ * @param userId The user id.
+ *
+ * @hide
+ */
+ public abstract boolean setPackageSuspendedAsUser(
+ String packageName, boolean suspended, int userId);
+
/** {@hide} */
public static boolean isMoveStatusFinished(int status) {
return (status < 0 || status > 100);
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index b79b6b6..019ed2b 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -4883,6 +4883,11 @@
} else {
ai.flags &= ~ApplicationInfo.FLAG_INSTALLED;
}
+ if (state.suspended) {
+ ai.flags |= ApplicationInfo.FLAG_SUSPENDED;
+ } else {
+ ai.flags &= ~ApplicationInfo.FLAG_SUSPENDED;
+ }
if (state.hidden) {
ai.privateFlags |= ApplicationInfo.PRIVATE_FLAG_HIDDEN;
} else {
diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java
index 9b28401..91fdf7f 100644
--- a/core/java/android/content/pm/PackageUserState.java
+++ b/core/java/android/content/pm/PackageUserState.java
@@ -29,6 +29,7 @@
public boolean notLaunched;
public boolean installed;
public boolean hidden; // Is the app restricted by owner / admin
+ public boolean suspended;
public int enabled;
public boolean blockUninstall;
@@ -43,6 +44,7 @@
public PackageUserState() {
installed = true;
hidden = false;
+ suspended = false;
enabled = COMPONENT_ENABLED_STATE_DEFAULT;
domainVerificationStatus =
PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED;
@@ -54,6 +56,7 @@
notLaunched = o.notLaunched;
enabled = o.enabled;
hidden = o.hidden;
+ suspended = o.suspended;
lastDisableAppCaller = o.lastDisableAppCaller;
disabledComponents = o.disabledComponents != null
? new ArraySet<>(o.disabledComponents) : null;