FileDocCategorySizeDatePackage
UserManagerService.javaAPI DocAndroid 5.1 API78811Thu Mar 12 22:22:42 GMT 2015com.android.server.pm

UserManagerService

public class UserManagerService extends IUserManager.Stub

Fields Summary
private static final String
LOG_TAG
private static final boolean
DBG
private static final String
TAG_NAME
private static final String
ATTR_FLAGS
private static final String
ATTR_ICON_PATH
private static final String
ATTR_ID
private static final String
ATTR_CREATION_TIME
private static final String
ATTR_LAST_LOGGED_IN_TIME
private static final String
ATTR_SALT
private static final String
ATTR_PIN_HASH
private static final String
ATTR_FAILED_ATTEMPTS
private static final String
ATTR_LAST_RETRY_MS
private static final String
ATTR_SERIAL_NO
private static final String
ATTR_NEXT_SERIAL_NO
private static final String
ATTR_PARTIAL
private static final String
ATTR_GUEST_TO_REMOVE
private static final String
ATTR_USER_VERSION
private static final String
ATTR_PROFILE_GROUP_ID
private static final String
TAG_GUEST_RESTRICTIONS
private static final String
TAG_USERS
private static final String
TAG_USER
private static final String
TAG_RESTRICTIONS
private static final String
TAG_ENTRY
private static final String
TAG_VALUE
private static final String
ATTR_KEY
private static final String
ATTR_VALUE_TYPE
private static final String
ATTR_MULTIPLE
private static final String
ATTR_TYPE_STRING_ARRAY
private static final String
ATTR_TYPE_STRING
private static final String
ATTR_TYPE_BOOLEAN
private static final String
ATTR_TYPE_INTEGER
private static final String
USER_INFO_DIR
private static final String
USER_LIST_FILENAME
private static final String
USER_PHOTO_FILENAME
private static final String
RESTRICTIONS_FILE_PREFIX
private static final String
XML_SUFFIX
private static final int
MIN_USER_ID
private static final int
USER_VERSION
private static final long
EPOCH_PLUS_30_YEARS
private static final int
BACKOFF_INC_INTERVAL
private static final int
MAX_MANAGED_PROFILES
private static final int[]
BACKOFF_TIMES
private final android.content.Context
mContext
private final PackageManagerService
mPm
private final Object
mInstallLock
private final Object
mPackagesLock
private final android.os.Handler
mHandler
private final File
mUsersDir
private final File
mUserListFile
private final File
mBaseUserPath
private final android.util.SparseArray
mUsers
private final android.util.SparseArray
mUserRestrictions
private final android.os.Bundle
mGuestRestrictions
private final android.util.SparseArray
mRestrictionsPinStates
private final android.util.SparseBooleanArray
mRemovingUserIds
Set of user IDs being actively removed. Removed IDs linger in this set for several seconds to work around a VFS caching issue.
private int[]
mUserIds
private int
mNextSerialNumber
private int
mUserVersion
private com.android.internal.app.IAppOpsService
mAppOpsService
private static UserManagerService
sInstance
Constructors Summary
UserManagerService(File dataDir, File baseUserPath)
Available for testing purposes.

        this(null, null, new Object(), new Object(), dataDir, baseUserPath);
    
UserManagerService(android.content.Context context, PackageManagerService pm, Object installLock, Object packagesLock)
Called by package manager to create the service. This is closely associated with the package manager, and the given lock is the package manager's own lock.

        this(context, pm, installLock, packagesLock,
                Environment.getDataDirectory(),
                new File(Environment.getDataDirectory(), "user"));
    
private UserManagerService(android.content.Context context, PackageManagerService pm, Object installLock, Object packagesLock, File dataDir, File baseUserPath)
Available for testing purposes.

        mContext = context;
        mPm = pm;
        mInstallLock = installLock;
        mPackagesLock = packagesLock;
        mHandler = new Handler();
        synchronized (mInstallLock) {
            synchronized (mPackagesLock) {
                mUsersDir = new File(dataDir, USER_INFO_DIR);
                mUsersDir.mkdirs();
                // Make zeroth user directory, for services to migrate their files to that location
                File userZeroDir = new File(mUsersDir, "0");
                userZeroDir.mkdirs();
                mBaseUserPath = baseUserPath;
                FileUtils.setPermissions(mUsersDir.toString(),
                        FileUtils.S_IRWXU|FileUtils.S_IRWXG
                        |FileUtils.S_IROTH|FileUtils.S_IXOTH,
                        -1, -1);
                mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
                initDefaultGuestRestrictions();
                readUserListLocked();
                // Prune out any partially created/partially removed users.
                ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
                for (int i = 0; i < mUsers.size(); i++) {
                    UserInfo ui = mUsers.valueAt(i);
                    if ((ui.partial || ui.guestToRemove) && i != 0) {
                        partials.add(ui);
                    }
                }
                for (int i = 0; i < partials.size(); i++) {
                    UserInfo ui = partials.get(i);
                    Slog.w(LOG_TAG, "Removing partially created user #" + i
                            + " (name=" + ui.name + ")");
                    removeUserStateLocked(ui.id);
                }
                sInstance = this;
            }
        }
    
Methods Summary
private static final voidcheckManageUsersPermission(java.lang.String message)
Enforces that only the system UID or root's UID or apps that have the {@link android.Manifest.permission#MANAGE_USERS MANAGE_USERS} permission can make certain calls to the UserManager.

param
message used as message if SecurityException is thrown
throws
SecurityException if the caller is not system or root

        final int uid = Binder.getCallingUid();
        if (uid != Process.SYSTEM_UID && uid != 0
                && ActivityManager.checkComponentPermission(
                        android.Manifest.permission.MANAGE_USERS,
                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
        }
    
public intcheckRestrictionsChallenge(java.lang.String pin)

        checkManageUsersPermission("Only system can verify the restrictions pin");
        int userId = UserHandle.getCallingUserId();
        synchronized (mPackagesLock) {
            RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
            // If there's no pin set, return error code
            if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) {
                return UserManager.PIN_VERIFICATION_FAILED_NOT_SET;
            } else if (pin == null) {
                // If just checking if user can be prompted, return remaining time
                int waitTime = getRemainingTimeForPinAttempt(pinState);
                Slog.d(LOG_TAG, "Remaining waittime peek=" + waitTime);
                return waitTime;
            } else {
                int waitTime = getRemainingTimeForPinAttempt(pinState);
                Slog.d(LOG_TAG, "Remaining waittime=" + waitTime);
                if (waitTime > 0) {
                    return waitTime;
                }
                if (passwordToHash(pin, pinState.salt).equals(pinState.pinHash)) {
                    pinState.failedAttempts = 0;
                    writeUserLocked(mUsers.get(userId));
                    return UserManager.PIN_VERIFICATION_SUCCESS;
                } else {
                    pinState.failedAttempts++;
                    pinState.lastAttemptTime = System.currentTimeMillis();
                    writeUserLocked(mUsers.get(userId));
                    return waitTime;
                }
            }
        }
    
