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

Commit c3249a87 authored by Anna Bauza's avatar Anna Bauza
Browse files

Add Android Multiuser Atom which will contain number of supported users.

Metric [Design Doc approved](https://eldar.corp.google.com/assessments/860295929/revisions/1?jsmode=du&mods=eldarui_search#sections/999001).

Bug: 240582354
Test: statsd_testdrive 10160
Change-Id: I3cfbd2f79e71df6b2952d73d5a2bd20268708129
parent 71dcdd66
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -74,6 +74,7 @@ interface IUserManager {
    String getUserAccount(int userId);
    void setUserAccount(int userId, String accountName);
    long getUserCreationTime(int userId);
    boolean isUserSwitcherEnabled(int mUserId);
    boolean isRestricted(int userId);
    boolean canHaveRestrictedProfile(int userId);
    int getUserSerialNumber(int userId);
+7 −17
Original line number Diff line number Diff line
@@ -5220,23 +5220,13 @@ public class UserManager {
    })
    @UserHandleAware
    public boolean isUserSwitcherEnabled(boolean showEvenIfNotActionable) {
        if (!supportsMultipleUsers()) {
            return false;
        }
        if (hasUserRestrictionForUser(DISALLOW_USER_SWITCH, mUserId)) {
            return false;
        }
        // If Demo Mode is on, don't show user switcher
        if (isDeviceInDemoMode(mContext)) {

        try {
            if (!mService.isUserSwitcherEnabled(mUserId)) {
                return false;
            }
        // Check the Settings.Global.USER_SWITCHER_ENABLED that the user can toggle on/off.
        final boolean userSwitcherSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.USER_SWITCHER_ENABLED,
                Resources.getSystem().getBoolean(R.bool.config_showUserSwitcherByDefault) ? 1 : 0)
                != 0;
        if (!userSwitcherSettingOn) {
            return false;
        } catch (RemoteException re) {
            throw re.rethrowFromSystemServer();
        }

        // The feature is enabled. But is it worth showing?
+60 −29
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server.pm;

import static android.content.Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS;
import static android.content.Intent.FLAG_ACTIVITY_NEW_TASK;
import static android.os.UserManager.DISALLOW_USER_SWITCH;

import android.Manifest;
import android.accounts.Account;
@@ -1802,6 +1803,19 @@ public class UserManagerService extends IUserManager.Stub {
        }
    }

    @Override
    public boolean isUserSwitcherEnabled(@UserIdInt int mUserId) {
        boolean multiUserSettingOn = Settings.Global.getInt(mContext.getContentResolver(),
                Settings.Global.USER_SWITCHER_ENABLED,
                Resources.getSystem().getBoolean(com.android.internal
                        .R.bool.config_showUserSwitcherByDefault) ? 1 : 0) != 0;

        return UserManager.supportsMultipleUsers()
                && !hasUserRestriction(DISALLOW_USER_SWITCH, mUserId)
                && !UserManager.isDeviceInDemoMode(mContext)
                && multiUserSettingOn;
    }

    @Override
    public boolean isRestricted(@UserIdInt int userId) {
        if (userId != UserHandle.getCallingUserId()) {
@@ -2195,7 +2209,6 @@ public class UserManagerService extends IUserManager.Stub {
                    originatingUserId, local);
            localChanged = updateLocalRestrictionsForTargetUsersLR(originatingUserId, local,
                    updatedLocalTargetUserIds);

            if (isDeviceOwner) {
                // Remember the global restriction owner userId to be able to make a distinction
                // in getUserRestrictionSource on who set local policies.
@@ -4672,14 +4685,16 @@ public class UserManagerService extends IUserManager.Stub {
                null, // use default PullAtomMetadata values
                BackgroundThread.getExecutor(),
                this::onPullAtom);
        statsManager.setPullAtomCallback(
                FrameworkStatsLog.MULTI_USER_INFO,
                null, // use default PullAtomMetadata values
                BackgroundThread.getExecutor(),
                this::onPullAtom);
    }

    /** Writes a UserInfo pulled atom for each user on the device. */
    private int onPullAtom(int atomTag, List<StatsEvent> data) {
        if (atomTag != FrameworkStatsLog.USER_INFO) {
            Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
            return android.app.StatsManager.PULL_SKIP;
        }
        if (atomTag == FrameworkStatsLog.USER_INFO) {
            final List<UserInfo> users = getUsersInternal(true, true, true);
            final int size = users.size();
            if (size > 1) {
@@ -4708,6 +4723,22 @@ public class UserManagerService extends IUserManager.Stub {
                    ));
                }
            }
        } else if (atomTag == FrameworkStatsLog.MULTI_USER_INFO) {
            if (UserManager.getMaxSupportedUsers() > 1) {
                int deviceOwnerUserId = UserHandle.USER_NULL;

                synchronized (mRestrictionsLock) {
                    deviceOwnerUserId = mDeviceOwnerUserId;
                }

                data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.MULTI_USER_INFO,
                        UserManager.getMaxSupportedUsers(),
                        isUserSwitcherEnabled(deviceOwnerUserId)));
            }
        } else {
            Slogf.e(LOG_TAG, "Unexpected atom tag: %d", atomTag);
            return android.app.StatsManager.PULL_SKIP;
        }
        return android.app.StatsManager.PULL_SUCCESS;
    }

