Polishing for migration to disable Weaver

- Prevent device from ending up in bad state if it rolls back to old
  build after the migration but before the boot has completed.
- Place a (generous) maximum number of retries on unwrapping the SP.
- Improve a comment.

Bug: 356324437
Test: atest FrameworksServicesTests:com.android.server.locksettings
Flag: EXEMPT uses config option instead
Change-Id: If05d11a0a884294896223aedd79d7f689380dc58
Merged-In: If05d11a0a884294896223aedd79d7f689380dc58
(cherry picked from commit 06f58c061f0cc6dd082c0001d259f00ee7588794)
diff --git a/services/core/java/com/android/server/locksettings/LockSettingsService.java b/services/core/java/com/android/server/locksettings/LockSettingsService.java
index ab1eb8c..6dd2dac 100644
--- a/services/core/java/com/android/server/locksettings/LockSettingsService.java
+++ b/services/core/java/com/android/server/locksettings/LockSettingsService.java
@@ -127,6 +127,7 @@
 import android.util.ArraySet;
 import android.util.Log;
 import android.util.LongSparseArray;
+import android.util.Pair;
 import android.util.Slog;
 import android.util.SparseArray;
 import android.util.SparseIntArray;
@@ -312,6 +313,10 @@
     @GuardedBy("mUserCreationAndRemovalLock")
     private boolean mThirdPartyAppsStarted;
 
+    // This list contains the (protectorId, userId) of any protectors that were by replaced by a
+    // migration and should be destroyed once rollback to the old build is no longer possible.
+    private ArrayList<Pair<Long, Integer>> mProtectorsToDestroyOnBootCompleted = new ArrayList<>();
+
     // Current password metrics for all secured users on the device. Updated when user unlocks the
     // device or changes password. Removed if user is stopped with its CE key evicted.
     @GuardedBy("this")
@@ -366,6 +371,10 @@
                 mLockSettingsService.migrateOldDataAfterSystemReady();
                 mLockSettingsService.deleteRepairModePersistentDataIfNeeded();
             } else if (phase == PHASE_BOOT_COMPLETED) {
+                // In the case of an upgrade, PHASE_BOOT_COMPLETED means that a rollback to the old
+                // build can no longer occur.  This is the time to destroy any migrated protectors.
+                mLockSettingsService.destroyMigratedProtectors();
+
                 mLockSettingsService.loadEscrowData();
             }
         }
@@ -1123,7 +1132,8 @@
             // - Upgrading from Android 14, where unsecured users didn't have Keystore super keys.
             //
             // - Upgrading from a build with config_disableWeaverOnUnsecuredUsers=false to one with
-            //   config_disableWeaverOnUnsecuredUsers=true.
+            //   config_disableWeaverOnUnsecuredUsers=true.  (We don't bother to proactively add
+            //   Weaver for the reverse update to false, as it's too late to help in that case.)
             //
             // The end result is that all users, regardless of whether they are secured or not, have
             // a synthetic password with all keys initialized and protected by it, and honoring
@@ -1176,13 +1186,17 @@
             // protected by Weaver.  Note that the problematic HAL can also deadlock if called with
             // the ActivityManagerService lock held, but that should not be a problem here since
             // that lock isn't held here, unlike unlockUserKeyIfUnsecured() where it is.
-            while (sp == null) {
+            for (int i = 0; i < 12 && sp == null; i++) {
                 Slog.e(TAG, "Failed to unwrap synthetic password. Waiting 5 seconds to retry.");
                 SystemClock.sleep(5000);
                 result = mSpManager.unlockLskfBasedProtector(getGateKeeperService(), protectorId,
                             LockscreenCredential.createNone(), userId, null);
                 sp = result.syntheticPassword;
             }
+            if (sp == null) {
+                throw new IllegalStateException(
+                        "Failed to unwrap synthetic password for unsecured user");
+            }
             // If the SP is protected by Weaver, then remove the Weaver protection in order to make
             // config_disableWeaverOnUnsecuredUsers=true take effect.
             if (result.usedWeaver) {
@@ -1199,9 +1213,10 @@
                     throw new IllegalStateException("New SP protector does not work");
                 }
 
-                // Replace the protector.
+                // Replace the protector.  Wait until PHASE_BOOT_COMPLETED to destroy the old
+                // protector, since the Weaver slot erasure and freeing cannot be rolled back.
                 setCurrentLskfBasedProtectorId(newProtectorId, userId);
-                mSpManager.destroyLskfBasedProtector(protectorId, userId);
+                mProtectorsToDestroyOnBootCompleted.add(new Pair(protectorId, userId));
             } else {
                 Slog.i(TAG, "Synthetic password is already not protected by Weaver");
             }
@@ -1225,6 +1240,17 @@
         initKeystoreSuperKeys(userId, sp, /* allowExisting= */ true);
     }
 
+    private void destroyMigratedProtectors() {
+        if (!mProtectorsToDestroyOnBootCompleted.isEmpty()) {
+            synchronized (mSpManager) {
+                for (Pair<Long, Integer> pair : mProtectorsToDestroyOnBootCompleted) {
+                    mSpManager.destroyLskfBasedProtector(pair.first, pair.second);
+                }
+            }
+        }
+        mProtectorsToDestroyOnBootCompleted = null; // The list is no longer needed.
+    }
+
     /**
      * Returns the lowest password quality that still presents the same UI for entering it.
      *