private voidcleanAppRestrictions(int userId)
Removes all the restrictions files (res_) for a given user. Does not do any permissions checking.

        synchronized (mPackagesLock) {
            File dir = Environment.getUserSystemDirectory(userId);
            String[] files = dir.list();
            if (files == null) return;
            for (String fileName : files) {
                if (fileName.startsWith(RESTRICTIONS_FILE_PREFIX)) {
                    File resFile = new File(dir, fileName);
                    if (resFile.exists()) {
                        resFile.delete();
                    }
                }
            }
        }
    
private voidcleanAppRestrictionsForPackage(java.lang.String pkg, int userId)
Removes the app restrictions file for a specific package and user id, if it exists.

        synchronized (mPackagesLock) {
            File dir = Environment.getUserSystemDirectory(userId);
            File resFile = new File(dir, packageToRestrictionsFileName(pkg));
            if (resFile.exists()) {
                resFile.delete();
            }
        }
    
public android.content.pm.UserInfocreateProfileForUser(java.lang.String name, int flags, int userId)

        checkManageUsersPermission("Only the system can create users");
        if (userId != UserHandle.USER_OWNER) {
            Slog.w(LOG_TAG, "Only user owner can have profiles");
            return null;
        }
        return createUserInternal(name, flags, userId);
    
public android.content.pm.UserInfocreateUser(java.lang.String name, int flags)

        checkManageUsersPermission("Only the system can create users");
        return createUserInternal(name, flags, UserHandle.USER_NULL);
    
