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