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

Commit 5fab20bc authored by Treehugger Robot's avatar Treehugger Robot Committed by Android (Google) Code Review
Browse files

Merge changes I866d20b9,I866d20b9 into main

* changes:
  Changed behavior of HsumBootUserInitializer to "ramp-up" a config.
  Added unit tests for HsumBootUserInitializer constructor.
parents 445981d2 de781d19
Loading
Loading
Loading
Loading
+72 −2
Original line number Diff line number Diff line
@@ -18,22 +18,27 @@ package com.android.server.pm;
import android.annotation.Nullable;
import android.annotation.UserIdInt;
import android.content.ContentResolver;
import android.content.Context;
import android.content.pm.PackageManager;
import android.content.pm.UserInfo;
import android.database.ContentObserver;
import android.multiuser.Flags;
import android.os.Build;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemProperties;
import android.os.UserHandle;
import android.os.UserManager;
import android.provider.Settings;
import android.util.Log;

import com.android.internal.R;
import com.android.internal.annotations.VisibleForTesting;
import com.android.server.am.ActivityManagerService;
import com.android.server.utils.Slogf;
import com.android.server.utils.TimingsTraceAndSlog;

import java.io.PrintWriter;
import java.util.Arrays;

/**
@@ -49,6 +54,13 @@ public final class HsumBootUserInitializer {
    // calls grows too much, we should change it to false.
    private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG);

    /**
     * Property used to override (for development purposes, on debuggable builds) the resource
     * configs used by {@link #designateMainUserOnBoot(Context)}
     */
    @VisibleForTesting
    static final String SYSPROP_DESIGNATE_MAIN_USER = "fw.designate_main_user_on_boot";

    private final UserManagerService mUms;
    private final ActivityManagerService mAms;
    private final PackageManagerService mPms;
@@ -78,13 +90,13 @@ public final class HsumBootUserInitializer {
    /** Static factory method for creating a {@link HsumBootUserInitializer} instance. */
    public static @Nullable HsumBootUserInitializer createInstance(UserManagerService ums,
            ActivityManagerService ams, PackageManagerService pms, ContentResolver contentResolver,
            boolean shouldDesignateMainUser, boolean shouldCreateInitialUser) {
            Context context) {

        if (!UserManager.isHeadlessSystemUserMode()) {
            return null;
        }
        return new HsumBootUserInitializer(ums, ams, pms, contentResolver,
                shouldDesignateMainUser, shouldCreateInitialUser);
                designateMainUserOnBoot(context), createInitialUserOnBoot(context));
    }

    @VisibleForTesting