private android.content.pm.UserInfocreateUserInternal(java.lang.String name, int flags, int parentId)

        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
                UserManager.DISALLOW_ADD_USER, false)) {
            Log.w(LOG_TAG, "Cannot add user. DISALLOW_ADD_USER is enabled.");
            return null;
        }
        final boolean isGuest = (flags & UserInfo.FLAG_GUEST) != 0;
        final long ident = Binder.clearCallingIdentity();
        UserInfo userInfo = null;
        try {
            synchronized (mInstallLock) {
                synchronized (mPackagesLock) {
                    UserInfo parent = null;
                    if (parentId != UserHandle.USER_NULL) {
                        parent = getUserInfoLocked(parentId);
                        if (parent == null) return null;
                    }
                    // If we're not adding a guest user and the limit has been reached,
                    // cannot add a user.
                    if (!isGuest && isUserLimitReachedLocked()) {
                        return null;
                    }
                    // If we're adding a guest and there already exists one, bail.
                    if (isGuest && findCurrentGuestUserLocked() != null) {
                        return null;
                    }
                    // Limit number of managed profiles that can be created
                    if ((flags & UserInfo.FLAG_MANAGED_PROFILE) != 0
                            && numberOfUsersOfTypeLocked(UserInfo.FLAG_MANAGED_PROFILE, true)
                                >= MAX_MANAGED_PROFILES) {
                        return null;
                    }
                    int userId = getNextAvailableIdLocked();
                    userInfo = new UserInfo(userId, name, null, flags);
                    File userPath = new File(mBaseUserPath, Integer.toString(userId));
                    userInfo.serialNumber = mNextSerialNumber++;
                    long now = System.currentTimeMillis();
                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
                    userInfo.partial = true;
                    Environment.getUserSystemDirectory(userInfo.id).mkdirs();
                    mUsers.put(userId, userInfo);
                    writeUserListLocked();
                    if (parent != null) {
                        if (parent.profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
                            parent.profileGroupId = parent.id;
                            writeUserLocked(parent);
                        }
                        userInfo.profileGroupId = parent.profileGroupId;
                    }
                    writeUserLocked(userInfo);
                    mPm.createNewUserLILPw(userId, userPath);
                    userInfo.partial = false;
                    writeUserLocked(userInfo);
                    updateUserIdsLocked();
                    Bundle restrictions = new Bundle();
                    mUserRestrictions.append(userId, restrictions);
                }
            }
            if (userInfo != null) {
                Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
                addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
                mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
                        android.Manifest.permission.MANAGE_USERS);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return userInfo;
    
protected voiddump(java.io.FileDescriptor fd, java.io.PrintWriter pw, java.lang.String[] args)

        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
                != PackageManager.PERMISSION_GRANTED) {
            pw.println("Permission Denial: can't dump UserManager from from pid="
                    + Binder.getCallingPid()
                    + ", uid=" + Binder.getCallingUid()
                    + " without permission "
                    + android.Manifest.permission.DUMP);
            return;
        }

        long now = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder();
        synchronized (mPackagesLock) {
            pw.println("Users:");
            for (int i = 0; i < mUsers.size(); i++) {
                UserInfo user = mUsers.valueAt(i);
                if (user == null) continue;
                pw.print("  "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
                if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
                if (user.partial) pw.print(" <partial>");
                pw.println();
                pw.print("    Created: ");
                if (user.creationTime == 0) {
                    pw.println("<unknown>");
                } else {
                    sb.setLength(0);
                    TimeUtils.formatDuration(now - user.creationTime, sb);
                    sb.append(" ago");
                    pw.println(sb);
                }
                pw.print("    Last logged in: ");
                if (user.lastLoggedInTime == 0) {
                    pw.println("<unknown>");
                } else {
                    sb.setLength(0);
                    TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
                    sb.append(" ago");
                    pw.println(sb);
                }
            }
        }
    
public booleanexists(int userId)

        synchronized (mPackagesLock) {
            return ArrayUtils.contains(mUserIds, userId);
        }
    
private voidfallbackToSingleUserLocked()

        // Create the primary user
        UserInfo primary = new UserInfo(UserHandle.USER_OWNER,
                mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
        mUsers.put(0, primary);
        mNextSerialNumber = MIN_USER_ID;
        mUserVersion = USER_VERSION;

        Bundle restrictions = new Bundle();
        mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);

        updateUserIdsLocked();
        initDefaultGuestRestrictions();

        writeUserListLocked();
        writeUserLocked(primary);
    
private android.content.pm.UserInfofindCurrentGuestUserLocked()
Find the current guest user. If the Guest user is partial, then do not include it in the results as it is about to die. This is different than {@link #numberOfUsersOfTypeLocked(int, boolean)} due to the special handling of Guests being removed.

        final int size = mUsers.size();
        for (int i = 0; i < size; i++) {
            final UserInfo user = mUsers.valueAt(i);
            if (user.isGuest() && !user.guestToRemove && !mRemovingUserIds.get(user.id)) {
                return user;
            }
        }
        return null;
    
voidfinishRemoveUser(int userHandle)

        if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
        // Let other services shutdown any activity and clean up their state before completely
        // wiping the user's system directory and removing from the user list
        long ident = Binder.clearCallingIdentity();
        try {
            Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
            mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
                    android.Manifest.permission.MANAGE_USERS,

                    new BroadcastReceiver() {
                        @Override
                        public void onReceive(Context context, Intent intent) {
                            if (DBG) {
                                Slog.i(LOG_TAG,
                                        "USER_REMOVED broadcast sent, cleaning up user data "
                                        + userHandle);
                            }
                            new Thread() {
                                public void run() {
                                    synchronized (mInstallLock) {
                                        synchronized (mPackagesLock) {
                                            removeUserStateLocked(userHandle);
                                        }
                                    }
                                }
                            }.start();
                        }
                    },

                    null, Activity.RESULT_OK, null, null);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
public android.os.BundlegetApplicationRestrictions(java.lang.String packageName)

        return getApplicationRestrictionsForUser(packageName, UserHandle.getCallingUserId());
    
public android.os.BundlegetApplicationRestrictionsForUser(java.lang.String packageName, int userId)

        if (UserHandle.getCallingUserId() != userId
                || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
            checkManageUsersPermission("Only system can get restrictions for other users/apps");
        }
        synchronized (mPackagesLock) {
            // Read the restrictions from XML
            return readApplicationRestrictionsLocked(packageName, userId);
        }
    
public android.os.BundlegetDefaultGuestRestrictions()

        checkManageUsersPermission("getDefaultGuestRestrictions");
        synchronized (mPackagesLock) {
            return new Bundle(mGuestRestrictions);
        }
    
public static com.android.server.pm.UserManagerServicegetInstance()


        
        synchronized (UserManagerService.class) {
            return sInstance;
        }
    
private intgetNextAvailableIdLocked()
Returns the next available user id, filling in any holes in the ids. TODO: May not be a good idea to recycle ids, in case it results in confusion for data and battery stats collection, or unexpected cross-talk.

return

        synchronized (mPackagesLock) {
            int i = MIN_USER_ID;
            while (i < Integer.MAX_VALUE) {
                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
                    break;
                }
                i++;
            }
            return i;
        }
    
public android.content.pm.UserInfogetProfileParent(int userHandle)

        checkManageUsersPermission("get the profile parent");
        synchronized (mPackagesLock) {
            UserInfo profile = getUserInfoLocked(userHandle);
            if (profile == null) {
                return null;
            }
            int parentUserId = profile.profileGroupId;
            if (parentUserId == UserInfo.NO_PROFILE_GROUP_ID) {
                return null;
            } else {
                return getUserInfoLocked(parentUserId);
            }
        }
    
public java.util.ListgetProfiles(int userId, boolean enabledOnly)

        if (userId != UserHandle.getCallingUserId()) {
            checkManageUsersPermission("getting profiles related to user " + userId);
        }
        final long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mPackagesLock) {
                return getProfilesLocked(userId, enabledOnly);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
private java.util.ListgetProfilesLocked(int userId, boolean enabledOnly)
Assume permissions already checked and caller's identity cleared

        UserInfo user = getUserInfoLocked(userId);
        ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
        if (user == null) {
            // Probably a dying user
            return users;
        }
        for (int i = 0; i < mUsers.size(); i++) {
            UserInfo profile = mUsers.valueAt(i);
            if (!isProfileOf(user, profile)) {
                continue;
            }
            if (enabledOnly && !profile.isEnabled()) {
                continue;
            }
            if (mRemovingUserIds.get(profile.id)) {
                continue;
            }
            users.add(profile);
        }
        return users;
    
private intgetRemainingTimeForPinAttempt(com.android.server.pm.UserManagerService$RestrictionsPinState pinState)

        int backoffIndex = Math.min(pinState.failedAttempts / BACKOFF_INC_INTERVAL,
                BACKOFF_TIMES.length - 1);
        int backoffTime = (pinState.failedAttempts % BACKOFF_INC_INTERVAL) == 0 ?
                BACKOFF_TIMES[backoffIndex] : 0;
        return (int) Math.max(backoffTime + pinState.lastAttemptTime - System.currentTimeMillis(),
                0);
    
private intgetUidForPackage(java.lang.String packageName)

        long ident = Binder.clearCallingIdentity();
        try {
            return mContext.getPackageManager().getApplicationInfo(packageName,
                    PackageManager.GET_UNINSTALLED_PACKAGES).uid;
        } catch (NameNotFoundException nnfe) {
            return -1;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
public intgetUserHandle(int userSerialNumber)

        synchronized (mPackagesLock) {
            for (int userId : mUserIds) {
                if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
            }
            // Not found
            return -1;
        }
    
public android.graphics.BitmapgetUserIcon(int userId)

        synchronized (mPackagesLock) {
            UserInfo info = mUsers.get(userId);
            if (info == null || info.partial) {
                Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
                return null;
            }
            int callingGroupId = mUsers.get(UserHandle.getCallingUserId()).profileGroupId;
            if (callingGroupId == UserInfo.NO_PROFILE_GROUP_ID
                    || callingGroupId != info.profileGroupId) {
                checkManageUsersPermission("get the icon of a user who is not related");
            }
            if (info.iconPath == null) {
                return null;
            }
            return BitmapFactory.decodeFile(info.iconPath);
        }
    
public int[]getUserIds()
Returns an array of user ids. This array is cached here for quick access, so do not modify or cache it elsewhere.

return
the array of user ids.

        synchronized (mPackagesLock) {
            return mUserIds;
        }
    
int[]getUserIdsLPr()

        return mUserIds;
    
public android.content.pm.UserInfogetUserInfo(int userId)

        checkManageUsersPermission("query user");
        synchronized (mPackagesLock) {
            return getUserInfoLocked(userId);
        }
    
private android.content.pm.UserInfogetUserInfoLocked(int userId)

        UserInfo ui = mUsers.get(userId);
        // If it is partial and not in the process of being removed, return as unknown user.
        if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
            Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
            return null;
        }
        return ui;
    
public android.os.BundlegetUserRestrictions(int userId)

        // checkManageUsersPermission("getUserRestrictions");

        synchronized (mPackagesLock) {
            Bundle restrictions = mUserRestrictions.get(userId);
            return restrictions != null ? new Bundle(restrictions) : new Bundle();
        }
    
public intgetUserSerialNumber(int userHandle)

        synchronized (mPackagesLock) {
            if (!exists(userHandle)) return -1;
            return getUserInfoLocked(userHandle).serialNumber;
        }
    
public java.util.ListgetUsers(boolean excludeDying)

        checkManageUsersPermission("query users");
        synchronized (mPackagesLock) {
            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
            for (int i = 0; i < mUsers.size(); i++) {
                UserInfo ui = mUsers.valueAt(i);
                if (ui.partial) {
                    continue;
                }
                if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
                    users.add(ui);
                }
            }
            return users;
        }
    
