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