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