Add support for uninstalling apex to PackageManagerShellCommand
Since uninstalling apexes is development/testing operation, only root
and shell are allowed to perform it.
Test: adb uninstall + adb reboot
Bug: 123667725
Change-Id: Ifd7b743b2581dbdeebd63c33fd343a830dfc377a
diff --git a/services/core/java/com/android/server/pm/ApexManager.java b/services/core/java/com/android/server/pm/ApexManager.java
index a4c04b2..5fdd872 100644
--- a/services/core/java/com/android/server/pm/ApexManager.java
+++ b/services/core/java/com/android/server/pm/ApexManager.java
@@ -41,6 +41,7 @@
import java.io.PrintWriter;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;
@@ -135,6 +136,17 @@
}
/**
+ * Checks if {@code packageName} is an apex package.
+ *
+ * @param packageName package to check.
+ * @return {@code true} if {@code packageName} is an apex package.
+ */
+ boolean isApexPackage(String packageName) {
+ populateActivePackagesCacheIfNeeded();
+ return mActivePackagesCache.containsKey(packageName);
+ }
+
+ /**
* Retrieves information about an apexd staged session i.e. the internal state used by apexd to
* track the different states of a session.
*
@@ -246,6 +258,23 @@
}
/**
+ * Uninstalls given {@code apexPackage}.
+ *
+ * <p>NOTE. Device must be rebooted in order for uninstall to take effect.
+ *
+ * @param apexPackagePath package to uninstall.
+ * @return {@code true} upon successful uninstall, {@code false} otherwise.
+ */
+ boolean uninstallApex(String apexPackagePath) {
+ try {
+ mApexService.unstagePackages(Collections.singletonList(apexPackagePath));
+ return true;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
* Dumps various state information to the provided {@link PrintWriter} object.
*
* @param pw the {@link PrintWriter} object to send information to.
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 6f006e7..1f87ae8 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -23902,6 +23902,50 @@
mDefaultHomeProvider = provider;
}
}
+
+ @Override
+ public boolean isApexPackage(String packageName) {
+ return PackageManagerService.this.mApexManager.isApexPackage(packageName);
+ }
+
+ @Override
+ public void uninstallApex(String packageName, long versionCode, int userId,
+ IntentSender intentSender) {
+ final int callerUid = Binder.getCallingUid();
+ if (callerUid != Process.ROOT_UID && callerUid != Process.SHELL_UID) {
+ throw new SecurityException("Not allowed to uninstall apexes");
+ }
+ PackageInstallerService.PackageDeleteObserverAdapter adapter =
+ new PackageInstallerService.PackageDeleteObserverAdapter(
+ PackageManagerService.this.mContext, intentSender, packageName,
+ false, userId);
+ if (userId != UserHandle.USER_ALL) {
+ adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
+ "Can't uninstall an apex for a single user");
+ return;
+ }
+ final ApexManager am = PackageManagerService.this.mApexManager;
+ PackageInfo activePackage = am.getActivePackage(packageName);
+ if (activePackage == null) {
+ adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
+ packageName + " is not an apex package");
+ return;
+ }
+ if (versionCode != PackageManager.VERSION_CODE_HIGHEST
+ && activePackage.getLongVersionCode() != versionCode) {
+ adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
+ "Active version " + activePackage.getLongVersionCode()
+ + " is not equal to " + versionCode + "]");
+ return;
+ }
+ if (!am.uninstallApex(activePackage.applicationInfo.sourceDir)) {
+ adapter.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_ABORTED,
+ "Failed to uninstall apex " + packageName);
+ } else {
+ adapter.onPackageDeleted(packageName, PackageManager.DELETE_SUCCEEDED,
+ null);
+ }
+ }
}
@GuardedBy("mPackages")
diff --git a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
index 912a50e..2d1afa7 100644
--- a/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
+++ b/services/core/java/com/android/server/pm/PackageManagerShellCommand.java
@@ -1619,30 +1619,36 @@
}
userId = translateUserId(userId, true /*allowAll*/, "runUninstall");
- if (userId == UserHandle.USER_ALL) {
- userId = UserHandle.USER_SYSTEM;
- flags |= PackageManager.DELETE_ALL_USERS;
- } else {
- final PackageInfo info = mInterface.getPackageInfo(packageName,
- PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
- if (info == null) {
- pw.println("Failure [not installed for " + userId + "]");
- return 1;
- }
- final boolean isSystem =
- (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
- // If we are being asked to delete a system app for just one
- // user set flag so it disables rather than reverting to system
- // version of the app.
- if (isSystem) {
- flags |= PackageManager.DELETE_SYSTEM_APP;
- }
- }
-
final LocalIntentReceiver receiver = new LocalIntentReceiver();
- mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
- versionCode), null /*callerPackageName*/, flags,
- receiver.getIntentSender(), userId);
+ PackageManagerInternal internal = LocalServices.getService(PackageManagerInternal.class);
+
+ if (internal.isApexPackage(packageName)) {
+ internal.uninstallApex(packageName, versionCode, userId, receiver.getIntentSender());
+ } else {
+ if (userId == UserHandle.USER_ALL) {
+ userId = UserHandle.USER_SYSTEM;
+ flags |= PackageManager.DELETE_ALL_USERS;
+ } else {
+ final PackageInfo info = mInterface.getPackageInfo(packageName,
+ PackageManager.MATCH_STATIC_SHARED_LIBRARIES, userId);
+ if (info == null) {
+ pw.println("Failure [not installed for " + userId + "]");
+ return 1;
+ }
+ final boolean isSystem =
+ (info.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0;
+ // If we are being asked to delete a system app for just one
+ // user set flag so it disables rather than reverting to system
+ // version of the app.
+ if (isSystem) {
+ flags |= PackageManager.DELETE_SYSTEM_APP;
+ }
+ }
+
+ mInterface.getPackageInstaller().uninstall(new VersionedPackage(packageName,
+ versionCode), null /*callerPackageName*/, flags,
+ receiver.getIntentSender(), userId);
+ }
final Intent result = receiver.getResult();
final int status = result.getIntExtra(PackageInstaller.EXTRA_STATUS,