diff options
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 370469ebe840..f48d78ac9cc3 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 30cf4e76ba04..61b1553e28a8 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<String> 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 acda77f185fd..4c4b7e6202f9 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 --> - <integer name="config_userTypePackageWhitelistMode">29</integer> <!-- 1+4+8+16 --> - <!-- TODO(b/143200798): Change to value 13, i.e. 1+4+8, when b/143200798 is resolved. --> + <integer name="config_userTypePackageWhitelistMode">13</integer> <!-- 1+4+8 --> <!-- Whether UI for multi user should be shown --> <bool name="config_enableMultiUserUI">false</bool> diff --git a/data/etc/preinstalled-packages-platform.xml b/data/etc/preinstalled-packages-platform.xml index efab27f96efa..17e1f2e0c229 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. --> <config> <install-in-user-type package="com.android.providers.settings"> diff --git a/services/core/java/android/content/pm/PackageManagerInternal.java b/services/core/java/android/content/pm/PackageManagerInternal.java index 7d85966d7bf6..c27ec66b5db3 100644 --- a/services/core/java/android/content/pm/PackageManagerInternal.java +++ b/services/core/java/android/content/pm/PackageManagerInternal.java @@ -427,17 +427,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 * @param origIntent The original intent that triggered ephemeral resolution @@ -522,12 +511,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. * @return A list of overlay packages. An empty list is returned if the diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java index 77a60242a6a6..8ef1182c0b3b 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; @@ -736,9 +737,10 @@ public class PackageManagerService extends IPackageManager.Stub final private ArrayMap<String, File> 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<String> mExistingSystemPackages = new ArraySet<>(); + private @Nullable ArraySet<String> mExistingPackages = null; /** * Whether or not system app permissions should be promoted from install to runtime. */ @@ -2569,9 +2571,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(); } } } @@ -2881,13 +2884,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); } } @@ -3348,7 +3350,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. @@ -12775,6 +12776,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; @@ -15607,7 +15609,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); @@ -15625,7 +15628,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<Integer> previousUserIds = new ArraySet<>(); if (res.removedInfo != null && res.removedInfo.installReasons != null) { final int installReasonCount = res.removedInfo.installReasons.size(); @@ -15636,10 +15639,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); } @@ -15647,6 +15660,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; @@ -17010,6 +17029,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); @@ -17084,6 +17104,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 @@ -17122,6 +17143,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) { @@ -17954,6 +17980,7 @@ public class PackageManagerService extends IPackageManager.Stub int[] broadcastUsers = null; int[] instantUserIds = null; SparseArray<Integer> installReasons; + SparseArray<Integer> uninstallReasons; boolean isRemovedPackageSystemUpdate = false; boolean isUpdate; boolean dataRemoved; @@ -18176,6 +18203,9 @@ public class PackageManagerService extends IPackageManager.Stub installedStateChanged = true; } deletedPs.setInstalled(installed, userId); + if (installed) { + deletedPs.setUninstallReason(UNINSTALL_REASON_UNKNOWN, userId); + } } } } @@ -18358,6 +18388,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); } @@ -18630,7 +18663,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); @@ -20781,6 +20816,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() { @@ -22733,16 +22770,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<String> installablePackages, + void createNewUser(int userId, @Nullable Set<String> userTypeInstallablePackages, String[] disallowedPackages) { synchronized (mInstallLock) { mSettings.createNewUserLI(this, mInstaller, userId, - installablePackages, disallowedPackages); + userTypeInstallablePackages, disallowedPackages); } synchronized (mLock) { scheduleWritePackageRestrictionsLocked(userId); @@ -23756,19 +23794,6 @@ public class PackageManagerService extends IPackageManager.Stub } @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, @Nullable String callingFeatureId, boolean isRequesterInstantApp, @@ -23838,16 +23863,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<PackageInfo> getOverlayPackages(int userId) { final ArrayList<PackageInfo> overlayPackages = new ArrayList<PackageInfo>(); synchronized (mLock) { diff --git a/services/core/java/com/android/server/pm/PackageSettingBase.java b/services/core/java/com/android/server/pm/PackageSettingBase.java index 318a233b6e14..7cb3df5a0350 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<String> 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<String, PackageUserState.SuspendParams> suspendParams, boolean instantApp, boolean virtualPreload, String lastDisableAppCaller, ArraySet<String> enabledComponents, ArraySet<String> 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<String> 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 62541ab4951e..f6ca87df482f 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<String> enabledComponents = null; ArraySet<String> 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<String> installablePackages, + @UserIdInt int userHandle, @Nullable Set<String> 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<PackageSetting> 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 ef8cad15290c..d2443fa735e9 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<String> installablePackages = + final Set<String> 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<String> 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 d6480d35110d..85c2306f6d89 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; * <li>Otherwise, the package is implicitly treated as blacklisted for all users</li> * </ul> * + * <p>Packages are only installed/uninstalled by this mechanism when a new user is created or during + * an update. In the case of updates:<ul> + * <li>new packages are (un)installed per the whitelist/blacklist</li> + * <li>pre-existing installed blacklisted packages are never uninstalled</li> + * <li>pre-existing not-installed whitelisted packages are only installed if the reason why they + * had been previously uninstalled was due to UserSystemPackageInstaller</li> + * </ul> + * * <p><b>NOTE:</b> 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: * <pre><code> @@ -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<String> preExistingPackages) { final int mode = getWhitelistMode(); checkWhitelistedSystemPackages(mode); final boolean isConsideredUpgrade = isUpgrade && !isIgnoreOtaMode(mode); @@ -198,20 +211,51 @@ 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<String> 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 fc5a0ba1af7b..7c3efeb01f48 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 |