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 static com.android.settingslib.Utils.getColorAttrDefaultColor;
20 
21 import android.app.Activity;
22 import android.app.ActivityManager;
23 import android.app.Dialog;
24 import android.app.admin.DevicePolicyManager;
25 import android.app.settings.SettingsEnums;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.DialogInterface;
29 import android.content.Intent;
30 import android.content.IntentFilter;
31 import android.content.pm.UserInfo;
32 import android.content.res.Resources;
33 import android.graphics.Bitmap;
34 import android.graphics.BitmapFactory;
35 import android.graphics.BlendMode;
36 import android.graphics.drawable.Drawable;
37 import android.graphics.drawable.LayerDrawable;
38 import android.multiuser.Flags;
39 import android.net.Uri;
40 import android.os.AsyncTask;
41 import android.os.Bundle;
42 import android.os.Handler;
43 import android.os.Message;
44 import android.os.Process;
45 import android.os.RemoteException;
46 import android.os.Trace;
47 import android.os.UserHandle;
48 import android.os.UserManager;
49 import android.provider.ContactsContract;
50 import android.provider.Settings;
51 import android.text.TextUtils;
52 import android.util.Log;
53 import android.util.SparseArray;
54 import android.view.Gravity;
55 import android.view.Menu;
56 import android.view.MenuInflater;
57 import android.view.MenuItem;
58 import android.view.WindowManagerGlobal;
59 import android.widget.SimpleAdapter;
60 import android.widget.Toast;
61 
62 import androidx.annotation.VisibleForTesting;
63 import androidx.annotation.WorkerThread;
64 import androidx.appcompat.app.AlertDialog;
65 import androidx.preference.Preference;
66 import androidx.preference.PreferenceGroup;
67 import androidx.preference.PreferenceScreen;
68 
69 import com.android.internal.util.UserIcons;
70 import com.android.internal.widget.LockPatternUtils;
71 import com.android.settings.R;
72 import com.android.settings.SettingsActivity;
73 import com.android.settings.SettingsPreferenceFragment;
74 import com.android.settings.Utils;
75 import com.android.settings.core.SubSettingLauncher;
76 import com.android.settings.password.ChooseLockGeneric;
77 import com.android.settings.search.BaseSearchIndexProvider;
78 import com.android.settings.widget.MainSwitchBarController;
79 import com.android.settings.widget.SettingsMainSwitchBar;
80 import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
81 import com.android.settingslib.RestrictedLockUtilsInternal;
82 import com.android.settingslib.RestrictedPreference;
83 import com.android.settingslib.drawable.CircleFramedDrawable;
84 import com.android.settingslib.search.SearchIndexable;
85 import com.android.settingslib.search.SearchIndexableRaw;
86 import com.android.settingslib.users.CreateUserDialogController;
87 import com.android.settingslib.users.EditUserInfoController;
88 import com.android.settingslib.users.GrantAdminDialogController;
89 import com.android.settingslib.users.UserCreatingDialog;
90 import com.android.settingslib.utils.ThreadUtils;
91 
92 import com.google.android.setupcompat.util.WizardManagerHelper;
93 
94 import java.io.IOException;
95 import java.io.InputStream;
96 import java.util.ArrayList;
97 import java.util.Collections;
98 import java.util.HashMap;
99 import java.util.List;
100 import java.util.concurrent.ExecutorService;
101 import java.util.concurrent.Executors;
102 import java.util.concurrent.Future;
103 import java.util.concurrent.atomic.AtomicBoolean;
104 import java.util.stream.Collectors;
105 
106 /**
107  * Screen that manages the list of users on the device.
108  * Secondary users and a guest user can be created if there is no restriction.
109  *
110  * The first user in the list is always the current user.
111  * Owner is the primary user.
112  */
113 @SearchIndexable
114 public class UserSettings extends SettingsPreferenceFragment
115         implements Preference.OnPreferenceClickListener,
116         MultiUserSwitchBarController.OnMultiUserSwitchChangedListener,
117         DialogInterface.OnDismissListener {
118 
119     private static final String TAG = "UserSettings";
120 
121     /** UserId of the user being removed */
122     private static final String SAVE_REMOVING_USER = "removing_user";
123     private static final String SAVE_CREATE_USER = "create_user";
124 
125     private static final String KEY_USER_LIST = "user_list";
126     private static final String KEY_USER_ME = "user_me";
127     private static final String KEY_USER_GUEST = "user_guest";
128     private static final String KEY_ADD_GUEST = "guest_add";
129     private static final String KEY_ADD_USER = "user_add";
130     private static final String KEY_ADD_SUPERVISED_USER = "supervised_user_add";
131     private static final String KEY_ADD_USER_WHEN_LOCKED = "user_settings_add_users_when_locked";
132     private static final String KEY_ENABLE_GUEST_TELEPHONY = "enable_guest_calling";
133     private static final String KEY_MULTIUSER_TOP_INTRO = "multiuser_top_intro";
134     private static final String KEY_TIMEOUT_TO_DOCK_USER = "timeout_to_dock_user_preference";
135     private static final String KEY_GUEST_CATEGORY = "guest_category";
136     private static final String KEY_GUEST_RESET = "guest_reset";
137     private static final String KEY_GUEST_EXIT = "guest_exit";
138     private static final String KEY_REMOVE_GUEST_ON_EXIT = "remove_guest_on_exit";
139     private static final String KEY_GUEST_USER_CATEGORY = "guest_user_category";
140     private static final String KEY_ALLOW_MULTIPLE_USERS = "allow_multiple_users";
141     private static final String KEY_USER_SETTINGS_SCREEN = "user_settings_screen";
142 
143     private static final String SETTING_GUEST_HAS_LOGGED_IN = "systemui.guest_has_logged_in";
144 
145     private static final int MENU_REMOVE_USER = Menu.FIRST;
146 
147     private static final IntentFilter USER_REMOVED_INTENT_FILTER;
148 
149     private static final int DIALOG_CONFIRM_REMOVE = 1;
150     private static final int DIALOG_ADD_USER = 2;
151     // Dialogs with id 3 and 4 got removed
152     private static final int DIALOG_USER_CANNOT_MANAGE = 5;
153     private static final int DIALOG_CHOOSE_USER_TYPE = 6;
154     private static final int DIALOG_NEED_LOCKSCREEN = 7;
155     private static final int DIALOG_CONFIRM_REMOVE_GUEST = 8;
156     private static final int DIALOG_USER_PROFILE_EDITOR = 9;
157     private static final int DIALOG_USER_PROFILE_EDITOR_ADD_USER = 10;
158     private static final int DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE = 11;
159     private static final int DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE = 12;
160     private static final int DIALOG_CONFIRM_RESET_AND_RESTART_GUEST = 13;
161     private static final int DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL = 14;
162     private static final int DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL = 15;
163     private static final int DIALOG_GRANT_ADMIN = 16;
164 
165     private static final int MESSAGE_UPDATE_LIST = 1;
166     private static final int MESSAGE_USER_CREATED = 2;
167     static final int MESSAGE_REMOVE_GUEST_ON_EXIT_CONTROLLER_GUEST_REMOVED = 3;
168 
169     private static final int USER_TYPE_USER = 1;
170     private static final int USER_TYPE_RESTRICTED_PROFILE = 2;
171 
172     private static final int REQUEST_CHOOSE_LOCK = 10;
173     private static final int REQUEST_EDIT_GUEST = 11;
174 
175     static final int RESULT_GUEST_REMOVED = 100;
176 
177     private static final String KEY_TITLE = "title";
178     private static final String KEY_SUMMARY = "summary";
179 
180     private static final String EXTRA_OPEN_DIALOG_USER_PROFILE_EDITOR =
181             "EXTRA_OPEN_DIALOG_USER_PROFILE_EDITOR";
182 
183     static {
184         USER_REMOVED_INTENT_FILTER = new IntentFilter(Intent.ACTION_USER_REMOVED);
185         USER_REMOVED_INTENT_FILTER.addAction(Intent.ACTION_USER_INFO_CHANGED);
186     }
187 
188     @VisibleForTesting
189     PreferenceGroup mUserListCategory;
190     @VisibleForTesting
191     PreferenceGroup mGuestUserCategory;
192     @VisibleForTesting
193     PreferenceGroup mGuestCategory;
194     @VisibleForTesting
195     Preference mGuestResetPreference;
196     @VisibleForTesting
197     Preference mGuestExitPreference;
198     @VisibleForTesting
199     UserPreference mMePreference;
200     @VisibleForTesting
201     RestrictedPreference mAddGuest;
202     @VisibleForTesting
203     RestrictedPreference mAddUser;
204     @VisibleForTesting
205     RestrictedPreference mAddSupervisedUser;
206     @VisibleForTesting
207     SparseArray<Bitmap> mUserIcons = new SparseArray<>();
208     private int mRemovingUserId = -1;
209     private boolean mAddingUser;
210     private boolean mGuestUserAutoCreated;
211     private String mConfigSupervisedUserCreationPackage;
212     private String mAddingUserName;
213     private UserCapabilities mUserCaps;
214     private boolean mShouldUpdateUserList = true;
215     private final Object mUserLock = new Object();
216     private UserManager mUserManager;
217     private static SparseArray<Bitmap> sDarkDefaultUserBitmapCache = new SparseArray<>();
218 
219     private MultiUserSwitchBarController mSwitchBarController;
220 
221     private GrantAdminDialogController mGrantAdminDialogController =
222             new GrantAdminDialogController();
223     private EditUserInfoController mEditUserInfoController =
224             new EditUserInfoController(Utils.FILE_PROVIDER_AUTHORITY);
225     private CreateUserDialogController mCreateUserDialogController =
226             new CreateUserDialogController(Utils.FILE_PROVIDER_AUTHORITY);
227     private AddUserWhenLockedPreferenceController mAddUserWhenLockedPreferenceController;
228     private GuestTelephonyPreferenceController mGuestTelephonyPreferenceController;
229     private RemoveGuestOnExitPreferenceController mRemoveGuestOnExitPreferenceController;
230     private MultiUserTopIntroPreferenceController mMultiUserTopIntroPreferenceController;
231     private TimeoutToDockUserPreferenceController mTimeoutToDockUserPreferenceController;
232     private UserCreatingDialog mUserCreatingDialog;
233     private final AtomicBoolean mGuestCreationScheduled = new AtomicBoolean();
234     private final ExecutorService mExecutor = Executors.newSingleThreadExecutor();
235 
236     private CharSequence mPendingUserName;
237     private Drawable mPendingUserIcon;
238     private boolean mPendingUserIsAdmin;
239 
240     // A place to cache the generated default avatar
241     private Drawable mDefaultIconDrawable;
242 
243     // TODO:   Replace current Handler solution to something that doesn't leak memory and works
244     // TODO:   during a configuration change
245     private Handler mHandler = new Handler() {
246         @Override
247         public void handleMessage(Message msg) {
248             switch (msg.what) {
249                 case MESSAGE_UPDATE_LIST:
250                     updateUserList();
251                     break;
252                 case MESSAGE_REMOVE_GUEST_ON_EXIT_CONTROLLER_GUEST_REMOVED:
253                     updateUserList();
254                     if (mGuestUserAutoCreated) {
255                         scheduleGuestCreation();
256                     }
257                     break;
258             }
259         }
260     };
261 
262     private BroadcastReceiver mUserChangeReceiver = new BroadcastReceiver() {
263         @Override
264         public void onReceive(Context context, Intent intent) {
265             if (intent.getAction().equals(Intent.ACTION_USER_REMOVED)) {
266                 mRemovingUserId = -1;
267             } else if (intent.getAction().equals(Intent.ACTION_USER_INFO_CHANGED)) {
268                 int userHandle = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
269                 if (userHandle != -1) {
270                     mUserIcons.remove(userHandle);
271                 }
272             }
273             mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
274         }
275     };
276 
277     @Override
getMetricsCategory()278     public int getMetricsCategory() {
279         return SettingsEnums.USER;
280     }
281 
282     @Override
onActivityCreated(Bundle savedInstanceState)283     public void onActivityCreated(Bundle savedInstanceState) {
284         super.onActivityCreated(savedInstanceState);
285         // Assume we are in a SettingsActivity. This is only safe because we currently use
286         // SettingsActivity as base for all preference fragments.
287         final SettingsActivity activity = (SettingsActivity) getActivity();
288         final SettingsMainSwitchBar switchBar = activity.getSwitchBar();
289         switchBar.setTitle(getContext().getString(R.string.multiple_users_main_switch_title));
290         if (!mUserCaps.mIsGuest) {
291             switchBar.show();
292         } else {
293             switchBar.hide();
294         }
295         mSwitchBarController = new MultiUserSwitchBarController(activity,
296                 new MainSwitchBarController(switchBar), this /* listener */);
297         getSettingsLifecycle().addObserver(mSwitchBarController);
298         boolean openUserEditDialog = getIntent().getBooleanExtra(
299                 EXTRA_OPEN_DIALOG_USER_PROFILE_EDITOR, false);
300         if (switchBar.isChecked() && openUserEditDialog) {
301             showDialog(DIALOG_USER_PROFILE_EDITOR);
302         }
303     }
304 
305     @Override
onCreate(Bundle icicle)306     public void onCreate(Bundle icicle) {
307         super.onCreate(icicle);
308         addPreferencesFromResource(R.xml.user_settings);
309         final Activity activity = getActivity();
310         if (!WizardManagerHelper.isDeviceProvisioned(activity)) {
311             activity.finish();
312             return;
313         }
314 
315         mGuestUserAutoCreated = getPrefContext().getResources().getBoolean(
316                 com.android.internal.R.bool.config_guestUserAutoCreated);
317 
318         mAddUserWhenLockedPreferenceController = new AddUserWhenLockedPreferenceController(
319                 activity, KEY_ADD_USER_WHEN_LOCKED);
320 
321         mGuestTelephonyPreferenceController = new GuestTelephonyPreferenceController(
322                 activity, KEY_ENABLE_GUEST_TELEPHONY);
323 
324         mRemoveGuestOnExitPreferenceController = new RemoveGuestOnExitPreferenceController(
325                 activity, KEY_REMOVE_GUEST_ON_EXIT, this, mHandler);
326 
327         mMultiUserTopIntroPreferenceController = new MultiUserTopIntroPreferenceController(activity,
328                 KEY_MULTIUSER_TOP_INTRO);
329 
330         mTimeoutToDockUserPreferenceController = new TimeoutToDockUserPreferenceController(
331                 activity, KEY_TIMEOUT_TO_DOCK_USER);
332 
333         final PreferenceScreen screen = getPreferenceScreen();
334         mAddUserWhenLockedPreferenceController.displayPreference(screen);
335         mGuestTelephonyPreferenceController.displayPreference(screen);
336         mRemoveGuestOnExitPreferenceController.displayPreference(screen);
337         mMultiUserTopIntroPreferenceController.displayPreference(screen);
338         mTimeoutToDockUserPreferenceController.displayPreference(screen);
339 
340         screen.findPreference(mAddUserWhenLockedPreferenceController.getPreferenceKey())
341                 .setOnPreferenceChangeListener(mAddUserWhenLockedPreferenceController);
342 
343         screen.findPreference(mGuestTelephonyPreferenceController.getPreferenceKey())
344                 .setOnPreferenceChangeListener(mGuestTelephonyPreferenceController);
345 
346         screen.findPreference(mRemoveGuestOnExitPreferenceController.getPreferenceKey())
347                 .setOnPreferenceChangeListener(mRemoveGuestOnExitPreferenceController);
348 
349         if (icicle != null) {
350             if (icicle.containsKey(SAVE_REMOVING_USER)) {
351                 mRemovingUserId = icicle.getInt(SAVE_REMOVING_USER);
352             }
353             if (icicle.containsKey(SAVE_CREATE_USER)) {
354                 mCreateUserDialogController.onRestoreInstanceState(icicle);
355             } else {
356                 mEditUserInfoController.onRestoreInstanceState(icicle);
357             }
358         }
359 
360         mUserCaps = UserCapabilities.create(activity);
361         mUserManager = (UserManager) activity.getSystemService(Context.USER_SERVICE);
362         if (!mUserCaps.mEnabled) {
363             return;
364         }
365 
366         final int myUserId = UserHandle.myUserId();
367 
368         mUserListCategory = (PreferenceGroup) findPreference(KEY_USER_LIST);
369         mMePreference = new UserPreference(getPrefContext(), null /* attrs */, myUserId);
370         mMePreference.setKey(KEY_USER_ME);
371         mMePreference.setOnPreferenceClickListener(this);
372 
373         mGuestCategory = findPreference(KEY_GUEST_CATEGORY);
374 
375         mGuestResetPreference = findPreference(KEY_GUEST_RESET);
376         mGuestResetPreference.setOnPreferenceClickListener(this);
377 
378         mGuestExitPreference = findPreference(KEY_GUEST_EXIT);
379         mGuestExitPreference.setOnPreferenceClickListener(this);
380 
381         mGuestUserCategory = findPreference(KEY_GUEST_USER_CATEGORY);
382 
383         mAddGuest = findPreference(KEY_ADD_GUEST);
384         mAddGuest.setOnPreferenceClickListener(this);
385 
386         mAddUser = findPreference(KEY_ADD_USER);
387         if (!mUserCaps.mCanAddRestrictedProfile) {
388             // Label should only mention adding a "user", not a "profile"
389             mAddUser.setTitle(com.android.settingslib.R.string.user_add_user);
390         }
391         mAddUser.setOnPreferenceClickListener(this);
392 
393         setConfigSupervisedUserCreationPackage();
394         mAddSupervisedUser = findPreference(KEY_ADD_SUPERVISED_USER);
395         mAddSupervisedUser.setOnPreferenceClickListener(this);
396 
397         activity.registerReceiverAsUser(
398                 mUserChangeReceiver, UserHandle.ALL, USER_REMOVED_INTENT_FILTER, null, mHandler,
399                 Context.RECEIVER_EXPORTED_UNAUDITED);
400 
401         updateUI();
402         mShouldUpdateUserList = false;
403     }
404 
405     @Override
onResume()406     public void onResume() {
407         super.onResume();
408 
409         if (!mUserCaps.mEnabled) {
410             return;
411         }
412         final PreferenceScreen screen = getPreferenceScreen();
413 
414         mAddUserWhenLockedPreferenceController.updateState(screen.findPreference(
415                 mAddUserWhenLockedPreferenceController.getPreferenceKey()));
416         mGuestTelephonyPreferenceController.updateState(screen.findPreference(
417                 mGuestTelephonyPreferenceController.getPreferenceKey()));
418         mTimeoutToDockUserPreferenceController.updateState(screen.findPreference(
419                 mTimeoutToDockUserPreferenceController.getPreferenceKey()));
420         mRemoveGuestOnExitPreferenceController.updateState(screen.findPreference(
421                 mRemoveGuestOnExitPreferenceController.getPreferenceKey()));
422         if (mShouldUpdateUserList) {
423             updateUI();
424         }
425     }
426 
427     @Override
onPause()428     public void onPause() {
429         mShouldUpdateUserList = true;
430         super.onPause();
431     }
432 
433     @Override
onDestroy()434     public void onDestroy() {
435         super.onDestroy();
436 
437         if (mUserCaps == null || !mUserCaps.mEnabled) {
438             return;
439         }
440 
441         getActivity().unregisterReceiver(mUserChangeReceiver);
442     }
443 
444     @Override
onSaveInstanceState(Bundle outState)445     public void onSaveInstanceState(Bundle outState) {
446         if (mCreateUserDialogController.isActive()) {
447             outState.putBoolean(SAVE_CREATE_USER, mCreateUserDialogController.isActive());
448             mCreateUserDialogController.onSaveInstanceState(outState);
449         } else {
450             mEditUserInfoController.onSaveInstanceState(outState);
451         }
452         outState.putInt(SAVE_REMOVING_USER, mRemovingUserId);
453         super.onSaveInstanceState(outState);
454     }
455 
456     @Override
startActivityForResult(Intent intent, int requestCode)457     public void startActivityForResult(Intent intent, int requestCode) {
458         mEditUserInfoController.startingActivityForResult();
459         mCreateUserDialogController.startingActivityForResult();
460         super.startActivityForResult(intent, requestCode);
461     }
462 
463     @Override
onCreateOptionsMenu(Menu menu, MenuInflater inflater)464     public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
465         int pos = 0;
466         if (!isCurrentUserAdmin() && canSwitchUserNow() && !isCurrentUserGuest()) {
467             String nickname = mUserManager.getUserName();
468             MenuItem removeThisUser = menu.add(0, MENU_REMOVE_USER, pos++,
469                     getResources().getString(R.string.user_remove_user_menu, nickname));
470             removeThisUser.setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
471 
472             final EnforcedAdmin disallowRemoveUserAdmin =
473                     RestrictedLockUtilsInternal.checkIfRestrictionEnforced(getContext(),
474                             UserManager.DISALLOW_REMOVE_USER, UserHandle.myUserId());
475             RestrictedLockUtilsInternal.setMenuItemAsDisabledByAdmin(getContext(), removeThisUser,
476                     disallowRemoveUserAdmin);
477         }
478         super.onCreateOptionsMenu(menu, inflater);
479     }
480 
481     @Override
onOptionsItemSelected(MenuItem item)482     public boolean onOptionsItemSelected(MenuItem item) {
483         final int itemId = item.getItemId();
484         if (itemId == MENU_REMOVE_USER) {
485             onRemoveUserClicked(UserHandle.myUserId());
486             return true;
487         } else {
488             return super.onOptionsItemSelected(item);
489         }
490     }
491 
492     @Override
onMultiUserSwitchChanged(boolean newState)493     public void onMultiUserSwitchChanged(boolean newState) {
494         updateUI();
495     }
496 
updateUI()497     private void updateUI() {
498         mUserCaps.updateAddUserCapabilities(getActivity());
499         loadProfile();
500         updateUserList();
501     }
502 
503     /**
504      * Loads profile information for the current user.
505      */
loadProfile()506     private void loadProfile() {
507         if (isCurrentUserGuest()) {
508             // No need to load profile information
509             mMePreference.setIcon(getEncircledDefaultIcon());
510             mMePreference.setTitle(mGuestUserAutoCreated
511                     ? com.android.settingslib.R.string.guest_reset_guest
512                     : com.android.settingslib.R.string.guest_exit_guest);
513             mMePreference.setSelectable(true);
514             // removing a guest will result in switching back to the admin user
515             mMePreference.setEnabled(canSwitchUserNow());
516             return;
517         }
518 
519         new AsyncTask<Void, Void, String>() {
520             @Override
521             protected void onPostExecute(String result) {
522                 finishLoadProfile(result);
523             }
524 
525             @Override
526             protected String doInBackground(Void... values) {
527                 UserInfo user = mUserManager.getUserInfo(UserHandle.myUserId());
528                 if (user.iconPath == null || user.iconPath.equals("")) {
529                     // Assign profile photo.
530                     copyMeProfilePhoto(getActivity(), user);
531                 }
532                 return user.name;
533             }
534         }.execute();
535     }
536 
finishLoadProfile(String profileName)537     private void finishLoadProfile(String profileName) {
538         if (getActivity() == null) {
539             return;
540         }
541         mMePreference.setTitle(getString(R.string.user_you, profileName));
542         int myUserId = UserHandle.myUserId();
543         Bitmap b = mUserManager.getUserIcon(myUserId);
544         if (b != null) {
545             mMePreference.setIcon(encircleUserIcon(b));
546             mUserIcons.put(myUserId, b);
547         }
548     }
549 
hasLockscreenSecurity()550     private boolean hasLockscreenSecurity() {
551         LockPatternUtils lpu = new LockPatternUtils(getActivity());
552         return lpu.isSecure(UserHandle.myUserId());
553     }
554 
launchChooseLockscreen()555     private void launchChooseLockscreen() {
556         Intent chooseLockIntent = new Intent(DevicePolicyManager.ACTION_SET_NEW_PASSWORD)
557                 .setPackage(getContext().getPackageName());
558         chooseLockIntent.putExtra(ChooseLockGeneric.ChooseLockGenericFragment.HIDE_INSECURE_OPTIONS,
559                 true);
560         startActivityForResult(chooseLockIntent, REQUEST_CHOOSE_LOCK);
561     }
562 
563     @Override
onActivityResult(int requestCode, int resultCode, Intent data)564     public void onActivityResult(int requestCode, int resultCode, Intent data) {
565         super.onActivityResult(requestCode, resultCode, data);
566 
567         if (requestCode == REQUEST_CHOOSE_LOCK) {
568             if (resultCode != Activity.RESULT_CANCELED && hasLockscreenSecurity()) {
569                 addUserNow(USER_TYPE_RESTRICTED_PROFILE);
570             }
571         } else if (mGuestUserAutoCreated && requestCode == REQUEST_EDIT_GUEST
572                 && resultCode == RESULT_GUEST_REMOVED) {
573             scheduleGuestCreation();
574         } else {
575             mCreateUserDialogController.onActivityResult(requestCode, resultCode, data);
576             mEditUserInfoController.onActivityResult(requestCode, resultCode, data);
577         }
578     }
579 
onAddUserClicked(int userType)580     private void onAddUserClicked(int userType) {
581         synchronized (mUserLock) {
582             if (mRemovingUserId == -1 && !mAddingUser) {
583                 switch (userType) {
584                     case USER_TYPE_USER:
585                         showDialog(DIALOG_ADD_USER);
586                         break;
587                     case USER_TYPE_RESTRICTED_PROFILE:
588                         if (hasLockscreenSecurity()) {
589                             showDialog(DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE);
590                         } else {
591                             showDialog(DIALOG_NEED_LOCKSCREEN);
592                         }
593                         break;
594                 }
595             }
596         }
597     }
598 
onAddSupervisedUserClicked()599     private void onAddSupervisedUserClicked() {
600         final Intent intent = new Intent()
601                 .setAction(UserManager.ACTION_CREATE_SUPERVISED_USER)
602                 .setPackage(mConfigSupervisedUserCreationPackage)
603                 .addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
604 
605         startActivity(intent);
606     }
607 
onAddGuestClicked()608     private void onAddGuestClicked() {
609         Context context = getContext();
610         final UserCreatingDialog guestCreatingDialog =
611                 new UserCreatingDialog(getActivity(), /* isGuest= */ true);
612         guestCreatingDialog.show();
613 
614         ThreadUtils.postOnBackgroundThread(() -> {
615             mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_GUEST_ADD);
616             Trace.beginSection("UserSettings.addGuest");
617             final UserInfo guest = mUserManager.createGuest(context);
618             Trace.endSection();
619 
620             ThreadUtils.postOnMainThread(() -> {
621                 guestCreatingDialog.dismiss();
622                 if (guest == null) {
623                     Toast.makeText(context,
624                             com.android.settingslib.R.string.add_guest_failed,
625                             Toast.LENGTH_SHORT).show();
626                     return;
627                 }
628                 openUserDetails(guest, true, context);
629             });
630         });
631     }
632 
onRemoveUserClicked(int userId)633     private void onRemoveUserClicked(int userId) {
634         synchronized (mUserLock) {
635             if (mRemovingUserId == -1 && !mAddingUser) {
636                 mRemovingUserId = userId;
637                 showDialog(DIALOG_CONFIRM_REMOVE);
638             }
639         }
640     }
641 
onUserCreated(UserInfo userInfo, Context context)642     private void onUserCreated(UserInfo userInfo, Context context) {
643         hideUserCreatingDialog();
644         mAddingUser = false;
645         openUserDetails(userInfo, true, context);
646     }
647 
hideUserCreatingDialog()648     private void hideUserCreatingDialog() {
649         if (mUserCreatingDialog != null && mUserCreatingDialog.isShowing()) {
650             mUserCreatingDialog.dismiss();
651         }
652     }
653 
onUserCreationFailed()654     private void onUserCreationFailed() {
655         Toast.makeText(getContext(),
656                 com.android.settingslib.R.string.add_user_failed,
657                 Toast.LENGTH_SHORT).show();
658         hideUserCreatingDialog();
659     }
660 
openUserDetails(UserInfo userInfo, boolean newUser)661     private void openUserDetails(UserInfo userInfo, boolean newUser) {
662         openUserDetails(userInfo, newUser, getContext());
663     }
664 
openUserDetails(UserInfo userInfo, boolean newUser, Context context)665     private void openUserDetails(UserInfo userInfo, boolean newUser, Context context) {
666         // to prevent a crash when config changes during user creation,
667         // we simply ignore this redirection step
668         if (context == null) {
669             return;
670         }
671 
672         Bundle extras = new Bundle();
673         extras.putInt(UserDetailsSettings.EXTRA_USER_ID, userInfo.id);
674         extras.putBoolean(AppRestrictionsFragment.EXTRA_NEW_USER, newUser);
675 
676         SubSettingLauncher launcher = new SubSettingLauncher(context)
677                 .setDestination(UserDetailsSettings.class.getName())
678                 .setArguments(extras)
679                 .setTitleText(userInfo.name)
680                 .setSourceMetricsCategory(getMetricsCategory());
681         if (mGuestUserAutoCreated && userInfo.isGuest()) {
682             launcher.setResultListener(this, REQUEST_EDIT_GUEST);
683         }
684         launcher.launch();
685     }
686 
687     @Override
onDialogShowing()688     public void onDialogShowing() {
689         super.onDialogShowing();
690 
691         setOnDismissListener(this);
692     }
693 
694     @Override
onCreateDialog(int dialogId)695     public Dialog onCreateDialog(int dialogId) {
696         Context context = getActivity();
697         if (context == null) {
698             return null;
699         }
700         switch (dialogId) {
701             case DIALOG_CONFIRM_REMOVE: {
702                 Dialog dlg =
703                         UserDialogs.createRemoveDialog(getActivity(), mRemovingUserId,
704                                 new DialogInterface.OnClickListener() {
705                                     public void onClick(DialogInterface dialog, int which) {
706                                         removeUserNow();
707                                     }
708                                 }
709                         );
710                 return dlg;
711             }
712             case DIALOG_USER_CANNOT_MANAGE:
713                 return new AlertDialog.Builder(context)
714                         .setMessage(R.string.user_cannot_manage_message)
715                         .setPositiveButton(android.R.string.ok, null)
716                         .create();
717             case DIALOG_ADD_USER: {
718                 synchronized (mUserLock) {
719                     mPendingUserName = getString(
720                             com.android.settingslib.R.string.user_new_user_name);
721                     mPendingUserIcon = null;
722                 }
723                 return buildAddUserDialog(USER_TYPE_USER);
724             }
725             case DIALOG_CHOOSE_USER_TYPE: {
726                 List<HashMap<String, String>> data = new ArrayList<HashMap<String, String>>();
727                 HashMap<String, String> addUserItem = new HashMap<String, String>();
728                 addUserItem.put(KEY_TITLE, getString(
729                         com.android.settingslib.R.string.user_add_user_item_title));
730                 addUserItem.put(KEY_SUMMARY, getString(
731                         com.android.settingslib.R.string.user_add_user_item_summary));
732                 HashMap<String, String> addProfileItem = new HashMap<String, String>();
733                 addProfileItem.put(KEY_TITLE, getString(
734                         com.android.settingslib.R.string.user_add_profile_item_title));
735                 addProfileItem.put(KEY_SUMMARY, getString(
736                         com.android.settingslib.R.string.user_add_profile_item_summary));
737                 data.add(addUserItem);
738                 data.add(addProfileItem);
739                 AlertDialog.Builder builder = new AlertDialog.Builder(context);
740                 SimpleAdapter adapter = new SimpleAdapter(builder.getContext(),
741                         data, R.layout.two_line_list_item,
742                         new String[]{KEY_TITLE, KEY_SUMMARY},
743                         new int[]{R.id.title, R.id.summary});
744                 builder.setTitle(com.android.settingslib.R.string.user_add_user_type_title);
745                 builder.setAdapter(adapter,
746                         new DialogInterface.OnClickListener() {
747                             @Override
748                             public void onClick(DialogInterface dialog, int which) {
749                                 onAddUserClicked(which == 0
750                                         ? USER_TYPE_USER
751                                         : USER_TYPE_RESTRICTED_PROFILE);
752                             }
753                         });
754                 return builder.create();
755             }
756             case DIALOG_NEED_LOCKSCREEN: {
757                 Dialog dlg = new AlertDialog.Builder(context)
758                         .setMessage(com.android.settingslib.R.string.user_need_lock_message)
759                         .setPositiveButton(com.android.settingslib.R.string.user_set_lock_button,
760                                 new DialogInterface.OnClickListener() {
761                                     @Override
762                                     public void onClick(DialogInterface dialog, int which) {
763                                         launchChooseLockscreen();
764                                     }
765                                 })
766                         .setNegativeButton(android.R.string.cancel, null)
767                         .create();
768                 return dlg;
769             }
770             case DIALOG_CONFIRM_REMOVE_GUEST: {
771                 Dialog dlg = new AlertDialog.Builder(context)
772                         .setTitle(com.android.settingslib.R.string.guest_remove_guest_dialog_title)
773                         .setMessage(R.string.user_exit_guest_confirm_message)
774                         .setPositiveButton(R.string.user_exit_guest_dialog_remove,
775                                 new DialogInterface.OnClickListener() {
776                                     @Override
777                                     public void onClick(DialogInterface dialog, int which) {
778                                         clearAndExitGuest();
779                                     }
780                                 })
781                         .setNegativeButton(android.R.string.cancel, null)
782                         .create();
783                 return dlg;
784             }
785             case DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL: {
786                 Dialog dlg = new AlertDialog.Builder(context)
787                         .setTitle(com.android.settingslib.R.string.guest_exit_dialog_title)
788                         .setMessage(com.android.settingslib.R.string.guest_exit_dialog_message)
789                         .setPositiveButton(
790                                 com.android.settingslib.R.string.guest_exit_dialog_button,
791                                 new DialogInterface.OnClickListener() {
792                                     @Override
793                                     public void onClick(DialogInterface dialog, int which) {
794                                         clearAndExitGuest();
795                                     }
796                                 })
797                         .setNeutralButton(android.R.string.cancel, null)
798                         .create();
799                 return dlg;
800             }
801             case DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL: {
802                 Dialog dlg = new AlertDialog.Builder(context)
803                         .setTitle(
804                             com.android.settingslib.R.string.guest_exit_dialog_title_non_ephemeral)
805                         .setMessage(
806                             com.android.settingslib
807                                 .R.string.guest_exit_dialog_message_non_ephemeral)
808                         .setPositiveButton(
809                             com.android.settingslib.R.string.guest_exit_save_data_button,
810                                 new DialogInterface.OnClickListener() {
811                                     @Override
812                                     public void onClick(DialogInterface dialog, int which) {
813                                         exitGuest();
814                                     }
815                                 })
816                         .setNegativeButton(
817                             com.android.settingslib.R.string.guest_exit_clear_data_button,
818                                 new DialogInterface.OnClickListener() {
819                                     @Override
820                                     public void onClick(DialogInterface dialog, int which) {
821                                         clearAndExitGuest();
822                                     }
823                                 })
824                         .setNeutralButton(android.R.string.cancel, null)
825                         .create();
826                 return dlg;
827             }
828             case DIALOG_USER_PROFILE_EDITOR: {
829                 return buildEditCurrentUserDialog();
830             }
831             case DIALOG_USER_PROFILE_EDITOR_ADD_USER: {
832                 synchronized (mUserLock) {
833                     mPendingUserName = getString(
834                             com.android.settingslib.R.string.user_new_user_name);
835                     mPendingUserIcon = null;
836                 }
837                 return buildAddUserDialog(USER_TYPE_USER);
838             }
839             case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE: {
840                 synchronized (mUserLock) {
841                     mPendingUserName = getString(
842                             com.android.settingslib.R.string.user_new_profile_name);
843                     mPendingUserIcon = null;
844                 }
845                 return buildAddUserDialog(USER_TYPE_RESTRICTED_PROFILE);
846             }
847             case DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE: {
848                 return UserDialogs.createResetGuestDialog(getActivity(),
849                         (dialog, which) -> clearAndExitGuest());
850             }
851             case DIALOG_CONFIRM_RESET_AND_RESTART_GUEST: {
852                 Dialog dlg = new AlertDialog.Builder(context)
853                         .setTitle(
854                             com.android.settingslib.R.string.guest_reset_and_restart_dialog_title)
855                         .setMessage(
856                             com.android.settingslib.R.string.guest_reset_and_restart_dialog_message)
857                         .setPositiveButton(
858                             com.android.settingslib.R.string.guest_reset_guest_confirm_button,
859                                 new DialogInterface.OnClickListener() {
860                                     @Override
861                                     public void onClick(DialogInterface dialog, int which) {
862                                         resetAndRestartGuest();
863                                     }
864                                 })
865                         .setNeutralButton(android.R.string.cancel, null)
866                         .create();
867                 return dlg;
868             }
869             default:
870                 return null;
871         }
872     }
873 
buildEditCurrentUserDialog()874     private Dialog buildEditCurrentUserDialog() {
875         final Activity activity = getActivity();
876         if (activity == null) {
877             return null;
878         }
879 
880         UserInfo user = mUserManager.getUserInfo(Process.myUserHandle().getIdentifier());
881         Drawable userIcon = Utils.getUserIcon(activity, mUserManager, user);
882 
883         return mEditUserInfoController.createDialog(
884                 activity,
885                 this::startActivityForResult,
886                 userIcon,
887                 user.name,
888                 (newUserName, newUserIcon) -> {
889                     if (newUserIcon != userIcon) {
890                         ThreadUtils.postOnBackgroundThread(() ->
891                                 mUserManager.setUserIcon(user.id,
892                                         UserIcons.convertToBitmapAtUserIconSize(
893                                                 activity.getResources(), newUserIcon)));
894                         mMePreference.setIcon(newUserIcon);
895                         if (Flags.avatarSync()) {
896                             final String pkg = getString(R.string.config_avatar_picker_package);
897                             final String action = pkg + ".set.confirm";
898                             activity.sendBroadcast(new Intent(action).setPackage(pkg));
899                         }
900                     }
901 
902                     if (!TextUtils.isEmpty(newUserName) && !newUserName.equals(user.name)) {
903                         mMePreference.setTitle(newUserName);
904                         mUserManager.setUserName(user.id, newUserName);
905                     }
906                 }, () -> {
907                     if (Flags.avatarSync()) {
908                         final String pkg = getString(R.string.config_avatar_picker_package);
909                         final String action = pkg + ".set.cancel";
910                         activity.sendBroadcast(new Intent(action).setPackage(pkg));
911                     }
912                 });
913     }
914 
915     private Dialog buildAddUserDialog(int userType) {
916         Dialog d;
917         synchronized (mUserLock) {
918             d = mCreateUserDialogController.createDialog(
919                     getActivity(),
920                     this::startActivityForResult,
921                     UserManager.isMultipleAdminEnabled(),
922                     (userName, userIcon, isAdmin) -> {
923                         mPendingUserIcon = userIcon;
924                         mPendingUserName = userName;
925                         mPendingUserIsAdmin = isAdmin;
926                         addUserNow(userType);
927                     },
928                     () -> {
929                         synchronized (mUserLock) {
930                             mPendingUserIcon = null;
931                             mPendingUserName = null;
932                         }
933                     }
934             );
935         }
936         return d;
937     }
938 
939     @Override
940     public int getDialogMetricsCategory(int dialogId) {
941         switch (dialogId) {
942             case DIALOG_CONFIRM_REMOVE:
943                 return SettingsEnums.DIALOG_USER_REMOVE;
944             case DIALOG_USER_CANNOT_MANAGE:
945                 return SettingsEnums.DIALOG_USER_CANNOT_MANAGE;
946             case DIALOG_GRANT_ADMIN:
947                 return SettingsEnums.DIALOG_GRANT_USER_ADMIN;
948             case DIALOG_ADD_USER:
949                 return SettingsEnums.DIALOG_USER_ADD;
950             case DIALOG_CHOOSE_USER_TYPE:
951                 return SettingsEnums.DIALOG_USER_CHOOSE_TYPE;
952             case DIALOG_NEED_LOCKSCREEN:
953                 return SettingsEnums.DIALOG_USER_NEED_LOCKSCREEN;
954             case DIALOG_CONFIRM_REMOVE_GUEST:
955             case DIALOG_CONFIRM_REMOVE_GUEST_WITH_AUTO_CREATE:
956             case DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL:
957             case DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL:
958             case DIALOG_CONFIRM_RESET_AND_RESTART_GUEST:
959                 return SettingsEnums.DIALOG_USER_CONFIRM_EXIT_GUEST;
960             case DIALOG_USER_PROFILE_EDITOR:
961             case DIALOG_USER_PROFILE_EDITOR_ADD_USER:
962             case DIALOG_USER_PROFILE_EDITOR_ADD_RESTRICTED_PROFILE:
963                 return SettingsEnums.DIALOG_USER_EDIT_PROFILE;
964             default:
965                 return 0;
966         }
967     }
968 
969     private void removeUserNow() {
970         if (mRemovingUserId == UserHandle.myUserId()) {
971             removeThisUser();
972         } else {
973             ThreadUtils.postOnBackgroundThread(new Runnable() {
974                 @Override
975                 public void run() {
976                     synchronized (mUserLock) {
977                         mUserManager.removeUser(mRemovingUserId);
978                         mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
979                     }
980                 }
981             });
982         }
983     }
984 
985     private void removeThisUser() {
986         if (!canSwitchUserNow()) {
987             Log.w(TAG, "Cannot remove current user when switching is disabled");
988             return;
989         }
990         try {
991             mUserManager.removeUserWhenPossible(
992                     UserHandle.of(UserHandle.myUserId()), /* overrideDevicePolicy= */ false);
993             ActivityManager.getService().switchUser(
994                     mUserManager.getPreviousForegroundUser().getIdentifier());
995         } catch (RemoteException re) {
996             Log.e(TAG, "Unable to remove self user");
997         }
998     }
999 
1000     private void switchToUserId(int userId) {
1001         if (!canSwitchUserNow()) {
1002             Log.w(TAG, "Cannot switch current user when switching is disabled");
1003             return;
1004         }
1005         try {
1006             ActivityManager.getService().switchUser(userId);
1007         } catch (RemoteException re) {
1008             Log.e(TAG, "Unable to switch user");
1009         }
1010     }
1011 
1012     private void addUserNow(final int userType) {
1013         Trace.beginAsyncSection("UserSettings.addUserNow", 0);
1014         synchronized (mUserLock) {
1015             mAddingUser = true;
1016             mAddingUserName = userType == USER_TYPE_USER
1017                     ? (mPendingUserName != null ? mPendingUserName.toString()
1018                     : getString(com.android.settingslib.R.string.user_new_user_name))
1019                     : (mPendingUserName != null ? mPendingUserName.toString()
1020                             : getString(com.android.settingslib.R.string.user_new_profile_name));
1021         }
1022 
1023         mUserCreatingDialog = new UserCreatingDialog(getActivity());
1024         mUserCreatingDialog.show();
1025         createUser(userType, mAddingUserName);
1026     }
1027 
1028     @VisibleForTesting
1029     void createUser(final int userType, String userName) {
1030         Context context = getContext();
1031         Resources resources = getResources();
1032         final Drawable selectedUserIcon = mPendingUserIcon;
1033         Future<?> unusedCreateUserFuture = ThreadUtils.postOnBackgroundThread(() -> {
1034             UserInfo user;
1035 
1036             if (userType == USER_TYPE_USER) {
1037                 user = mUserManager.createUser(
1038                         userName,
1039                         mUserManager.USER_TYPE_FULL_SECONDARY,
1040                         0);
1041                 if (mPendingUserIsAdmin) {
1042                     mUserManager.setUserAdmin(user.id);
1043                 }
1044             } else {
1045                 user = mUserManager.createRestrictedProfile(userName);
1046             }
1047 
1048             ThreadUtils.postOnMainThread(() -> {
1049                 if (user == null) {
1050                     mAddingUser = false;
1051                     mPendingUserIcon = null;
1052                     mPendingUserName = null;
1053                     onUserCreationFailed();
1054                     return;
1055                 }
1056 
1057                 Future<?> unusedSettingIconFuture = ThreadUtils.postOnBackgroundThread(() -> {
1058                     Drawable newUserIcon = selectedUserIcon;
1059                     if (newUserIcon == null) {
1060                         newUserIcon = UserIcons.getDefaultUserIcon(resources, user.id, false);
1061                     }
1062                     mUserManager.setUserIcon(
1063                             user.id, UserIcons.convertToBitmapAtUserIconSize(
1064                                     resources, newUserIcon));
1065                 });
1066 
1067                 mPendingUserIcon = null;
1068                 mPendingUserName = null;
1069 
1070                 onUserCreated(user, context);
1071             });
1072         });
1073     }
1074 
1075 
1076     /**
1077      * Erase the current user (guest) and switch to another user.
1078      */
1079     @VisibleForTesting
1080     void clearAndExitGuest() {
1081         // Just to be safe
1082         if (!isCurrentUserGuest()) {
1083             return;
1084         }
1085         mMetricsFeatureProvider.action(getActivity(),
1086                 SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
1087 
1088         int guestUserId = UserHandle.myUserId();
1089         // Using markGuestForDeletion allows us to create a new guest before this one is
1090         // fully removed.
1091         boolean marked = mUserManager.markGuestForDeletion(guestUserId);
1092         if (!marked) {
1093             Log.w(TAG, "Couldn't mark the guest for deletion for user " + guestUserId);
1094             return;
1095         }
1096 
1097         removeThisUser();
1098         if (mGuestUserAutoCreated) {
1099             scheduleGuestCreation();
1100         }
1101     }
1102 
1103     /**
1104      * Switch to another user.
1105      */
1106     private void exitGuest() {
1107         // Just to be safe
1108         if (!isCurrentUserGuest()) {
1109             return;
1110         }
1111         mMetricsFeatureProvider.action(getActivity(),
1112                 SettingsEnums.ACTION_USER_GUEST_EXIT_CONFIRMED);
1113         switchToUserId(mUserManager.getPreviousForegroundUser().getIdentifier());
1114     }
1115 
1116     private int createGuest() {
1117         UserInfo guest;
1118         Context context = getPrefContext();
1119         try {
1120             guest = mUserManager.createGuest(context);
1121         } catch (UserManager.UserOperationException e) {
1122             Log.e(TAG, "Couldn't create guest user", e);
1123             return UserHandle.USER_NULL;
1124         }
1125         if (guest == null) {
1126             Log.e(TAG, "Couldn't create guest, most likely because there already exists one");
1127             return UserHandle.USER_NULL;
1128         }
1129         return guest.id;
1130     }
1131 
1132     /**
1133      * Remove current guest and start a new guest session
1134      */
1135     private void resetAndRestartGuest() {
1136         // Just to be safe
1137         if (!isCurrentUserGuest()) {
1138             return;
1139         }
1140         int oldGuestUserId = UserHandle.myUserId();
1141         // Using markGuestForDeletion allows us to create a new guest before this one is
1142         // fully removed.
1143         boolean marked = mUserManager.markGuestForDeletion(oldGuestUserId);
1144         if (!marked) {
1145             Log.w(TAG, "Couldn't mark the guest for deletion for user " + oldGuestUserId);
1146             return;
1147         }
1148 
1149         try {
1150             // Create a new guest in the foreground, and then immediately switch to it
1151             int newGuestUserId = createGuest();
1152             if (newGuestUserId == UserHandle.USER_NULL) {
1153                 Log.e(TAG, "Could not create new guest, switching back to previous user");
1154                 switchToUserId(mUserManager.getPreviousForegroundUser().getIdentifier());
1155                 mUserManager.removeUser(oldGuestUserId);
1156                 WindowManagerGlobal.getWindowManagerService().lockNow(/* options= */ null);
1157                 return;
1158             }
1159             switchToUserId(newGuestUserId);
1160             mUserManager.removeUser(oldGuestUserId);
1161         } catch (RemoteException e) {
1162             Log.e(TAG, "Couldn't remove guest because ActivityManager or WindowManager is dead");
1163             return;
1164         }
1165     }
1166 
1167     /**
1168      * Create a guest user in the background
1169      */
1170     @VisibleForTesting
1171     void scheduleGuestCreation() {
1172         // TODO(b/191067027): Move guest recreation to system_server
1173         if (mGuestCreationScheduled.compareAndSet(/* expect= */ false, /* update= */ true)) {
1174             // Once mGuestCreationScheduled=true, mAddGuest needs to be updated so that it shows
1175             // "Resetting guest..."
1176             mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
1177             mExecutor.execute(() -> {
1178                 UserInfo guest = mUserManager.createGuest(getContext());
1179                 mGuestCreationScheduled.set(false);
1180                 if (guest == null) {
1181                     Log.e(TAG, "Unable to automatically recreate guest user");
1182                 }
1183                 // The list needs to be updated whether or not guest creation worked. If guest
1184                 // creation failed, the list needs to update so that "Add guest" is displayed.
1185                 // Otherwise, the UX could be stuck in a state where there is no way to switch to
1186                 // the guest user (e.g. Guest would not be selectable, and it would be stuck
1187                 // saying "Resetting guest...")
1188                 mHandler.sendEmptyMessage(MESSAGE_UPDATE_LIST);
1189             });
1190         }
1191     }
1192 
1193     @VisibleForTesting
1194     void updateUserList() {
1195         final Context context = getActivity();
1196         if (context == null) {
1197             return;
1198         }
1199 
1200         List<UserInfo> users;
1201         if (mUserCaps.mUserSwitcherEnabled) {
1202             // Only users that can be switched to should show up here.
1203             // e.g. Managed profiles appear under Accounts Settings instead
1204             users = mUserManager.getAliveUsers().stream()
1205                     .filter(UserInfo::supportsSwitchToByUser)
1206                     .collect(Collectors.toList());
1207         } else {
1208             // Only current user will be displayed in case of multi-user switch is disabled
1209             users = List.of(mUserManager.getUserInfo(context.getUserId()));
1210         }
1211 
1212         final ArrayList<Integer> missingIcons = new ArrayList<>();
1213         final ArrayList<UserPreference> userPreferences = new ArrayList<>();
1214 
1215         // mMePreference shows a icon for current user. However when current user is a guest, we
1216         // don't show the guest user icon, instead we show two preferences for guest user to
1217         // exit and reset itself. Hence we don't add mMePreference, i.e. guest user to the
1218         // list of users visible in the UI.
1219         if (!isCurrentUserGuest()) {
1220             userPreferences.add(mMePreference);
1221         }
1222 
1223         boolean canOpenUserDetails =
1224                 isCurrentUserAdmin() || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser);
1225         for (UserInfo user : users) {
1226             if (user.isGuest()) {
1227                 // Guest user is added to guest category via updateGuestCategory
1228                 // and not to user list so skip guest here
1229                 continue;
1230             }
1231             UserPreference pref;
1232             if (user.id == UserHandle.myUserId()) {
1233                 pref = mMePreference;
1234             } else {
1235                 pref = new UserPreference(getPrefContext(), null, user.id);
1236                 pref.setTitle(user.name);
1237                 userPreferences.add(pref);
1238                 pref.setOnPreferenceClickListener(this);
1239                 pref.setEnabled(canOpenUserDetails);
1240                 pref.setSelectable(true);
1241                 pref.setKey("id=" + user.id);
1242             }
1243             if (pref == null) {
1244                 continue;
1245             }
1246             if (user.isMain()) {
1247                 pref.setSummary(R.string.user_owner);
1248             } else if (user.isAdmin()) {
1249                 pref.setSummary(R.string.user_admin);
1250             }
1251             if (user.id != UserHandle.myUserId() && !user.isGuest() && !user.isInitialized()) {
1252                 // sometimes after creating a guest the initialized flag isn't immediately set
1253                 // and we don't want to show "Not set up" summary for them
1254                 if (user.isRestricted()) {
1255                     pref.setSummary(R.string.user_summary_restricted_not_set_up);
1256                 } else {
1257                     pref.setSummary(R.string.user_summary_not_set_up);
1258                     // Disallow setting up user which results in user switching when the
1259                     // restriction is set.
1260                     pref.setEnabled(!mUserCaps.mDisallowSwitchUser && canSwitchUserNow());
1261                 }
1262             } else if (user.isRestricted()) {
1263                 pref.setSummary(R.string.user_summary_restricted_profile);
1264             }
1265             if (user.iconPath != null) {
1266                 if (mUserIcons.get(user.id) == null) {
1267                     // Icon not loaded yet, print a placeholder
1268                     missingIcons.add(user.id);
1269                     pref.setIcon(getEncircledDefaultIcon());
1270                 } else {
1271                     setPhotoId(pref, user);
1272                 }
1273             } else {
1274                 // Icon not available yet, print a placeholder
1275                 pref.setIcon(getEncircledDefaultIcon());
1276             }
1277         }
1278 
1279         // Add a temporary entry for the user being created
1280         if (mAddingUser) {
1281             UserPreference pref = new UserPreference(getPrefContext(), null,
1282                     UserPreference.USERID_UNKNOWN);
1283             pref.setEnabled(false);
1284             pref.setTitle(mAddingUserName);
1285             pref.setIcon(getEncircledDefaultIcon());
1286             userPreferences.add(pref);
1287         }
1288 
1289         // Sort list of users by serialNum
1290         Collections.sort(userPreferences, UserPreference.SERIAL_NUMBER_COMPARATOR);
1291 
1292         getActivity().invalidateOptionsMenu();
1293 
1294         // Load the icons
1295         if (missingIcons.size() > 0) {
1296             loadIconsAsync(missingIcons);
1297         }
1298 
1299         // If restricted profiles are supported, mUserListCategory will have a special title
1300         if (mUserCaps.mCanAddRestrictedProfile) {
1301             mUserListCategory.setTitle(R.string.user_list_title);
1302         } else if (isCurrentUserGuest()) {
1303             mUserListCategory.setTitle(R.string.other_user_category_title);
1304         } else {
1305             mUserListCategory.setTitle(R.string.user_category_title);
1306         }
1307 
1308         // Remove everything from mUserListCategory and add new users.
1309         mUserListCategory.removeAll();
1310 
1311         final Preference addUserOnLockScreen = getPreferenceScreen().findPreference(
1312                 mAddUserWhenLockedPreferenceController.getPreferenceKey());
1313         mAddUserWhenLockedPreferenceController.updateState(addUserOnLockScreen);
1314 
1315         final Preference guestCallPreference = getPreferenceScreen().findPreference(
1316                 mGuestTelephonyPreferenceController.getPreferenceKey());
1317         mGuestTelephonyPreferenceController.updateState(guestCallPreference);
1318 
1319         final Preference multiUserTopIntroPreference = getPreferenceScreen().findPreference(
1320                 mMultiUserTopIntroPreferenceController.getPreferenceKey());
1321         mMultiUserTopIntroPreferenceController.updateState(multiUserTopIntroPreference);
1322         updateGuestPreferences();
1323         updateGuestCategory(context, users);
1324         updateAddUser(context);
1325         updateAddSupervisedUser(context);
1326 
1327         for (UserPreference userPreference : userPreferences) {
1328             userPreference.setOrder(Preference.DEFAULT_ORDER);
1329             mUserListCategory.addPreference(userPreference);
1330         }
1331 
1332     }
1333 
1334     @VisibleForTesting
1335     void setConfigSupervisedUserCreationPackage() {
1336         mConfigSupervisedUserCreationPackage = getPrefContext().getString(
1337                 com.android.internal.R.string.config_supervisedUserCreationPackage);
1338     }
1339 
1340     private boolean isCurrentUserGuest() {
1341         return mUserCaps.mIsGuest;
1342     }
1343 
1344     private boolean isCurrentUserAdmin() {
1345         return mUserCaps.mIsAdmin;
1346     }
1347 
1348     private boolean canSwitchUserNow() {
1349         return mUserManager.getUserSwitchability() == UserManager.SWITCHABILITY_STATUS_OK;
1350     }
1351 
1352     private void updateGuestPreferences() {
1353         // reset guest and exit guest preferences are shown only in guest mode.
1354         // For all other users these are not visible.
1355         mGuestCategory.setVisible(false);
1356         mGuestResetPreference.setVisible(false);
1357         mGuestExitPreference.setVisible(false);
1358         if (!isCurrentUserGuest()) {
1359             return;
1360         }
1361         mGuestCategory.setVisible(true);
1362         mGuestExitPreference.setVisible(true);
1363         mGuestResetPreference.setVisible(true);
1364 
1365         boolean isGuestFirstLogin = Settings.Secure.getIntForUser(
1366                 getContext().getContentResolver(),
1367                 SETTING_GUEST_HAS_LOGGED_IN,
1368                 0,
1369                 UserHandle.myUserId()) <= 1;
1370         String guestExitSummary;
1371         if (mUserCaps.mIsEphemeral) {
1372             guestExitSummary = getContext().getString(
1373                     com.android.settingslib.R.string.guest_notification_ephemeral);
1374         } else if (isGuestFirstLogin) {
1375             guestExitSummary = getContext().getString(
1376                     com.android.settingslib.R.string.guest_notification_non_ephemeral);
1377         } else {
1378             guestExitSummary = getContext().getString(
1379                     com.android.settingslib.R
1380                             .string.guest_notification_non_ephemeral_non_first_login);
1381         }
1382         mGuestExitPreference.setSummary(guestExitSummary);
1383     }
1384 
1385     private void updateGuestCategory(Context context, List<UserInfo> users) {
1386         // show guest category title and related guest preferences
1387         // - if guest is created, then show guest user preference
1388         // - if guest is not created and its allowed to create guest,
1389         //   then show "add guest" preference
1390         // - if allowed, show "reset guest on exit" preference
1391         // - if there is nothing to show, then make the guest category as not visible
1392         // - guest category is not visible for guest user.
1393         UserPreference pref = null;
1394         boolean isGuestAlreadyCreated = false;
1395         boolean canOpenUserDetails =
1396                 isCurrentUserAdmin() || (canSwitchUserNow() && !mUserCaps.mDisallowSwitchUser);
1397 
1398         mGuestUserCategory.removeAll();
1399         mGuestUserCategory.setVisible(false);
1400         for (UserInfo user : users) {
1401             if (!user.isGuest() || !user.isEnabled()) {
1402                 // Only look at enabled, guest users
1403                 continue;
1404             }
1405             final Context prefContext = getPrefContext();
1406             pref = new UserPreference(prefContext, null, user.id);
1407             pref.setTitle(user.name);
1408             pref.setOnPreferenceClickListener(this);
1409             pref.setEnabled(canOpenUserDetails);
1410             pref.setSelectable(true);
1411             Drawable icon = getContext().getDrawable(
1412                     com.android.settingslib.R.drawable.ic_account_circle_outline);
1413             icon.setTint(
1414                     getColorAttrDefaultColor(getContext(), android.R.attr.colorControlNormal));
1415             pref.setIcon(encircleUserIcon(
1416                     UserIcons.convertToBitmapAtUserIconSize(
1417                             getContext().getResources(), icon)));
1418             pref.setKey(KEY_USER_GUEST);
1419             pref.setOrder(Preference.DEFAULT_ORDER);
1420             if (mUserCaps.mDisallowSwitchUser) {
1421                 pref.setDisabledByAdmin(
1422                         RestrictedLockUtilsInternal.getDeviceOwner(context));
1423             } else {
1424                 pref.setDisabledByAdmin(null);
1425             }
1426             if (mUserCaps.mUserSwitcherEnabled) {
1427                 mGuestUserCategory.addPreference(pref);
1428                 // guest user preference is shown hence also make guest category visible
1429                 mGuestUserCategory.setVisible(true);
1430             }
1431             isGuestAlreadyCreated = true;
1432         }
1433         boolean isVisible = updateAddGuestPreference(context, isGuestAlreadyCreated);
1434         if (isVisible) {
1435             // "add guest" preference is shown hence also make guest category visible
1436             mGuestUserCategory.setVisible(true);
1437         }
1438         final Preference removeGuestOnExit = getPreferenceScreen().findPreference(
1439                 mRemoveGuestOnExitPreferenceController.getPreferenceKey());
1440         mRemoveGuestOnExitPreferenceController.updateState(removeGuestOnExit);
1441         if (mRemoveGuestOnExitPreferenceController.isAvailable()) {
1442             // "reset guest on exit" preference is shown hence also make guest category visible
1443             mGuestUserCategory.setVisible(true);
1444         }
1445         if (isCurrentUserGuest()) {
1446             // guest category is not visible for guest user.
1447             mGuestUserCategory.setVisible(false);
1448         }
1449     }
1450 
1451     private boolean updateAddGuestPreference(Context context, boolean isGuestAlreadyCreated) {
1452         boolean isVisible = false;
1453         if (!isGuestAlreadyCreated && mUserCaps.mCanAddGuest
1454                 && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_GUEST)
1455                 && WizardManagerHelper.isDeviceProvisioned(context)
1456                 && mUserCaps.mUserSwitcherEnabled) {
1457             Drawable icon = context.getDrawable(
1458                     com.android.settingslib.R.drawable.ic_account_circle);
1459             mAddGuest.setIcon(centerAndTint(icon));
1460             isVisible = true;
1461             mAddGuest.setVisible(true);
1462             mAddGuest.setSelectable(true);
1463             if (mGuestUserAutoCreated && mGuestCreationScheduled.get()) {
1464                 mAddGuest.setTitle(com.android.internal.R.string.guest_name);
1465                 mAddGuest.setSummary(com.android.settingslib.R.string.guest_resetting);
1466                 mAddGuest.setEnabled(false);
1467             } else {
1468                 mAddGuest.setTitle(com.android.settingslib.R.string.guest_new_guest);
1469                 mAddGuest.setEnabled(canSwitchUserNow());
1470             }
1471         } else {
1472             mAddGuest.setVisible(false);
1473         }
1474         return isVisible;
1475     }
1476 
1477     private void updateAddUser(Context context) {
1478         updateAddUserCommon(context, mAddUser, mUserCaps.mCanAddRestrictedProfile);
1479         Drawable icon = context.getDrawable(
1480                 com.android.settingslib.R.drawable.ic_account_circle_filled);
1481         mAddUser.setIcon(centerAndTint(icon));
1482     }
1483 
1484     private void updateAddSupervisedUser(Context context) {
1485         if (!TextUtils.isEmpty(mConfigSupervisedUserCreationPackage)) {
1486             updateAddUserCommon(context, mAddSupervisedUser, false);
1487             Drawable icon = context.getDrawable(
1488                     com.android.settingslib.R.drawable.ic_add_supervised_user);
1489             mAddSupervisedUser.setIcon(centerAndTint(icon));
1490         } else {
1491             mAddSupervisedUser.setVisible(false);
1492         }
1493     }
1494 
1495     private void updateAddUserCommon(Context context, RestrictedPreference addUser,
1496             boolean canAddRestrictedProfile) {
1497         if ((mUserCaps.mCanAddUser && !mUserCaps.mDisallowAddUserSetByAdmin)
1498                 && WizardManagerHelper.isDeviceProvisioned(context)
1499                 && mUserCaps.mUserSwitcherEnabled) {
1500             addUser.setVisible(true);
1501             addUser.setSelectable(true);
1502             final boolean canAddMoreUsers =
1503                     mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_SECONDARY)
1504                             || (canAddRestrictedProfile
1505                             && mUserManager.canAddMoreUsers(UserManager.USER_TYPE_FULL_RESTRICTED));
1506             addUser.setEnabled(canAddMoreUsers && !mAddingUser && canSwitchUserNow());
1507 
1508             if (!canAddMoreUsers) {
1509                 addUser.setSummary(getString(R.string.user_add_max_count));
1510             } else {
1511                 addUser.setSummary(null);
1512             }
1513             if (addUser.isEnabled()) {
1514                 addUser.setDisabledByAdmin(
1515                         mUserCaps.mDisallowAddUser ? mUserCaps.mEnforcedAdmin : null);
1516             }
1517         } else {
1518             addUser.setVisible(false);
1519         }
1520     }
1521 
1522     private Drawable centerAndTint(Drawable icon) {
1523         icon.setTintBlendMode(BlendMode.SRC_IN);
1524         icon.setTint(getColorAttrDefaultColor(getContext(), android.R.attr.textColorPrimary));
1525 
1526         Drawable bg = getContext().getDrawable(com.android.settingslib.R.drawable.user_avatar_bg)
1527                 .mutate();
1528         LayerDrawable ld = new LayerDrawable(new Drawable[] {bg, icon});
1529         int size = getContext().getResources().getDimensionPixelSize(
1530                 R.dimen.multiple_users_avatar_size);
1531         int bgSize = getContext().getResources().getDimensionPixelSize(
1532                 R.dimen.multiple_users_user_icon_size);
1533         ld.setLayerSize(1, size, size);
1534         ld.setLayerSize(0, bgSize, bgSize);
1535         ld.setLayerGravity(1, Gravity.CENTER);
1536 
1537         return ld;
1538     }
1539 
1540     /**
1541      * @return number of non-guest non-managed users
1542      */
1543     @VisibleForTesting
1544     int getRealUsersCount() {
1545         return (int) mUserManager.getUsers()
1546                 .stream()
1547                 .filter(user -> !user.isGuest() && !user.isProfile())
1548                 .count();
1549     }
1550 
1551     private void loadIconsAsync(List<Integer> missingIcons) {
1552         new AsyncTask<List<Integer>, Void, Void>() {
1553             @Override
1554             protected void onPostExecute(Void result) {
1555                 updateUserList();
1556             }
1557 
1558             @Override
1559             protected Void doInBackground(List<Integer>... values) {
1560                 for (int userId : values[0]) {
1561                     Bitmap bitmap = mUserManager.getUserIcon(userId);
1562                     if (bitmap == null) {
1563                         bitmap = getDefaultUserIconAsBitmap(getContext().getResources(), userId);
1564                     }
1565                     mUserIcons.append(userId, bitmap);
1566                 }
1567                 return null;
1568             }
1569         }.execute(missingIcons);
1570     }
1571 
1572     private Drawable getEncircledDefaultIcon() {
1573         if (mDefaultIconDrawable == null) {
1574             mDefaultIconDrawable = encircleUserIcon(
1575                     getDefaultUserIconAsBitmap(getContext().getResources(), UserHandle.USER_NULL));
1576         }
1577         return mDefaultIconDrawable;
1578     }
1579 
1580     private void setPhotoId(Preference pref, UserInfo user) {
1581         Bitmap bitmap = mUserIcons.get(user.id);
1582         if (bitmap != null) {
1583             pref.setIcon(encircleUserIcon(bitmap));
1584         }
1585     }
1586 
1587     @Override
1588     public boolean onPreferenceClick(Preference pref) {
1589         mMetricsFeatureProvider.logSettingsTileClick(pref.getKey(), getMetricsCategory());
1590         if (isCurrentUserGuest()) {
1591             if (mGuestResetPreference != null && pref == mGuestResetPreference) {
1592                 showDialog(DIALOG_CONFIRM_RESET_AND_RESTART_GUEST);
1593                 return true;
1594             }
1595             if (mGuestExitPreference != null && pref == mGuestExitPreference) {
1596                 if (mUserCaps.mIsEphemeral) {
1597                     showDialog(DIALOG_CONFIRM_EXIT_GUEST_EPHEMERAL);
1598                 } else {
1599                     showDialog(DIALOG_CONFIRM_EXIT_GUEST_NON_EPHEMERAL);
1600                 }
1601                 return true;
1602             }
1603         }
1604         if (pref == mMePreference) {
1605             if (!isCurrentUserGuest()) {
1606                 showDialog(DIALOG_USER_PROFILE_EDITOR);
1607                 return true;
1608             }
1609         } else if (pref instanceof UserPreference) {
1610             UserInfo userInfo = mUserManager.getUserInfo(((UserPreference) pref).getUserId());
1611             openUserDetails(userInfo, false);
1612             return true;
1613         } else if (pref == mAddUser) {
1614             mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_ADD);
1615             // If we allow both types, show a picker, otherwise directly go to
1616             // flow for full user.
1617             if (mUserCaps.mCanAddRestrictedProfile) {
1618                 showDialog(DIALOG_CHOOSE_USER_TYPE);
1619             } else {
1620                 onAddUserClicked(USER_TYPE_USER);
1621             }
1622             return true;
1623         } else if (pref == mAddSupervisedUser) {
1624             mMetricsFeatureProvider.action(getActivity(), SettingsEnums.ACTION_USER_SUPERVISED_ADD);
1625             Trace.beginSection("UserSettings.addSupervisedUser");
1626             onAddSupervisedUserClicked();
1627             Trace.endSection();
1628             return true;
1629         } else if (pref == mAddGuest) {
1630             mAddGuest.setEnabled(false); // prevent multiple tap issue
1631             onAddGuestClicked();
1632             return true;
1633         }
1634         return false;
1635     }
1636 
1637     private Drawable encircleUserIcon(Bitmap icon) {
1638         return new CircleFramedDrawable(
1639                 icon,
1640                 getActivity().getResources().getDimensionPixelSize(
1641                         R.dimen.multiple_users_user_icon_size));
1642     }
1643 
1644     @Override
1645     public void onDismiss(DialogInterface dialog) {
1646         synchronized (mUserLock) {
1647             mRemovingUserId = -1;
1648             updateUserList();
1649             if (mCreateUserDialogController.isActive()) {
1650                 mCreateUserDialogController.finish();
1651             }
1652         }
1653     }
1654 
1655     @Override
1656     public int getHelpResource() {
1657         return R.string.help_url_users;
1658     }
1659 
1660     /**
1661      * Returns a default user icon (as a {@link Bitmap}) for the given user.
1662      *
1663      * Note that for guest users, you should pass in {@code UserHandle.USER_NULL}.
1664      *
1665      * @param resources resources object to fetch the user icon.
1666      * @param userId    the user id or {@code UserHandle.USER_NULL} for a non-user specific icon
1667      */
1668     private static Bitmap getDefaultUserIconAsBitmap(Resources resources, int userId) {
1669         Bitmap bitmap = null;
1670         // Try finding the corresponding bitmap in the dark bitmap cache
1671         bitmap = sDarkDefaultUserBitmapCache.get(userId);
1672         if (bitmap == null) {
1673             bitmap = UserIcons.convertToBitmapAtUserIconSize(resources,
1674                     UserIcons.getDefaultUserIcon(resources, userId, false));
1675             // Save it to cache
1676             sDarkDefaultUserBitmapCache.put(userId, bitmap);
1677         }
1678         return bitmap;
1679     }
1680 
1681     /**
1682      * Assign the default photo to user with {@paramref userId}
1683      *
1684      * @param context used to get the {@link UserManager}
1685      * @param userId  used to get the icon bitmap
1686      * @return true if assign photo successfully, false if failed
1687      */
1688     @VisibleForTesting
1689     static boolean assignDefaultPhoto(Context context, int userId) {
1690         if (context == null) {
1691             return false;
1692         }
1693         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
1694         Bitmap bitmap = getDefaultUserIconAsBitmap(context.getResources(), userId);
1695         um.setUserIcon(userId, bitmap);
1696 
1697         return true;
1698     }
1699 
1700     @WorkerThread
1701     static void copyMeProfilePhoto(Context context, UserInfo user) {
1702         Uri contactUri = ContactsContract.Profile.CONTENT_URI;
1703 
1704         int userId = user != null ? user.id : UserHandle.myUserId();
1705 
1706         InputStream avatarDataStream = ContactsContract.Contacts.openContactPhotoInputStream(
1707                 context.getContentResolver(),
1708                 contactUri, true);
1709         // If there's no profile photo, assign a default avatar
1710         if (avatarDataStream == null) {
1711             assignDefaultPhoto(context, userId);
1712             return;
1713         }
1714 
1715         UserManager um = (UserManager) context.getSystemService(Context.USER_SERVICE);
1716         Bitmap decodedIcon = BitmapFactory.decodeStream(avatarDataStream);
1717         CircleFramedDrawable drawable = CircleFramedDrawable.getInstance(context, decodedIcon);
1718         Bitmap icon = UserIcons.convertToBitmapAtUserIconSize(context.getResources(), drawable);
1719 
1720         um.setUserIcon(userId, icon);
1721         try {
1722             avatarDataStream.close();
1723         } catch (IOException ioe) {
1724         }
1725     }
1726 
1727     public static final BaseSearchIndexProvider SEARCH_INDEX_DATA_PROVIDER =
1728             new BaseSearchIndexProvider(R.xml.user_settings) {
1729 
1730                 @Override
1731                 protected boolean isPageSearchEnabled(Context context) {
1732                     final UserCapabilities userCaps = UserCapabilities.create(context);
1733                     return userCaps.mEnabled;
1734                 }
1735 
1736                 @Override
1737                 public List<SearchIndexableRaw> getRawDataToIndex(Context context,
1738                         boolean enabled) {
1739                     final List<SearchIndexableRaw> rawData = new ArrayList<>();
1740                     if (!UserManager.supportsMultipleUsers()) {
1741                         return rawData;
1742                     }
1743 
1744                     SearchIndexableRaw multipleUsersData = new SearchIndexableRaw(context);
1745                     multipleUsersData.key = KEY_USER_SETTINGS_SCREEN;
1746                     multipleUsersData.title =
1747                             context.getString(R.string.user_settings_title);
1748                     multipleUsersData.keywords =
1749                             context.getString(R.string.multiple_users_title_keywords);
1750                     multipleUsersData.screenTitle =
1751                             context.getString(R.string.user_settings_title);
1752                     rawData.add(multipleUsersData);
1753 
1754                     SearchIndexableRaw allowMultipleUsersResult = new SearchIndexableRaw(context);
1755 
1756                     allowMultipleUsersResult.key = KEY_ALLOW_MULTIPLE_USERS;
1757                     allowMultipleUsersResult.title =
1758                             context.getString(R.string.multiple_users_main_switch_title);
1759                     allowMultipleUsersResult.keywords =
1760                             context.getString(R.string.multiple_users_main_switch_keywords);
1761                     allowMultipleUsersResult.screenTitle =
1762                             context.getString(R.string.user_settings_title);
1763                     allowMultipleUsersResult.className =
1764                             MultiUserSwitchBarController.class.getName();
1765 
1766                     rawData.add(allowMultipleUsersResult);
1767                     return rawData;
1768                 }
1769 
1770                 @Override
1771                 public List<String> getNonIndexableKeysFromXml(Context context, int xmlResId,
1772                         boolean suppressAllPage) {
1773                     final List<String> niks = super.getNonIndexableKeysFromXml(context, xmlResId,
1774                             suppressAllPage);
1775                     AddUserWhenLockedPreferenceController controller =
1776                             new AddUserWhenLockedPreferenceController(
1777                                     context, KEY_ADD_USER_WHEN_LOCKED);
1778                     controller.updateNonIndexableKeys(niks);
1779                     new AutoSyncDataPreferenceController(context, null /* parent */)
1780                             .updateNonIndexableKeys(niks);
1781                     new AutoSyncPersonalDataPreferenceController(context, null /* parent */)
1782                             .updateNonIndexableKeys(niks);
1783                     new AutoSyncWorkDataPreferenceController(context, null /* parent */)
1784                             .updateNonIndexableKeys(niks);
1785                     if (suppressAllPage) {
1786                         niks.add(KEY_ALLOW_MULTIPLE_USERS);
1787                     }
1788                     return niks;
1789                 }
1790             };
1791 }
1792