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