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