Clean up apks installed for a removed user
When a user is removed, enumerate through all installed packages
to see if any of them are not installed for any user. Delete the
package if no user has it "installed".
Added a pm option to install an apk for a specific user.
Fixed a crash in UserManagerService when executing the above
cleanup - dying users generate a null UserInfo.
Bug: 15426024
Change-Id: I571decde1ae1c257d0da6db153b896aad6d6bcb4
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 2cb9077..fbaeb37 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7698,8 +7698,21 @@
public void installPackage(String originPath, IPackageInstallObserver2 observer,
int installFlags, String installerPackageName, VerificationParams verificationParams,
String packageAbiOverride) {
+ installPackageAsUser(originPath, observer, installFlags, installerPackageName, verificationParams,
+ packageAbiOverride, UserHandle.getCallingUserId());
+ }
+
+ @Override
+ public void installPackageAsUser(String originPath, IPackageInstallObserver2 observer,
+ int installFlags, String installerPackageName, VerificationParams verificationParams,
+ String packageAbiOverride, int userId) {
mContext.enforceCallingOrSelfPermission(android.Manifest.permission.INSTALL_PACKAGES,
null);
+ if (UserHandle.getCallingUserId() != userId) {
+ mContext.enforceCallingOrSelfPermission(
+ android.Manifest.permission.INTERACT_ACROSS_USERS_FULL,
+ "installPackage " + userId);
+ }
final File originFile = new File(originPath);
final int uid = Binder.getCallingUid();
@@ -7717,7 +7730,7 @@
if ((installFlags & PackageManager.INSTALL_ALL_USERS) != 0) {
user = UserHandle.ALL;
} else {
- user = new UserHandle(UserHandle.getUserId(uid));
+ user = new UserHandle(userId);
}
final int filteredInstallFlags;
@@ -12965,7 +12978,7 @@
}
/** Called by UserManagerService */
- void cleanUpUserLILPw(int userHandle) {
+ void cleanUpUserLILPw(UserManagerService userManager, int userHandle) {
mDirtyUsers.remove(userHandle);
mSettings.removeUserLPw(userHandle);
mPendingBroadcasts.remove(userHandle);
@@ -12976,6 +12989,50 @@
mInstaller.removeUserDataDirs(userHandle);
}
mUserNeedsBadging.delete(userHandle);
+ removeUnusedPackagesLILPw(userManager, userHandle);
+ }
+
+ /**
+ * We're removing userHandle and would like to remove any downloaded packages
+ * that are no longer in use by any other user.
+ * @param userHandle the user being removed
+ */
+ private void removeUnusedPackagesLILPw(UserManagerService userManager, final int userHandle) {
+ final boolean DEBUG_CLEAN_APKS = false;
+ int [] users = userManager.getUserIdsLPr();
+ Iterator<PackageSetting> psit = mSettings.mPackages.values().iterator();
+ while (psit.hasNext()) {
+ PackageSetting ps = psit.next();
+ final String packageName = ps.pkg.packageName;
+ // Skip over if system app
+ if ((ps.pkgFlags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ continue;
+ }
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, "Checking package " + packageName);
+ }
+ boolean keep = false;
+ for (int i = 0; i < users.length; i++) {
+ if (users[i] != userHandle && ps.getInstalled(users[i])) {
+ keep = true;
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Keeping package " + packageName + " for user "
+ + users[i]);
+ }
+ break;
+ }
+ }
+ if (!keep) {
+ if (DEBUG_CLEAN_APKS) {
+ Slog.i(TAG, " Removing package " + packageName);
+ }
+ mHandler.post(new Runnable() {
+ public void run() {
+ deletePackageX(packageName, userHandle, 0);
+ } //end run
+ });
+ }
+ }
}
/** Called by UserManagerService */
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 8ded7ca..2929939 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -293,6 +293,10 @@
private List<UserInfo> getProfilesLocked(int userId, boolean enabledOnly) {
UserInfo user = getUserInfoLocked(userId);
ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
+ if (user == null) {
+ // Probably a dying user
+ return users;
+ }
for (int i = 0; i < mUsers.size(); i++) {
UserInfo profile = mUsers.valueAt(i);
if (!isProfileOf(user, profile)) {
@@ -1280,7 +1284,7 @@
private void removeUserStateLocked(final int userHandle) {
// Cleanup package manager settings
- mPm.cleanUpUserLILPw(userHandle);
+ mPm.cleanUpUserLILPw(this, userHandle);
// Remove this user from the list
mUsers.remove(userHandle);