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