Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ff116c87 authored by Eric Biggers's avatar Eric Biggers
Browse files

Allow migration to SP to be rolled back by filesystem checkpoint

On upgrade from Android 13 or earlier, LockSettingsService is creating a
synthetic password (SP) for all users that didn't have one before, and
re-encrypting the user's CE key with the SP.  Currently this happens at
PHASE_BOOT_COMPLETED, since Weaver is not yet guaranteed to be available
at the previous phase, PHASE_THIRD_PARTY_APPS_CAN_START.

An issue with using PHASE_BOOT_COMPLETED is that during an upgrade,
PHASE_BOOT_COMPLETED happens after the userdata filesystem checkpoint
has already been committed.  Therefore, if a problem occurs with the
migration to SP, the changes won't be rolled back and the device will be
left in a broken state, recoverable only via a factory reset.

Important migrations like this should happen before the checkpoint is
committed.  Therefore, replace the use of PHASE_BOOT_COMPLETED with a
direct call into LockSettingsService in an appropriate place, similar to
the existing LockSettingsService.systemReady() call.

I also considered creating a PHASE_THIRD_PARTY_APPS_STARTED boot phase.
However, any new boot phase would become part of the services API
(services/api/current.txt), which is more than I'd like to do here.

Test: Made an intentionally broken build with
      LockSettingsService.onThirdPartyAppsStarted() changed to throw a
      RuntimeException at the end, crashing system_server.  Tested an
      OTA from tm-qpr2-release to that build, on a device that didn't
      have a lockscreen credential set on user 0 (so that user 0 was
      migrated to SP-based credentials just before the crash).  The OTA
      failed as expected and successfully rolled back to the original
      build, with user 0's data still accessible (this was not possible
      before this change).