public booleanhasRestrictionsChallenge()

        int userId = UserHandle.getCallingUserId();
        synchronized (mPackagesLock) {
            return hasRestrictionsPinLocked(userId);
        }
    
private booleanhasRestrictionsPinLocked(int userId)

        RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
        if (pinState == null || pinState.salt == 0 || pinState.pinHash == null) {
            return false;
        }
        return true;
    
public booleanhasUserRestriction(java.lang.String restrictionKey, int userId)

        synchronized (mPackagesLock) {
            Bundle restrictions = mUserRestrictions.get(userId);
            return restrictions != null ? restrictions.getBoolean(restrictionKey) : false;
        }
    
private voidinitDefaultGuestRestrictions()
If default guest restrictions haven't been initialized yet, add the basic restrictions.

        if (mGuestRestrictions.isEmpty()) {
            mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
            mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
        }
    
private booleanisPackageInstalled(java.lang.String pkg, int userId)

        final ApplicationInfo info = mPm.getApplicationInfo(pkg,
                PackageManager.GET_UNINSTALLED_PACKAGES,
                userId);
        if (info == null || (info.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
            return false;
        }
        return true;
    
private booleanisProfileOf(android.content.pm.UserInfo user, android.content.pm.UserInfo profile)

        return user.id == profile.id ||
                (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                && user.profileGroupId == profile.profileGroupId);
    
public booleanisRestricted()

        synchronized (mPackagesLock) {
            return getUserInfoLocked(UserHandle.getCallingUserId()).isRestricted();
        }
    
private booleanisUserLimitReachedLocked()
Check if we've hit the limit of how many users can be created.

        int aliveUserCount = 0;
        final int totalUserCount = mUsers.size();
        // Skip over users being removed
        for (int i = 0; i < totalUserCount; i++) {
            UserInfo user = mUsers.valueAt(i);
            if (!mRemovingUserIds.get(user.id)
                    && !user.isGuest() && !user.partial) {
                aliveUserCount++;
            }
        }
        return aliveUserCount >= UserManager.getMaxSupportedUsers();
    
public voidmakeInitialized(int userId)

        checkManageUsersPermission("makeInitialized");
        synchronized (mPackagesLock) {
            UserInfo info = mUsers.get(userId);
            if (info == null || info.partial) {
                Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
            }
            if ((info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
                info.flags |= UserInfo.FLAG_INITIALIZED;
                writeUserLocked(info);
            }
        }
    
public booleanmarkGuestForDeletion(int userHandle)
Mark this guest user for deletion to allow us to create another guest and switch to that user before actually removing this guest.

param
userHandle the userid of the current guest
return
whether the user could be marked for deletion

        checkManageUsersPermission("Only the system can remove users");
        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
                UserManager.DISALLOW_REMOVE_USER, false)) {
            Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled.");
            return false;
        }

        long ident = Binder.clearCallingIdentity();
        try {
            final UserInfo user;
            synchronized (mPackagesLock) {
                user = mUsers.get(userHandle);
                if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
                    return false;
                }
                if (!user.isGuest()) {
                    return false;
                }
                // We set this to a guest user that is to be removed. This is a temporary state
                // where we are allowed to add new Guest users, even if this one is still not
                // removed. This user will still show up in getUserInfo() calls.
                // If we don't get around to removing this Guest user, it will be purged on next
                // startup.
                user.guestToRemove = true;
                // Mark it as disabled, so that it isn't returned any more when
                // profiles are queried.
                user.flags |= UserInfo.FLAG_DISABLED;
                writeUserLocked(user);
            }
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
        return true;
    
private intnumberOfUsersOfTypeLocked(int flags, boolean excludeDying)

        int count = 0;
        for (int i = mUsers.size() - 1; i >= 0; i--) {
            UserInfo user = mUsers.valueAt(i);
            if (!excludeDying || !mRemovingUserIds.get(user.id)) {
                if ((user.flags & flags) != 0) {
                    count++;
                }
            }
        }
        return count;
    
private java.lang.StringpackageToRestrictionsFileName(java.lang.String packageName)

        return RESTRICTIONS_FILE_PREFIX + packageName + XML_SUFFIX;
    
private java.lang.StringpasswordToHash(java.lang.String password, long salt)

        if (password == null) {
            return null;
        }
        String algo = null;
        String hashed = salt + password;
        try {
            byte[] saltedPassword = (password + salt).getBytes();
            byte[] sha1 = MessageDigest.getInstance(algo = "SHA-1").digest(saltedPassword);
            byte[] md5 = MessageDigest.getInstance(algo = "MD5").digest(saltedPassword);
            hashed = toHex(sha1) + toHex(md5);
        } catch (NoSuchAlgorithmException e) {
            Log.w(LOG_TAG, "Failed to encode string because of missing algorithm: " + algo);
        }
        return hashed;
    
