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