+170 −29
Original line number Diff line number Diff line
@@ -16,53 +16,79 @@

package com.android.server.pm;

import static android.os.UserManager.DISALLOW_USER_SWITCH;

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

import android.app.ActivityManager;
import android.app.PropertyInvalidatedCache;
import android.content.Context;
import android.content.pm.UserInfo;
import android.os.Bundle;
import android.os.FileUtils;
import android.os.Looper;
import android.os.Parcelable;
import android.os.UserHandle;
import android.os.UserManager;
import android.platform.test.annotations.Postsubmit;
import android.support.test.uiautomator.UiDevice;
import android.test.AndroidTestCase;
import android.text.TextUtils;
import android.util.AtomicFile;

import androidx.test.InstrumentationRegistry;
import androidx.test.filters.SmallTest;
import androidx.test.runner.AndroidJUnit4;

import com.android.server.LocalServices;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;

import java.io.File;
import java.io.IOException;
import java.util.Arrays;

/** Test {@link UserManagerService} functionality. */
@Postsubmit
@SmallTest
public class UserManagerServiceTest extends AndroidTestCase {
@RunWith(AndroidJUnit4.class)
public class UserManagerServiceTest {
    private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["};
    private File restrictionsFile;
    private int tempUserId = UserHandle.USER_NULL;
    private final Context mContext = InstrumentationRegistry.getInstrumentation().getContext();
    private UserManagerService mUserManagerService;

    @Before
    public void setup() throws Exception {
        // Currently UserManagerService cannot be instantiated twice inside a VM without a cleanup
        // TODO: Remove once UMS supports proper dependency injection
        if (Looper.myLooper() == null) {
            Looper.prepare();
        }
        // Disable binder caches in this process.
        PropertyInvalidatedCache.disableForTestMode();

        LocalServices.removeServiceForTest(UserManagerInternal.class);
        mUserManagerService = new UserManagerService(InstrumentationRegistry.getContext());

    @Override
    protected void setUp() throws Exception {
        super.setUp();
        restrictionsFile = new File(mContext.getCacheDir(), "restrictions.xml");
        restrictionsFile.delete();
    }

    @Override
    protected void tearDown() throws Exception {
    @After
    public void teardown() throws Exception {
        restrictionsFile.delete();
        if (tempUserId != UserHandle.USER_NULL) {
            UserManager.get(mContext).removeUser(tempUserId);
        }
        super.tearDown();
    }

    @Test
    public void testWriteReadApplicationRestrictions() throws IOException {
        AtomicFile atomicFile = new AtomicFile(restrictionsFile);
        Bundle bundle = createBundle();
        UserManagerService.writeApplicationRestrictionsLAr(bundle, atomicFile);
        assertTrue(atomicFile.getBaseFile().exists());
        assertThat(atomicFile.getBaseFile().exists()).isTrue();
        String s = FileUtils.readTextFile(restrictionsFile, 10000, "");
        System.out.println("restrictionsFile: " + s);
        bundle = UserManagerService.readApplicationRestrictionsLAr(atomicFile);
@@ -70,22 +96,22 @@ public class UserManagerServiceTest extends AndroidTestCase {
        assertBundle(bundle);
    }

    @Test
    public void testAddUserWithAccount() {
        UserManager um = UserManager.get(mContext);
        UserInfo user = um.createUser("Test User", 0);
        assertNotNull(user);
        assertThat(user).isNotNull();
        tempUserId = user.id;
        String accountName = "Test Account";
        um.setUserAccount(tempUserId, accountName);
        assertEquals(accountName, um.getUserAccount(tempUserId));
        assertThat(um.getUserAccount(tempUserId)).isEqualTo(accountName);
    }

    @Test
    public void testUserSystemPackageWhitelist() throws Exception {
        String cmd = "cmd user report-system-user-package-whitelist-problems --critical-only";
        final String result = runShellCommand(cmd);
        if (!TextUtils.isEmpty(result)) {
            fail("Command '" + cmd + " reported errors:\n" + result);
        }
        assertThat(result).isEmpty();
    }

    private Bundle createBundle() {
@@ -114,26 +140,141 @@ public class UserManagerServiceTest extends AndroidTestCase {
    }

    private void assertBundle(Bundle bundle) {
        assertFalse(bundle.getBoolean("boolean_0"));
        assertTrue(bundle.getBoolean("boolean_1"));
        assertEquals(100, bundle.getInt("integer"));
        assertEquals("", bundle.getString("empty"));
        assertEquals("text", bundle.getString("string"));
        assertEquals(Arrays.asList(STRING_ARRAY), Arrays.asList(bundle.getStringArray("string[]")));
        assertThat(bundle.getBoolean("boolean_0")).isFalse();
        assertThat(bundle.getBoolean("boolean_1")).isTrue();
        assertThat(bundle.getInt("integer")).isEqualTo(100);
        assertThat(bundle.getString("empty")).isEqualTo("");
        assertThat(bundle.getString("string")).isEqualTo("text");
        assertThat(Arrays.asList(bundle.getStringArray("string[]")))
                .isEqualTo(Arrays.asList(STRING_ARRAY));
        Parcelable[] bundle_array = bundle.getParcelableArray("bundle_array");
        assertEquals(2, bundle_array.length);
        assertThat(bundle_array.length).isEqualTo(2);
        Bundle bundle1 = (Bundle) bundle_array[0];
        assertEquals("bundle_array_string", bundle1.getString("bundle_array_string"));
        assertNotNull(bundle1.getBundle("bundle_array_bundle"));
        assertThat(bundle1.getString("bundle_array_string"))
                .isEqualTo("bundle_array_string");
        assertThat(bundle1.getBundle("bundle_array_bundle")).isNotNull();
        Bundle bundle2 = (Bundle) bundle_array[1];
        assertEquals("bundle_array_string2", bundle2.getString("bundle_array_string2"));
        assertThat(bundle2.getString("bundle_array_string2"))
                .isEqualTo("bundle_array_string2");
        Bundle childBundle = bundle.getBundle("bundle");
        assertEquals("bundle_string", childBundle.getString("bundle_string"));
        assertEquals(1, childBundle.getInt("bundle_int"));
        assertThat(childBundle.getString("bundle_string"))
                .isEqualTo("bundle_string");
        assertThat(childBundle.getInt("bundle_int")).isEqualTo(1);
    }

    @Test
    public void assertHasUserRestriction() throws Exception {
        int userId = ActivityManager.getCurrentUser();

        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
        assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, userId)).isTrue();

        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
        assertThat(mUserManagerService.hasUserRestriction(DISALLOW_USER_SWITCH, userId)).isFalse();
    }

    @Test
    public void assertIsUserSwitcherEnabledOnMultiUserSettings() throws Exception {
        int userId = ActivityManager.getCurrentUser();
        resetUserSwitcherEnabled();

        setUserSwitch(false);
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();

        setUserSwitch(true);
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
    }

    @Test
    public void assertIsUserSwitcherEnabledOnMaxSupportedUsers()  throws Exception {
        int userId = ActivityManager.getCurrentUser();
        setMaxSupportedUsers(1);

        assertThat(UserManager.supportsMultipleUsers()).isFalse();
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();

        setMaxSupportedUsers(8);

        assertThat(UserManager.supportsMultipleUsers()).isTrue();
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
    }


    @Test
    public void assertIsUserSwitcherEnabledOnShowMultiuserUI()  throws Exception {
        int userId = ActivityManager.getCurrentUser();
        setShowMultiuserUI(false);

        assertThat(UserManager.supportsMultipleUsers()).isFalse();
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();

        setShowMultiuserUI(true);

        assertThat(UserManager.supportsMultipleUsers()).isTrue();
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
    }

    @Test
    public void assertIsUserSwitcherEnabledOnUserRestrictions() throws Exception {
        int userId = ActivityManager.getCurrentUser();
        resetUserSwitcherEnabled();

        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, true, userId);
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();

        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
    }

    @Test
    public void assertIsUserSwitcherEnabledOnDemoMode()  throws Exception {
        int userId = ActivityManager.getCurrentUser();
        resetUserSwitcherEnabled();

        setDeviceDemoMode(true);
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isFalse();

        setDeviceDemoMode(false);
        assertThat(mUserManagerService.isUserSwitcherEnabled(userId)).isTrue();
    }

    private void resetUserSwitcherEnabled() throws Exception {
        int userId = ActivityManager.getCurrentUser();
        setUserSwitch(true);
        setShowMultiuserUI(true);
        setDeviceDemoMode(false);
        setMaxSupportedUsers(8);
        mUserManagerService.setUserRestriction(DISALLOW_USER_SWITCH, false, userId);
    }

    private void setUserSwitch(boolean enabled) {
        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
                android.provider.Settings.Global.USER_SWITCHER_ENABLED, enabled ? 1 : 0);
    }

    private void setDeviceDemoMode(boolean enabled) {
        android.provider.Settings.Global.putInt(mContext.getContentResolver(),
                android.provider.Settings.Global.DEVICE_DEMO_MODE, enabled ? 1 : 0);
    }


    private static String runShellCommand(String cmd) throws Exception {
        return UiDevice.getInstance(InstrumentationRegistry.getInstrumentation())
                .executeShellCommand(cmd);
    }

    private static String setSystemProperty(String name, String value) throws Exception {
        final String oldValue = runShellCommand("getprop " + name);
        assertThat(runShellCommand("setprop " + name + " " + value))
                .isEqualTo("");
        return oldValue;
    }

    private static void setMaxSupportedUsers(int max) throws Exception {
        setSystemProperty("fw.max_users", String.valueOf(max));
    }

    public static void setShowMultiuserUI(boolean show) throws Exception {
        setSystemProperty("fw.show_multiuserui", String.valueOf(show));
    }
}