Merge "Add Android Multiuser Atom which will contain number of supported users." into tm-qpr-dev

This commit is contained in:
Anna Bauza
2022-08-24 18:15:25 +00:00
committed by Android (Google) Code Review
4 changed files with 239 additions and 79 deletions

View File

@@ -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);

View File

@@ -5132,23 +5132,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)) {
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;
try {
if (!mService.isUserSwitcherEnabled(mUserId)) {
return false;
}
} catch (RemoteException re) {
throw re.rethrowFromSystemServer();
}
// The feature is enabled. But is it worth showing?

View File

@@ -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;
@@ -1759,6 +1760,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()) {
@@ -2049,7 +2063,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.
@@ -4432,45 +4445,60 @@ 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) {
if (atomTag == FrameworkStatsLog.USER_INFO) {
final List<UserInfo> users = getUsersInternal(true, true, true);
final int size = users.size();
if (size > 1) {
for (int idx = 0; idx < size; idx++) {
final UserInfo user = users.get(idx);
final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
final String userTypeCustom = (userTypeStandard == FrameworkStatsLog
.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN)
?
user.userType : null;
boolean isUserRunningUnlocked;
synchronized (mUserStates) {
isUserRunningUnlocked =
mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
}
data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
user.id,
userTypeStandard,
userTypeCustom,
user.flags,
user.creationTime,
user.lastLoggedInTime,
isUserRunningUnlocked
));
}
}
} 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;
}
final List<UserInfo> users = getUsersInternal(true, true, true);
final int size = users.size();
for (int idx = 0; idx < size; idx++) {
final UserInfo user = users.get(idx);
if (user.id == UserHandle.USER_SYSTEM) {
// Skip user 0. It's not interesting. We already know it exists, is running, and (if
// we know the device configuration) its userType.
continue;
}
final int userTypeStandard = UserManager.getUserTypeForStatsd(user.userType);
final String userTypeCustom = (userTypeStandard ==
FrameworkStatsLog.USER_LIFECYCLE_JOURNEY_REPORTED__USER_TYPE__TYPE_UNKNOWN) ?
user.userType : null;
boolean isUserRunningUnlocked;
synchronized (mUserStates) {
isUserRunningUnlocked =
mUserStates.get(user.id, -1) == UserState.STATE_RUNNING_UNLOCKED;
}
data.add(FrameworkStatsLog.buildStatsEvent(FrameworkStatsLog.USER_INFO,
user.id,
userTypeStandard,
userTypeCustom,
user.flags,
user.creationTime,
user.lastLoggedInTime,
isUserRunningUnlocked
));
}
return android.app.StatsManager.PULL_SUCCESS;
}

View File

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