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