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

Commit 9768c05c authored by Mayank Garg's avatar Mayank Garg
Browse files

Integrate new boot user flow with auto

Bug: 261913541
Bug: 261924826
Test: atest UserManagerServiceTest
Change-Id: I4740fe58bef942e534b6eef24bc5a1ddce270d02
Merged-In: I4740fe58bef942e534b6eef24bc5a1ddce270d02
(cherry picked from commit ad154e15)
parent bdf9aaf0
Loading
Loading
Loading
Loading
+2 −1
Original line number Diff line number Diff line
@@ -573,5 +573,6 @@ public abstract class UserManagerInternal {

     * @throws UserManager.CheckedUserOperationException if no switchable user can be found
     */
    public abstract @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException;
    public abstract @UserIdInt int getBootUser(boolean waitUntilSet)
            throws UserManager.CheckedUserOperationException;
}
+70 −38
Original line number Diff line number Diff line
@@ -161,7 +161,9 @@ import java.util.LinkedList;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicReference;

@@ -278,6 +280,8 @@ public class UserManagerService extends IUserManager.Stub {
    static final int WRITE_USER_MSG = 1;
    static final int WRITE_USER_DELAY = 2*1000;  // 2 seconds

    private static final long BOOT_USER_SET_TIMEOUT_MS = 300_000;

    // Tron counters
    private static final String TRON_GUEST_CREATED = "users_guest_created";
    private static final String TRON_USER_CREATED = "users_user_created";
@@ -333,6 +337,8 @@ public class UserManagerService extends IUserManager.Stub {
    /** Indicates that this is the 1st boot after the system user mode was changed by emulation. */
    private boolean mUpdatingSystemUserMode;

    /** Count down latch to wait while boot user is not set.*/
    private final CountDownLatch mBootUserLatch = new CountDownLatch(1);
    /**
     * Internal non-parcelable wrapper for UserInfo that is not exposed to other system apps.
     */
@@ -952,18 +958,62 @@ public class UserManagerService extends IUserManager.Stub {
            Slogf.i(LOG_TAG, "setBootUser %d", userId);
            mBootUser = userId;
        }
        mBootUserLatch.countDown();
    }

    @Override
    public @UserIdInt int getBootUser() {
        checkCreateUsersPermission("Get boot user");
        try {
            return mLocalService.getBootUser();
            return getBootUserUnchecked();
        } catch (UserManager.CheckedUserOperationException e) {
            throw e.toServiceSpecificException();
        }
    }

    private @UserIdInt int getBootUserUnchecked() throws UserManager.CheckedUserOperationException {
        synchronized (mUsersLock) {
            if (mBootUser != UserHandle.USER_NULL) {
                final UserData userData = mUsers.get(mBootUser);
                if (userData != null && userData.info.supportsSwitchToByUser()) {
                    Slogf.i(LOG_TAG, "Using provided boot user: %d", mBootUser);
                    return mBootUser;
                } else {
                    Slogf.w(LOG_TAG,
                            "Provided boot user cannot be switched to: %d", mBootUser);
                }
            }
        }

        if (isHeadlessSystemUserMode()) {
            // Return the previous foreground user, if there is one.
            final int previousUser = getPreviousFullUserToEnterForeground();
            if (previousUser != UserHandle.USER_NULL) {
                Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
                return previousUser;
            }
            // No previous user. Return the first switchable user if there is one.
            synchronized (mUsersLock) {
                final int userSize = mUsers.size();
                for (int i = 0; i < userSize; i++) {
                    final UserData userData = mUsers.valueAt(i);
                    if (userData.info.supportsSwitchToByUser()) {
                        int firstSwitchable = userData.info.id;
                        Slogf.i(LOG_TAG,
                                "Boot user is first switchable user %d", firstSwitchable);
                        return firstSwitchable;
                    }
                }
            }
            // No switchable users found. Uh oh!
            throw new UserManager.CheckedUserOperationException(
                    "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
        }
        // Not HSUM, return system user.
        return UserHandle.USER_SYSTEM;
    }


    @Override
    public int getPreviousFullUserToEnterForeground() {
        checkQueryOrCreateUsersPermission("get previous user");
@@ -7182,47 +7232,29 @@ public class UserManagerService extends IUserManager.Stub {
        }

        @Override
        public @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException {
            synchronized (mUsersLock) {
                // TODO(b/242195409): On Automotive, block if boot user not provided.
                if (mBootUser != UserHandle.USER_NULL) {
                    final UserData userData = mUsers.get(mBootUser);
                    if (userData != null && userData.info.supportsSwitchToByUser()) {
                        Slogf.i(LOG_TAG, "Using provided boot user: %d", mBootUser);
                        return mBootUser;
                    } else {
                        Slogf.w(LOG_TAG,
                                "Provided boot user cannot be switched to: %d", mBootUser);
                    }
                }
            }

            if (isHeadlessSystemUserMode()) {
                // Return the previous foreground user, if there is one.
                final int previousUser = getPreviousFullUserToEnterForeground();
                if (previousUser != UserHandle.USER_NULL) {
                    Slogf.i(LOG_TAG, "Boot user is previous user %d", previousUser);
                    return previousUser;
                }
                // No previous user. Return the first switchable user if there is one.
                synchronized (mUsersLock) {
                    final int userSize = mUsers.size();
                    for (int i = 0; i < userSize; i++) {
                        final UserData userData = mUsers.valueAt(i);
                        if (userData.info.supportsSwitchToByUser()) {
                            int firstSwitchable = userData.info.id;
                            Slogf.i(LOG_TAG,
                                    "Boot user is first switchable user %d", firstSwitchable);
                            return firstSwitchable;
        public @UserIdInt int getBootUser(boolean waitUntilSet)
                throws UserManager.CheckedUserOperationException {
            if (waitUntilSet) {
                final TimingsTraceAndSlog t = new TimingsTraceAndSlog();
                t.traceBegin("wait-boot-user");
                try {
                    if (mBootUserLatch.getCount() != 0) {
                        Slogf.d(LOG_TAG,
                                "Sleeping for boot user to be set. "
                                + "Max sleep for Time: %d", BOOT_USER_SET_TIMEOUT_MS);
                    }
                    if (!mBootUserLatch.await(BOOT_USER_SET_TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
                        Slogf.w(LOG_TAG, "Boot user not set. Timeout: %d",
                                BOOT_USER_SET_TIMEOUT_MS);
                    }
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    Slogf.w(LOG_TAG, e, "InterruptedException during wait for boot user.");
                }
                // No switchable users found. Uh oh!
                throw new UserManager.CheckedUserOperationException(
                        "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
                t.traceEnd();
            }
            // Not HSUM, return system user.
            return UserHandle.USER_SYSTEM;

            return getBootUserUnchecked();
        }

    } // class LocalService
+11 −4
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.os.Handler;
@@ -27,6 +28,7 @@ import android.os.UserManager;
import android.provider.Settings;

import com.android.server.am.ActivityManagerService;
import com.android.server.pm.PackageManagerService;
import com.android.server.pm.UserManagerInternal;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;
@@ -41,6 +43,7 @@ final class HsumBootUserInitializer {

    private final UserManagerInternal mUmi;
    private final ActivityManagerService mAms;
    private final PackageManagerService mPms;
    private final ContentResolver mContentResolver;

    private final ContentObserver mDeviceProvisionedObserver =
@@ -63,20 +66,23 @@ final class HsumBootUserInitializer {

    /** Static factory method for creating a {@link HsumBootUserInitializer} instance. */
    public static @Nullable HsumBootUserInitializer createInstance(ActivityManagerService am,
            ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
            PackageManagerService pms, ContentResolver contentResolver,
            boolean shouldAlwaysHaveMainUser) {

        if (!UserManager.isHeadlessSystemUserMode()) {
            return null;
        }
        return new HsumBootUserInitializer(
                LocalServices.getService(UserManagerInternal.class),
                am, contentResolver, shouldAlwaysHaveMainUser);
                am, pms, contentResolver, shouldAlwaysHaveMainUser);
    }

    private HsumBootUserInitializer(UserManagerInternal umi, ActivityManagerService am,
            ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
            PackageManagerService pms, ContentResolver contentResolver,
            boolean shouldAlwaysHaveMainUser) {
        mUmi = umi;
        mAms = am;
        mPms = pms;
        mContentResolver = contentResolver;
        mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser;
    }
@@ -131,7 +137,8 @@ final class HsumBootUserInitializer {

        try {
            t.traceBegin("getBootUser");
            final int bootUser = mUmi.getBootUser();
            final int bootUser = mUmi.getBootUser(/* waitUntilSet= */ mPms
                    .hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE, /* version= */0));
            t.traceEnd();
            t.traceBegin("switchToBootUser-" + bootUser);
            switchToBootUser(bootUser);
+2 −3
Original line number Diff line number Diff line
@@ -2719,7 +2719,7 @@ public final class SystemServer implements Dumpable {
        // on it in their setup, but likely needs to be done after LockSettingsService is ready.
        final HsumBootUserInitializer hsumBootUserInitializer =
                HsumBootUserInitializer.createInstance(
                        mActivityManagerService, mContentResolver,
                        mActivityManagerService, mPackageManagerService, mContentResolver,
                        context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin));
        if (hsumBootUserInitializer != null) {
            t.traceBegin("HsumBootUserInitializer.init");
@@ -3019,8 +3019,7 @@ public final class SystemServer implements Dumpable {
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
            t.traceEnd();

            if (hsumBootUserInitializer != null && !isAutomotive) {
                // TODO(b/261924826): remove isAutomotive check once the workflow is finalized
            if (hsumBootUserInitializer != null) {
                t.traceBegin("HsumBootUserInitializer.systemRunning");
                hsumBootUserInitializer.systemRunning(t);
                t.traceEnd();
+9 −6
Original line number Diff line number Diff line
@@ -260,7 +260,7 @@ public final class UserManagerServiceTest {
        mUms.setBootUser(OTHER_USER_ID);

        assertWithMessage("getBootUser")
                .that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
                .that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
    }

    @Test
@@ -273,7 +273,8 @@ public final class UserManagerServiceTest {
        mUms.setBootUser(PROFILE_USER_ID);

        assertWithMessage("getBootUser")
                .that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
                .that(mUmi.getBootUser(/* waitUntilSet= */ false))
                .isEqualTo(UserHandle.USER_SYSTEM);
    }

    @Test
@@ -289,7 +290,7 @@ public final class UserManagerServiceTest {

        // Boot user not switchable so return most recently in foreground.
        assertWithMessage("getBootUser")
                .that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
                .that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
    }

    @Test
@@ -299,7 +300,8 @@ public final class UserManagerServiceTest {
        addUser(OTHER_USER_ID);

        assertWithMessage("getBootUser")
                .that(mUmi.getBootUser()).isEqualTo(UserHandle.USER_SYSTEM);
                .that(mUmi.getBootUser(/* waitUntilSet= */ false))
                .isEqualTo(UserHandle.USER_SYSTEM);
    }

    @Test
@@ -312,14 +314,15 @@ public final class UserManagerServiceTest {
        setLastForegroundTime(OTHER_USER_ID, 2_000_000L);

        assertWithMessage("getBootUser")
                .that(mUmi.getBootUser()).isEqualTo(OTHER_USER_ID);
                .that(mUmi.getBootUser(/* waitUntilSet= */ false)).isEqualTo(OTHER_USER_ID);
    }

    @Test
    public void testGetBootUser_Headless_ThrowsIfOnlySystemUserExists() throws Exception {
        setSystemUserHeadless(true);

        assertThrows(UserManager.CheckedUserOperationException.class, () -> mUmi.getBootUser());
        assertThrows(UserManager.CheckedUserOperationException.class,
                () -> mUmi.getBootUser(/* waitUntilSet= */ false));
    }

    private void mockCurrentUser(@UserIdInt int userId) {