private android.os.BundlereadApplicationRestrictionsLocked(java.lang.String packageName, int userId)

        final Bundle restrictions = new Bundle();
        final ArrayList<String> values = new ArrayList<String>();

        FileInputStream fis = null;
        try {
            AtomicFile restrictionsFile =
                    new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
                            packageToRestrictionsFileName(packageName)));
            fis = restrictionsFile.openRead();
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(fis, null);
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG
                    && type != XmlPullParser.END_DOCUMENT) {
                ;
            }

            if (type != XmlPullParser.START_TAG) {
                Slog.e(LOG_TAG, "Unable to read restrictions file "
                        + restrictionsFile.getBaseFile());
                return restrictions;
            }

            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
                    String key = parser.getAttributeValue(null, ATTR_KEY);
                    String valType = parser.getAttributeValue(null, ATTR_VALUE_TYPE);
                    String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
                    if (multiple != null) {
                        values.clear();
                        int count = Integer.parseInt(multiple);
                        while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                            if (type == XmlPullParser.START_TAG
                                    && parser.getName().equals(TAG_VALUE)) {
                                values.add(parser.nextText().trim());
                                count--;
                            }
                        }
                        String [] valueStrings = new String[values.size()];
                        values.toArray(valueStrings);
                        restrictions.putStringArray(key, valueStrings);
                    } else {
                        String value = parser.nextText().trim();
                        if (ATTR_TYPE_BOOLEAN.equals(valType)) {
                            restrictions.putBoolean(key, Boolean.parseBoolean(value));
                        } else if (ATTR_TYPE_INTEGER.equals(valType)) {
                            restrictions.putInt(key, Integer.parseInt(value));
                        } else {
                            restrictions.putString(key, value);
                        }
                    }
                }
            }
        } catch (IOException ioe) {
        } catch (XmlPullParserException pe) {
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
        return restrictions;
    
private voidreadBoolean(org.xmlpull.v1.XmlPullParser parser, android.os.Bundle restrictions, java.lang.String restrictionKey)

        String value = parser.getAttributeValue(null, restrictionKey);
        if (value != null) {
            restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
        }
    
private intreadIntAttribute(org.xmlpull.v1.XmlPullParser parser, java.lang.String attr, int defaultValue)

        String valueString = parser.getAttributeValue(null, attr);
        if (valueString == null) return defaultValue;
        try {
            return Integer.parseInt(valueString);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    
private longreadLongAttribute(org.xmlpull.v1.XmlPullParser parser, java.lang.String attr, long defaultValue)

        String valueString = parser.getAttributeValue(null, attr);
        if (valueString == null) return defaultValue;
        try {
            return Long.parseLong(valueString);
        } catch (NumberFormatException nfe) {
            return defaultValue;
        }
    
private voidreadRestrictionsLocked(org.xmlpull.v1.XmlPullParser parser, android.os.Bundle restrictions)

        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
        readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
        readBoolean(parser, restrictions,
                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
        readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_REMOVE_USER);
        readBoolean(parser, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_VPN);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
        readBoolean(parser, restrictions, UserManager.DISALLOW_FACTORY_RESET);
        readBoolean(parser, restrictions, UserManager.DISALLOW_ADD_USER);
        readBoolean(parser, restrictions, UserManager.ENSURE_VERIFY_APPS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_APPS_CONTROL);
        readBoolean(parser, restrictions,
                UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
        readBoolean(parser, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
        readBoolean(parser, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
        readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_SMS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
        readBoolean(parser, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
        readBoolean(parser, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
    
private voidreadUserListLocked()

        if (!mUserListFile.exists()) {
            fallbackToSingleUserLocked();
            return;
        }
        FileInputStream fis = null;
        AtomicFile userListFile = new AtomicFile(mUserListFile);
        try {
            fis = userListFile.openRead();
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(fis, null);
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG
                    && type != XmlPullParser.END_DOCUMENT) {
                ;
            }

            if (type != XmlPullParser.START_TAG) {
                Slog.e(LOG_TAG, "Unable to read user list");
                fallbackToSingleUserLocked();
                return;
            }

            mNextSerialNumber = -1;
            if (parser.getName().equals(TAG_USERS)) {
                String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
                if (lastSerialNumber != null) {
                    mNextSerialNumber = Integer.parseInt(lastSerialNumber);
                }
                String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);
                if (versionNumber != null) {
                    mUserVersion = Integer.parseInt(versionNumber);
                }
            }

            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
                if (type == XmlPullParser.START_TAG) {
                    final String name = parser.getName();
                    if (name.equals(TAG_USER)) {
                        String id = parser.getAttributeValue(null, ATTR_ID);
                        UserInfo user = readUserLocked(Integer.parseInt(id));

                        if (user != null) {
                            mUsers.put(user.id, user);
                            if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
                                mNextSerialNumber = user.id + 1;
                            }
                        }
                    } else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
                        while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                                && type != XmlPullParser.END_TAG) {
                            if (type == XmlPullParser.START_TAG) {
                                if (parser.getName().equals(TAG_RESTRICTIONS)) {
                                    readRestrictionsLocked(parser, mGuestRestrictions);
                                }
                                break;
                            }
                        }
                    }
                }
            }
            updateUserIdsLocked();
            upgradeIfNecessaryLocked();
        } catch (IOException ioe) {
            fallbackToSingleUserLocked();
        } catch (XmlPullParserException pe) {
            fallbackToSingleUserLocked();
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
    
private android.content.pm.UserInforeadUserLocked(int id)

        int flags = 0;
        int serialNumber = id;
        String name = null;
        String iconPath = null;
        long creationTime = 0L;
        long lastLoggedInTime = 0L;
        long salt = 0L;
        String pinHash = null;
        int failedAttempts = 0;
        int profileGroupId = UserInfo.NO_PROFILE_GROUP_ID;
        long lastAttemptTime = 0L;
        boolean partial = false;
        boolean guestToRemove = false;
        Bundle restrictions = new Bundle();

        FileInputStream fis = null;
        try {
            AtomicFile userFile =
                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + XML_SUFFIX));
            fis = userFile.openRead();
            XmlPullParser parser = Xml.newPullParser();
            parser.setInput(fis, null);
            int type;
            while ((type = parser.next()) != XmlPullParser.START_TAG
                    && type != XmlPullParser.END_DOCUMENT) {
                ;
            }

            if (type != XmlPullParser.START_TAG) {
                Slog.e(LOG_TAG, "Unable to read user " + id);
                return null;
            }

            if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
                int storedId = readIntAttribute(parser, ATTR_ID, -1);
                if (storedId != id) {
                    Slog.e(LOG_TAG, "User id does not match the file name");
                    return null;
                }
                serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);
                flags = readIntAttribute(parser, ATTR_FLAGS, 0);
                iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
                creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
                lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
                salt = readLongAttribute(parser, ATTR_SALT, 0L);
                pinHash = parser.getAttributeValue(null, ATTR_PIN_HASH);
                failedAttempts = readIntAttribute(parser, ATTR_FAILED_ATTEMPTS, 0);
                lastAttemptTime = readLongAttribute(parser, ATTR_LAST_RETRY_MS, 0L);
                profileGroupId = readIntAttribute(parser, ATTR_PROFILE_GROUP_ID,
                        UserInfo.NO_PROFILE_GROUP_ID);
                if (profileGroupId == UserInfo.NO_PROFILE_GROUP_ID) {
                    // This attribute was added and renamed during development of L.
                    // TODO Remove upgrade path by 1st May 2014
                    profileGroupId = readIntAttribute(parser, "relatedGroupId",
                            UserInfo.NO_PROFILE_GROUP_ID);
                }
                String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
                if ("true".equals(valueString)) {
                    partial = true;
                }
                valueString = parser.getAttributeValue(null, ATTR_GUEST_TO_REMOVE);
                if ("true".equals(valueString)) {
                    guestToRemove = true;
                }

                int outerDepth = parser.getDepth();
                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
                       && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
                        continue;
                    }
                    String tag = parser.getName();
                    if (TAG_NAME.equals(tag)) {
                        type = parser.next();
                        if (type == XmlPullParser.TEXT) {
                            name = parser.getText();
                        }
                    } else if (TAG_RESTRICTIONS.equals(tag)) {
                        readRestrictionsLocked(parser, restrictions);
                    }
                }
            }

            UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
            userInfo.serialNumber = serialNumber;
            userInfo.creationTime = creationTime;
            userInfo.lastLoggedInTime = lastLoggedInTime;
            userInfo.partial = partial;
            userInfo.guestToRemove = guestToRemove;
            userInfo.profileGroupId = profileGroupId;
            mUserRestrictions.append(id, restrictions);
            if (salt != 0L) {
                RestrictionsPinState pinState = mRestrictionsPinStates.get(id);
                if (pinState == null) {
                    pinState = new RestrictionsPinState();
                    mRestrictionsPinStates.put(id, pinState);
                }
                pinState.salt = salt;
                pinState.pinHash = pinHash;
                pinState.failedAttempts = failedAttempts;
                pinState.lastAttemptTime = lastAttemptTime;
            }
            return userInfo;

        } catch (IOException ioe) {
        } catch (XmlPullParserException pe) {
        } finally {
            if (fis != null) {
                try {
                    fis.close();
                } catch (IOException e) {
                }
            }
        }
        return null;
    
