Home | History | Annotate | Download | only in policy
      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 
     17 package com.android.systemui.statusbar.policy;
     18 
     19 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
     20 
     21 import android.R.attr;
     22 import android.app.ActivityManager;
     23 import android.app.Dialog;
     24 import android.app.Notification;
     25 import android.app.NotificationManager;
     26 import android.app.PendingIntent;
     27 import android.content.BroadcastReceiver;
     28 import android.content.Context;
     29 import android.content.DialogInterface;
     30 import android.content.Intent;
     31 import android.content.IntentFilter;
     32 import android.content.pm.UserInfo;
     33 import android.database.ContentObserver;
     34 import android.graphics.Bitmap;
     35 import android.graphics.PorterDuff.Mode;
     36 import android.graphics.drawable.Drawable;
     37 import android.os.AsyncTask;
     38 import android.os.Handler;
     39 import android.os.RemoteException;
     40 import android.os.UserHandle;
     41 import android.os.UserManager;
     42 import android.provider.Settings;
     43 import android.telephony.PhoneStateListener;
     44 import android.telephony.TelephonyManager;
     45 import android.util.Log;
     46 import android.util.SparseArray;
     47 import android.util.SparseBooleanArray;
     48 import android.view.View;
     49 import android.view.ViewGroup;
     50 import android.widget.BaseAdapter;
     51 
     52 import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
     53 import com.android.internal.annotations.VisibleForTesting;
     54 import com.android.internal.messages.nano.SystemMessageProto.SystemMessage;
     55 import com.android.internal.util.UserIcons;
     56 import com.android.settingslib.RestrictedLockUtils;
     57 import com.android.settingslib.Utils;
     58 import com.android.systemui.Dependency;
     59 import com.android.systemui.GuestResumeSessionReceiver;
     60 import com.android.systemui.Prefs;
     61 import com.android.systemui.Prefs.Key;
     62 import com.android.systemui.R;
     63 import com.android.systemui.SystemUI;
     64 import com.android.systemui.SystemUISecondaryUserService;
     65 import com.android.systemui.plugins.qs.DetailAdapter;
     66 import com.android.systemui.qs.tiles.UserDetailView;
     67 import com.android.systemui.plugins.ActivityStarter;
     68 import com.android.systemui.statusbar.phone.SystemUIDialog;
     69 import com.android.systemui.util.NotificationChannels;
     70 
     71 import java.io.FileDescriptor;
     72 import java.io.PrintWriter;
     73 import java.lang.ref.WeakReference;
     74 import java.util.ArrayList;
     75 import java.util.List;
     76 
     77 /**
     78  * Keeps a list of all users on the device for user switching.
     79  */
     80 public 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