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