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