private voidremoveDirectoryRecursive(java.io.File parent)

        if (parent.isDirectory()) {
            String[] files = parent.list();
            for (String filename : files) {
                File child = new File(parent, filename);
                removeDirectoryRecursive(child);
            }
        }
        parent.delete();
    
public voidremoveRestrictions()

        checkManageUsersPermission("Only system can remove restrictions");
        final int userHandle = UserHandle.getCallingUserId();
        removeRestrictionsForUser(userHandle, true);
    
private voidremoveRestrictionsForUser(int userHandle, boolean unhideApps)

        synchronized (mPackagesLock) {
            // Remove all user restrictions
            setUserRestrictions(new Bundle(), userHandle);
            // Remove restrictions pin
            setRestrictionsChallenge(null);
            // Remove any app restrictions
            cleanAppRestrictions(userHandle);
        }
        if (unhideApps) {
            unhideAllInstalledAppsForUser(userHandle);
        }
    
public booleanremoveUser(int userHandle)
Removes a user and all data directories created for that user. This method should be called after the user's processes have been terminated.

param
userHandle the user's id

        checkManageUsersPermission("Only the system can remove users");
        if (getUserRestrictions(UserHandle.getCallingUserId()).getBoolean(
                UserManager.DISALLOW_REMOVE_USER, false)) {
            Log.w(LOG_TAG, "Cannot remove user. DISALLOW_REMOVE_USER is enabled.");
            return false;
        }

        long ident = Binder.clearCallingIdentity();
        try {
            final UserInfo user;
            synchronized (mPackagesLock) {
                user = mUsers.get(userHandle);
                if (userHandle == 0 || user == null || mRemovingUserIds.get(userHandle)) {
                    return false;
                }

                // We remember deleted user IDs to prevent them from being
                // reused during the current boot; they can still be reused
                // after a reboot.
                mRemovingUserIds.put(userHandle, true);

                try {
                    mAppOpsService.removeUser(userHandle);
                } catch (RemoteException e) {
                    Log.w(LOG_TAG, "Unable to notify AppOpsService of removing user", e);
                }
                // Set this to a partially created user, so that the user will be purged
                // on next startup, in case the runtime stops now before stopping and
                // removing the user completely.
                user.partial = true;
                // Mark it as disabled, so that it isn't returned any more when
                // profiles are queried.
                user.flags |= UserInfo.FLAG_DISABLED;
                writeUserLocked(user);
            }

            if (user.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID
                    && user.isManagedProfile()) {
                // Send broadcast to notify system that the user removed was a
                // managed user.
                sendProfileRemovedBroadcast(user.profileGroupId, user.id);
            }

            if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
            int res;
            try {
                res = ActivityManagerNative.getDefault().stopUser(userHandle,
                        new IStopUserCallback.Stub() {
                            @Override
                            public void userStopped(int userId) {
                                finishRemoveUser(userId);
                            }
                            @Override
                            public void userStopAborted(int userId) {
                            }
                        });
            } catch (RemoteException e) {
                return false;
            }
            return res == ActivityManager.USER_OP_SUCCESS;
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
private voidremoveUserStateLocked(int userHandle)

        // Cleanup package manager settings
        mPm.cleanUpUserLILPw(this, userHandle);

        // Remove this user from the list
        mUsers.remove(userHandle);

        mRestrictionsPinStates.remove(userHandle);
        // Remove user file
        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + XML_SUFFIX));
        userFile.delete();
        // Update the user list
        writeUserListLocked();
        updateUserIdsLocked();
        removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
    
private java.lang.StringrestrictionsFileNameToPackage(java.lang.String fileName)

        return fileName.substring(RESTRICTIONS_FILE_PREFIX.length(),
                (int) (fileName.length() - XML_SUFFIX.length()));
    
private voidsendProfileRemovedBroadcast(int parentUserId, int removedUserId)

        Intent managedProfileIntent = new Intent(Intent.ACTION_MANAGED_PROFILE_REMOVED);
        managedProfileIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY |
                Intent.FLAG_RECEIVER_FOREGROUND);
        managedProfileIntent.putExtra(Intent.EXTRA_USER, new UserHandle(removedUserId));
        mContext.sendBroadcastAsUser(managedProfileIntent, new UserHandle(parentUserId), null);
    
private voidsendUserInfoChangedBroadcast(int userId)

        Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
        changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
        changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
        mContext.sendBroadcastAsUser(changedIntent, UserHandle.ALL);
    
public voidsetApplicationRestrictions(java.lang.String packageName, android.os.Bundle restrictions, int userId)

        if (UserHandle.getCallingUserId() != userId
                || !UserHandle.isSameApp(Binder.getCallingUid(), getUidForPackage(packageName))) {
            checkManageUsersPermission("Only system can set restrictions for other users/apps");
        }
        synchronized (mPackagesLock) {
            if (restrictions == null || restrictions.isEmpty()) {
                cleanAppRestrictionsForPackage(packageName, userId);
            } else {
                // Write the restrictions to XML
                writeApplicationRestrictionsLocked(packageName, restrictions, userId);
            }
        }

        if (isPackageInstalled(packageName, userId)) {
            // Notify package of changes via an intent - only sent to explicitly registered receivers.
            Intent changeIntent = new Intent(Intent.ACTION_APPLICATION_RESTRICTIONS_CHANGED);
            changeIntent.setPackage(packageName);
            changeIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
            mContext.sendBroadcastAsUser(changeIntent, new UserHandle(userId));
        }
    
public voidsetDefaultGuestRestrictions(android.os.Bundle restrictions)

        checkManageUsersPermission("setDefaultGuestRestrictions");
        synchronized (mPackagesLock) {
            mGuestRestrictions.clear();
            mGuestRestrictions.putAll(restrictions);
            writeUserListLocked();
        }
    
public booleansetRestrictionsChallenge(java.lang.String newPin)

        checkManageUsersPermission("Only system can modify the restrictions pin");
        int userId = UserHandle.getCallingUserId();
        synchronized (mPackagesLock) {
            RestrictionsPinState pinState = mRestrictionsPinStates.get(userId);
            if (pinState == null) {
                pinState = new RestrictionsPinState();
            }
            if (newPin == null) {
                pinState.salt = 0;
                pinState.pinHash = null;
            } else {
                try {
                    pinState.salt = SecureRandom.getInstance("SHA1PRNG").nextLong();
                } catch (NoSuchAlgorithmException e) {
                    pinState.salt = (long) (Math.random() * Long.MAX_VALUE);
                }
                pinState.pinHash = passwordToHash(newPin, pinState.salt);
                pinState.failedAttempts = 0;
            }
            mRestrictionsPinStates.put(userId, pinState);
            writeUserLocked(mUsers.get(userId));
        }
        return true;
    
