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

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

Added unit tests for HsumBootUserInitializer constructor.

That class takes 2 boolean arguments, which in turn are defined by
a combination of multiple components (framework resources,
system properties, build type, and perhaps flags in the future).

This CL adds unit tests for the 2 methods that define those
arguments, and also move them to HsumBootUserInitializer itself
(previous they were defined on SystemServer, which is the class that
instantes HsumBootUserInitializer).

Bug: 402486365
Test: atest FrameworksMockingServicesTests --test-filter='.*HsumBootUserInitializer.*OnBootTest.*'
Flag: EXEMPT refactoring

Change-Id: I866d20b9356976e922168e41968a1a8dd56189f5
parent 97d8da20
Loading
Loading
Loading
Loading
+54 −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,25 @@ 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.println(SystemProperties.get(SYSPROP_DESIGNATE_MAIN_USER, "N/A"));

        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 +413,25 @@ 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 (!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);
+123 −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.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.os.Build;
import android.os.SystemProperties;
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();

    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()));
    }

    // 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 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);
    }
}
+163 −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.designateMainUserOnBoot;

import android.util.Log;

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

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

public final class HsumBootUserInitializerDesignateMainUserOnBootTest
        extends AbstractHsumBootUserInitializerConstructorHelpersTestCase {

    private final boolean mIsDebuggable;
    private final boolean mSysPropDesignateMainUser;
    private final boolean mConfigDesignateMainUser;
    private final boolean mConfigIsMainUserPermanentAdmin;
    private final boolean mResult;

    /** Useless javadoc to make checkstyle happy... */
    @Parameters(name =
            "{index}: dbgBuild={0},sysprop={1},cfgDesignateMU={2},cfgIsMUPermAdm={3},result={4}")
    public static Collection<Object[]> junitParametersPassedToConstructor() {
        return Arrays.asList(new Object[][] {
                // Note: entries below are broken in 3 lines to make them easier to read / maintain:
                // - build type and emulation
                // - input (configs)
                // - expected output

                // User build, sysprop not set
                {
                        DEBUGGABLE(false), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(false),
                        RESULT(false)
                },
                {
                        DEBUGGABLE(false), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(true),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(false), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(false),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(false), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(true),
                        RESULT(true)
                },

                // User build, sysprop set - everything should be the same as above
                {
                        DEBUGGABLE(false), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(false),
                        RESULT(false)
                },
                {
                        DEBUGGABLE(false), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(true),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(false), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(false),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(false), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(true),
                        RESULT(true)
                },

                // Debuggable build - result should be value of property (false)
                {
                        DEBUGGABLE(true), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(false),
                        RESULT(false)
                },
                {
                        DEBUGGABLE(true), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(true),
                        RESULT(false)
                },
                {
                        DEBUGGABLE(true), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(false),
                        RESULT(false)
                },
                {
                        DEBUGGABLE(true), SYSPROP(false),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(true),
                        RESULT(false)
                },

                // Debuggable build - result should be value of property (true)
                {
                        DEBUGGABLE(true), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(false),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(true), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(false), CFG_IS_PERM_ADM(true),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(true), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(false),
                        RESULT(true)
                },
                {
                        DEBUGGABLE(true), SYSPROP(true),
                        CFG_DESIGNATE_MAIN(true), CFG_IS_PERM_ADM(true),
                        RESULT(true)
                },
        });
    }

    public HsumBootUserInitializerDesignateMainUserOnBootTest(boolean isDebuggable,
            boolean sysPropDesignateMainUser, boolean configDesignateMainUser,
            boolean configIsMainUserPermanentAdmin, boolean result) {
        mConfigDesignateMainUser = configDesignateMainUser;
        mConfigIsMainUserPermanentAdmin = configIsMainUserPermanentAdmin;
        mSysPropDesignateMainUser = sysPropDesignateMainUser;
        mIsDebuggable = isDebuggable;
        mResult = result;
        Log.v(mTag, "Constructor: isDebuggable=" + isDebuggable
                + ", sysPropDesignateMainUser=" + sysPropDesignateMainUser
                + ", configDesignateMainUser=" + configDesignateMainUser
                + ", configIsMainUserPermanentAdmin=" + configIsMainUserPermanentAdmin
                + ", result=" + result);
    }

    @Test
    public void testDesignateMainUserOnBoot() {
        mockSysPropDesignateMainUser(mSysPropDesignateMainUser);
        mockIsDebuggable(mIsDebuggable);
        mockConfigDesignateMainUser(mConfigDesignateMainUser);
        mockConfigIsMainUserPermanentAdmin(mConfigIsMainUserPermanentAdmin);

        boolean result = designateMainUserOnBoot(mMockContext);

        expect.withMessage("designateMainUserOnBoot()").that(result).isEqualTo(mResult);
    }
}
Loading