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