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