• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, Handler handler, ActivityStarter activityStarter)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")
refreshUsers(int forcePictureLoadForId)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 
pauseRefreshUsers()283     private void pauseRefreshUsers() {
284         if (!mPauseRefreshUsers) {
285             mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS);
286             mPauseRefreshUsers = true;
287         }
288     }
289 
notifyAdapters()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 
isSimpleUserSwitcher()301     public boolean isSimpleUserSwitcher() {
302         return mSimpleUserSwitcher;
303     }
304 
useFullscreenUserSwitcher()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 
logoutCurrentUser()321     public void logoutCurrentUser() {
322         int currentUser = ActivityManager.getCurrentUser();
323         if (currentUser != UserHandle.USER_SYSTEM) {
324             pauseRefreshUsers();
325             ActivityManager.logoutCurrentUser();
326         }
327     }
328 
removeUserId(int userId)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 
switchTo(UserRecord record)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 
switchTo(int userId)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 
getSwitchableUserCount()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 
switchToUserId(int id)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 
showExitGuestDialog(int id)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 
showAddUserDialog()413     private void showAddUserDialog() {
414         if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
415             mAddUserDialog.cancel();
416         }
417         mAddUserDialog = new AddUserDialog(mContext);
418         mAddUserDialog.show();
419     }
420 
exitGuest(int id)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 
listenForCallState()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 
showGuestNotification(int guestUserId)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 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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 
getCurrentUserName(Context context)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 
onDensityOrFontScaleChanged()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 
BaseUserAdapter(UserSwitcherController controller)628         protected BaseUserAdapter(UserSwitcherController controller) {
629             mController = controller;
630             controller.mAdapters.add(new WeakReference<>(this));
631         }
632 
633         @Override
getCount()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
getItem(int position)655         public UserRecord getItem(int position) {
656             return mController.mUsers.get(position);
657         }
658 
659         @Override
getItemId(int position)660         public long getItemId(int position) {
661             return position;
662         }
663 
switchTo(UserRecord record)664         public void switchTo(UserRecord record) {
665             mController.switchTo(record);
666         }
667 
getName(Context context, UserRecord item)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 
getDrawable(Context context, UserRecord item)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 
refresh()690         public void refresh() {
691             mController.refreshUsers(UserHandle.USER_NULL);
692         }
693     }
694 
checkIfAddUserDisallowedByAdminOnly(UserRecord record)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 
startActivity(Intent intent)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 
UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled)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 
copyWithIsCurrent(boolean _isCurrent)735         public UserRecord copyWithIsCurrent(boolean _isCurrent) {
736             return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
737                     isSwitchToEnabled);
738         }
739 
resolveId()740         public int resolveId() {
741             if (isGuest || info == null) {
742                 return UserHandle.USER_NULL;
743             }
744             return info.id;
745         }
746 
toString()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 
ExitGuestDialog(Context context, int guestId)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
onClick(DialogInterface dialog, int which)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 
AddUserDialog(Context context)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
onClick(DialogInterface dialog, int which)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