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

Commit e68c01f7 authored by Satoshi Niwa's avatar Satoshi Niwa
Browse files

Refactor main user logic for backup activation in BackupManagerService

- Removed the state variable `mDefaultBackupUserId`.
- Modified `isDefaultBackupActiveUser()` to dynamically determine the
  correct default user based on the current mode (HSUM or not) and the
  *current* result of `UserManager.getMainUser()`
- Introduced `mDidMainUserExistAtBoot` (boolean) to track if the main
  user was available during BMS construction.
- Renamed `updateDefaultBackupUserIdIfNeeded()` to
  `stopServiceForSystemUserIfMainUserCreated()` to better reflect its
  purpose.

Test: Existing unit tests in BackupManagerServiceTest pass
Bug: 374830726
Bug: 406114361
Flag: EXEMPT refactoring
Change-Id: I2f214437229b76a6cd38e3246ec9d2b39101ee5c
parent d9806f11
Loading
Loading
Loading
Loading
+54 −39
Original line number Diff line number Diff line
@@ -155,18 +155,21 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM
    };

    /**
     * The user that the backup is activated by default for.
     * Tracks whether {@link UserManager#getMainUser()} returned a non-null value
     * during {@link BackupManagerService} construction.
     *
     * <p>This is used to handle an edge case on first boot where the main user might be created
     * *after* BackupManagerService starts. If the main user didn't exist at boot, we might
     * need to stop the backup service for the system user later.
     *
     * <p>If there is a {@link UserManager#getMainUser()}, this will be that user. If not, it will
     * be {@link UserHandle#USER_SYSTEM}.
     *
     * <p>Note: on the first ever boot of a new device, this might change once the first user is
     * unlocked. See {@link #updateDefaultBackupUserIdIfNeeded()}.
     *
     * @see #isBackupActivatedForUser(int)
     * @see #stopServiceForSystemUserIfMainUserCreated()
     */
    @UserIdInt private int mDefaultBackupUserId;
    private boolean mDidMainUserExistAtBoot = false;

    /**
     * Tracks whether any user unlock event has occurred since the system booted.
     * @see #stopServiceForSystemUserIfMainUserCreated()
     */
    private boolean mHasFirstUserUnlockedSinceBoot = false;

    public BackupManagerService(Context context) {
@@ -183,9 +186,11 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM
        mTransportWhitelist = (transportWhitelist == null) ? emptySet() : transportWhitelist;
        mContext.registerReceiver(
                mUserRemovedReceiver, new IntentFilter(Intent.ACTION_USER_REMOVED));
        UserHandle mainUser = getUserManager().getMainUser();
        mDefaultBackupUserId = mainUser == null ? UserHandle.USER_SYSTEM : mainUser.getIdentifier();
        Slog.d(TAG, "Default backup user id = " + mDefaultBackupUserId);
        mDidMainUserExistAtBoot = getUserManager().getMainUser() != null;
        if (!mDidMainUserExistAtBoot) {
            // This might happen on the first boot if BMS starts before the main user is created.
            Slog.d(TAG, "Main user does not exist yet");
        }
    }

    @VisibleForTesting
