• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.annotation.NonNull;
20 import android.app.Activity;
21 import android.app.ActivityManager;
22 import android.app.Dialog;
23 import android.app.admin.DevicePolicyManager;
24 import android.app.settings.SettingsEnums;
25 import android.content.BroadcastReceiver;
26 import android.content.Context;
27 import android.content.DialogInterface;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.content.SharedPreferences;
31 import android.content.pm.UserInfo;
32 import android.content.res.Resources;
33 import android.graphics.Bitmap;
34 import android.graphics.BitmapFactory;
35 import android.graphics.drawable.Drawable;
36 import android.net.Uri;
37 import android.os.AsyncTask;
38 import android.os.Bundle;
39 import android.os.Handler;
40 import android.os.Message;
41 import android.os.Process;
42 import android.os.RemoteException;
43 import android.os.UserHandle;
44 import android.os.UserManager;
45 import android.provider.ContactsContract;
46 import android.text.TextUtils;
47 import android.util.Log;
48 import android.util.SparseArray;
49 import android.view.Menu;
50 import android.view.MenuInflater;
51 import android.view.MenuItem;
52 import android.widget.SimpleAdapter;
53 import android.widget.Toast;
54 
55 import androidx.annotation.VisibleForTesting;
56 import androidx.annotation.WorkerThread;
57 import androidx.appcompat.app.AlertDialog;
58 import androidx.preference.Preference;
59 import androidx.preference.PreferenceGroup;
60 import androidx.preference.PreferenceScreen;
61 
62 import com.android.internal.util.UserIcons;
63 import com.android.internal.widget.LockPatternUtils;
64 import com.android.settings.R;
65 import com.android.settings.SettingsActivity;
66 import com.android.settings.SettingsPreferenceFragment;
67 import com.android.settings.Utils;
68 import com.android.settings.core.SubSettingLauncher;
69 import com.android.settings.password.ChooseLockGeneric;
70 import com.android.settings.search.BaseSearchIndexProvider;
71 import com.android.settings.widget.MainSwitchBarController;
72 import com.android.settings.widget.SettingsMainSwitchBar;
73 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
74 import com.android.settingslib.RestrictedLockUtilsInternal;
75 import com.android.settingslib.RestrictedPreference;
76 import com.android.settingslib.drawable.CircleFramedDrawable;
77 import com.android.settingslib.search.SearchIndexable;
78 import com.android.settingslib.users.EditUserInfoController;
79 import com.android.settingslib.users.UserCreatingDialog;
80 import com.android.settingslib.utils.ThreadUtils;
81 
82 import com.google.android.setupcompat.util.WizardManagerHelper;
83 
84 import java.io.IOException;
85 import java.io.InputStream;
86 import java.util.ArrayList;
87 import java.util.Collections;
88 import java.util.HashMap;
89 import java.util.List;
90 import java.util.concurrent.ExecutorService;
91 import java.util.concurrent.Executors;
92 import java.util.concurrent.atomic.AtomicBoolean;
93 
94 /**
95  * Screen that manages the list of users on the device.
96  * Secondary users and a guest user can be created if there is no restriction.
97  *
98  * The first user in the list is always the current user.
99  * Owner is the primary user.
100  */
101 @SearchIndexable
102 public class UserSettings extends SettingsPreferenceFragment
103         implements Preference.OnPreferenceClickListener,
104         MultiUserSwitchBarController.OnMultiUserSwitchChangedListener,
105         DialogInterface.OnDismissListener {
106 
107     private static final String TAG = "UserSettings";
108 
109     /** UserId of the user being removed */
110     private static final String SAVE_REMOVING_USER = "removing_user";
111 
112     private static final String KEY_USER_LIST = "user_list";
113     private static final String KEY_USER_ME = "user_me";
114     private static final String KEY_USER_GUEST = "user_guest";
115     private static final String KEY_ADD_GUEST = "guest_add";
116     private static final String KEY_ADD_USER = "user_add";
117     private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked";
118     private static final String KEY_MULTIUSER_TOP_INTRO = "multiuser_top_intro";
119 
120     private static final int MENU_REMOVE_USER = Menu.FIRST;
121 
122     private static final IntentFilter USER_REMOVED_INTENT_FILTER;
123 
124     private static final int DIALOG_CONFIRM_REMOVE = 1;
125     private static final int DIALOG_ADD_USER = 2;
126     // Dialogs with id 3 and 4 got removed
127     private static final int DIALOG_USER_CANNOT_MANAGE = 5;
128     private static final int DIALOG_CHOOSE_USER_TYPE = 6;
129     private static final int DIALOG_NEED_LOCKSCREEN = 7;
130     private static final int DIALOG_CONFIRM_EXIT_GUEST = 8;
131     private static final int DIALOG_USER_PROFILE_EDITOR = 9;
132     private static final int DIALOG_USER_PROFILE_EDITOR_ADD_USER = 10;
133     private static final int DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE = 11;
134     private static final int DIALOG_CONFIRM_RESET_GUEST = 12;
135 
136     private static final int MESSAGE_UPDATE_LIST = 1;
137     private static final int MESSAGE_USER_CREATED = 2;
138 
139     private static final int USER_TYPE_USER = 1;
140     private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
141 
142     private static final int REQUEST_CHOOSE_LOCK = 10;
143     private static final int REQUEST_EDIT_GUEST = 11;
144 
145     static final int RESULT_GUEST_REMOVED = 100;
146 
147     private static final String KEY_ADD_USER_LONG_MESSAGE_DISPLAYED =
148             "key_add_user_long_message_displayed";
149 
150     private static final String KEY_TITLE = "title";
151     private static final String KEY_SUMMARY = "summary";
152 
153     static {
154         USER_REMOVED_INTENT_FILTER = new IntentFilter(Intent.ACTION_USER_REMOVED);
155         USER_REMOVED_INTENT_FILTER.addAction(Intent.ACTION_USER_INFO_CHANGED);
156     }
157 
158     @VisibleForTesting
159     PreferenceGroup mUserListCategory;
160     @VisibleForTesting
161     UserPreference mMePreference;
162     @VisibleForTesting
163     RestrictedPreference mAddGuest;
164     @VisibleForTesting
165     RestrictedPreference mAddUser;
166     @VisibleForTesting
167     SparseArray<Bitmap> mUserIcons = new SparseArray<>();
168     private int mRemovingUserId = -1;
169     private boolean mAddingUser;
170     private boolean mGuestUserAutoCreated;
171     private String mAddingUserName;
172     private UserCapabilities mUserCaps;
173     private boolean mShouldUpdateUserList = true;
174     private final Object mUserLock = new Object();
175     private UserManager mUserManager;
176     private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>();
177 
178     private MultiUserSwitchBarController mSwitchBarController;
179     private EditUserInfoController mEditUserInfoController =
180             new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY);
181     private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
182     private MultiUserTopIntroPreferenceController mMultiUserTopIntroPreferenceController;
183     private UserCreatingDialog mUserCreatingDialog;
184     private final AtomicBoolean mGuestCreationScheduled = new AtomicBoolean();
185     private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
186 
187     private CharSequence mPendingUserName;
188     private Drawable mPendingUserIcon;
189 
190     // A place to cache the generated default avatar
191     private Drawable mDefaultIconDrawable;
192 
193     // TODO:   Replace current Handler solution to something that doesn't leak memory and works
194     // TODO:   during a configuration change
195     private Handler mHandler = new Handler() {
196         @Override
197         public void handleMessage(Message msg) {
198             switch (msg.what) {
199                 case MESSAGE_UPDATE_LIST:
200                     updateUserList();
201                     break;
202                 case MESSAGE_USER_CREATED:
203                     onUserCreated(msg.arg1);
204                     break;
205             }
206         }
207     };
208 
209     private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
210         @Override
211         public void onReceive(Context context, Intent intent) {
212             if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
213                 mRemovingUserId = -1;
214             } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) {
215                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
216                 if (userHandle != -1) {
217                     mUserIcons.remove(userHandle);
218                 }
219             }
220             mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
221         }
222     };
223 
224     @Override
getMetricsCategory()225     public int getMetricsCategory() {
226         return SettingsEnums.USER;
227     }
228 
229     @Override
onActivityCreated(Bundle savedInstanceState)230     public void onActivityCreated(Bundle savedInstanceState) {
231         super.onActivityCreated(savedInstanceState);
232         // Assume we are in a SettingsActivity. This is only safe because we currently use
233         // SettingsActivity as base for all preference fragments.
234         final SettingsActivity activity = (SettingsActivity) getActivity();
235         final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
236         switchBar.setTitle(getContext().getString(R.string.multiple_users_main_switch_title));
237         switchBar.show();
238         mSwitchBarController = new MultiUserSwitchBarController(activity,
239                 new MainSwitchBarController(switchBar), this /* listener */);
240         getSettingsLifecycle().addObserver(mSwitchBarController);
241     }
242 
243     @Override
onCreate(Bundle icicle)244     public void onCreate(Bundle icicle) {
245         super.onCreate(icicle);
246         addPreferencesFromResource(R.xml.user_settings);
247         final Activity activity = getActivity();
248         if (!WizardManagerHelper.isDeviceProvisioned(activity)) {
249             activity.finish();
250             return;
251         }
252 
253         mGuestUserAutoCreated = getPrefContext().getResources().getBoolean(
254                         com.android.internal.R.bool.config_guestUserAutoCreated);
255 
256         mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
257                 activity, KEY_ADD_USER_WHEN_LOCKED);
258 
259         mMultiUserTopIntroPreferenceController = new MultiUserTopIntroPreferenceController(activity,
260                 KEY_MULTIUSER_TOP_INTRO);
261 
262         final PreferenceScreen screen = getPreferenceScreen();
263         mAddUserWhenLockedPreferenceController.displayPreference(screen);
264         mMultiUserTopIntroPreferenceController.displayPreference(screen);
265 
266         screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey())
267                 .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController);
268 
269         if (icicle != null) {
270             if (icicle.containsKey(SAVE_REMOVING_USER)) {
271                 mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER);
272             }
273             mEditUserInfoController.onRestoreInstanceState(icicle);
274         }
275 
276         mUserCaps = UserCapabilities.create(activity);
277         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
278         if (!mUserCaps.mEnabled) {
279             return;
280         }
281 
282         final int myUserId = UserHandle.myUserId();
283 
284         mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
285         mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId);
286         mMePreference.setKey(KEY_USER_ME);
287         mMePreference.setOnPreferenceClickListener(this);
288         if (mUserCaps.mIsAdmin) {
289             mMePreference.setSummary(R.string.user_admin);
290         }
291 
292         mAddGuest = findPreference(KEY_ADD_GUEST);
293         mAddGuest.setOnPreferenceClickListener(this);
294 
295         mAddUser = findPreference(KEY_ADD_USER);
296         if (!mUserCaps.mCanAddRestrictedProfile) {
297             // Label should only mention adding a "user", not a "profile"
298             mAddUser.setTitle(R.string.user_add_user_menu);
299         }
300         mAddUser.setOnPreferenceClickListener(this);
301 
302         activity.registerReceiverAsUser(
303                 mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler);
304 
305         updateUI();
306         mShouldUpdateUserList = false;
307     }
308 
309     @Override
onResume()310     public void onResume() {
311         super.onResume();
312 
313         if (!mUserCaps.mEnabled) {
314             return;
315         }
316         final PreferenceScreen screen = getPreferenceScreen();
317 
318         mAddUserWhenLockedPreferenceController.updateState(screen.findPreference(
319                 mAddUserWhenLockedPreferenceController.getPreferenceKey()));
320 
321         if (mShouldUpdateUserList) {
322             updateUI();
323         }
324     }
325 
326     @Override
onPause()327     public void onPause() {
328         mShouldUpdateUserList = true;
329         super.onPause();
330     }
331 
332     @Override
onDestroy()333     public void onDestroy() {
334         super.onDestroy();
335 
336         if (mUserCaps == null || !mUserCaps.mEnabled) {
337             return;
338         }
339 
340         getActivity().unregisterReceiver(mUserChangeReceiver);
341     }
342 
343     @Override
onSaveInstanceState(Bundle outState)344     public void onSaveInstanceState(Bundle outState) {
345         mEditUserInfoController.onSaveInstanceState(outState);
346         outState.putInt(SAVE_REMOVING_USER, mRemovingUserId);
347         super.onSaveInstanceState(outState);
348     }
349 
350     @Override
startActivityForResult(Intent intent, int requestCode)351     public void startActivityForResult(Intent intent, int requestCode) {
352         mEditUserInfoController.startingActivityForResult();
353         super.startActivityForResult(intent, requestCode);
354     }
355 
356     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)357     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
358         int pos = 0;
359         // TODO(b/191509236): The menu item does not need to be accessible for guest users,
360         //  regardless of mGuestUserAutoCreated
361         if (!mUserCaps.mIsAdmin && canSwitchUserNow() && !(isCurrentUserGuest()
362                 && mGuestUserAutoCreated)) {
363             String nickname = mUserManager.getUserName();
364             MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++,
365                     getResources().getString(R.string.user_remove_user_menu, nickname));
366             removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
367 
368             final EnforcedAdmin disallowRemoveUserAdmin =
369                     RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getContext(),
370                             UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
371             RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getContext(), removeThisUser,
372                     disallowRemoveUserAdmin);
373         }
374         super.onCreateOptionsMenu(menu, inflater);
375     }
376 
377     @Override
onOptionsItemSelected(MenuItem item)378     public boolean onOptionsItemSelected(MenuItem item) {
379         final int itemId = item.getItemId();
380         if (itemId == MENU_REMOVE_USER) {
381             onRemoveUserClicked(UserHandle.myUserId());
382             return true;
383         } else {
384             return super.onOptionsItemSelected(item);
385         }
386     }
387 
388     @Override
onMultiUserSwitchChanged(boolean newState)389     public void onMultiUserSwitchChanged(boolean newState) {
390         updateUI();
391     }
392 
updateUI()393     private void updateUI() {
394         mUserCaps.updateAddUserCapabilities(getActivity());
395         loadProfile();
396         updateUserList();
397     }
398 
399     /**
400      * Loads profile information for the current user.
401      */
loadProfile()402     private void loadProfile() {
403         if (isCurrentUserGuest()) {
404             // No need to load profile information
405             mMePreference.setIcon(getEncircledDefaultIcon());
406             mMePreference.setTitle(
407                     mGuestUserAutoCreated ? com.android.settingslib.R.string.guest_reset_guest
408                             : R.string.user_exit_guest_title);
409             mMePreference.setSelectable(true);
410             // removing a guest will result in switching back to the admin user
411             mMePreference.setEnabled(canSwitchUserNow());
412             return;
413         }
414 
415         new AsyncTask<Void, Void, String>() {
416             @Override
417             protected void onPostExecute(String result) {
418                 finishLoadProfile(result);
419             }
420 
421             @Override
422             protected String doInBackground(Void... values) {
423                 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
424                 if (user.iconPath == null || user.iconPath.equals("")) {
425                     // Assign profile photo.
426                     copyMeProfilePhoto(getActivity(), user);
427                 }
428                 return user.name;
429             }
430         }.execute();
431     }
432 
finishLoadProfile(String profileName)433     private void finishLoadProfile(String profileName) {
434         if (getActivity() == null) {
435             return;
436         }
437         mMePreference.setTitle(getString(R.string.user_you, profileName));
438         int myUserId = UserHandle.myUserId();
439         Bitmap b = mUserManager.getUserIcon(myUserId);
440         if (b != null) {
441             mMePreference.setIcon(encircle(b));
442             mUserIcons.put(myUserId, b);
443         }
444     }
445 
hasLockscreenSecurity()446     private boolean hasLockscreenSecurity() {
447         LockPatternUtils lpu = new LockPatternUtils(getActivity());
448         return lpu.isSecure(UserHandle.myUserId());
449     }
450 
launchChooseLockscreen()451     private void launchChooseLockscreen() {
452         Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD);
453         chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS,
454                 true);
455         startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK);
456     }
457 
458     @Override
onActivityResult(int requestCode, int resultCode, Intent data)459     public void onActivityResult(int requestCode, int resultCode, Intent data) {
460         super.onActivityResult(requestCode, resultCode, data);
461 
462         if (requestCode == REQUEST_CHOOSE_LOCK) {
463             if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) {
464                 addUserNow(USER_TYPE_RESTRICTED_PROFILE);
465             }
466         } else if (mGuestUserAutoCreated && requestCode == REQUEST_EDIT_GUEST
467                 && resultCode == RESULT_GUEST_REMOVED) {
468             scheduleGuestCreation();
469         } else {
470             mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
471         }
472     }
473 
onAddUserClicked(int userType)474     private void onAddUserClicked(int userType) {
475         synchronized (mUserLock) {
476             if (mRemovingUserId == -1 && !mAddingUser) {
477                 switch (userType) {
478                     case USER_TYPE_USER:
479                         showDialog(DIALOG_ADD_USER);
480                         break;
481                     case USER_TYPE_RESTRICTED_PROFILE:
482                         if (hasLockscreenSecurity()) {
483                             showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE);
484                         } else {
485                             showDialog(DIALOG_NEED_LOCKSCREEN);
486                         }
487                         break;
488                 }
489             }
490         }
491     }
492 
onRemoveUserClicked(int userId)493     private void onRemoveUserClicked(int userId) {
494         synchronized (mUserLock) {
495             if (mRemovingUserId == -1 && !mAddingUser) {
496                 mRemovingUserId = userId;
497                 showDialog(DIALOG_CONFIRM_REMOVE);
498             }
499         }
500     }
501 
onUserCreated(int userId)502     private void onUserCreated(int userId) {
503         hideUserCreatingDialog();
504         // prevent crash when config changes during user creation
505         if (getContext() == null) {
506             return;
507         }
508         mAddingUser = false;
509         UserInfo userInfo = mUserManager.getUserInfo(userId);
510         openUserDetails(userInfo, true);
511     }
512 
hideUserCreatingDialog()513     private void hideUserCreatingDialog() {
514         if (mUserCreatingDialog != null && mUserCreatingDialog.isShowing()) {
515             mUserCreatingDialog.dismiss();
516         }
517     }
518 
onUserCreationFailed()519     private void onUserCreationFailed() {
520         Toast.makeText(getContext(),
521                 com.android.settingslib.R.string.add_user_failed,
522                 Toast.LENGTH_SHORT).show();
523         hideUserCreatingDialog();
524     }
525 
openUserDetails(UserInfo userInfo, boolean newUser)526     private void openUserDetails(UserInfo userInfo, boolean newUser) {
527         Bundle extras = new Bundle();
528         extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userInfo.id);
529         extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, newUser);
530 
531         final Context context = getContext();
532         SubSettingLauncher launcher = new SubSettingLauncher(context)
533                 .setDestination(UserDetailsSettings.class.getName())
534                 .setArguments(extras)
535                 .setTitleText(getUserName(context, userInfo))
536                 .setSourceMetricsCategory(getMetricsCategory());
537         if (mGuestUserAutoCreated && userInfo.isGuest()) {
538             launcher.setResultListener(this, REQUEST_EDIT_GUEST);
539         }
540         launcher.launch();
541     }
542 
543     @Override
onDialogShowing()544     public void onDialogShowing() {
545         super.onDialogShowing();
546 
547         setOnDismissListener(this);
548     }
549 
550     @Override
onCreateDialog(int dialogId)551     public Dialog onCreateDialog(int dialogId) {
552         Context context = getActivity();
553         if (context == null) {
554             return null;
555         }
556         switch (dialogId) {
557             case DIALOG_CONFIRM_REMOVE: {
558                 Dialog dlg =
559                         UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId,
560                                 new DialogInterface.OnClickListener() {
561                                     public void onClick(DialogInterface dialog, int which) {
562                                         removeUserNow();
563                                     }
564                                 }
565                         );
566                 return dlg;
567             }
568             case DIALOG_USER_CANNOT_MANAGE:
569                 return new AlertDialog.Builder(context)
570                         .setMessage(R.string.user_cannot_manage_message)
571                         .setPositiveButton(android.R.string.ok, null)
572                         .create();
573             case DIALOG_ADD_USER: {
574                 final SharedPreferences preferences = getActivity().getPreferences(
575                         Context.MODE_PRIVATE);
576                 final boolean longMessageDisplayed = preferences.getBoolean(
577                         KEY_ADD_USER_LONG_MESSAGE_DISPLAYED, false);
578                 final int messageResId = longMessageDisplayed
579                         ? com.android.settingslib.R.string.user_add_user_message_short
580                         : com.android.settingslib.R.string.user_add_user_message_long;
581                 Dialog dlg = new AlertDialog.Builder(context)
582                         .setTitle(com.android.settingslib.R.string.user_add_user_title)
583                         .setMessage(messageResId)
584                         .setPositiveButton(android.R.string.ok,
585                                 new DialogInterface.OnClickListener() {
586                                     public void onClick(DialogInterface dialog, int which) {
587                                         showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_USER);
588                                         if (!longMessageDisplayed) {
589                                             preferences.edit().putBoolean(
590                                                     KEY_ADD_USER_LONG_MESSAGE_DISPLAYED,
591                                                     true).apply();
592                                         }
593                                     }
594                                 })
595                         .setNegativeButton(android.R.string.cancel, null)
596                         .create();
597                 return dlg;
598             }
599             case DIALOG_CHOOSE_USER_TYPE: {
600                 List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
601                 HashMap<String, String> addUserItem = new HashMap<String, String>();
602                 addUserItem.put(KEY_TITLE, getString(
603                         com.android.settingslib.R.string.user_add_user_item_title));
604                 addUserItem.put(KEY_SUMMARY, getString(
605                         com.android.settingslib.R.string.user_add_user_item_summary));
606                 HashMap<String, String> addProfileItem = new HashMap<String, String>();
607                 addProfileItem.put(KEY_TITLE, getString(
608                         com.android.settingslib.R.string.user_add_profile_item_title));
609                 addProfileItem.put(KEY_SUMMARY, getString(
610                         com.android.settingslib.R.string.user_add_profile_item_summary));
611                 data.add(addUserItem);
612                 data.add(addProfileItem);
613                 AlertDialog.Builder builder = new AlertDialog.Builder(context);
614                 SimpleAdapter adapter = new SimpleAdapter(builder.getContext(),
615                         data, R.layout.two_line_list_item,
616                         new String[]{KEY_TITLE, KEY_SUMMARY},
617                         new int[]{R.id.title, R.id.summary});
618                 builder.setTitle(com.android.settingslib.R.string.user_add_user_type_title);
619                 builder.setAdapter(adapter,
620                         new DialogInterface.OnClickListener() {
621                             @Override
622                             public void onClick(DialogInterface dialog, int which) {
623                                 onAddUserClicked(which == 0
624                                         ? USER_TYPE_USER
625                                         : USER_TYPE_RESTRICTED_PROFILE);
626                             }
627                         });
628                 return builder.create();
629             }
630             case DIALOG_NEED_LOCKSCREEN: {
631                 Dialog dlg = new AlertDialog.Builder(context)
632                         .setMessage(com.android.settingslib.R.string.user_need_lock_message)
633                         .setPositiveButton(com.android.settingslib.R.string.user_set_lock_button,
634                                 new DialogInterface.OnClickListener() {
635                                     @Override
636                                     public void onClick(DialogInterface dialog, int which) {
637                                         launchChooseLockscreen();
638                                     }
639                                 })
640                         .setNegativeButton(android.R.string.cancel, null)
641                         .create();
642                 return dlg;
643             }
644             case DIALOG_CONFIRM_EXIT_GUEST: {
645                 Dialog dlg = new AlertDialog.Builder(context)
646                         .setTitle(R.string.user_exit_guest_confirm_title)
647                         .setMessage(R.string.user_exit_guest_confirm_message)
648                         .setPositiveButton(R.string.user_exit_guest_dialog_remove,
649                                 new DialogInterface.OnClickListener() {
650                                     @Override
651                                     public void onClick(DialogInterface dialog, int which) {
652                                         exitGuest();
653                                     }
654                                 })
655                         .setNegativeButton(android.R.string.cancel, null)
656                         .create();
657                 return dlg;
658             }
659             case DIALOG_USER_PROFILE_EDITOR: {
660                 return buildEditCurrentUserDialog();
661             }
662             case DIALOG_USER_PROFILE_EDITOR_ADD_USER: {
663                 synchronized (mUserLock) {
664                     mPendingUserName = getString(
665                             com.android.settingslib.R.string.user_new_user_name);
666                     mPendingUserIcon = null;
667                 }
668                 return buildAddUserDialog(USER_TYPE_USER);
669             }
670             case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE: {
671                 synchronized (mUserLock) {
672                     mPendingUserName = getString(
673                             com.android.settingslib.R.string.user_new_profile_name);
674                     mPendingUserIcon = null;
675                 }
676                 return buildAddUserDialog(USER_TYPE_RESTRICTED_PROFILE);
677             }
678             case DIALOG_CONFIRM_RESET_GUEST: {
679                 return UserDialogs.createResetGuestDialog(getActivity(),
680                         (dialog, which) -> resetGuest());
681             }
682             default:
683                 return null;
684         }
685     }
686 
buildEditCurrentUserDialog()687     private Dialog buildEditCurrentUserDialog() {
688         final Activity activity = getActivity();
689         if (activity == null) {
690             return null;
691         }
692 
693         UserInfo user = mUserManager.getUserInfo(Process.myUserHandle().getIdentifier());
694         Drawable userIcon = Utils.getUserIcon(activity, mUserManager, user);
695 
696         return mEditUserInfoController.createDialog(
697                 activity,
698                 this::startActivityForResult,
699                 userIcon,
700                 user.name,
701                 getString(com.android.settingslib.R.string.profile_info_settings_title),
702                 (newUserName, newUserIcon) -> {
703                     if (newUserIcon != userIcon) {
704                         ThreadUtils.postOnBackgroundThread(() ->
705                                 mUserManager.setUserIcon(user.id,
706                                         UserIcons.convertToBitmap(newUserIcon)));
707                         mMePreference.setIcon(newUserIcon);
708                     }
709 
710                     if (!TextUtils.isEmpty(newUserName) && !newUserName.equals(user.name)) {
711                         mMePreference.setTitle(newUserName);
712                         mUserManager.setUserName(user.id, newUserName);
713                     }
714                 }, null);
715     }
716 
717     private Dialog buildAddUserDialog(int userType) {
718         Dialog d;
719         synchronized (mUserLock) {
720             d = mEditUserInfoController.createDialog(
721                     getActivity(),
722                     this::startActivityForResult,
723                     null,
724                     mPendingUserName.toString(),
725                     getString(userType == USER_TYPE_USER
726                             ? com.android.settingslib.R.string.user_info_settings_title
727                             : com.android.settingslib.R.string.profile_info_settings_title),
728                     (userName, userIcon) -> {
729                         mPendingUserIcon = userIcon;
730                         mPendingUserName = userName;
731                         addUserNow(userType);
732                     },
733                     () -> {
734                         synchronized (mUserLock) {
735                             mPendingUserIcon = null;
736                             mPendingUserName = null;
737                         }
738                     }
739             );
740         }
741         return d;
742     }
743 
744     @Override
745     public int getDialogMetricsCategory(int dialogId) {
746         switch (dialogId) {
747             case DIALOG_CONFIRM_REMOVE:
748                 return SettingsEnums.DIALOG_USER_REMOVE;
749             case DIALOG_USER_CANNOT_MANAGE:
750                 return SettingsEnums.DIALOG_USER_CANNOT_MANAGE;
751             case DIALOG_ADD_USER:
752                 return SettingsEnums.DIALOG_USER_ADD;
753             case DIALOG_CHOOSE_USER_TYPE:
754                 return SettingsEnums.DIALOG_USER_CHOOSE_TYPE;
755             case DIALOG_NEED_LOCKSCREEN:
756                 return SettingsEnums.DIALOG_USER_NEED_LOCKSCREEN;
757             case DIALOG_CONFIRM_EXIT_GUEST:
758             case DIALOG_CONFIRM_RESET_GUEST:
759                 return SettingsEnums.DIALOG_USER_CONFIRM_EXIT_GUEST;
760             case DIALOG_USER_PROFILE_EDITOR:
761             case DIALOG_USER_PROFILE_EDITOR_ADD_USER:
762             case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE:
763                 return SettingsEnums.DIALOG_USER_EDIT_PROFILE;
764             default:
765                 return 0;
766         }
767     }
768 
769     private void removeUserNow() {
770         if (mRemovingUserId == UserHandle.myUserId()) {
771             removeThisUser();
772         } else {
773             ThreadUtils.postOnBackgroundThread(new Runnable() {
774                 @Override
775                 public void run() {
776                     synchronized (mUserLock) {
777                         mUserManager.removeUser(mRemovingUserId);
778                         mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
779                     }
780                 }
781             });
782         }
783     }
784 
785     private void removeThisUser() {
786         if (!canSwitchUserNow()) {
787             Log.w(TAG, "Cannot remove current user when switching is disabled");
788             return;
789         }
790         try {
791             getContext().getSystemService(UserManager.class)
792                     .removeUserOrSetEphemeral(UserHandle.myUserId(),
793                             /* evenWhenDisallowed= */ false);
794             ActivityManager.getService().switchUser(UserHandle.USER_SYSTEM);
795         } catch (RemoteException re) {
796             Log.e(TAG, "Unable to remove self user");
797         }
798     }
799 
800     private void addUserNow(final int userType) {
801         synchronized (mUserLock) {
802             mAddingUser = true;
803             mAddingUserName = userType == USER_TYPE_USER
804                     ? (mPendingUserName != null ? mPendingUserName.toString()
805                     : getString(R.string.user_new_user_name))
806                     : (mPendingUserName != null ? mPendingUserName.toString()
807                             : getString(R.string.user_new_profile_name));
808         }
809 
810         mUserCreatingDialog = new UserCreatingDialog(getActivity());
811         mUserCreatingDialog.show();
812         ThreadUtils.postOnBackgroundThread(new Runnable() {
813             @Override
814             public void run() {
815                 UserInfo user;
816                 String username;
817 
818                 synchronized (mUserLock) {
819                     username = mAddingUserName;
820                 }
821 
822                 // Could take a few seconds
823                 if (userType == USER_TYPE_USER) {
824                     user = mUserManager.createUser(username, 0);
825                 } else {
826                     user = mUserManager.createRestrictedProfile(username);
827                 }
828 
829                 synchronized (mUserLock) {
830                     if (user == null) {
831                         mAddingUser = false;
832                         mPendingUserIcon = null;
833                         mPendingUserName = null;
834                         ThreadUtils.postOnMainThread(() -> onUserCreationFailed());
835                         return;
836                     }
837 
838                     Drawable newUserIcon = mPendingUserIcon;
839                     if (newUserIcon == null) {
840                         newUserIcon = UserIcons.getDefaultUserIcon(getResources(), user.id, false);
841                     }
842                     mUserManager.setUserIcon(user.id, UserIcons.convertToBitmap(newUserIcon));
843 
844                     if (userType == USER_TYPE_USER) {
845                         mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
846                     }
847 
848                     mHandler.sendMessage(mHandler.obtainMessage(
849                             MESSAGE_USER_CREATED, user.id, user.serialNumber));
850 
851                     mPendingUserIcon = null;
852                     mPendingUserName = null;
853                 }
854             }
855         });
856     }
857 
858     /**
859      * Erase the current user (guest) and switch to another user.
860      */
861     @VisibleForTesting
862     void exitGuest() {
863         // Just to be safe
864         if (!isCurrentUserGuest()) {
865             return;
866         }
867         mMetricsFeatureProvider.action(getActivity(),
868                 SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
869         removeThisUser();
870     }
871 
872     /**
873      * Erase the current user (assuming it is a guest user), and create a new one in the background
874      */
875     @VisibleForTesting
876     void resetGuest() {
877         // Just to be safe
878         if (!isCurrentUserGuest()) {
879             return;
880         }
881         int guestUserId = UserHandle.myUserId();
882         // Using markGuestForDeletion allows us to create a new guest before this one is
883         // fully removed. This could happen if someone calls scheduleGuestCreation()
884         // immediately after calling this method.
885         boolean marked = mUserManager.markGuestForDeletion(guestUserId);
886         if (!marked) {
887             Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
888             return;
889         }
890         exitGuest();
891         scheduleGuestCreation();
892     }
893 
894     /**
895      * Create a guest user in the background
896      */
897     @VisibleForTesting
898     void scheduleGuestCreation() {
899         // TODO(b/191067027): Move guest recreation to system_server
900         if (mGuestCreationScheduled.compareAndSet(/* expect= */ false, /* update= */ true)) {
901             // Once mGuestCreationScheduled=true, mAddGuest needs to be updated so that it shows
902             // "Resetting guest..."
903             mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
904             mExecutor.execute(() -> {
905                 UserInfo guest = mUserManager.createGuest(
906                         getContext(), getString(com.android.settingslib.R.string.user_guest));
907                 mGuestCreationScheduled.set(false);
908                 if (guest == null) {
909                     Log.e(TAG, "Unable to automatically recreate guest user");
910                 }
911                 // The list needs to be updated whether or not guest creation worked. If guest
912                 // creation failed, the list needs to update so that "Add guest" is displayed.
913                 // Otherwise, the UX could be stuck in a state where there is no way to switch to
914                 // the guest user (e.g. Guest would not be selectable, and it would be stuck
915                 // saying "Resetting guest...")
916                 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
917             });
918         }
919     }
920 
921     @VisibleForTesting
922     void updateUserList() {
923         final Context context = getActivity();
924         if (context == null) {
925             return;
926         }
927         final List<UserInfo> users = mUserManager.getAliveUsers();
928 
929         final ArrayList<Integer> missingIcons = new ArrayList<>();
930         final ArrayList<UserPreference> userPreferences = new ArrayList<>();
931         userPreferences.add(mMePreference);
932 
933         boolean canOpenUserDetails =
934                 mUserCaps.mIsAdmin || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser);
935         for (UserInfo user : users) {
936             if (!user.supportsSwitchToByUser()) {
937                 // Only users that can be switched to should show up here.
938                 // e.g. Managed profiles appear under Accounts Settings instead
939                 continue;
940             }
941             UserPreference pref;
942             if (user.id == UserHandle.myUserId()) {
943                 pref = mMePreference;
944             } else {
945                 final Context prefContext = getPrefContext();
946                 pref = new UserPreference(prefContext, null, user.id);
947                 pref.setTitle(getUserName(prefContext, user));
948                 userPreferences.add(pref);
949                 pref.setOnPreferenceClickListener(this);
950                 pref.setEnabled(canOpenUserDetails);
951                 pref.setSelectable(true);
952 
953                 if (user.isGuest()) {
954                     pref.setIcon(getEncircledDefaultIcon());
955                     pref.setKey(KEY_USER_GUEST);
956                     if (mUserCaps.mDisallowSwitchUser) {
957                         pref.setDisabledByAdmin(
958                                 RestrictedLockUtilsInternal.getDeviceOwner(context));
959                     } else {
960                         pref.setDisabledByAdmin(null);
961                     }
962                 } else {
963                     pref.setKey("id=" + user.id);
964                     if (user.isAdmin()) {
965                         pref.setSummary(R.string.user_admin);
966                     }
967                 }
968             }
969             if (pref == null) {
970                 continue;
971             }
972             if (user.id != UserHandle.myUserId() && !user.isGuest() && !user.isInitialized()) {
973                 // sometimes after creating a guest the initialized flag isn't immediately set
974                 // and we don't want to show "Not set up" summary for them
975                 if (user.isRestricted()) {
976                     pref.setSummary(R.string.user_summary_restricted_not_set_up);
977                 } else {
978                     pref.setSummary(R.string.user_summary_not_set_up);
979                     // Disallow setting up user which results in user switching when the
980                     // restriction is set.
981                     pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow());
982                 }
983             } else if (user.isRestricted()) {
984                 pref.setSummary(R.string.user_summary_restricted_profile);
985             }
986             if (user.iconPath != null) {
987                 if (mUserIcons.get(user.id) == null) {
988                     // Icon not loaded yet, print a placeholder
989                     missingIcons.add(user.id);
990                     pref.setIcon(getEncircledDefaultIcon());
991                 } else {
992                     setPhotoId(pref, user);
993                 }
994             } else {
995                 // Icon not available yet, print a placeholder
996                 pref.setIcon(getEncircledDefaultIcon());
997             }
998         }
999 
1000         // Add a temporary entry for the user being created
1001         if (mAddingUser) {
1002             UserPreference pref = new UserPreference(getPrefContext(), null,
1003                     UserPreference.USERID_UNKNOWN);
1004             pref.setEnabled(false);
1005             pref.setTitle(mAddingUserName);
1006             pref.setIcon(getEncircledDefaultIcon());
1007             userPreferences.add(pref);
1008         }
1009 
1010 
1011         // Sort list of users by serialNum
1012         Collections.sort(userPreferences, UserPreference.SERIAL_NUMBER_COMPARATOR);
1013 
1014         getActivity().invalidateOptionsMenu();
1015 
1016         // Load the icons
1017         if (missingIcons.size() > 0) {
1018             loadIconsAsync(missingIcons);
1019         }
1020 
1021         // If profiles are supported, mUserListCategory will have a special title
1022         if (mUserCaps.mCanAddRestrictedProfile) {
1023             mUserListCategory.setTitle(R.string.user_list_title);
1024         } else {
1025             mUserListCategory.setTitle(null);
1026         }
1027 
1028         // Remove everything from mUserListCategory and add new users.
1029         mUserListCategory.removeAll();
1030 
1031         // If multi-user is disabled, just show top info and return.
1032         final Preference addUserOnLockScreen = getPreferenceScreen().findPreference(
1033                 mAddUserWhenLockedPreferenceController.getPreferenceKey());
1034         mAddUserWhenLockedPreferenceController.updateState(addUserOnLockScreen);
1035 
1036         final Preference multiUserTopIntroPrefence = getPreferenceScreen().findPreference(
1037                 mMultiUserTopIntroPreferenceController.getPreferenceKey());
1038         mMultiUserTopIntroPreferenceController.updateState(multiUserTopIntroPrefence);
1039         mUserListCategory.setVisible(mUserCaps.mUserSwitcherEnabled);
1040 
1041         updateAddGuest(context, users.stream().anyMatch(UserInfo::isGuest));
1042         updateAddUser(context);
1043 
1044         if (!mUserCaps.mUserSwitcherEnabled) {
1045             return;
1046         }
1047 
1048         for (UserPreference userPreference : userPreferences) {
1049             userPreference.setOrder(Preference.DEFAULT_ORDER);
1050             mUserListCategory.addPreference(userPreference);
1051         }
1052 
1053     }
1054 
1055     private boolean isCurrentUserGuest() {
1056         return mUserCaps.mIsGuest;
1057     }
1058 
1059     private boolean canSwitchUserNow() {
1060         return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK;
1061     }
1062 
1063     private void updateAddGuest(Context context, boolean isGuestAlreadyCreated) {
1064         if (!isGuestAlreadyCreated && mUserCaps.mCanAddGuest
1065                 && WizardManagerHelper.isDeviceProvisioned(context)
1066                 && mUserCaps.mUserSwitcherEnabled) {
1067             mAddGuest.setVisible(true);
1068             mAddGuest.setIcon(getEncircledDefaultIcon());
1069             mAddGuest.setSelectable(true);
1070             if (mGuestUserAutoCreated && mGuestCreationScheduled.get()) {
1071                 mAddGuest.setTitle(com.android.settingslib.R.string.user_guest);
1072                 mAddGuest.setSummary(R.string.guest_resetting);
1073                 mAddGuest.setEnabled(false);
1074             } else {
1075                 mAddGuest.setTitle(com.android.settingslib.R.string.guest_new_guest);
1076                 mAddGuest.setEnabled(canSwitchUserNow());
1077             }
1078         } else {
1079             mAddGuest.setVisible(false);
1080         }
1081     }
1082 
1083     private void updateAddUser(Context context) {
1084         if ((mUserCaps.mCanAddUser || mUserCaps.mDisallowAddUserSetByAdmin)
1085                 && WizardManagerHelper.isDeviceProvisioned(context)
1086                 && mUserCaps.mUserSwitcherEnabled) {
1087             mAddUser.setVisible(true);
1088             mAddUser.setSelectable(true);
1089             final boolean canAddMoreUsers = mUserManager.canAddMoreUsers();
1090             mAddUser.setEnabled(canAddMoreUsers && !mAddingUser && canSwitchUserNow());
1091             if (!canAddMoreUsers) {
1092                 mAddUser.setSummary(
1093                         getString(R.string.user_add_max_count, getRealUsersCount()));
1094             } else {
1095                 mAddUser.setSummary(null);
1096             }
1097             if (mAddUser.isEnabled()) {
1098                 mAddUser.setDisabledByAdmin(
1099                         mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null);
1100             }
1101         } else {
1102             mAddUser.setVisible(false);
1103         }
1104     }
1105 
1106     /**
1107      * @return number of non-guest non-managed users
1108      */
1109     @VisibleForTesting
1110     int getRealUsersCount() {
1111         return (int) mUserManager.getUsers()
1112                 .stream()
1113                 .filter(user -> !user.isGuest() && !user.isProfile())
1114                 .count();
1115     }
1116 
1117     private void loadIconsAsync(List<Integer> missingIcons) {
1118         new AsyncTask<List<Integer>, Void, Void>() {
1119             @Override
1120             protected void onPostExecute(Void result) {
1121                 updateUserList();
1122             }
1123 
1124             @Override
1125             protected Void doInBackground(List<Integer>... values) {
1126                 for (int userId : values[0]) {
1127                     Bitmap bitmap = mUserManager.getUserIcon(userId);
1128                     if (bitmap == null) {
1129                         bitmap = getDefaultUserIconAsBitmap(getContext().getResources(), userId);
1130                     }
1131                     mUserIcons.append(userId, bitmap);
1132                 }
1133                 return null;
1134             }
1135         }.execute(missingIcons);
1136     }
1137 
1138     private Drawable getEncircledDefaultIcon() {
1139         if (mDefaultIconDrawable == null) {
1140             mDefaultIconDrawable = encircle(
1141                     getDefaultUserIconAsBitmap(getContext().getResources(), UserHandle.USER_NULL));
1142         }
1143         return mDefaultIconDrawable;
1144     }
1145 
1146     private void setPhotoId(Preference pref, UserInfo user) {
1147         Bitmap bitmap = mUserIcons.get(user.id);
1148         if (bitmap != null) {
1149             pref.setIcon(encircle(bitmap));
1150         }
1151     }
1152 
1153     /** Returns the user's name, or the appropriate string in the case of a Guest. */
1154     public static String getUserName(Context context, @NonNull UserInfo userInfo) {
1155         if (userInfo.isGuest()) {
1156             return context.getString(R.string.user_guest);
1157         }
1158         return userInfo.name;
1159     }
1160 
1161     @Override
1162     public boolean onPreferenceClick(Preference pref) {
1163         if (pref == mMePreference) {
1164             if (isCurrentUserGuest()) {
1165                 if (mGuestUserAutoCreated) {
1166                     showDialog(DIALOG_CONFIRM_RESET_GUEST);
1167                 } else {
1168                     showDialog(DIALOG_CONFIRM_EXIT_GUEST);
1169                 }
1170             } else {
1171                 showDialog(DIALOG_USER_PROFILE_EDITOR);
1172             }
1173             return true;
1174         } else if (pref instanceof UserPreference) {
1175             UserInfo userInfo = mUserManager.getUserInfo(((UserPreference) pref).getUserId());
1176             openUserDetails(userInfo, false);
1177             return true;
1178         } else if (pref == mAddUser) {
1179             // If we allow both types, show a picker, otherwise directly go to
1180             // flow for full user.
1181             if (mUserCaps.mCanAddRestrictedProfile) {
1182                 showDialog(DIALOG_CHOOSE_USER_TYPE);
1183             } else {
1184                 onAddUserClicked(USER_TYPE_USER);
1185             }
1186             return true;
1187         } else if (pref == mAddGuest) {
1188             mAddGuest.setEnabled(false); // prevent multiple tap issue
1189             mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_GUEST_ADD);
1190             UserInfo guest = mUserManager.createGuest(
1191                     getContext(), getString(com.android.settingslib.R.string.user_guest));
1192             if (guest == null) {
1193                 Toast.makeText(getContext(),
1194                         com.android.settingslib.R.string.add_user_failed,
1195                         Toast.LENGTH_SHORT).show();
1196                 return true;
1197             }
1198             openUserDetails(guest, true);
1199             return true;
1200         }
1201         return false;
1202     }
1203 
1204     private Drawable encircle(Bitmap icon) {
1205         Drawable circled = CircleFramedDrawable.getInstance(getActivity(), icon);
1206         return circled;
1207     }
1208 
1209     @Override
1210     public void onDismiss(DialogInterface dialog) {
1211         synchronized (mUserLock) {
1212             mRemovingUserId = -1;
1213             updateUserList();
1214         }
1215     }
1216 
1217     @Override
1218     public int getHelpResource() {
1219         return R.string.help_url_users;
1220     }
1221 
1222     /**
1223      * Returns a default user icon (as a {@link Bitmap}) for the given user.
1224      *
1225      * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
1226      *
1227      * @param resources resources object to fetch the user icon.
1228      * @param userId    the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
1229      */
1230     private static Bitmap getDefaultUserIconAsBitmap(Resources resources, int userId) {
1231         Bitmap bitmap = null;
1232         // Try finding the corresponding bitmap in the dark bitmap cache
1233         bitmap = sDarkDefaultUserBitmapCache.get(userId);
1234         if (bitmap == null) {
1235             bitmap = UserIcons.convertToBitmap(
1236                     UserIcons.getDefaultUserIcon(resources, userId, false));
1237             // Save it to cache
1238             sDarkDefaultUserBitmapCache.put(userId, bitmap);
1239         }
1240         return bitmap;
1241     }
1242 
1243     /**
1244      * Assign the default photo to user with {@paramref userId}
1245      *
1246      * @param context used to get the {@link UserManager}
1247      * @param userId  used to get the icon bitmap
1248      * @return true if assign photo successfully, false if failed
1249      */
1250     @VisibleForTesting
1251     static boolean assignDefaultPhoto(Context context, int userId) {
1252         if (context == null) {
1253             return false;
1254         }
1255         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
1256         Bitmap bitmap = getDefaultUserIconAsBitmap(context.getResources(), userId);
1257         um.setUserIcon(userId, bitmap);
1258 
1259         return true;
1260     }
1261 
1262     @WorkerThread
1263     static void copyMeProfilePhoto(Context context, UserInfo user) {
1264         Uri contactUri = ContactsContract.Profile.CONTENT_URI;
1265 
1266         int userId = user != null ? user.id : UserHandle.myUserId();
1267 
1268         InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
1269                 context.getContentResolver(),
1270                 contactUri, true);
1271         // If there's no profile photo, assign a default avatar
1272         if (avatarDataStream == null) {
1273             assignDefaultPhoto(context, userId);
1274             return;
1275         }
1276 
1277         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
1278         Bitmap icon = BitmapFactory.decodeStream(avatarDataStream);
1279         um.setUserIcon(userId, icon);
1280         try {
1281             avatarDataStream.close();
1282         } catch (IOException ioe) {
1283         }
1284     }
1285 
1286     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
1287             new BaseSearchIndexProvider(R.xml.user_settings) {
1288 
1289                 @Override
1290                 protected boolean isPageSearchEnabled(Context context) {
1291                     final UserCapabilities userCaps = UserCapabilities.create(context);
1292                     return userCaps.mEnabled;
1293                 }
1294 
1295                 @Override
1296                 public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId,
1297                         boolean suppressAllPage) {
1298                     final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId,
1299                             suppressAllPage);
1300                     AddUserWhenLockedPreferenceController controller =
1301                             new AddUserWhenLockedPreferenceController(
1302                                     context, KEY_ADD_USER_WHEN_LOCKED);
1303                     controller.updateNonIndexableKeys(niks);
1304                     new AutoSyncDataPreferenceController(context, null /* parent */)
1305                             .updateNonIndexableKeys(niks);
1306                     new AutoSyncPersonalDataPreferenceController(context, null /* parent */)
1307                             .updateNonIndexableKeys(niks);
1308                     new AutoSyncWorkDataPreferenceController(context, null /* parent */)
1309                             .updateNonIndexableKeys(niks);
1310                     return niks;
1311                 }
1312             };
1313 }
1314