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