summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/content/pm/PackageManager.java21
-rw-r--r--core/java/android/content/pm/PackageUserState.java7
-rw-r--r--core/res/res/values/config.xml3
-rw-r--r--data/etc/preinstalled-packages-platform.xml4
-rw-r--r--services/core/java/android/content/pm/PackageManagerInternal.java17
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java97
-rw-r--r--services/core/java/com/android/server/pm/PackageSettingBase.java14
-rw-r--r--services/core/java/com/android/server/pm/Settings.java39
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java10
-rw-r--r--services/core/java/com/android/server/pm/UserSystemPackageInstaller.java60
-rw-r--r--services/tests/servicestests/src/com/android/server/pm/PackageUserStateTest.java4
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