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