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.R; 61 import com.android.systemui.SystemUI; 62 import com.android.systemui.SystemUISecondaryUserService; 63 import com.android.systemui.plugins.qs.DetailAdapter; 64 import com.android.systemui.qs.tiles.UserDetailView; 65 import com.android.systemui.plugins.ActivityStarter; 66 import com.android.systemui.statusbar.phone.SystemUIDialog; 67 import com.android.systemui.util.NotificationChannels; 68 69 import java.io.FileDescriptor; 70 import java.io.PrintWriter; 71 import java.lang.ref.WeakReference; 72 import java.util.ArrayList; 73 import java.util.List; 74 75 /** 76 * Keeps a list of all users on the device for user switching. 77 */ 78 public class UserSwitcherController { 79 80 private static final String TAG = "UserSwitcherController"; 81 private static final boolean DEBUG = false; 82 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING = 83 "lockscreenSimpleUserSwitcher"; 84 private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST"; 85 private static final String ACTION_LOGOUT_USER = "com.android.systemui.LOGOUT_USER"; 86 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000; 87 88 private static final String TAG_REMOVE_GUEST = "remove_guest"; 89 private static final String TAG_LOGOUT_USER = "logout_user"; 90 91 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; 92 93 protected final Context mContext; 94 protected final UserManager mUserManager; 95 private final ArrayList<WeakReference<BaseUserAdapter>> mAdapters = new ArrayList<>(); 96 private final GuestResumeSessionReceiver mGuestResumeSessionReceiver 97 = new GuestResumeSessionReceiver(); 98 private final KeyguardMonitor mKeyguardMonitor; 99 protected final Handler mHandler; 100 private final ActivityStarter mActivityStarter; 101 102 private ArrayList<UserRecord> mUsers = new ArrayList<>(); 103 private Dialog mExitGuestDialog; 104 private Dialog mAddUserDialog; 105 private int mLastNonGuestUser = UserHandle.USER_SYSTEM; 106 private boolean mResumeUserOnGuestLogout = true; 107 private boolean mSimpleUserSwitcher; 108 private boolean mAddUsersWhenLocked; 109 private boolean mPauseRefreshUsers; 110 private int mSecondaryUser = UserHandle.USER_NULL; 111 private Intent mSecondaryUserServiceIntent; 112 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2); 113 114 public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, 115 Handler handler, ActivityStarter activityStarter) { 116 mContext = context; 117 mGuestResumeSessionReceiver.register(context); 118 mKeyguardMonitor = keyguardMonitor; 119 mHandler = handler; 120 mActivityStarter = activityStarter; 121 mUserManager = UserManager.get(context); 122 IntentFilter filter = new IntentFilter(); 123 filter.addAction(Intent.ACTION_USER_ADDED); 124 filter.addAction(Intent.ACTION_USER_REMOVED); 125 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 126 filter.addAction(Intent.ACTION_USER_SWITCHED); 127 filter.addAction(Intent.ACTION_USER_STOPPED); 128 filter.addAction(Intent.ACTION_USER_UNLOCKED); 129 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, 130 null /* permission */, null /* scheduler */); 131 132 mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class); 133 134 filter = new IntentFilter(); 135 filter.addAction(ACTION_REMOVE_GUEST); 136 filter.addAction(ACTION_LOGOUT_USER); 137 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, 138 PERMISSION_SELF, null /* scheduler */); 139 140 mContext.getContentResolver().registerContentObserver( 141 Settings.Global.getUriFor(SIMPLE_USER_SWITCHER_GLOBAL_SETTING), true, 142 mSettingsObserver); 143 mContext.getContentResolver().registerContentObserver( 144 Settings.Global.getUriFor(Settings.Global.ADD_USERS_WHEN_LOCKED), true, 145 mSettingsObserver); 146 mContext.getContentResolver().registerContentObserver( 147 Settings.Global.getUriFor( 148 Settings.Global.ALLOW_USER_SWITCHING_WHEN_SYSTEM_USER_LOCKED), 149 true, mSettingsObserver); 150 // Fetch initial values. 151 mSettingsObserver.onChange(false); 152 153 keyguardMonitor.addCallback(mCallback); 154 listenForCallState(); 155 156 refreshUsers(UserHandle.USER_NULL); 157 } 158 159 /** 160 * Refreshes users from UserManager. 161 * 162 * The pictures are only loaded if they have not been loaded yet. 163 * 164 * @param forcePictureLoadForId forces the picture of the given user to be reloaded. 165 */ 166 @SuppressWarnings("unchecked") 167 private void refreshUsers(int forcePictureLoadForId) { 168 if (DEBUG) Log.d(TAG, "refreshUsers(forcePictureLoadForId=" + forcePictureLoadForId+")"); 169 if (forcePictureLoadForId != UserHandle.USER_NULL) { 170 mForcePictureLoadForUserId.put(forcePictureLoadForId, true); 171 } 172 173 if (mPauseRefreshUsers) { 174 return; 175 } 176 177 boolean forceAllUsers = mForcePictureLoadForUserId.get(UserHandle.USER_ALL); 178 SparseArray<Bitmap> bitmaps = new SparseArray<>(mUsers.size()); 179 final int N = mUsers.size(); 180 for (int i = 0; i < N; i++) { 181 UserRecord r = mUsers.get(i); 182 if (r == null || r.picture == null || r.info == null || forceAllUsers 183 || mForcePictureLoadForUserId.get(r.info.id)) { 184 continue; 185 } 186 bitmaps.put(r.info.id, r.picture); 187 } 188 mForcePictureLoadForUserId.clear(); 189 190 final boolean addUsersWhenLocked = mAddUsersWhenLocked; 191 new AsyncTask<SparseArray<Bitmap>, Void, ArrayList<UserRecord>>() { 192 @SuppressWarnings("unchecked") 193 @Override 194 protected ArrayList<UserRecord> doInBackground(SparseArray<Bitmap>... params) { 195 final SparseArray<Bitmap> bitmaps = params[0]; 196 List<UserInfo> infos = mUserManager.getUsers(true); 197 if (infos == null) { 198 return null; 199 } 200 ArrayList<UserRecord> records = new ArrayList<>(infos.size()); 201 int currentId = ActivityManager.getCurrentUser(); 202 boolean canSwitchUsers = mUserManager.canSwitchUsers(); 203 UserInfo currentUserInfo = null; 204 UserRecord guestRecord = null; 205 206 for (UserInfo info : infos) { 207 boolean isCurrent = currentId == info.id; 208 if (isCurrent) { 209 currentUserInfo = info; 210 } 211 boolean switchToEnabled = canSwitchUsers || isCurrent; 212 if (info.isEnabled()) { 213 if (info.isGuest()) { 214 // Tapping guest icon triggers remove and a user switch therefore 215 // the icon shouldn't be enabled even if the user is current 216 guestRecord = new UserRecord(info, null /* picture */, 217 true /* isGuest */, isCurrent, false /* isAddUser */, 218 false /* isRestricted */, canSwitchUsers); 219 } else if (info.supportsSwitchToByUser()) { 220 Bitmap picture = bitmaps.get(info.id); 221 if (picture == null) { 222 picture = mUserManager.getUserIcon(info.id); 223 224 if (picture != null) { 225 int avatarSize = mContext.getResources() 226 .getDimensionPixelSize(R.dimen.max_avatar_size); 227 picture = Bitmap.createScaledBitmap( 228 picture, avatarSize, avatarSize, true); 229 } 230 } 231 int index = isCurrent ? 0 : records.size(); 232 records.add(index, new UserRecord(info, picture, false /* isGuest */, 233 isCurrent, false /* isAddUser */, false /* isRestricted */, 234 switchToEnabled)); 235 } 236 } 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 288 private void pauseRefreshUsers() { 289 if (!mPauseRefreshUsers) { 290 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS); 291 mPauseRefreshUsers = true; 292 } 293 } 294 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 306 public boolean isSimpleUserSwitcher() { 307 return mSimpleUserSwitcher; 308 } 309 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 326 public void setResumeUserOnGuestLogout(boolean resume) { 327 mResumeUserOnGuestLogout = resume; 328 } 329 330 public void logoutCurrentUser() { 331 int currentUser = ActivityManager.getCurrentUser(); 332 if (currentUser != UserHandle.USER_SYSTEM) { 333 pauseRefreshUsers(); 334 ActivityManager.logoutCurrentUser(); 335 } 336 } 337 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 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 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 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.supportsSwitchTo()) { 409 count++; 410 } 411 } 412 return count; 413 } 414 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 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 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 443 public void showAddUserDialog() { 444 if (mAddUserDialog != null && mAddUserDialog.isShowing()) { 445 mAddUserDialog.cancel(); 446 } 447 mAddUserDialog = new AddUserDialog(mContext); 448 mAddUserDialog.show(); 449 } 450 451 protected void exitGuest(int id, int targetId) { 452 switchToUserId(targetId); 453 mUserManager.removeUser(id); 454 } 455 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 int currentUserId = ActivityManager.getCurrentUser(); 470 UserInfo userInfo = mUserManager.getUserInfo(currentUserId); 471 if (userInfo != null && userInfo.isGuest()) { 472 showGuestNotification(currentUserId); 473 } 474 refreshUsers(UserHandle.USER_NULL); 475 } 476 }; 477 478 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 479 @Override 480 public void onReceive(Context context, Intent intent) { 481 if (DEBUG) { 482 Log.v(TAG, "Broadcast: a=" + intent.getAction() 483 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); 484 } 485 486 boolean unpauseRefreshUsers = false; 487 int forcePictureLoadForId = UserHandle.USER_NULL; 488 489 if (ACTION_REMOVE_GUEST.equals(intent.getAction())) { 490 int currentUser = ActivityManager.getCurrentUser(); 491 UserInfo userInfo = mUserManager.getUserInfo(currentUser); 492 if (userInfo != null && userInfo.isGuest()) { 493 showExitGuestDialog(currentUser); 494 } 495 return; 496 } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) { 497 logoutCurrentUser(); 498 } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 499 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 500 mExitGuestDialog.cancel(); 501 mExitGuestDialog = null; 502 } 503 504 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 505 final UserInfo userInfo = mUserManager.getUserInfo(currentId); 506 final int N = mUsers.size(); 507 for (int i = 0; i < N; i++) { 508 UserRecord record = mUsers.get(i); 509 if (record.info == null) continue; 510 boolean shouldBeCurrent = record.info.id == currentId; 511 if (record.isCurrent != shouldBeCurrent) { 512 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent)); 513 } 514 if (shouldBeCurrent && !record.isGuest) { 515 mLastNonGuestUser = record.info.id; 516 } 517 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) { 518 // Immediately remove restricted records in case the AsyncTask is too slow. 519 mUsers.remove(i); 520 i--; 521 } 522 } 523 notifyAdapters(); 524 525 // Disconnect from the old secondary user's service 526 if (mSecondaryUser != UserHandle.USER_NULL) { 527 context.stopServiceAsUser(mSecondaryUserServiceIntent, 528 UserHandle.of(mSecondaryUser)); 529 mSecondaryUser = UserHandle.USER_NULL; 530 } 531 // Connect to the new secondary user's service (purely to ensure that a persistent 532 // SystemUI application is created for that user) 533 if (userInfo != null && userInfo.id != UserHandle.USER_SYSTEM) { 534 context.startServiceAsUser(mSecondaryUserServiceIntent, 535 UserHandle.of(userInfo.id)); 536 mSecondaryUser = userInfo.id; 537 } 538 539 if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest() 540 && userInfo.id != UserHandle.USER_SYSTEM) { 541 showLogoutNotification(currentId); 542 } 543 if (userInfo != null && userInfo.isGuest()) { 544 showGuestNotification(currentId); 545 } 546 unpauseRefreshUsers = true; 547 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) { 548 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 549 UserHandle.USER_NULL); 550 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 551 // Unlocking the system user may require a refresh 552 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 553 if (userId != UserHandle.USER_SYSTEM) { 554 return; 555 } 556 } 557 refreshUsers(forcePictureLoadForId); 558 if (unpauseRefreshUsers) { 559 mUnpauseRefreshUsers.run(); 560 } 561 } 562 563 private void showLogoutNotification(int userId) { 564 PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext, 565 0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM); 566 Notification.Builder builder = 567 new Notification.Builder(mContext, NotificationChannels.GENERAL) 568 .setVisibility(Notification.VISIBILITY_SECRET) 569 .setSmallIcon(R.drawable.ic_person) 570 .setContentTitle(mContext.getString( 571 R.string.user_logout_notification_title)) 572 .setContentText(mContext.getString( 573 R.string.user_logout_notification_text)) 574 .setContentIntent(logoutPI) 575 .setOngoing(true) 576 .setShowWhen(false) 577 .addAction(R.drawable.ic_delete, 578 mContext.getString(R.string.user_logout_notification_action), 579 logoutPI); 580 SystemUI.overrideNotificationAppName(mContext, builder); 581 NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER, 582 SystemMessage.NOTE_LOGOUT_USER, builder.build(), new UserHandle(userId)); 583 } 584 }; 585 586 private void showGuestNotification(int guestUserId) { 587 boolean canSwitchUsers = mUserManager.canSwitchUsers(); 588 // Disable 'Remove guest' action if cannot switch users right now 589 PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext, 590 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null; 591 592 Notification.Builder builder = 593 new Notification.Builder(mContext, NotificationChannels.GENERAL) 594 .setVisibility(Notification.VISIBILITY_SECRET) 595 .setSmallIcon(R.drawable.ic_person) 596 .setContentTitle(mContext.getString(R.string.guest_notification_title)) 597 .setContentText(mContext.getString(R.string.guest_notification_text)) 598 .setContentIntent(removeGuestPI) 599 .setShowWhen(false) 600 .addAction(R.drawable.ic_delete, 601 mContext.getString(R.string.guest_notification_remove_action), 602 removeGuestPI); 603 SystemUI.overrideNotificationAppName(mContext, builder); 604 NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, 605 SystemMessage.NOTE_REMOVE_GUEST, builder.build(), new UserHandle(guestUserId)); 606 } 607 608 private final Runnable mUnpauseRefreshUsers = new Runnable() { 609 @Override 610 public void run() { 611 mHandler.removeCallbacks(this); 612 mPauseRefreshUsers = false; 613 refreshUsers(UserHandle.USER_NULL); 614 } 615 }; 616 617 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 618 public void onChange(boolean selfChange) { 619 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(), 620 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0; 621 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), 622 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; 623 refreshUsers(UserHandle.USER_NULL); 624 }; 625 }; 626 627 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 628 pw.println("UserSwitcherController state:"); 629 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser); 630 pw.print(" mUsers.size="); pw.println(mUsers.size()); 631 for (int i = 0; i < mUsers.size(); i++) { 632 final UserRecord u = mUsers.get(i); 633 pw.print(" "); pw.println(u.toString()); 634 } 635 } 636 637 public String getCurrentUserName(Context context) { 638 if (mUsers.isEmpty()) return null; 639 UserRecord item = mUsers.get(0); 640 if (item == null || item.info == null) return null; 641 if (item.isGuest) return context.getString(R.string.guest_nickname); 642 return item.info.name; 643 } 644 645 public void onDensityOrFontScaleChanged() { 646 refreshUsers(UserHandle.USER_ALL); 647 } 648 649 @VisibleForTesting 650 public void addAdapter(WeakReference<BaseUserAdapter> adapter) { 651 mAdapters.add(adapter); 652 } 653 654 @VisibleForTesting 655 public ArrayList<UserRecord> getUsers() { 656 return mUsers; 657 } 658 659 public static abstract class BaseUserAdapter extends BaseAdapter { 660 661 final UserSwitcherController mController; 662 private final KeyguardMonitor mKeyguardMonitor; 663 664 protected BaseUserAdapter(UserSwitcherController controller) { 665 mController = controller; 666 mKeyguardMonitor = Dependency.get(KeyguardMonitor.class); 667 controller.addAdapter(new WeakReference<>(this)); 668 } 669 670 public int getUserCount() { 671 boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() 672 && mKeyguardMonitor.isSecure() 673 && !mKeyguardMonitor.canSkipBouncer(); 674 if (!secureKeyguardShowing) { 675 return mController.getUsers().size(); 676 } 677 // The lock screen is secure and showing. Filter out restricted records. 678 final int N = mController.getUsers().size(); 679 int count = 0; 680 for (int i = 0; i < N; i++) { 681 if (mController.getUsers().get(i).isGuest) continue; 682 if (mController.getUsers().get(i).isRestricted) { 683 break; 684 } else { 685 count++; 686 } 687 } 688 return count; 689 } 690 691 @Override 692 public int getCount() { 693 boolean secureKeyguardShowing = mKeyguardMonitor.isShowing() 694 && mKeyguardMonitor.isSecure() 695 && !mKeyguardMonitor.canSkipBouncer(); 696 if (!secureKeyguardShowing) { 697 return mController.getUsers().size(); 698 } 699 // The lock screen is secure and showing. Filter out restricted records. 700 final int N = mController.getUsers().size(); 701 int count = 0; 702 for (int i = 0; i < N; i++) { 703 if (mController.getUsers().get(i).isRestricted) { 704 break; 705 } else { 706 count++; 707 } 708 } 709 return count; 710 } 711 712 @Override 713 public UserRecord getItem(int position) { 714 return mController.getUsers().get(position); 715 } 716 717 @Override 718 public long getItemId(int position) { 719 return position; 720 } 721 722 public void switchTo(UserRecord record) { 723 mController.switchTo(record); 724 } 725 726 public String getName(Context context, UserRecord item) { 727 if (item.isGuest) { 728 if (item.isCurrent) { 729 return context.getString(R.string.guest_exit_guest); 730 } else { 731 return context.getString( 732 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname); 733 } 734 } else if (item.isAddUser) { 735 return context.getString(R.string.user_add_user); 736 } else { 737 return item.info.name; 738 } 739 } 740 741 public Drawable getDrawable(Context context, UserRecord item) { 742 if (item.isAddUser) { 743 return context.getDrawable(R.drawable.ic_add_circle_qs); 744 } 745 Drawable icon = UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ false); 746 if (item.isGuest) { 747 icon.setColorFilter(Utils.getColorAttr(context, android.R.attr.colorForeground), 748 Mode.SRC_IN); 749 } 750 return icon; 751 } 752 753 public void refresh() { 754 mController.refreshUsers(UserHandle.USER_NULL); 755 } 756 } 757 758 private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) { 759 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext, 760 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser()); 761 if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext, 762 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) { 763 record.isDisabledByAdmin = true; 764 record.enforcedAdmin = admin; 765 } else { 766 record.isDisabledByAdmin = false; 767 record.enforcedAdmin = null; 768 } 769 } 770 771 public void startActivity(Intent intent) { 772 mActivityStarter.startActivity(intent, true); 773 } 774 775 public static final class UserRecord { 776 public final UserInfo info; 777 public final Bitmap picture; 778 public final boolean isGuest; 779 public final boolean isCurrent; 780 public final boolean isAddUser; 781 /** If true, the record is only visible to the owner and only when unlocked. */ 782 public final boolean isRestricted; 783 public boolean isDisabledByAdmin; 784 public EnforcedAdmin enforcedAdmin; 785 public boolean isSwitchToEnabled; 786 787 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, 788 boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) { 789 this.info = info; 790 this.picture = picture; 791 this.isGuest = isGuest; 792 this.isCurrent = isCurrent; 793 this.isAddUser = isAddUser; 794 this.isRestricted = isRestricted; 795 this.isSwitchToEnabled = isSwitchToEnabled; 796 } 797 798 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 799 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, 800 isSwitchToEnabled); 801 } 802 803 public int resolveId() { 804 if (isGuest || info == null) { 805 return UserHandle.USER_NULL; 806 } 807 return info.id; 808 } 809 810 public String toString() { 811 StringBuilder sb = new StringBuilder(); 812 sb.append("UserRecord("); 813 if (info != null) { 814 sb.append("name=\"").append(info.name).append("\" id=").append(info.id); 815 } else { 816 if (isGuest) { 817 sb.append("<add guest placeholder>"); 818 } else if (isAddUser) { 819 sb.append("<add user placeholder>"); 820 } 821 } 822 if (isGuest) sb.append(" <isGuest>"); 823 if (isAddUser) sb.append(" <isAddUser>"); 824 if (isCurrent) sb.append(" <isCurrent>"); 825 if (picture != null) sb.append(" <hasPicture>"); 826 if (isRestricted) sb.append(" <isRestricted>"); 827 if (isDisabledByAdmin) { 828 sb.append(" <isDisabledByAdmin>"); 829 sb.append(" enforcedAdmin=").append(enforcedAdmin); 830 } 831 if (isSwitchToEnabled) { 832 sb.append(" <isSwitchToEnabled>"); 833 } 834 sb.append(')'); 835 return sb.toString(); 836 } 837 } 838 839 public final DetailAdapter userDetailAdapter = new DetailAdapter() { 840 private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS); 841 842 @Override 843 public CharSequence getTitle() { 844 return mContext.getString(R.string.quick_settings_user_title); 845 } 846 847 @Override 848 public View createDetailView(Context context, View convertView, ViewGroup parent) { 849 UserDetailView v; 850 if (!(convertView instanceof UserDetailView)) { 851 v = UserDetailView.inflate(context, parent, false); 852 v.createAndSetAdapter(UserSwitcherController.this); 853 } else { 854 v = (UserDetailView) convertView; 855 } 856 v.refreshAdapter(); 857 return v; 858 } 859 860 @Override 861 public Intent getSettingsIntent() { 862 return USER_SETTINGS_INTENT; 863 } 864 865 @Override 866 public Boolean getToggleState() { 867 return null; 868 } 869 870 @Override 871 public void setToggleState(boolean state) { 872 } 873 874 @Override 875 public int getMetricsCategory() { 876 return MetricsEvent.QS_USERDETAIL; 877 } 878 }; 879 880 private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() { 881 @Override 882 public void onKeyguardShowingChanged() { 883 884 // When Keyguard is going away, we don't need to update our items immediately which 885 // helps making the transition faster. 886 if (!mKeyguardMonitor.isShowing()) { 887 mHandler.post(UserSwitcherController.this::notifyAdapters); 888 } else { 889 notifyAdapters(); 890 } 891 } 892 }; 893 894 private final class ExitGuestDialog extends SystemUIDialog implements 895 DialogInterface.OnClickListener { 896 897 private final int mGuestId; 898 private final int mTargetId; 899 900 public ExitGuestDialog(Context context, int guestId, int targetId) { 901 super(context); 902 setTitle(R.string.guest_exit_guest_dialog_title); 903 setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); 904 setButton(DialogInterface.BUTTON_NEGATIVE, 905 context.getString(android.R.string.cancel), this); 906 setButton(DialogInterface.BUTTON_POSITIVE, 907 context.getString(R.string.guest_exit_guest_dialog_remove), this); 908 setCanceledOnTouchOutside(false); 909 mGuestId = guestId; 910 mTargetId = targetId; 911 } 912 913 @Override 914 public void onClick(DialogInterface dialog, int which) { 915 if (which == BUTTON_NEGATIVE) { 916 cancel(); 917 } else { 918 dismiss(); 919 exitGuest(mGuestId, mTargetId); 920 } 921 } 922 } 923 924 private final class AddUserDialog extends SystemUIDialog implements 925 DialogInterface.OnClickListener { 926 927 public AddUserDialog(Context context) { 928 super(context); 929 setTitle(R.string.user_add_user_title); 930 setMessage(context.getString(R.string.user_add_user_message_short)); 931 setButton(DialogInterface.BUTTON_NEGATIVE, 932 context.getString(android.R.string.cancel), this); 933 setButton(DialogInterface.BUTTON_POSITIVE, 934 context.getString(android.R.string.ok), this); 935 } 936 937 @Override 938 public void onClick(DialogInterface dialog, int which) { 939 if (which == BUTTON_NEGATIVE) { 940 cancel(); 941 } else { 942 dismiss(); 943 if (ActivityManager.isUserAMonkey()) { 944 return; 945 } 946 UserInfo user = mUserManager.createUser( 947 mContext.getString(R.string.user_new_user_name), 0 /* flags */); 948 if (user == null) { 949 // Couldn't create user, most likely because there are too many, but we haven't 950 // been able to reload the list yet. 951 return; 952 } 953 int id = user.id; 954 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon( 955 id, /* light= */ false)); 956 mUserManager.setUserIcon(id, icon); 957 switchToUserId(id); 958 } 959 } 960 } 961 } 962