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