blob: b55dd247ce21991f27881e5061e174e241a8939e [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 Yamasani258848d2012-08-10 17:06:33 -070025import android.content.Context;
26import android.content.Intent;
Amith Yamasani0b285492011-04-14 17:35:23 -070027import android.content.pm.ApplicationInfo;
28import android.content.pm.PackageManager;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070029import android.content.pm.UserInfo;
Amith Yamasani258848d2012-08-10 17:06:33 -070030import android.os.Binder;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070031import android.os.Environment;
32import android.os.FileUtils;
Amith Yamasani258848d2012-08-10 17:06:33 -070033import android.os.IUserManager;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070034import android.os.ParcelFileDescriptor;
Amith Yamasani258848d2012-08-10 17:06:33 -070035import android.os.Process;
Amith Yamasani0b285492011-04-14 17:35:23 -070036import android.os.SystemClock;
Amith Yamasani742a6712011-05-04 14:49:28 -070037import android.os.UserId;
Amith Yamasani0b285492011-04-14 17:35:23 -070038import android.util.Log;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070039import android.util.Slog;
40import android.util.SparseArray;
41import android.util.Xml;
42
43import java.io.BufferedOutputStream;
44import java.io.File;
45import java.io.FileInputStream;
Amith Yamasanib8151ec2012-04-18 18:02:48 -070046import java.io.FileNotFoundException;
Amith Yamasani4b2e9342011-03-31 12:38:53 -070047import java.io.FileOutputStream;
48import java.io.IOException;
49import java.util.ArrayList;
50import java.util.List;
51
52import org.xmlpull.v1.XmlPullParser;
53import org.xmlpull.v1.XmlPullParserException;
54import org.xmlpull.v1.XmlSerializer;
55
Amith Yamasani258848d2012-08-10 17:06:33 -070056public class UserManagerService extends IUserManager.Stub {
Amith Yamasanib8151ec2012-04-18 18:02:48 -070057
Amith Yamasani258848d2012-08-10 17:06:33 -070058 private static final String TAG = "UserManagerService";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070059
Amith Yamasani4b2e9342011-03-31 12:38:53 -070060 private static final String TAG_NAME = "name";
61
62 private static final String ATTR_FLAGS = "flags";
63
Amith Yamasanib8151ec2012-04-18 18:02:48 -070064 private static final String ATTR_ICON_PATH = "icon";
65
Amith Yamasani4b2e9342011-03-31 12:38:53 -070066 private static final String ATTR_ID = "id";
67
68 private static final String TAG_USERS = "users";
69
70 private static final String TAG_USER = "user";
71
Amith Yamasani0b285492011-04-14 17:35:23 -070072 private static final String LOG_TAG = "UserManager";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070073
Amith Yamasani0b285492011-04-14 17:35:23 -070074 private static final String USER_INFO_DIR = "system" + File.separator + "users";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070075 private static final String USER_LIST_FILENAME = "userlist.xml";
Amith Yamasanib8151ec2012-04-18 18:02:48 -070076 private static final String USER_PHOTO_FILENAME = "photo.png";
Amith Yamasani4b2e9342011-03-31 12:38:53 -070077
Amith Yamasani13593602012-03-22 16:16:17 -070078 private SparseArray<UserInfo> mUsers = new SparseArray<UserInfo>();
Amith Yamasani4b2e9342011-03-31 12:38:53 -070079
80 private final File mUsersDir;
81 private final File mUserListFile;
Amith Yamasani0b285492011-04-14 17:35:23 -070082 private int[] mUserIds;
Amith Yamasani258848d2012-08-10 17:06:33 -070083 private boolean mGuestEnabled;
Amith Yamasani0b285492011-04-14 17:35:23 -070084
85 private Installer mInstaller;
86 private File mBaseUserPath;
Amith Yamasani258848d2012-08-10 17:06:33 -070087 private Context mContext;
88 private static UserManagerService sInstance;
89 private PackageManagerService mPm;
90
91 public synchronized static UserManagerService getInstance(Context context) {
92 if (sInstance == null) {
93 sInstance = new UserManagerService(context);
94 }
95 return sInstance;
96 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -070097
98 /**
99 * Available for testing purposes.
100 */
Amith Yamasani258848d2012-08-10 17:06:33 -0700101 UserManagerService(File dataDir, File baseUserPath) {
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700102 mUsersDir = new File(dataDir, USER_INFO_DIR);
103 mUsersDir.mkdirs();
Amith Yamasani483f3b02012-03-13 16:08:00 -0700104 // Make zeroth user directory, for services to migrate their files to that location
105 File userZeroDir = new File(mUsersDir, "0");
106 userZeroDir.mkdirs();
Amith Yamasani0b285492011-04-14 17:35:23 -0700107 mBaseUserPath = baseUserPath;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700108 FileUtils.setPermissions(mUsersDir.toString(),
109 FileUtils.S_IRWXU|FileUtils.S_IRWXG
110 |FileUtils.S_IROTH|FileUtils.S_IXOTH,
111 -1, -1);
112 mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
113 readUserList();
114 }
115
Amith Yamasani258848d2012-08-10 17:06:33 -0700116 public UserManagerService(Context context) {
117 this(Environment.getDataDirectory(), new File(Environment.getDataDirectory(), "user"));
118 mContext = context;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700119 }
120
Amith Yamasani258848d2012-08-10 17:06:33 -0700121 void setInstaller(PackageManagerService pm, Installer installer) {
122 mInstaller = installer;
123 mPm = pm;
124 }
125
126 @Override
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700127 public List<UserInfo> getUsers() {
Amith Yamasani258848d2012-08-10 17:06:33 -0700128 enforceSystemOrRoot("Only the system can query users");
Amith Yamasani13593602012-03-22 16:16:17 -0700129 synchronized (mUsers) {
130 ArrayList<UserInfo> users = new ArrayList<UserInfo>(mUsers.size());
131 for (int i = 0; i < mUsers.size(); i++) {
132 users.add(mUsers.valueAt(i));
133 }
134 return users;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700135 }
Amith Yamasani13593602012-03-22 16:16:17 -0700136 }
137
Amith Yamasani258848d2012-08-10 17:06:33 -0700138 @Override
139 public UserInfo getUserInfo(int userId) {
140 enforceSystemOrRoot("Only the system can query user");
Amith Yamasani13593602012-03-22 16:16:17 -0700141 synchronized (mUsers) {
142 UserInfo info = mUsers.get(userId);
143 return info;
144 }
145 }
146
147 public boolean exists(int userId) {
148 synchronized (mUsers) {
149 return ArrayUtils.contains(mUserIds, userId);
150 }
151 }
152
Amith Yamasani258848d2012-08-10 17:06:33 -0700153 @Override
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700154 public void setUserName(int userId, String name) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700155 enforceSystemOrRoot("Only the system can rename users");
Amith Yamasani13593602012-03-22 16:16:17 -0700156 synchronized (mUsers) {
157 UserInfo info = mUsers.get(userId);
158 if (name != null && !name.equals(info.name)) {
159 info.name = name;
160 writeUserLocked(info);
161 }
162 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700163 }
164
Amith Yamasani258848d2012-08-10 17:06:33 -0700165 @Override
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700166 public ParcelFileDescriptor setUserIcon(int userId) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700167 enforceSystemOrRoot("Only the system can update users");
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700168 synchronized (mUsers) {
169 UserInfo info = mUsers.get(userId);
170 if (info == null) return null;
171 ParcelFileDescriptor fd = updateIconBitmapLocked(info);
172 if (fd != null) {
173 writeUserLocked(info);
174 }
175 return fd;
176 }
177 }
178
Amith Yamasani258848d2012-08-10 17:06:33 -0700179 @Override
180 public void setGuestEnabled(boolean enable) {
181 enforceSystemOrRoot("Only the system can enable guest users");
182 synchronized (mUsers) {
183 if (mGuestEnabled != enable) {
184 mGuestEnabled = enable;
185 // Erase any guest user that currently exists
186 for (int i = 0; i < mUsers.size(); i++) {
187 UserInfo user = mUsers.valueAt(i);
188 if (user.isGuest()) {
189 if (!enable) {
190 removeUser(user.id);
191 }
192 return;
193 }
194 }
195 // No guest was found
196 if (enable) {
197 createUser("Guest", UserInfo.FLAG_GUEST);
198 }
199 }
200 }
201 }
202
203 @Override
204 public boolean isGuestEnabled() {
205 synchronized (mUsers) {
206 return mGuestEnabled;
207 }
208 }
209
210 @Override
211 public void wipeUser(int userHandle) {
212 enforceSystemOrRoot("Only the system can wipe users");
213 // TODO:
214 }
215
216 /**
217 * Enforces that only the system UID or root's UID can call a method exposed
218 * via Binder.
219 *
220 * @param message used as message if SecurityException is thrown
221 * @throws SecurityException if the caller is not system or root
222 */
223 private static final void enforceSystemOrRoot(String message) {
224 final int uid = Binder.getCallingUid();
225 if (uid != Process.SYSTEM_UID && uid != 0) {
226 throw new SecurityException(message);
227 }
228 }
229
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700230 private ParcelFileDescriptor updateIconBitmapLocked(UserInfo info) {
231 try {
232 File dir = new File(mUsersDir, Integer.toString(info.id));
233 File file = new File(dir, USER_PHOTO_FILENAME);
234 if (!dir.exists()) {
235 dir.mkdir();
236 FileUtils.setPermissions(
237 dir.getPath(),
238 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH,
239 -1, -1);
240 }
241 ParcelFileDescriptor fd = ParcelFileDescriptor.open(file,
242 MODE_CREATE|MODE_READ_WRITE);
243 info.iconPath = file.getAbsolutePath();
244 return fd;
245 } catch (FileNotFoundException e) {
246 Slog.w(TAG, "Error setting photo for user ", e);
247 }
248 return null;
249 }
250
Amith Yamasani0b285492011-04-14 17:35:23 -0700251 /**
252 * Returns an array of user ids. This array is cached here for quick access, so do not modify or
253 * cache it elsewhere.
254 * @return the array of user ids.
255 */
256 int[] getUserIds() {
257 return mUserIds;
258 }
259
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700260 private void readUserList() {
Amith Yamasani13593602012-03-22 16:16:17 -0700261 synchronized (mUsers) {
262 readUserListLocked();
263 }
264 }
265
266 private void readUserListLocked() {
Amith Yamasani258848d2012-08-10 17:06:33 -0700267 mGuestEnabled = false;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700268 if (!mUserListFile.exists()) {
Amith Yamasani13593602012-03-22 16:16:17 -0700269 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700270 return;
271 }
272 FileInputStream fis = null;
273 try {
274 fis = new FileInputStream(mUserListFile);
275 XmlPullParser parser = Xml.newPullParser();
276 parser.setInput(fis, null);
277 int type;
278 while ((type = parser.next()) != XmlPullParser.START_TAG
279 && type != XmlPullParser.END_DOCUMENT) {
280 ;
281 }
282
283 if (type != XmlPullParser.START_TAG) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700284 Slog.e(LOG_TAG, "Unable to read user list");
Amith Yamasani13593602012-03-22 16:16:17 -0700285 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700286 return;
287 }
288
289 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) {
290 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
291 String id = parser.getAttributeValue(null, ATTR_ID);
292 UserInfo user = readUser(Integer.parseInt(id));
293 if (user != null) {
294 mUsers.put(user.id, user);
295 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700296 if (user.isGuest()) {
297 mGuestEnabled = true;
298 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700299 }
300 }
Amith Yamasani13593602012-03-22 16:16:17 -0700301 updateUserIdsLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700302 } catch (IOException ioe) {
Amith Yamasani13593602012-03-22 16:16:17 -0700303 fallbackToSingleUserLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700304 } catch (XmlPullParserException pe) {
Amith Yamasani13593602012-03-22 16:16:17 -0700305 fallbackToSingleUserLocked();
Dianne Hackbornbfd89b32011-12-15 18:22:54 -0800306 } finally {
307 if (fis != null) {
308 try {
309 fis.close();
310 } catch (IOException e) {
311 }
312 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700313 }
314 }
315
Amith Yamasani13593602012-03-22 16:16:17 -0700316 private void fallbackToSingleUserLocked() {
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700317 // Create the primary user
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700318 UserInfo primary = new UserInfo(0, "Primary", null,
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700319 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY);
320 mUsers.put(0, primary);
Amith Yamasani13593602012-03-22 16:16:17 -0700321 updateUserIdsLocked();
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700322
Amith Yamasani13593602012-03-22 16:16:17 -0700323 writeUserListLocked();
324 writeUserLocked(primary);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700325 }
326
327 /*
328 * Writes the user file in this format:
329 *
330 * <user flags="20039023" id="0">
331 * <name>Primary</name>
332 * </user>
333 */
Amith Yamasani13593602012-03-22 16:16:17 -0700334 private void writeUserLocked(UserInfo userInfo) {
Amith Yamasani742a6712011-05-04 14:49:28 -0700335 FileOutputStream fos = null;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700336 try {
337 final File mUserFile = new File(mUsersDir, userInfo.id + ".xml");
Amith Yamasani742a6712011-05-04 14:49:28 -0700338 fos = new FileOutputStream(mUserFile);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700339 final BufferedOutputStream bos = new BufferedOutputStream(fos);
340
341 // XmlSerializer serializer = XmlUtils.serializerInstance();
342 final XmlSerializer serializer = new FastXmlSerializer();
343 serializer.setOutput(bos, "utf-8");
344 serializer.startDocument(null, true);
345 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
346
347 serializer.startTag(null, TAG_USER);
348 serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id));
349 serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags));
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700350 if (userInfo.iconPath != null) {
351 serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath);
352 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700353
354 serializer.startTag(null, TAG_NAME);
355 serializer.text(userInfo.name);
356 serializer.endTag(null, TAG_NAME);
357
358 serializer.endTag(null, TAG_USER);
359
360 serializer.endDocument();
361 } catch (IOException ioe) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700362 Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe);
Amith Yamasani742a6712011-05-04 14:49:28 -0700363 } finally {
364 if (fos != null) {
365 try {
366 fos.close();
367 } catch (IOException ioe) {
368 }
369 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700370 }
371 }
372
373 /*
374 * Writes the user list file in this format:
375 *
376 * <users>
377 * <user id="0"></user>
378 * <user id="2"></user>
379 * </users>
380 */
Amith Yamasani13593602012-03-22 16:16:17 -0700381 private void writeUserListLocked() {
Amith Yamasani742a6712011-05-04 14:49:28 -0700382 FileOutputStream fos = null;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700383 try {
Amith Yamasani742a6712011-05-04 14:49:28 -0700384 fos = new FileOutputStream(mUserListFile);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700385 final BufferedOutputStream bos = new BufferedOutputStream(fos);
386
387 // XmlSerializer serializer = XmlUtils.serializerInstance();
388 final XmlSerializer serializer = new FastXmlSerializer();
389 serializer.setOutput(bos, "utf-8");
390 serializer.startDocument(null, true);
391 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
392
393 serializer.startTag(null, TAG_USERS);
394
395 for (int i = 0; i < mUsers.size(); i++) {
396 UserInfo user = mUsers.valueAt(i);
397 serializer.startTag(null, TAG_USER);
398 serializer.attribute(null, ATTR_ID, Integer.toString(user.id));
399 serializer.endTag(null, TAG_USER);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700400 }
401
402 serializer.endTag(null, TAG_USERS);
403
404 serializer.endDocument();
405 } catch (IOException ioe) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700406 Slog.e(LOG_TAG, "Error writing user list");
Amith Yamasani742a6712011-05-04 14:49:28 -0700407 } finally {
408 if (fos != null) {
409 try {
410 fos.close();
411 } catch (IOException ioe) {
412 }
413 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700414 }
415 }
416
417 private UserInfo readUser(int id) {
418 int flags = 0;
419 String name = null;
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700420 String iconPath = null;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700421
422 FileInputStream fis = null;
423 try {
424 File userFile = new File(mUsersDir, Integer.toString(id) + ".xml");
425 fis = new FileInputStream(userFile);
426 XmlPullParser parser = Xml.newPullParser();
427 parser.setInput(fis, null);
428 int type;
429 while ((type = parser.next()) != XmlPullParser.START_TAG
430 && type != XmlPullParser.END_DOCUMENT) {
431 ;
432 }
433
434 if (type != XmlPullParser.START_TAG) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700435 Slog.e(LOG_TAG, "Unable to read user " + id);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700436 return null;
437 }
438
439 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) {
440 String storedId = parser.getAttributeValue(null, ATTR_ID);
441 if (Integer.parseInt(storedId) != id) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700442 Slog.e(LOG_TAG, "User id does not match the file name");
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700443 return null;
444 }
445 String flagString = parser.getAttributeValue(null, ATTR_FLAGS);
446 flags = Integer.parseInt(flagString);
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700447 iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700448
449 while ((type = parser.next()) != XmlPullParser.START_TAG
450 && type != XmlPullParser.END_DOCUMENT) {
451 }
452 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) {
453 type = parser.next();
454 if (type == XmlPullParser.TEXT) {
455 name = parser.getText();
456 }
457 }
458 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700459
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700460 UserInfo userInfo = new UserInfo(id, name, iconPath, flags);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700461 return userInfo;
462
463 } catch (IOException ioe) {
464 } catch (XmlPullParserException pe) {
Dianne Hackbornbfd89b32011-12-15 18:22:54 -0800465 } finally {
466 if (fis != null) {
467 try {
468 fis.close();
469 } catch (IOException e) {
470 }
471 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700472 }
473 return null;
474 }
475
Amith Yamasani258848d2012-08-10 17:06:33 -0700476 @Override
Amith Yamasani13593602012-03-22 16:16:17 -0700477 public UserInfo createUser(String name, int flags) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700478 enforceSystemOrRoot("Only the system can create users");
Amith Yamasani0b285492011-04-14 17:35:23 -0700479 int userId = getNextAvailableId();
Amith Yamasanib8151ec2012-04-18 18:02:48 -0700480 UserInfo userInfo = new UserInfo(userId, name, null, flags);
Amith Yamasani0b285492011-04-14 17:35:23 -0700481 File userPath = new File(mBaseUserPath, Integer.toString(userId));
Amith Yamasani13593602012-03-22 16:16:17 -0700482 if (!createPackageFolders(userId, userPath)) {
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700483 return null;
484 }
Amith Yamasani13593602012-03-22 16:16:17 -0700485 synchronized (mUsers) {
486 mUsers.put(userId, userInfo);
487 writeUserListLocked();
488 writeUserLocked(userInfo);
489 updateUserIdsLocked();
490 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700491 if (userInfo != null) {
492 Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED);
493 addedIntent.putExtra(Intent.EXTRA_USERID, userInfo.id);
494 mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
495 }
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700496 return userInfo;
497 }
498
Amith Yamasani0b285492011-04-14 17:35:23 -0700499 /**
500 * Removes a user and all data directories created for that user. This method should be called
501 * after the user's processes have been terminated.
502 * @param id the user's id
503 */
Amith Yamasani258848d2012-08-10 17:06:33 -0700504 public boolean removeUser(int userHandle) {
505 enforceSystemOrRoot("Only the system can remove users");
Amith Yamasani13593602012-03-22 16:16:17 -0700506 synchronized (mUsers) {
Amith Yamasani258848d2012-08-10 17:06:33 -0700507 return removeUserLocked(userHandle);
Amith Yamasani13593602012-03-22 16:16:17 -0700508 }
509 }
510
Amith Yamasani258848d2012-08-10 17:06:33 -0700511 private boolean removeUserLocked(int userHandle) {
512 final UserInfo user = mUsers.get(userHandle);
513 if (userHandle == 0 || user == null) {
514 return false;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700515 }
Amith Yamasani258848d2012-08-10 17:06:33 -0700516
517 mPm.cleanUpUser(userHandle);
518
519 // Remove this user from the list
520 mUsers.remove(userHandle);
521 // Remove user file
522 File userFile = new File(mUsersDir, userHandle + ".xml");
523 userFile.delete();
524 // Update the user list
525 writeUserListLocked();
526 updateUserIdsLocked();
527
528 // Let other services shutdown any activity
529 Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED);
530 addedIntent.putExtra(Intent.EXTRA_USERID, userHandle);
531 mContext.sendBroadcast(addedIntent, android.Manifest.permission.MANAGE_ACCOUNTS);
532
533 removePackageFolders(userHandle);
534 return true;
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700535 }
536
Amith Yamasani0b285492011-04-14 17:35:23 -0700537 public void installPackageForAllUsers(String packageName, int uid) {
538 for (int userId : mUserIds) {
539 // Don't do it for the primary user, it will become recursive.
540 if (userId == 0)
541 continue;
Amith Yamasani742a6712011-05-04 14:49:28 -0700542 mInstaller.createUserData(packageName, UserId.getUid(userId, uid),
Amith Yamasani0b285492011-04-14 17:35:23 -0700543 userId);
544 }
545 }
546
547 public void clearUserDataForAllUsers(String packageName) {
548 for (int userId : mUserIds) {
549 // Don't do it for the primary user, it will become recursive.
550 if (userId == 0)
551 continue;
552 mInstaller.clearUserData(packageName, userId);
553 }
554 }
555
556 public void removePackageForAllUsers(String packageName) {
557 for (int userId : mUserIds) {
558 // Don't do it for the primary user, it will become recursive.
559 if (userId == 0)
560 continue;
561 mInstaller.remove(packageName, userId);
562 }
563 }
564
565 /**
566 * Caches the list of user ids in an array, adjusting the array size when necessary.
567 */
Amith Yamasani13593602012-03-22 16:16:17 -0700568 private void updateUserIdsLocked() {
Amith Yamasani0b285492011-04-14 17:35:23 -0700569 if (mUserIds == null || mUserIds.length != mUsers.size()) {
570 mUserIds = new int[mUsers.size()];
571 }
572 for (int i = 0; i < mUsers.size(); i++) {
573 mUserIds[i] = mUsers.keyAt(i);
574 }
575 }
576
577 /**
578 * Returns the next available user id, filling in any holes in the ids.
Amith Yamasani742a6712011-05-04 14:49:28 -0700579 * TODO: May not be a good idea to recycle ids, in case it results in confusion
580 * for data and battery stats collection, or unexpected cross-talk.
Amith Yamasani0b285492011-04-14 17:35:23 -0700581 * @return
582 */
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700583 private int getNextAvailableId() {
584 int i = 0;
585 while (i < Integer.MAX_VALUE) {
586 if (mUsers.indexOfKey(i) < 0) {
587 break;
588 }
589 i++;
590 }
591 return i;
592 }
593
Amith Yamasani13593602012-03-22 16:16:17 -0700594 private boolean createPackageFolders(int id, File userPath) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700595 // mInstaller may not be available for unit-tests.
Amith Yamasani13593602012-03-22 16:16:17 -0700596 if (mInstaller == null) return true;
Amith Yamasani0b285492011-04-14 17:35:23 -0700597
Amith Yamasani0b285492011-04-14 17:35:23 -0700598 // Create the user path
599 userPath.mkdir();
600 FileUtils.setPermissions(userPath.toString(), FileUtils.S_IRWXU | FileUtils.S_IRWXG
601 | FileUtils.S_IXOTH, -1, -1);
602
Amith Yamasani742a6712011-05-04 14:49:28 -0700603 mInstaller.cloneUserData(0, id, false);
604
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700605 return true;
606 }
607
Amith Yamasani13593602012-03-22 16:16:17 -0700608 boolean removePackageFolders(int id) {
Amith Yamasani0b285492011-04-14 17:35:23 -0700609 // mInstaller may not be available for unit-tests.
610 if (mInstaller == null) return true;
611
612 mInstaller.removeUserDataDirs(id);
Amith Yamasani4b2e9342011-03-31 12:38:53 -0700613 return true;
614 }
615}