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