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
Change-Id: I69fe4d1dd4e9da83574b431257f7be6d1ac8b2bb
This commit is contained in:
Robert Horvath
2020-04-08 17:05:05 +02:00
parent 61bea7ba19
commit d336f843bd
6 changed files with 87 additions and 0 deletions

View File

@@ -11801,6 +11801,7 @@ package android.content.pm {
method public void registerSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback, @NonNull android.os.Handler);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull String, @NonNull android.content.IntentSender);
method @RequiresPermission(anyOf={android.Manifest.permission.DELETE_PACKAGES, android.Manifest.permission.REQUEST_DELETE_PACKAGES}) public void uninstall(@NonNull android.content.pm.VersionedPackage, @NonNull android.content.IntentSender);
method @RequiresPermission(android.Manifest.permission.DELETE_PACKAGES) public void uninstallExistingPackage(@NonNull String, @Nullable android.content.IntentSender);
method public void unregisterSessionCallback(@NonNull android.content.pm.PackageInstaller.SessionCallback);
method public void updateSessionAppIcon(int, @Nullable android.graphics.Bitmap);
method public void updateSessionAppLabel(int, @Nullable CharSequence);

View File

@@ -52,6 +52,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);

View File

@@ -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);

View File

@@ -723,6 +723,25 @@ 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.
*/
@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

View File

@@ -936,6 +936,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) {

View File

@@ -17698,9 +17698,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);
@@ -17721,6 +17759,7 @@ public class PackageManagerService extends IPackageManager.Stub
final int uid = Binder.getCallingUid();
if (!isOrphaned(internalPackageName)
&& !allowSilentUninstall
&& !isCallerAllowedToSilentlyUninstall(uid, internalPackageName)) {
mHandler.post(() -> {
try {