• 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 
switchToUserId(int id)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 
showExitGuestDialog(int id)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 
showAddUserDialog()401     private void showAddUserDialog() {
402         if (mAddUserDialog != null && mAddUserDialog.isShowing()) {
403             mAddUserDialog.cancel();
404         }
405         mAddUserDialog = new AddUserDialog(mContext);
406         mAddUserDialog.show();
407     }
408 
exitGuest(int id)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 
listenForCallState()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 
showGuestNotification(int guestUserId)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 
dump(FileDescriptor fd, PrintWriter pw, String[] args)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 
getCurrentUserName(Context context)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 
onDensityOrFontScaleChanged()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 
BaseUserAdapter(UserSwitcherController controller)612         protected BaseUserAdapter(UserSwitcherController controller) {
613             mController = controller;
614             controller.mAdapters.add(new WeakReference<>(this));
615         }
616 
617         @Override
getCount()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
getItem(int position)639         public UserRecord getItem(int position) {
640             return mController.mUsers.get(position);
641         }
642 
643         @Override
getItemId(int position)644         public long getItemId(int position) {
645             return position;
646         }
647 
switchTo(UserRecord record)648         public void switchTo(UserRecord record) {
649             mController.switchTo(record);
650         }
651 
getName(Context context, UserRecord item)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 
getDrawable(Context context, UserRecord item)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 
refresh()674         public void refresh() {
675             mController.refreshUsers(UserHandle.USER_NULL);
676         }
677     }
678 
checkIfAddUserDisallowedByAdminOnly(UserRecord record)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 
startActivity(Intent intent)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 
UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled)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 
copyWithIsCurrent(boolean _isCurrent)719         public UserRecord copyWithIsCurrent(boolean _isCurrent) {
720             return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted,
721                     isSwitchToEnabled);
722         }
723 
resolveId()724         public int resolveId() {
725             if (isGuest || info == null) {
726                 return UserHandle.USER_NULL;
727             }
728             return info.id;
729         }
730 
toString()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 
ExitGuestDialog(Context context, int guestId)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
onClick(DialogInterface dialog, int which)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 
AddUserDialog(Context context)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
onClick(DialogInterface dialog, int which)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