blob: be86628eea121c6f3c5dd60cabf29294c858fda8 [file] [log] [blame]
Amith Yamasani4b2e9342011-03-31 12:38:53 -07001/*
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
Amith Yamasanib8151ec2012-04-18 18:02:48 -070019import static android.os.ParcelFileDescriptor.MODE_CREATE;
20import static android.os.ParcelFileDescriptor.MODE_READ_WRITE;
21
Amith Yamasani13593602012-03-22 16:16:17 -070022import com.android.internal.util.ArrayUtils;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070023import com.android.internal.util.FastXmlSerializer;
24
Amith Yamasani2a003292012-08-14 18:25:45 -070025import android.app.ActivityManager;
Dianne Hackborn80a4af22012-08-27 19:18:31 -070026import android.app.ActivityManagerNative;
27import android.app.IStopUserCallback;
Amith Yamasani258848d2012-08-10 17:06:33 -070028import android.content.Context;
29import android.content.Intent;
Amith Yamasani0b285492011-04-14 17:35:23 -070030import android.content.pm.PackageManager;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070031import android.content.pm.UserInfo;
Amith Yamasanie928d7d2012-09-17 21:46:51 -070032import android.graphics.Bitmap;
33import android.graphics.BitmapFactory;
Amith Yamasani258848d2012-08-10 17:06:33 -070034import android.os.Binder;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070035import android.os.Environment;
36import android.os.FileUtils;
Amith Yamasani258848d2012-08-10 17:06:33 -070037import android.os.IUserManager;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070038import android.os.ParcelFileDescriptor;
Amith Yamasani258848d2012-08-10 17:06:33 -070039import android.os.Process;
Dianne Hackborn80a4af22012-08-27 19:18:31 -070040import android.os.RemoteException;
Dianne Hackbornf02b60a2012-08-16 10:48:27 -070041import android.os.UserHandle;
Amith Yamasani2a003292012-08-14 18:25:45 -070042import android.util.AtomicFile;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070043import android.util.Slog;
44import android.util.SparseArray;
45import android.util.Xml;
46
47import java.io.BufferedOutputStream;
48import java.io.File;
49import java.io.FileInputStream;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070050import java.io.FileNotFoundException;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070051import java.io.FileOutputStream;
52import java.io.IOException;
53import java.util.ArrayList;
54import java.util.List;
55
56import org.xmlpull.v1.XmlPullParser;
57import org.xmlpull.v1.XmlPullParserException;
58import org.xmlpull.v1.XmlSerializer;
59
Amith Yamasani258848d2012-08-10 17:06:33 -070060public class UserManagerService extends IUserManager.Stub {
Amith Yamasanib8151ec2012-04-18 18:02:48 -070061
Amith Yamasani2a003292012-08-14 18:25:45 -070062 private static final String LOG_TAG = "UserManagerService";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070063
Amith Yamasani4b2e9342011-03-31 12:38:53 -070064 private static final String TAG_NAME = "name";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070065 private static final String ATTR_FLAGS = "flags";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070066 private static final String ATTR_ICON_PATH = "icon";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070067 private static final String ATTR_ID = "id";
Amith Yamasani2a003292012-08-14 18:25:45 -070068 private static final String ATTR_SERIAL_NO = "serialNumber";
69 private static final String ATTR_NEXT_SERIAL_NO = "nextSerialNumber";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070070 private static final String TAG_USERS = "users";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070071 private static final String TAG_USER = "user";
72
Amith Yamasani0b285492011-04-14 17:35:23 -070073 private static final String USER_INFO_DIR = "system" + File.separator + "users";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070074 private static final String USER_LIST_FILENAME = "userlist.xml";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070075 private static final String USER_PHOTO_FILENAME = "photo.png";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070076
Dianne Hackborn4428e172012-08-24 17:43:05 -070077 private final Context mContext;
78 private final PackageManagerService mPm;
79 private final Object mInstallLock;
80 private final Object mPackagesLock;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070081
82 private final File mUsersDir;
83 private final File mUserListFile;
Dianne Hackborn4428e172012-08-24 17:43:05 -070084 private final File mBaseUserPath;
85
86 private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
87
Dianne Hackborn5dc5a002012-09-15 19:33:48 -070088 private final int mUserLimit;
89
Amith Yamasani0b285492011-04-14 17:35:23 -070090 private int[] mUserIds;
Amith Yamasani258848d2012-08-10 17:06:33 -070091 private boolean mGuestEnabled;
Amith Yamasani2a003292012-08-14 18:25:45 -070092 private int mNextSerialNumber;
Amith Yamasani0b285492011-04-14 17:35:23 -070093
Amith Yamasani258848d2012-08-10 17:06:33 -070094 private static UserManagerService sInstance;
Amith Yamasani258848d2012-08-10 17:06:33 -070095
Dianne Hackborn4428e172012-08-24 17:43:05 -070096 public static UserManagerService getInstance() {
97 synchronized (UserManagerService.class) {
98 return sInstance;
Amith Yamasani258848d2012-08-10 17:06:33 -070099 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700100 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700101
102 /**
103 * Available for testing purposes.
104 */
Amith Yamasani258848d2012-08-10 17:06:33 -0700105 UserManagerService(File dataDir, File baseUserPath) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700106 this(null, null, new Object(), new Object(), dataDir, baseUserPath);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700107 }
108
Dianne Hackborn4428e172012-08-24 17:43:05 -0700109 /**
110 * Called by package manager to create the service. This is closely
111 * associated with the package manager, and the given lock is the
112 * package manager's own lock.
113 */
114 UserManagerService(Context context, PackageManagerService pm,
115 Object installLock, Object packagesLock) {
116 this(context, pm, installLock, packagesLock,
117 Environment.getDataDirectory(),
118 new File(Environment.getDataDirectory(), "user"));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700119 }
120
Dianne Hackborn4428e172012-08-24 17:43:05 -0700121 /**
122 * Available for testing purposes.
123 */
124 private UserManagerService(Context context, PackageManagerService pm,
125 Object installLock, Object packagesLock,
126 File dataDir, File baseUserPath) {
127 synchronized (UserManagerService.class) {
128 mContext = context;
129 mPm = pm;
130 mInstallLock = installLock;
131 mPackagesLock = packagesLock;
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700132 mUserLimit = mContext.getResources().getInteger(
133 com.android.internal.R.integer.config_multiuserMaximumUsers);
Dianne Hackborn4428e172012-08-24 17:43:05 -0700134 mUsersDir = new File(dataDir, USER_INFO_DIR);
135 mUsersDir.mkdirs();
136 // Make zeroth user directory, for services to migrate their files to that location
137 File userZeroDir = new File(mUsersDir, "0");
138 userZeroDir.mkdirs();
139 mBaseUserPath = baseUserPath;
140 FileUtils.setPermissions(mUsersDir.toString(),
141 FileUtils.S_IRWXU|FileUtils.S_IRWXG
142 |FileUtils.S_IROTH|FileUtils.S_IXOTH,
143 -1, -1);
144 mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
145 readUserList();
146 sInstance = this;
147 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700148 }
149
150 @Override
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700151 public List<UserInfo> getUsers() {
Amith Yamasani2a003292012-08-14 18:25:45 -0700152 checkManageUsersPermission("query users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700153 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700154 ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
155 for (int i = 0; i < mUsers.size(); i++) {
156 users.add(mUsers.valueAt(i));
157 }
158 return users;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700159 }
Amith Yamasani13593602012-03-22 16:16:17 -0700160 }
161
Amith Yamasani258848d2012-08-10 17:06:33 -0700162 @Override
163 public UserInfo getUserInfo(int userId) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700164 checkManageUsersPermission("query user");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700165 synchronized (mPackagesLock) {
Amith Yamasani195263742012-08-21 15:40:12 -0700166 return getUserInfoLocked(userId);
Amith Yamasani13593602012-03-22 16:16:17 -0700167 }
168 }
169
Amith Yamasani195263742012-08-21 15:40:12 -0700170 /*
171 * Should be locked on mUsers before calling this.
172 */
173 private UserInfo getUserInfoLocked(int userId) {
174 return mUsers.get(userId);
175 }
176
Amith Yamasani13593602012-03-22 16:16:17 -0700177 public boolean exists(int userId) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700178 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700179 return ArrayUtils.contains(mUserIds, userId);
180 }
181 }
182
Amith Yamasani258848d2012-08-10 17:06:33 -0700183 @Override
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700184 public void setUserName(int userId, String name) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700185 checkManageUsersPermission("rename users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700186 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700187 UserInfo info = mUsers.get(userId);
188 if (name != null && !name.equals(info.name)) {
189 info.name = name;
190 writeUserLocked(info);
191 }
192 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700193 sendUserInfoChangedBroadcast(userId);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700194 }
195
Amith Yamasani258848d2012-08-10 17:06:33 -0700196 @Override
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700197 public void setUserIcon(int userId, Bitmap bitmap) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700198 checkManageUsersPermission("update users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700199 synchronized (mPackagesLock) {
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700200 UserInfo info = mUsers.get(userId);
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700201 if (info == null) return;
202 writeBitmapLocked(info, bitmap);
203 writeUserLocked(info);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700204 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700205 sendUserInfoChangedBroadcast(userId);
206 }
207
208 private void sendUserInfoChangedBroadcast(int userId) {
209 Intent changedIntent = new Intent(Intent.ACTION_USER_INFO_CHANGED);
210 changedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userId);
211 changedIntent.addFlags(Intent.FLAG_RECEIVER_REGISTERED_ONLY);
212 mContext.sendBroadcastAsUser(changedIntent, new UserHandle(userId));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700213 }
214
Amith Yamasani258848d2012-08-10 17:06:33 -0700215 @Override
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700216 public Bitmap getUserIcon(int userId) {
Amith Yamasani3b49f072012-09-17 10:21:43 -0700217 checkManageUsersPermission("read users");
218 synchronized (mPackagesLock) {
219 UserInfo info = mUsers.get(userId);
220 if (info == null || info.iconPath == null) return null;
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700221 return BitmapFactory.decodeFile(info.iconPath);
Amith Yamasani3b49f072012-09-17 10:21:43 -0700222 }
223 }
224
225 @Override
Amith Yamasani258848d2012-08-10 17:06:33 -0700226 public void setGuestEnabled(boolean enable) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700227 checkManageUsersPermission("enable guest users");
Dianne Hackborn4428e172012-08-24 17:43:05 -0700228 synchronized (mPackagesLock) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700229 if (mGuestEnabled != enable) {
230 mGuestEnabled = enable;
231 // Erase any guest user that currently exists
232 for (int i = 0; i < mUsers.size(); i++) {
233 UserInfo user = mUsers.valueAt(i);
234 if (user.isGuest()) {
235 if (!enable) {
236 removeUser(user.id);
237 }
238 return;
239 }
240 }
241 // No guest was found
242 if (enable) {
243 createUser("Guest", UserInfo.FLAG_GUEST);
244 }
245 }
246 }
247 }
248
249 @Override
250 public boolean isGuestEnabled() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700251 synchronized (mPackagesLock) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700252 return mGuestEnabled;
253 }
254 }
255
256 @Override
257 public void wipeUser(int userHandle) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700258 checkManageUsersPermission("wipe user");
Amith Yamasani258848d2012-08-10 17:06:33 -0700259 // TODO:
260 }
261
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700262 public void makeInitialized(int userId) {
263 checkManageUsersPermission("makeInitialized");
264 synchronized (mPackagesLock) {
265 UserInfo info = mUsers.get(userId);
266 if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) {
267 info.flags |= UserInfo.FLAG_INITIALIZED;
268 writeUserLocked(info);
269 }
270 }
271 }
272
Amith Yamasani258848d2012-08-10 17:06:33 -0700273 /**
Amith Yamasanifaea76f2012-09-11 10:59:48 -0700274 * Check if we've hit the limit of how many users can be created.
275 */
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700276 private boolean isUserLimitReachedLocked() {
277 int nUsers = mUsers.size();
278 return nUsers >= mUserLimit;
Amith Yamasanifaea76f2012-09-11 10:59:48 -0700279 }
280
281 /**
Amith Yamasani195263742012-08-21 15:40:12 -0700282 * Enforces that only the system UID or root's UID or apps that have the
283 * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS}
284 * permission can make certain calls to the UserManager.
Amith Yamasani258848d2012-08-10 17:06:33 -0700285 *
286 * @param message used as message if SecurityException is thrown
287 * @throws SecurityException if the caller is not system or root
288 */
Amith Yamasani2a003292012-08-14 18:25:45 -0700289 private static final void checkManageUsersPermission(String message) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700290 final int uid = Binder.getCallingUid();
Amith Yamasani2a003292012-08-14 18:25:45 -0700291 if (uid != Process.SYSTEM_UID && uid != 0
292 && ActivityManager.checkComponentPermission(
293 android.Manifest.permission.MANAGE_USERS,
294 uid, -1, true) != PackageManager.PERMISSION_GRANTED) {
295 throw new SecurityException("You need MANAGE_USERS permission to: " + message);
Amith Yamasani258848d2012-08-10 17:06:33 -0700296 }
297 }
298
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700299 private void writeBitmapLocked(UserInfo info, Bitmap bitmap) {
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700300 try {
301 File dir = new File(mUsersDir, Integer.toString(info.id));
302 File file = new File(dir, USER_PHOTO_FILENAME);
303 if (!dir.exists()) {
304 dir.mkdir();
305 FileUtils.setPermissions(
306 dir.getPath(),
307 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
308 -1, -1);
309 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700310 FileOutputStream os;
311 if (bitmap.compress(Bitmap.CompressFormat.PNG, 100, os = new FileOutputStream(file))) {
Amith Yamasani3b49f072012-09-17 10:21:43 -0700312 info.iconPath = file.getAbsolutePath();
313 }
Amith Yamasanie928d7d2012-09-17 21:46:51 -0700314 try {
315 os.close();
316 } catch (IOException ioe) {
317 // What the ... !
318 }
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700319 } catch (FileNotFoundException e) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700320 Slog.w(LOG_TAG, "Error setting photo for user ", e);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700321 }
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700322 }
323
Amith Yamasani0b285492011-04-14 17:35:23 -0700324 /**
325 * Returns an array of user ids. This array is cached here for quick access, so do not modify or
326 * cache it elsewhere.
327 * @return the array of user ids.
328 */
Dianne Hackborn1676c852012-09-10 14:52:30 -0700329 public int[] getUserIds() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700330 synchronized (mPackagesLock) {
Dianne Hackborn7767eac2012-08-23 18:25:40 -0700331 return mUserIds;
332 }
Amith Yamasani0b285492011-04-14 17:35:23 -0700333 }
334
Dianne Hackborn4428e172012-08-24 17:43:05 -0700335 int[] getUserIdsLPr() {
336 return mUserIds;
337 }
338
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700339 private void readUserList() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700340 synchronized (mPackagesLock) {
Amith Yamasani13593602012-03-22 16:16:17 -0700341 readUserListLocked();
342 }
343 }
344
345 private void readUserListLocked() {
Amith Yamasani258848d2012-08-10 17:06:33 -0700346 mGuestEnabled = false;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700347 if (!mUserListFile.exists()) {
Amith Yamasani13593602012-03-22 16:16:17 -0700348 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700349 return;
350 }
351 FileInputStream fis = null;
Amith Yamasani2a003292012-08-14 18:25:45 -0700352 AtomicFile userListFile = new AtomicFile(mUserListFile);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700353 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700354 fis = userListFile.openRead();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700355 XmlPullParser parser = Xml.newPullParser();
356 parser.setInput(fis, null);
357 int type;
358 while ((type = parser.next()) != XmlPullParser.START_TAG
359 && type != XmlPullParser.END_DOCUMENT) {
360 ;
361 }
362
363 if (type != XmlPullParser.START_TAG) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700364 Slog.e(LOG_TAG, "Unable to read user list");
Amith Yamasani13593602012-03-22 16:16:17 -0700365 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700366 return;
367 }
368
Amith Yamasani2a003292012-08-14 18:25:45 -0700369 mNextSerialNumber = -1;
370 if (parser.getName().equals(TAG_USERS)) {
371 String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO);
372 if (lastSerialNumber != null) {
373 mNextSerialNumber = Integer.parseInt(lastSerialNumber);
374 }
375 }
376
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700377 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
378 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
379 String id = parser.getAttributeValue(null, ATTR_ID);
380 UserInfo user = readUser(Integer.parseInt(id));
381 if (user != null) {
382 mUsers.put(user.id, user);
Amith Yamasani2a003292012-08-14 18:25:45 -0700383 if (user.isGuest()) {
384 mGuestEnabled = true;
385 }
386 if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) {
387 mNextSerialNumber = user.id + 1;
388 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700389 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700390 }
391 }
Amith Yamasani13593602012-03-22 16:16:17 -0700392 updateUserIdsLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700393 } catch (IOException ioe) {
Amith Yamasani13593602012-03-22 16:16:17 -0700394 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700395 } catch (XmlPullParserException pe) {
Amith Yamasani13593602012-03-22 16:16:17 -0700396 fallbackToSingleUserLocked();
Dianne Hackbornbfd89b32011-12-15 18:22:54 -0800397 } finally {
398 if (fis != null) {
399 try {
400 fis.close();
401 } catch (IOException e) {
402 }
403 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700404 }
405 }
406
Amith Yamasani13593602012-03-22 16:16:17 -0700407 private void fallbackToSingleUserLocked() {
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700408 // Create the primary user
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700409 UserInfo primary = new UserInfo(0, "Primary", null,
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700410 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
411 mUsers.put(0, primary);
Amith Yamasani13593602012-03-22 16:16:17 -0700412 updateUserIdsLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700413
Amith Yamasani13593602012-03-22 16:16:17 -0700414 writeUserListLocked();
415 writeUserLocked(primary);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700416 }
417
418 /*
419 * Writes the user file in this format:
420 *
421 * <user flags="20039023" id="0">
422 * <name>Primary</name>
423 * </user>
424 */
Amith Yamasani13593602012-03-22 16:16:17 -0700425 private void writeUserLocked(UserInfo userInfo) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700426 FileOutputStream fos = null;
Amith Yamasani2a003292012-08-14 18:25:45 -0700427 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml"));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700428 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700429 fos = userFile.startWrite();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700430 final BufferedOutputStream bos = new BufferedOutputStream(fos);
431
432 // XmlSerializer serializer = XmlUtils.serializerInstance();
433 final XmlSerializer serializer = new FastXmlSerializer();
434 serializer.setOutput(bos, "utf-8");
435 serializer.startDocument(null, true);
436 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
437
438 serializer.startTag(null, TAG_USER);
439 serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
Amith Yamasani2a003292012-08-14 18:25:45 -0700440 serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700441 serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700442 if (userInfo.iconPath != null) {
443 serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
444 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700445
446 serializer.startTag(null, TAG_NAME);
447 serializer.text(userInfo.name);
448 serializer.endTag(null, TAG_NAME);
449
450 serializer.endTag(null, TAG_USER);
451
452 serializer.endDocument();
Amith Yamasani2a003292012-08-14 18:25:45 -0700453 userFile.finishWrite(fos);
454 } catch (Exception ioe) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700455 Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
Amith Yamasani2a003292012-08-14 18:25:45 -0700456 userFile.failWrite(fos);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700457 }
458 }
459
460 /*
461 * Writes the user list file in this format:
462 *
Amith Yamasani2a003292012-08-14 18:25:45 -0700463 * <users nextSerialNumber="3">
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700464 * <user id="0"></user>
465 * <user id="2"></user>
466 * </users>
467 */
Amith Yamasani13593602012-03-22 16:16:17 -0700468 private void writeUserListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -0700469 FileOutputStream fos = null;
Amith Yamasani2a003292012-08-14 18:25:45 -0700470 AtomicFile userListFile = new AtomicFile(mUserListFile);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700471 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700472 fos = userListFile.startWrite();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700473 final BufferedOutputStream bos = new BufferedOutputStream(fos);
474
475 // XmlSerializer serializer = XmlUtils.serializerInstance();
476 final XmlSerializer serializer = new FastXmlSerializer();
477 serializer.setOutput(bos, "utf-8");
478 serializer.startDocument(null, true);
479 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
480
481 serializer.startTag(null, TAG_USERS);
Amith Yamasani2a003292012-08-14 18:25:45 -0700482 serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber));
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700483
484 for (int i = 0; i < mUsers.size(); i++) {
485 UserInfo user = mUsers.valueAt(i);
486 serializer.startTag(null, TAG_USER);
487 serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
488 serializer.endTag(null, TAG_USER);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700489 }
490
491 serializer.endTag(null, TAG_USERS);
492
493 serializer.endDocument();
Amith Yamasani2a003292012-08-14 18:25:45 -0700494 userListFile.finishWrite(fos);
495 } catch (Exception e) {
496 userListFile.failWrite(fos);
Amith Yamasani0b285492011-04-14 17:35:23 -0700497 Slog.e(LOG_TAG, "Error writing user list");
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700498 }
499 }
500
501 private UserInfo readUser(int id) {
502 int flags = 0;
Amith Yamasani2a003292012-08-14 18:25:45 -0700503 int serialNumber = id;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700504 String name = null;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700505 String iconPath = null;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700506
507 FileInputStream fis = null;
508 try {
Amith Yamasani2a003292012-08-14 18:25:45 -0700509 AtomicFile userFile =
510 new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml"));
511 fis = userFile.openRead();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700512 XmlPullParser parser = Xml.newPullParser();
513 parser.setInput(fis, null);
514 int type;
515 while ((type = parser.next()) != XmlPullParser.START_TAG
516 && type != XmlPullParser.END_DOCUMENT) {
517 ;
518 }
519
520 if (type != XmlPullParser.START_TAG) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700521 Slog.e(LOG_TAG, "Unable to read user " + id);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700522 return null;
523 }
524
525 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
526 String storedId = parser.getAttributeValue(null, ATTR_ID);
527 if (Integer.parseInt(storedId) != id) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700528 Slog.e(LOG_TAG, "User id does not match the file name");
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700529 return null;
530 }
Amith Yamasani2a003292012-08-14 18:25:45 -0700531 String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO);
532 if (serialNumberValue != null) {
533 serialNumber = Integer.parseInt(serialNumberValue);
534 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700535 String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
536 flags = Integer.parseInt(flagString);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700537 iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700538
539 while ((type = parser.next()) != XmlPullParser.START_TAG
540 && type != XmlPullParser.END_DOCUMENT) {
541 }
542 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
543 type = parser.next();
544 if (type == XmlPullParser.TEXT) {
545 name = parser.getText();
546 }
547 }
548 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700549
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700550 UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
Amith Yamasani2a003292012-08-14 18:25:45 -0700551 userInfo.serialNumber = serialNumber;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700552 return userInfo;
553
554 } catch (IOException ioe) {
555 } catch (XmlPullParserException pe) {
Dianne Hackbornbfd89b32011-12-15 18:22:54 -0800556 } finally {
557 if (fis != null) {
558 try {
559 fis.close();
560 } catch (IOException e) {
561 }
562 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700563 }
564 return null;
565 }
566
Amith Yamasani258848d2012-08-10 17:06:33 -0700567 @Override
Amith Yamasani13593602012-03-22 16:16:17 -0700568 public UserInfo createUser(String name, int flags) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700569 checkManageUsersPermission("Only the system can create users");
Amith Yamasanifaea76f2012-09-11 10:59:48 -0700570
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700571 final long ident = Binder.clearCallingIdentity();
572 final UserInfo userInfo;
573 try {
574 synchronized (mInstallLock) {
575 synchronized (mPackagesLock) {
576 if (isUserLimitReachedLocked()) return null;
577 int userId = getNextAvailableIdLocked();
578 userInfo = new UserInfo(userId, name, null, flags);
579 File userPath = new File(mBaseUserPath, Integer.toString(userId));
580 userInfo.serialNumber = mNextSerialNumber++;
581 mUsers.put(userId, userInfo);
582 writeUserListLocked();
583 writeUserLocked(userInfo);
584 updateUserIdsLocked();
585 mPm.createNewUserLILPw(userId, userPath);
586 }
Dianne Hackborn4428e172012-08-24 17:43:05 -0700587 }
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700588 if (userInfo != null) {
589 Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
590 addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id);
591 mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
592 android.Manifest.permission.MANAGE_USERS);
593 }
594 } finally {
595 Binder.restoreCallingIdentity(ident);
Amith Yamasani258848d2012-08-10 17:06:33 -0700596 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700597 return userInfo;
598 }
599
Amith Yamasani0b285492011-04-14 17:35:23 -0700600 /**
601 * Removes a user and all data directories created for that user. This method should be called
602 * after the user's processes have been terminated.
603 * @param id the user's id
604 */
Amith Yamasani258848d2012-08-10 17:06:33 -0700605 public boolean removeUser(int userHandle) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700606 checkManageUsersPermission("Only the system can remove users");
Dianne Hackborn80a4af22012-08-27 19:18:31 -0700607 final UserInfo user;
608 synchronized (mPackagesLock) {
609 user = mUsers.get(userHandle);
610 if (userHandle == 0 || user == null) {
611 return false;
612 }
613 }
614
615 int res;
616 try {
617 res = ActivityManagerNative.getDefault().stopUser(userHandle,
618 new IStopUserCallback.Stub() {
619 @Override
620 public void userStopped(int userId) {
621 finishRemoveUser(userId);
622 }
623 @Override
624 public void userStopAborted(int userId) {
625 }
626 });
627 } catch (RemoteException e) {
628 return false;
629 }
630
631 return res == ActivityManager.USER_OP_SUCCESS;
632 }
633
634 void finishRemoveUser(int userHandle) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700635 synchronized (mInstallLock) {
636 synchronized (mPackagesLock) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700637 // Cleanup package manager settings
638 mPm.cleanUpUserLILPw(userHandle);
639
640 // Remove this user from the list
641 mUsers.remove(userHandle);
642 // Remove user file
643 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml"));
644 userFile.delete();
645 // Update the user list
646 writeUserListLocked();
647 updateUserIdsLocked();
Amith Yamasani61f57372012-08-31 12:12:28 -0700648 removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle));
Dianne Hackborn4428e172012-08-24 17:43:05 -0700649 }
650 }
Amith Yamasani0cd867c2012-08-22 16:45:47 -0700651
Amith Yamasani2a003292012-08-14 18:25:45 -0700652 // Let other services shutdown any activity
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700653 long ident = Binder.clearCallingIdentity();
654 try {
655 Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
656 addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle);
657 mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL,
658 android.Manifest.permission.MANAGE_USERS);
659 } finally {
660 Binder.restoreCallingIdentity(ident);
661 }
Amith Yamasani2a003292012-08-14 18:25:45 -0700662 }
663
Amith Yamasani61f57372012-08-31 12:12:28 -0700664 private void removeDirectoryRecursive(File parent) {
665 if (parent.isDirectory()) {
666 String[] files = parent.list();
667 for (String filename : files) {
668 File child = new File(parent, filename);
669 removeDirectoryRecursive(child);
670 }
671 }
672 parent.delete();
673 }
674
Amith Yamasani2a003292012-08-14 18:25:45 -0700675 @Override
676 public int getUserSerialNumber(int userHandle) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700677 synchronized (mPackagesLock) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700678 if (!exists(userHandle)) return -1;
Amith Yamasani195263742012-08-21 15:40:12 -0700679 return getUserInfoLocked(userHandle).serialNumber;
Amith Yamasani2a003292012-08-14 18:25:45 -0700680 }
681 }
682
683 @Override
684 public int getUserHandle(int userSerialNumber) {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700685 synchronized (mPackagesLock) {
Amith Yamasani2a003292012-08-14 18:25:45 -0700686 for (int userId : mUserIds) {
Amith Yamasani195263742012-08-21 15:40:12 -0700687 if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId;
Amith Yamasani2a003292012-08-14 18:25:45 -0700688 }
689 // Not found
690 return -1;
Amith Yamasani13593602012-03-22 16:16:17 -0700691 }
692 }
693
Amith Yamasani0b285492011-04-14 17:35:23 -0700694 /**
695 * Caches the list of user ids in an array, adjusting the array size when necessary.
696 */
Amith Yamasani13593602012-03-22 16:16:17 -0700697 private void updateUserIdsLocked() {
Dianne Hackborn7767eac2012-08-23 18:25:40 -0700698 int[] newUsers = new int[mUsers.size()];
Amith Yamasani0b285492011-04-14 17:35:23 -0700699 for (int i = 0; i < mUsers.size(); i++) {
Dianne Hackborn7767eac2012-08-23 18:25:40 -0700700 newUsers[i] = mUsers.keyAt(i);
Amith Yamasani0b285492011-04-14 17:35:23 -0700701 }
Dianne Hackborn7767eac2012-08-23 18:25:40 -0700702 mUserIds = newUsers;
Amith Yamasani0b285492011-04-14 17:35:23 -0700703 }
704
705 /**
706 * Returns the next available user id, filling in any holes in the ids.
Amith Yamasani742a6712011-05-04 14:49:28 -0700707 * TODO: May not be a good idea to recycle ids, in case it results in confusion
708 * for data and battery stats collection, or unexpected cross-talk.
Amith Yamasani0b285492011-04-14 17:35:23 -0700709 * @return
710 */
Dianne Hackborn5dc5a002012-09-15 19:33:48 -0700711 private int getNextAvailableIdLocked() {
Dianne Hackborn4428e172012-08-24 17:43:05 -0700712 synchronized (mPackagesLock) {
Amith Yamasani07a0ede2012-09-17 14:54:26 -0700713 int i = 10;
Amith Yamasani195263742012-08-21 15:40:12 -0700714 while (i < Integer.MAX_VALUE) {
715 if (mUsers.indexOfKey(i) < 0) {
716 break;
717 }
718 i++;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700719 }
Amith Yamasani195263742012-08-21 15:40:12 -0700720 return i;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700721 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700722 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700723}