UserManagerService.java revision 634cf31345ae843392215237e741d76271a5cfed
1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.server.pm;
18
19import com.android.internal.util.ArrayUtils;
20import com.android.internal.util.FastXmlSerializer;
21
22import android.app.ActivityManager;
23import android.app.ActivityManagerNative;
24import android.app.IStopUserCallback;
25import android.content.Context;
26import android.content.Intent;
27import android.content.pm.PackageManager;
28import android.content.pm.UserInfo;
29import android.graphics.Bitmap;
30import android.graphics.BitmapFactory;
31import android.os.Binder;
32import android.os.Environment;
33import android.os.FileUtils;
34import android.os.IUserManager;
35import android.os.Process;
36import android.os.RemoteException;
37import android.os.UserHandle;
38import android.os.UserManager;
39import android.util.AtomicFile;
40import android.util.Slog;
41import android.util.SparseArray;
42import android.util.TimeUtils;
43import android.util.Xml;
44
45import java.io.BufferedOutputStream;
46import java.io.File;
47import java.io.FileDescriptor;
48import java.io.FileInputStream;
49import java.io.FileNotFoundException;
50import java.io.FileOutputStream;
51import java.io.IOException;
52import java.io.PrintWriter;
53import java.util.ArrayList;
54import java.util.HashSet;
55import java.util.List;
56
57import org.xmlpull.v1.XmlPullParser;
58import org.xmlpull.v1.XmlPullParserException;
59import org.xmlpull.v1.XmlSerializer;
60
61public class UserManagerService extends IUserManager.Stub {
62
63    private static final String LOG_TAG = "UserManagerService";
64
65    private static final String TAG_NAME = "name";
66    private static final String ATTR_FLAGS = "flags";
67    private static final String ATTR_ICON_PATH = "icon";
68    private static final String ATTR_ID = "id";
69    private static final String ATTR_CREATION_TIME = "created";
70    private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn";
71    private static final String ATTR_SERIAL_NO = "serialNumber";
72    private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
73    private static final String ATTR_PARTIAL = "partial";
74    private static final String TAG_USERS = "users";
75    private static final String TAG_USER = "user";
76
77    private static final String USER_INFO_DIR = "system" + File.separator + "users";
78    private static final String USER_LIST_FILENAME = "userlist.xml";
79    private static final String USER_PHOTO_FILENAME = "photo.png";
80
81    private static final int MIN_USER_ID = 10;
82
83    private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
84
85    private final Context mContext;
86    private final PackageManagerService mPm;
87    private final Object mInstallLock;
88    private final Object mPackagesLock;
89
90    private final File mUsersDir;
91    private final File mUserListFile;
92    private final File mBaseUserPath;
93
94    private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
95    private HashSet<Integer> mRemovingUserIds = new HashSet<Integer>();
96
97    private int[] mUserIds;
98    private boolean mGuestEnabled;
99    private int mNextSerialNumber;
100
101    private static UserManagerService sInstance;
102
103    public static UserManagerService getInstance() {
104        synchronized (UserManagerService.class) {
105            return sInstance;
106        }
107    }
108
109    /**
110     * Available for testing purposes.
111     */
112    UserManagerService(File dataDir, File baseUserPath) {
113        this(null, null, new Object(), new Object(), dataDir, baseUserPath);
114    }
115
116    /**
117     * Called by package manager to create the service.  This is closely
118     * associated with the package manager, and the given lock is the
119     * package manager's own lock.
120     */
121    UserManagerService(Context context, PackageManagerService pm,
122            Object installLock, Object packagesLock) {
123        this(context, pm, installLock, packagesLock,
124                Environment.getDataDirectory(),
125                new File(Environment.getDataDirectory(), "user"));
126    }
127
128    /**
129     * Available for testing purposes.
130     */
131    private UserManagerService(Context context, PackageManagerService pm,
132            Object installLock, Object packagesLock,
133            File dataDir, File baseUserPath) {
134        mContext = context;
135        mPm = pm;
136        mInstallLock = installLock;
137        mPackagesLock = packagesLock;
138        synchronized (mInstallLock) {
139            synchronized (mPackagesLock) {
140                mUsersDir = new File(dataDir, USER_INFO_DIR);
141                mUsersDir.mkdirs();
142                // Make zeroth user directory, for services to migrate their files to that location
143                File userZeroDir = new File(mUsersDir, "0");
144                userZeroDir.mkdirs();
145                mBaseUserPath = baseUserPath;
146                FileUtils.setPermissions(mUsersDir.toString(),
147                        FileUtils.S_IRWXU|FileUtils.S_IRWXG
148                        |FileUtils.S_IROTH|FileUtils.S_IXOTH,
149                        -1, -1);
150                mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
151                readUserListLocked();
152                // Prune out any partially created users.
153                ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
154                for (int i = 0; i < mUsers.size(); i++) {
155                    UserInfo ui = mUsers.valueAt(i);
156                    if (ui.partial && i != 0) {
157                        partials.add(ui);
158                    }
159                }
160                for (int i = 0; i < partials.size(); i++) {
161                    UserInfo ui = partials.get(i);
162                    Slog.w(LOG_TAG, "Removing partially created user #" + i
163                            + " (name=" + ui.name + ")");
164                    removeUserStateLocked(ui.id);
165                }
166                sInstance = this;
167            }
168        }
169    }
170
171    @Override
172    public List<UserInfo> getUsers(boolean excludeDying) {
173        checkManageUsersPermission("query users");
174        synchronized (mPackagesLock) {
175            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
176            for (int i = 0; i < mUsers.size(); i++) {
177                UserInfo ui = mUsers.valueAt(i);
178                if (ui.partial) {
179                    continue;
180                }
181                if (!excludeDying || !mRemovingUserIds.contains(ui.id)) {
182                    users.add(ui);
183                }
184            }
185            return users;
186        }
187    }
188
189    @Override
190    public UserInfo getUserInfo(int userId) {
191        checkManageUsersPermission("query user");
192        synchronized (mPackagesLock) {
193            return getUserInfoLocked(userId);
194        }
195    }
196
197    /*
198     * Should be locked on mUsers before calling this.
199     */
200    private UserInfo getUserInfoLocked(int userId) {
201        UserInfo ui = mUsers.get(userId);
202        if (ui != null && ui.partial) {
203            Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
204            return null;
205        }
206        return ui;
207    }
208
209    public boolean exists(int userId) {
210        synchronized (mPackagesLock) {
211            return ArrayUtils.contains(mUserIds, userId);
212        }
213    }
214
215    @Override
216    public void setUserName(int userId, String name) {
217        checkManageUsersPermission("rename users");
218        boolean changed = false;
219        synchronized (mPackagesLock) {
220            UserInfo info = mUsers.get(userId);
221            if (info == null || info.partial) {
222                Slog.w(LOG_TAG, "setUserName: unknown user #" + userId);
223                return;
224            }
225            if (name != null && !name.equals(info.name)) {
226                info.name = name;
227                writeUserLocked(info);
228                changed = true;
229            }
230        }
231        if (changed) {
232            sendUserInfoChangedBroadcast(userId);
233        }
234    }
235
236    @Override
237    public void setUserIcon(int userId, Bitmap bitmap) {
238        checkManageUsersPermission("update users");
239        synchronized (mPackagesLock) {
240            UserInfo info = mUsers.get(userId);
241            if (info == null || info.partial) {
242                Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
243                return;
244            }
245            writeBitmapLocked(info, bitmap);
246            writeUserLocked(info);
247        }
248        sendUserInfoChangedBroadcast(userId);
249    }
250
251    private void sendUserInfoChangedBroadcast(int userId) {
252        Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
253        changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
254        changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
255        mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId));
256    }
257
258    @Override
259    public Bitmap getUserIcon(int userId) {
260        checkManageUsersPermission("read users");
261        synchronized (mPackagesLock) {
262            UserInfo info = mUsers.get(userId);
263            if (info == null || info.partial) {
264                Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
265                return null;
266            }
267            if (info.iconPath == null) {
268                return null;
269            }
270            return BitmapFactory.decodeFile(info.iconPath);
271        }
272    }
273
274    @Override
275    public void setGuestEnabled(boolean enable) {
276        checkManageUsersPermission("enable guest users");
277        synchronized (mPackagesLock) {
278            if (mGuestEnabled != enable) {
279                mGuestEnabled = enable;
280                // Erase any guest user that currently exists
281                for (int i = 0; i < mUsers.size(); i++) {
282                    UserInfo user = mUsers.valueAt(i);
283                    if (!user.partial && user.isGuest()) {
284                        if (!enable) {
285                            removeUser(user.id);
286                        }
287                        return;
288                    }
289                }
290                // No guest was found
291                if (enable) {
292                    createUser("Guest", UserInfo.FLAG_GUEST);
293                }
294            }
295        }
296    }
297
298    @Override
299    public boolean isGuestEnabled() {
300        synchronized (mPackagesLock) {
301            return mGuestEnabled;
302        }
303    }
304
305    @Override
306    public void wipeUser(int userHandle) {
307        checkManageUsersPermission("wipe user");
308        // TODO:
309    }
310
311    public void makeInitialized(int userId) {
312        checkManageUsersPermission("makeInitialized");
313        synchronized (mPackagesLock) {
314            UserInfo info = mUsers.get(userId);
315            if (info == null || info.partial) {
316                Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
317            }
318            if ((info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
319                info.flags |= UserInfo.FLAG_INITIALIZED;
320                writeUserLocked(info);
321            }
322        }
323    }
324
325    /**
326     * Check if we've hit the limit of how many users can be created.
327     */
328    private boolean isUserLimitReachedLocked() {
329        int nUsers = mUsers.size();
330        return nUsers >= UserManager.getMaxSupportedUsers();
331    }
332
333    /**
334     * Enforces that only the system UID or root's UID or apps that have the
335     * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
336     * permission can make certain calls to the UserManager.
337     *
338     * @param message used as message if SecurityException is thrown
339     * @throws SecurityException if the caller is not system or root
340     */
341    private static final void checkManageUsersPermission(String message) {
342        final int uid = Binder.getCallingUid();
343        if (uid != Process.SYSTEM_UID && uid != 0
344                && ActivityManager.checkComponentPermission(
345                        android.Manifest.permission.MANAGE_USERS,
346                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
347            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
348        }
349    }
350
351    private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
352        try {
353            File dir = new File(mUsersDir, Integer.toString(info.id));
354            File file = new File(dir, USER_PHOTO_FILENAME);
355            if (!dir.exists()) {
356                dir.mkdir();
357                FileUtils.setPermissions(
358                        dir.getPath(),
359                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
360                        -1, -1);
361            }
362            FileOutputStream os;
363            if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {
364                info.iconPath = file.getAbsolutePath();
365            }
366            try {
367                os.close();
368            } catch (IOException ioe) {
369                // What the ... !
370            }
371        } catch (FileNotFoundException e) {
372            Slog.w(LOG_TAG, "Error setting photo for user ", e);
373        }
374    }
375
376    /**
377     * Returns an array of user ids. This array is cached here for quick access, so do not modify or
378     * cache it elsewhere.
379     * @return the array of user ids.
380     */
381    public int[] getUserIds() {
382        synchronized (mPackagesLock) {
383            return mUserIds;
384        }
385    }
386
387    int[] getUserIdsLPr() {
388        return mUserIds;
389    }
390
391    private void readUserList() {
392        synchronized (mPackagesLock) {
393            readUserListLocked();
394        }
395    }
396
397    private void readUserListLocked() {
398        mGuestEnabled = false;
399        if (!mUserListFile.exists()) {
400            fallbackToSingleUserLocked();
401            return;
402        }
403        FileInputStream fis = null;
404        AtomicFile userListFile = new AtomicFile(mUserListFile);
405        try {
406            fis = userListFile.openRead();
407            XmlPullParser parser = Xml.newPullParser();
408            parser.setInput(fis, null);
409            int type;
410            while ((type = parser.next()) != XmlPullParser.START_TAG
411                    && type != XmlPullParser.END_DOCUMENT) {
412                ;
413            }
414
415            if (type != XmlPullParser.START_TAG) {
416                Slog.e(LOG_TAG, "Unable to read user list");
417                fallbackToSingleUserLocked();
418                return;
419            }
420
421            mNextSerialNumber = -1;
422            if (parser.getName().equals(TAG_USERS)) {
423                String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
424                if (lastSerialNumber != null) {
425                    mNextSerialNumber = Integer.parseInt(lastSerialNumber);
426                }
427            }
428
429            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
430                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
431                    String id = parser.getAttributeValue(null, ATTR_ID);
432                    UserInfo user = readUser(Integer.parseInt(id));
433                    if (user != null) {
434                        mUsers.put(user.id, user);
435                        if (user.isGuest()) {
436                            mGuestEnabled = true;
437                        }
438                        if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
439                            mNextSerialNumber = user.id + 1;
440                        }
441                    }
442                }
443            }
444            updateUserIdsLocked();
445        } catch (IOException ioe) {
446            fallbackToSingleUserLocked();
447        } catch (XmlPullParserException pe) {
448            fallbackToSingleUserLocked();
449        } finally {
450            if (fis != null) {
451                try {
452                    fis.close();
453                } catch (IOException e) {
454                }
455            }
456        }
457    }
458
459    private void fallbackToSingleUserLocked() {
460        // Create the primary user
461        UserInfo primary = new UserInfo(0, "Primary", null,
462                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
463        mUsers.put(0, primary);
464        mNextSerialNumber = MIN_USER_ID;
465        updateUserIdsLocked();
466
467        writeUserListLocked();
468        writeUserLocked(primary);
469    }
470
471    /*
472     * Writes the user file in this format:
473     *
474     * <user flags="20039023" id="0">
475     *   <name>Primary</name>
476     * </user>
477     */
478    private void writeUserLocked(UserInfo userInfo) {
479        FileOutputStream fos = null;
480        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
481        try {
482            fos = userFile.startWrite();
483            final BufferedOutputStream bos = new BufferedOutputStream(fos);
484
485            // XmlSerializer serializer = XmlUtils.serializerInstance();
486            final XmlSerializer serializer = new FastXmlSerializer();
487            serializer.setOutput(bos, "utf-8");
488            serializer.startDocument(null, true);
489            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
490
491            serializer.startTag(null, TAG_USER);
492            serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
493            serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
494            serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
495            serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
496            serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
497                    Long.toString(userInfo.lastLoggedInTime));
498            if (userInfo.iconPath != null) {
499                serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);
500            }
501            if (userInfo.partial) {
502                serializer.attribute(null, ATTR_PARTIAL, "true");
503            }
504
505            serializer.startTag(null, TAG_NAME);
506            serializer.text(userInfo.name);
507            serializer.endTag(null, TAG_NAME);
508
509            serializer.endTag(null, TAG_USER);
510
511            serializer.endDocument();
512            userFile.finishWrite(fos);
513        } catch (Exception ioe) {
514            Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
515            userFile.failWrite(fos);
516        }
517    }
518
519    /*
520     * Writes the user list file in this format:
521     *
522     * <users nextSerialNumber="3">
523     *   <user id="0"></user>
524     *   <user id="2"></user>
525     * </users>
526     */
527    private void writeUserListLocked() {
528        FileOutputStream fos = null;
529        AtomicFile userListFile = new AtomicFile(mUserListFile);
530        try {
531            fos = userListFile.startWrite();
532            final BufferedOutputStream bos = new BufferedOutputStream(fos);
533
534            // XmlSerializer serializer = XmlUtils.serializerInstance();
535            final XmlSerializer serializer = new FastXmlSerializer();
536            serializer.setOutput(bos, "utf-8");
537            serializer.startDocument(null, true);
538            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
539
540            serializer.startTag(null, TAG_USERS);
541            serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
542
543            for (int i = 0; i < mUsers.size(); i++) {
544                UserInfo user = mUsers.valueAt(i);
545                serializer.startTag(null, TAG_USER);
546                serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
547                serializer.endTag(null, TAG_USER);
548            }
549
550            serializer.endTag(null, TAG_USERS);
551
552            serializer.endDocument();
553            userListFile.finishWrite(fos);
554        } catch (Exception e) {
555            userListFile.failWrite(fos);
556            Slog.e(LOG_TAG, "Error writing user list");
557        }
558    }
559
560    private UserInfo readUser(int id) {
561        int flags = 0;
562        int serialNumber = id;
563        String name = null;
564        String iconPath = null;
565        long creationTime = 0L;
566        long lastLoggedInTime = 0L;
567        boolean partial = false;
568
569        FileInputStream fis = null;
570        try {
571            AtomicFile userFile =
572                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
573            fis = userFile.openRead();
574            XmlPullParser parser = Xml.newPullParser();
575            parser.setInput(fis, null);
576            int type;
577            while ((type = parser.next()) != XmlPullParser.START_TAG
578                    && type != XmlPullParser.END_DOCUMENT) {
579                ;
580            }
581
582            if (type != XmlPullParser.START_TAG) {
583                Slog.e(LOG_TAG, "Unable to read user " + id);
584                return null;
585            }
586
587            if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
588                int storedId = readIntAttribute(parser, ATTR_ID, -1);
589                if (storedId != id) {
590                    Slog.e(LOG_TAG, "User id does not match the file name");
591                    return null;
592                }
593                serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);
594                flags = readIntAttribute(parser, ATTR_FLAGS, 0);
595                iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
596                creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
597                lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
598                String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
599                if ("true".equals(valueString)) {
600                    partial = true;
601                }
602
603                while ((type = parser.next()) != XmlPullParser.START_TAG
604                        && type != XmlPullParser.END_DOCUMENT) {
605                }
606                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
607                    type = parser.next();
608                    if (type == XmlPullParser.TEXT) {
609                        name = parser.getText();
610                    }
611                }
612            }
613
614            UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
615            userInfo.serialNumber = serialNumber;
616            userInfo.creationTime = creationTime;
617            userInfo.lastLoggedInTime = lastLoggedInTime;
618            userInfo.partial = partial;
619            return userInfo;
620
621        } catch (IOException ioe) {
622        } catch (XmlPullParserException pe) {
623        } finally {
624            if (fis != null) {
625                try {
626                    fis.close();
627                } catch (IOException e) {
628                }
629            }
630        }
631        return null;
632    }
633
634    private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
635        String valueString = parser.getAttributeValue(null, attr);
636        if (valueString == null) return defaultValue;
637        try {
638            return Integer.parseInt(valueString);
639        } catch (NumberFormatException nfe) {
640            return defaultValue;
641        }
642    }
643
644    private long readLongAttribute(XmlPullParser parser, String attr, long defaultValue) {
645        String valueString = parser.getAttributeValue(null, attr);
646        if (valueString == null) return defaultValue;
647        try {
648            return Long.parseLong(valueString);
649        } catch (NumberFormatException nfe) {
650            return defaultValue;
651        }
652    }
653
654    @Override
655    public UserInfo createUser(String name, int flags) {
656        checkManageUsersPermission("Only the system can create users");
657
658        final long ident = Binder.clearCallingIdentity();
659        final UserInfo userInfo;
660        try {
661            synchronized (mInstallLock) {
662                synchronized (mPackagesLock) {
663                    if (isUserLimitReachedLocked()) return null;
664                    int userId = getNextAvailableIdLocked();
665                    userInfo = new UserInfo(userId, name, null, flags);
666                    File userPath = new File(mBaseUserPath, Integer.toString(userId));
667                    userInfo.serialNumber = mNextSerialNumber++;
668                    long now = System.currentTimeMillis();
669                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
670                    userInfo.partial = true;
671                    mUsers.put(userId, userInfo);
672                    writeUserListLocked();
673                    writeUserLocked(userInfo);
674                    mPm.createNewUserLILPw(userId, userPath);
675                    userInfo.partial = false;
676                    writeUserLocked(userInfo);
677                    updateUserIdsLocked();
678                }
679            }
680            if (userInfo != null) {
681                Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
682                addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
683                mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
684                        android.Manifest.permission.MANAGE_USERS);
685            }
686        } finally {
687            Binder.restoreCallingIdentity(ident);
688        }
689        return userInfo;
690    }
691
692    /**
693     * Removes a user and all data directories created for that user. This method should be called
694     * after the user's processes have been terminated.
695     * @param id the user's id
696     */
697    public boolean removeUser(int userHandle) {
698        checkManageUsersPermission("Only the system can remove users");
699        final UserInfo user;
700        synchronized (mPackagesLock) {
701            user = mUsers.get(userHandle);
702            if (userHandle == 0 || user == null) {
703                return false;
704            }
705            mRemovingUserIds.add(userHandle);
706        }
707
708        int res;
709        try {
710            res = ActivityManagerNative.getDefault().stopUser(userHandle,
711                    new IStopUserCallback.Stub() {
712                        @Override
713                        public void userStopped(int userId) {
714                            finishRemoveUser(userId);
715                        }
716                        @Override
717                        public void userStopAborted(int userId) {
718                        }
719            });
720        } catch (RemoteException e) {
721            return false;
722        }
723
724        return res == ActivityManager.USER_OP_SUCCESS;
725    }
726
727    void finishRemoveUser(int userHandle) {
728        synchronized (mInstallLock) {
729            synchronized (mPackagesLock) {
730                removeUserStateLocked(userHandle);
731            }
732        }
733
734        // Let other services shutdown any activity
735        long ident = Binder.clearCallingIdentity();
736        try {
737            Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
738            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
739            mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
740                    android.Manifest.permission.MANAGE_USERS);
741        } finally {
742            Binder.restoreCallingIdentity(ident);
743        }
744    }
745
746    private void removeUserStateLocked(int userHandle) {
747        // Cleanup package manager settings
748        mPm.cleanUpUserLILPw(userHandle);
749
750        // Remove this user from the list
751        mUsers.remove(userHandle);
752        mRemovingUserIds.remove(userHandle);
753        // Remove user file
754        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
755        userFile.delete();
756        // Update the user list
757        writeUserListLocked();
758        updateUserIdsLocked();
759        removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
760    }
761
762    private void removeDirectoryRecursive(File parent) {
763        if (parent.isDirectory()) {
764            String[] files = parent.list();
765            for (String filename : files) {
766                File child = new File(parent, filename);
767                removeDirectoryRecursive(child);
768            }
769        }
770        parent.delete();
771    }
772
773    @Override
774    public int getUserSerialNumber(int userHandle) {
775        synchronized (mPackagesLock) {
776            if (!exists(userHandle)) return -1;
777            return getUserInfoLocked(userHandle).serialNumber;
778        }
779    }
780
781    @Override
782    public int getUserHandle(int userSerialNumber) {
783        synchronized (mPackagesLock) {
784            for (int userId : mUserIds) {
785                if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
786            }
787            // Not found
788            return -1;
789        }
790    }
791
792    /**
793     * Caches the list of user ids in an array, adjusting the array size when necessary.
794     */
795    private void updateUserIdsLocked() {
796        int num = 0;
797        for (int i = 0; i < mUsers.size(); i++) {
798            if (!mUsers.valueAt(i).partial) {
799                num++;
800            }
801        }
802        int[] newUsers = new int[num];
803        for (int i = 0; i < mUsers.size(); i++) {
804            if (!mUsers.valueAt(i).partial) {
805                newUsers[i] = mUsers.keyAt(i);
806            }
807        }
808        mUserIds = newUsers;
809    }
810
811    /**
812     * Make a note of the last started time of a user.
813     * @param userId the user that was just foregrounded
814     */
815    public void userForeground(int userId) {
816        synchronized (mPackagesLock) {
817            UserInfo user = mUsers.get(userId);
818            long now = System.currentTimeMillis();
819            if (user == null || user.partial) {
820                Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
821                return;
822            }
823            if (now > EPOCH_PLUS_30_YEARS) {
824                user.lastLoggedInTime = now;
825                writeUserLocked(user);
826            }
827        }
828    }
829
830    /**
831     * Returns the next available user id, filling in any holes in the ids.
832     * TODO: May not be a good idea to recycle ids, in case it results in confusion
833     * for data and battery stats collection, or unexpected cross-talk.
834     * @return
835     */
836    private int getNextAvailableIdLocked() {
837        synchronized (mPackagesLock) {
838            int i = MIN_USER_ID;
839            while (i < Integer.MAX_VALUE) {
840                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.contains(i)) {
841                    break;
842                }
843                i++;
844            }
845            return i;
846        }
847    }
848
849    @Override
850    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
851        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
852                != PackageManager.PERMISSION_GRANTED) {
853            pw.println("Permission Denial: can't dump UserManager from from pid="
854                    + Binder.getCallingPid()
855                    + ", uid=" + Binder.getCallingUid()
856                    + " without permission "
857                    + android.Manifest.permission.DUMP);
858            return;
859        }
860
861        long now = System.currentTimeMillis();
862        StringBuilder sb = new StringBuilder();
863        synchronized (mPackagesLock) {
864            pw.println("Users:");
865            for (int i = 0; i < mUsers.size(); i++) {
866                UserInfo user = mUsers.valueAt(i);
867                if (user == null) continue;
868                pw.print("  "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
869                if (mRemovingUserIds.contains(mUsers.keyAt(i))) pw.print(" <removing> ");
870                if (user.partial) pw.print(" <partial>");
871                pw.println();
872                pw.print("    Created: ");
873                if (user.creationTime == 0) {
874                    pw.println("<unknown>");
875                } else {
876                    sb.setLength(0);
877                    TimeUtils.formatDuration(now - user.creationTime, sb);
878                    sb.append(" ago");
879                    pw.println(sb);
880                }
881                pw.print("    Last logged in: ");
882                if (user.lastLoggedInTime == 0) {
883                    pw.println("<unknown>");
884                } else {
885                    sb.setLength(0);
886                    TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
887                    sb.append(" ago");
888                    pw.println(sb);
889                }
890            }
891        }
892    }
893}
894