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