1 /* 2 * Copyright (C) 2012 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.settings.users; 18 19 import static android.os.Process.myUserHandle; 20 21 import android.app.Activity; 22 import android.app.ActivityManager; 23 import android.app.Dialog; 24 import android.app.admin.DevicePolicyManager; 25 import android.app.settings.SettingsEnums; 26 import android.content.BroadcastReceiver; 27 import android.content.Context; 28 import android.content.DialogInterface; 29 import android.content.Intent; 30 import android.content.IntentFilter; 31 import android.content.SharedPreferences; 32 import android.content.pm.UserInfo; 33 import android.content.res.Resources; 34 import android.graphics.Bitmap; 35 import android.graphics.BitmapFactory; 36 import android.graphics.drawable.Drawable; 37 import android.net.Uri; 38 import android.os.AsyncTask; 39 import android.os.Bundle; 40 import android.os.Handler; 41 import android.os.Message; 42 import android.os.RemoteException; 43 import android.os.UserHandle; 44 import android.os.UserManager; 45 import android.provider.ContactsContract; 46 import android.util.Log; 47 import android.util.SparseArray; 48 import android.view.Menu; 49 import android.view.MenuInflater; 50 import android.view.MenuItem; 51 import android.widget.SimpleAdapter; 52 53 import androidx.annotation.VisibleForTesting; 54 import androidx.annotation.WorkerThread; 55 import androidx.appcompat.app.AlertDialog; 56 import androidx.preference.Preference; 57 import androidx.preference.PreferenceGroup; 58 import androidx.preference.PreferenceScreen; 59 60 import com.android.internal.util.UserIcons; 61 import com.android.internal.widget.LockPatternUtils; 62 import com.android.settings.R; 63 import com.android.settings.SettingsActivity; 64 import com.android.settings.SettingsPreferenceFragment; 65 import com.android.settings.Utils; 66 import com.android.settings.core.SubSettingLauncher; 67 import com.android.settings.password.ChooseLockGeneric; 68 import com.android.settings.search.BaseSearchIndexProvider; 69 import com.android.settings.widget.SwitchBar; 70 import com.android.settings.widget.SwitchBarController; 71 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 72 import com.android.settingslib.RestrictedLockUtilsInternal; 73 import com.android.settingslib.RestrictedPreference; 74 import com.android.settingslib.drawable.CircleFramedDrawable; 75 import com.android.settingslib.search.SearchIndexable; 76 import com.android.settingslib.utils.ThreadUtils; 77 78 import com.google.android.setupcompat.util.WizardManagerHelper; 79 80 import java.io.IOException; 81 import java.io.InputStream; 82 import java.util.ArrayList; 83 import java.util.Collections; 84 import java.util.HashMap; 85 import java.util.List; 86 import java.util.Random; 87 88 /** 89 * Screen that manages the list of users on the device. 90 * Secondary users and a guest user can be created if there is no restriction. 91 * 92 * The first user in the list is always the current user. 93 * Owner is the primary user. 94 */ 95 @SearchIndexable 96 public class UserSettings extends SettingsPreferenceFragment 97 implements Preference.OnPreferenceClickListener, 98 MultiUserSwitchBarController.OnMultiUserSwitchChangedListener, 99 DialogInterface.OnDismissListener { 100 101 private static final String TAG = "UserSettings"; 102 103 /** UserId of the user being removed */ 104 private static final String SAVE_REMOVING_USER = "removing_user"; 105 106 private static final String KEY_USER_LIST = "user_list"; 107 private static final String KEY_USER_ME = "user_me"; 108 private static final String KEY_USER_GUEST = "user_guest"; 109 private static final String KEY_ADD_GUEST = "guest_add"; 110 private static final String KEY_ADD_USER = "user_add"; 111 private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked"; 112 private static final String KEY_MULTIUSER_FOOTER = "multiuser_footer"; 113 114 private static final int MENU_REMOVE_USER = Menu.FIRST; 115 116 private static final IntentFilter USER_REMOVED_INTENT_FILTER; 117 118 private static final int DIALOG_CONFIRM_REMOVE = 1; 119 private static final int DIALOG_ADD_USER = 2; 120 // Dialogs with id 3 and 4 got removed 121 private static final int DIALOG_USER_CANNOT_MANAGE = 5; 122 private static final int DIALOG_CHOOSE_USER_TYPE = 6; 123 private static final int DIALOG_NEED_LOCKSCREEN = 7; 124 private static final int DIALOG_CONFIRM_EXIT_GUEST = 8; 125 private static final int DIALOG_USER_PROFILE_EDITOR = 9; 126 private static final int DIALOG_USER_PROFILE_EDITOR_ADD_USER = 10; 127 private static final int DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE = 11; 128 129 private static final int MESSAGE_UPDATE_LIST = 1; 130 private static final int MESSAGE_USER_CREATED = 2; 131 132 private static final int USER_TYPE_USER = 1; 133 private static final int USER_TYPE_RESTRICTED_PROFILE = 2; 134 135 private static final int REQUEST_CHOOSE_LOCK = 10; 136 137 private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED = 138 "key_add_user_long_message_displayed"; 139 140 private static final String KEY_TITLE = "title"; 141 private static final String KEY_SUMMARY = "summary"; 142 143 static { 144 USER_REMOVED_INTENT_FILTER = new IntentFilter(Intent.ACTION_USER_REMOVED); 145 USER_REMOVED_INTENT_FILTER.addAction(Intent.ACTION_USER_INFO_CHANGED); 146 } 147 148 @VisibleForTesting 149 PreferenceGroup mUserListCategory; 150 @VisibleForTesting 151 UserPreference mMePreference; 152 @VisibleForTesting 153 RestrictedPreference mAddGuest; 154 @VisibleForTesting 155 RestrictedPreference mAddUser; 156 @VisibleForTesting 157 SparseArray<Bitmap> mUserIcons = new SparseArray<>(); 158 private int mRemovingUserId = -1; 159 private boolean mAddingUser; 160 private String mAddingUserName; 161 private UserCapabilities mUserCaps; 162 private boolean mShouldUpdateUserList = true; 163 private final Object mUserLock = new Object(); 164 private UserManager mUserManager; 165 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>(); 166 167 private MultiUserSwitchBarController mSwitchBarController; 168 private EditUserInfoController mEditUserInfoController = new EditUserInfoController(); 169 private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController; 170 private MultiUserFooterPreferenceController mMultiUserFooterPreferenceController; 171 172 private CharSequence mPendingUserName; 173 private Drawable mPendingUserIcon; 174 175 // A place to cache the generated default avatar 176 private Drawable mDefaultIconDrawable; 177 178 private Handler mHandler = new Handler() { 179 @Override 180 public void handleMessage(Message msg) { 181 switch (msg.what) { 182 case MESSAGE_UPDATE_LIST: 183 updateUserList(); 184 break; 185 case MESSAGE_USER_CREATED: 186 onUserCreated(msg.arg1); 187 break; 188 } 189 } 190 }; 191 192 private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() { 193 @Override 194 public void onReceive(Context context, Intent intent) { 195 if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) { 196 mRemovingUserId = -1; 197 } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) { 198 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 199 if (userHandle != -1) { 200 mUserIcons.remove(userHandle); 201 } 202 } 203 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 204 } 205 }; 206 207 @Override getMetricsCategory()208 public int getMetricsCategory() { 209 return SettingsEnums.USER; 210 } 211 212 @Override onActivityCreated(Bundle savedInstanceState)213 public void onActivityCreated(Bundle savedInstanceState) { 214 super.onActivityCreated(savedInstanceState); 215 // Assume we are in a SettingsActivity. This is only safe because we currently use 216 // SettingsActivity as base for all preference fragments. 217 final SettingsActivity activity = (SettingsActivity) getActivity(); 218 final SwitchBar switchBar = activity.getSwitchBar(); 219 mSwitchBarController = new MultiUserSwitchBarController(activity, 220 new SwitchBarController(switchBar), this /* listener */); 221 getSettingsLifecycle().addObserver(mSwitchBarController); 222 switchBar.show(); 223 } 224 225 @Override onCreate(Bundle icicle)226 public void onCreate(Bundle icicle) { 227 super.onCreate(icicle); 228 addPreferencesFromResource(R.xml.user_settings); 229 final Activity activity = getActivity(); 230 if (!WizardManagerHelper.isDeviceProvisioned(activity)) { 231 activity.finish(); 232 return; 233 } 234 235 mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController( 236 activity, KEY_ADD_USER_WHEN_LOCKED); 237 238 mMultiUserFooterPreferenceController = new MultiUserFooterPreferenceController(activity, 239 KEY_MULTIUSER_FOOTER); 240 241 final PreferenceScreen screen = getPreferenceScreen(); 242 mAddUserWhenLockedPreferenceController.displayPreference(screen); 243 mMultiUserFooterPreferenceController.displayPreference(screen); 244 245 screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey()) 246 .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController); 247 248 if (icicle != null) { 249 if (icicle.containsKey(SAVE_REMOVING_USER)) { 250 mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER); 251 } 252 mEditUserInfoController.onRestoreInstanceState(icicle); 253 } 254 255 mUserCaps = UserCapabilities.create(activity); 256 mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE); 257 if (!mUserCaps.mEnabled) { 258 return; 259 } 260 261 final int myUserId = UserHandle.myUserId(); 262 263 mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST); 264 mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId); 265 mMePreference.setKey(KEY_USER_ME); 266 mMePreference.setOnPreferenceClickListener(this); 267 if (mUserCaps.mIsAdmin) { 268 mMePreference.setSummary(R.string.user_admin); 269 } 270 271 mAddGuest = findPreference(KEY_ADD_GUEST); 272 mAddGuest.setOnPreferenceClickListener(this); 273 274 mAddUser = findPreference(KEY_ADD_USER); 275 if (!mUserCaps.mCanAddRestrictedProfile) { 276 // Label should only mention adding a "user", not a "profile" 277 mAddUser.setTitle(R.string.user_add_user_menu); 278 } 279 mAddUser.setOnPreferenceClickListener(this); 280 281 activity.registerReceiverAsUser( 282 mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler); 283 284 updateUI(); 285 mShouldUpdateUserList = false; 286 } 287 288 @Override onResume()289 public void onResume() { 290 super.onResume(); 291 292 if (!mUserCaps.mEnabled) { 293 return; 294 } 295 final PreferenceScreen screen = getPreferenceScreen(); 296 297 mAddUserWhenLockedPreferenceController.updateState(screen.findPreference( 298 mAddUserWhenLockedPreferenceController.getPreferenceKey())); 299 300 if (mShouldUpdateUserList) { 301 updateUI(); 302 } 303 } 304 305 @Override onPause()306 public void onPause() { 307 mShouldUpdateUserList = true; 308 super.onPause(); 309 } 310 311 @Override onDestroy()312 public void onDestroy() { 313 super.onDestroy(); 314 315 if (mUserCaps == null || !mUserCaps.mEnabled) { 316 return; 317 } 318 319 getActivity().unregisterReceiver(mUserChangeReceiver); 320 } 321 322 @Override onSaveInstanceState(Bundle outState)323 public void onSaveInstanceState(Bundle outState) { 324 super.onSaveInstanceState(outState); 325 mEditUserInfoController.onSaveInstanceState(outState); 326 outState.putInt(SAVE_REMOVING_USER, mRemovingUserId); 327 } 328 329 @Override startActivityForResult(Intent intent, int requestCode)330 public void startActivityForResult(Intent intent, int requestCode) { 331 mEditUserInfoController.startingActivityForResult(); 332 super.startActivityForResult(intent, requestCode); 333 } 334 335 @Override onCreateOptionsMenu(Menu menu, MenuInflater inflater)336 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { 337 int pos = 0; 338 if (!mUserCaps.mIsAdmin && canSwitchUserNow()) { 339 String nickname = mUserManager.getUserName(); 340 MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++, 341 getResources().getString(R.string.user_remove_user_menu, nickname)); 342 removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER); 343 344 final EnforcedAdmin disallowRemoveUserAdmin = 345 RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getContext(), 346 UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId()); 347 RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getContext(), removeThisUser, 348 disallowRemoveUserAdmin); 349 } 350 super.onCreateOptionsMenu(menu, inflater); 351 } 352 353 @Override onOptionsItemSelected(MenuItem item)354 public boolean onOptionsItemSelected(MenuItem item) { 355 final int itemId = item.getItemId(); 356 if (itemId == MENU_REMOVE_USER) { 357 onRemoveUserClicked(UserHandle.myUserId()); 358 return true; 359 } else { 360 return super.onOptionsItemSelected(item); 361 } 362 } 363 364 @Override onMultiUserSwitchChanged(boolean newState)365 public void onMultiUserSwitchChanged(boolean newState) { 366 updateUI(); 367 } 368 updateUI()369 private void updateUI() { 370 mUserCaps.updateAddUserCapabilities(getActivity()); 371 loadProfile(); 372 updateUserList(); 373 } 374 375 /** 376 * Loads profile information for the current user. 377 */ loadProfile()378 private void loadProfile() { 379 if (isCurrentUserGuest()) { 380 // No need to load profile information 381 mMePreference.setIcon(getEncircledDefaultIcon()); 382 mMePreference.setTitle(R.string.user_exit_guest_title); 383 mMePreference.setSelectable(true); 384 // removing a guest will result in switching back to the admin user 385 mMePreference.setEnabled(canSwitchUserNow()); 386 return; 387 } 388 389 new AsyncTask<Void, Void, String>() { 390 @Override 391 protected void onPostExecute(String result) { 392 finishLoadProfile(result); 393 } 394 395 @Override 396 protected String doInBackground(Void... values) { 397 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId()); 398 if (user.iconPath == null || user.iconPath.equals("")) { 399 // Assign profile photo. 400 copyMeProfilePhoto(getActivity(), user); 401 } 402 return user.name; 403 } 404 }.execute(); 405 } 406 finishLoadProfile(String profileName)407 private void finishLoadProfile(String profileName) { 408 if (getActivity() == null) { 409 return; 410 } 411 mMePreference.setTitle(getString(R.string.user_you, profileName)); 412 int myUserId = UserHandle.myUserId(); 413 Bitmap b = mUserManager.getUserIcon(myUserId); 414 if (b != null) { 415 mMePreference.setIcon(encircle(b)); 416 mUserIcons.put(myUserId, b); 417 } 418 } 419 hasLockscreenSecurity()420 private boolean hasLockscreenSecurity() { 421 LockPatternUtils lpu = new LockPatternUtils(getActivity()); 422 return lpu.isSecure(UserHandle.myUserId()); 423 } 424 launchChooseLockscreen()425 private void launchChooseLockscreen() { 426 Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD); 427 chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY, 428 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING); 429 startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK); 430 } 431 432 @Override onActivityResult(int requestCode, int resultCode, Intent data)433 public void onActivityResult(int requestCode, int resultCode, Intent data) { 434 super.onActivityResult(requestCode, resultCode, data); 435 436 if (requestCode == REQUEST_CHOOSE_LOCK) { 437 if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) { 438 addUserNow(USER_TYPE_RESTRICTED_PROFILE); 439 } 440 } else { 441 mEditUserInfoController.onActivityResult(requestCode, resultCode, data); 442 } 443 } 444 onAddUserClicked(int userType)445 private void onAddUserClicked(int userType) { 446 synchronized (mUserLock) { 447 if (mRemovingUserId == -1 && !mAddingUser) { 448 switch (userType) { 449 case USER_TYPE_USER: 450 showDialog(DIALOG_ADD_USER); 451 break; 452 case USER_TYPE_RESTRICTED_PROFILE: 453 if (hasLockscreenSecurity()) { 454 showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE); 455 } else { 456 showDialog(DIALOG_NEED_LOCKSCREEN); 457 } 458 break; 459 } 460 } 461 } 462 } 463 onRemoveUserClicked(int userId)464 private void onRemoveUserClicked(int userId) { 465 synchronized (mUserLock) { 466 if (mRemovingUserId == -1 && !mAddingUser) { 467 mRemovingUserId = userId; 468 showDialog(DIALOG_CONFIRM_REMOVE); 469 } 470 } 471 } 472 onUserCreated(int userId)473 private void onUserCreated(int userId) { 474 mAddingUser = false; 475 UserInfo userInfo = mUserManager.getUserInfo(userId); 476 openUserDetails(userInfo, true); 477 } 478 openUserDetails(UserInfo userInfo, boolean newUser)479 private void openUserDetails(UserInfo userInfo, boolean newUser) { 480 Bundle extras = new Bundle(); 481 extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userInfo.id); 482 extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, newUser); 483 new SubSettingLauncher(getContext()) 484 .setDestination(UserDetailsSettings.class.getName()) 485 .setArguments(extras) 486 .setTitleText(userInfo.name) 487 .setSourceMetricsCategory(getMetricsCategory()) 488 .launch(); 489 } 490 491 @Override onDialogShowing()492 public void onDialogShowing() { 493 super.onDialogShowing(); 494 495 setOnDismissListener(this); 496 } 497 498 @Override onCreateDialog(int dialogId)499 public Dialog onCreateDialog(int dialogId) { 500 Context context = getActivity(); 501 if (context == null) { 502 return null; 503 } 504 switch (dialogId) { 505 case DIALOG_CONFIRM_REMOVE: { 506 Dialog dlg = 507 UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId, 508 new DialogInterface.OnClickListener() { 509 public void onClick(DialogInterface dialog, int which) { 510 removeUserNow(); 511 } 512 } 513 ); 514 return dlg; 515 } 516 case DIALOG_USER_CANNOT_MANAGE: 517 return new AlertDialog.Builder(context) 518 .setMessage(R.string.user_cannot_manage_message) 519 .setPositiveButton(android.R.string.ok, null) 520 .create(); 521 case DIALOG_ADD_USER: { 522 final SharedPreferences preferences = getActivity().getPreferences( 523 Context.MODE_PRIVATE); 524 final boolean longMessageDisplayed = preferences.getBoolean( 525 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false); 526 final int messageResId = longMessageDisplayed 527 ? com.android.settingslib.R.string.user_add_user_message_short 528 : com.android.settingslib.R.string.user_add_user_message_long; 529 Dialog dlg = new AlertDialog.Builder(context) 530 .setTitle(com.android.settingslib.R.string.user_add_user_title) 531 .setMessage(messageResId) 532 .setPositiveButton(android.R.string.ok, 533 new DialogInterface.OnClickListener() { 534 public void onClick(DialogInterface dialog, int which) { 535 showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER); 536 if (!longMessageDisplayed) { 537 preferences.edit().putBoolean( 538 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, 539 true).apply(); 540 } 541 } 542 }) 543 .setNegativeButton(android.R.string.cancel, null) 544 .create(); 545 return dlg; 546 } 547 case DIALOG_CHOOSE_USER_TYPE: { 548 List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>(); 549 HashMap<String, String> addUserItem = new HashMap<String, String>(); 550 addUserItem.put(KEY_TITLE, getString( 551 com.android.settingslib.R.string.user_add_user_item_title)); 552 addUserItem.put(KEY_SUMMARY, getString( 553 com.android.settingslib.R.string.user_add_user_item_summary)); 554 HashMap<String, String> addProfileItem = new HashMap<String, String>(); 555 addProfileItem.put(KEY_TITLE, getString( 556 com.android.settingslib.R.string.user_add_profile_item_title)); 557 addProfileItem.put(KEY_SUMMARY, getString( 558 com.android.settingslib.R.string.user_add_profile_item_summary)); 559 data.add(addUserItem); 560 data.add(addProfileItem); 561 AlertDialog.Builder builder = new AlertDialog.Builder(context); 562 SimpleAdapter adapter = new SimpleAdapter(builder.getContext(), 563 data, R.layout.two_line_list_item, 564 new String[]{KEY_TITLE, KEY_SUMMARY}, 565 new int[]{R.id.title, R.id.summary}); 566 builder.setTitle(com.android.settingslib.R.string.user_add_user_type_title); 567 builder.setAdapter(adapter, 568 new DialogInterface.OnClickListener() { 569 @Override 570 public void onClick(DialogInterface dialog, int which) { 571 onAddUserClicked(which == 0 572 ? USER_TYPE_USER 573 : USER_TYPE_RESTRICTED_PROFILE); 574 } 575 }); 576 return builder.create(); 577 } 578 case DIALOG_NEED_LOCKSCREEN: { 579 Dialog dlg = new AlertDialog.Builder(context) 580 .setMessage(com.android.settingslib.R.string.user_need_lock_message) 581 .setPositiveButton(com.android.settingslib.R.string.user_set_lock_button, 582 new DialogInterface.OnClickListener() { 583 @Override 584 public void onClick(DialogInterface dialog, int which) { 585 launchChooseLockscreen(); 586 } 587 }) 588 .setNegativeButton(android.R.string.cancel, null) 589 .create(); 590 return dlg; 591 } 592 case DIALOG_CONFIRM_EXIT_GUEST: { 593 Dialog dlg = new AlertDialog.Builder(context) 594 .setTitle(R.string.user_exit_guest_confirm_title) 595 .setMessage(R.string.user_exit_guest_confirm_message) 596 .setPositiveButton(R.string.user_exit_guest_dialog_remove, 597 new DialogInterface.OnClickListener() { 598 @Override 599 public void onClick(DialogInterface dialog, int which) { 600 exitGuest(); 601 } 602 }) 603 .setNegativeButton(android.R.string.cancel, null) 604 .create(); 605 return dlg; 606 } 607 case DIALOG_USER_PROFILE_EDITOR: { 608 UserHandle user = myUserHandle(); 609 UserInfo info = mUserManager.getUserInfo(user.getIdentifier()); 610 return mEditUserInfoController.createDialog( 611 this, 612 Utils.getUserIcon(getPrefContext(), mUserManager, info), 613 info.name, 614 getString(com.android.settingslib.R.string.profile_info_settings_title), 615 new EditUserInfoController.OnContentChangedCallback() { 616 @Override 617 public void onPhotoChanged(UserHandle user, Drawable photo) { 618 ThreadUtils.postOnBackgroundThread(new Runnable() { 619 @Override 620 public void run() { 621 mUserManager.setUserIcon(user.getIdentifier(), 622 UserIcons.convertToBitmap(photo)); 623 } 624 }); 625 mMePreference.setIcon(photo); 626 } 627 628 @Override 629 public void onLabelChanged(UserHandle user, CharSequence label) { 630 mMePreference.setTitle(label.toString()); 631 mUserManager.setUserName(user.getIdentifier(), label.toString()); 632 } 633 }, 634 user, 635 null); 636 } 637 case DIALOG_USER_PROFILE_EDITOR_ADD_USER: { 638 synchronized (mUserLock) { 639 mPendingUserIcon = UserIcons.getDefaultUserIcon(getPrefContext().getResources(), 640 new Random(System.currentTimeMillis()).nextInt(8), false); 641 mPendingUserName = getString( 642 com.android.settingslib.R.string.user_new_user_name); 643 } 644 return buildAddUserProfileEditorDialog(USER_TYPE_USER); 645 } 646 case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE: { 647 synchronized (mUserLock) { 648 mPendingUserIcon = UserIcons.getDefaultUserIcon(getPrefContext().getResources(), 649 new Random(System.currentTimeMillis()).nextInt(8), false); 650 mPendingUserName = getString( 651 com.android.settingslib.R.string.user_new_profile_name); 652 } 653 return buildAddUserProfileEditorDialog(USER_TYPE_RESTRICTED_PROFILE); 654 } 655 default: 656 return null; 657 } 658 } 659 660 private Dialog buildAddUserProfileEditorDialog(int userType) { 661 Dialog d; 662 synchronized (mUserLock) { 663 d = mEditUserInfoController.createDialog( 664 this, 665 mPendingUserIcon, 666 mPendingUserName, 667 getString(userType == USER_TYPE_USER 668 ? com.android.settingslib.R.string.user_info_settings_title 669 : com.android.settingslib.R.string.profile_info_settings_title), 670 new EditUserInfoController.OnContentChangedCallback() { 671 @Override 672 public void onPhotoChanged(UserHandle user, Drawable photo) { 673 mPendingUserIcon = photo; 674 } 675 676 @Override 677 public void onLabelChanged(UserHandle user, CharSequence label) { 678 mPendingUserName = label; 679 } 680 }, 681 myUserHandle(), 682 new EditUserInfoController.OnDialogCompleteCallback() { 683 @Override 684 public void onPositive() { 685 addUserNow(userType); 686 } 687 688 @Override 689 public void onNegativeOrCancel() { 690 synchronized (mUserLock) { 691 mPendingUserIcon = null; 692 mPendingUserName = null; 693 } 694 } 695 }); 696 } 697 return d; 698 } 699 700 @Override 701 public int getDialogMetricsCategory(int dialogId) { 702 switch (dialogId) { 703 case DIALOG_CONFIRM_REMOVE: 704 return SettingsEnums.DIALOG_USER_REMOVE; 705 case DIALOG_USER_CANNOT_MANAGE: 706 return SettingsEnums.DIALOG_USER_CANNOT_MANAGE; 707 case DIALOG_ADD_USER: 708 return SettingsEnums.DIALOG_USER_ADD; 709 case DIALOG_CHOOSE_USER_TYPE: 710 return SettingsEnums.DIALOG_USER_CHOOSE_TYPE; 711 case DIALOG_NEED_LOCKSCREEN: 712 return SettingsEnums.DIALOG_USER_NEED_LOCKSCREEN; 713 case DIALOG_CONFIRM_EXIT_GUEST: 714 return SettingsEnums.DIALOG_USER_CONFIRM_EXIT_GUEST; 715 case DIALOG_USER_PROFILE_EDITOR: 716 case DIALOG_USER_PROFILE_EDITOR_ADD_USER: 717 case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE: 718 return SettingsEnums.DIALOG_USER_EDIT_PROFILE; 719 default: 720 return 0; 721 } 722 } 723 724 private void removeUserNow() { 725 if (mRemovingUserId == UserHandle.myUserId()) { 726 removeThisUser(); 727 } else { 728 ThreadUtils.postOnBackgroundThread(new Runnable() { 729 @Override 730 public void run() { 731 synchronized (mUserLock) { 732 mUserManager.removeUser(mRemovingUserId); 733 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 734 } 735 } 736 }); 737 } 738 } 739 740 private void removeThisUser() { 741 if (!canSwitchUserNow()) { 742 Log.w(TAG, "Cannot remove current user when switching is disabled"); 743 return; 744 } 745 try { 746 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM); 747 getContext().getSystemService(UserManager.class).removeUser(UserHandle.myUserId()); 748 } catch (RemoteException re) { 749 Log.e(TAG, "Unable to remove self user"); 750 } 751 } 752 753 private void addUserNow(final int userType) { 754 synchronized (mUserLock) { 755 mAddingUser = true; 756 mAddingUserName = userType == USER_TYPE_USER 757 ? (mPendingUserName != null ? mPendingUserName.toString() 758 : getString(R.string.user_new_user_name)) 759 : (mPendingUserName != null ? mPendingUserName.toString() 760 : getString(R.string.user_new_profile_name)); 761 } 762 ThreadUtils.postOnBackgroundThread(new Runnable() { 763 @Override 764 public void run() { 765 UserInfo user; 766 String username; 767 768 synchronized (mUserLock) { 769 username = mAddingUserName; 770 } 771 772 // Could take a few seconds 773 if (userType == USER_TYPE_USER) { 774 user = mUserManager.createUser(username, 0); 775 } else { 776 user = mUserManager.createRestrictedProfile(username); 777 } 778 779 synchronized (mUserLock) { 780 if (user == null) { 781 mAddingUser = false; 782 mPendingUserIcon = null; 783 mPendingUserName = null; 784 return; 785 } 786 787 if (mPendingUserIcon != null) { 788 mUserManager.setUserIcon(user.id, 789 UserIcons.convertToBitmap(mPendingUserIcon)); 790 } 791 792 if (userType == USER_TYPE_USER) { 793 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST); 794 } 795 796 mHandler.sendMessage(mHandler.obtainMessage( 797 MESSAGE_USER_CREATED, user.id, user.serialNumber)); 798 799 mPendingUserIcon = null; 800 mPendingUserName = null; 801 } 802 } 803 }); 804 } 805 806 /** 807 * Erase the current user (guest) and switch to another user. 808 */ 809 private void exitGuest() { 810 // Just to be safe 811 if (!isCurrentUserGuest()) { 812 return; 813 } 814 removeThisUser(); 815 } 816 817 @VisibleForTesting 818 void updateUserList() { 819 final Context context = getActivity(); 820 if (context == null) { 821 return; 822 } 823 final List<UserInfo> users = mUserManager.getUsers(true); 824 825 final ArrayList<Integer> missingIcons = new ArrayList<>(); 826 final ArrayList<UserPreference> userPreferences = new ArrayList<>(); 827 userPreferences.add(mMePreference); 828 829 boolean canOpenUserDetails = 830 mUserCaps.mIsAdmin || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser); 831 for (UserInfo user : users) { 832 if (!user.supportsSwitchToByUser()) { 833 // Only users that can be switched to should show up here. 834 // e.g. Managed profiles appear under Accounts Settings instead 835 continue; 836 } 837 UserPreference pref; 838 if (user.id == UserHandle.myUserId()) { 839 pref = mMePreference; 840 } else if (user.isGuest()) { 841 pref = new UserPreference(getPrefContext(), null, user.id); 842 pref.setTitle(R.string.user_guest); 843 pref.setIcon(getEncircledDefaultIcon()); 844 pref.setKey(KEY_USER_GUEST); 845 userPreferences.add(pref); 846 pref.setEnabled(canOpenUserDetails); 847 pref.setSelectable(true); 848 849 if (mUserCaps.mDisallowSwitchUser) { 850 pref.setDisabledByAdmin(RestrictedLockUtilsInternal.getDeviceOwner(context)); 851 } else { 852 pref.setDisabledByAdmin(null); 853 } 854 pref.setOnPreferenceClickListener(this); 855 } else { 856 pref = new UserPreference(getPrefContext(), null, user.id); 857 pref.setKey("id=" + user.id); 858 userPreferences.add(pref); 859 if (user.isAdmin()) { 860 pref.setSummary(R.string.user_admin); 861 } 862 pref.setTitle(user.name); 863 pref.setOnPreferenceClickListener(this); 864 pref.setEnabled(canOpenUserDetails); 865 pref.setSelectable(true); 866 } 867 if (pref == null) { 868 continue; 869 } 870 if (user.id != UserHandle.myUserId() && !user.isGuest() && !user.isInitialized()) { 871 // sometimes after creating a guest the initialized flag isn't immediately set 872 // and we don't want to show "Not set up" summary for them 873 if (user.isRestricted()) { 874 pref.setSummary(R.string.user_summary_restricted_not_set_up); 875 } else { 876 pref.setSummary(R.string.user_summary_not_set_up); 877 // Disallow setting up user which results in user switching when the 878 // restriction is set. 879 pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow()); 880 } 881 } else if (user.isRestricted()) { 882 pref.setSummary(R.string.user_summary_restricted_profile); 883 } 884 if (user.iconPath != null) { 885 if (mUserIcons.get(user.id) == null) { 886 // Icon not loaded yet, print a placeholder 887 missingIcons.add(user.id); 888 pref.setIcon(getEncircledDefaultIcon()); 889 } else { 890 setPhotoId(pref, user); 891 } 892 } else { 893 // Icon not available yet, print a placeholder 894 pref.setIcon(getEncircledDefaultIcon()); 895 } 896 } 897 898 // Add a temporary entry for the user being created 899 if (mAddingUser) { 900 UserPreference pref = new UserPreference(getPrefContext(), null, 901 UserPreference.USERID_UNKNOWN); 902 pref.setEnabled(false); 903 pref.setTitle(mAddingUserName); 904 pref.setIcon(getEncircledDefaultIcon()); 905 userPreferences.add(pref); 906 } 907 908 909 // Sort list of users by serialNum 910 Collections.sort(userPreferences, UserPreference.SERIAL_NUMBER_COMPARATOR); 911 912 getActivity().invalidateOptionsMenu(); 913 914 // Load the icons 915 if (missingIcons.size() > 0) { 916 loadIconsAsync(missingIcons); 917 } 918 919 // If profiles are supported, mUserListCategory will have a special title 920 if (mUserCaps.mCanAddRestrictedProfile) { 921 mUserListCategory.setTitle(R.string.user_list_title); 922 } else { 923 mUserListCategory.setTitle(null); 924 } 925 926 // Remove everything from mUserListCategory and add new users. 927 mUserListCategory.removeAll(); 928 929 // If multi-user is disabled, just show footer and return. 930 final Preference addUserOnLockScreen = getPreferenceScreen().findPreference( 931 mAddUserWhenLockedPreferenceController.getPreferenceKey()); 932 mAddUserWhenLockedPreferenceController.updateState(addUserOnLockScreen); 933 934 final Preference multiUserFooterPrefence = getPreferenceScreen().findPreference( 935 mMultiUserFooterPreferenceController.getPreferenceKey()); 936 mMultiUserFooterPreferenceController.updateState(multiUserFooterPrefence); 937 mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled); 938 939 updateAddGuest(context, users.stream().anyMatch(UserInfo::isGuest)); 940 updateAddUser(context); 941 942 if (!mUserCaps.mUserSwitcherEnabled) { 943 return; 944 } 945 946 for (UserPreference userPreference : userPreferences) { 947 userPreference.setOrder(Preference.DEFAULT_ORDER); 948 mUserListCategory.addPreference(userPreference); 949 } 950 951 } 952 953 private boolean isCurrentUserGuest() { 954 return mUserCaps.mIsGuest; 955 } 956 957 private boolean canSwitchUserNow() { 958 return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK; 959 } 960 961 private void updateAddGuest(Context context, boolean isGuestAlreadyCreated) { 962 if (!isGuestAlreadyCreated && mUserCaps.mCanAddGuest 963 && WizardManagerHelper.isDeviceProvisioned(context) 964 && mUserCaps.mUserSwitcherEnabled) { 965 mAddGuest.setVisible(true); 966 mAddGuest.setIcon(getEncircledDefaultIcon()); 967 mAddGuest.setEnabled(canSwitchUserNow()); 968 mAddGuest.setSelectable(true); 969 } else { 970 mAddGuest.setVisible(false); 971 } 972 } 973 974 private void updateAddUser(Context context) { 975 if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin) 976 && WizardManagerHelper.isDeviceProvisioned(context) 977 && mUserCaps.mUserSwitcherEnabled) { 978 mAddUser.setVisible(true); 979 mAddUser.setSelectable(true); 980 final boolean canAddMoreUsers = mUserManager.canAddMoreUsers(); 981 mAddUser.setEnabled(canAddMoreUsers && !mAddingUser && canSwitchUserNow()); 982 if (!canAddMoreUsers) { 983 mAddUser.setSummary( 984 getString(R.string.user_add_max_count, getRealUsersCount())); 985 } else { 986 mAddUser.setSummary(null); 987 } 988 if (mAddUser.isEnabled()) { 989 mAddUser.setDisabledByAdmin( 990 mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null); 991 } 992 } else { 993 mAddUser.setVisible(false); 994 } 995 } 996 997 /** 998 * @return number of non-guest non-managed users 999 */ 1000 @VisibleForTesting 1001 int getRealUsersCount() { 1002 return (int) mUserManager.getUsers() 1003 .stream() 1004 .filter(user -> !user.isGuest() && !user.isProfile()) 1005 .count(); 1006 } 1007 1008 private void loadIconsAsync(List<Integer> missingIcons) { 1009 new AsyncTask<List<Integer>, Void, Void>() { 1010 @Override 1011 protected void onPostExecute(Void result) { 1012 updateUserList(); 1013 } 1014 1015 @Override 1016 protected Void doInBackground(List<Integer>... values) { 1017 for (int userId : values[0]) { 1018 Bitmap bitmap = mUserManager.getUserIcon(userId); 1019 if (bitmap == null) { 1020 bitmap = getDefaultUserIconAsBitmap(getContext().getResources(), userId); 1021 } 1022 mUserIcons.append(userId, bitmap); 1023 } 1024 return null; 1025 } 1026 }.execute(missingIcons); 1027 } 1028 1029 private Drawable getEncircledDefaultIcon() { 1030 if (mDefaultIconDrawable == null) { 1031 mDefaultIconDrawable = encircle( 1032 getDefaultUserIconAsBitmap(getContext().getResources(), UserHandle.USER_NULL)); 1033 } 1034 return mDefaultIconDrawable; 1035 } 1036 1037 private void setPhotoId(Preference pref, UserInfo user) { 1038 Bitmap bitmap = mUserIcons.get(user.id); 1039 if (bitmap != null) { 1040 pref.setIcon(encircle(bitmap)); 1041 } 1042 } 1043 1044 @Override 1045 public boolean onPreferenceClick(Preference pref) { 1046 if (pref == mMePreference) { 1047 if (isCurrentUserGuest()) { 1048 showDialog(DIALOG_CONFIRM_EXIT_GUEST); 1049 } else { 1050 showDialog(DIALOG_USER_PROFILE_EDITOR); 1051 } 1052 return true; 1053 } else if (pref instanceof UserPreference) { 1054 UserInfo userInfo = mUserManager.getUserInfo(((UserPreference) pref).getUserId()); 1055 openUserDetails(userInfo, false); 1056 return true; 1057 } else if (pref == mAddUser) { 1058 // If we allow both types, show a picker, otherwise directly go to 1059 // flow for full user. 1060 if (mUserCaps.mCanAddRestrictedProfile) { 1061 showDialog(DIALOG_CHOOSE_USER_TYPE); 1062 } else { 1063 onAddUserClicked(USER_TYPE_USER); 1064 } 1065 return true; 1066 } else if (pref == mAddGuest) { 1067 mAddGuest.setEnabled(false); // prevent multiple tap issue 1068 UserInfo guest = mUserManager.createGuest( 1069 getContext(), getString(com.android.settingslib.R.string.user_guest)); 1070 openUserDetails(guest, true); 1071 return true; 1072 } 1073 return false; 1074 } 1075 1076 private Drawable encircle(Bitmap icon) { 1077 Drawable circled = CircleFramedDrawable.getInstance(getActivity(), icon); 1078 return circled; 1079 } 1080 1081 @Override 1082 public void onDismiss(DialogInterface dialog) { 1083 synchronized (mUserLock) { 1084 mRemovingUserId = -1; 1085 updateUserList(); 1086 } 1087 } 1088 1089 @Override 1090 public int getHelpResource() { 1091 return R.string.help_url_users; 1092 } 1093 1094 /** 1095 * Returns a default user icon (as a {@link Bitmap}) for the given user. 1096 * 1097 * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}. 1098 * 1099 * @param resources resources object to fetch the user icon. 1100 * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon 1101 */ 1102 private static Bitmap getDefaultUserIconAsBitmap(Resources resources, int userId) { 1103 Bitmap bitmap = null; 1104 // Try finding the corresponding bitmap in the dark bitmap cache 1105 bitmap = sDarkDefaultUserBitmapCache.get(userId); 1106 if (bitmap == null) { 1107 bitmap = UserIcons.convertToBitmap( 1108 UserIcons.getDefaultUserIcon(resources, userId, false)); 1109 // Save it to cache 1110 sDarkDefaultUserBitmapCache.put(userId, bitmap); 1111 } 1112 return bitmap; 1113 } 1114 1115 /** 1116 * Assign the default photo to user with {@paramref userId} 1117 * 1118 * @param context used to get the {@link UserManager} 1119 * @param userId used to get the icon bitmap 1120 * @return true if assign photo successfully, false if failed 1121 */ 1122 @VisibleForTesting 1123 static boolean assignDefaultPhoto(Context context, int userId) { 1124 if (context == null) { 1125 return false; 1126 } 1127 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 1128 Bitmap bitmap = getDefaultUserIconAsBitmap(context.getResources(), userId); 1129 um.setUserIcon(userId, bitmap); 1130 1131 return true; 1132 } 1133 1134 @WorkerThread 1135 static void copyMeProfilePhoto(Context context, UserInfo user) { 1136 Uri contactUri = ContactsContract.Profile.CONTENT_URI; 1137 1138 int userId = user != null ? user.id : UserHandle.myUserId(); 1139 1140 InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream( 1141 context.getContentResolver(), 1142 contactUri, true); 1143 // If there's no profile photo, assign a default avatar 1144 if (avatarDataStream == null) { 1145 assignDefaultPhoto(context, userId); 1146 return; 1147 } 1148 1149 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE); 1150 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream); 1151 um.setUserIcon(userId, icon); 1152 try { 1153 avatarDataStream.close(); 1154 } catch (IOException ioe) { 1155 } 1156 } 1157 1158 public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER = 1159 new BaseSearchIndexProvider(R.xml.user_settings) { 1160 1161 @Override 1162 protected boolean isPageSearchEnabled(Context context) { 1163 final UserCapabilities userCaps = UserCapabilities.create(context); 1164 return userCaps.mEnabled; 1165 } 1166 1167 @Override 1168 public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId, 1169 boolean suppressAllPage) { 1170 final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId, 1171 suppressAllPage); 1172 AddUserWhenLockedPreferenceController controller = 1173 new AddUserWhenLockedPreferenceController( 1174 context, KEY_ADD_USER_WHEN_LOCKED); 1175 controller.updateNonIndexableKeys(niks); 1176 new AutoSyncDataPreferenceController(context, null /* parent */) 1177 .updateNonIndexableKeys(niks); 1178 new AutoSyncPersonalDataPreferenceController(context, null /* parent */) 1179 .updateNonIndexableKeys(niks); 1180 new AutoSyncWorkDataPreferenceController(context, null /* parent */) 1181 .updateNonIndexableKeys(niks); 1182 return niks; 1183 } 1184 }; 1185 } 1186