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