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