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