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

Commit 99c9c23d authored by Adam Bookatz's avatar Adam Bookatz Committed by Android (Google) Code Review
Browse files

Merge "HsumBootUserInitializer creates MainUser earlier"

parents ccd98b96 e5a06ca6
Loading
Loading
Loading
Loading
+6 −5
Original line number Diff line number Diff line
@@ -553,11 +553,12 @@ public abstract class UserManagerInternal {
     * switched to.
     *
     * <p>Otherwise, in {@link UserManager#isHeadlessSystemUserMode() headless system user mode},
     * this will be the user who was last in the foreground on this device. If there is no
     * switchable user on the device, a new user will be created and its id will be returned.
     * this will be the user who was last in the foreground on this device.
     *
     * <p>In non-headless system user mode, the return value will be {@link UserHandle#USER_SYSTEM}.
     * <p>In non-headless system user mode, the return value will be
     * {@link android.os.UserHandle#USER_SYSTEM}.

     * @throws UserManager.CheckedUserOperationException if no switchable user can be found
     */
    public abstract @UserIdInt int getBootUser()
            throws UserManager.CheckedUserOperationException;
    public abstract @UserIdInt int getBootUser() throws UserManager.CheckedUserOperationException;
}
+7 −23
Original line number Diff line number Diff line
@@ -5049,6 +5049,8 @@ public class UserManagerService extends IUserManager.Stub {
        //...then external ones
        Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
        addedIntent.addFlags(Intent.FLAG_RECEIVER_INCLUDE_BACKGROUND);
        // In HSUM, MainUser might be created before PHASE_ACTIVITY_MANAGER_READY has been sent.
        addedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY_BEFORE_BOOT);
        addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
        // Also, add the UserHandle for mainline modules which can't use the @hide
        // EXTRA_USER_HANDLE.
@@ -6758,18 +6760,6 @@ public class UserManagerService extends IUserManager.Stub {
        return mLocalService.isUserInitialized(userId);
    }

    /**
     * Creates a new user, intended to be the initial user on a device in headless system user mode.
     */
    private UserInfo createInitialUserForHsum() throws UserManager.CheckedUserOperationException {
        final int flags = UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN;

        // Null name will be replaced with "Owner" on-demand to allow for localisation.
        return createUserInternalUnchecked(/* name= */ null, UserManager.USER_TYPE_FULL_SECONDARY,
                flags, UserHandle.USER_NULL, /* preCreate= */ false,
                /* disallowedPackages= */ null, /* token= */ null);
    }

    private class LocalService extends UserManagerInternal {
        @Override
        public void setDevicePolicyUserRestrictions(@UserIdInt int originatingUserId,
@@ -7249,15 +7239,9 @@ public class UserManagerService extends IUserManager.Stub {
                        }
                    }
                }
                // No switchable users. Create the initial user.
                final UserInfo newInitialUser = createInitialUserForHsum();
                if (newInitialUser == null) {
                // No switchable users found. Uh oh!
                throw new UserManager.CheckedUserOperationException(
                            "Initial user creation failed", USER_OPERATION_ERROR_UNKNOWN);
                }
                Slogf.i(LOG_TAG,
                        "No switchable users. Boot user is new user %d", newInitialUser.id);
                return newInitialUser.id;
                        "No switchable users found", USER_OPERATION_ERROR_UNKNOWN);
            }
            // Not HSUM, return system user.
            return UserHandle.USER_SYSTEM;
@@ -7437,14 +7421,14 @@ public class UserManagerService extends IUserManager.Stub {

    /**
     * Returns true, when user has {@link UserInfo#FLAG_MAIN} and system property
     * {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true.
     * {@link com.android.internal.R.bool#config_isMainUserPermanentAdmin} is true.
     */
    private boolean isNonRemovableMainUser(UserInfo userInfo) {
        return userInfo.isMain() && isMainUserPermanentAdmin();
    }

    /**
     * Returns true, when {@link com.android.internal.R.bool.isMainUserPermanentAdmin} is true.
     * Returns true if {@link com.android.internal.R.bool#config_isMainUserPermanentAdmin} is true.
     * If the main user is a permanent admin user it can't be deleted
     * or downgraded to non-admin status.
     */
+71 −9
Original line number Diff line number Diff line
@@ -15,8 +15,10 @@
 */
package com.android.server;

import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.pm.UserInfo;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
@@ -30,22 +32,43 @@ import com.android.server.utils.TimingsTraceAndSlog;
 * Class responsible for booting the device in the proper user on headless system user mode.
 *
 */
// TODO(b/204091126): STOPSHIP - provide proper APIs
final class BootUserInitializer {
final class HsumBootUserInitializer {

    private static final String TAG = BootUserInitializer.class.getSimpleName();

     // TODO(b/204091126): STOPSHIP - set to false or dynamic value
    private static final boolean DEBUG = true;
    private static final String TAG = HsumBootUserInitializer.class.getSimpleName();

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

    BootUserInitializer(ActivityManagerService am, ContentResolver contentResolver) {
    /** Whether this device should always have a non-removable MainUser, including at first boot. */
    private final boolean mShouldAlwaysHaveMainUser;

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

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

    private HsumBootUserInitializer(UserManagerInternal umi, ActivityManagerService am,
            ContentResolver contentResolver, boolean shouldAlwaysHaveMainUser) {
        mUmi = umi;
        mAms = am;
        mContentResolver = contentResolver;
        this.mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser;
    }

    /**
     * Initialize this object, and create MainUser if needed.
     *
     * Should be called before PHASE_SYSTEM_SERVICES_READY as services' setups may require MainUser,
     * but probably after PHASE_LOCK_SETTINGS_READY since that may be needed for user creation.
     */
    public void init(TimingsTraceAndSlog t) {
        Slogf.i(TAG, "init())");

@@ -53,17 +76,56 @@ final class BootUserInitializer {
        // this class or the setup wizard app
        provisionHeadlessSystemUser();

        if (mShouldAlwaysHaveMainUser) {
            t.traceBegin("createMainUserIfNeeded");
            createMainUserIfNeeded();
            t.traceEnd();
        }
    }

    private void createMainUserIfNeeded() {
        int mainUser = mUmi.getMainUserId();
        if (mainUser != UserHandle.USER_NULL) {
            Slogf.d(TAG, "Found existing MainUser, userId=%d", mainUser);
            return;
        }

        Slogf.d(TAG, "Creating a new MainUser");
        try {
            final UserInfo newInitialUser = mUmi.createUserEvenWhenDisallowed(
                    /* name= */ null, // null will appear as "Owner" in on-demand localisation
                    UserManager.USER_TYPE_FULL_SECONDARY,
                    UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN,
                    /* disallowedPackages= */ null,
                    /* token= */ null);
            if (newInitialUser == null) {
                Slogf.wtf(TAG, "Initial bootable MainUser creation failed: returned null");
            } else {
                Slogf.i(TAG, "Successfully created MainUser, userId=%d", newInitialUser.id);
            }
        } catch (UserManager.CheckedUserOperationException e) {
            Slogf.wtf(TAG, "Initial bootable MainUser creation failed", e);
        }
    }

    /**
     * Put the device into the correct user state: unlock the system and switch to the boot user.
     *
     * Should only call once PHASE_THIRD_PARTY_APPS_CAN_START is reached to ensure that privileged
     * apps have had the chance to set the boot user, if applicable.
     */
    public void systemRunning(TimingsTraceAndSlog t) {
        unlockSystemUser(t);

        try {
            t.traceBegin("getBootUser");
            int bootUser = LocalServices.getService(UserManagerInternal.class).getBootUser();
            final int bootUser = mUmi.getBootUser();
            t.traceEnd();
            t.traceBegin("switchToBootUser-" + bootUser);
            switchToBootUser(bootUser);
            t.traceEnd();
        } catch (UserManager.CheckedUserOperationException e) {
            Slogf.wtf(TAG, "Failed to created boot user", e);
            Slogf.wtf(TAG, "Failed to switch to boot user since there isn't one.");
        }
    }

+16 −5
Original line number Diff line number Diff line
@@ -75,7 +75,6 @@ import android.os.StrictMode;
import android.os.SystemClock;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.os.storage.IStorageManager;
import android.provider.DeviceConfig;
import android.provider.Settings;
@@ -2694,6 +2693,18 @@ public final class SystemServer implements Dumpable {
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_LOCK_SETTINGS_READY);
        t.traceEnd();

        // Create initial user if needed, which should be done early since some system services rely
        // on it in their setup, but likely needs to be done after LockSettingsService is ready.
        final HsumBootUserInitializer hsumBootUserInitializer =
                HsumBootUserInitializer.createInstance(
                        mActivityManagerService, mContentResolver,
                        context.getResources().getBoolean(R.bool.config_isMainUserPermanentAdmin));
        if (hsumBootUserInitializer != null) {
            t.traceBegin("HsumBootUserInitializer.init");
            hsumBootUserInitializer.init(t);
            t.traceEnd();
        }

        t.traceBegin("StartBootPhaseSystemServicesReady");
        mSystemServiceManager.startBootPhase(t, SystemService.PHASE_SYSTEM_SERVICES_READY);
        t.traceEnd();
@@ -2961,10 +2972,10 @@ public final class SystemServer implements Dumpable {
            mSystemServiceManager.startBootPhase(t, SystemService.PHASE_THIRD_PARTY_APPS_CAN_START);
            t.traceEnd();

            if (UserManager.isHeadlessSystemUserMode() && !isAutomotive) {
                // TODO(b/204091126): remove isAutomotive check once the workflow is finalized
                t.traceBegin("BootUserInitializer");
                new BootUserInitializer(mActivityManagerService, mContentResolver).init(t);
            if (hsumBootUserInitializer != null && !isAutomotive) {
                // TODO(b/261924826): remove isAutomotive check once the workflow is finalized
                t.traceBegin("HsumBootUserInitializer.systemRunning");
                hsumBootUserInitializer.systemRunning(t);
                t.traceEnd();
            }

+3 −13
Original line number Diff line number Diff line
@@ -20,6 +20,7 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;

import static com.google.common.truth.Truth.assertWithMessage;

import static org.junit.Assert.assertThrows;
import static org.mockito.Mockito.any;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.when;
@@ -316,21 +317,10 @@ public final class UserManagerServiceTest extends ExtendedMockitoTestCase {
    }

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

        int bootUser = mUmi.getBootUser();

        assertWithMessage("getStartingUser")
                .that(bootUser).isNotEqualTo(UserHandle.USER_SYSTEM);

        UserData newUser = mUsers.get(bootUser);
        assertWithMessage("New boot user is a full user")
                .that(newUser.info.isFull()).isTrue();
        assertWithMessage("New boot user is an admin user")
                .that(newUser.info.isAdmin()).isTrue();
        assertWithMessage("New boot user is the main user")
                .that(newUser.info.isMain()).isTrue();
        assertThrows(UserManager.CheckedUserOperationException.class, () -> mUmi.getBootUser());
    }

    private void mockCurrentUser(@UserIdInt int userId) {