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