Loading core/java/android/os/IUserManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,8 @@ interface IUserManager { UserInfo getProfileParent(int userHandle); boolean isSameProfileGroup(int userId, int otherUserId); UserInfo getUserInfo(int userHandle); String getUserAccount(int userId); void setUserAccount(int userId, String accountName); long getUserCreationTime(int userHandle); boolean isRestricted(); boolean canHaveRestrictedProfile(int userId); Loading core/java/android/os/UserManager.java +35 −0 Original line number Diff line number Diff line Loading @@ -15,8 +15,10 @@ */ package android.os; import android.Manifest; import android.accounts.AccountManager; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityManager; import android.app.ActivityManagerNative; Loading Loading @@ -1120,6 +1122,39 @@ public class UserManager { } } /** * @return the user's account name, null if not found. * @hide */ @RequiresPermission( allOf = { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) public @Nullable String getUserAccount(int userHandle) { try { return mService.getUserAccount(userHandle); } catch (RemoteException re) { Log.w(TAG, "Could not get user account", re); return null; } } /** * Set account name for the given user. * @hide */ @RequiresPermission( allOf = { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) public void setUserAccount(int userHandle, @Nullable String accountName) { try { mService.setUserAccount(userHandle, accountName); } catch (RemoteException re) { Log.w(TAG, "Could not set user account", re); } } /** * Returns information for Primary user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. Loading services/core/java/com/android/server/pm/UserManagerService.java +93 −11 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; Loading Loading @@ -58,6 +59,7 @@ import android.system.Os; import android.system.OsConstants; import android.util.AtomicFile; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading Loading @@ -90,6 +92,7 @@ import java.util.ArrayList; import java.util.List; import libcore.io.IoUtils; import libcore.util.Objects; /** * Service for {@link UserManager}. Loading @@ -107,6 +110,7 @@ public class UserManagerService extends IUserManager.Stub { private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE private static final String TAG_NAME = "name"; private static final String TAG_ACCOUNT = "account"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ICON_PATH = "icon"; private static final String ATTR_ID = "id"; Loading Loading @@ -177,6 +181,14 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private final SparseArray<UserInfo> mUsers = new SparseArray<>(); /** * This collection contains each user's account name if the user chose to set one up * during the initial user creation process. Keeping this information separate from mUsers * to avoid accidentally leak it. */ @GuardedBy("mUsersLock") private final SparseArray<String> mUserAccounts = new SparseArray<>(); /** * User restrictions set via UserManager. This doesn't include restrictions set by * device owner / profile owners. Loading Loading @@ -335,6 +347,33 @@ public class UserManagerService extends IUserManager.Stub { } } @Override public String getUserAccount(int userId) { checkManageUserAndAcrossUsersFullPermission("get user account"); synchronized (mUsersLock) { return mUserAccounts.get(userId); } } @Override public void setUserAccount(int userId, String accountName) { checkManageUserAndAcrossUsersFullPermission("set user account"); UserInfo userToUpdate = null; synchronized (mPackagesLock) { synchronized (mUsersLock) { String currentAccount = mUserAccounts.get(userId); if (!Objects.equal(currentAccount, accountName)) { mUserAccounts.put(userId, accountName); userToUpdate = mUsers.get(userId); } } if (userToUpdate != null) { writeUserLP(userToUpdate); } } } @Override public UserInfo getPrimaryUser() { checkManageUsersPermission("query users"); Loading Loading @@ -1050,6 +1089,30 @@ public class UserManagerService extends IUserManager.Stub { return aliveUserCount; } /** * Enforces that only the system UID or root's UID or apps that have the * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} and * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL INTERACT_ACROSS_USERS_FULL} * permissions can make certain calls to the UserManager. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller does not have enough privilege. */ private static final void checkManageUserAndAcrossUsersFullPermission(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != 0 && ActivityManager.checkComponentPermission( Manifest.permission.MANAGE_USERS, uid, -1, true) != PackageManager.PERMISSION_GRANTED && ActivityManager.checkComponentPermission( Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid, -1, true) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException( "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: " + message); } } /** * Enforces that only the system UID or root's UID or apps that have the * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} Loading @@ -1068,13 +1131,6 @@ public class UserManagerService extends IUserManager.Stub { } } private static void checkSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != 0) { throw new SecurityException("Only system may call: " + message); } } private void writeBitmapLP(UserInfo info, Bitmap bitmap) { try { File dir = new File(mUsersDir, Integer.toString(info.id)); Loading Loading @@ -1156,11 +1212,14 @@ public class UserManagerService extends IUserManager.Stub { final String name = parser.getName(); if (name.equals(TAG_USER)) { String id = parser.getAttributeValue(null, ATTR_ID); UserInfo user = readUserLP(Integer.parseInt(id)); Pair<UserInfo, String> userPair = readUserLP(Integer.parseInt(id)); if (user != null) { if (userPair != null) { UserInfo user = userPair.first; String account = userPair.second; synchronized (mUsersLock) { mUsers.put(user.id, user); mUserAccounts.put(user.id, account); if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) { mNextSerialNumber = user.id + 1; } Loading Loading @@ -1360,6 +1419,17 @@ public class UserManagerService extends IUserManager.Stub { mDevicePolicyLocalUserRestrictions.get(userInfo.id), TAG_DEVICE_POLICY_RESTRICTIONS); } // Update the account field if it is set. String account; synchronized (mUsersLock) { account = mUserAccounts.get(userInfo.id); } if (account != null) { serializer.startTag(null, TAG_ACCOUNT); serializer.text(account); serializer.endTag(null, TAG_ACCOUNT); } serializer.endTag(null, TAG_USER); serializer.endDocument(); Loading Loading @@ -1432,10 +1502,11 @@ public class UserManagerService extends IUserManager.Stub { } } private UserInfo readUserLP(int id) { private Pair<UserInfo, String> readUserLP(int id) { int flags = 0; int serialNumber = id; String name = null; String account = null; String iconPath = null; long creationTime = 0L; long lastLoggedInTime = 0L; Loading Loading @@ -1504,6 +1575,11 @@ public class UserManagerService extends IUserManager.Stub { UserRestrictionsUtils.readRestrictions(parser, baseRestrictions); } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { UserRestrictionsUtils.readRestrictions(parser, localRestrictions); } else if (TAG_ACCOUNT.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { account = parser.getText(); } } } } Loading @@ -1520,7 +1596,7 @@ public class UserManagerService extends IUserManager.Stub { mBaseUserRestrictions.put(id, baseRestrictions); mDevicePolicyLocalUserRestrictions.put(id, localRestrictions); } return userInfo; return new Pair<>(userInfo, account); } catch (IOException ioe) { } catch (XmlPullParserException pe) { Loading Loading @@ -1947,6 +2023,7 @@ public class UserManagerService extends IUserManager.Stub { // Remove this user from the list synchronized (mUsersLock) { mUsers.remove(userHandle); mUserAccounts.delete(userHandle); mIsUserManaged.delete(userHandle); } synchronized (mRestrictionsLock) { Loading Loading @@ -2507,6 +2584,11 @@ public class UserManagerService extends IUserManager.Stub { pw, " ", mCachedEffectiveUserRestrictions.get(user.id)); } pw.println(); String accountName = mUserAccounts.get(userId); if (accountName != null) { pw.print(" Account name: " + accountName); pw.println(); } } } pw.println(" Device policy global restrictions:"); Loading services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java +17 −0 Original line number Diff line number Diff line Loading @@ -16,9 +16,12 @@ package com.android.server.pm; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.FileUtils; import android.os.Parcelable; import android.os.UserHandle; import android.os.UserManager; import android.test.AndroidTestCase; import android.util.AtomicFile; Loading @@ -29,6 +32,7 @@ import java.util.Arrays; public class UserManagerServiceTest extends AndroidTestCase { private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["}; private File restrictionsFile; private int tempUserId = UserHandle.USER_NULL; @Override protected void setUp() throws Exception { Loading @@ -40,6 +44,9 @@ public class UserManagerServiceTest extends AndroidTestCase { @Override protected void tearDown() throws Exception { restrictionsFile.delete(); if (tempUserId != UserHandle.USER_NULL) { UserManager.get(mContext).removeUser(tempUserId); } super.tearDown(); } Loading @@ -55,6 +62,16 @@ public class UserManagerServiceTest extends AndroidTestCase { assertBundle(bundle); } public void testAddUserWithAccount() { UserManager um = UserManager.get(mContext); UserInfo user = um.createUser("Test User", 0); assertNotNull(user); tempUserId = user.id; String accountName = "Test Account"; um.setUserAccount(tempUserId, accountName); assertEquals(accountName, um.getUserAccount(tempUserId)); } private Bundle createBundle() { Bundle result = new Bundle(); // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[] Loading Loading
core/java/android/os/IUserManager.aidl +2 −0 Original line number Diff line number Diff line Loading @@ -48,6 +48,8 @@ interface IUserManager { UserInfo getProfileParent(int userHandle); boolean isSameProfileGroup(int userId, int otherUserId); UserInfo getUserInfo(int userHandle); String getUserAccount(int userId); void setUserAccount(int userId, String accountName); long getUserCreationTime(int userHandle); boolean isRestricted(); boolean canHaveRestrictedProfile(int userId); Loading
core/java/android/os/UserManager.java +35 −0 Original line number Diff line number Diff line Loading @@ -15,8 +15,10 @@ */ package android.os; import android.Manifest; import android.accounts.AccountManager; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.ActivityManager; import android.app.ActivityManagerNative; Loading Loading @@ -1120,6 +1122,39 @@ public class UserManager { } } /** * @return the user's account name, null if not found. * @hide */ @RequiresPermission( allOf = { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) public @Nullable String getUserAccount(int userHandle) { try { return mService.getUserAccount(userHandle); } catch (RemoteException re) { Log.w(TAG, "Could not get user account", re); return null; } } /** * Set account name for the given user. * @hide */ @RequiresPermission( allOf = { Manifest.permission.INTERACT_ACROSS_USERS_FULL, Manifest.permission.MANAGE_USERS }) public void setUserAccount(int userHandle, @Nullable String accountName) { try { mService.setUserAccount(userHandle, accountName); } catch (RemoteException re) { Log.w(TAG, "Could not set user account", re); } } /** * Returns information for Primary user. * Requires {@link android.Manifest.permission#MANAGE_USERS} permission. Loading
services/core/java/com/android/server/pm/UserManagerService.java +93 −11 Original line number Diff line number Diff line Loading @@ -16,6 +16,7 @@ package com.android.server.pm; import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; Loading Loading @@ -58,6 +59,7 @@ import android.system.Os; import android.system.OsConstants; import android.util.AtomicFile; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.util.SparseArray; import android.util.SparseBooleanArray; Loading Loading @@ -90,6 +92,7 @@ import java.util.ArrayList; import java.util.List; import libcore.io.IoUtils; import libcore.util.Objects; /** * Service for {@link UserManager}. Loading @@ -107,6 +110,7 @@ public class UserManagerService extends IUserManager.Stub { private static final boolean DBG_WITH_STACKTRACE = false; // DO NOT SUBMIT WITH TRUE private static final String TAG_NAME = "name"; private static final String TAG_ACCOUNT = "account"; private static final String ATTR_FLAGS = "flags"; private static final String ATTR_ICON_PATH = "icon"; private static final String ATTR_ID = "id"; Loading Loading @@ -177,6 +181,14 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private final SparseArray<UserInfo> mUsers = new SparseArray<>(); /** * This collection contains each user's account name if the user chose to set one up * during the initial user creation process. Keeping this information separate from mUsers * to avoid accidentally leak it. */ @GuardedBy("mUsersLock") private final SparseArray<String> mUserAccounts = new SparseArray<>(); /** * User restrictions set via UserManager. This doesn't include restrictions set by * device owner / profile owners. Loading Loading @@ -335,6 +347,33 @@ public class UserManagerService extends IUserManager.Stub { } } @Override public String getUserAccount(int userId) { checkManageUserAndAcrossUsersFullPermission("get user account"); synchronized (mUsersLock) { return mUserAccounts.get(userId); } } @Override public void setUserAccount(int userId, String accountName) { checkManageUserAndAcrossUsersFullPermission("set user account"); UserInfo userToUpdate = null; synchronized (mPackagesLock) { synchronized (mUsersLock) { String currentAccount = mUserAccounts.get(userId); if (!Objects.equal(currentAccount, accountName)) { mUserAccounts.put(userId, accountName); userToUpdate = mUsers.get(userId); } } if (userToUpdate != null) { writeUserLP(userToUpdate); } } } @Override public UserInfo getPrimaryUser() { checkManageUsersPermission("query users"); Loading Loading @@ -1050,6 +1089,30 @@ public class UserManagerService extends IUserManager.Stub { return aliveUserCount; } /** * Enforces that only the system UID or root's UID or apps that have the * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} and * {@link android.Manifest.permission#INTERACT_ACROSS_USERS_FULL INTERACT_ACROSS_USERS_FULL} * permissions can make certain calls to the UserManager. * * @param message used as message if SecurityException is thrown * @throws SecurityException if the caller does not have enough privilege. */ private static final void checkManageUserAndAcrossUsersFullPermission(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != 0 && ActivityManager.checkComponentPermission( Manifest.permission.MANAGE_USERS, uid, -1, true) != PackageManager.PERMISSION_GRANTED && ActivityManager.checkComponentPermission( Manifest.permission.INTERACT_ACROSS_USERS_FULL, uid, -1, true) != PackageManager.PERMISSION_GRANTED) { throw new SecurityException( "You need MANAGE_USERS and INTERACT_ACROSS_USERS_FULL permission to: " + message); } } /** * Enforces that only the system UID or root's UID or apps that have the * {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} Loading @@ -1068,13 +1131,6 @@ public class UserManagerService extends IUserManager.Stub { } } private static void checkSystemOrRoot(String message) { final int uid = Binder.getCallingUid(); if (uid != Process.SYSTEM_UID && uid != 0) { throw new SecurityException("Only system may call: " + message); } } private void writeBitmapLP(UserInfo info, Bitmap bitmap) { try { File dir = new File(mUsersDir, Integer.toString(info.id)); Loading Loading @@ -1156,11 +1212,14 @@ public class UserManagerService extends IUserManager.Stub { final String name = parser.getName(); if (name.equals(TAG_USER)) { String id = parser.getAttributeValue(null, ATTR_ID); UserInfo user = readUserLP(Integer.parseInt(id)); Pair<UserInfo, String> userPair = readUserLP(Integer.parseInt(id)); if (user != null) { if (userPair != null) { UserInfo user = userPair.first; String account = userPair.second; synchronized (mUsersLock) { mUsers.put(user.id, user); mUserAccounts.put(user.id, account); if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) { mNextSerialNumber = user.id + 1; } Loading Loading @@ -1360,6 +1419,17 @@ public class UserManagerService extends IUserManager.Stub { mDevicePolicyLocalUserRestrictions.get(userInfo.id), TAG_DEVICE_POLICY_RESTRICTIONS); } // Update the account field if it is set. String account; synchronized (mUsersLock) { account = mUserAccounts.get(userInfo.id); } if (account != null) { serializer.startTag(null, TAG_ACCOUNT); serializer.text(account); serializer.endTag(null, TAG_ACCOUNT); } serializer.endTag(null, TAG_USER); serializer.endDocument(); Loading Loading @@ -1432,10 +1502,11 @@ public class UserManagerService extends IUserManager.Stub { } } private UserInfo readUserLP(int id) { private Pair<UserInfo, String> readUserLP(int id) { int flags = 0; int serialNumber = id; String name = null; String account = null; String iconPath = null; long creationTime = 0L; long lastLoggedInTime = 0L; Loading Loading @@ -1504,6 +1575,11 @@ public class UserManagerService extends IUserManager.Stub { UserRestrictionsUtils.readRestrictions(parser, baseRestrictions); } else if (TAG_DEVICE_POLICY_RESTRICTIONS.equals(tag)) { UserRestrictionsUtils.readRestrictions(parser, localRestrictions); } else if (TAG_ACCOUNT.equals(tag)) { type = parser.next(); if (type == XmlPullParser.TEXT) { account = parser.getText(); } } } } Loading @@ -1520,7 +1596,7 @@ public class UserManagerService extends IUserManager.Stub { mBaseUserRestrictions.put(id, baseRestrictions); mDevicePolicyLocalUserRestrictions.put(id, localRestrictions); } return userInfo; return new Pair<>(userInfo, account); } catch (IOException ioe) { } catch (XmlPullParserException pe) { Loading Loading @@ -1947,6 +2023,7 @@ public class UserManagerService extends IUserManager.Stub { // Remove this user from the list synchronized (mUsersLock) { mUsers.remove(userHandle); mUserAccounts.delete(userHandle); mIsUserManaged.delete(userHandle); } synchronized (mRestrictionsLock) { Loading Loading @@ -2507,6 +2584,11 @@ public class UserManagerService extends IUserManager.Stub { pw, " ", mCachedEffectiveUserRestrictions.get(user.id)); } pw.println(); String accountName = mUserAccounts.get(userId); if (accountName != null) { pw.print(" Account name: " + accountName); pw.println(); } } } pw.println(" Device policy global restrictions:"); Loading
services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java +17 −0 Original line number Diff line number Diff line Loading @@ -16,9 +16,12 @@ package com.android.server.pm; import android.content.pm.UserInfo; import android.os.Bundle; import android.os.FileUtils; import android.os.Parcelable; import android.os.UserHandle; import android.os.UserManager; import android.test.AndroidTestCase; import android.util.AtomicFile; Loading @@ -29,6 +32,7 @@ import java.util.Arrays; public class UserManagerServiceTest extends AndroidTestCase { private static String[] STRING_ARRAY = new String[] {"<tag", "<![CDATA["}; private File restrictionsFile; private int tempUserId = UserHandle.USER_NULL; @Override protected void setUp() throws Exception { Loading @@ -40,6 +44,9 @@ public class UserManagerServiceTest extends AndroidTestCase { @Override protected void tearDown() throws Exception { restrictionsFile.delete(); if (tempUserId != UserHandle.USER_NULL) { UserManager.get(mContext).removeUser(tempUserId); } super.tearDown(); } Loading @@ -55,6 +62,16 @@ public class UserManagerServiceTest extends AndroidTestCase { assertBundle(bundle); } public void testAddUserWithAccount() { UserManager um = UserManager.get(mContext); UserInfo user = um.createUser("Test User", 0); assertNotNull(user); tempUserId = user.id; String accountName = "Test Account"; um.setUserAccount(tempUserId, accountName); assertEquals(accountName, um.getUserAccount(tempUserId)); } private Bundle createBundle() { Bundle result = new Bundle(); // Tests for 6 allowed types: Integer, Boolean, String, String[], Bundle and Parcelable[] Loading