@@ -345,11 +350,23 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM

    /** Returns whether the user's backup is initially set to active. */
    private boolean isDefaultBackupActiveUser(@UserIdInt int userId) {
        // The system user is not a real user in headless mode.
        if (UserManager.isHeadlessSystemUserMode() && userId == UserHandle.USER_SYSTEM) {
        // In non-HSUM, the system user is the primary user and handles backup.
        if (!UserManager.isHeadlessSystemUserMode()) {
            return userId == UserHandle.USER_SYSTEM;
        }

        // In HSUM, the system user is not a real user and should not have backup activated.
        if (userId == UserHandle.USER_SYSTEM) {
            return false;
        }

        UserHandle mainUser = getUserManager().getMainUser();
        if (mainUser == null) {
            return false;
        }
        return userId == mDefaultBackupUserId;

        // In HSUM, the main user is the one whose backup should be active by default.
        return userId == mainUser.getIdentifier();
    }

    @VisibleForTesting
@@ -1712,7 +1729,7 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM
            // backup is not essential for device functioning.
            mBackupManagerService.postToHandler(
                    () -> {
                        mBackupManagerService.updateDefaultBackupUserIdIfNeeded();
                        mBackupManagerService.stopServiceForSystemUserIfMainUserCreated();
                        mBackupManagerService.startServiceForUser(user.getUserIdentifier());
                        mBackupManagerService.mHasFirstUserUnlockedSinceBoot = true;
                    });
@@ -1730,18 +1747,24 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM
    }

    /**
     * On the first ever boot of a new device, the 'main' user might not exist for a short period of
     * time and be created after {@link BackupManagerService} is created. In this case the {@link
     * #mDefaultBackupUserId} will be the system user initially, but we need to change it to the
     * newly created {@link UserManager#getMainUser()} later.
     *
     * <p>{@link Lifecycle#onUserUnlocking(SystemService.TargetUser)} (for any user) is the earliest
     * point where we know that a main user (if there is going to be one) is created.
     * Handles an edge case specific to the *first boot* of a device.
     *
     * This method is called upon the *first* user unlock event after boot. It checks if:
     * - This is indeed the first user unlock since boot.
     * - The main user did *not* exist when BMS was constructed.
     * - A main user now exists and it's *not* the system user.
     * If all conditions are met, then the method checks if USER_SYSTEM's service should be
     * stopped based on current state.
     *
     * Normally, the service for USER_SYSTEM isn't expected to be running yet when this executes
     * during the unlock sequence. This method acts primarily as a safeguard against a rare race
     * condition where `setBackupServiceActive(USER_SYSTEM)` could be called concurrently after
     * the unlock task is posted but before this method runs.
     */
    private void updateDefaultBackupUserIdIfNeeded() {
    private void stopServiceForSystemUserIfMainUserCreated() {
        // The default user can only change before any user unlocks since boot, and it will only
        // change from the system user to a non-system user.
        if (mHasFirstUserUnlockedSinceBoot || mDefaultBackupUserId != UserHandle.USER_SYSTEM) {
        if (mHasFirstUserUnlockedSinceBoot || mDidMainUserExistAtBoot) {
            return;
        }

@@ -1750,20 +1773,12 @@ public class BackupManagerService extends IBackupManager.Stub implements BackupM
            return;
        }

        if (mDefaultBackupUserId != mainUser.getIdentifier()) {
            int oldDefaultBackupUserId = mDefaultBackupUserId;
            mDefaultBackupUserId = mainUser.getIdentifier();
            // We don't expect the service to be started for the old default user but we attempt to
            // stop its service to be safe.
            if (!isBackupActivatedForUser(oldDefaultBackupUserId)) {
                stopServiceForUser(oldDefaultBackupUserId);
            }
            Slog.i(
                    TAG,
                    "Default backup user changed from "
                            + oldDefaultBackupUserId
                            + " to "
                            + mDefaultBackupUserId);
        if (mainUser.getIdentifier() == UserHandle.USER_SYSTEM) {
            return;
        }

        if (!isBackupActivatedForUser(UserHandle.USER_SYSTEM)) {
            stopServiceForUser(UserHandle.USER_SYSTEM);
        }
    }
}
+11 −2
Original line number Diff line number Diff line
@@ -125,6 +125,9 @@ public class BackupManagerServiceTest {
                () -> UserBackupManagerService.createAndInitializeService(eq(NON_SYSTEM_USER),
                        any(), any(), any()));

        // Assume non-headless mode by default.
        mockHeadlessSystemUserMode(false);

        when(mNonSystemUserBackupManagerService.getUserId()).thenReturn(NON_SYSTEM_USER);
        when(mUserManagerMock.getUserInfo(UserHandle.USER_SYSTEM)).thenReturn(mUserInfoMock);
        when(mUserManagerMock.getUserInfo(NON_SYSTEM_USER)).thenReturn(mUserInfoMock);
@@ -723,6 +726,7 @@ public class BackupManagerServiceTest {
        when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
        assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));

        mockHeadlessSystemUserMode(true);
        simulateUserUnlocked(UserHandle.USER_SYSTEM);

        assertTrue(mService.isBackupServiceActive(NON_SYSTEM_USER));
@@ -737,6 +741,7 @@ public class BackupManagerServiceTest {
        when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(NON_SYSTEM_USER));
        assertFalse(mService.isBackupServiceActive(NON_SYSTEM_USER));

        mockHeadlessSystemUserMode(true);
        simulateUserUnlocked(UserHandle.USER_SYSTEM);

        assertFalse(mService.isUserReadyForBackup(UserHandle.USER_SYSTEM));
@@ -757,7 +762,7 @@ public class BackupManagerServiceTest {

    private void createBackupManagerServiceAndUnlockSystemUser() {
        // Assume non-headless mode for standard system user tests.
        doReturn(false).when(() -> UserManager.isHeadlessSystemUserMode());
        mockHeadlessSystemUserMode(false);

        mService = new BackupManagerServiceTestable(mContextMock);
        createBackupServiceLifecycle(mContextMock, mService);
@@ -770,7 +775,7 @@ public class BackupManagerServiceTest {
     */
    private void setMockMainUserAndCreateBackupManagerService(int userId) {
        // Assume headless mode for tests involving a non-system main user explicitly.
        doReturn(true).when(() -> UserManager.isHeadlessSystemUserMode());
        mockHeadlessSystemUserMode(true);

        when(mUserManagerMock.getMainUser()).thenReturn(UserHandle.of(userId));
        mService = new BackupManagerServiceTestable(mContextMock);
@@ -807,6 +812,10 @@ public class BackupManagerServiceTest {
        return new File(sTestDir, "rememberActivated-" + userId);
    }

    private static void mockHeadlessSystemUserMode(boolean isHeadless) {
        doReturn(isHeadless).when(UserManager::isHeadlessSystemUserMode);
    }

    private static void mockDumpPermissionsGranted(boolean granted) {
        doReturn(granted)
                .when(() -> DumpUtils.checkDumpAndUsageStatsPermission(any(), any(), any()));