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