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