From b3b9258ab6a01af5e0df4b8385b73084cd9ec530 Mon Sep 17 00:00:00 2001 From: Xiaohui Chen Date: Mon, 7 Dec 2015 11:22:13 -0800 Subject: [PATCH] UserManager: get/set user account name Bug: 25935510 Change-Id: I0b621fb300be74209534e08a11f1d1a7c049cd5a --- core/java/android/os/IUserManager.aidl | 2 + core/java/android/os/UserManager.java | 35 ++++++ .../android/server/pm/UserManagerService.java | 104 ++++++++++++++++-- .../server/pm/UserManagerServiceTest.java | 17 +++ 4 files changed, 147 insertions(+), 11 deletions(-) diff --git a/core/java/android/os/IUserManager.aidl b/core/java/android/os/IUserManager.aidl index c71d6cc00e54a..f03de7418023f 100644 --- a/core/java/android/os/IUserManager.aidl +++ b/core/java/android/os/IUserManager.aidl @@ -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); diff --git a/core/java/android/os/UserManager.java b/core/java/android/os/UserManager.java index c2fc5e4f64c2a..97f25c0021727 100644 --- a/core/java/android/os/UserManager.java +++ b/core/java/android/os/UserManager.java @@ -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; @@ -1083,6 +1085,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. diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java index b8599155036d9..571ea82a17958 100644 --- a/services/core/java/com/android/server/pm/UserManagerService.java +++ b/services/core/java/com/android/server/pm/UserManagerService.java @@ -16,6 +16,7 @@ package com.android.server.pm; +import android.Manifest; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.Activity; @@ -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; @@ -90,6 +92,7 @@ import java.util.ArrayList; import java.util.List; import libcore.io.IoUtils; +import libcore.util.Objects; /** * Service for {@link UserManager}. @@ -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"; @@ -177,6 +181,14 @@ public class UserManagerService extends IUserManager.Stub { @GuardedBy("mUsersLock") private final SparseArray 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 mUserAccounts = new SparseArray<>(); + /** * User restrictions set via UserManager. This doesn't include restrictions set by * device owner / profile owners. @@ -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"); @@ -981,6 +1020,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} @@ -999,13 +1062,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)); @@ -1087,11 +1143,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 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; } @@ -1291,6 +1350,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(); @@ -1363,10 +1433,11 @@ public class UserManagerService extends IUserManager.Stub { } } - private UserInfo readUserLP(int id) { + private Pair readUserLP(int id) { int flags = 0; int serialNumber = id; String name = null; + String account = null; String iconPath = null; long creationTime = 0L; long lastLoggedInTime = 0L; @@ -1435,6 +1506,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(); + } } } } @@ -1451,7 +1527,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) { @@ -1873,6 +1949,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) { @@ -2433,6 +2510,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:"); diff --git a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java index 9e701385d59d9..48a11c5cb7f15 100644 --- a/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java +++ b/services/tests/servicestests/src/com/android/server/pm/UserManagerServiceTest.java @@ -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; @@ -29,6 +32,7 @@ import java.util.Arrays; public class UserManagerServiceTest extends AndroidTestCase { private static String[] STRING_ARRAY = new String[] {"