public voidsetUserEnabled(int userId)

        checkManageUsersPermission("enable user");
        synchronized (mPackagesLock) {
            UserInfo info = getUserInfoLocked(userId);
            if (info != null && !info.isEnabled()) {
                info.flags ^= UserInfo.FLAG_DISABLED;
                writeUserLocked(info);
            }
        }
    
public voidsetUserIcon(int userId, android.graphics.Bitmap bitmap)

        checkManageUsersPermission("update users");
        long ident = Binder.clearCallingIdentity();
        try {
            synchronized (mPackagesLock) {
                UserInfo info = mUsers.get(userId);
                if (info == null || info.partial) {
                    Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
                    return;
                }
                writeBitmapLocked(info, bitmap);
                writeUserLocked(info);
            }
            sendUserInfoChangedBroadcast(userId);
        } finally {
            Binder.restoreCallingIdentity(ident);
        }
    
public voidsetUserName(int userId, java.lang.String name)

        checkManageUsersPermission("rename users");
        boolean changed = false;
        synchronized (mPackagesLock) {
            UserInfo info = mUsers.get(userId);
            if (info == null || info.partial) {
                Slog.w(LOG_TAG, "setUserName: unknown user #" + userId);
                return;
            }
            if (name != null && !name.equals(info.name)) {
                info.name = name;
                writeUserLocked(info);
                changed = true;
            }
        }
        if (changed) {
            sendUserInfoChangedBroadcast(userId);
        }
    
public voidsetUserRestrictions(android.os.Bundle restrictions, int userId)

        checkManageUsersPermission("setUserRestrictions");
        if (restrictions == null) return;

        synchronized (mPackagesLock) {
            mUserRestrictions.get(userId).clear();
            mUserRestrictions.get(userId).putAll(restrictions);
            long token = Binder.clearCallingIdentity();
            try {
                mAppOpsService.setUserRestrictions(mUserRestrictions.get(userId), userId);
            } catch (RemoteException e) {
                Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
            } finally {
                Binder.restoreCallingIdentity(token);
            }
            writeUserLocked(mUsers.get(userId));
        }
    
voidsystemReady()

        userForeground(UserHandle.USER_OWNER);
        mAppOpsService = IAppOpsService.Stub.asInterface(
                ServiceManager.getService(Context.APP_OPS_SERVICE));
        for (int i = 0; i < mUserIds.length; ++i) {
            try {
                mAppOpsService.setUserRestrictions(mUserRestrictions.get(mUserIds[i]), mUserIds[i]);
            } catch (RemoteException e) {
                Log.w(LOG_TAG, "Unable to notify AppOpsService of UserRestrictions");
            }
        }
    
private static java.lang.StringtoHex(byte[] ary)

        final String hex = "0123456789ABCDEF";
        String ret = "";
        for (int i = 0; i < ary.length; i++) {
            ret += hex.charAt((ary[i] >> 4) & 0xf);
            ret += hex.charAt(ary[i] & 0xf);
        }
        return ret;
    
private voidunhideAllInstalledAppsForUser(int userHandle)

        mHandler.post(new Runnable() {
            @Override
            public void run() {
                List<ApplicationInfo> apps =
                        mPm.getInstalledApplications(PackageManager.GET_UNINSTALLED_PACKAGES,
                                userHandle).getList();
                final long ident = Binder.clearCallingIdentity();
                try {
                    for (ApplicationInfo appInfo : apps) {
                        if ((appInfo.flags & ApplicationInfo.FLAG_INSTALLED) != 0
                                && (appInfo.flags & ApplicationInfo.FLAG_HIDDEN) != 0) {
                            mPm.setApplicationHiddenSettingAsUser(appInfo.packageName, false,
                                    userHandle);
                        }
                    }
                } finally {
                    Binder.restoreCallingIdentity(ident);
                }
            }
        });
    
private voidupdateUserIdsLocked()
Caches the list of user ids in an array, adjusting the array size when necessary.

        int num = 0;
        for (int i = 0; i < mUsers.size(); i++) {
            if (!mUsers.valueAt(i).partial) {
                num++;
            }
        }
        final int[] newUsers = new int[num];
        int n = 0;
        for (int i = 0; i < mUsers.size(); i++) {
            if (!mUsers.valueAt(i).partial) {
                newUsers[n++] = mUsers.keyAt(i);
            }
        }
        mUserIds = newUsers;
    
private voidupgradeIfNecessaryLocked()
Upgrade steps between versions, either for fixing bugs or changing the data format.

        int userVersion = mUserVersion;
        if (userVersion < 1) {
            // Assign a proper name for the owner, if not initialized correctly before
            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
            if ("Primary".equals(user.name)) {
                user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
                writeUserLocked(user);
            }
            userVersion = 1;
        }

        if (userVersion < 2) {
            // Owner should be marked as initialized
            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
            if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
                user.flags |= UserInfo.FLAG_INITIALIZED;
                writeUserLocked(user);
            }
            userVersion = 2;
        }


        if (userVersion < 4) {
            userVersion = 4;
        }

        if (userVersion < 5) {
            initDefaultGuestRestrictions();
            userVersion = 5;
        }

        if (userVersion < USER_VERSION) {
            Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
                    + USER_VERSION);
        } else {
            mUserVersion = userVersion;
            writeUserListLocked();
        }
    
public voiduserForeground(int userId)
Make a note of the last started time of a user and do some cleanup.

param
userId the user that was just foregrounded

        synchronized (mPackagesLock) {
            UserInfo user = mUsers.get(userId);
            long now = System.currentTimeMillis();
            if (user == null || user.partial) {
                Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
                return;
            }
            if (now > EPOCH_PLUS_30_YEARS) {
                user.lastLoggedInTime = now;
                writeUserLocked(user);
            }
        }
    