Bug: 232452368
Change-Id: I77d30f9be57de7b7c4818680732331549ecb73c8
parent 0fc931f2
Loading
Loading
Loading
Loading
+6 −0
Original line number Diff line number Diff line
@@ -53,6 +53,12 @@ public abstract class LockSettingsInternal {
    public static final int ARM_REBOOT_ERROR_STORE_ESCROW_KEY = 7;
    // TODO(b/183140900) split store escrow key errors into detailed ones.

    /**
     * This is called when Weaver is guaranteed to be available (if the device supports Weaver).
     * It does any synthetic password related work that was delayed from earlier in the boot.
     */
    public abstract void onThirdPartyAppsStarted();

    /**
     * Unlocks the credential-encrypted storage for the given user if the user is not secured, i.e.
     * doesn't have an LSKF.
+33 −34
Original line number Diff line number Diff line
@@ -248,14 +248,14 @@ public class LockSettingsService extends ILockSettings.Stub {

    // Locking order is mUserCreationAndRemovalLock -> mSpManager.
    private final Object mUserCreationAndRemovalLock = new Object();
    // These two arrays are only used at boot time.  To save memory, they are set to null when
    // PHASE_BOOT_COMPLETED is reached.
    // These two arrays are only used at boot time.  To save memory, they are set to null near the
    // end of the boot, when onThirdPartyAppsStarted() is called.
    @GuardedBy("mUserCreationAndRemovalLock")
    private SparseIntArray mEarlyCreatedUsers = new SparseIntArray();
    @GuardedBy("mUserCreationAndRemovalLock")
    private SparseIntArray mEarlyRemovedUsers = new SparseIntArray();
    @GuardedBy("mUserCreationAndRemovalLock")
    private boolean mBootComplete;
    private boolean mThirdPartyAppsStarted;

    // Current password metrics for all secured users on the device. Updated when user unlocks the
    // device or changes password. Removed when user is stopped.
@@ -297,16 +297,9 @@ public class LockSettingsService extends ILockSettings.Stub {
        @Override
        public void onBootPhase(int phase) {
            super.onBootPhase(phase);
            switch (phase) {
                case PHASE_ACTIVITY_MANAGER_READY:
            if (phase == PHASE_ACTIVITY_MANAGER_READY) {
                mLockSettingsService.migrateOldDataAfterSystemReady();
                mLockSettingsService.loadEscrowData();
                    break;
                case PHASE_BOOT_COMPLETED:
                    mLockSettingsService.bootCompleted();
                    break;
                default:
                    break;
            }
        }

@@ -749,8 +742,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * <p>
     * This is primarily needed for users that were removed by Android 13 or earlier, which didn't
     * guarantee removal of LSS state as it relied on the {@code ACTION_USER_REMOVED} intent.  It is
     * also needed because {@link #removeUser()} delays requests to remove LSS state until the
     * {@code PHASE_BOOT_COMPLETED} boot phase, so they can be lost.
     * also needed because {@link #removeUser()} delays requests to remove LSS state until Weaver is
     * guaranteed to be available, so they can be lost.
     * <p>
     * Stale state is detected by checking whether the user serial number changed.  This works
     * because user serial numbers are never reused.
@@ -931,7 +924,9 @@ public class LockSettingsService extends ILockSettings.Stub {
        return success;
    }

    private void bootCompleted() {
    // This is called when Weaver is guaranteed to be available (if the device supports Weaver).
    // It does any synthetic password related work that was delayed from earlier in the boot.
    private void onThirdPartyAppsStarted() {
        synchronized (mUserCreationAndRemovalLock) {
            // Handle delayed calls to LSS.removeUser() and LSS.createNewUser().
            for (int i = 0; i < mEarlyRemovedUsers.size(); i++) {
@@ -976,7 +971,7 @@ public class LockSettingsService extends ILockSettings.Stub {
                setString("migrated_all_users_to_sp_and_bound_ce", "true", 0);
            }

            mBootComplete = true;
            mThirdPartyAppsStarted = true;
        }
    }

@@ -2304,14 +2299,14 @@ public class LockSettingsService extends ILockSettings.Stub {

    private void createNewUser(@UserIdInt int userId, int userSerialNumber) {
        synchronized (mUserCreationAndRemovalLock) {
            // Before PHASE_BOOT_COMPLETED, don't actually create the synthetic password yet, but
            // rather automatically delay it to later.  We do this because protecting the synthetic
            // During early boot, don't actually create the synthetic password yet, but rather
            // automatically delay it to later.  We do this because protecting the synthetic
            // password requires the Weaver HAL if the device supports it, and some devices don't
            // make Weaver available until fairly late in the boot process.  This logic ensures a
            // consistent flow across all devices, regardless of their Weaver implementation.
            if (!mBootComplete) {
                Slogf.i(TAG, "Delaying locksettings state creation for user %d until boot complete",
                        userId);
            if (!mThirdPartyAppsStarted) {
                Slogf.i(TAG, "Delaying locksettings state creation for user %d until third-party " +
                        "apps are started", userId);
                mEarlyCreatedUsers.put(userId, userSerialNumber);
                mEarlyRemovedUsers.delete(userId);
                return;
@@ -2325,14 +2320,14 @@ public class LockSettingsService extends ILockSettings.Stub {

    private void removeUser(@UserIdInt int userId) {
        synchronized (mUserCreationAndRemovalLock) {
            // Before PHASE_BOOT_COMPLETED, don't actually remove the LSS state yet, but rather
            // automatically delay it to later.  We do this because deleting synthetic password
            // protectors requires the Weaver HAL if the device supports it, and some devices don't
            // make Weaver available until fairly late in the boot process.  This logic ensures a
            // consistent flow across all devices, regardless of their Weaver implementation.
            if (!mBootComplete) {
                Slogf.i(TAG, "Delaying locksettings state removal for user %d until boot complete",
                        userId);
            // During early boot, don't actually remove the LSS state yet, but rather automatically
            // delay it to later.  We do this because deleting synthetic password protectors
            // requires the Weaver HAL if the device supports it, and some devices don't make Weaver
            // available until fairly late in the boot process.  This logic ensures a consistent
            // flow across all devices, regardless of their Weaver implementation.
            if (!mThirdPartyAppsStarted) {
                Slogf.i(TAG, "Delaying locksettings state removal for user %d until third-party " +
                        "apps are started", userId);
                if (mEarlyCreatedUsers.indexOfKey(userId) >= 0) {
                    mEarlyCreatedUsers.delete(userId);
                } else {
@@ -2634,9 +2629,8 @@ public class LockSettingsService extends ILockSettings.Stub {
     * protects the user's CE key with a key derived from the SP.
     * <p>
     * This is called just once in the lifetime of the user: at user creation time (possibly delayed
     * until {@code PHASE_BOOT_COMPLETED} to ensure that the Weaver HAL is available if the device
     * supports it), or when upgrading from Android 13 or earlier where users with no LSKF didn't
     * necessarily have an SP.
     * until the time when Weaver is guaranteed to be available), or when upgrading from Android 13
     * or earlier where users with no LSKF didn't necessarily have an SP.
     */
    @GuardedBy("mSpManager")
    @VisibleForTesting
@@ -3159,7 +3153,7 @@ public class LockSettingsService extends ILockSettings.Stub {

        pw.println("PasswordHandleCount: " + mGatekeeperPasswords.size());
        synchronized (mUserCreationAndRemovalLock) {
            pw.println("BootComplete: " + mBootComplete);
            pw.println("ThirdPartyAppsStarted: " + mThirdPartyAppsStarted);
        }
    }

@@ -3316,6 +3310,11 @@ public class LockSettingsService extends ILockSettings.Stub {

    private final class LocalService extends LockSettingsInternal {

        @Override
        public void onThirdPartyAppsStarted() {
            LockSettingsService.this.onThirdPartyAppsStarted();
        }

        @Override
        public void unlockUserKeyIfUnsecured(@UserIdInt int userId) {
            LockSettingsService.this.unlockUserKeyIfUnsecured(userId);
+9 −0
Original line number Diff line number Diff line
@@ -103,6 +103,7 @@ import com.android.internal.util.ConcurrentUtils;
import com.android.internal.util.EmergencyAffordanceManager;
import com.android.internal.util.FrameworkStatsLog;
import com.android.internal.widget.ILockSettings;
import com.android.internal.widget.LockSettingsInternal;
import com.android.server.am.ActivityManagerService;
import com.android.server.ambientcontext.AmbientContextManagerService;
import com.android.server.appbinding.AppBindingService;
@@ -3008,6 +3009,14 @@ public final class SystemServer implements Dumpable {
            t.traceEnd();
        }, t);

        t.traceBegin("LockSettingsThirdPartyAppsStarted");
        LockSettingsInternal lockSettingsInternal =
            LocalServices.getService(LockSettingsInternal.class);
        if (lockSettingsInternal != null) {
            lockSettingsInternal.onThirdPartyAppsStarted();
        }
        t.traceEnd();

        t.traceBegin("StartSystemUI");
        try {
            startSystemUi(context, windowManagerF);