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

Commit 0267c806 authored by Satoshi Niwa's avatar Satoshi Niwa Committed by Android (Google) Code Review
Browse files

Merge "Refactor main user logic for backup activation in BackupManagerService" into main

parents ca884cad e68c01f7
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()));