From 77880fa8607856571711cf3330a3bee77d91d106 Mon Sep 17 00:00:00 2001 From: Robert Horvath Date: Wed, 8 Apr 2020 17:05:05 +0200 Subject: [PATCH] Add PackageInstaller#uninstallExistingPackage This new API allows an app to be uninstalled silently by any app holding the DELETE_PACKAGES permission, as long as the app is installed in another user so won't be fully removed from the device. Bug: 149601842 Test: atest UninstallExistingPackageTest Merged-In: I69fe4d1dd4e9da83574b431257f7be6d1ac8b2bb Change-Id: I69fe4d1dd4e9da83574b431257f7be6d1ac8b2bb --- .../android/content/pm/IPackageInstaller.aidl | 3 ++ .../android/content/pm/IPackageManager.aidl | 10 +++++ .../android/content/pm/PackageInstaller.java | 21 ++++++++++ .../server/pm/PackageInstallerService.java | 15 +++++++ .../server/pm/PackageManagerService.java | 39 +++++++++++++++++++ 5 files changed, 88 insertions(+) diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 37baae35734b6..010589617e09c 100644 --- a/core/java/android/content/pm/IPackageInstaller.aidl +++ b/core/java/android/content/pm/IPackageInstaller.aidl @@ -51,6 +51,9 @@ interface IPackageInstaller { void uninstall(in VersionedPackage versionedPackage, String callerPackageName, int flags, in IntentSender statusReceiver, int userId); + void uninstallExistingPackage(in VersionedPackage versionedPackage, String callerPackageName, + in IntentSender statusReceiver, int userId); + void installExistingPackage(String packageName, int installFlags, int installReason, in IntentSender statusReceiver, int userId, in List whiteListedPermissions); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 8bebafff37f02..f257326904fdd 100644 --- a/core/java/android/content/pm/IPackageManager.aidl +++ b/core/java/android/content/pm/IPackageManager.aidl @@ -235,6 +235,16 @@ interface IPackageManager { void deletePackageVersioned(in VersionedPackage versionedPackage, IPackageDeleteObserver2 observer, int userId, int flags); + /** + * Delete a package for a specific user. + * + * @param versionedPackage The package to delete. + * @param observer a callback to use to notify when the package deletion in finished. + * @param userId the id of the user for whom to delete the package + */ + void deleteExistingPackageAsUser(in VersionedPackage versionedPackage, + IPackageDeleteObserver2 observer, int userId); + @UnsupportedAppUsage String getInstallerPackageName(in String packageName); diff --git a/core/java/android/content/pm/PackageInstaller.java b/core/java/android/content/pm/PackageInstaller.java index 85a3986a65f97..ed75504529b92 100644 --- a/core/java/android/content/pm/PackageInstaller.java +++ b/core/java/android/content/pm/PackageInstaller.java @@ -720,6 +720,27 @@ public class PackageInstaller { } } + /** + * Uninstall the given package for the user for which this installer was created if the package + * will still exist for other users on the device. + * + * @param packageName The package to install. + * @param statusReceiver Where to deliver the result. + * + * {@hide} + */ + @RequiresPermission(Manifest.permission.DELETE_PACKAGES) + public void uninstallExistingPackage(@NonNull String packageName, + @Nullable IntentSender statusReceiver) { + Objects.requireNonNull(packageName, "packageName cannot be null"); + try { + mInstaller.uninstallExistingPackage( + new VersionedPackage(packageName, PackageManager.VERSION_CODE_HIGHEST), + mInstallerPackageName, statusReceiver, mUserId); + } catch (RemoteException e) { + throw e.rethrowFromSystemServer(); + } + } /** {@hide} */ @SystemApi diff --git a/services/core/java/com/android/server/pm/PackageInstallerService.java b/services/core/java/com/android/server/pm/PackageInstallerService.java index 3367cd556b2b8..0462a1990df4f 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -932,6 +932,21 @@ public class PackageInstallerService extends IPackageInstaller.Stub implements } } + @Override + public void uninstallExistingPackage(VersionedPackage versionedPackage, + String callerPackageName, IntentSender statusReceiver, int userId) { + final int callingUid = Binder.getCallingUid(); + mContext.enforceCallingOrSelfPermission(Manifest.permission.DELETE_PACKAGES, null); + mPermissionManager.enforceCrossUserPermission(callingUid, userId, true, true, "uninstall"); + if ((callingUid != Process.SHELL_UID) && (callingUid != Process.ROOT_UID)) { + mAppOps.checkPackage(callingUid, callerPackageName); + } + + final PackageDeleteObserverAdapter adapter = new PackageDeleteObserverAdapter(mContext, + statusReceiver, versionedPackage.getPackageName(), false, userId); + mPm.deleteExistingPackageAsUser(versionedPackage, adapter.getBinder(), userId); + } + @Override public void installExistingPackage(String packageName, int installFlags, int installReason, IntentSender statusReceiver, int userId, List whiteListedPermissions) { diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8b191dda5590e..a9842ee37739d 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17810,9 +17810,47 @@ public class PackageManagerService extends IPackageManager.Stub new LegacyPackageDeleteObserver(observer).getBinder(), userId, flags); } + @Override + public void deleteExistingPackageAsUser(VersionedPackage versionedPackage, + final IPackageDeleteObserver2 observer, final int userId) { + mContext.enforceCallingOrSelfPermission( + android.Manifest.permission.DELETE_PACKAGES, null); + Preconditions.checkNotNull(versionedPackage); + Preconditions.checkNotNull(observer); + final String packageName = versionedPackage.getPackageName(); + final long versionCode = versionedPackage.getLongVersionCode(); + + int installedForUsersCount = 0; + synchronized (mLock) { + // Normalize package name to handle renamed packages and static libs + final String internalPkgName = resolveInternalPackageNameLPr(packageName, versionCode); + final PackageSetting ps = mSettings.getPackageLPr(internalPkgName); + if (ps != null) { + int[] installedUsers = ps.queryInstalledUsers(mUserManager.getUserIds(), true); + installedForUsersCount = installedUsers.length; + } + } + + if (installedForUsersCount > 1) { + deletePackageVersionedInternal(versionedPackage, observer, userId, 0, true); + } else { + try { + observer.onPackageDeleted(packageName, PackageManager.DELETE_FAILED_INTERNAL_ERROR, + null); + } catch (RemoteException re) { + } + } + } + @Override public void deletePackageVersioned(VersionedPackage versionedPackage, final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags) { + deletePackageVersionedInternal(versionedPackage, observer, userId, deleteFlags, false); + } + + private void deletePackageVersionedInternal(VersionedPackage versionedPackage, + final IPackageDeleteObserver2 observer, final int userId, final int deleteFlags, + final boolean allowSilentUninstall) { final int callingUid = Binder.getCallingUid(); mContext.enforceCallingOrSelfPermission( android.Manifest.permission.DELETE_PACKAGES, null); @@ -17833,6 +17871,7 @@ public class PackageManagerService extends IPackageManager.Stub final int uid = Binder.getCallingUid(); if (!isOrphaned(internalPackageName) + && !allowSilentUninstall && !isCallerAllowedToSilentlyUninstall(uid, internalPackageName)) { mHandler.post(() -> { try {