diff options
| author | 2020-04-08 17:05:05 +0200 | |
|---|---|---|
| committer | 2020-05-12 09:18:56 +0000 | |
| commit | 77880fa8607856571711cf3330a3bee77d91d106 (patch) | |
| tree | ea3117516faf9a4c1e4b479bbc5960ed8675574a | |
| parent | c8f22beefece1cafccd46683a8ec4ecf543512a0 (diff) | |
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
5 files changed, 88 insertions, 0 deletions
diff --git a/core/java/android/content/pm/IPackageInstaller.aidl b/core/java/android/content/pm/IPackageInstaller.aidl index 37baae35734b..010589617e09 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<String> whiteListedPermissions); diff --git a/core/java/android/content/pm/IPackageManager.aidl b/core/java/android/content/pm/IPackageManager.aidl index 8bebafff37f0..f257326904fd 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 85a3986a65f9..ed75504529b9 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 3367cd556b2b..0462a1990df4 100644 --- a/services/core/java/com/android/server/pm/PackageInstallerService.java +++ b/services/core/java/com/android/server/pm/PackageInstallerService.java @@ -933,6 +933,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<String> whiteListedPermissions) { mPm.installExistingPackageAsUser(packageName, userId, installFlags, installReason, diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 8b191dda5590..a9842ee37739 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -17811,8 +17811,46 @@ public class PackageManagerService extends IPackageManager.Stub } @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 { |