Reconcile app data twice to pick up packages installed during unlock
Tries to reconcile app data inside UserManagerService twice in case
something like a carrier app was enabled during the user unlock phase.
This tries to fix a race condition between the user unlock state being
set and PackageManagerService attempting to read the state to determine
whether user encrypted credential storage should be created.
This is a re-attempt of this fix from the reverted
If086f5126d508739d1079662776f4951ea339f43.
Bug: 187103629
Test: manual, install app during reconcilation
Change-Id: I7cc18c11eccfdc6eea4d50e1880e9c675c59db28
diff --git a/services/core/java/com/android/server/am/UserController.java b/services/core/java/com/android/server/am/UserController.java
index fa7eae3..256c472 100644
--- a/services/core/java/com/android/server/am/UserController.java
+++ b/services/core/java/com/android/server/am/UserController.java
@@ -594,7 +594,8 @@
final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
t.traceBegin("UM.onBeforeUnlockUser-" + userId);
- mInjector.getUserManager().onBeforeUnlockUser(userId);
+ final ArraySet<String> reconciledPackages =
+ mInjector.getUserManager().onBeforeUnlockUser(userId);
t.traceEnd();
synchronized (mLock) {
// Do not proceed if unexpected state
@@ -603,6 +604,9 @@
}
}
mInjector.getUserManagerInternal().setUserState(userId, uss.state);
+ t.traceBegin("UM.onUserStateRunningUnlocking-" + userId);
+ mInjector.getUserManager().onUserStateRunningUnlocking(userId, reconciledPackages);
+ t.traceEnd();
uss.mUnlockProgress.setProgress(20);
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index b44e2ed..bf3bbbb 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -7593,7 +7593,7 @@
}
List<String> deferPackages = reconcileAppsDataLI(StorageManager.UUID_PRIVATE_INTERNAL,
UserHandle.USER_SYSTEM, storageFlags, true /* migrateAppData */,
- true /* onlyCoreApps */);
+ true /* onlyCoreApps */, null);
mPrepareAppDataFuture = SystemServerInitThreadPool.submit(() -> {
TimingsTraceLog traceLog = new TimingsTraceLog("SystemServerTimingAsync",
Trace.TRACE_TAG_PACKAGE_MANAGER);
@@ -21438,7 +21438,8 @@
try {
sm.prepareUserStorage(volumeUuid, user.id, user.serialNumber, flags);
synchronized (mInstallLock) {
- reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */);
+ reconcileAppsDataLI(volumeUuid, user.id, flags, true /* migrateAppData */,
+ null);
}
} catch (IllegalStateException e) {
// Device was probably ejected, and we'll process that event momentarily
@@ -21627,21 +21628,32 @@
* <p>
* Verifies that directories exist and that ownership and labeling is
* correct for all installed apps on all mounted volumes.
+ *
+ * @param reconciledPackages A set that will be populated with package names that have
+ * successfully had their data reconciled. Any package names already
+ * contained will be skipped. Because this must be mutable when
+ * non-null, it is typed {@link ArraySet} to prevent accidental
+ * usage of {@link Collections#emptySet()}. Null can be passed if the
+ * caller doesn't need this functionality.
*/
- void reconcileAppsData(int userId, int flags, boolean migrateAppsData) {
+ @NonNull
+ void reconcileAppsData(int userId, int flags, boolean migrateAppsData,
+ @Nullable ArraySet<String> reconciledPackages) {
final StorageManager storage = mInjector.getSystemService(StorageManager.class);
for (VolumeInfo vol : storage.getWritablePrivateVolumes()) {
final String volumeUuid = vol.getFsUuid();
synchronized (mInstallLock) {
- reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData);
+ reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppsData,
+ reconciledPackages);
}
}
}
@GuardedBy("mInstallLock")
private void reconcileAppsDataLI(String volumeUuid, int userId, int flags,
- boolean migrateAppData) {
- reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */);
+ boolean migrateAppData, @Nullable ArraySet<String> reconciledPackages) {
+ reconcileAppsDataLI(volumeUuid, userId, flags, migrateAppData, false /* onlyCoreApps */,
+ reconciledPackages);
}
/**
@@ -21656,7 +21668,8 @@
*/
@GuardedBy("mInstallLock")
private List<String> reconcileAppsDataLI(String volumeUuid, int userId, int flags,
- boolean migrateAppData, boolean onlyCoreApps) {
+ boolean migrateAppData, boolean onlyCoreApps,
+ @Nullable ArraySet<String> reconciledPackages) {
Slog.v(TAG, "reconcileAppsData for " + volumeUuid + " u" + userId + " 0x"
+ Integer.toHexString(flags) + " migrateAppData=" + migrateAppData);
List<String> result = onlyCoreApps ? new ArrayList<>() : null;
@@ -21719,6 +21732,9 @@
int preparedCount = 0;
for (PackageSetting ps : packages) {
final String packageName = ps.name;
+ if (reconciledPackages != null && reconciledPackages.contains(packageName)) {
+ continue;
+ }
if (ps.pkg == null) {
Slog.w(TAG, "Odd, missing scanned package " + packageName);
// TODO: might be due to legacy ASEC apps; we should circle back
@@ -21734,6 +21750,10 @@
if (ps.getInstalled(userId)) {
prepareAppDataAndMigrate(batch, ps.pkg, userId, flags, migrateAppData);
preparedCount++;
+
+ if (reconciledPackages != null) {
+ reconciledPackages.add(packageName);
+ }
}
}
executeBatchLI(batch);
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index 0d909a9..7f91a81 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -4805,7 +4805,7 @@
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_DE);
t.traceEnd();
t.traceBegin("reconcileAppsData");
- mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData);
+ mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_DE, migrateAppsData, null);
t.traceEnd();
if (userId != UserHandle.USER_SYSTEM) {
@@ -4821,11 +4821,14 @@
/**
* Called right before a user is unlocked. This gives us a chance to prepare
* app storage.
+ *
+ * @return set of packages that reconciled app data
*/
- public void onBeforeUnlockUser(@UserIdInt int userId) {
+ @NonNull public ArraySet<String> onBeforeUnlockUser(@UserIdInt int userId) {
UserInfo userInfo = getUserInfo(userId);
if (userInfo == null) {
- return;
+ // PMS requires mutable set, so the API uses ArraySet to prevent Collections.emptySet()
+ return new ArraySet<>();
}
final int userSerial = userInfo.serialNumber;
// Migrate only if build fingerprints mismatch
@@ -4836,8 +4839,33 @@
mUserDataPreparer.prepareUserData(userId, userSerial, StorageManager.FLAG_STORAGE_CE);
t.traceEnd();
- t.traceBegin("reconcileAppsData-" + userId);
- mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData);
+ final ArraySet<String> reconciledPackages = new ArraySet<>();
+ t.traceBegin("reconcileAppsDataFirstPass-" + userId);
+ mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData,
+ reconciledPackages);
+ t.traceEnd();
+ return reconciledPackages;
+ }
+
+ /**
+ * Called right after a user state is moved to {@link UserState#STATE_RUNNING_UNLOCKING}. This
+ * gives us a chance to reconcile app data for apps installed since
+ * {@link #onBeforeUnlockUser(int)} was called.
+ *
+ * @param previouslyReconciledPackages the result from {@link #onBeforeUnlockUser(int)}
+ */
+ public void onUserStateRunningUnlocking(@UserIdInt int userId,
+ @NonNull ArraySet<String> previouslyReconciledPackages) {
+ final UserInfo userInfo = getUserInfo(userId);
+ if (userInfo == null) {
+ return;
+ }
+ // Migrate only if build fingerprints mismatch
+ boolean migrateAppsData = !Build.FINGERPRINT.equals(userInfo.lastLoggedInFingerprint);
+ final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
+ t.traceBegin("reconcileAppsDataSecondPass-" + userId);
+ mPm.reconcileAppsData(userId, StorageManager.FLAG_STORAGE_CE, migrateAppsData,
+ previouslyReconciledPackages);
t.traceEnd();
}