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