private voidwriteApplicationRestrictionsLocked(java.lang.String packageName, android.os.Bundle restrictions, int userId)

        FileOutputStream fos = null;
        AtomicFile restrictionsFile = new AtomicFile(
                new File(Environment.getUserSystemDirectory(userId),
                        packageToRestrictionsFileName(packageName)));
        try {
            fos = restrictionsFile.startWrite();
            final BufferedOutputStream bos = new BufferedOutputStream(fos);

            // XmlSerializer serializer = XmlUtils.serializerInstance();
            final XmlSerializer serializer = new FastXmlSerializer();
            serializer.setOutput(bos, "utf-8");
            serializer.startDocument(null, true);
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

            serializer.startTag(null, TAG_RESTRICTIONS);

            for (String key : restrictions.keySet()) {
                Object value = restrictions.get(key);
                serializer.startTag(null, TAG_ENTRY);
                serializer.attribute(null, ATTR_KEY, key);

                if (value instanceof Boolean) {
                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_BOOLEAN);
                    serializer.text(value.toString());
                } else if (value instanceof Integer) {
                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_INTEGER);
                    serializer.text(value.toString());
                } else if (value == null || value instanceof String) {
                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING);
                    serializer.text(value != null ? (String) value : "");
                } else {
                    serializer.attribute(null, ATTR_VALUE_TYPE, ATTR_TYPE_STRING_ARRAY);
                    String[] values = (String[]) value;
                    serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
                    for (String choice : values) {
                        serializer.startTag(null, TAG_VALUE);
                        serializer.text(choice != null ? choice : "");
                        serializer.endTag(null, TAG_VALUE);
                    }
                }
                serializer.endTag(null, TAG_ENTRY);
            }

            serializer.endTag(null, TAG_RESTRICTIONS);

            serializer.endDocument();
            restrictionsFile.finishWrite(fos);
        } catch (Exception e) {
            restrictionsFile.failWrite(fos);
            Slog.e(LOG_TAG, "Error writing application restrictions list");
        }
    
private voidwriteBitmapLocked(android.content.pm.UserInfo info, android.graphics.Bitmap bitmap)

        try {
            File dir = new File(mUsersDir, Integer.toString(info.id));
            File file = new File(dir, USER_PHOTO_FILENAME);
            if (!dir.exists()) {
                dir.mkdir();
                FileUtils.setPermissions(
                        dir.getPath(),
                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
                        -1, -1);
            }
            FileOutputStream os;
            if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {
                info.iconPath = file.getAbsolutePath();
            }
            try {
                os.close();
            } catch (IOException ioe) {
                // What the ... !
            }
        } catch (FileNotFoundException e) {
            Slog.w(LOG_TAG, "Error setting photo for user ", e);
        }
    
private voidwriteBoolean(org.xmlpull.v1.XmlSerializer xml, android.os.Bundle restrictions, java.lang.String restrictionKey)

        if (restrictions.containsKey(restrictionKey)) {
            xml.attribute(null, restrictionKey,
                    Boolean.toString(restrictions.getBoolean(restrictionKey)));
        }
    
private voidwriteRestrictionsLocked(org.xmlpull.v1.XmlSerializer serializer, android.os.Bundle restrictions)

        serializer.startTag(null, TAG_RESTRICTIONS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
        writeBoolean(serializer, restrictions,
                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CREDENTIALS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_REMOVE_USER);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_DEBUGGING_FEATURES);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_VPN);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_TETHERING);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_FACTORY_RESET);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADD_USER);
        writeBoolean(serializer, restrictions, UserManager.ENSURE_VERIFY_APPS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_CELL_BROADCASTS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_MOBILE_NETWORKS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_APPS_CONTROL);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_MOUNT_PHYSICAL_MEDIA);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNMUTE_MICROPHONE);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_ADJUST_VOLUME);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_CALLS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_SMS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CREATE_WINDOWS);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
        writeBoolean(serializer, restrictions, UserManager.DISALLOW_OUTGOING_BEAM);
        serializer.endTag(null, TAG_RESTRICTIONS);
    
private voidwriteUserListLocked()

        FileOutputStream fos = null;
        AtomicFile userListFile = new AtomicFile(mUserListFile);
        try {
            fos = userListFile.startWrite();
            final BufferedOutputStream bos = new BufferedOutputStream(fos);

            // XmlSerializer serializer = XmlUtils.serializerInstance();
            final XmlSerializer serializer = new FastXmlSerializer();
            serializer.setOutput(bos, "utf-8");
            serializer.startDocument(null, true);
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

            serializer.startTag(null, TAG_USERS);
            serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
            serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));

            serializer.startTag(null, TAG_GUEST_RESTRICTIONS);
            writeRestrictionsLocked(serializer, mGuestRestrictions);
            serializer.endTag(null, TAG_GUEST_RESTRICTIONS);
            for (int i = 0; i < mUsers.size(); i++) {
                UserInfo user = mUsers.valueAt(i);
                serializer.startTag(null, TAG_USER);
                serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
                serializer.endTag(null, TAG_USER);
            }

            serializer.endTag(null, TAG_USERS);

            serializer.endDocument();
            userListFile.finishWrite(fos);
        } catch (Exception e) {
            userListFile.failWrite(fos);
            Slog.e(LOG_TAG, "Error writing user list");
        }
    
private voidwriteUserLocked(android.content.pm.UserInfo userInfo)

        FileOutputStream fos = null;
        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + XML_SUFFIX));
        try {
            fos = userFile.startWrite();
            final BufferedOutputStream bos = new BufferedOutputStream(fos);

            // XmlSerializer serializer = XmlUtils.serializerInstance();
            final XmlSerializer serializer = new FastXmlSerializer();
            serializer.setOutput(bos, "utf-8");
            serializer.startDocument(null, true);
            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);

            serializer.startTag(null, TAG_USER);
            serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
            serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
            serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
            serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
            serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
                    Long.toString(userInfo.lastLoggedInTime));
            RestrictionsPinState pinState = mRestrictionsPinStates.get(userInfo.id);
            if (pinState != null) {
                if (pinState.salt != 0) {
                    serializer.attribute(null, ATTR_SALT, Long.toString(pinState.salt));
                }
                if (pinState.pinHash != null) {
                    serializer.attribute(null, ATTR_PIN_HASH, pinState.pinHash);
                }
                if (pinState.failedAttempts != 0) {
                    serializer.attribute(null, ATTR_FAILED_ATTEMPTS,
                            Integer.toString(pinState.failedAttempts));
                    serializer.attribute(null, ATTR_LAST_RETRY_MS,
                            Long.toString(pinState.lastAttemptTime));
                }
            }
            if (userInfo.iconPath != null) {
                serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);
            }
            if (userInfo.partial) {
                serializer.attribute(null, ATTR_PARTIAL, "true");
            }
            if (userInfo.guestToRemove) {
                serializer.attribute(null, ATTR_GUEST_TO_REMOVE, "true");
            }
            if (userInfo.profileGroupId != UserInfo.NO_PROFILE_GROUP_ID) {
                serializer.attribute(null, ATTR_PROFILE_GROUP_ID,
                        Integer.toString(userInfo.profileGroupId));
            }

            serializer.startTag(null, TAG_NAME);
            serializer.text(userInfo.name);
            serializer.endTag(null, TAG_NAME);
            Bundle restrictions = mUserRestrictions.get(userInfo.id);
            if (restrictions != null) {
                writeRestrictionsLocked(serializer, restrictions);
            }
            serializer.endTag(null, TAG_USER);

            serializer.endDocument();
            userFile.finishWrite(fos);
        } catch (Exception ioe) {
            Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
            userFile.failWrite(fos);
        }