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