1/*
2 * Copyright (C) 2014 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.systemui.statusbar.policy;
18
19import android.app.ActivityManager;
20import android.app.ActivityManagerNative;
21import android.app.Dialog;
22import android.app.Notification;
23import android.app.NotificationManager;
24import android.app.PendingIntent;
25import android.content.BroadcastReceiver;
26import android.content.Context;
27import android.content.DialogInterface;
28import android.content.Intent;
29import android.content.IntentFilter;
30import android.content.pm.UserInfo;
31import android.database.ContentObserver;
32import android.graphics.Bitmap;
33import android.graphics.drawable.Drawable;
34import android.os.AsyncTask;
35import android.os.Handler;
36import android.os.RemoteException;
37import android.os.UserHandle;
38import android.os.UserManager;
39import android.provider.Settings;
40import android.telephony.PhoneStateListener;
41import android.telephony.TelephonyManager;
42import android.util.Log;
43import android.util.SparseArray;
44import android.util.SparseBooleanArray;
45import android.view.View;
46import android.view.ViewGroup;
47import android.widget.BaseAdapter;
48
49import com.android.internal.logging.MetricsProto.MetricsEvent;
50import com.android.internal.util.UserIcons;
51import com.android.settingslib.RestrictedLockUtils;
52import com.android.systemui.GuestResumeSessionReceiver;
53import com.android.systemui.R;
54import com.android.systemui.SystemUI;
55import com.android.systemui.SystemUISecondaryUserService;
56import com.android.systemui.qs.QSTile;
57import com.android.systemui.qs.tiles.UserDetailView;
58import com.android.systemui.statusbar.phone.ActivityStarter;
59import com.android.systemui.statusbar.phone.SystemUIDialog;
60
61import java.io.FileDescriptor;
62import java.io.PrintWriter;
63import java.lang.ref.WeakReference;
64import java.util.ArrayList;
65import java.util.List;
66
67import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
68
69/**
70 * Keeps a list of all users on the device for user switching.
71 */
72public class UserSwitcherController {
73
74    private static final String TAG = "UserSwitcherController";
75    private static final boolean DEBUG = false;
76    private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING =
77            "lockscreenSimpleUserSwitcher";
78    private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST";
79    private static final String ACTION_LOGOUT_USER = "com.android.systemui.LOGOUT_USER";
80    private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000;
81
82    private static final int ID_REMOVE_GUEST = 1010;
83    private static final int ID_LOGOUT_USER = 1011;
84    private static final String TAG_REMOVE_GUEST = "remove_guest";
85    private static final String TAG_LOGOUT_USER = "logout_user";
86
87    private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF";
88
89    private final Context mContext;
90    private final UserManager mUserManager;
91    private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>();
92    private final GuestResumeSessionReceiver mGuestResumeSessionReceiver
93            = new GuestResumeSessionReceiver();
94    private final KeyguardMonitor mKeyguardMonitor;
95    private final Handler mHandler;
96    private final ActivityStarter mActivityStarter;
97
98    private ArrayList<UserRecord> mUsers = new ArrayList<>();
99    private Dialog mExitGuestDialog;
100    private Dialog mAddUserDialog;
101    private int mLastNonGuestUser = UserHandle.USER_SYSTEM;
102    private boolean mSimpleUserSwitcher;
103    private boolean mAddUsersWhenLocked;
104    private boolean mPauseRefreshUsers;
105    private int mSecondaryUser = UserHandle.USER_NULL;
106    private Intent mSecondaryUserServiceIntent;
107    private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2);
108
109    public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor,
110            Handler handler, ActivityStarter activityStarter) {
111        mContext = context;
112        mGuestResumeSessionReceiver.register(context);
113        mKeyguardMonitor = keyguardMonitor;
114        mHandler = handler;
115        mActivityStarter = activityStarter;
116        mUserManager = UserManager.get(context);
117        IntentFilter filter = new IntentFilter();
118        filter.addAction(Intent.ACTION_USER_ADDED);
119        filter.addAction(Intent.ACTION_USER_REMOVED);
120        filter.addAction(Intent.ACTION_USER_INFO_CHANGED);
121        filter.addAction(Intent.ACTION_USER_SWITCHED);
122        filter.addAction(Intent.ACTION_USER_STOPPED);
123        filter.addAction(Intent.ACTION_USER_UNLOCKED);
124        mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
125                null /* permission */, null /* scheduler */);
126
127        mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class);
128
129        filter = new IntentFilter();
130        filter.addAction(ACTION_REMOVE_GUEST);
131        filter.addAction(ACTION_LOGOUT_USER);
132        mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter,
133                PERMISSION_SELF, null /* scheduler */);
134
135        mContext.getContentResolver().registerContentObserver(
136                Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true,
137                mSettingsObserver);
138        mContext.getContentResolver().registerContentObserver(
139                Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true,
140                mSettingsObserver);
141        mContext.getContentResolver().registerContentObserver(
142                Settings.Global.getUriFor(
143                        Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED),
144                true, mSettingsObserver);
145        // Fetch initial values.
146        mSettingsObserver.onChange(false);
147
148        keyguardMonitor.addCallback(mCallback);
149        listenForCallState();
150
151        refreshUsers(UserHandle.USER_NULL);
152    }
153
154    /**
155     * Refreshes users from UserManager.
156     *
157     * The pictures are only loaded if they have not been loaded yet.
158     *
159     * @param forcePictureLoadForId forces the picture of the given user to be reloaded.
160     */
161    @SuppressWarnings("unchecked")
162    private void refreshUsers(int forcePictureLoadForId) {
163        if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")");
164        if (forcePictureLoadForId != UserHandle.USER_NULL) {
165            mForcePictureLoadForUserId.put(forcePictureLoadForId, true);
166        }
167
168        if (mPauseRefreshUsers) {
169            return;
170        }
171
172        boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL);
173        SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size());
174        final int N = mUsers.size();
175        for (int i = 0; i < N; i++) {
176            UserRecord r = mUsers.get(i);
177            if (r == null || r.picture == null || r.info == null || forceAllUsers
178                    || mForcePictureLoadForUserId.get(r.info.id)) {
179                continue;
180            }
181            bitmaps.put(r.info.id, r.picture);
182        }
183        mForcePictureLoadForUserId.clear();
184
185        final boolean addUsersWhenLocked = mAddUsersWhenLocked;
186        new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() {
187            @SuppressWarnings("unchecked")
188            @Override
189            protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) {
190                final SparseArray<Bitmap> bitmaps = params[0];
191                List<UserInfo> infos = mUserManager.getUsers(true);
192                if (infos == null) {
193                    return null;
194                }
195                ArrayList<UserRecord> records = new ArrayList<>(infos.size());
196                int currentId = ActivityManager.getCurrentUser();
197                boolean canSwitchUsers = mUserManager.canSwitchUsers();
198                UserInfo currentUserInfo = null;
199                UserRecord guestRecord = null;
200
201                for (UserInfo info : infos) {
202                    boolean isCurrent = currentId == info.id;
203                    if (isCurrent) {
204                        currentUserInfo = info;
205                    }
206                    boolean switchToEnabled = canSwitchUsers || isCurrent;
207                    if (info.isEnabled()) {
208                        if (info.isGuest()) {
209                            // Tapping guest icon triggers remove and a user switch therefore
210                            // the icon shouldn't be enabled even if the user is current
211                            guestRecord = new UserRecord(info, null /* picture */,
212                                    true /* isGuest */, isCurrent, false /* isAddUser */,
213                                    false /* isRestricted */, canSwitchUsers);
214                        } else if (info.supportsSwitchToByUser()) {
215                            Bitmap picture = bitmaps.get(info.id);
216                            if (picture == null) {
217                                picture = mUserManager.getUserIcon(info.id);
218
219                                if (picture != null) {
220                                    int avatarSize = mContext.getResources()
221                                            .getDimensionPixelSize(R.dimen.max_avatar_size);
222                                    picture = Bitmap.createScaledBitmap(
223                                            picture, avatarSize, avatarSize, true);
224                                }
225                            }
226                            int index = isCurrent ? 0 : records.size();
227                            records.add(index, new UserRecord(info, picture, false /* isGuest */,
228                                    isCurrent, false /* isAddUser */, false /* isRestricted */,
229                                    switchToEnabled));
230                        }
231                    }
232                }
233
234                boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction(
235                                UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM);
236                boolean currentUserCanCreateUsers = currentUserInfo != null
237                        && (currentUserInfo.isAdmin()
238                                || currentUserInfo.id == UserHandle.USER_SYSTEM)
239                        && systemCanCreateUsers;
240                boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked;
241                boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers)
242                        && guestRecord == null;
243                boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers)
244                        && mUserManager.canAddMoreUsers();
245                boolean createIsRestricted = !addUsersWhenLocked;
246
247                if (!mSimpleUserSwitcher) {
248                    if (guestRecord == null) {
249                        if (canCreateGuest) {
250                            guestRecord = new UserRecord(null /* info */, null /* picture */,
251                                    true /* isGuest */, false /* isCurrent */,
252                                    false /* isAddUser */, createIsRestricted, canSwitchUsers);
253                            checkIfAddUserDisallowedByAdminOnly(guestRecord);
254                            records.add(guestRecord);
255                        }
256                    } else {
257                        int index = guestRecord.isCurrent ? 0 : records.size();
258                        records.add(index, guestRecord);
259                    }
260                }
261
262                if (!mSimpleUserSwitcher && canCreateUser) {
263                    UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */,
264                            false /* isGuest */, false /* isCurrent */, true /* isAddUser */,
265                            createIsRestricted, canSwitchUsers);
266                    checkIfAddUserDisallowedByAdminOnly(addUserRecord);
267                    records.add(addUserRecord);
268                }
269
270                return records;
271            }
272
273            @Override
274            protected void onPostExecute(ArrayList<UserRecord> userRecords) {
275                if (userRecords != null) {
276                    mUsers = userRecords;
277                    notifyAdapters();
278                }
279            }
280        }.execute((SparseArray) bitmaps);
281    }
282
283    private void pauseRefreshUsers() {
284        if (!mPauseRefreshUsers) {
285            mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
286            mPauseRefreshUsers = true;
287        }
288    }
289
290    private void notifyAdapters() {
291        for (int i = mAdapters.size() - 1; i >= 0; i--) {
292            BaseUserAdapter adapter = mAdapters.get(i).get();
293            if (adapter != null) {
294                adapter.notifyDataSetChanged();
295            } else {
296                mAdapters.remove(i);
297            }
298        }
299    }
300
301    public boolean isSimpleUserSwitcher() {
302        return mSimpleUserSwitcher;
303    }
304
305    public boolean useFullscreenUserSwitcher() {
306        // Use adb to override:
307        // adb shell settings put system enable_fullscreen_user_switcher 0  # Turn it off.
308        // adb shell settings put system enable_fullscreen_user_switcher 1  # Turn it on.
309        // Restart SystemUI or adb reboot.
310        final int DEFAULT = -1;
311        final int overrideUseFullscreenUserSwitcher =
312                Settings.System.getInt(mContext.getContentResolver(),
313                        "enable_fullscreen_user_switcher", DEFAULT);
314        if (overrideUseFullscreenUserSwitcher != DEFAULT) {
315            return overrideUseFullscreenUserSwitcher != 0;
316        }
317        // Otherwise default to the build setting.
318        return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher);
319    }
320
321    public void logoutCurrentUser() {
322        int currentUser = ActivityManager.getCurrentUser();
323        if (currentUser != UserHandle.USER_SYSTEM) {
324            pauseRefreshUsers();
325            ActivityManager.logoutCurrentUser();
326        }
327    }
328
329    public void removeUserId(int userId) {
330        if (userId == UserHandle.USER_SYSTEM) {
331            Log.w(TAG, "User " + userId + " could not removed.");
332            return;
333        }
334        if (ActivityManager.getCurrentUser() == userId) {
335            switchToUserId(UserHandle.USER_SYSTEM);
336        }
337        if (mUserManager.removeUser(userId)) {
338            refreshUsers(UserHandle.USER_NULL);
339        }
340    }
341
342    public void switchTo(UserRecord record) {
343        int id;
344        if (record.isGuest && record.info == null) {
345            // No guest user. Create one.
346            UserInfo guest = mUserManager.createGuest(
347                    mContext, mContext.getString(R.string.guest_nickname));
348            if (guest == null) {
349                // Couldn't create guest, most likely because there already exists one, we just
350                // haven't reloaded the user list yet.
351                return;
352            }
353            id = guest.id;
354        } else if (record.isAddUser) {
355            showAddUserDialog();
356            return;
357        } else {
358            id = record.info.id;
359        }
360
361        if (ActivityManager.getCurrentUser() == id) {
362            if (record.isGuest) {
363                showExitGuestDialog(id);
364            }
365            return;
366        }
367
368        switchToUserId(id);
369    }
370
371    public void switchTo(int userId) {
372        final int count = mUsers.size();
373        for (int i = 0; i < count; ++i) {
374            UserRecord record = mUsers.get(i);
375            if (record.info != null && record.info.id == userId) {
376                switchTo(record);
377                return;
378            }
379        }
380
381        Log.e(TAG, "Couldn't switch to user, id=" + userId);
382    }
383
384    private void switchToUserId(int id) {
385        try {
386            pauseRefreshUsers();
387            ActivityManagerNative.getDefault().switchUser(id);
388        } catch (RemoteException e) {
389            Log.e(TAG, "Couldn't switch user.", e);
390        }
391    }
392
393    private void showExitGuestDialog(int id) {
394        if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
395            mExitGuestDialog.cancel();
396        }
397        mExitGuestDialog = new ExitGuestDialog(mContext, id);
398        mExitGuestDialog.show();
399    }
400
401    private void showAddUserDialog() {
402        if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
403            mAddUserDialog.cancel();
404        }
405        mAddUserDialog = new AddUserDialog(mContext);
406        mAddUserDialog.show();
407    }
408
409    private void exitGuest(int id) {
410        int newId = UserHandle.USER_SYSTEM;
411        if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
412            UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
413            if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
414                newId = info.id;
415            }
416        }
417        switchToUserId(newId);
418        mUserManager.removeUser(id);
419    }
420
421    private void listenForCallState() {
422        TelephonyManager.from(mContext).listen(new PhoneStateListener() {
423            private int mCallState;
424            @Override
425            public void onCallStateChanged(int state, String incomingNumber) {
426                if (mCallState == state) return;
427                if (DEBUG) Log.v(TAG, "Call state changed: " + state);
428                mCallState = state;
429                int currentUserId = ActivityManager.getCurrentUser();
430                UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
431                if (userInfo != null && userInfo.isGuest()) {
432                    showGuestNotification(currentUserId);
433                }
434                refreshUsers(UserHandle.USER_NULL);
435            }
436        }, PhoneStateListener.LISTEN_CALL_STATE);
437    }
438
439    private BroadcastReceiver mReceiver = new BroadcastReceiver() {
440        @Override
441        public void onReceive(Context context, Intent intent) {
442            if (DEBUG) {
443                Log.v(TAG, "Broadcast: a=" + intent.getAction()
444                       + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
445            }
446
447            boolean unpauseRefreshUsers = false;
448            int forcePictureLoadForId = UserHandle.USER_NULL;
449
450            if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
451                int currentUser = ActivityManager.getCurrentUser();
452                UserInfo userInfo = mUserManager.getUserInfo(currentUser);
453                if (userInfo != null && userInfo.isGuest()) {
454                    showExitGuestDialog(currentUser);
455                }
456                return;
457            } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) {
458                logoutCurrentUser();
459            } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
460                if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
461                    mExitGuestDialog.cancel();
462                    mExitGuestDialog = null;
463                }
464
465                final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
466                final UserInfo userInfo = mUserManager.getUserInfo(currentId);
467                final int N = mUsers.size();
468                for (int i = 0; i < N; i++) {
469                    UserRecord record = mUsers.get(i);
470                    if (record.info == null) continue;
471                    boolean shouldBeCurrent = record.info.id == currentId;
472                    if (record.isCurrent != shouldBeCurrent) {
473                        mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
474                    }
475                    if (shouldBeCurrent && !record.isGuest) {
476                        mLastNonGuestUser = record.info.id;
477                    }
478                    if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) {
479                        // Immediately remove restricted records in case the AsyncTask is too slow.
480                        mUsers.remove(i);
481                        i--;
482                    }
483                }
484                notifyAdapters();
485
486                // Disconnect from the old secondary user's service
487                if (mSecondaryUser != UserHandle.USER_NULL) {
488                    context.stopServiceAsUser(mSecondaryUserServiceIntent,
489                            UserHandle.of(mSecondaryUser));
490                    mSecondaryUser = UserHandle.USER_NULL;
491                }
492                // Connect to the new secondary user's service (purely to ensure that a persistent
493                // SystemUI application is created for that user)
494                if (userInfo != null && !userInfo.isPrimary()) {
495                    context.startServiceAsUser(mSecondaryUserServiceIntent,
496                            UserHandle.of(userInfo.id));
497                    mSecondaryUser = userInfo.id;
498                }
499
500                if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()
501                        && userInfo.id != UserHandle.USER_SYSTEM) {
502                    showLogoutNotification(currentId);
503                }
504                if (userInfo != null && userInfo.isGuest()) {
505                    showGuestNotification(currentId);
506                }
507                unpauseRefreshUsers = true;
508            } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
509                forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
510                        UserHandle.USER_NULL);
511            } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
512                // Unlocking the system user may require a refresh
513                int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
514                if (userId != UserHandle.USER_SYSTEM) {
515                    return;
516                }
517            }
518            refreshUsers(forcePictureLoadForId);
519            if (unpauseRefreshUsers) {
520                mUnpauseRefreshUsers.run();
521            }
522        }
523
524        private void showLogoutNotification(int userId) {
525            PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext,
526                    0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
527            Notification.Builder builder = new Notification.Builder(mContext)
528                    .setVisibility(Notification.VISIBILITY_SECRET)
529                    .setPriority(Notification.PRIORITY_MIN)
530                    .setSmallIcon(R.drawable.ic_person)
531                    .setContentTitle(mContext.getString(R.string.user_logout_notification_title))
532                    .setContentText(mContext.getString(R.string.user_logout_notification_text))
533                    .setContentIntent(logoutPI)
534                    .setOngoing(true)
535                    .setShowWhen(false)
536                    .addAction(R.drawable.ic_delete,
537                            mContext.getString(R.string.user_logout_notification_action),
538                            logoutPI);
539            SystemUI.overrideNotificationAppName(mContext, builder);
540            NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER, ID_LOGOUT_USER,
541                    builder.build(), new UserHandle(userId));
542        }
543    };
544
545    private void showGuestNotification(int guestUserId) {
546        boolean canSwitchUsers = mUserManager.canSwitchUsers();
547        // Disable 'Remove guest' action if cannot switch users right now
548        PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext,
549                0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null;
550
551        Notification.Builder builder = new Notification.Builder(mContext)
552                .setVisibility(Notification.VISIBILITY_SECRET)
553                .setPriority(Notification.PRIORITY_MIN)
554                .setSmallIcon(R.drawable.ic_person)
555                .setContentTitle(mContext.getString(R.string.guest_notification_title))
556                .setContentText(mContext.getString(R.string.guest_notification_text))
557                .setContentIntent(removeGuestPI)
558                .setShowWhen(false)
559                .addAction(R.drawable.ic_delete,
560                        mContext.getString(R.string.guest_notification_remove_action),
561                        removeGuestPI);
562        SystemUI.overrideNotificationAppName(mContext, builder);
563        NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST,
564                builder.build(), new UserHandle(guestUserId));
565    }
566
567    private final Runnable mUnpauseRefreshUsers = new Runnable() {
568        @Override
569        public void run() {
570            mHandler.removeCallbacks(this);
571            mPauseRefreshUsers = false;
572            refreshUsers(UserHandle.USER_NULL);
573        }
574    };
575
576    private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
577        public void onChange(boolean selfChange) {
578            mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
579                    SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
580            mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
581                    Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
582            refreshUsers(UserHandle.USER_NULL);
583        };
584    };
585
586    public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
587        pw.println("UserSwitcherController state:");
588        pw.println("  mLastNonGuestUser=" + mLastNonGuestUser);
589        pw.print("  mUsers.size="); pw.println(mUsers.size());
590        for (int i = 0; i < mUsers.size(); i++) {
591            final UserRecord u = mUsers.get(i);
592            pw.print("    "); pw.println(u.toString());
593        }
594    }
595
596    public String getCurrentUserName(Context context) {
597        if (mUsers.isEmpty()) return null;
598        UserRecord item = mUsers.get(0);
599        if (item == null || item.info == null) return null;
600        if (item.isGuest) return context.getString(R.string.guest_nickname);
601        return item.info.name;
602    }
603
604    public void onDensityOrFontScaleChanged() {
605        refreshUsers(UserHandle.USER_ALL);
606    }
607
608    public static abstract class BaseUserAdapter extends BaseAdapter {
609
610        final UserSwitcherController mController;
611
612        protected BaseUserAdapter(UserSwitcherController controller) {
613            mController = controller;
614            controller.mAdapters.add(new WeakReference<>(this));
615        }
616
617        @Override
618        public int getCount() {
619            boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing()
620                    && mController.mKeyguardMonitor.isSecure()
621                    && !mController.mKeyguardMonitor.canSkipBouncer();
622            if (!secureKeyguardShowing) {
623                return mController.mUsers.size();
624            }
625            // The lock screen is secure and showing. Filter out restricted records.
626            final int N = mController.mUsers.size();
627            int count = 0;
628            for (int i = 0; i < N; i++) {
629                if (mController.mUsers.get(i).isRestricted) {
630                    break;
631                } else {
632                    count++;
633                }
634            }
635            return count;
636        }
637
638        @Override
639        public UserRecord getItem(int position) {
640            return mController.mUsers.get(position);
641        }
642
643        @Override
644        public long getItemId(int position) {
645            return position;
646        }
647
648        public void switchTo(UserRecord record) {
649            mController.switchTo(record);
650        }
651
652        public String getName(Context context, UserRecord item) {
653            if (item.isGuest) {
654                if (item.isCurrent) {
655                    return context.getString(R.string.guest_exit_guest);
656                } else {
657                    return context.getString(
658                            item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
659                }
660            } else if (item.isAddUser) {
661                return context.getString(R.string.user_add_user);
662            } else {
663                return item.info.name;
664            }
665        }
666
667        public Drawable getDrawable(Context context, UserRecord item) {
668            if (item.isAddUser) {
669                return context.getDrawable(R.drawable.ic_add_circle_qs);
670            }
671            return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
672        }
673
674        public void refresh() {
675            mController.refreshUsers(UserHandle.USER_NULL);
676        }
677    }
678
679    private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
680        EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
681                UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
682        if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext,
683                UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
684            record.isDisabledByAdmin = true;
685            record.enforcedAdmin = admin;
686        } else {
687            record.isDisabledByAdmin = false;
688            record.enforcedAdmin = null;
689        }
690    }
691
692    public void startActivity(Intent intent) {
693        mActivityStarter.startActivity(intent, true);
694    }
695
696    public static final class UserRecord {
697        public final UserInfo info;
698        public final Bitmap picture;
699        public final boolean isGuest;
700        public final boolean isCurrent;
701        public final boolean isAddUser;
702        /** If true, the record is only visible to the owner and only when unlocked. */
703        public final boolean isRestricted;
704        public boolean isDisabledByAdmin;
705        public EnforcedAdmin enforcedAdmin;
706        public boolean isSwitchToEnabled;
707
708        public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
709                boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) {
710            this.info = info;
711            this.picture = picture;
712            this.isGuest = isGuest;
713            this.isCurrent = isCurrent;
714            this.isAddUser = isAddUser;
715            this.isRestricted = isRestricted;
716            this.isSwitchToEnabled = isSwitchToEnabled;
717        }
718
719        public UserRecord copyWithIsCurrent(boolean _isCurrent) {
720            return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
721                    isSwitchToEnabled);
722        }
723
724        public int resolveId() {
725            if (isGuest || info == null) {
726                return UserHandle.USER_NULL;
727            }
728            return info.id;
729        }
730
731        public String toString() {
732            StringBuilder sb = new StringBuilder();
733            sb.append("UserRecord(");
734            if (info != null) {
735                sb.append("name=\"").append(info.name).append("\" id=").append(info.id);
736            } else {
737                if (isGuest) {
738                    sb.append("<add guest placeholder>");
739                } else if (isAddUser) {
740                    sb.append("<add user placeholder>");
741                }
742            }
743            if (isGuest) sb.append(" <isGuest>");
744            if (isAddUser) sb.append(" <isAddUser>");
745            if (isCurrent) sb.append(" <isCurrent>");
746            if (picture != null) sb.append(" <hasPicture>");
747            if (isRestricted) sb.append(" <isRestricted>");
748            if (isDisabledByAdmin) {
749                sb.append(" <isDisabledByAdmin>");
750                sb.append(" enforcedAdmin=").append(enforcedAdmin);
751            }
752            if (isSwitchToEnabled) {
753                sb.append(" <isSwitchToEnabled>");
754            }
755            sb.append(')');
756            return sb.toString();
757        }
758    }
759
760    public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() {
761        private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
762
763        @Override
764        public CharSequence getTitle() {
765            return mContext.getString(R.string.quick_settings_user_title);
766        }
767
768        @Override
769        public View createDetailView(Context context, View convertView, ViewGroup parent) {
770            UserDetailView v;
771            if (!(convertView instanceof UserDetailView)) {
772                v = UserDetailView.inflate(context, parent, false);
773                v.createAndSetAdapter(UserSwitcherController.this);
774            } else {
775                v = (UserDetailView) convertView;
776            }
777            v.refreshAdapter();
778            return v;
779        }
780
781        @Override
782        public Intent getSettingsIntent() {
783            return USER_SETTINGS_INTENT;
784        }
785
786        @Override
787        public Boolean getToggleState() {
788            return null;
789        }
790
791        @Override
792        public void setToggleState(boolean state) {
793        }
794
795        @Override
796        public int getMetricsCategory() {
797            return MetricsEvent.QS_USERDETAIL;
798        }
799    };
800
801    private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
802        @Override
803        public void onKeyguardChanged() {
804            notifyAdapters();
805        }
806    };
807
808    private final class ExitGuestDialog extends SystemUIDialog implements
809            DialogInterface.OnClickListener {
810
811        private final int mGuestId;
812
813        public ExitGuestDialog(Context context, int guestId) {
814            super(context);
815            setTitle(R.string.guest_exit_guest_dialog_title);
816            setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
817            setButton(DialogInterface.BUTTON_NEGATIVE,
818                    context.getString(android.R.string.cancel), this);
819            setButton(DialogInterface.BUTTON_POSITIVE,
820                    context.getString(R.string.guest_exit_guest_dialog_remove), this);
821            setCanceledOnTouchOutside(false);
822            mGuestId = guestId;
823        }
824
825        @Override
826        public void onClick(DialogInterface dialog, int which) {
827            if (which == BUTTON_NEGATIVE) {
828                cancel();
829            } else {
830                dismiss();
831                exitGuest(mGuestId);
832            }
833        }
834    }
835
836    private final class AddUserDialog extends SystemUIDialog implements
837            DialogInterface.OnClickListener {
838
839        public AddUserDialog(Context context) {
840            super(context);
841            setTitle(R.string.user_add_user_title);
842            setMessage(context.getString(R.string.user_add_user_message_short));
843            setButton(DialogInterface.BUTTON_NEGATIVE,
844                    context.getString(android.R.string.cancel), this);
845            setButton(DialogInterface.BUTTON_POSITIVE,
846                    context.getString(android.R.string.ok), this);
847        }
848
849        @Override
850        public void onClick(DialogInterface dialog, int which) {
851            if (which == BUTTON_NEGATIVE) {
852                cancel();
853            } else {
854                dismiss();
855                if (ActivityManager.isUserAMonkey()) {
856                    return;
857                }
858                UserInfo user = mUserManager.createUser(
859                        mContext.getString(R.string.user_new_user_name), 0 /* flags */);
860                if (user == null) {
861                    // Couldn't create user, most likely because there are too many, but we haven't
862                    // been able to reload the list yet.
863                    return;
864                }
865                int id = user.id;
866                Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
867                        id, /* light= */ false));
868                mUserManager.setUserIcon(id, icon);
869                switchToUserId(id);
870            }
871        }
872    }
873}
874