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