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

Commit ab0a6a5e authored by Felipe Leme's avatar Felipe Leme
Browse files

Changed HsumBootUserInitializer so it "demotes" the main user if needed.

The main user is created on first boot on devices that set
config_isMainUserPermanentAdmin to true. But the concept of main user
is temporary (and eventually will be deprecated), so we need a way to
"demote" the main user once a device doesn't need it; for now, we'll
simply change the flag on boot when the property changes (which is
what this CL do) as the main user is not quite deprecated yet (and
there shouldn't be any devices other than some internal ones that
would be making this transition), although in the future we might
need a more sophisticated mechanism (like an internal callback to let
SystemServices know about the demotion).

Test: atest HsumBootUserInitializerTest
Test: atest FrameworksMockingServicesTests --test-filter=".*UserManagerServiceMockedTest#.*DemoteMainUser.*"

Bug: 402486365
Flag: android.multiuser.create_initial_user

Change-Id: I6807d6ab12a16cc5a892d1bdca91a4fd3b761fe3
parent da84cef0
Loading
Loading
Loading
Loading
+7 −0
Original line number Diff line number Diff line
@@ -507,6 +507,13 @@ flag {
     bug: "409650316"
}

flag {
     name: "demote_main_user"
     namespace: "multiuser"
     description: "Remove the FLAG_MAIN on boot when the device doesn't support Main User anymore"
     bug: "402486365"
}

flag {
    name: "setupwizard_username_population"
    namespace: "multiuser"
+37 −13
Original line number Diff line number Diff line
@@ -69,8 +69,8 @@ public final class HsumBootUserInitializer {
                }
            };

    /** Whether this device should always have a non-removable MainUser, including at first boot. */
    private final boolean mShouldAlwaysHaveMainUser;
    /** Whether it should create a main user on first boot. */
    private final boolean mShouldCreateMainUser;

    /** Whether it should create an initial user, but without setting it as the main user. */
    private final boolean mShouldCreateInitialUser;