@@ -319,6 +331,27 @@ public final class HsumBootUserInitializer {
        }
    }

    /** Called by {@code SystemServer.dump()} */
    public static void dump(PrintWriter pw, Context context) {
        var res = context.getResources();

        pw.print("Designate main user on boot: ");
        pw.println(designateMainUserOnBoot(context));
        pw.print("  config_designateMainUser: ");
        pw.print(res.getBoolean(R.bool.config_designateMainUser));
        pw.print(" config_isMainUserPermanentAdmin: ");
        pw.print(res.getBoolean(R.bool.config_isMainUserPermanentAdmin));
        pw.print(" " + SYSPROP_DESIGNATE_MAIN_USER + ": ");
        pw.print(SystemProperties.get(SYSPROP_DESIGNATE_MAIN_USER, "N/A"));
        pw.print(" flag_create_initial_user: ");
        pw.println(Flags.createInitialUser());

        pw.print("Create initial user on boot: ");
        pw.println(createInitialUserOnBoot(context));
        pw.print("  config_createInitialUser: ");
        pw.println(res.getBoolean(R.bool.config_createInitialUser));
    }

    private void observeDeviceProvisioning() {
        if (isDeviceProvisioned()) {
            return;
@@ -382,4 +415,41 @@ public final class HsumBootUserInitializer {
            Slogf.wtf(TAG, "Failed to start user %d in foreground", bootUserId);
        }
    }

    // Methods below are used to create the parameters used in the factory method and used to be
    // defined on SystemServer, but were moved here so they can be unit tested, as SystemServer is
    // not included on FrameworksMockingServicesTests (and besides, it makes more sense to define
    // the logic here than on SystemServer itself).

    @VisibleForTesting
    static boolean designateMainUserOnBoot(Context context) {
        var res = context.getResources();
        boolean defaultValue = res.getBoolean(R.bool.config_designateMainUser)
                || res.getBoolean(R.bool.config_isMainUserPermanentAdmin);
        if (DEBUG) {
            Slogf.d(TAG, "designateMainUserOnBoot(): defaultValue=%b (because "
                    + "config_designateMainUser=%b and config_isMainUserPermanentAdmin=%b)",
                    defaultValue,
                    res.getBoolean(R.bool.config_designateMainUser),
                    res.getBoolean(R.bool.config_isMainUserPermanentAdmin));
        }
        // Ignore devices that should not create a main user while flag is not ramped up yet
        // TODO(b/402486365): remove this workaround after flag is ramped up
        if (!Flags.createInitialUser() && res.getBoolean(R.bool.config_createInitialUser)
                && !defaultValue) {
            Slogf.i(TAG, "designateMainUserOnBoot(): overriding defaultValue to true (because "
                    + "Flags.createInitialUser()=%b and config_createInitialUser=%b)",
                    Flags.createInitialUser(), res.getBoolean(R.bool.config_createInitialUser));
            defaultValue = true;
        }
        if (!Build.isDebuggable()) {
            return defaultValue;
        }
        return SystemProperties.getBoolean(SYSPROP_DESIGNATE_MAIN_USER, defaultValue);
    }

    @VisibleForTesting
    static boolean createInitialUserOnBoot(Context context) {
        return context.getResources().getBoolean(R.bool.config_createInitialUser);
    }
}
+2 −35
Original line number Diff line number Diff line
@@ -557,13 +557,6 @@ public final class SystemServer implements Dumpable {
    private static final String SYSPROP_FDTRACK_INTERVAL =
            "persist.sys.debug.fdtrack_interval";

    /**
     * Property used to override (for development purposes, on debuggable builds) the resource
     * configs used by {@link #designateMainUserOnBoot()}
     */
    private static final String SYSPROP_DESIGNATE_MAIN_USER = "fw.designate_main_user_on_boot";


    private static int getMaxFd() {
        FileDescriptor fd = null;
        try {
@@ -734,32 +727,7 @@ public final class SystemServer implements Dumpable {
        pw.print("Runtime start-elapsed time: ");
        TimeUtils.formatDuration(mRuntimeStartElapsedTime, pw); pw.println();

        var res = mSystemContext.getResources();
        pw.print("Designate main user on boot: ");
        pw.println(designateMainUserOnBoot());
        pw.print("  config_designateMainUser: ");
        pw.print(res.getBoolean(R.bool.config_designateMainUser));
        pw.print(" config_isMainUserPermanentAdmin: ");
        pw.print(res.getBoolean(R.bool.config_isMainUserPermanentAdmin));
        pw.print(" " + SYSPROP_DESIGNATE_MAIN_USER + ": ");
        pw.println(SystemProperties.get(SYSPROP_DESIGNATE_MAIN_USER, "N/A"));

        pw.print("Create initial user on boot: ");
        pw.println(createInitialUserOnBoot());
    }

    private boolean designateMainUserOnBoot() {
        var res = mSystemContext.getResources();
        boolean defaultValue = res.getBoolean(R.bool.config_designateMainUser)
                || res.getBoolean(R.bool.config_isMainUserPermanentAdmin);
        if (!Build.isDebuggable()) {
            return defaultValue;
        }
        return SystemProperties.getBoolean(SYSPROP_DESIGNATE_MAIN_USER, defaultValue);
    }

    private boolean createInitialUserOnBoot() {
        return mSystemContext.getResources().getBoolean(R.bool.config_createInitialUser);
        HsumBootUserInitializer.dump(pw, mSystemContext);
    }

    /**
@@ -3087,8 +3055,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(mUserManagerService, mActivityManagerService,
                        mPackageManagerService, mContentResolver,
                        designateMainUserOnBoot(), createInitialUserOnBoot());
                        mPackageManagerService, mContentResolver, mSystemContext);
        if (hsumBootUserInitializer != null) {
            t.traceBegin("HsumBootUserInitializer.init");
            hsumBootUserInitializer.init(t);
+151 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.pm;

import static android.multiuser.Flags.FLAG_CREATE_INITIAL_USER;

import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
import static com.android.server.pm.HsumBootUserInitializer.SYSPROP_DESIGNATE_MAIN_USER;

import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.when;

import android.content.Context;
import android.content.res.Resources;
import android.multiuser.Flags;
import android.os.Build;
import android.os.SystemProperties;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.Log;

import com.android.internal.R;
import com.android.modules.utils.testing.ExtendedMockitoRule;

import com.google.common.truth.Expect;

import org.junit.Before;
import org.junit.Rule;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.mockito.Mock;

// Base class for testing static methods used to create the constructor parameters
@RunWith(Parameterized.class)
abstract class AbstractHsumBootUserInitializerConstructorHelpersTestCase {

    @Rule
    public final Expect expect = Expect.create();

    @Rule
    public final ExtendedMockitoRule extendedMockito = new ExtendedMockitoRule.Builder(this)
            .mockStatic(Build.class)
            .mockStatic(SystemProperties.class)
            .build();

    @Rule
    public final SetFlagsRule setFlagsRule =
            new SetFlagsRule(SetFlagsRule.DefaultInitValueType.DEVICE_DEFAULT);

    protected final String mTag = getClass().getSimpleName();

    @Mock
    protected Context mMockContext;

    @Mock
    private Resources mMockResources;

    @Before
    public final void setCommonFixtures() {
        when(mMockContext.getResources()).thenReturn(mMockResources);
    }

    protected final void mockConfigDesignateMainUser(boolean value) {
        Log.v(mTag, "mockConfigDesignateMainUser(" + value + ")");
        when(mMockResources.getBoolean(R.bool.config_designateMainUser)).thenReturn(value);
    }

    protected final void mockConfigIsMainUserPermanentAdmin(boolean value) {
        Log.v(mTag, "mockConfigIsMainUserPermanentAdmin(" + value + ")");
        when(mMockResources.getBoolean(R.bool.config_isMainUserPermanentAdmin)).thenReturn(value);
    }

    protected final void mockConfigCreateInitialUser(boolean value) {
        Log.v(mTag, "mockConfigCreateInitialUser(" + value + ")");
        when(mMockResources.getBoolean(R.bool.config_createInitialUser)).thenReturn(value);
    }

    protected final void mockIsDebuggable(boolean value) {
        Log.v(mTag, "mockIsDebuggable(" + value + ")");
        doReturn(value).when(Build::isDebuggable);
    }

    protected final void mockSysPropDesignateMainUser(boolean value) {
        Log.v(mTag, "mockSysPropDesignateMainUser(" + value + ")");
        doReturn(value).when(
                () -> SystemProperties.getBoolean(eq(SYSPROP_DESIGNATE_MAIN_USER), anyBoolean()));
    }

    @SuppressWarnings("deprecation") // TODO(b/341129262): SetFlagsRule methods are deprecated
    protected final void setCreateInitialUserFlag(boolean value) {
        boolean before =  Flags.createInitialUser();
        if (before == value) {
            Log.v(mTag, "setCreateInitialUserFlag(): already " + value);
            return;
        }
        Log.v(mTag, "setCreateInitialUserFlag(): changing from " + before + " to " + value);
        if (value) {
            setFlagsRule.enableFlags(FLAG_CREATE_INITIAL_USER);
        } else {
            setFlagsRule.disableFlags(FLAG_CREATE_INITIAL_USER);
        }
    }


    // Helper methods to make values parameterized values easier to read
    // NOTE: not really "Generated code", but that's the only why to calm down checkstyle, otherwise
    // it will complain they should be all upper case
    // CHECKSTYLE:OFF Generated code

    protected static boolean DEBUGGABLE(boolean value) {
        return value;
    }

    protected static boolean SYSPROP(boolean value) {
        return value;
    }

    protected static boolean FLAG(boolean value) {
        return value;
    }

    protected static boolean CFG_DESIGNATE_MAIN(boolean value) {
        return value;
    }

    protected static boolean CFG_IS_PERM_ADM(boolean value) {
        return value;
    }

    protected static boolean CFG_CREATE_INITIAL(boolean value) {
        return value;
    }

    protected static boolean RESULT(boolean value) {
        return value;
    }
    // CHECKSTYLE:ON Generated code
}
+61 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2025 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.android.server.pm;

import static com.android.server.pm.HsumBootUserInitializer.createInitialUserOnBoot;

import android.util.Log;

import org.junit.Test;
import org.junit.runners.Parameterized.Parameters;

import java.util.Arrays;
import java.util.Collection;

// TODO(b/402486365): using a parameterized  test seems to be an overkill for now, but it will
// make it easier to add more parameters (like a flag)
public final class HsumBootUserInitializerCreateInitialUserOnBootTest
        extends AbstractHsumBootUserInitializerConstructorHelpersTestCase {

    private final boolean mConfigCreateInitialUser;
    private final boolean mResult;

    /** Useless javadoc to make checkstyle happy... */
    @Parameters(name = "{index}: cfgCreateIU={0},result={1}")
    public static Collection<Object[]> junitParametersPassedToConstructor() {
        return Arrays.asList(new Object[][] {
            { CFG_CREATE_INITIAL(false), RESULT(false) },
            { CFG_CREATE_INITIAL(true), RESULT(true) }
        });
    }

    public HsumBootUserInitializerCreateInitialUserOnBootTest(boolean configCreateInitialUser,
            boolean result) {
        mConfigCreateInitialUser = configCreateInitialUser;
        mResult = result;
        Log.v(mTag, "Constructor: configCreateInitialUser=" + configCreateInitialUser
                + ", result=" + result);
    }

    @Test
    public void testCreateInitialUserOnBoot() {
        mockConfigCreateInitialUser(mConfigCreateInitialUser);

        boolean result = createInitialUserOnBoot(mMockContext);

        expect.withMessage("createInitialUserOnBoot()").that(result).isEqualTo(mResult);
    }
}
+425 −0

File added.

Preview size limit exceeded, changes collapsed.

Loading