From 1efd27cde359a1ff71d88519dbbdc2f7f1f1d1a4 Mon Sep 17 00:00:00 2001 From: Adam Bookatz Date: Thu, 19 Mar 2020 18:37:36 -0700 Subject: [PATCH] UserSystemPackageInstaller only (un)installs when appropriate The UserSystemPackageInstaller (USPI) uninstalls system packages that are not needed, depending on the user type. When that determination changes (or the feature is disabled) it can also re-install those packages. This cl specifies when it is appropriate for USPI to actually perform the (un)installation. 1. Introduces uninstallReason: records the reason why a package was uninstalled from a given user. Right now, the only values are UNKNOWN and USER_TYPE. The latter indicates that the USPI system uninstalled the package. If the USPI whitelist changes (or the USPI feature is disabled), uninstalled packages will only be installed by USPI if the uninstallReason was USER_TYPE. Any further uninstalls (e.g. by ManagedProvisioning) will reset the uninstallReason to UNKNOWN, so USPI will no longer install such packages in the future. This prevents USPI from reinstalling system packages that other mechanisms (such as ManagedProvisioning) uninstalled. 2. USPI will uninstall a system package if it is blacklisted, but only if that system package is new, i.e in two circumstances: a. on first boot b. on an OTA where the package was not present prior to the OTA Bug: 143200798 Test: atest UserSystemPackageInstallerTest Test: Confirmed (un)installations during manually simulated OTAs Change-Id: Ia0714d1faa8f7c79082f2cc93a92ae36b9a4c918 Merged-In: Ia0714d1faa8f7c79082f2cc93a92ae36b9a4c918 --- .../android/content/pm/PackageManager.java | 21 ++++ .../android/content/pm/PackageUserState.java | 7 ++ core/res/res/values/config.xml | 3 +- data/etc/preinstalled-packages-platform.xml | 4 +- .../content/pm/PackageManagerInternal.java | 17 ---- .../server/pm/PackageManagerService.java | 97 +++++++++++-------- .../android/server/pm/PackageSettingBase.java | 14 ++- .../java/com/android/server/pm/Settings.java | 39 ++++++-- .../android/server/pm/UserManagerService.java | 10 +- .../server/pm/UserSystemPackageInstaller.java | 60 ++++++++++-- .../server/pm/PackageUserStateTest.java | 4 + 11 files changed, 191 insertions(+), 85 deletions(-) diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java index 370469ebe8404..f48d78ac9cc3e 100644 --- a/core/java/android/content/pm/PackageManager.java +++ b/core/java/android/content/pm/PackageManager.java @@ -1031,6 +1031,27 @@ public abstract class PackageManager { */ public static final int INSTALL_REASON_ROLLBACK = 5; + /** @hide */ + @IntDef(prefix = { "UNINSTALL_REASON_" }, value = { + UNINSTALL_REASON_UNKNOWN, + UNINSTALL_REASON_USER_TYPE, + }) + @Retention(RetentionPolicy.SOURCE) + public @interface UninstallReason {} + + /** + * Code indicating that the reason for uninstalling this package is unknown. + * @hide + */ + public static final int UNINSTALL_REASON_UNKNOWN = 0; + + /** + * Code indicating that this package was uninstalled due to the type of user. + * See UserSystemPackageInstaller + * @hide + */ + public static final int UNINSTALL_REASON_USER_TYPE = 1; + /** * @hide */ diff --git a/core/java/android/content/pm/PackageUserState.java b/core/java/android/content/pm/PackageUserState.java index 30cf4e76ba040..61b1553e28a8a 100644 --- a/core/java/android/content/pm/PackageUserState.java +++ b/core/java/android/content/pm/PackageUserState.java @@ -74,6 +74,7 @@ public class PackageUserState { public int appLinkGeneration; public int categoryHint = ApplicationInfo.CATEGORY_UNDEFINED; public int installReason; + public @PackageManager.UninstallReason int uninstallReason; public String harmfulAppWarning; public ArraySet disabledComponents; @@ -92,6 +93,7 @@ public class PackageUserState { domainVerificationStatus = PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; installReason = PackageManager.INSTALL_REASON_UNKNOWN; + uninstallReason = PackageManager.UNINSTALL_REASON_UNKNOWN; } @VisibleForTesting @@ -112,6 +114,7 @@ public class PackageUserState { appLinkGeneration = o.appLinkGeneration; categoryHint = o.categoryHint; installReason = o.installReason; + uninstallReason = o.uninstallReason; disabledComponents = ArrayUtils.cloneOrNull(o.disabledComponents); enabledComponents = ArrayUtils.cloneOrNull(o.enabledComponents); overlayPaths = @@ -353,6 +356,9 @@ public class PackageUserState { if (installReason != oldState.installReason) { return false; } + if (uninstallReason != oldState.uninstallReason) { + return false; + } if ((disabledComponents == null && oldState.disabledComponents != null) || (disabledComponents != null && oldState.disabledComponents == null)) { return false; @@ -407,6 +413,7 @@ public class PackageUserState { hashCode = 31 * hashCode + appLinkGeneration; hashCode = 31 * hashCode + categoryHint; hashCode = 31 * hashCode + installReason; + hashCode = 31 * hashCode + uninstallReason; hashCode = 31 * hashCode + Objects.hashCode(disabledComponents); hashCode = 31 * hashCode + Objects.hashCode(enabledComponents); hashCode = 31 * hashCode + Objects.hashCode(harmfulAppWarning); diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml index acda77f185fdb..4c4b7e6202f98 100644 --- a/core/res/res/values/config.xml +++ b/core/res/res/values/config.xml @@ -2406,8 +2406,7 @@ - to henceforth disable feature and try to undo its previous effects: 0 Note: This list must be kept current with PACKAGE_WHITELIST_MODE_PROP in frameworks/base/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java --> - 29 - + 13 false diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml index efab27f96efa8..17e1f2e0c229f 100644 --- a/data/etc/preinstalled-packages-platform.xml +++ b/data/etc/preinstalled-packages-platform.xml @@ -97,8 +97,8 @@ The way that a device treats system packages that do not have any entry (for any is determined by the config resource value config_userTypePackageWhitelistMode. See frameworks/base/core/res/res/values/config.xml#config_userTypePackageWhitelistMode. -Changes to the whitelist during system updates can result in installing new system packages -to pre-existing users, but cannot uninstall system packages from pre-existing users. +Changes to the whitelist during system updates can result in installing additional system packages +to pre-existing users, but cannot uninstall pre-existing system packages from pre-existing users. --> diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 7d85966d7bf65..c27ec66b5db3e 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -426,17 +426,6 @@ public abstract class PackageManagerInternal { */ public abstract String getNameForUid(int uid); - /** - * Marks a package as installed (or not installed) for a given user. - * - * @param pkg the package whose installation is to be set - * @param userId the user for whom to set it - * @param installed the new installed state - * @return true if the installed state changed as a result - */ - public abstract boolean setInstalled(AndroidPackage pkg, - @UserIdInt int userId, boolean installed); - /** * Request to perform the second phase of ephemeral resolution. * @param responseObj The response of the first phase of ephemeral resolution @@ -521,12 +510,6 @@ public abstract class PackageManagerInternal { */ public abstract boolean isPackagePersistent(String packageName); - /** - * Returns whether or not the given package represents a legacy system application released - * prior to runtime permissions. - */ - public abstract boolean isLegacySystemApp(AndroidPackage pkg); - /** * Get all overlay packages for a user. * @param userId The user for which to get the overlays. diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 34363c8df8f6c..673373d70d4e5 100644 --- a/services/core/java/com/android/server/pm/PackageManagerService.java +++ b/services/core/java/com/android/server/pm/PackageManagerService.java @@ -93,6 +93,7 @@ import static android.content.pm.PackageManager.MOVE_FAILED_SYSTEM_PACKAGE; import static android.content.pm.PackageManager.PERMISSION_DENIED; import static android.content.pm.PackageManager.PERMISSION_GRANTED; import static android.content.pm.PackageManager.RESTRICTION_NONE; +import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; import static android.content.pm.PackageParser.isApkFile; import static android.os.Trace.TRACE_TAG_PACKAGE_MANAGER; import static android.os.incremental.IncrementalManager.isIncrementalPath; @@ -737,9 +738,10 @@ public class PackageManagerService extends IPackageManager.Stub final private ArrayMap mExpectingBetter = new ArrayMap<>(); /** - * Tracks existing system packages prior to receiving an OTA. Keys are package name. + * Tracks existing packages prior to receiving an OTA. Keys are package name. + * Only non-null during an OTA, and even then it is nulled again once systemReady(). */ - final private ArraySet mExistingSystemPackages = new ArraySet<>(); + private @Nullable ArraySet mExistingPackages = null; /** * Whether or not system app permissions should be promoted from install to runtime. */ @@ -2570,9 +2572,10 @@ public class PackageManagerService extends IPackageManager.Stub private void installWhitelistedSystemPackages() { synchronized (mLock) { final boolean scheduleWrite = mUserManager.installWhitelistedSystemPackages( - isFirstBoot(), isDeviceUpgrading()); + isFirstBoot(), isDeviceUpgrading(), mExistingPackages); if (scheduleWrite) { scheduleWritePackageRestrictionsLocked(UserHandle.USER_ALL); + scheduleWriteSettingsLocked(); } } } @@ -2873,13 +2876,12 @@ public class PackageManagerService extends IPackageManager.Stub mIsPreNMR1Upgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.N_MR1; mIsPreQUpgrade = mIsUpgrade && ver.sdkVersion < Build.VERSION_CODES.Q; - // save off the names of pre-existing system packages prior to scanning; we don't - // want to automatically grant runtime permissions for new system apps - if (mPromoteSystemApps) { + // Save the names of pre-existing packages prior to scanning, so we can determine + // which system packages are completely new due to an upgrade. + if (isDeviceUpgrading()) { + mExistingPackages = new ArraySet<>(mSettings.mPackages.size()); for (PackageSetting ps : mSettings.mPackages.values()) { - if (isSystemApp(ps)) { - mExistingSystemPackages.add(ps.name); - } + mExistingPackages.add(ps.name); } } @@ -3340,7 +3342,6 @@ public class PackageManagerService extends IPackageManager.Stub } // clear only after permissions and other defaults have been updated - mExistingSystemPackages.clear(); mPromoteSystemApps = false; // All the changes are done during package scanning. @@ -12767,6 +12768,7 @@ public class PackageManagerService extends IPackageManager.Stub pkgSetting.setInstalled(true, userId); pkgSetting.setHidden(false, userId); pkgSetting.setInstallReason(installReason, userId); + pkgSetting.setUninstallReason(PackageManager.UNINSTALL_REASON_UNKNOWN, userId); mSettings.writePackageRestrictionsLPr(userId); mSettings.writeKernelMappingLPr(pkgSetting); installed = true; @@ -15599,7 +15601,8 @@ public class PackageManagerService extends IPackageManager.Stub } // It's implied that when a user requests installation, they want the app to be - // installed and enabled. + // installed and enabled. (This does not apply to USER_ALL, which here means only + // install on users for which the app is already installed). if (userId != UserHandle.USER_ALL) { ps.setInstalled(true, userId); ps.setEnabled(COMPONENT_ENABLED_STATE_DEFAULT, userId, installerPackageName); @@ -15617,7 +15620,7 @@ public class PackageManagerService extends IPackageManager.Stub mSettings.addInstallerPackageNames(installSource); // When replacing an existing package, preserve the original install reason for all - // users that had the package installed before. + // users that had the package installed before. Similarly for uninstall reasons. final Set previousUserIds = new ArraySet<>(); if (res.removedInfo != null && res.removedInfo.installReasons != null) { final int installReasonCount = res.removedInfo.installReasons.size(); @@ -15628,10 +15631,20 @@ public class PackageManagerService extends IPackageManager.Stub previousUserIds.add(previousUserId); } } + if (res.removedInfo != null && res.removedInfo.uninstallReasons != null) { + for (int i = 0; i < res.removedInfo.uninstallReasons.size(); i++) { + final int previousUserId = res.removedInfo.uninstallReasons.keyAt(i); + final int previousReason = res.removedInfo.uninstallReasons.valueAt(i); + ps.setUninstallReason(previousReason, previousUserId); + } + } // Set install reason for users that are having the package newly installed. + final int[] allUsersList = mUserManager.getUserIds(); if (userId == UserHandle.USER_ALL) { - for (int currentUserId : mUserManager.getUserIds()) { + // TODO(b/152629990): It appears that the package doesn't actually get newly + // installed in this case, so the installReason shouldn't get modified? + for (int currentUserId : allUsersList) { if (!previousUserIds.contains(currentUserId)) { ps.setInstallReason(installReason, currentUserId); } @@ -15639,6 +15652,12 @@ public class PackageManagerService extends IPackageManager.Stub } else if (!previousUserIds.contains(userId)) { ps.setInstallReason(installReason, userId); } + // Ensure that the uninstall reason is UNKNOWN for users with the package installed. + for (int currentUserId : allUsersList) { + if (ps.getInstalled(currentUserId)) { + ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, currentUserId); + } + } mSettings.writeKernelMappingLPr(ps); } res.name = pkgName; @@ -17002,6 +17021,7 @@ public class PackageManagerService extends IPackageManager.Stub final String pkgName11 = parsedPackage.getPackageName(); final int[] allUsers; final int[] installedUsers; + final int[] uninstalledUsers; synchronized (mLock) { oldPackage = mPackages.get(pkgName11); @@ -17076,6 +17096,7 @@ public class PackageManagerService extends IPackageManager.Stub // In case of rollback, remember per-user/profile install state allUsers = mUserManager.getUserIds(); installedUsers = ps.queryInstalledUsers(allUsers, true); + uninstalledUsers = ps.queryInstalledUsers(allUsers, false); // don't allow an upgrade from full to ephemeral @@ -17114,6 +17135,11 @@ public class PackageManagerService extends IPackageManager.Stub final int userId = installedUsers[i]; res.removedInfo.installReasons.put(userId, ps.getInstallReason(userId)); } + res.removedInfo.uninstallReasons = new SparseArray<>(uninstalledUsers.length); + for (int i = 0; i < uninstalledUsers.length; i++) { + final int userId = uninstalledUsers[i]; + res.removedInfo.uninstallReasons.put(userId, ps.getUninstallReason(userId)); + } sysPkg = oldPackage.isSystem(); if (sysPkg) { @@ -17946,6 +17972,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] broadcastUsers = null; int[] instantUserIds = null; SparseArray installReasons; + SparseArray uninstallReasons; boolean isRemovedPackageSystemUpdate = false; boolean isUpdate; boolean dataRemoved; @@ -18168,6 +18195,9 @@ public class PackageManagerService extends IPackageManager.Stub installedStateChanged = true; } deletedPs.setInstalled(installed, userId); + if (installed) { + deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); + } } } } @@ -18350,6 +18380,9 @@ public class PackageManagerService extends IPackageManager.Stub installedStateChanged = true; } ps.setInstalled(installed, userId); + if (installed) { + ps.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); + } mSettings.writeRuntimePermissionsForUserLPr(userId, false); } @@ -18622,7 +18655,9 @@ public class PackageManagerService extends IPackageManager.Stub null /*enabledComponents*/, null /*disabledComponents*/, ps.readUserState(nextUserId).domainVerificationStatus, - 0, PackageManager.INSTALL_REASON_UNKNOWN, + 0 /*linkGeneration*/, + PackageManager.INSTALL_REASON_UNKNOWN, + PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); } mSettings.writeKernelMappingLPr(ps); @@ -20773,6 +20808,8 @@ public class PackageManagerService extends IPackageManager.Stub // installation on reboot. Make sure this is the last component to be call since the // installation might require other components to be ready. mInstallerService.restoreAndApplyStagedSessionIfNeeded(); + + mExistingPackages = null; } public void waitForAppDataPrepared() { @@ -22725,16 +22762,17 @@ public class PackageManagerService extends IPackageManager.Stub /** * Called by UserManagerService. * - * @param installablePackages system packages that should be initially installed for this user, - * or {@code null} if all system packages should be installed + * @param userTypeInstallablePackages system packages that should be initially installed for + * this type of user, or {@code null} if all system packages + * should be installed * @param disallowedPackages packages that should not be initially installed. Takes precedence * over installablePackages. */ - void createNewUser(int userId, @Nullable Set installablePackages, + void createNewUser(int userId, @Nullable Set userTypeInstallablePackages, String[] disallowedPackages) { synchronized (mInstallLock) { mSettings.createNewUserLI(this, mInstaller, userId, - installablePackages, disallowedPackages); + userTypeInstallablePackages, disallowedPackages); } synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); @@ -23747,19 +23785,6 @@ public class PackageManagerService extends IPackageManager.Stub return PackageManagerService.this.getNameForUid(uid); } - @Override - public boolean setInstalled(AndroidPackage pkg, @UserIdInt int userId, - boolean installed) { - synchronized (mLock) { - final PackageSetting ps = mSettings.mPackages.get(pkg.getPackageName()); - if (ps.getInstalled(userId) != installed) { - ps.setInstalled(installed, userId); - return true; - } - return false; - } - } - @Override public void requestInstantAppResolutionPhaseTwo(AuxiliaryResolveInfo responseObj, Intent origIntent, String resolvedType, String callingPackage, @@ -23829,16 +23854,6 @@ public class PackageManagerService extends IPackageManager.Stub } } - @Override - public boolean isLegacySystemApp(AndroidPackage pkg) { - synchronized (mLock) { - final PackageSetting ps = getPackageSetting(pkg.getPackageName()); - return mPromoteSystemApps - && ps.isSystem() - && mExistingSystemPackages.contains(ps.name); - } - } - @Override public List getOverlayPackages(int userId) { final ArrayList overlayPackages = new ArrayList(); diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 318a233b6e148..7cb3df5a03504 100644 --- a/services/core/java/com/android/server/pm/PackageSettingBase.java +++ b/services/core/java/com/android/server/pm/PackageSettingBase.java @@ -24,6 +24,7 @@ import android.annotation.NonNull; import android.content.pm.ApplicationInfo; import android.content.pm.IntentFilterVerificationInfo; import android.content.pm.PackageManager; +import android.content.pm.PackageManager.UninstallReason; import android.content.pm.PackageParser; import android.content.pm.PackageUserState; import android.content.pm.Signature; @@ -315,6 +316,14 @@ public abstract class PackageSettingBase extends SettingBase { modifyUserState(userId).installReason = installReason; } + int getUninstallReason(int userId) { + return readUserState(userId).uninstallReason; + } + + void setUninstallReason(@UninstallReason int uninstallReason, int userId) { + modifyUserState(userId).uninstallReason = uninstallReason; + } + void setOverlayPaths(List overlayPaths, int userId) { modifyUserState(userId).setOverlayPaths(overlayPaths == null ? null : overlayPaths.toArray(new String[overlayPaths.size()])); @@ -471,7 +480,7 @@ public abstract class PackageSettingBase extends SettingBase { ArrayMap suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet enabledComponents, ArraySet disabledComponents, - int domainVerifState, int linkGeneration, int installReason, + int domainVerifState, int linkGeneration, int installReason, int uninstallReason, String harmfulAppWarning) { PackageUserState state = modifyUserState(userId); state.ceDataInode = ceDataInode; @@ -489,6 +498,7 @@ public abstract class PackageSettingBase extends SettingBase { state.domainVerificationStatus = domainVerifState; state.appLinkGeneration = linkGeneration; state.installReason = installReason; + state.uninstallReason = uninstallReason; state.instantApp = instantApp; state.virtualPreload = virtualPreload; state.harmfulAppWarning = harmfulAppWarning; @@ -502,7 +512,7 @@ public abstract class PackageSettingBase extends SettingBase { otherState.virtualPreload, otherState.lastDisableAppCaller, otherState.enabledComponents, otherState.disabledComponents, otherState.domainVerificationStatus, otherState.appLinkGeneration, - otherState.installReason, otherState.harmfulAppWarning); + otherState.installReason, otherState.uninstallReason, otherState.harmfulAppWarning); } ArraySet getEnabledComponents(int userId) { diff --git a/services/core/java/com/android/server/pm/Settings.java b/services/core/java/com/android/server/pm/Settings.java index 62541ab4951e7..f6ca87df482ff 100644 --- a/services/core/java/com/android/server/pm/Settings.java +++ b/services/core/java/com/android/server/pm/Settings.java @@ -24,6 +24,8 @@ import static android.content.pm.PackageManager.INSTALL_FAILED_SHARED_USER_INCOM import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_ALWAYS; import static android.content.pm.PackageManager.INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED; import static android.content.pm.PackageManager.MATCH_DEFAULT_ONLY; +import static android.content.pm.PackageManager.UNINSTALL_REASON_UNKNOWN; +import static android.content.pm.PackageManager.UNINSTALL_REASON_USER_TYPE; import static android.os.Process.PACKAGE_INFO_GID; import static android.os.Process.SYSTEM_UID; @@ -266,6 +268,7 @@ public final class Settings { private static final String ATTR_DOMAIN_VERIFICATON_STATE = "domainVerificationStatus"; private static final String ATTR_APP_LINK_GENERATION = "app-link-generation"; private static final String ATTR_INSTALL_REASON = "install-reason"; + private static final String ATTR_UNINSTALL_REASON = "uninstall-reason"; private static final String ATTR_INSTANT_APP = "instant-app"; private static final String ATTR_VIRTUAL_PRELOAD = "virtual-preload"; private static final String ATTR_HARMFUL_APP_WARNING = "harmful-app-warning"; @@ -684,7 +687,9 @@ public final class Settings { null /*enabledComponents*/, null /*disabledComponents*/, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, - 0, PackageManager.INSTALL_REASON_UNKNOWN, + 0 /*linkGeneration*/, + PackageManager.INSTALL_REASON_UNKNOWN, + PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); } } @@ -768,6 +773,7 @@ public final class Settings { if (allUserInfos != null) { for (UserInfo userInfo : allUserInfos) { pkgSetting.setInstalled(true, userInfo.id); + pkgSetting.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userInfo.id); } } } @@ -1580,7 +1586,9 @@ public final class Settings { null /*enabledComponents*/, null /*disabledComponents*/, INTENT_FILTER_DOMAIN_VERIFICATION_STATUS_UNDEFINED, - 0, PackageManager.INSTALL_REASON_UNKNOWN, + 0 /*linkGeneration*/, + PackageManager.INSTALL_REASON_UNKNOWN, + PackageManager.UNINSTALL_REASON_UNKNOWN, null /*harmfulAppWarning*/); } return; @@ -1678,6 +1686,8 @@ public final class Settings { } final int installReason = XmlUtils.readIntAttribute(parser, ATTR_INSTALL_REASON, PackageManager.INSTALL_REASON_UNKNOWN); + final int uninstallReason = XmlUtils.readIntAttribute(parser, + ATTR_UNINSTALL_REASON, PackageManager.UNINSTALL_REASON_UNKNOWN); ArraySet enabledComponents = null; ArraySet disabledComponents = null; @@ -1751,7 +1761,7 @@ public final class Settings { hidden, distractionFlags, suspended, suspendParamsMap, instantApp, virtualPreload, enabledCaller, enabledComponents, disabledComponents, verifState, - linkGeneration, installReason, harmfulAppWarning); + linkGeneration, installReason, uninstallReason, harmfulAppWarning); } else if (tagName.equals("preferred-activities")) { readPreferredActivitiesLPw(parser, userId); } else if (tagName.equals(TAG_PERSISTENT_PREFERRED_ACTIVITIES)) { @@ -2079,6 +2089,10 @@ public final class Settings { serializer.attribute(null, ATTR_INSTALL_REASON, Integer.toString(ustate.installReason)); } + if (ustate.uninstallReason != PackageManager.UNINSTALL_REASON_UNKNOWN) { + serializer.attribute(null, ATTR_UNINSTALL_REASON, + Integer.toString(ustate.uninstallReason)); + } if (ustate.harmfulAppWarning != null) { serializer.attribute(null, ATTR_HARMFUL_APP_WARNING, ustate.harmfulAppWarning); @@ -4126,7 +4140,7 @@ public final class Settings { } void createNewUserLI(@NonNull PackageManagerService service, @NonNull Installer installer, - @UserIdInt int userHandle, @Nullable Set installablePackages, + @UserIdInt int userHandle, @Nullable Set userTypeInstallablePackages, String[] disallowedPackages) { final TimingsTraceAndSlog t = new TimingsTraceAndSlog(TAG + "Timing", Trace.TRACE_TAG_PACKAGE_MANAGER); @@ -4137,7 +4151,7 @@ public final class Settings { String[] seinfos; int[] targetSdkVersions; int packagesCount; - final boolean skipPackageWhitelist = installablePackages == null; + final boolean skipPackageWhitelist = userTypeInstallablePackages == null; synchronized (mLock) { Collection packages = mPackages.values(); packagesCount = packages.size(); @@ -4152,13 +4166,20 @@ public final class Settings { if (ps.pkg == null) { continue; } - final boolean shouldInstall = ps.isSystem() && - (skipPackageWhitelist || installablePackages.contains(ps.name)) && + final boolean shouldMaybeInstall = ps.isSystem() && !ArrayUtils.contains(disallowedPackages, ps.name) && !ps.getPkgState().isHiddenUntilInstalled(); + final boolean shouldReallyInstall = shouldMaybeInstall && + (skipPackageWhitelist || userTypeInstallablePackages.contains(ps.name)); // Only system apps are initially installed. - ps.setInstalled(shouldInstall, userHandle); - if (!shouldInstall) { + ps.setInstalled(shouldReallyInstall, userHandle); + // If userTypeInstallablePackages is the *only* reason why we're not installing, + // then uninstallReason is USER_TYPE. If there's a different reason, or if we + // actually are installing, put UNKNOWN. + final int uninstallReason = (shouldMaybeInstall && !shouldReallyInstall) ? + UNINSTALL_REASON_USER_TYPE : UNINSTALL_REASON_UNKNOWN; + ps.setUninstallReason(uninstallReason, userHandle); + if (!shouldReallyInstall) { writeKernelMappingLPr(ps); } // Need to create a data directory for all apps under this user. Accumulate all diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index ef8cad15290c7..d2443fa735e9d 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -3438,10 +3438,10 @@ public class UserManagerService extends IUserManager.Stub { StorageManager.FLAG_STORAGE_DE | StorageManager.FLAG_STORAGE_CE); t.traceEnd(); - final Set installablePackages = + final Set userTypeInstallablePackages = mSystemPackageInstaller.getInstallablePackagesForUserType(userType); t.traceBegin("PM.createNewUser"); - mPm.createNewUser(userId, installablePackages, disallowedPackages); + mPm.createNewUser(userId, userTypeInstallablePackages, disallowedPackages); t.traceEnd(); userInfo.partial = false; @@ -3562,8 +3562,10 @@ public class UserManagerService extends IUserManager.Stub { } /** Install/uninstall system packages for all users based on their user-type, as applicable. */ - boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) { - return mSystemPackageInstaller.installWhitelistedSystemPackages(isFirstBoot, isUpgrade); + boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade, + @Nullable ArraySet existingPackages) { + return mSystemPackageInstaller.installWhitelistedSystemPackages( + isFirstBoot, isUpgrade, existingPackages); } private long getCreationTime() { diff --git a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java index d6480d35110dd..85c2306f6d899 100644 --- a/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java +++ b/services/core/java/com/android/server/pm/UserSystemPackageInstaller.java @@ -20,6 +20,7 @@ import android.annotation.IntDef; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.UserIdInt; +import android.content.pm.PackageManager; import android.content.pm.PackageManagerInternal; import android.content.pm.PackageParser; import android.content.res.Resources; @@ -74,6 +75,14 @@ import java.util.Set; *
  • Otherwise, the package is implicitly treated as blacklisted for all users
  • * * + *

    Packages are only installed/uninstalled by this mechanism when a new user is created or during + * an update. In the case of updates:

      + *
    • new packages are (un)installed per the whitelist/blacklist
    • + *
    • pre-existing installed blacklisted packages are never uninstalled
    • + *
    • pre-existing not-installed whitelisted packages are only installed if the reason why they + * had been previously uninstalled was due to UserSystemPackageInstaller
    • + *
    + * *

    NOTE: the {@code SystemConfig} state is only updated on first boot or after a system * update. So, to verify changes during development, you can emulate the latter by calling: *

    
    @@ -171,8 +180,12 @@ class UserSystemPackageInstaller {
          *
          * This is responsible for enforcing the whitelist for pre-existing users (i.e. USER_SYSTEM);
          * enforcement for new users is done when they are created in UserManagerService.createUser().
    +     *
    +     * @param preExistingPackages list of packages on the device prior to the upgrade. Cannot be
    +     *                            null if isUpgrade is true.
          */
    -    boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade) {
    +    boolean installWhitelistedSystemPackages(boolean isFirstBoot, boolean isUpgrade,
    +            @Nullable ArraySet preExistingPackages) {
             final int mode = getWhitelistMode();
             checkWhitelistedSystemPackages(mode);
             final boolean isConsideredUpgrade = isUpgrade && !isIgnoreOtaMode(mode);
    @@ -198,19 +211,50 @@ class UserSystemPackageInstaller {
                     final boolean install =
                             (userWhitelist == null || userWhitelist.contains(pkg.getPackageName()))
                                     && !pkgSetting.getPkgState().isHiddenUntilInstalled();
    -                if (isConsideredUpgrade && !isFirstBoot && !install) {
    -                    return; // To be careful, we don’t uninstall apps during OTAs
    -                }
    -                final boolean changed = pmInt.setInstalled(pkg, userId, install);
    -                if (changed) {
    -                    Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
    -                            + pkg.getPackageName() + " for user " + userId);
    +                if (pkgSetting.getInstalled(userId) == install
    +                        || !shouldChangeInstallationState(pkgSetting, install, userId, isFirstBoot,
    +                                isConsideredUpgrade, preExistingPackages)) {
    +                    return;
                     }
    +                pkgSetting.setInstalled(install, userId);
    +                pkgSetting.setUninstallReason(
    +                        install ? PackageManager.UNINSTALL_REASON_UNKNOWN :
    +                                PackageManager.UNINSTALL_REASON_USER_TYPE,
    +                        userId);
    +                Slog.i(TAG, (install ? "Installed " : "Uninstalled ")
    +                        + pkg.getPackageName() + " for user " + userId);
                 });
             }
             return true;
         }
     
    +    /**
    +     * Returns whether to proceed with install/uninstall for the given package.
    +     * In particular, do not install a package unless it was only uninstalled due to the user type;
    +     * and do not uninstall a package if it previously was installed (prior to the OTA).
    +     *
    +     * Should be called only within PackageManagerInternal.forEachPackageSetting() since it
    +     * requires the LP lock.
    +     *
    +     * @param preOtaPkgs list of packages on the device prior to the upgrade.
    +     *                   Cannot be null if isUpgrade is true.
    +     */
    +    private static boolean shouldChangeInstallationState(PackageSetting pkgSetting,
    +                                                         boolean install,
    +                                                         @UserIdInt int userId,
    +                                                         boolean isFirstBoot,
    +                                                         boolean isUpgrade,
    +                                                         @Nullable ArraySet preOtaPkgs) {
    +        if (install) {
    +            // Only proceed with install if we are the only reason why it had been uninstalled.
    +            return pkgSetting.getUninstallReason(userId)
    +                    == PackageManager.UNINSTALL_REASON_USER_TYPE;
    +        } else {
    +            // Only proceed with uninstall if the package is new to the device.
    +            return isFirstBoot || (isUpgrade && !preOtaPkgs.contains(pkgSetting.name));
    +        }
    +    }
    +
         /**
          * Checks whether the system packages and the mWhitelistedPackagesForUserTypes whitelist are
          * in 1-to-1 correspondence.
    diff --git a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
    index fc5a0ba1af7b6..7c3efeb01f48a 100644
    --- a/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
    +++ b/services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java
    @@ -84,6 +84,10 @@ public class PackageUserStateTest {
             oldUserState = new PackageUserState();
             oldUserState.suspended = true;
             assertThat(testUserState.equals(oldUserState), is(false));
    +
    +        oldUserState = new PackageUserState();
    +        oldUserState.uninstallReason = PackageManager.UNINSTALL_REASON_USER_TYPE;
    +        assertThat(testUserState.equals(oldUserState), is(false));
         }
     
         @Test