UserManagerService.java revision 86118baa4fef80c485ba51c6985a6fa082b7310c
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 static android.text.format.DateUtils.MINUTE_IN_MILLIS;
20
21import android.app.Activity;
22import android.app.ActivityManager;
23import android.app.ActivityManagerNative;
24import android.app.IStopUserCallback;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.Intent;
28import android.content.RestrictionEntry;
29import android.content.pm.PackageManager;
30import android.content.pm.PackageManager.NameNotFoundException;
31import android.content.pm.UserInfo;
32import android.graphics.Bitmap;
33import android.graphics.BitmapFactory;
34import android.os.Binder;
35import android.os.Bundle;
36import android.os.Environment;
37import android.os.FileUtils;
38import android.os.Handler;
39import android.os.IUserManager;
40import android.os.Process;
41import android.os.RemoteException;
42import android.os.UserHandle;
43import android.os.UserManager;
44import android.util.AtomicFile;
45import android.util.Slog;
46import android.util.SparseArray;
47import android.util.SparseBooleanArray;
48import android.util.TimeUtils;
49import android.util.Xml;
50
51import com.android.internal.util.ArrayUtils;
52import com.android.internal.util.FastXmlSerializer;
53
54import org.xmlpull.v1.XmlPullParser;
55import org.xmlpull.v1.XmlPullParserException;
56import org.xmlpull.v1.XmlSerializer;
57
58import java.io.BufferedOutputStream;
59import java.io.File;
60import java.io.FileDescriptor;
61import java.io.FileInputStream;
62import java.io.FileNotFoundException;
63import java.io.FileOutputStream;
64import java.io.IOException;
65import java.io.PrintWriter;
66import java.util.ArrayList;
67import java.util.List;
68
69public class UserManagerService extends IUserManager.Stub {
70
71    private static final String LOG_TAG = "UserManagerService";
72
73    private static final boolean DBG = false;
74
75    private static final String TAG_NAME = "name";
76    private static final String ATTR_FLAGS = "flags";
77    private static final String ATTR_ICON_PATH = "icon";
78    private static final String ATTR_ID = "id";
79    private static final String ATTR_CREATION_TIME = "created";
80    private static final String ATTR_LAST_LOGGED_IN_TIME = "lastLoggedIn";
81    private static final String ATTR_SERIAL_NO = "serialNumber";
82    private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
83    private static final String ATTR_PARTIAL = "partial";
84    private static final String ATTR_USER_VERSION = "version";
85    private static final String TAG_USERS = "users";
86    private static final String TAG_USER = "user";
87    private static final String TAG_RESTRICTIONS = "restrictions";
88    private static final String TAG_ENTRY = "entry";
89    private static final String TAG_VALUE = "value";
90    private static final String ATTR_KEY = "key";
91    private static final String ATTR_MULTIPLE = "m";
92
93    private static final String USER_INFO_DIR = "system" + File.separator + "users";
94    private static final String USER_LIST_FILENAME = "userlist.xml";
95    private static final String USER_PHOTO_FILENAME = "photo.png";
96
97    private static final String RESTRICTIONS_FILE_PREFIX = "res_";
98
99    private static final int MIN_USER_ID = 10;
100
101    private static final int USER_VERSION = 2;
102
103    private static final long EPOCH_PLUS_30_YEARS = 30L * 365 * 24 * 60 * 60 * 1000L; // ms
104
105    private final Context mContext;
106    private final PackageManagerService mPm;
107    private final Object mInstallLock;
108    private final Object mPackagesLock;
109
110    private final Handler mHandler;
111
112    private final File mUsersDir;
113    private final File mUserListFile;
114    private final File mBaseUserPath;
115
116    private final SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
117    private final SparseArray<Bundle> mUserRestrictions = new SparseArray<Bundle>();
118
119    /**
120     * Set of user IDs being actively removed. Removed IDs linger in this set
121     * for several seconds to work around a VFS caching issue.
122     */
123    // @GuardedBy("mPackagesLock")
124    private final SparseBooleanArray mRemovingUserIds = new SparseBooleanArray();
125
126    private int[] mUserIds;
127    private boolean mGuestEnabled;
128    private int mNextSerialNumber;
129    private int mUserVersion = 0;
130
131    private static UserManagerService sInstance;
132
133    public static UserManagerService getInstance() {
134        synchronized (UserManagerService.class) {
135            return sInstance;
136        }
137    }
138
139    /**
140     * Available for testing purposes.
141     */
142    UserManagerService(File dataDir, File baseUserPath) {
143        this(null, null, new Object(), new Object(), dataDir, baseUserPath);
144    }
145
146    /**
147     * Called by package manager to create the service.  This is closely
148     * associated with the package manager, and the given lock is the
149     * package manager's own lock.
150     */
151    UserManagerService(Context context, PackageManagerService pm,
152            Object installLock, Object packagesLock) {
153        this(context, pm, installLock, packagesLock,
154                Environment.getDataDirectory(),
155                new File(Environment.getDataDirectory(), "user"));
156    }
157
158    /**
159     * Available for testing purposes.
160     */
161    private UserManagerService(Context context, PackageManagerService pm,
162            Object installLock, Object packagesLock,
163            File dataDir, File baseUserPath) {
164        mContext = context;
165        mPm = pm;
166        mInstallLock = installLock;
167        mPackagesLock = packagesLock;
168        mHandler = new Handler();
169        synchronized (mInstallLock) {
170            synchronized (mPackagesLock) {
171                mUsersDir = new File(dataDir, USER_INFO_DIR);
172                mUsersDir.mkdirs();
173                // Make zeroth user directory, for services to migrate their files to that location
174                File userZeroDir = new File(mUsersDir, "0");
175                userZeroDir.mkdirs();
176                mBaseUserPath = baseUserPath;
177                FileUtils.setPermissions(mUsersDir.toString(),
178                        FileUtils.S_IRWXU|FileUtils.S_IRWXG
179                        |FileUtils.S_IROTH|FileUtils.S_IXOTH,
180                        -1, -1);
181                mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
182                readUserListLocked();
183                // Prune out any partially created/partially removed users.
184                ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
185                for (int i = 0; i < mUsers.size(); i++) {
186                    UserInfo ui = mUsers.valueAt(i);
187                    if (ui.partial && i != 0) {
188                        partials.add(ui);
189                    }
190                }
191                for (int i = 0; i < partials.size(); i++) {
192                    UserInfo ui = partials.get(i);
193                    Slog.w(LOG_TAG, "Removing partially created user #" + i
194                            + " (name=" + ui.name + ")");
195                    removeUserStateLocked(ui.id);
196                }
197                sInstance = this;
198            }
199        }
200    }
201
202    @Override
203    public List<UserInfo> getUsers(boolean excludeDying) {
204        checkManageUsersPermission("query users");
205        synchronized (mPackagesLock) {
206            ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
207            for (int i = 0; i < mUsers.size(); i++) {
208                UserInfo ui = mUsers.valueAt(i);
209                if (ui.partial) {
210                    continue;
211                }
212                if (!excludeDying || !mRemovingUserIds.get(ui.id)) {
213                    users.add(ui);
214                }
215            }
216            return users;
217        }
218    }
219
220    @Override
221    public UserInfo getUserInfo(int userId) {
222        checkManageUsersPermission("query user");
223        synchronized (mPackagesLock) {
224            return getUserInfoLocked(userId);
225        }
226    }
227
228    @Override
229    public boolean isRestricted() {
230        synchronized (mPackagesLock) {
231            return getUserInfoLocked(UserHandle.getCallingUserId()).isRestricted();
232        }
233    }
234
235    /*
236     * Should be locked on mUsers before calling this.
237     */
238    private UserInfo getUserInfoLocked(int userId) {
239        UserInfo ui = mUsers.get(userId);
240        // If it is partial and not in the process of being removed, return as unknown user.
241        if (ui != null && ui.partial && !mRemovingUserIds.get(userId)) {
242            Slog.w(LOG_TAG, "getUserInfo: unknown user #" + userId);
243            return null;
244        }
245        return ui;
246    }
247
248    public boolean exists(int userId) {
249        synchronized (mPackagesLock) {
250            return ArrayUtils.contains(mUserIds, userId);
251        }
252    }
253
254    @Override
255    public void setUserName(int userId, String name) {
256        checkManageUsersPermission("rename users");
257        boolean changed = false;
258        synchronized (mPackagesLock) {
259            UserInfo info = mUsers.get(userId);
260            if (info == null || info.partial) {
261                Slog.w(LOG_TAG, "setUserName: unknown user #" + userId);
262                return;
263            }
264            if (name != null && !name.equals(info.name)) {
265                info.name = name;
266                writeUserLocked(info);
267                changed = true;
268            }
269        }
270        if (changed) {
271            sendUserInfoChangedBroadcast(userId);
272        }
273    }
274
275    @Override
276    public void setUserIcon(int userId, Bitmap bitmap) {
277        checkManageUsersPermission("update users");
278        synchronized (mPackagesLock) {
279            UserInfo info = mUsers.get(userId);
280            if (info == null || info.partial) {
281                Slog.w(LOG_TAG, "setUserIcon: unknown user #" + userId);
282                return;
283            }
284            writeBitmapLocked(info, bitmap);
285            writeUserLocked(info);
286        }
287        sendUserInfoChangedBroadcast(userId);
288    }
289
290    private void sendUserInfoChangedBroadcast(int userId) {
291        Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
292        changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
293        changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
294        mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId));
295    }
296
297    @Override
298    public Bitmap getUserIcon(int userId) {
299        checkManageUsersPermission("read users");
300        synchronized (mPackagesLock) {
301            UserInfo info = mUsers.get(userId);
302            if (info == null || info.partial) {
303                Slog.w(LOG_TAG, "getUserIcon: unknown user #" + userId);
304                return null;
305            }
306            if (info.iconPath == null) {
307                return null;
308            }
309            return BitmapFactory.decodeFile(info.iconPath);
310        }
311    }
312
313    @Override
314    public void setGuestEnabled(boolean enable) {
315        checkManageUsersPermission("enable guest users");
316        synchronized (mPackagesLock) {
317            if (mGuestEnabled != enable) {
318                mGuestEnabled = enable;
319                // Erase any guest user that currently exists
320                for (int i = 0; i < mUsers.size(); i++) {
321                    UserInfo user = mUsers.valueAt(i);
322                    if (!user.partial && user.isGuest()) {
323                        if (!enable) {
324                            removeUser(user.id);
325                        }
326                        return;
327                    }
328                }
329                // No guest was found
330                if (enable) {
331                    createUser("Guest", UserInfo.FLAG_GUEST);
332                }
333            }
334        }
335    }
336
337    @Override
338    public boolean isGuestEnabled() {
339        synchronized (mPackagesLock) {
340            return mGuestEnabled;
341        }
342    }
343
344    @Override
345    public void wipeUser(int userHandle) {
346        checkManageUsersPermission("wipe user");
347        // TODO:
348    }
349
350    public void makeInitialized(int userId) {
351        checkManageUsersPermission("makeInitialized");
352        synchronized (mPackagesLock) {
353            UserInfo info = mUsers.get(userId);
354            if (info == null || info.partial) {
355                Slog.w(LOG_TAG, "makeInitialized: unknown user #" + userId);
356            }
357            if ((info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
358                info.flags |= UserInfo.FLAG_INITIALIZED;
359                writeUserLocked(info);
360            }
361        }
362    }
363
364    @Override
365    public Bundle getUserRestrictions(int userId) {
366        // checkManageUsersPermission("getUserRestrictions");
367
368        synchronized (mPackagesLock) {
369            Bundle restrictions = mUserRestrictions.get(userId);
370            return restrictions != null ? restrictions : Bundle.EMPTY;
371        }
372    }
373
374    @Override
375    public void setUserRestrictions(Bundle restrictions, int userId) {
376        checkManageUsersPermission("setUserRestrictions");
377
378        synchronized (mPackagesLock) {
379            mUserRestrictions.get(userId).putAll(restrictions);
380            writeUserLocked(mUsers.get(userId));
381        }
382    }
383
384    /**
385     * Check if we've hit the limit of how many users can be created.
386     */
387    private boolean isUserLimitReachedLocked() {
388        int nUsers = mUsers.size();
389        return nUsers >= UserManager.getMaxSupportedUsers();
390    }
391
392    /**
393     * Enforces that only the system UID or root's UID or apps that have the
394     * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
395     * permission can make certain calls to the UserManager.
396     *
397     * @param message used as message if SecurityException is thrown
398     * @throws SecurityException if the caller is not system or root
399     */
400    private static final void checkManageUsersPermission(String message) {
401        final int uid = Binder.getCallingUid();
402        if (uid != Process.SYSTEM_UID && uid != 0
403                && ActivityManager.checkComponentPermission(
404                        android.Manifest.permission.MANAGE_USERS,
405                        uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
406            throw new SecurityException("You need MANAGE_USERS permission to: " + message);
407        }
408    }
409
410    private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
411        try {
412            File dir = new File(mUsersDir, Integer.toString(info.id));
413            File file = new File(dir, USER_PHOTO_FILENAME);
414            if (!dir.exists()) {
415                dir.mkdir();
416                FileUtils.setPermissions(
417                        dir.getPath(),
418                        FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
419                        -1, -1);
420            }
421            FileOutputStream os;
422            if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {
423                info.iconPath = file.getAbsolutePath();
424            }
425            try {
426                os.close();
427            } catch (IOException ioe) {
428                // What the ... !
429            }
430        } catch (FileNotFoundException e) {
431            Slog.w(LOG_TAG, "Error setting photo for user ", e);
432        }
433    }
434
435    /**
436     * Returns an array of user ids. This array is cached here for quick access, so do not modify or
437     * cache it elsewhere.
438     * @return the array of user ids.
439     */
440    public int[] getUserIds() {
441        synchronized (mPackagesLock) {
442            return mUserIds;
443        }
444    }
445
446    int[] getUserIdsLPr() {
447        return mUserIds;
448    }
449
450    private void readUserList() {
451        synchronized (mPackagesLock) {
452            readUserListLocked();
453        }
454    }
455
456    private void readUserListLocked() {
457        mGuestEnabled = false;
458        if (!mUserListFile.exists()) {
459            fallbackToSingleUserLocked();
460            return;
461        }
462        FileInputStream fis = null;
463        AtomicFile userListFile = new AtomicFile(mUserListFile);
464        try {
465            fis = userListFile.openRead();
466            XmlPullParser parser = Xml.newPullParser();
467            parser.setInput(fis, null);
468            int type;
469            while ((type = parser.next()) != XmlPullParser.START_TAG
470                    && type != XmlPullParser.END_DOCUMENT) {
471                ;
472            }
473
474            if (type != XmlPullParser.START_TAG) {
475                Slog.e(LOG_TAG, "Unable to read user list");
476                fallbackToSingleUserLocked();
477                return;
478            }
479
480            mNextSerialNumber = -1;
481            if (parser.getName().equals(TAG_USERS)) {
482                String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
483                if (lastSerialNumber != null) {
484                    mNextSerialNumber = Integer.parseInt(lastSerialNumber);
485                }
486                String versionNumber = parser.getAttributeValue(null, ATTR_USER_VERSION);
487                if (versionNumber != null) {
488                    mUserVersion = Integer.parseInt(versionNumber);
489                }
490            }
491
492            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
493                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
494                    String id = parser.getAttributeValue(null, ATTR_ID);
495                    UserInfo user = readUserLocked(Integer.parseInt(id));
496
497                    if (user != null) {
498                        mUsers.put(user.id, user);
499                        if (user.isGuest()) {
500                            mGuestEnabled = true;
501                        }
502                        if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
503                            mNextSerialNumber = user.id + 1;
504                        }
505                    }
506                }
507            }
508            updateUserIdsLocked();
509            upgradeIfNecessary();
510        } catch (IOException ioe) {
511            fallbackToSingleUserLocked();
512        } catch (XmlPullParserException pe) {
513            fallbackToSingleUserLocked();
514        } finally {
515            if (fis != null) {
516                try {
517                    fis.close();
518                } catch (IOException e) {
519                }
520            }
521        }
522    }
523
524    /**
525     * Upgrade steps between versions, either for fixing bugs or changing the data format.
526     */
527    private void upgradeIfNecessary() {
528        int userVersion = mUserVersion;
529        if (userVersion < 1) {
530            // Assign a proper name for the owner, if not initialized correctly before
531            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
532            if ("Primary".equals(user.name)) {
533                user.name = mContext.getResources().getString(com.android.internal.R.string.owner_name);
534                writeUserLocked(user);
535            }
536            userVersion = 1;
537        }
538
539        if (userVersion < 2) {
540            // Owner should be marked as initialized
541            UserInfo user = mUsers.get(UserHandle.USER_OWNER);
542            if ((user.flags & UserInfo.FLAG_INITIALIZED) == 0) {
543                user.flags |= UserInfo.FLAG_INITIALIZED;
544                writeUserLocked(user);
545            }
546            userVersion = 2;
547        }
548
549        if (userVersion < USER_VERSION) {
550            Slog.w(LOG_TAG, "User version " + mUserVersion + " didn't upgrade as expected to "
551                    + USER_VERSION);
552        } else {
553            mUserVersion = userVersion;
554            writeUserListLocked();
555        }
556    }
557
558    private void fallbackToSingleUserLocked() {
559        // Create the primary user
560        UserInfo primary = new UserInfo(UserHandle.USER_OWNER,
561                mContext.getResources().getString(com.android.internal.R.string.owner_name), null,
562                UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY | UserInfo.FLAG_INITIALIZED);
563        mUsers.put(0, primary);
564        mNextSerialNumber = MIN_USER_ID;
565
566        Bundle restrictions = new Bundle();
567        mUserRestrictions.append(UserHandle.USER_OWNER, restrictions);
568
569        updateUserIdsLocked();
570
571        writeUserListLocked();
572        writeUserLocked(primary);
573    }
574
575    /*
576     * Writes the user file in this format:
577     *
578     * <user flags="20039023" id="0">
579     *   <name>Primary</name>
580     * </user>
581     */
582    private void writeUserLocked(UserInfo userInfo) {
583        FileOutputStream fos = null;
584        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
585        try {
586            fos = userFile.startWrite();
587            final BufferedOutputStream bos = new BufferedOutputStream(fos);
588
589            // XmlSerializer serializer = XmlUtils.serializerInstance();
590            final XmlSerializer serializer = new FastXmlSerializer();
591            serializer.setOutput(bos, "utf-8");
592            serializer.startDocument(null, true);
593            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
594
595            serializer.startTag(null, TAG_USER);
596            serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
597            serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
598            serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
599            serializer.attribute(null, ATTR_CREATION_TIME, Long.toString(userInfo.creationTime));
600            serializer.attribute(null, ATTR_LAST_LOGGED_IN_TIME,
601                    Long.toString(userInfo.lastLoggedInTime));
602            if (userInfo.iconPath != null) {
603                serializer.attribute(null,  ATTR_ICON_PATH, userInfo.iconPath);
604            }
605            if (userInfo.partial) {
606                serializer.attribute(null, ATTR_PARTIAL, "true");
607            }
608
609            serializer.startTag(null, TAG_NAME);
610            serializer.text(userInfo.name);
611            serializer.endTag(null, TAG_NAME);
612
613            Bundle restrictions = mUserRestrictions.get(userInfo.id);
614            if (restrictions != null) {
615                serializer.startTag(null, TAG_RESTRICTIONS);
616                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
617                writeBoolean(serializer, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
618                writeBoolean(serializer, restrictions, UserManager.DISALLOW_INSTALL_APPS);
619                writeBoolean(serializer, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
620                writeBoolean(serializer, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
621                writeBoolean(serializer, restrictions,
622                        UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
623                writeBoolean(serializer, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
624                writeBoolean(serializer, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
625                serializer.endTag(null, TAG_RESTRICTIONS);
626            }
627            serializer.endTag(null, TAG_USER);
628
629            serializer.endDocument();
630            userFile.finishWrite(fos);
631        } catch (Exception ioe) {
632            Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
633            userFile.failWrite(fos);
634        }
635    }
636
637    /*
638     * Writes the user list file in this format:
639     *
640     * <users nextSerialNumber="3">
641     *   <user id="0"></user>
642     *   <user id="2"></user>
643     * </users>
644     */
645    private void writeUserListLocked() {
646        FileOutputStream fos = null;
647        AtomicFile userListFile = new AtomicFile(mUserListFile);
648        try {
649            fos = userListFile.startWrite();
650            final BufferedOutputStream bos = new BufferedOutputStream(fos);
651
652            // XmlSerializer serializer = XmlUtils.serializerInstance();
653            final XmlSerializer serializer = new FastXmlSerializer();
654            serializer.setOutput(bos, "utf-8");
655            serializer.startDocument(null, true);
656            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
657
658            serializer.startTag(null, TAG_USERS);
659            serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
660            serializer.attribute(null, ATTR_USER_VERSION, Integer.toString(mUserVersion));
661
662            for (int i = 0; i < mUsers.size(); i++) {
663                UserInfo user = mUsers.valueAt(i);
664                serializer.startTag(null, TAG_USER);
665                serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
666                serializer.endTag(null, TAG_USER);
667            }
668
669            serializer.endTag(null, TAG_USERS);
670
671            serializer.endDocument();
672            userListFile.finishWrite(fos);
673        } catch (Exception e) {
674            userListFile.failWrite(fos);
675            Slog.e(LOG_TAG, "Error writing user list");
676        }
677    }
678
679    private UserInfo readUserLocked(int id) {
680        int flags = 0;
681        int serialNumber = id;
682        String name = null;
683        String iconPath = null;
684        long creationTime = 0L;
685        long lastLoggedInTime = 0L;
686        boolean partial = false;
687        Bundle restrictions = new Bundle();
688
689        FileInputStream fis = null;
690        try {
691            AtomicFile userFile =
692                    new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
693            fis = userFile.openRead();
694            XmlPullParser parser = Xml.newPullParser();
695            parser.setInput(fis, null);
696            int type;
697            while ((type = parser.next()) != XmlPullParser.START_TAG
698                    && type != XmlPullParser.END_DOCUMENT) {
699                ;
700            }
701
702            if (type != XmlPullParser.START_TAG) {
703                Slog.e(LOG_TAG, "Unable to read user " + id);
704                return null;
705            }
706
707            if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
708                int storedId = readIntAttribute(parser, ATTR_ID, -1);
709                if (storedId != id) {
710                    Slog.e(LOG_TAG, "User id does not match the file name");
711                    return null;
712                }
713                serialNumber = readIntAttribute(parser, ATTR_SERIAL_NO, id);
714                flags = readIntAttribute(parser, ATTR_FLAGS, 0);
715                iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
716                creationTime = readLongAttribute(parser, ATTR_CREATION_TIME, 0);
717                lastLoggedInTime = readLongAttribute(parser, ATTR_LAST_LOGGED_IN_TIME, 0);
718                String valueString = parser.getAttributeValue(null, ATTR_PARTIAL);
719                if ("true".equals(valueString)) {
720                    partial = true;
721                }
722
723                int outerDepth = parser.getDepth();
724                while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
725                       && (type != XmlPullParser.END_TAG || parser.getDepth() > outerDepth)) {
726                    if (type == XmlPullParser.END_TAG || type == XmlPullParser.TEXT) {
727                        continue;
728                    }
729                    String tag = parser.getName();
730                    if (TAG_NAME.equals(tag)) {
731                        type = parser.next();
732                        if (type == XmlPullParser.TEXT) {
733                            name = parser.getText();
734                        }
735                    } else if (TAG_RESTRICTIONS.equals(tag)) {
736                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_WIFI);
737                        readBoolean(parser, restrictions, UserManager.DISALLOW_MODIFY_ACCOUNTS);
738                        readBoolean(parser, restrictions, UserManager.DISALLOW_INSTALL_APPS);
739                        readBoolean(parser, restrictions, UserManager.DISALLOW_UNINSTALL_APPS);
740                        readBoolean(parser, restrictions, UserManager.DISALLOW_SHARE_LOCATION);
741                        readBoolean(parser, restrictions,
742                                UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES);
743                        readBoolean(parser, restrictions, UserManager.DISALLOW_CONFIG_BLUETOOTH);
744                        readBoolean(parser, restrictions, UserManager.DISALLOW_USB_FILE_TRANSFER);
745                    }
746                }
747            }
748
749            UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
750            userInfo.serialNumber = serialNumber;
751            userInfo.creationTime = creationTime;
752            userInfo.lastLoggedInTime = lastLoggedInTime;
753            userInfo.partial = partial;
754            mUserRestrictions.append(id, restrictions);
755            return userInfo;
756
757        } catch (IOException ioe) {
758        } catch (XmlPullParserException pe) {
759        } finally {
760            if (fis != null) {
761                try {
762                    fis.close();
763                } catch (IOException e) {
764                }
765            }
766        }
767        return null;
768    }
769
770    private void readBoolean(XmlPullParser parser, Bundle restrictions,
771            String restrictionKey) {
772        String value = parser.getAttributeValue(null, restrictionKey);
773        if (value != null) {
774            restrictions.putBoolean(restrictionKey, Boolean.parseBoolean(value));
775        }
776    }
777
778    private void writeBoolean(XmlSerializer xml, Bundle restrictions, String restrictionKey)
779            throws IOException {
780        if (restrictions.containsKey(restrictionKey)) {
781            xml.attribute(null, restrictionKey,
782                    Boolean.toString(restrictions.getBoolean(restrictionKey)));
783        }
784    }
785
786    private int readIntAttribute(XmlPullParser parser, String attr, int defaultValue) {
787        String valueString = parser.getAttributeValue(null, attr);
788        if (valueString == null) return defaultValue;
789        try {
790            return Integer.parseInt(valueString);
791        } catch (NumberFormatException nfe) {
792            return defaultValue;
793        }
794    }
795
796    private long readLongAttribute(XmlPullParser parser, String attr, long defaultValue) {
797        String valueString = parser.getAttributeValue(null, attr);
798        if (valueString == null) return defaultValue;
799        try {
800            return Long.parseLong(valueString);
801        } catch (NumberFormatException nfe) {
802            return defaultValue;
803        }
804    }
805
806    @Override
807    public UserInfo createUser(String name, int flags) {
808        checkManageUsersPermission("Only the system can create users");
809
810        final long ident = Binder.clearCallingIdentity();
811        final UserInfo userInfo;
812        try {
813            synchronized (mInstallLock) {
814                synchronized (mPackagesLock) {
815                    if (isUserLimitReachedLocked()) return null;
816                    int userId = getNextAvailableIdLocked();
817                    userInfo = new UserInfo(userId, name, null, flags);
818                    File userPath = new File(mBaseUserPath, Integer.toString(userId));
819                    userInfo.serialNumber = mNextSerialNumber++;
820                    long now = System.currentTimeMillis();
821                    userInfo.creationTime = (now > EPOCH_PLUS_30_YEARS) ? now : 0;
822                    userInfo.partial = true;
823                    Environment.getUserSystemDirectory(userInfo.id).mkdirs();
824                    mUsers.put(userId, userInfo);
825                    writeUserListLocked();
826                    writeUserLocked(userInfo);
827                    mPm.createNewUserLILPw(userId, userPath);
828                    userInfo.partial = false;
829                    writeUserLocked(userInfo);
830                    updateUserIdsLocked();
831                    Bundle restrictions = new Bundle();
832                    mUserRestrictions.append(userId, restrictions);
833                }
834            }
835            if (userInfo != null) {
836                Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
837                addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
838                mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
839                        android.Manifest.permission.MANAGE_USERS);
840            }
841        } finally {
842            Binder.restoreCallingIdentity(ident);
843        }
844        return userInfo;
845    }
846
847    /**
848     * Removes a user and all data directories created for that user. This method should be called
849     * after the user's processes have been terminated.
850     * @param id the user's id
851     */
852    public boolean removeUser(int userHandle) {
853        checkManageUsersPermission("Only the system can remove users");
854        final UserInfo user;
855        synchronized (mPackagesLock) {
856            user = mUsers.get(userHandle);
857            if (userHandle == 0 || user == null) {
858                return false;
859            }
860            mRemovingUserIds.put(userHandle, true);
861            // Set this to a partially created user, so that the user will be purged
862            // on next startup, in case the runtime stops now before stopping and
863            // removing the user completely.
864            user.partial = true;
865            writeUserLocked(user);
866        }
867        if (DBG) Slog.i(LOG_TAG, "Stopping user " + userHandle);
868        int res;
869        try {
870            res = ActivityManagerNative.getDefault().stopUser(userHandle,
871                    new IStopUserCallback.Stub() {
872                        @Override
873                        public void userStopped(int userId) {
874                            finishRemoveUser(userId);
875                        }
876                        @Override
877                        public void userStopAborted(int userId) {
878                        }
879            });
880        } catch (RemoteException e) {
881            return false;
882        }
883
884        return res == ActivityManager.USER_OP_SUCCESS;
885    }
886
887    void finishRemoveUser(final int userHandle) {
888        if (DBG) Slog.i(LOG_TAG, "finishRemoveUser " + userHandle);
889        // Let other services shutdown any activity and clean up their state before completely
890        // wiping the user's system directory and removing from the user list
891        long ident = Binder.clearCallingIdentity();
892        try {
893            Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
894            addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
895            mContext.sendOrderedBroadcastAsUser(addedIntent, UserHandle.ALL,
896                    android.Manifest.permission.MANAGE_USERS,
897
898                    new BroadcastReceiver() {
899                        @Override
900                        public void onReceive(Context context, Intent intent) {
901                            if (DBG) {
902                                Slog.i(LOG_TAG,
903                                        "USER_REMOVED broadcast sent, cleaning up user data "
904                                        + userHandle);
905                            }
906                            new Thread() {
907                                public void run() {
908                                    synchronized (mInstallLock) {
909                                        synchronized (mPackagesLock) {
910                                            removeUserStateLocked(userHandle);
911                                        }
912                                    }
913                                }
914                            }.start();
915                        }
916                    },
917
918                    null, Activity.RESULT_OK, null, null);
919        } finally {
920            Binder.restoreCallingIdentity(ident);
921        }
922    }
923
924    private void removeUserStateLocked(final int userHandle) {
925        // Cleanup package manager settings
926        mPm.cleanUpUserLILPw(userHandle);
927
928        // Remove this user from the list
929        mUsers.remove(userHandle);
930
931        // Have user ID linger for several seconds to let external storage VFS
932        // cache entries expire. This must be greater than the 'entry_valid'
933        // timeout used by the FUSE daemon.
934        mHandler.postDelayed(new Runnable() {
935            @Override
936            public void run() {
937                synchronized (mPackagesLock) {
938                    mRemovingUserIds.delete(userHandle);
939                }
940            }
941        }, MINUTE_IN_MILLIS);
942
943        // Remove user file
944        AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
945        userFile.delete();
946        // Update the user list
947        writeUserListLocked();
948        updateUserIdsLocked();
949        removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
950    }
951
952    private void removeDirectoryRecursive(File parent) {
953        if (parent.isDirectory()) {
954            String[] files = parent.list();
955            for (String filename : files) {
956                File child = new File(parent, filename);
957                removeDirectoryRecursive(child);
958            }
959        }
960        parent.delete();
961    }
962
963    @Override
964    public List<RestrictionEntry> getApplicationRestrictions(String packageName, int userId) {
965        if (UserHandle.getCallingUserId() != userId
966                || Binder.getCallingUid() != getUidForPackage(packageName)) {
967            checkManageUsersPermission("Only system can get restrictions for other users/apps");
968        }
969        synchronized (mPackagesLock) {
970            // Read the restrictions from XML
971            return readApplicationRestrictionsLocked(packageName, userId);
972        }
973    }
974
975    @Override
976    public void setApplicationRestrictions(String packageName, List<RestrictionEntry> entries,
977            int userId) {
978        if (UserHandle.getCallingUserId() != userId
979                || Binder.getCallingUid() != getUidForPackage(packageName)) {
980            checkManageUsersPermission("Only system can set restrictions for other users/apps");
981        }
982        synchronized (mPackagesLock) {
983            // Write the restrictions to XML
984            writeApplicationRestrictionsLocked(packageName, entries, userId);
985        }
986    }
987
988    private int getUidForPackage(String packageName) {
989        try {
990            return mContext.getPackageManager().getApplicationInfo(packageName,
991                    PackageManager.GET_UNINSTALLED_PACKAGES).uid;
992        } catch (NameNotFoundException nnfe) {
993            return -1;
994        }
995    }
996
997    private List<RestrictionEntry> readApplicationRestrictionsLocked(String packageName,
998            int userId) {
999        final ArrayList<RestrictionEntry> entries = new ArrayList<RestrictionEntry>();
1000        final ArrayList<String> values = new ArrayList<String>();
1001
1002        FileInputStream fis = null;
1003        try {
1004            AtomicFile restrictionsFile =
1005                    new AtomicFile(new File(Environment.getUserSystemDirectory(userId),
1006                            RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
1007            fis = restrictionsFile.openRead();
1008            XmlPullParser parser = Xml.newPullParser();
1009            parser.setInput(fis, null);
1010            int type;
1011            while ((type = parser.next()) != XmlPullParser.START_TAG
1012                    && type != XmlPullParser.END_DOCUMENT) {
1013                ;
1014            }
1015
1016            if (type != XmlPullParser.START_TAG) {
1017                Slog.e(LOG_TAG, "Unable to read restrictions file "
1018                        + restrictionsFile.getBaseFile());
1019                return entries;
1020            }
1021
1022            while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1023                if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_ENTRY)) {
1024                    String key = parser.getAttributeValue(null, ATTR_KEY);
1025                    String multiple = parser.getAttributeValue(null, ATTR_MULTIPLE);
1026                    if (multiple != null) {
1027                        int count = Integer.parseInt(multiple);
1028                        while (count > 0 && (type = parser.next()) != XmlPullParser.END_DOCUMENT) {
1029                            if (type == XmlPullParser.START_TAG
1030                                    && parser.getName().equals(TAG_VALUE)) {
1031                                values.add(parser.nextText().trim());
1032                                count--;
1033                            }
1034                        }
1035                        String [] valueStrings = new String[values.size()];
1036                        values.toArray(valueStrings);
1037                        Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + valueStrings);
1038                        RestrictionEntry entry = new RestrictionEntry(key, valueStrings);
1039                        entries.add(entry);
1040                    } else {
1041                        String value = parser.nextText().trim();
1042                        Slog.d(LOG_TAG, "Got RestrictionEntry " + key + "," + value);
1043                        RestrictionEntry entry = new RestrictionEntry(key, value);
1044                        entries.add(entry);
1045                    }
1046                }
1047            }
1048
1049        } catch (IOException ioe) {
1050        } catch (XmlPullParserException pe) {
1051        } finally {
1052            if (fis != null) {
1053                try {
1054                    fis.close();
1055                } catch (IOException e) {
1056                }
1057            }
1058        }
1059        return entries;
1060    }
1061
1062    private void writeApplicationRestrictionsLocked(String packageName,
1063            List<RestrictionEntry> entries, int userId) {
1064        FileOutputStream fos = null;
1065        AtomicFile restrictionsFile = new AtomicFile(
1066                new File(Environment.getUserSystemDirectory(userId),
1067                        RESTRICTIONS_FILE_PREFIX + packageName + ".xml"));
1068        try {
1069            fos = restrictionsFile.startWrite();
1070            final BufferedOutputStream bos = new BufferedOutputStream(fos);
1071
1072            // XmlSerializer serializer = XmlUtils.serializerInstance();
1073            final XmlSerializer serializer = new FastXmlSerializer();
1074            serializer.setOutput(bos, "utf-8");
1075            serializer.startDocument(null, true);
1076            serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
1077
1078            serializer.startTag(null, TAG_RESTRICTIONS);
1079
1080            for (RestrictionEntry entry : entries) {
1081                serializer.startTag(null, TAG_ENTRY);
1082                serializer.attribute(null, ATTR_KEY, entry.getKey());
1083                if (entry.getSelectedString() != null || entry.getAllSelectedStrings() == null) {
1084                    String value = entry.getSelectedString();
1085                    serializer.text(value != null ? value : "");
1086                } else {
1087                    String[] values = entry.getAllSelectedStrings();
1088                    serializer.attribute(null, ATTR_MULTIPLE, Integer.toString(values.length));
1089                    for (String value : values) {
1090                        serializer.startTag(null, TAG_VALUE);
1091                        serializer.text(value != null ? value : "");
1092                        serializer.endTag(null, TAG_VALUE);
1093                    }
1094                }
1095                serializer.endTag(null, TAG_ENTRY);
1096            }
1097
1098            serializer.endTag(null, TAG_RESTRICTIONS);
1099
1100            serializer.endDocument();
1101            restrictionsFile.finishWrite(fos);
1102        } catch (Exception e) {
1103            restrictionsFile.failWrite(fos);
1104            Slog.e(LOG_TAG, "Error writing application restrictions list");
1105        }
1106    }
1107
1108    @Override
1109    public int getUserSerialNumber(int userHandle) {
1110        synchronized (mPackagesLock) {
1111            if (!exists(userHandle)) return -1;
1112            return getUserInfoLocked(userHandle).serialNumber;
1113        }
1114    }
1115
1116    @Override
1117    public int getUserHandle(int userSerialNumber) {
1118        synchronized (mPackagesLock) {
1119            for (int userId : mUserIds) {
1120                if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
1121            }
1122            // Not found
1123            return -1;
1124        }
1125    }
1126
1127    /**
1128     * Caches the list of user ids in an array, adjusting the array size when necessary.
1129     */
1130    private void updateUserIdsLocked() {
1131        int num = 0;
1132        for (int i = 0; i < mUsers.size(); i++) {
1133            if (!mUsers.valueAt(i).partial) {
1134                num++;
1135            }
1136        }
1137        final int[] newUsers = new int[num];
1138        int n = 0;
1139        for (int i = 0; i < mUsers.size(); i++) {
1140            if (!mUsers.valueAt(i).partial) {
1141                newUsers[n++] = mUsers.keyAt(i);
1142            }
1143        }
1144        mUserIds = newUsers;
1145    }
1146
1147    /**
1148     * Make a note of the last started time of a user.
1149     * @param userId the user that was just foregrounded
1150     */
1151    public void userForeground(int userId) {
1152        synchronized (mPackagesLock) {
1153            UserInfo user = mUsers.get(userId);
1154            long now = System.currentTimeMillis();
1155            if (user == null || user.partial) {
1156                Slog.w(LOG_TAG, "userForeground: unknown user #" + userId);
1157                return;
1158            }
1159            if (now > EPOCH_PLUS_30_YEARS) {
1160                user.lastLoggedInTime = now;
1161                writeUserLocked(user);
1162            }
1163        }
1164    }
1165
1166    /**
1167     * Returns the next available user id, filling in any holes in the ids.
1168     * TODO: May not be a good idea to recycle ids, in case it results in confusion
1169     * for data and battery stats collection, or unexpected cross-talk.
1170     * @return
1171     */
1172    private int getNextAvailableIdLocked() {
1173        synchronized (mPackagesLock) {
1174            int i = MIN_USER_ID;
1175            while (i < Integer.MAX_VALUE) {
1176                if (mUsers.indexOfKey(i) < 0 && !mRemovingUserIds.get(i)) {
1177                    break;
1178                }
1179                i++;
1180            }
1181            return i;
1182        }
1183    }
1184
1185    @Override
1186    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
1187        if (mContext.checkCallingOrSelfPermission(android.Manifest.permission.DUMP)
1188                != PackageManager.PERMISSION_GRANTED) {
1189            pw.println("Permission Denial: can't dump UserManager from from pid="
1190                    + Binder.getCallingPid()
1191                    + ", uid=" + Binder.getCallingUid()
1192                    + " without permission "
1193                    + android.Manifest.permission.DUMP);
1194            return;
1195        }
1196
1197        long now = System.currentTimeMillis();
1198        StringBuilder sb = new StringBuilder();
1199        synchronized (mPackagesLock) {
1200            pw.println("Users:");
1201            for (int i = 0; i < mUsers.size(); i++) {
1202                UserInfo user = mUsers.valueAt(i);
1203                if (user == null) continue;
1204                pw.print("  "); pw.print(user); pw.print(" serialNo="); pw.print(user.serialNumber);
1205                if (mRemovingUserIds.get(mUsers.keyAt(i))) pw.print(" <removing> ");
1206                if (user.partial) pw.print(" <partial>");
1207                pw.println();
1208                pw.print("    Created: ");
1209                if (user.creationTime == 0) {
1210                    pw.println("<unknown>");
1211                } else {
1212                    sb.setLength(0);
1213                    TimeUtils.formatDuration(now - user.creationTime, sb);
1214                    sb.append(" ago");
1215                    pw.println(sb);
1216                }
1217                pw.print("    Last logged in: ");
1218                if (user.lastLoggedInTime == 0) {
1219                    pw.println("<unknown>");
1220                } else {
1221                    sb.setLength(0);
1222                    TimeUtils.formatDuration(now - user.lastLoggedInTime, sb);
1223                    sb.append(" ago");
1224                    pw.println(sb);
1225                }
1226            }
1227        }
1228    }
1229}
1230