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