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     public int getSwitchableUserCount() {
    385         int count = 0;
    386         final int N = mUsers.size();
    387         for (int i = 0; i < N; ++i) {
    388             UserRecord record = mUsers.get(i);
    389             if (record.info != null && record.info.supportsSwitchTo()) {
    390                 count++;
    391             }
    392         }
    393         return count;
    394     }
    395 
    396     private void switchToUserId(int id) {
    397         try {
    398             pauseRefreshUsers();
    399             ActivityManagerNative.getDefault().switchUser(id);
    400         } catch (RemoteException e) {
    401             Log.e(TAG, "Couldn't switch user.", e);
    402         }
    403     }
    404 
    405     private void showExitGuestDialog(int id) {
    406         if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
    407             mExitGuestDialog.cancel();
    408         }
    409         mExitGuestDialog = new ExitGuestDialog(mContext, id);
    410         mExitGuestDialog.show();
    411     }
    412 
    413     private void showAddUserDialog() {
    414         if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
    415             mAddUserDialog.cancel();
    416         }
    417         mAddUserDialog = new AddUserDialog(mContext);
    418         mAddUserDialog.show();
    419     }
    420 
    421     private void exitGuest(int id) {
    422         int newId = UserHandle.USER_SYSTEM;
    423         if (mLastNonGuestUser != UserHandle.USER_SYSTEM) {
    424             UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser);
    425             if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) {
    426                 newId = info.id;
    427             }
    428         }
    429         switchToUserId(newId);
    430         mUserManager.removeUser(id);
    431     }
    432 
    433     private void listenForCallState() {
    434         TelephonyManager.from(mContext).listen(mPhoneStateListener,
    435                 PhoneStateListener.LISTEN_CALL_STATE);
    436     }
    437 
    438     private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() {
    439         private int mCallState;
    440 
    441         @Override
    442         public void onCallStateChanged(int state, String incomingNumber) {
    443             if (mCallState == state) return;
    444             if (DEBUG) Log.v(TAG, "Call state changed: " + state);
    445             mCallState = state;
    446             int currentUserId = ActivityManager.getCurrentUser();
    447             UserInfo userInfo = mUserManager.getUserInfo(currentUserId);
    448             if (userInfo != null && userInfo.isGuest()) {
    449                 showGuestNotification(currentUserId);
    450             }
    451             refreshUsers(UserHandle.USER_NULL);
    452         }
    453     };
    454 
    455     private BroadcastReceiver mReceiver = new BroadcastReceiver() {
    456         @Override
    457         public void onReceive(Context context, Intent intent) {
    458             if (DEBUG) {
    459                 Log.v(TAG, "Broadcast: a=" + intent.getAction()
    460                        + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1));
    461             }
    462 
    463             boolean unpauseRefreshUsers = false;
    464             int forcePictureLoadForId = UserHandle.USER_NULL;
    465 
    466             if (ACTION_REMOVE_GUEST.equals(intent.getAction())) {
    467                 int currentUser = ActivityManager.getCurrentUser();
    468                 UserInfo userInfo = mUserManager.getUserInfo(currentUser);
    469                 if (userInfo != null && userInfo.isGuest()) {
    470                     showExitGuestDialog(currentUser);
    471                 }
    472                 return;
    473             } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) {
    474                 logoutCurrentUser();
    475             } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) {
    476                 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) {
    477                     mExitGuestDialog.cancel();
    478                     mExitGuestDialog = null;
    479                 }
    480 
    481                 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1);
    482                 final UserInfo userInfo = mUserManager.getUserInfo(currentId);
    483                 final int N = mUsers.size();
    484                 for (int i = 0; i < N; i++) {
    485                     UserRecord record = mUsers.get(i);
    486                     if (record.info == null) continue;
    487                     boolean shouldBeCurrent = record.info.id == currentId;
    488                     if (record.isCurrent != shouldBeCurrent) {
    489                         mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent));
    490                     }
    491                     if (shouldBeCurrent && !record.isGuest) {
    492                         mLastNonGuestUser = record.info.id;
    493                     }
    494                     if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) {
    495                         // Immediately remove restricted records in case the AsyncTask is too slow.
    496                         mUsers.remove(i);
    497                         i--;
    498                     }
    499                 }
    500                 notifyAdapters();
    501 
    502                 // Disconnect from the old secondary user's service
    503                 if (mSecondaryUser != UserHandle.USER_NULL) {
    504                     context.stopServiceAsUser(mSecondaryUserServiceIntent,
    505                             UserHandle.of(mSecondaryUser));
    506                     mSecondaryUser = UserHandle.USER_NULL;
    507                 }
    508                 // Connect to the new secondary user's service (purely to ensure that a persistent
    509                 // SystemUI application is created for that user)
    510                 if (userInfo != null && !userInfo.isPrimary()) {
    511                     context.startServiceAsUser(mSecondaryUserServiceIntent,
    512                             UserHandle.of(userInfo.id));
    513                     mSecondaryUser = userInfo.id;
    514                 }
    515 
    516                 if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest()
    517                         && userInfo.id != UserHandle.USER_SYSTEM) {
    518                     showLogoutNotification(currentId);
    519                 }
    520                 if (userInfo != null && userInfo.isGuest()) {
    521                     showGuestNotification(currentId);
    522                 }
    523                 unpauseRefreshUsers = true;
    524             } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) {
    525                 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE,
    526                         UserHandle.USER_NULL);
    527             } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) {
    528                 // Unlocking the system user may require a refresh
    529                 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL);
    530                 if (userId != UserHandle.USER_SYSTEM) {
    531                     return;
    532                 }
    533             }
    534             refreshUsers(forcePictureLoadForId);
    535             if (unpauseRefreshUsers) {
    536                 mUnpauseRefreshUsers.run();
    537             }
    538         }
    539 
    540         private void showLogoutNotification(int userId) {
    541             PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext,
    542                     0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM);
    543             Notification.Builder builder = new Notification.Builder(mContext)
    544                     .setVisibility(Notification.VISIBILITY_SECRET)
    545                     .setPriority(Notification.PRIORITY_MIN)
    546                     .setSmallIcon(R.drawable.ic_person)
    547                     .setContentTitle(mContext.getString(R.string.user_logout_notification_title))
    548                     .setContentText(mContext.getString(R.string.user_logout_notification_text))
    549                     .setContentIntent(logoutPI)
    550                     .setOngoing(true)
    551                     .setShowWhen(false)
    552                     .addAction(R.drawable.ic_delete,
    553                             mContext.getString(R.string.user_logout_notification_action),
    554                             logoutPI);
    555             SystemUI.overrideNotificationAppName(mContext, builder);
    556             NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER, ID_LOGOUT_USER,
    557                     builder.build(), new UserHandle(userId));
    558         }
    559     };
    560 
    561     private void showGuestNotification(int guestUserId) {
    562         boolean canSwitchUsers = mUserManager.canSwitchUsers();
    563         // Disable 'Remove guest' action if cannot switch users right now
    564         PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext,
    565                 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null;
    566 
    567         Notification.Builder builder = new Notification.Builder(mContext)
    568                 .setVisibility(Notification.VISIBILITY_SECRET)
    569                 .setPriority(Notification.PRIORITY_MIN)
    570                 .setSmallIcon(R.drawable.ic_person)
    571                 .setContentTitle(mContext.getString(R.string.guest_notification_title))
    572                 .setContentText(mContext.getString(R.string.guest_notification_text))
    573                 .setContentIntent(removeGuestPI)
    574                 .setShowWhen(false)
    575                 .addAction(R.drawable.ic_delete,
    576                         mContext.getString(R.string.guest_notification_remove_action),
    577                         removeGuestPI);
    578         SystemUI.overrideNotificationAppName(mContext, builder);
    579         NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST,
    580                 builder.build(), new UserHandle(guestUserId));
    581     }
    582 
    583     private final Runnable mUnpauseRefreshUsers = new Runnable() {
    584         @Override
    585         public void run() {
    586             mHandler.removeCallbacks(this);
    587             mPauseRefreshUsers = false;
    588             refreshUsers(UserHandle.USER_NULL);
    589         }
    590     };
    591 
    592     private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) {
    593         public void onChange(boolean selfChange) {
    594             mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(),
    595                     SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0;
    596             mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(),
    597                     Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0;
    598             refreshUsers(UserHandle.USER_NULL);
    599         };
    600     };
    601 
    602     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
    603         pw.println("UserSwitcherController state:");
    604         pw.println("  mLastNonGuestUser=" + mLastNonGuestUser);
    605         pw.print("  mUsers.size="); pw.println(mUsers.size());
    606         for (int i = 0; i < mUsers.size(); i++) {
    607             final UserRecord u = mUsers.get(i);
    608             pw.print("    "); pw.println(u.toString());
    609         }
    610     }
    611 
    612     public String getCurrentUserName(Context context) {
    613         if (mUsers.isEmpty()) return null;
    614         UserRecord item = mUsers.get(0);
    615         if (item == null || item.info == null) return null;
    616         if (item.isGuest) return context.getString(R.string.guest_nickname);
    617         return item.info.name;
    618     }
    619 
    620     public void onDensityOrFontScaleChanged() {
    621         refreshUsers(UserHandle.USER_ALL);
    622     }
    623 
    624     public static abstract class BaseUserAdapter extends BaseAdapter {
    625 
    626         final UserSwitcherController mController;
    627 
    628         protected BaseUserAdapter(UserSwitcherController controller) {
    629             mController = controller;
    630             controller.mAdapters.add(new WeakReference<>(this));
    631         }
    632 
    633         @Override
    634         public int getCount() {
    635             boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing()
    636                     && mController.mKeyguardMonitor.isSecure()
    637                     && !mController.mKeyguardMonitor.canSkipBouncer();
    638             if (!secureKeyguardShowing) {
    639                 return mController.mUsers.size();
    640             }
    641             // The lock screen is secure and showing. Filter out restricted records.
    642             final int N = mController.mUsers.size();
    643             int count = 0;
    644             for (int i = 0; i < N; i++) {
    645                 if (mController.mUsers.get(i).isRestricted) {
    646                     break;
    647                 } else {
    648                     count++;
    649                 }
    650             }
    651             return count;
    652         }
    653 
    654         @Override
    655         public UserRecord getItem(int position) {
    656             return mController.mUsers.get(position);
    657         }
    658 
    659         @Override
    660         public long getItemId(int position) {
    661             return position;
    662         }
    663 
    664         public void switchTo(UserRecord record) {
    665             mController.switchTo(record);
    666         }
    667 
    668         public String getName(Context context, UserRecord item) {
    669             if (item.isGuest) {
    670                 if (item.isCurrent) {
    671                     return context.getString(R.string.guest_exit_guest);
    672                 } else {
    673                     return context.getString(
    674                             item.info == null ? R.string.guest_new_guest : R.string.guest_nickname);
    675                 }
    676             } else if (item.isAddUser) {
    677                 return context.getString(R.string.user_add_user);
    678             } else {
    679                 return item.info.name;
    680             }
    681         }
    682 
    683         public Drawable getDrawable(Context context, UserRecord item) {
    684             if (item.isAddUser) {
    685                 return context.getDrawable(R.drawable.ic_add_circle_qs);
    686             }
    687             return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true);
    688         }
    689 
    690         public void refresh() {
    691             mController.refreshUsers(UserHandle.USER_NULL);
    692         }
    693     }
    694 
    695     private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) {
    696         EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext,
    697                 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser());
    698         if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext,
    699                 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) {
    700             record.isDisabledByAdmin = true;
    701             record.enforcedAdmin = admin;
    702         } else {
    703             record.isDisabledByAdmin = false;
    704             record.enforcedAdmin = null;
    705         }
    706     }
    707 
    708     public void startActivity(Intent intent) {
    709         mActivityStarter.startActivity(intent, true);
    710     }
    711 
    712     public static final class UserRecord {
    713         public final UserInfo info;
    714         public final Bitmap picture;
    715         public final boolean isGuest;
    716         public final boolean isCurrent;
    717         public final boolean isAddUser;
    718         /** If true, the record is only visible to the owner and only when unlocked. */
    719         public final boolean isRestricted;
    720         public boolean isDisabledByAdmin;
    721         public EnforcedAdmin enforcedAdmin;
    722         public boolean isSwitchToEnabled;
    723 
    724         public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent,
    725                 boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) {
    726             this.info = info;
    727             this.picture = picture;
    728             this.isGuest = isGuest;
    729             this.isCurrent = isCurrent;
    730             this.isAddUser = isAddUser;
    731             this.isRestricted = isRestricted;
    732             this.isSwitchToEnabled = isSwitchToEnabled;
    733         }
    734 
    735         public UserRecord copyWithIsCurrent(boolean _isCurrent) {
    736             return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
    737                     isSwitchToEnabled);
    738         }
    739 
    740         public int resolveId() {
    741             if (isGuest || info == null) {
    742                 return UserHandle.USER_NULL;
    743             }
    744             return info.id;
    745         }
    746 
    747         public String toString() {
    748             StringBuilder sb = new StringBuilder();
    749             sb.append("UserRecord(");
    750             if (info != null) {
    751                 sb.append("name=\"").append(info.name).append("\" id=").append(info.id);
    752             } else {
    753                 if (isGuest) {
    754                     sb.append("<add guest placeholder>");
    755                 } else if (isAddUser) {
    756                     sb.append("<add user placeholder>");
    757                 }
    758             }
    759             if (isGuest) sb.append(" <isGuest>");
    760             if (isAddUser) sb.append(" <isAddUser>");
    761             if (isCurrent) sb.append(" <isCurrent>");
    762             if (picture != null) sb.append(" <hasPicture>");
    763             if (isRestricted) sb.append(" <isRestricted>");
    764             if (isDisabledByAdmin) {
    765                 sb.append(" <isDisabledByAdmin>");
    766                 sb.append(" enforcedAdmin=").append(enforcedAdmin);
    767             }
    768             if (isSwitchToEnabled) {
    769                 sb.append(" <isSwitchToEnabled>");
    770             }
    771             sb.append(')');
    772             return sb.toString();
    773         }
    774     }
    775 
    776     public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() {
    777         private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS);
    778 
    779         @Override
    780         public CharSequence getTitle() {
    781             return mContext.getString(R.string.quick_settings_user_title);
    782         }
    783 
    784         @Override
    785         public View createDetailView(Context context, View convertView, ViewGroup parent) {
    786             UserDetailView v;
    787             if (!(convertView instanceof UserDetailView)) {
    788                 v = UserDetailView.inflate(context, parent, false);
    789                 v.createAndSetAdapter(UserSwitcherController.this);
    790             } else {
    791                 v = (UserDetailView) convertView;
    792             }
    793             v.refreshAdapter();
    794             return v;
    795         }
    796 
    797         @Override
    798         public Intent getSettingsIntent() {
    799             return USER_SETTINGS_INTENT;
    800         }
    801 
    802         @Override
    803         public Boolean getToggleState() {
    804             return null;
    805         }
    806 
    807         @Override
    808         public void setToggleState(boolean state) {
    809         }
    810 
    811         @Override
    812         public int getMetricsCategory() {
    813             return MetricsEvent.QS_USERDETAIL;
    814         }
    815     };
    816 
    817     private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() {
    818         @Override
    819         public void onKeyguardChanged() {
    820             notifyAdapters();
    821         }
    822     };
    823 
    824     private final class ExitGuestDialog extends SystemUIDialog implements
    825             DialogInterface.OnClickListener {
    826 
    827         private final int mGuestId;
    828 
    829         public ExitGuestDialog(Context context, int guestId) {
    830             super(context);
    831             setTitle(R.string.guest_exit_guest_dialog_title);
    832             setMessage(context.getString(R.string.guest_exit_guest_dialog_message));
    833             setButton(DialogInterface.BUTTON_NEGATIVE,
    834                     context.getString(android.R.string.cancel), this);
    835             setButton(DialogInterface.BUTTON_POSITIVE,
    836                     context.getString(R.string.guest_exit_guest_dialog_remove), this);
    837             setCanceledOnTouchOutside(false);
    838             mGuestId = guestId;
    839         }
    840 
    841         @Override
    842         public void onClick(DialogInterface dialog, int which) {
    843             if (which == BUTTON_NEGATIVE) {
    844                 cancel();
    845             } else {
    846                 dismiss();
    847                 exitGuest(mGuestId);
    848             }
    849         }
    850     }
    851 
    852     private final class AddUserDialog extends SystemUIDialog implements
    853             DialogInterface.OnClickListener {
    854 
    855         public AddUserDialog(Context context) {
    856             super(context);
    857             setTitle(R.string.user_add_user_title);
    858             setMessage(context.getString(R.string.user_add_user_message_short));
    859             setButton(DialogInterface.BUTTON_NEGATIVE,
    860                     context.getString(android.R.string.cancel), this);
    861             setButton(DialogInterface.BUTTON_POSITIVE,
    862                     context.getString(android.R.string.ok), this);
    863         }
    864 
    865         @Override
    866         public void onClick(DialogInterface dialog, int which) {
    867             if (which == BUTTON_NEGATIVE) {
    868                 cancel();
    869             } else {
    870                 dismiss();
    871                 if (ActivityManager.isUserAMonkey()) {
    872                     return;
    873                 }
    874                 UserInfo user = mUserManager.createUser(
    875                         mContext.getString(R.string.user_new_user_name), 0 /* flags */);
    876                 if (user == null) {
    877                     // Couldn't create user, most likely because there are too many, but we haven't
    878                     // been able to reload the list yet.
    879                     return;
    880                 }
    881                 int id = user.id;
    882                 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon(
    883                         id, /* light= */ false));
    884                 mUserManager.setUserIcon(id, icon);
    885                 switchToUserId(id);
    886             }
    887         }
    888     }
    889 }
    890