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