2 * Copyright (C) 2012 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
17 package com.android.settings.users;
19 import android.app.Activity;
20 import android.app.ActivityManager;
21 import android.app.AlertDialog;
22 import android.app.Dialog;
23 import android.app.admin.DevicePolicyManager;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.DialogInterface;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.SharedPreferences;
30 import android.content.pm.UserInfo;
31 import android.content.res.Resources;
32 import android.graphics.Bitmap;
33 import android.graphics.BitmapFactory;
34 import android.graphics.drawable.Drawable;
35 import android.net.Uri;
36 import android.os.AsyncTask;
37 import android.os.Bundle;
38 import android.os.Handler;
39 import android.os.Message;
40 import android.os.RemoteException;
41 import android.os.UserHandle;
42 import android.os.UserManager;
43 import android.provider.ContactsContract;
44 import android.provider.Settings.Global;
45 import android.support.annotation.VisibleForTesting;
46 import android.support.annotation.WorkerThread;
47 import android.support.v7.preference.Preference;
48 import android.support.v7.preference.Preference.OnPreferenceClickListener;
49 import android.support.v7.preference.PreferenceGroup;
50 import android.support.v7.preference.PreferenceScreen;
51 import android.util.Log;
52 import android.util.SparseArray;
53 import android.view.Menu;
54 import android.view.MenuInflater;
55 import android.view.MenuItem;
56 import android.view.View;
57 import android.view.View.OnClickListener;
58 import android.widget.SimpleAdapter;
60 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
61 import com.android.internal.util.UserIcons;
62 import com.android.internal.widget.LockPatternUtils;
63 import com.android.settings.R;
64 import com.android.settings.SettingsActivity;
65 import com.android.settings.SettingsPreferenceFragment;
66 import com.android.settings.Utils;
67 import com.android.settings.dashboard.SummaryLoader;
68 import com.android.settings.password.ChooseLockGeneric;
69 import com.android.settings.search.BaseSearchIndexProvider;
70 import com.android.settings.search.Indexable;
71 import com.android.settings.search.SearchIndexableRaw;
72 import com.android.settingslib.RestrictedLockUtils;
73 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
74 import com.android.settingslib.RestrictedPreference;
75 import com.android.settingslib.drawable.CircleFramedDrawable;
77 import java.io.IOException;
78 import java.io.InputStream;
79 import java.util.ArrayList;
80 import java.util.Collections;
81 import java.util.HashMap;
82 import java.util.List;
85 * Screen that manages the list of users on the device.
86 * Guest user is an always visible entry, even if the guest is not currently
87 * active/created. It is meant for controlling properties of a guest user.
89 * The first one is always the current user.
90 * Owner is the primary user.
92 public class UserSettings extends SettingsPreferenceFragment
93 implements OnPreferenceClickListener, OnClickListener, DialogInterface.OnDismissListener,
94 EditUserInfoController.OnContentChangedCallback, Indexable {
96 private static final String TAG = "UserSettings";
98 /** UserId of the user being removed */
99 private static final String SAVE_REMOVING_USER = "removing_user";
100 /** UserId of the user that was just added */
101 private static final String SAVE_ADDING_USER = "adding_user";
103 private static final String KEY_USER_LIST = "user_list";
104 private static final String KEY_USER_ME = "user_me";
105 private static final String KEY_ADD_USER = "user_add";
106 private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked";
108 private static final int MENU_REMOVE_USER = Menu.FIRST;
110 private static final int DIALOG_CONFIRM_REMOVE = 1;
111 private static final int DIALOG_ADD_USER = 2;
112 private static final int DIALOG_SETUP_USER = 3;
113 private static final int DIALOG_SETUP_PROFILE = 4;
114 private static final int DIALOG_USER_CANNOT_MANAGE = 5;
115 private static final int DIALOG_CHOOSE_USER_TYPE = 6;
116 private static final int DIALOG_NEED_LOCKSCREEN = 7;
117 private static final int DIALOG_CONFIRM_EXIT_GUEST = 8;
118 private static final int DIALOG_USER_PROFILE_EDITOR = 9;
120 private static final int MESSAGE_UPDATE_LIST = 1;
121 private static final int MESSAGE_SETUP_USER = 2;
122 private static final int MESSAGE_CONFIG_USER = 3;
124 private static final int USER_TYPE_USER = 1;
125 private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
127 private static final int REQUEST_CHOOSE_LOCK = 10;
129 private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED =
130 "key_add_user_long_message_displayed";
132 private static final String KEY_TITLE = "title";
133 private static final String KEY_SUMMARY = "summary";
135 private PreferenceGroup mUserListCategory;
136 private UserPreference mMePreference;
137 private RestrictedPreference mAddUser;
138 private int mRemovingUserId = -1;
139 private int mAddedUserId = 0;
140 private boolean mAddingUser;
141 private String mAddingUserName;
142 private UserCapabilities mUserCaps;
143 private boolean mShouldUpdateUserList = true;
144 private final Object mUserLock = new Object();
145 private UserManager mUserManager;
146 private SparseArray<Bitmap> mUserIcons = new SparseArray<>();
147 private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>();
149 private EditUserInfoController mEditUserInfoController = new EditUserInfoController();
150 private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
151 private AutoSyncDataPreferenceController mAutoSyncDataPreferenceController;
152 private AutoSyncPersonalDataPreferenceController mAutoSyncPersonalDataPreferenceController;
153 private AutoSyncWorkDataPreferenceController mAutoSyncWorkDataPreferenceController;
155 // A place to cache the generated default avatar
156 private Drawable mDefaultIconDrawable;
158 private Handler mHandler = new Handler() {
160 public void handleMessage(Message msg) {
162 case MESSAGE_UPDATE_LIST:
165 case MESSAGE_SETUP_USER:
166 onUserCreated(msg.arg1);
168 case MESSAGE_CONFIG_USER:
169 onManageUserClicked(msg.arg1, true);
175 private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
177 public void onReceive(Context context, Intent intent) {
178 if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
179 mRemovingUserId = -1;
180 } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) {
181 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
182 if (userHandle != -1) {
183 mUserIcons.remove(userHandle);
186 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
191 public int getMetricsCategory() {
192 return MetricsEvent.USER;
196 public void onCreate(Bundle icicle) {
197 super.onCreate(icicle);
198 addPreferencesFromResource(R.xml.user_settings);
199 if (Global.getInt(getContext().getContentResolver(), Global.DEVICE_PROVISIONED, 0) == 0) {
200 getActivity().finish();
203 final Context context = getActivity();
204 mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
205 context, KEY_ADD_USER_WHEN_LOCKED, getLifecycle());
207 mAutoSyncDataPreferenceController = new AutoSyncDataPreferenceController(context, this);
208 mAutoSyncPersonalDataPreferenceController =
209 new AutoSyncPersonalDataPreferenceController(context, this);
210 mAutoSyncWorkDataPreferenceController =
211 new AutoSyncWorkDataPreferenceController(context, this);
213 final PreferenceScreen screen = getPreferenceScreen();
214 mAddUserWhenLockedPreferenceController.displayPreference(screen);
215 mAutoSyncDataPreferenceController.displayPreference(screen);
216 mAutoSyncPersonalDataPreferenceController.displayPreference(screen);
217 mAutoSyncWorkDataPreferenceController.displayPreference(screen);
218 screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey())
219 .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController);
221 if (icicle != null) {
222 if (icicle.containsKey(SAVE_ADDING_USER)) {
223 mAddedUserId = icicle.getInt(SAVE_ADDING_USER);
225 if (icicle.containsKey(SAVE_REMOVING_USER)) {
226 mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER);
228 mEditUserInfoController.onRestoreInstanceState(icicle);
231 mUserCaps = UserCapabilities.create(context);
232 mUserManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
233 if (!mUserCaps.mEnabled) {
237 final int myUserId = UserHandle.myUserId();
239 mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
240 mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId,
241 null /* settings icon handler */,
242 null /* delete icon handler */);
243 mMePreference.setKey(KEY_USER_ME);
244 mMePreference.setOnPreferenceClickListener(this);
245 if (mUserCaps.mIsAdmin) {
246 mMePreference.setSummary(R.string.user_admin);
248 mAddUser = (RestrictedPreference) findPreference(KEY_ADD_USER);
249 mAddUser.useAdminDisabledSummary(false);
250 // Determine if add user/profile button should be visible
251 if (mUserCaps.mCanAddUser && Utils.isDeviceProvisioned(getActivity())) {
252 mAddUser.setVisible(true);
253 mAddUser.setOnPreferenceClickListener(this);
254 // change label to only mention user, if restricted profiles are not supported
255 if (!mUserCaps.mCanAddRestrictedProfile) {
256 mAddUser.setTitle(R.string.user_add_user_menu);
259 mAddUser.setVisible(false);
261 final IntentFilter filter = new IntentFilter(Intent.ACTION_USER_REMOVED);
262 filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
263 context.registerReceiverAsUser(mUserChangeReceiver, UserHandle.ALL, filter, null, mHandler);
266 mShouldUpdateUserList = false;
270 public void onResume() {
273 if (!mUserCaps.mEnabled) {
276 final PreferenceScreen screen = getPreferenceScreen();
278 if (mAutoSyncDataPreferenceController.isAvailable()) {
279 mAutoSyncDataPreferenceController.updateState(screen.findPreference(
280 mAutoSyncDataPreferenceController.getPreferenceKey()));
282 if (mAddUserWhenLockedPreferenceController.isAvailable()) {
283 mAddUserWhenLockedPreferenceController.updateState(screen.findPreference(
284 mAddUserWhenLockedPreferenceController.getPreferenceKey()));
286 if (mAutoSyncPersonalDataPreferenceController.isAvailable()) {
287 mAutoSyncPersonalDataPreferenceController.updateState(screen.findPreference(
288 mAutoSyncPersonalDataPreferenceController.getPreferenceKey()));
290 if (mAutoSyncWorkDataPreferenceController.isAvailable()) {
291 mAutoSyncWorkDataPreferenceController.updateState(screen.findPreference(
292 mAutoSyncWorkDataPreferenceController.getPreferenceKey()));
295 if (mShouldUpdateUserList) {
296 mUserCaps.updateAddUserCapabilities(getActivity());
303 public void onPause() {
304 mShouldUpdateUserList = true;
309 public void onDestroy() {
312 if (mUserCaps == null || !mUserCaps.mEnabled) {
316 getActivity().unregisterReceiver(mUserChangeReceiver);
320 public void onSaveInstanceState(Bundle outState) {
321 super.onSaveInstanceState(outState);
322 mEditUserInfoController.onSaveInstanceState(outState);
323 outState.putInt(SAVE_ADDING_USER, mAddedUserId);
324 outState.putInt(SAVE_REMOVING_USER, mRemovingUserId);
328 public void startActivityForResult(Intent intent, int requestCode) {
329 mEditUserInfoController.startingActivityForResult();
330 super.startActivityForResult(intent, requestCode);
334 public boolean onPreferenceTreeClick(Preference preference) {
335 if (mAutoSyncDataPreferenceController.handlePreferenceTreeClick(preference)) {
338 if (mAutoSyncPersonalDataPreferenceController.handlePreferenceTreeClick(preference)) {
341 if (mAutoSyncWorkDataPreferenceController.handlePreferenceTreeClick(preference)) {
344 return super.onPreferenceTreeClick(preference);
348 public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
350 UserManager um = getContext().getSystemService(UserManager.class);
351 boolean allowRemoveUser = !um.hasUserRestriction(UserManager.DISALLOW_REMOVE_USER);
352 boolean canSwitchUsers = um.canSwitchUsers();
353 if (!mUserCaps.mIsAdmin && allowRemoveUser && canSwitchUsers) {
354 String nickname = mUserManager.getUserName();
355 MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++,
356 getResources().getString(R.string.user_remove_user_menu, nickname));
357 removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
359 super.onCreateOptionsMenu(menu, inflater);
363 public boolean onOptionsItemSelected(MenuItem item) {
364 final int itemId = item.getItemId();
365 if (itemId == MENU_REMOVE_USER) {
366 onRemoveUserClicked(UserHandle.myUserId());
369 return super.onOptionsItemSelected(item);
374 * Loads profile information for the current user.
376 private void loadProfile() {
377 if (mUserCaps.mIsGuest) {
378 // No need to load profile information
379 mMePreference.setIcon(getEncircledDefaultIcon());
380 mMePreference.setTitle(R.string.user_exit_guest_title);
384 new AsyncTask<Void, Void, String>() {
386 protected void onPostExecute(String result) {
387 finishLoadProfile(result);
391 protected String doInBackground(Void... values) {
392 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
393 if (user.iconPath == null || user.iconPath.equals("")) {
394 // Assign profile photo.
395 copyMeProfilePhoto(getActivity(), user);
402 private void finishLoadProfile(String profileName) {
403 if (getActivity() == null) return;
404 mMePreference.setTitle(getString(R.string.user_you, profileName));
405 int myUserId = UserHandle.myUserId();
406 Bitmap b = mUserManager.getUserIcon(myUserId);
408 mMePreference.setIcon(encircle(b));
409 mUserIcons.put(myUserId, b);
413 private boolean hasLockscreenSecurity() {
414 LockPatternUtils lpu = new LockPatternUtils(getActivity());
415 return lpu.isSecure(UserHandle.myUserId());
418 private void launchChooseLockscreen() {
419 Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
420 chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.MINIMUM_QUALITY_KEY,
421 DevicePolicyManager.PASSWORD_QUALITY_SOMETHING);
422 startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK);
426 public void onActivityResult(int requestCode, int resultCode, Intent data) {
427 super.onActivityResult(requestCode, resultCode, data);
429 if (requestCode == REQUEST_CHOOSE_LOCK) {
430 if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) {
431 addUserNow(USER_TYPE_RESTRICTED_PROFILE);
434 mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
438 private void onAddUserClicked(int userType) {
439 synchronized (mUserLock) {
440 if (mRemovingUserId == -1 && !mAddingUser) {
443 showDialog(DIALOG_ADD_USER);
445 case USER_TYPE_RESTRICTED_PROFILE:
446 if (hasLockscreenSecurity()) {
447 addUserNow(USER_TYPE_RESTRICTED_PROFILE);
449 showDialog(DIALOG_NEED_LOCKSCREEN);
457 private void onRemoveUserClicked(int userId) {
458 synchronized (mUserLock) {
459 if (mRemovingUserId == -1 && !mAddingUser) {
460 mRemovingUserId = userId;
461 showDialog(DIALOG_CONFIRM_REMOVE);
466 private UserInfo createRestrictedProfile() {
467 UserInfo newUserInfo = mUserManager.createRestrictedProfile(mAddingUserName);
468 if (newUserInfo != null && !assignDefaultPhoto(getActivity(), newUserInfo.id)) {
474 private UserInfo createTrustedUser() {
475 UserInfo newUserInfo = mUserManager.createUser(mAddingUserName, 0);
476 if (newUserInfo != null && !assignDefaultPhoto(getActivity(), newUserInfo.id)) {
482 private void onManageUserClicked(int userId, boolean newUser) {
484 if (userId == UserPreference.USERID_GUEST_DEFAULTS) {
485 Bundle extras = new Bundle();
486 extras.putBoolean(UserDetailsSettings.EXTRA_USER_GUEST, true);
487 ((SettingsActivity) getActivity()).startPreferencePanel(this,
488 UserDetailsSettings.class.getName(),
489 extras, R.string.user_guest, null, null, 0);
492 UserInfo info = mUserManager.getUserInfo(userId);
493 if (info.isRestricted() && mUserCaps.mIsAdmin) {
494 Bundle extras = new Bundle();
495 extras.putInt(RestrictedProfileSettings.EXTRA_USER_ID, userId);
496 extras.putBoolean(RestrictedProfileSettings.EXTRA_NEW_USER, newUser);
497 ((SettingsActivity) getActivity()).startPreferencePanel(this,
498 RestrictedProfileSettings.class.getName(),
499 extras, R.string.user_restrictions_title, null,
501 } else if (info.id == UserHandle.myUserId()) {
502 // Jump to owner info panel
503 OwnerInfoSettings.show(this);
504 } else if (mUserCaps.mIsAdmin) {
505 Bundle extras = new Bundle();
506 extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userId);
507 ((SettingsActivity) getActivity()).startPreferencePanel(this,
508 UserDetailsSettings.class.getName(),
510 -1, /* No title res id */
511 info.name, /* title */
513 0 /* resultRequestCode */);
517 private void onUserCreated(int userId) {
518 mAddedUserId = userId;
521 Log.w(TAG, "Cannot show dialog after onPause");
524 if (mUserManager.getUserInfo(userId).isRestricted()) {
525 showDialog(DIALOG_SETUP_PROFILE);
527 showDialog(DIALOG_SETUP_USER);
532 public void onDialogShowing() {
533 super.onDialogShowing();
535 setOnDismissListener(this);
539 public Dialog onCreateDialog(int dialogId) {
540 Context context = getActivity();
541 if (context == null) return null;
543 case DIALOG_CONFIRM_REMOVE: {
545 UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId,
546 new DialogInterface.OnClickListener() {
547 public void onClick(DialogInterface dialog, int which) {
554 case DIALOG_USER_CANNOT_MANAGE:
555 return new AlertDialog.Builder(context)
556 .setMessage(R.string.user_cannot_manage_message)
557 .setPositiveButton(android.R.string.ok, null)
559 case DIALOG_ADD_USER: {
560 final SharedPreferences preferences = getActivity().getPreferences(
561 Context.MODE_PRIVATE);
562 final boolean longMessageDisplayed = preferences.getBoolean(
563 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false);
564 final int messageResId = longMessageDisplayed
565 ? R.string.user_add_user_message_short
566 : R.string.user_add_user_message_long;
567 final int userType = dialogId == DIALOG_ADD_USER
568 ? USER_TYPE_USER : USER_TYPE_RESTRICTED_PROFILE;
569 Dialog dlg = new AlertDialog.Builder(context)
570 .setTitle(R.string.user_add_user_title)
571 .setMessage(messageResId)
572 .setPositiveButton(android.R.string.ok,
573 new DialogInterface.OnClickListener() {
574 public void onClick(DialogInterface dialog, int which) {
575 addUserNow(userType);
576 if (!longMessageDisplayed) {
577 preferences.edit().putBoolean(
578 KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, true).apply();
582 .setNegativeButton(android.R.string.cancel, null)
586 case DIALOG_SETUP_USER: {
587 Dialog dlg = new AlertDialog.Builder(context)
588 .setTitle(R.string.user_setup_dialog_title)
589 .setMessage(R.string.user_setup_dialog_message)
590 .setPositiveButton(R.string.user_setup_button_setup_now,
591 new DialogInterface.OnClickListener() {
592 public void onClick(DialogInterface dialog, int which) {
593 switchUserNow(mAddedUserId);
596 .setNegativeButton(R.string.user_setup_button_setup_later, null)
600 case DIALOG_SETUP_PROFILE: {
601 Dialog dlg = new AlertDialog.Builder(context)
602 .setMessage(R.string.user_setup_profile_dialog_message)
603 .setPositiveButton(android.R.string.ok,
604 new DialogInterface.OnClickListener() {
605 public void onClick(DialogInterface dialog, int which) {
606 switchUserNow(mAddedUserId);
609 .setNegativeButton(android.R.string.cancel, null)
613 case DIALOG_CHOOSE_USER_TYPE: {
614 List<HashMap<String, String>> data = new ArrayList<HashMap<String,String>>();
615 HashMap<String,String> addUserItem = new HashMap<String,String>();
616 addUserItem.put(KEY_TITLE, getString(R.string.user_add_user_item_title));
617 addUserItem.put(KEY_SUMMARY, getString(R.string.user_add_user_item_summary));
618 HashMap<String,String> addProfileItem = new HashMap<String,String>();
619 addProfileItem.put(KEY_TITLE, getString(R.string.user_add_profile_item_title));
620 addProfileItem.put(KEY_SUMMARY, getString(R.string.user_add_profile_item_summary));
621 data.add(addUserItem);
622 data.add(addProfileItem);
623 AlertDialog.Builder builder = new AlertDialog.Builder(context);
624 SimpleAdapter adapter = new SimpleAdapter(builder.getContext(),
625 data, R.layout.two_line_list_item,
626 new String[] {KEY_TITLE, KEY_SUMMARY},
627 new int[] {R.id.title, R.id.summary});
628 builder.setTitle(R.string.user_add_user_type_title);
629 builder.setAdapter(adapter,
630 new DialogInterface.OnClickListener() {
632 public void onClick(DialogInterface dialog, int which) {
633 onAddUserClicked(which == 0
635 : USER_TYPE_RESTRICTED_PROFILE);
638 return builder.create();
640 case DIALOG_NEED_LOCKSCREEN: {
641 Dialog dlg = new AlertDialog.Builder(context)
642 .setMessage(R.string.user_need_lock_message)
643 .setPositiveButton(R.string.user_set_lock_button,
644 new DialogInterface.OnClickListener() {
646 public void onClick(DialogInterface dialog, int which) {
647 launchChooseLockscreen();
650 .setNegativeButton(android.R.string.cancel, null)
654 case DIALOG_CONFIRM_EXIT_GUEST: {
655 Dialog dlg = new AlertDialog.Builder(context)
656 .setTitle(R.string.user_exit_guest_confirm_title)
657 .setMessage(R.string.user_exit_guest_confirm_message)
658 .setPositiveButton(R.string.user_exit_guest_dialog_remove,
659 new DialogInterface.OnClickListener() {
661 public void onClick(DialogInterface dialog, int which) {
665 .setNegativeButton(android.R.string.cancel, null)
669 case DIALOG_USER_PROFILE_EDITOR: {
670 Dialog dlg = mEditUserInfoController.createDialog(
673 mMePreference.getTitle(),
674 R.string.profile_info_settings_title,
676 android.os.Process.myUserHandle());
685 public int getDialogMetricsCategory(int dialogId) {
687 case DIALOG_CONFIRM_REMOVE:
688 return MetricsEvent.DIALOG_USER_REMOVE;
689 case DIALOG_USER_CANNOT_MANAGE:
690 return MetricsEvent.DIALOG_USER_CANNOT_MANAGE;
691 case DIALOG_ADD_USER:
692 return MetricsEvent.DIALOG_USER_ADD;
693 case DIALOG_SETUP_USER:
694 return MetricsEvent.DIALOG_USER_SETUP;
695 case DIALOG_SETUP_PROFILE:
696 return MetricsEvent.DIALOG_USER_SETUP_PROFILE;
697 case DIALOG_CHOOSE_USER_TYPE:
698 return MetricsEvent.DIALOG_USER_CHOOSE_TYPE;
699 case DIALOG_NEED_LOCKSCREEN:
700 return MetricsEvent.DIALOG_USER_NEED_LOCKSCREEN;
701 case DIALOG_CONFIRM_EXIT_GUEST:
702 return MetricsEvent.DIALOG_USER_CONFIRM_EXIT_GUEST;
703 case DIALOG_USER_PROFILE_EDITOR:
704 return MetricsEvent.DIALOG_USER_EDIT_PROFILE;
710 private void removeUserNow() {
711 if (mRemovingUserId == UserHandle.myUserId()) {
716 synchronized (mUserLock) {
717 mUserManager.removeUser(mRemovingUserId);
718 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
725 private void removeThisUser() {
726 if (!mUserManager.canSwitchUsers()) {
727 Log.w(TAG, "Cannot remove current user when switching is disabled");
731 ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
732 getContext().getSystemService(UserManager.class).removeUser(UserHandle.myUserId());
733 } catch (RemoteException re) {
734 Log.e(TAG, "Unable to remove self user");
738 private void addUserNow(final int userType) {
739 synchronized (mUserLock) {
741 mAddingUserName = userType == USER_TYPE_USER ? getString(R.string.user_new_user_name)
742 : getString(R.string.user_new_profile_name);
747 // Could take a few seconds
748 if (userType == USER_TYPE_USER) {
749 user = createTrustedUser();
751 user = createRestrictedProfile();
757 synchronized (mUserLock) {
758 if (userType == USER_TYPE_USER) {
759 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
760 // Skip setting up user which results in user switching when the
761 // restriction is set.
762 if (!mUserCaps.mDisallowSwitchUser) {
763 mHandler.sendMessage(mHandler.obtainMessage(
764 MESSAGE_SETUP_USER, user.id, user.serialNumber));
767 mHandler.sendMessage(mHandler.obtainMessage(
768 MESSAGE_CONFIG_USER, user.id, user.serialNumber));
776 private void switchUserNow(int userId) {
778 ActivityManager.getService().switchUser(userId);
779 } catch (RemoteException re) {
785 * Erase the current user (guest) and switch to another user.
787 private void exitGuest() {
789 if (!mUserCaps.mIsGuest) {
795 private void updateUserList() {
796 if (getActivity() == null) return;
797 List<UserInfo> users = mUserManager.getUsers(true);
798 final Context context = getActivity();
800 final boolean voiceCapable = Utils.isVoiceCapable(context);
801 final ArrayList<Integer> missingIcons = new ArrayList<>();
802 final ArrayList<UserPreference> userPreferences = new ArrayList<>();
803 int guestId = UserPreference.USERID_GUEST_DEFAULTS;
804 userPreferences.add(mMePreference);
806 for (UserInfo user : users) {
807 if (!user.supportsSwitchToByUser()) {
808 // Only users that can be switched to should show up here.
809 // e.g. Managed profiles appear under Accounts Settings instead
813 if (user.id == UserHandle.myUserId()) {
814 pref = mMePreference;
815 } else if (user.isGuest()) {
816 // Skip over Guest. We add generic Guest settings after this loop
821 // Secondary user: Settings
823 // Restricted Profile: There is no Restricted Profile
824 // Without Telephony:
825 // Secondary user: Delete
827 // Restricted Profile: Settings
828 final boolean showSettings = mUserCaps.mIsAdmin
829 && (voiceCapable || user.isRestricted());
830 final boolean showDelete = mUserCaps.mIsAdmin
831 && (!voiceCapable && !user.isRestricted() && !user.isGuest());
832 pref = new UserPreference(getPrefContext(), null, user.id,
833 showSettings ? this : null,
834 showDelete ? this : null);
835 pref.setKey("id=" + user.id);
836 userPreferences.add(pref);
837 if (user.isAdmin()) {
838 pref.setSummary(R.string.user_admin);
840 pref.setTitle(user.name);
841 pref.setSelectable(false);
846 if (!isInitialized(user)) {
847 if (user.isRestricted()) {
848 pref.setSummary(R.string.user_summary_restricted_not_set_up);
850 pref.setSummary(R.string.user_summary_not_set_up);
852 // Disallow setting up user which results in user switching when the restriction is
854 if (!mUserCaps.mDisallowSwitchUser) {
855 pref.setOnPreferenceClickListener(this);
856 pref.setSelectable(true);
858 } else if (user.isRestricted()) {
859 pref.setSummary(R.string.user_summary_restricted_profile);
861 if (user.iconPath != null) {
862 if (mUserIcons.get(user.id) == null) {
863 // Icon not loaded yet, print a placeholder
864 missingIcons.add(user.id);
865 pref.setIcon(getEncircledDefaultIcon());
867 setPhotoId(pref, user);
870 // Icon not available yet, print a placeholder
871 pref.setIcon(getEncircledDefaultIcon());
875 // Add a temporary entry for the user being created
877 UserPreference pref = new UserPreference(getPrefContext(), null,
878 UserPreference.USERID_UNKNOWN, null, null);
879 pref.setEnabled(false);
880 pref.setTitle(mAddingUserName);
881 pref.setIcon(getEncircledDefaultIcon());
882 userPreferences.add(pref);
885 // Check if Guest tile should be added.
886 if (!mUserCaps.mIsGuest && (mUserCaps.mCanAddGuest ||
887 mUserCaps.mDisallowAddUserSetByAdmin)) {
888 // Add a virtual Guest user for guest defaults
889 UserPreference pref = new UserPreference(getPrefContext(), null,
890 UserPreference.USERID_GUEST_DEFAULTS,
891 mUserCaps.mIsAdmin && voiceCapable? this : null /* settings icon handler */,
892 null /* delete icon handler */);
893 pref.setTitle(R.string.user_guest);
894 pref.setIcon(getEncircledDefaultIcon());
895 userPreferences.add(pref);
896 if (mUserCaps.mDisallowAddUser) {
897 pref.setDisabledByAdmin(mUserCaps.mEnforcedAdmin);
898 } else if (mUserCaps.mDisallowSwitchUser) {
899 pref.setDisabledByAdmin(RestrictedLockUtils.getDeviceOwner(context));
901 pref.setDisabledByAdmin(null);
903 int finalGuestId = guestId;
904 pref.setOnPreferenceClickListener(preference -> {
905 int id = finalGuestId;
906 if (id == UserPreference.USERID_GUEST_DEFAULTS) {
907 UserInfo guest = mUserManager.createGuest(
908 getContext(), preference.getTitle().toString());
914 ActivityManager.getService().switchUser(id);
915 } catch (RemoteException e) {
916 e.rethrowFromSystemServer();
922 // Sort list of users by serialNum
923 Collections.sort(userPreferences, UserPreference.SERIAL_NUMBER_COMPARATOR);
925 getActivity().invalidateOptionsMenu();
928 if (missingIcons.size() > 0) {
929 loadIconsAsync(missingIcons);
932 // Remove everything from mUserListCategory and add new users.
933 mUserListCategory.removeAll();
934 // If profiles are supported, mUserListCategory will have a special title
935 if (mUserCaps.mCanAddRestrictedProfile) {
936 mUserListCategory.setTitle(R.string.user_list_title);
938 mUserListCategory.setTitle(null);
941 for (UserPreference userPreference : userPreferences) {
942 userPreference.setOrder(Preference.DEFAULT_ORDER);
943 mUserListCategory.addPreference(userPreference);
946 // Append Add user to the end of the list
947 if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin) &&
948 Utils.isDeviceProvisioned(getActivity())) {
949 boolean moreUsers = mUserManager.canAddMoreUsers();
950 mAddUser.setEnabled(moreUsers && !mAddingUser);
952 mAddUser.setSummary(getString(R.string.user_add_max_count, getMaxRealUsers()));
954 mAddUser.setSummary(null);
956 if (mAddUser.isEnabled()) {
957 mAddUser.setDisabledByAdmin(
958 mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null);
964 private int getMaxRealUsers() {
965 // guest is not counted against getMaxSupportedUsers() number
966 final int maxUsersAndGuest = UserManager.getMaxSupportedUsers() + 1;
967 final List<UserInfo> users = mUserManager.getUsers();
968 // managed profiles are counted against getMaxSupportedUsers()
969 int managedProfiles = 0;
970 for (UserInfo user : users) {
971 if (user.isManagedProfile()) {
975 return maxUsersAndGuest - managedProfiles;
978 private void loadIconsAsync(List<Integer> missingIcons) {
979 new AsyncTask<List<Integer>, Void, Void>() {
981 protected void onPostExecute(Void result) {
986 protected Void doInBackground(List<Integer>... values) {
987 for (int userId : values[0]) {
988 Bitmap bitmap = mUserManager.getUserIcon(userId);
989 if (bitmap == null) {
990 bitmap = getDefaultUserIconAsBitmap(getContext().getResources(), userId);
992 mUserIcons.append(userId, bitmap);
996 }.execute(missingIcons);
999 private Drawable getEncircledDefaultIcon() {
1000 if (mDefaultIconDrawable == null) {
1001 mDefaultIconDrawable = encircle(
1002 getDefaultUserIconAsBitmap(getContext().getResources(), UserHandle.USER_NULL));
1004 return mDefaultIconDrawable;
1007 private void setPhotoId(Preference pref, UserInfo user) {
1008 Bitmap bitmap = mUserIcons.get(user.id);
1009 if (bitmap != null) {
1010 pref.setIcon(encircle(bitmap));
1015 public boolean onPreferenceClick(Preference pref) {
1016 if (pref == mMePreference) {
1017 if (mUserCaps.mIsGuest) {
1018 showDialog(DIALOG_CONFIRM_EXIT_GUEST);
1021 // If this is a limited user, launch the user info settings instead of profile editor
1022 if (mUserManager.isLinkedUser()) {
1023 onManageUserClicked(UserHandle.myUserId(), false);
1025 showDialog(DIALOG_USER_PROFILE_EDITOR);
1027 } else if (pref instanceof UserPreference) {
1028 int userId = ((UserPreference) pref).getUserId();
1029 // Get the latest status of the user
1030 UserInfo user = mUserManager.getUserInfo(userId);
1031 if (!isInitialized(user)) {
1032 mHandler.sendMessage(mHandler.obtainMessage(
1033 MESSAGE_SETUP_USER, user.id, user.serialNumber));
1035 } else if (pref == mAddUser) {
1036 // If we allow both types, show a picker, otherwise directly go to
1037 // flow for full user.
1038 if (mUserCaps.mCanAddRestrictedProfile) {
1039 showDialog(DIALOG_CHOOSE_USER_TYPE);
1041 onAddUserClicked(USER_TYPE_USER);
1047 private boolean isInitialized(UserInfo user) {
1048 return (user.flags & UserInfo.FLAG_INITIALIZED) != 0;
1051 private Drawable encircle(Bitmap icon) {
1052 Drawable circled = CircleFramedDrawable.getInstance(getActivity(), icon);
1057 public void onClick(View v) {
1058 if (v.getTag() instanceof UserPreference) {
1059 int userId = ((UserPreference) v.getTag()).getUserId();
1060 switch (v.getId()) {
1061 case UserPreference.DELETE_ID:
1062 final EnforcedAdmin removeDisallowedAdmin =
1063 RestrictedLockUtils.checkIfRestrictionEnforced(getContext(),
1064 UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
1065 if (removeDisallowedAdmin != null) {
1066 RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
1067 removeDisallowedAdmin);
1069 onRemoveUserClicked(userId);
1072 case UserPreference.SETTINGS_ID:
1073 onManageUserClicked(userId, false);
1080 public void onDismiss(DialogInterface dialog) {
1081 synchronized (mUserLock) {
1082 mRemovingUserId = -1;
1088 public int getHelpResource() {
1089 return R.string.help_url_users;
1093 public void onPhotoChanged(Drawable photo) {
1094 mMePreference.setIcon(photo);
1098 public void onLabelChanged(CharSequence label) {
1099 mMePreference.setTitle(label);
1103 * Returns a default user icon (as a {@link Bitmap}) for the given user.
1105 * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
1106 * @param resources resources object to fetch the user icon.
1107 * @param userId the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
1109 private static Bitmap getDefaultUserIconAsBitmap(Resources resources, int userId) {
1110 Bitmap bitmap = null;
1111 // Try finding the corresponding bitmap in the dark bitmap cache
1112 bitmap = sDarkDefaultUserBitmapCache.get(userId);
1113 if (bitmap == null) {
1114 bitmap = UserIcons.convertToBitmap(
1115 UserIcons.getDefaultUserIcon(resources, userId, false));
1117 sDarkDefaultUserBitmapCache.put(userId, bitmap);
1123 * Assign the default photo to user with {@paramref userId}
1124 * @param context used to get the {@link UserManager}
1125 * @param userId used to get the icon bitmap
1126 * @return true if assign photo successfully, false if failed
1129 static boolean assignDefaultPhoto(Context context, int userId) {
1130 if (context == null) {
1133 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
1134 Bitmap bitmap = getDefaultUserIconAsBitmap(context.getResources(), userId);
1135 um.setUserIcon(userId, bitmap);
1141 static void copyMeProfilePhoto(Context context, UserInfo user) {
1142 Uri contactUri = ContactsContract.Profile.CONTENT_URI;
1144 int userId = user != null ? user.id : UserHandle.myUserId();
1146 InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
1147 context.getContentResolver(),
1149 // If there's no profile photo, assign a default avatar
1150 if (avatarDataStream == null) {
1151 assignDefaultPhoto(context, userId);
1155 UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
1156 Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
1157 um.setUserIcon(userId, icon);
1159 avatarDataStream.close();
1160 } catch (IOException ioe) { }
1163 private static class SummaryProvider implements SummaryLoader.SummaryProvider {
1165 private final Context mContext;
1166 private final SummaryLoader mSummaryLoader;
1168 public SummaryProvider(Context context, SummaryLoader summaryLoader) {
1170 mSummaryLoader = summaryLoader;
1174 public void setListening(boolean listening) {
1176 UserInfo info = mContext.getSystemService(UserManager.class).getUserInfo(
1177 UserHandle.myUserId());
1178 mSummaryLoader.setSummary(this, mContext.getString(R.string.users_summary,
1184 public static final SummaryLoader.SummaryProviderFactory SUMMARY_PROVIDER_FACTORY =
1185 new SummaryLoader.SummaryProviderFactory() {
1187 public SummaryLoader.SummaryProvider createSummaryProvider(Activity activity,
1188 SummaryLoader summaryLoader) {
1189 return new SummaryProvider(activity, summaryLoader);
1193 public static final Indexable.SearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
1194 new BaseSearchIndexProvider() {
1196 public List<SearchIndexableRaw> getRawDataToIndex(Context context,
1198 final List<SearchIndexableRaw> result = new ArrayList<>();
1199 final UserCapabilities userCaps = UserCapabilities.create(context);
1200 if (!userCaps.mEnabled) {
1203 final Resources res = context.getResources();
1204 SearchIndexableRaw data = new SearchIndexableRaw(context);
1205 data.title = res.getString(R.string.user_settings_title);
1206 data.key = "users_settings";
1207 data.screenTitle = res.getString(R.string.user_settings_title);
1210 if (userCaps.mCanAddUser || userCaps.mDisallowAddUserSetByAdmin) {
1211 data = new SearchIndexableRaw(context);
1212 data.title = res.getString(userCaps.mCanAddRestrictedProfile ?
1213 R.string.user_add_user_or_profile_menu
1214 : R.string.user_add_user_menu);
1215 data.screenTitle = res.getString(R.string.user_settings_title);
1216 data.key = "user_settings_add_users";