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