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