UserManagerService.java revision 07a0ede729f9b2f472b659b67b4cbc3602aa289a
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 = updateIconBitmapLocked(info); 200 if (fd != null) { 201 writeUserLocked(info); 202 } 203 return fd; 204 } 205 } 206 207 @Override 208 public void setGuestEnabled(boolean enable) { 209 checkManageUsersPermission("enable guest users"); 210 synchronized (mPackagesLock) { 211 if (mGuestEnabled != enable) { 212 mGuestEnabled = enable; 213 // Erase any guest user that currently exists 214 for (int i = 0; i < mUsers.size(); i++) { 215 UserInfo user = mUsers.valueAt(i); 216 if (user.isGuest()) { 217 if (!enable) { 218 removeUser(user.id); 219 } 220 return; 221 } 222 } 223 // No guest was found 224 if (enable) { 225 createUser("Guest", UserInfo.FLAG_GUEST); 226 } 227 } 228 } 229 } 230 231 @Override 232 public boolean isGuestEnabled() { 233 synchronized (mPackagesLock) { 234 return mGuestEnabled; 235 } 236 } 237 238 @Override 239 public void wipeUser(int userHandle) { 240 checkManageUsersPermission("wipe user"); 241 // TODO: 242 } 243 244 public void makeInitialized(int userId) { 245 checkManageUsersPermission("makeInitialized"); 246 synchronized (mPackagesLock) { 247 UserInfo info = mUsers.get(userId); 248 if (info != null && (info.flags&UserInfo.FLAG_INITIALIZED) == 0) { 249 info.flags |= UserInfo.FLAG_INITIALIZED; 250 writeUserLocked(info); 251 } 252 } 253 } 254 255 /** 256 * Check if we've hit the limit of how many users can be created. 257 */ 258 private boolean isUserLimitReachedLocked() { 259 int nUsers = mUsers.size(); 260 return nUsers >= mUserLimit; 261 } 262 263 /** 264 * Enforces that only the system UID or root's UID or apps that have the 265 * {@link android.Manifest.permission.MANAGE_USERS MANAGE_USERS} 266 * permission can make certain calls to the UserManager. 267 * 268 * @param message used as message if SecurityException is thrown 269 * @throws SecurityException if the caller is not system or root 270 */ 271 private static final void checkManageUsersPermission(String message) { 272 final int uid = Binder.getCallingUid(); 273 if (uid != Process.SYSTEM_UID && uid != 0 274 && ActivityManager.checkComponentPermission( 275 android.Manifest.permission.MANAGE_USERS, 276 uid, -1, true) != PackageManager.PERMISSION_GRANTED) { 277 throw new SecurityException("You need MANAGE_USERS permission to: " + message); 278 } 279 } 280 281 private ParcelFileDescriptor updateIconBitmapLocked(UserInfo info) { 282 try { 283 File dir = new File(mUsersDir, Integer.toString(info.id)); 284 File file = new File(dir, USER_PHOTO_FILENAME); 285 if (!dir.exists()) { 286 dir.mkdir(); 287 FileUtils.setPermissions( 288 dir.getPath(), 289 FileUtils.S_IRWXU|FileUtils.S_IRWXG|FileUtils.S_IXOTH, 290 -1, -1); 291 } 292 ParcelFileDescriptor fd = ParcelFileDescriptor.open(file, 293 MODE_CREATE|MODE_READ_WRITE); 294 info.iconPath = file.getAbsolutePath(); 295 return fd; 296 } catch (FileNotFoundException e) { 297 Slog.w(LOG_TAG, "Error setting photo for user ", e); 298 } 299 return null; 300 } 301 302 /** 303 * Returns an array of user ids. This array is cached here for quick access, so do not modify or 304 * cache it elsewhere. 305 * @return the array of user ids. 306 */ 307 public int[] getUserIds() { 308 synchronized (mPackagesLock) { 309 return mUserIds; 310 } 311 } 312 313 int[] getUserIdsLPr() { 314 return mUserIds; 315 } 316 317 private void readUserList() { 318 synchronized (mPackagesLock) { 319 readUserListLocked(); 320 } 321 } 322 323 private void readUserListLocked() { 324 mGuestEnabled = false; 325 if (!mUserListFile.exists()) { 326 fallbackToSingleUserLocked(); 327 return; 328 } 329 FileInputStream fis = null; 330 AtomicFile userListFile = new AtomicFile(mUserListFile); 331 try { 332 fis = userListFile.openRead(); 333 XmlPullParser parser = Xml.newPullParser(); 334 parser.setInput(fis, null); 335 int type; 336 while ((type = parser.next()) != XmlPullParser.START_TAG 337 && type != XmlPullParser.END_DOCUMENT) { 338 ; 339 } 340 341 if (type != XmlPullParser.START_TAG) { 342 Slog.e(LOG_TAG, "Unable to read user list"); 343 fallbackToSingleUserLocked(); 344 return; 345 } 346 347 mNextSerialNumber = -1; 348 if (parser.getName().equals(TAG_USERS)) { 349 String lastSerialNumber = parser.getAttributeValue(null, ATTR_NEXT_SERIAL_NO); 350 if (lastSerialNumber != null) { 351 mNextSerialNumber = Integer.parseInt(lastSerialNumber); 352 } 353 } 354 355 while ((type = parser.next()) != XmlPullParser.END_DOCUMENT) { 356 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { 357 String id = parser.getAttributeValue(null, ATTR_ID); 358 UserInfo user = readUser(Integer.parseInt(id)); 359 if (user != null) { 360 mUsers.put(user.id, user); 361 if (user.isGuest()) { 362 mGuestEnabled = true; 363 } 364 if (mNextSerialNumber < 0 || mNextSerialNumber <= user.id) { 365 mNextSerialNumber = user.id + 1; 366 } 367 } 368 } 369 } 370 updateUserIdsLocked(); 371 } catch (IOException ioe) { 372 fallbackToSingleUserLocked(); 373 } catch (XmlPullParserException pe) { 374 fallbackToSingleUserLocked(); 375 } finally { 376 if (fis != null) { 377 try { 378 fis.close(); 379 } catch (IOException e) { 380 } 381 } 382 } 383 } 384 385 private void fallbackToSingleUserLocked() { 386 // Create the primary user 387 UserInfo primary = new UserInfo(0, "Primary", null, 388 UserInfo.FLAG_ADMIN | UserInfo.FLAG_PRIMARY); 389 mUsers.put(0, primary); 390 updateUserIdsLocked(); 391 392 writeUserListLocked(); 393 writeUserLocked(primary); 394 } 395 396 /* 397 * Writes the user file in this format: 398 * 399 * <user flags="20039023" id="0"> 400 * <name>Primary</name> 401 * </user> 402 */ 403 private void writeUserLocked(UserInfo userInfo) { 404 FileOutputStream fos = null; 405 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userInfo.id + ".xml")); 406 try { 407 fos = userFile.startWrite(); 408 final BufferedOutputStream bos = new BufferedOutputStream(fos); 409 410 // XmlSerializer serializer = XmlUtils.serializerInstance(); 411 final XmlSerializer serializer = new FastXmlSerializer(); 412 serializer.setOutput(bos, "utf-8"); 413 serializer.startDocument(null, true); 414 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 415 416 serializer.startTag(null, TAG_USER); 417 serializer.attribute(null, ATTR_ID, Integer.toString(userInfo.id)); 418 serializer.attribute(null, ATTR_SERIAL_NO, Integer.toString(userInfo.serialNumber)); 419 serializer.attribute(null, ATTR_FLAGS, Integer.toString(userInfo.flags)); 420 if (userInfo.iconPath != null) { 421 serializer.attribute(null, ATTR_ICON_PATH, userInfo.iconPath); 422 } 423 424 serializer.startTag(null, TAG_NAME); 425 serializer.text(userInfo.name); 426 serializer.endTag(null, TAG_NAME); 427 428 serializer.endTag(null, TAG_USER); 429 430 serializer.endDocument(); 431 userFile.finishWrite(fos); 432 } catch (Exception ioe) { 433 Slog.e(LOG_TAG, "Error writing user info " + userInfo.id + "\n" + ioe); 434 userFile.failWrite(fos); 435 } 436 } 437 438 /* 439 * Writes the user list file in this format: 440 * 441 * <users nextSerialNumber="3"> 442 * <user id="0"></user> 443 * <user id="2"></user> 444 * </users> 445 */ 446 private void writeUserListLocked() { 447 FileOutputStream fos = null; 448 AtomicFile userListFile = new AtomicFile(mUserListFile); 449 try { 450 fos = userListFile.startWrite(); 451 final BufferedOutputStream bos = new BufferedOutputStream(fos); 452 453 // XmlSerializer serializer = XmlUtils.serializerInstance(); 454 final XmlSerializer serializer = new FastXmlSerializer(); 455 serializer.setOutput(bos, "utf-8"); 456 serializer.startDocument(null, true); 457 serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true); 458 459 serializer.startTag(null, TAG_USERS); 460 serializer.attribute(null, ATTR_NEXT_SERIAL_NO, Integer.toString(mNextSerialNumber)); 461 462 for (int i = 0; i < mUsers.size(); i++) { 463 UserInfo user = mUsers.valueAt(i); 464 serializer.startTag(null, TAG_USER); 465 serializer.attribute(null, ATTR_ID, Integer.toString(user.id)); 466 serializer.endTag(null, TAG_USER); 467 } 468 469 serializer.endTag(null, TAG_USERS); 470 471 serializer.endDocument(); 472 userListFile.finishWrite(fos); 473 } catch (Exception e) { 474 userListFile.failWrite(fos); 475 Slog.e(LOG_TAG, "Error writing user list"); 476 } 477 } 478 479 private UserInfo readUser(int id) { 480 int flags = 0; 481 int serialNumber = id; 482 String name = null; 483 String iconPath = null; 484 485 FileInputStream fis = null; 486 try { 487 AtomicFile userFile = 488 new AtomicFile(new File(mUsersDir, Integer.toString(id) + ".xml")); 489 fis = userFile.openRead(); 490 XmlPullParser parser = Xml.newPullParser(); 491 parser.setInput(fis, null); 492 int type; 493 while ((type = parser.next()) != XmlPullParser.START_TAG 494 && type != XmlPullParser.END_DOCUMENT) { 495 ; 496 } 497 498 if (type != XmlPullParser.START_TAG) { 499 Slog.e(LOG_TAG, "Unable to read user " + id); 500 return null; 501 } 502 503 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_USER)) { 504 String storedId = parser.getAttributeValue(null, ATTR_ID); 505 if (Integer.parseInt(storedId) != id) { 506 Slog.e(LOG_TAG, "User id does not match the file name"); 507 return null; 508 } 509 String serialNumberValue = parser.getAttributeValue(null, ATTR_SERIAL_NO); 510 if (serialNumberValue != null) { 511 serialNumber = Integer.parseInt(serialNumberValue); 512 } 513 String flagString = parser.getAttributeValue(null, ATTR_FLAGS); 514 flags = Integer.parseInt(flagString); 515 iconPath = parser.getAttributeValue(null, ATTR_ICON_PATH); 516 517 while ((type = parser.next()) != XmlPullParser.START_TAG 518 && type != XmlPullParser.END_DOCUMENT) { 519 } 520 if (type == XmlPullParser.START_TAG && parser.getName().equals(TAG_NAME)) { 521 type = parser.next(); 522 if (type == XmlPullParser.TEXT) { 523 name = parser.getText(); 524 } 525 } 526 } 527 528 UserInfo userInfo = new UserInfo(id, name, iconPath, flags); 529 userInfo.serialNumber = serialNumber; 530 return userInfo; 531 532 } catch (IOException ioe) { 533 } catch (XmlPullParserException pe) { 534 } finally { 535 if (fis != null) { 536 try { 537 fis.close(); 538 } catch (IOException e) { 539 } 540 } 541 } 542 return null; 543 } 544 545 @Override 546 public UserInfo createUser(String name, int flags) { 547 checkManageUsersPermission("Only the system can create users"); 548 549 final long ident = Binder.clearCallingIdentity(); 550 final UserInfo userInfo; 551 try { 552 synchronized (mInstallLock) { 553 synchronized (mPackagesLock) { 554 if (isUserLimitReachedLocked()) return null; 555 int userId = getNextAvailableIdLocked(); 556 userInfo = new UserInfo(userId, name, null, flags); 557 File userPath = new File(mBaseUserPath, Integer.toString(userId)); 558 userInfo.serialNumber = mNextSerialNumber++; 559 mUsers.put(userId, userInfo); 560 writeUserListLocked(); 561 writeUserLocked(userInfo); 562 updateUserIdsLocked(); 563 mPm.createNewUserLILPw(userId, userPath); 564 } 565 } 566 if (userInfo != null) { 567 Intent addedIntent = new Intent(Intent.ACTION_USER_ADDED); 568 addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userInfo.id); 569 mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, 570 android.Manifest.permission.MANAGE_USERS); 571 } 572 } finally { 573 Binder.restoreCallingIdentity(ident); 574 } 575 return userInfo; 576 } 577 578 /** 579 * Removes a user and all data directories created for that user. This method should be called 580 * after the user's processes have been terminated. 581 * @param id the user's id 582 */ 583 public boolean removeUser(int userHandle) { 584 checkManageUsersPermission("Only the system can remove users"); 585 final UserInfo user; 586 synchronized (mPackagesLock) { 587 user = mUsers.get(userHandle); 588 if (userHandle == 0 || user == null) { 589 return false; 590 } 591 } 592 593 int res; 594 try { 595 res = ActivityManagerNative.getDefault().stopUser(userHandle, 596 new IStopUserCallback.Stub() { 597 @Override 598 public void userStopped(int userId) { 599 finishRemoveUser(userId); 600 } 601 @Override 602 public void userStopAborted(int userId) { 603 } 604 }); 605 } catch (RemoteException e) { 606 return false; 607 } 608 609 return res == ActivityManager.USER_OP_SUCCESS; 610 } 611 612 void finishRemoveUser(int userHandle) { 613 synchronized (mInstallLock) { 614 synchronized (mPackagesLock) { 615 // Cleanup package manager settings 616 mPm.cleanUpUserLILPw(userHandle); 617 618 // Remove this user from the list 619 mUsers.remove(userHandle); 620 // Remove user file 621 AtomicFile userFile = new AtomicFile(new File(mUsersDir, userHandle + ".xml")); 622 userFile.delete(); 623 // Update the user list 624 writeUserListLocked(); 625 updateUserIdsLocked(); 626 removeDirectoryRecursive(Environment.getUserSystemDirectory(userHandle)); 627 } 628 } 629 630 // Let other services shutdown any activity 631 long ident = Binder.clearCallingIdentity(); 632 try { 633 Intent addedIntent = new Intent(Intent.ACTION_USER_REMOVED); 634 addedIntent.putExtra(Intent.EXTRA_USER_HANDLE, userHandle); 635 mContext.sendBroadcastAsUser(addedIntent, UserHandle.ALL, 636 android.Manifest.permission.MANAGE_USERS); 637 } finally { 638 Binder.restoreCallingIdentity(ident); 639 } 640 } 641 642 private void removeDirectoryRecursive(File parent) { 643 if (parent.isDirectory()) { 644 String[] files = parent.list(); 645 for (String filename : files) { 646 File child = new File(parent, filename); 647 removeDirectoryRecursive(child); 648 } 649 } 650 parent.delete(); 651 } 652 653 @Override 654 public int getUserSerialNumber(int userHandle) { 655 synchronized (mPackagesLock) { 656 if (!exists(userHandle)) return -1; 657 return getUserInfoLocked(userHandle).serialNumber; 658 } 659 } 660 661 @Override 662 public int getUserHandle(int userSerialNumber) { 663 synchronized (mPackagesLock) { 664 for (int userId : mUserIds) { 665 if (getUserInfoLocked(userId).serialNumber == userSerialNumber) return userId; 666 } 667 // Not found 668 return -1; 669 } 670 } 671 672 /** 673 * Caches the list of user ids in an array, adjusting the array size when necessary. 674 */ 675 private void updateUserIdsLocked() { 676 int[] newUsers = new int[mUsers.size()]; 677 for (int i = 0; i < mUsers.size(); i++) { 678 newUsers[i] = mUsers.keyAt(i); 679 } 680 mUserIds = newUsers; 681 } 682 683 /** 684 * Returns the next available user id, filling in any holes in the ids. 685 * TODO: May not be a good idea to recycle ids, in case it results in confusion 686 * for data and battery stats collection, or unexpected cross-talk. 687 * @return 688 */ 689 private int getNextAvailableIdLocked() { 690 synchronized (mPackagesLock) { 691 int i = 10; 692 while (i < Integer.MAX_VALUE) { 693 if (mUsers.indexOfKey(i) < 0) { 694 break; 695 } 696 i++; 697 } 698 return i; 699 } 700 } 701} 702