@@ -90,12 +90,12 @@ public final class HsumBootUserInitializer {
    @VisibleForTesting
    HsumBootUserInitializer(UserManagerService ums, ActivityManagerService ams,
            PackageManagerService pms, ContentResolver contentResolver,
            boolean shouldAlwaysHaveMainUser, boolean shouldCreateInitialUser) {
            boolean shouldCreateMainUser, boolean shouldCreateInitialUser) {
        mUms = ums;
        mAms = ams;
        mPms = pms;
        mContentResolver = contentResolver;
        mShouldAlwaysHaveMainUser = shouldAlwaysHaveMainUser;
        mShouldCreateMainUser = shouldCreateMainUser;
        mShouldCreateInitialUser = shouldCreateInitialUser;
    }

@@ -105,7 +105,7 @@ public final class HsumBootUserInitializer {
            Slogf.d(TAG, "preCreateInitialUserFlagInit())");
        }

        if (mShouldAlwaysHaveMainUser) {
        if (mShouldCreateMainUser) {
            t.traceBegin("createMainUserIfNeeded");
            preCreateInitialUserCreateMainUserIfNeeded();
            t.traceEnd();
@@ -129,7 +129,7 @@ public final class HsumBootUserInitializer {
                    UserManager.USER_TYPE_FULL_SECONDARY,
                    UserInfo.FLAG_ADMIN | UserInfo.FLAG_MAIN,
                    /* parentId= */ UserHandle.USER_NULL,
                    /* preCreated= */ false,
                    /* preCreate= */ false,
                    /* disallowedPackages= */ null,
                    /* token= */ null);
            if (newInitialUser != null) {
@@ -153,9 +153,9 @@ public final class HsumBootUserInitializer {
     */
    public void init(TimingsTraceAndSlog t) {
        if (DEBUG) {
            Slogf.d(TAG, "init(): shouldAlwaysHaveMainUser=%b, shouldCreateInitialUser=%b, "
            Slogf.d(TAG, "init(): shouldCreateMainUser=%b, shouldCreateInitialUser=%b, "
                    + "Flags.createInitialUser=%b",
                    mShouldAlwaysHaveMainUser, mShouldCreateInitialUser, Flags.createInitialUser());
                    mShouldCreateMainUser, mShouldCreateInitialUser, Flags.createInitialUser());
        } else {
            Slogf.i(TAG, "Initializing");
        }
@@ -165,11 +165,19 @@ public final class HsumBootUserInitializer {
            return;
        }

        if (mShouldAlwaysHaveMainUser) {
            createMainUserIfNeeded(t);
        t.traceBegin("getMainUserId");
        int mainUserId = mUms.getMainUserId();
        t.traceEnd();

        if (mShouldCreateMainUser) {
            createMainUserIfNeeded(t, mainUserId);
            return;
        }

        t.traceBegin("demoteMainUserIfNeeded");
        demoteMainUserIfNeeded(t, mainUserId);
        t.traceEnd();

        if (mShouldCreateInitialUser) {
            createAdminUserIfNeeded(t);
            return;
@@ -179,11 +187,10 @@ public final class HsumBootUserInitializer {
        }
    }

    private void createMainUserIfNeeded(TimingsTraceAndSlog t) {
    private void createMainUserIfNeeded(TimingsTraceAndSlog t, @UserIdInt int mainUserId) {
        // Always tracing as it used to be done by the caller
        t.traceBegin("createMainUserIfNeeded");
        try {
            int mainUserId = mUms.getMainUserId();
            if (mainUserId != UserHandle.USER_NULL) {
                if (DEBUG) {
                    Slogf.d(TAG, "createMainUserIfNeeded(): found MainUser (userId=%d)",
@@ -197,6 +204,23 @@ public final class HsumBootUserInitializer {
        }
    }

    private void demoteMainUserIfNeeded(TimingsTraceAndSlog t, @UserIdInt int mainUserId) {
        if (mainUserId == UserHandle.USER_NULL) {
            if (DEBUG) {
                Slogf.d(TAG, "demoteMainUserIfNeeded(): didn't find MainUser");
            }
            return;
        }
        t.traceBegin("demoteMainUserIfNeeded");
        try {
            Slogf.i(TAG, "Demoting main user (%d)", mainUserId);
            if (!mUms.demoteMainUser()) {
                Slogf.wtf(TAG, "Failed to demote main user");
            }
        } finally {
            t.traceEnd();
        }
    }
    private void createAdminUserIfNeeded(TimingsTraceAndSlog t) {
        t.traceBegin("createAdminUserIfNeeded");
        try {
@@ -230,7 +254,7 @@ public final class HsumBootUserInitializer {
                    UserManager.USER_TYPE_FULL_SECONDARY,
                    flags,
                    /* parentId= */ UserHandle.USER_NULL,
                    /* preCreated= */ false,
                    /* preCreate= */ false,
                    /* disallowedPackages= */ null,
                    /* token= */ null);
            Slogf.i(TAG, "Successfully created %s, userId=%d", logName, newInitialUser.id);
+4 −1
Original line number Diff line number Diff line
@@ -104,6 +104,8 @@ public class UserJourneyLogger {
            FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__REVOKE_ADMIN;
    public static final int USER_JOURNEY_USER_LIFECYCLE =
            FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__USER_LIFECYCLE;
    public static final int USER_JOURNEY_DEMOTE_MAIN_USER =
            FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__JOURNEY__DEMOTE_MAIN_USER;

    @IntDef(prefix = {"USER_JOURNEY"}, value = {
            USER_JOURNEY_UNKNOWN,
@@ -115,7 +117,8 @@ public class UserJourneyLogger {
            USER_JOURNEY_USER_REMOVE,
            USER_JOURNEY_GRANT_ADMIN,
            USER_JOURNEY_REVOKE_ADMIN,
            USER_JOURNEY_USER_LIFECYCLE
            USER_JOURNEY_USER_LIFECYCLE,
            USER_JOURNEY_DEMOTE_MAIN_USER
    })
    public @interface UserJourney {
    }
+57 −17
Original line number Diff line number Diff line
@@ -43,6 +43,7 @@ import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_UNSPECIFIED;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_ALREADY_AN_ADMIN;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_USER_IS_NOT_AN_ADMIN;
import static com.android.server.pm.UserJourneyLogger.ERROR_CODE_INVALID_USER_TYPE;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_DEMOTE_MAIN_USER;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_GRANT_ADMIN;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_REVOKE_ADMIN;
import static com.android.server.pm.UserJourneyLogger.USER_JOURNEY_USER_CREATE;
@@ -587,7 +588,7 @@ public class UserManagerService extends IUserManager.Stub {
    @GuardedBy("mUserLifecycleListeners")
    private final ArrayList<UserLifecycleListener> mUserLifecycleListeners = new ArrayList<>();

    private final UserJourneyLogger mUserJourneyLogger = new UserJourneyLogger();
    private final UserJourneyLogger mUserJourneyLogger;

    private final LockPatternUtils mLockPatternUtils;

@@ -1065,7 +1066,7 @@ public class UserManagerService extends IUserManager.Stub {
    // TODO(b/28848102) Add support for test dependencies injection
    @VisibleForTesting
    UserManagerService(Context context) {
        this(context, /* pm= */ null, /* userDataPreparer= */ null,
        this(context, /* pm= */ null, /* userDataPreparer= */ null, new UserJourneyLogger(),
                /* packagesLock= */ new Object(), context.getCacheDir(), /* users= */ null);
    }

@@ -1076,16 +1077,17 @@ public class UserManagerService extends IUserManager.Stub {
     */
    UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
            Object packagesLock) {
        this(context, pm, userDataPreparer, packagesLock, Environment.getDataDirectory(),
                /* users= */ null);
        this(context, pm, userDataPreparer, new UserJourneyLogger(), packagesLock,
                Environment.getDataDirectory(), /* users= */ null);
    }

    @VisibleForTesting
    UserManagerService(Context context, PackageManagerService pm,
            UserDataPreparer userDataPreparer, Object packagesLock, File dataDir,
    UserManagerService(Context context, PackageManagerService pm, UserDataPreparer userDataPreparer,
            UserJourneyLogger userJourneyLogger,  Object packagesLock, File dataDir,
            SparseArray<UserData> users) {
        mContext = context;
        mPm = pm;
        mUserJourneyLogger = userJourneyLogger;
        mPackagesLock = packagesLock;
        mUsers = users != null ? users : new SparseArray<>();
        mHandler = new MainHandler();
@@ -1358,17 +1360,24 @@ public class UserManagerService extends IUserManager.Stub {

    private @CanBeNULL @UserIdInt int getMainUserIdUnchecked() {
        synchronized (mUsersLock) {
            final int userSize = mUsers.size();
            var mainUser = getMainUserLU();
            return mainUser == null ? UserHandle.USER_NULL : mainUser.id;
        }
    }

    @GuardedBy("mUsersLock")
    private @Nullable UserInfo getMainUserLU() {
        int userSize = mUsers.size();
        for (int i = 0; i < userSize; i++) {
                final UserInfo user = mUsers.valueAt(i).info;
            UserInfo user = mUsers.valueAt(i).info;
            if (user.isMain() && !mRemovingUserIds.get(user.id)) {
                    return user.id;
                }
                return user;
            }
        }
        return UserHandle.USER_NULL;
        return null;
    }


    private @CanBeNULL @UserIdInt int getPrivateProfileUserId() {
        synchronized (mUsersLock) {
            for (int userId : getUserIds()) {
@@ -5347,6 +5356,7 @@ public class UserManagerService extends IUserManager.Stub {

        initDefaultGuestRestrictions();

        Slogf.i(LOG_TAG, "Creating system user: %s", userData);
        writeUserLP(userData);
        writeUserListLP();
    }
@@ -6155,11 +6165,15 @@ public class UserManagerService extends IUserManager.Stub {
                                USER_OPERATION_ERROR_UNKNOWN);
                    }
                }
                if (isMainUser && getMainUserIdUnchecked() != UserHandle.USER_NULL) {
                if (isMainUser) {
                    int mainUserId = getMainUserIdUnchecked();
                    if (mainUserId != UserHandle.USER_NULL) {
                        throwCheckedUserOperationException(
                            "Cannot add user with FLAG_MAIN as main user already exists.",
                                "Cannot add user with FLAG_MAIN as main user already exists (id="
                                        + mainUserId + ").",
                                UserManager.USER_OPERATION_ERROR_MAX_USERS);
                    }
                }
                if (!canAddMoreUsersOfType(userTypeDetails)) {
                    if (isUserLimitReachedForLogging()) {
                        throwCheckedUserOperationException(
@@ -8826,6 +8840,32 @@ public class UserManagerService extends IUserManager.Stub {
        return defaultValue;
    }

    boolean demoteMainUser() {
        if (!android.multiuser.Flags.demoteMainUser()) {
            Slog.d(LOG_TAG, "demoteMainUser(): ignoring because flag is disabled");
            return false;
        }

        synchronized (mUsersLock) {
            var mainUser = getMainUserLU();
            if (mainUser == null) {
                Slog.e(LOG_TAG, "demoteMainUser(): no main user");
                return false;
            }
            if (mainUser.id == UserHandle.USER_SYSTEM) {
                Slog.e(LOG_TAG, "demoteMainUser(): cannot demote system user");
                return false;
            }

            Slogf.i(LOG_TAG, "Demoting main user (%s)", mainUser);
            mUserJourneyLogger.logUserJourneyBegin(mainUser.id, USER_JOURNEY_DEMOTE_MAIN_USER);
            var userData = getUserDataLU(mainUser.id);
            userData.info.flags ^= UserInfo.FLAG_MAIN;
            writeUserLP(userData);
            return true;
        }
    }

    /**
     * Returns true if {@link com.android.internal.R.bool#config_canSwitchToHeadlessSystemUser}
     * is true. If allowed, headless system user can run in the foreground even though
+37 −2
Original line number Diff line number Diff line
@@ -21,6 +21,7 @@ import static android.os.UserHandle.USER_SYSTEM;

import static com.android.server.pm.HsumBootUserInitializerTest.ExpectedResult.ADMIN_USER_CREATED;
import static com.android.server.pm.HsumBootUserInitializerTest.ExpectedResult.MAIN_USER_CREATED;
import static com.android.server.pm.HsumBootUserInitializerTest.ExpectedResult.MAIN_USER_DEMOTED;
import static com.android.server.pm.HsumBootUserInitializerTest.ExpectedResult.NO_USER_CREATED;
import static com.android.server.pm.HsumBootUserInitializerTest.InitialUsers.SYSTEM_AND_MAIN;
import static com.android.server.pm.HsumBootUserInitializerTest.InitialUsers.SYSTEM_AND_NON_MAIN;
@@ -110,11 +111,11 @@ public final class HsumBootUserInitializerTest {
        return Arrays.asList(new Object[][] {
                // shouldAlwaysHaveMainUser false, shouldCreateInitialUser false
                { false, false, SYSTEM_ONLY, NO_USER_CREATED },
                { false, false, SYSTEM_AND_MAIN, NO_USER_CREATED },
                { false, false, SYSTEM_AND_MAIN, MAIN_USER_DEMOTED },
                { false, false, SYSTEM_AND_NON_MAIN, NO_USER_CREATED },
                // shouldAlwaysHaveMainUser false, shouldCreateInitialUser true
                { false, true, SYSTEM_ONLY, ADMIN_USER_CREATED},
                { false, true, SYSTEM_AND_MAIN, NO_USER_CREATED },
                { false, true, SYSTEM_AND_MAIN, MAIN_USER_DEMOTED },
                { false, true, SYSTEM_AND_NON_MAIN, NO_USER_CREATED},
                // shouldAlwaysHaveMainUser true, shouldCreateInitialUser false
                { true, false, SYSTEM_ONLY, MAIN_USER_CREATED},
@@ -163,6 +164,9 @@ public final class HsumBootUserInitializerTest {
            case MAIN_USER_CREATED:
                mockCreateNewUser(MAIN_USER_ID);
                break;
            case MAIN_USER_DEMOTED:
                mockGetMainUserId(MAIN_USER_ID);
                break;
            default:
                // don't need to mock it
        }
@@ -192,13 +196,20 @@ public final class HsumBootUserInitializerTest {
            case ADMIN_USER_CREATED:
                expectAdminUserCreated();
                expectSetBootUserId(NON_SYSTEM_USER_ID);
                expectMainUserNotDemoted();
                break;
            case MAIN_USER_CREATED:
                expectMainUserCreated();
                expectSetBootUserId(MAIN_USER_ID);
                expectMainUserNotDemoted();
                break;
            case NO_USER_CREATED:
                expectNoUserCreated();
                expectMainUserNotDemoted();
                break;
            case MAIN_USER_DEMOTED:
                expectNoUserCreated();
                expectMainUserDemoted();
                break;
        }
    }
@@ -216,12 +227,14 @@ public final class HsumBootUserInitializerTest {
            // When the flag is disabled, it shouldn't trigger the "create admin user" workflow
            case ADMIN_USER_CREATED:
            case NO_USER_CREATED:
            case MAIN_USER_DEMOTED:
                expectNoUserCreated();
                break;
            case MAIN_USER_CREATED:
                expectMainUserCreated();
                break;
        }
        expectMainUserNotDemoted();
    }

    private HsumBootUserInitializer createHsumBootUserInitializer(
@@ -266,6 +279,27 @@ public final class HsumBootUserInitializerTest {
        expectSetBootUserIdNeverCalled();
    }


    private void expectMainUserDemoted() {
        try {
            verify(mMockUms).demoteMainUser();
        } catch (Exception e) {
            String msg = "should have demoted main user";
            Log.e(TAG, msg, e);
            expect.withMessage(msg).fail();
        }
    }

    private void expectMainUserNotDemoted() {
        try {
            verify(mMockUms, never()).demoteMainUser();
        } catch (Exception e) {
            String msg = "should not have demoted main user";
            Log.e(TAG, msg, e);
            expect.withMessage(msg).fail();
        }
    }

    private void expectSetBootUserId(@UserIdInt int userId) {
        try {
            verify(mMockUms).setBootUserIdUnchecked(userId);
@@ -316,6 +350,7 @@ public final class HsumBootUserInitializerTest {
    public enum ExpectedResult {
        NO_USER_CREATED,
        MAIN_USER_CREATED,
        MAIN_USER_DEMOTED,
        ADMIN_USER_CREATED
    }
}
Loading