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 android.app.ActivityManager; 20 import android.app.ActivityManagerNative; 21 import android.app.Dialog; 22 import android.app.Notification; 23 import android.app.NotificationManager; 24 import android.app.PendingIntent; 25 import android.content.BroadcastReceiver; 26 import android.content.Context; 27 import android.content.DialogInterface; 28 import android.content.Intent; 29 import android.content.IntentFilter; 30 import android.content.pm.UserInfo; 31 import android.database.ContentObserver; 32 import android.graphics.Bitmap; 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.logging.MetricsProto.MetricsEvent; 50 import com.android.internal.util.UserIcons; 51 import com.android.settingslib.RestrictedLockUtils; 52 import com.android.systemui.GuestResumeSessionReceiver; 53 import com.android.systemui.R; 54 import com.android.systemui.SystemUI; 55 import com.android.systemui.SystemUISecondaryUserService; 56 import com.android.systemui.qs.QSTile; 57 import com.android.systemui.qs.tiles.UserDetailView; 58 import com.android.systemui.statusbar.phone.ActivityStarter; 59 import com.android.systemui.statusbar.phone.SystemUIDialog; 60 61 import java.io.FileDescriptor; 62 import java.io.PrintWriter; 63 import java.lang.ref.WeakReference; 64 import java.util.ArrayList; 65 import java.util.List; 66 67 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 68 69 /** 70 * Keeps a list of all users on the device for user switching. 71 */ 72 public class UserSwitcherController { 73 74 private static final String TAG = "UserSwitcherController"; 75 private static final boolean DEBUG = false; 76 private static final String SIMPLE_USER_SWITCHER_GLOBAL_SETTING = 77 "lockscreenSimpleUserSwitcher"; 78 private static final String ACTION_REMOVE_GUEST = "com.android.systemui.REMOVE_GUEST"; 79 private static final String ACTION_LOGOUT_USER = "com.android.systemui.LOGOUT_USER"; 80 private static final int PAUSE_REFRESH_USERS_TIMEOUT_MS = 3000; 81 82 private static final int ID_REMOVE_GUEST = 1010; 83 private static final int ID_LOGOUT_USER = 1011; 84 private static final String TAG_REMOVE_GUEST = "remove_guest"; 85 private static final String TAG_LOGOUT_USER = "logout_user"; 86 87 private static final String PERMISSION_SELF = "com.android.systemui.permission.SELF"; 88 89 private final Context mContext; 90 private 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 private 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 mSimpleUserSwitcher; 103 private boolean mAddUsersWhenLocked; 104 private boolean mPauseRefreshUsers; 105 private int mSecondaryUser = UserHandle.USER_NULL; 106 private Intent mSecondaryUserServiceIntent; 107 private SparseBooleanArray mForcePictureLoadForUserId = new SparseBooleanArray(2); 108 109 public UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, 110 Handler handler, ActivityStarter activityStarter) { 111 mContext = context; 112 mGuestResumeSessionReceiver.register(context); 113 mKeyguardMonitor = keyguardMonitor; 114 mHandler = handler; 115 mActivityStarter = activityStarter; 116 mUserManager = UserManager.get(context); 117 IntentFilter filter = new IntentFilter(); 118 filter.addAction(Intent.ACTION_USER_ADDED); 119 filter.addAction(Intent.ACTION_USER_REMOVED); 120 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 121 filter.addAction(Intent.ACTION_USER_SWITCHED); 122 filter.addAction(Intent.ACTION_USER_STOPPED); 123 filter.addAction(Intent.ACTION_USER_UNLOCKED); 124 mContext.registerReceiverAsUser(mReceiver, UserHandle.SYSTEM, filter, 125 null /* permission */, null /* scheduler */); 126 127 mSecondaryUserServiceIntent = new Intent(context, SystemUISecondaryUserService.class); 128 129 filter = new IntentFilter(); 130 filter.addAction(ACTION_REMOVE_GUEST); 131 filter.addAction(ACTION_LOGOUT_USER); 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 234 boolean systemCanCreateUsers = !mUserManager.hasBaseUserRestriction( 235 UserManager.DISALLOW_ADD_USER, UserHandle.SYSTEM); 236 boolean currentUserCanCreateUsers = currentUserInfo != null 237 && (currentUserInfo.isAdmin() 238 || currentUserInfo.id == UserHandle.USER_SYSTEM) 239 && systemCanCreateUsers; 240 boolean anyoneCanCreateUsers = systemCanCreateUsers && addUsersWhenLocked; 241 boolean canCreateGuest = (currentUserCanCreateUsers || anyoneCanCreateUsers) 242 && guestRecord == null; 243 boolean canCreateUser = (currentUserCanCreateUsers || anyoneCanCreateUsers) 244 && mUserManager.canAddMoreUsers(); 245 boolean createIsRestricted = !addUsersWhenLocked; 246 247 if (!mSimpleUserSwitcher) { 248 if (guestRecord == null) { 249 if (canCreateGuest) { 250 guestRecord = new UserRecord(null /* info */, null /* picture */, 251 true /* isGuest */, false /* isCurrent */, 252 false /* isAddUser */, createIsRestricted, canSwitchUsers); 253 checkIfAddUserDisallowedByAdminOnly(guestRecord); 254 records.add(guestRecord); 255 } 256 } else { 257 int index = guestRecord.isCurrent ? 0 : records.size(); 258 records.add(index, guestRecord); 259 } 260 } 261 262 if (!mSimpleUserSwitcher && canCreateUser) { 263 UserRecord addUserRecord = new UserRecord(null /* info */, null /* picture */, 264 false /* isGuest */, false /* isCurrent */, true /* isAddUser */, 265 createIsRestricted, canSwitchUsers); 266 checkIfAddUserDisallowedByAdminOnly(addUserRecord); 267 records.add(addUserRecord); 268 } 269 270 return records; 271 } 272 273 @Override 274 protected void onPostExecute(ArrayList<UserRecord> userRecords) { 275 if (userRecords != null) { 276 mUsers = userRecords; 277 notifyAdapters(); 278 } 279 } 280 }.execute((SparseArray) bitmaps); 281 } 282 283 private void pauseRefreshUsers() { 284 if (!mPauseRefreshUsers) { 285 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS); 286 mPauseRefreshUsers = true; 287 } 288 } 289 290 private void notifyAdapters() { 291 for (int i = mAdapters.size() - 1; i >= 0; i--) { 292 BaseUserAdapter adapter = mAdapters.get(i).get(); 293 if (adapter != null) { 294 adapter.notifyDataSetChanged(); 295 } else { 296 mAdapters.remove(i); 297 } 298 } 299 } 300 301 public boolean isSimpleUserSwitcher() { 302 return mSimpleUserSwitcher; 303 } 304 305 public boolean useFullscreenUserSwitcher() { 306 // Use adb to override: 307 // adb shell settings put system enable_fullscreen_user_switcher 0 # Turn it off. 308 // adb shell settings put system enable_fullscreen_user_switcher 1 # Turn it on. 309 // Restart SystemUI or adb reboot. 310 final int DEFAULT = -1; 311 final int overrideUseFullscreenUserSwitcher = 312 Settings.System.getInt(mContext.getContentResolver(), 313 "enable_fullscreen_user_switcher", DEFAULT); 314 if (overrideUseFullscreenUserSwitcher != DEFAULT) { 315 return overrideUseFullscreenUserSwitcher != 0; 316 } 317 // Otherwise default to the build setting. 318 return mContext.getResources().getBoolean(R.bool.config_enableFullscreenUserSwitcher); 319 } 320 321 public void logoutCurrentUser() { 322 int currentUser = ActivityManager.getCurrentUser(); 323 if (currentUser != UserHandle.USER_SYSTEM) { 324 pauseRefreshUsers(); 325 ActivityManager.logoutCurrentUser(); 326 } 327 } 328 329 public void removeUserId(int userId) { 330 if (userId == UserHandle.USER_SYSTEM) { 331 Log.w(TAG, "User " + userId + " could not removed."); 332 return; 333 } 334 if (ActivityManager.getCurrentUser() == userId) { 335 switchToUserId(UserHandle.USER_SYSTEM); 336 } 337 if (mUserManager.removeUser(userId)) { 338 refreshUsers(UserHandle.USER_NULL); 339 } 340 } 341 342 public void switchTo(UserRecord record) { 343 int id; 344 if (record.isGuest && record.info == null) { 345 // No guest user. Create one. 346 UserInfo guest = mUserManager.createGuest( 347 mContext, mContext.getString(R.string.guest_nickname)); 348 if (guest == null) { 349 // Couldn't create guest, most likely because there already exists one, we just 350 // haven't reloaded the user list yet. 351 return; 352 } 353 id = guest.id; 354 } else if (record.isAddUser) { 355 showAddUserDialog(); 356 return; 357 } else { 358 id = record.info.id; 359 } 360 361 if (ActivityManager.getCurrentUser() == id) { 362 if (record.isGuest) { 363 showExitGuestDialog(id); 364 } 365 return; 366 } 367 368 switchToUserId(id); 369 } 370 371 public void switchTo(int userId) { 372 final int count = mUsers.size(); 373 for (int i = 0; i < count; ++i) { 374 UserRecord record = mUsers.get(i); 375 if (record.info != null && record.info.id == userId) { 376 switchTo(record); 377 return; 378 } 379 } 380 381 Log.e(TAG, "Couldn't switch to user, id=" + userId); 382 } 383 384 private void switchToUserId(int id) { 385 try { 386 pauseRefreshUsers(); 387 ActivityManagerNative.getDefault().switchUser(id); 388 } catch (RemoteException e) { 389 Log.e(TAG, "Couldn't switch user.", e); 390 } 391 } 392 393 private void showExitGuestDialog(int id) { 394 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 395 mExitGuestDialog.cancel(); 396 } 397 mExitGuestDialog = new ExitGuestDialog(mContext, id); 398 mExitGuestDialog.show(); 399 } 400 401 private void showAddUserDialog() { 402 if (mAddUserDialog != null && mAddUserDialog.isShowing()) { 403 mAddUserDialog.cancel(); 404 } 405 mAddUserDialog = new AddUserDialog(mContext); 406 mAddUserDialog.show(); 407 } 408 409 private void exitGuest(int id) { 410 int newId = UserHandle.USER_SYSTEM; 411 if (mLastNonGuestUser != UserHandle.USER_SYSTEM) { 412 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); 413 if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { 414 newId = info.id; 415 } 416 } 417 switchToUserId(newId); 418 mUserManager.removeUser(id); 419 } 420 421 private void listenForCallState() { 422 TelephonyManager.from(mContext).listen(new PhoneStateListener() { 423 private int mCallState; 424 @Override 425 public void onCallStateChanged(int state, String incomingNumber) { 426 if (mCallState == state) return; 427 if (DEBUG) Log.v(TAG, "Call state changed: " + state); 428 mCallState = state; 429 int currentUserId = ActivityManager.getCurrentUser(); 430 UserInfo userInfo = mUserManager.getUserInfo(currentUserId); 431 if (userInfo != null && userInfo.isGuest()) { 432 showGuestNotification(currentUserId); 433 } 434 refreshUsers(UserHandle.USER_NULL); 435 } 436 }, PhoneStateListener.LISTEN_CALL_STATE); 437 } 438 439 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 440 @Override 441 public void onReceive(Context context, Intent intent) { 442 if (DEBUG) { 443 Log.v(TAG, "Broadcast: a=" + intent.getAction() 444 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); 445 } 446 447 boolean unpauseRefreshUsers = false; 448 int forcePictureLoadForId = UserHandle.USER_NULL; 449 450 if (ACTION_REMOVE_GUEST.equals(intent.getAction())) { 451 int currentUser = ActivityManager.getCurrentUser(); 452 UserInfo userInfo = mUserManager.getUserInfo(currentUser); 453 if (userInfo != null && userInfo.isGuest()) { 454 showExitGuestDialog(currentUser); 455 } 456 return; 457 } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) { 458 logoutCurrentUser(); 459 } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 460 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 461 mExitGuestDialog.cancel(); 462 mExitGuestDialog = null; 463 } 464 465 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 466 final UserInfo userInfo = mUserManager.getUserInfo(currentId); 467 final int N = mUsers.size(); 468 for (int i = 0; i < N; i++) { 469 UserRecord record = mUsers.get(i); 470 if (record.info == null) continue; 471 boolean shouldBeCurrent = record.info.id == currentId; 472 if (record.isCurrent != shouldBeCurrent) { 473 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent)); 474 } 475 if (shouldBeCurrent && !record.isGuest) { 476 mLastNonGuestUser = record.info.id; 477 } 478 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) { 479 // Immediately remove restricted records in case the AsyncTask is too slow. 480 mUsers.remove(i); 481 i--; 482 } 483 } 484 notifyAdapters(); 485 486 // Disconnect from the old secondary user's service 487 if (mSecondaryUser != UserHandle.USER_NULL) { 488 context.stopServiceAsUser(mSecondaryUserServiceIntent, 489 UserHandle.of(mSecondaryUser)); 490 mSecondaryUser = UserHandle.USER_NULL; 491 } 492 // Connect to the new secondary user's service (purely to ensure that a persistent 493 // SystemUI application is created for that user) 494 if (userInfo != null && !userInfo.isPrimary()) { 495 context.startServiceAsUser(mSecondaryUserServiceIntent, 496 UserHandle.of(userInfo.id)); 497 mSecondaryUser = userInfo.id; 498 } 499 500 if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest() 501 && userInfo.id != UserHandle.USER_SYSTEM) { 502 showLogoutNotification(currentId); 503 } 504 if (userInfo != null && userInfo.isGuest()) { 505 showGuestNotification(currentId); 506 } 507 unpauseRefreshUsers = true; 508 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) { 509 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 510 UserHandle.USER_NULL); 511 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 512 // Unlocking the system user may require a refresh 513 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 514 if (userId != UserHandle.USER_SYSTEM) { 515 return; 516 } 517 } 518 refreshUsers(forcePictureLoadForId); 519 if (unpauseRefreshUsers) { 520 mUnpauseRefreshUsers.run(); 521 } 522 } 523 524 private void showLogoutNotification(int userId) { 525 PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext, 526 0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM); 527 Notification.Builder builder = new Notification.Builder(mContext) 528 .setVisibility(Notification.VISIBILITY_SECRET) 529 .setPriority(Notification.PRIORITY_MIN) 530 .setSmallIcon(R.drawable.ic_person) 531 .setContentTitle(mContext.getString(R.string.user_logout_notification_title)) 532 .setContentText(mContext.getString(R.string.user_logout_notification_text)) 533 .setContentIntent(logoutPI) 534 .setOngoing(true) 535 .setShowWhen(false) 536 .addAction(R.drawable.ic_delete, 537 mContext.getString(R.string.user_logout_notification_action), 538 logoutPI); 539 SystemUI.overrideNotificationAppName(mContext, builder); 540 NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER, ID_LOGOUT_USER, 541 builder.build(), new UserHandle(userId)); 542 } 543 }; 544 545 private void showGuestNotification(int guestUserId) { 546 boolean canSwitchUsers = mUserManager.canSwitchUsers(); 547 // Disable 'Remove guest' action if cannot switch users right now 548 PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext, 549 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null; 550 551 Notification.Builder builder = new Notification.Builder(mContext) 552 .setVisibility(Notification.VISIBILITY_SECRET) 553 .setPriority(Notification.PRIORITY_MIN) 554 .setSmallIcon(R.drawable.ic_person) 555 .setContentTitle(mContext.getString(R.string.guest_notification_title)) 556 .setContentText(mContext.getString(R.string.guest_notification_text)) 557 .setContentIntent(removeGuestPI) 558 .setShowWhen(false) 559 .addAction(R.drawable.ic_delete, 560 mContext.getString(R.string.guest_notification_remove_action), 561 removeGuestPI); 562 SystemUI.overrideNotificationAppName(mContext, builder); 563 NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST, 564 builder.build(), new UserHandle(guestUserId)); 565 } 566 567 private final Runnable mUnpauseRefreshUsers = new Runnable() { 568 @Override 569 public void run() { 570 mHandler.removeCallbacks(this); 571 mPauseRefreshUsers = false; 572 refreshUsers(UserHandle.USER_NULL); 573 } 574 }; 575 576 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 577 public void onChange(boolean selfChange) { 578 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(), 579 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0; 580 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), 581 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; 582 refreshUsers(UserHandle.USER_NULL); 583 }; 584 }; 585 586 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 587 pw.println("UserSwitcherController state:"); 588 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser); 589 pw.print(" mUsers.size="); pw.println(mUsers.size()); 590 for (int i = 0; i < mUsers.size(); i++) { 591 final UserRecord u = mUsers.get(i); 592 pw.print(" "); pw.println(u.toString()); 593 } 594 } 595 596 public String getCurrentUserName(Context context) { 597 if (mUsers.isEmpty()) return null; 598 UserRecord item = mUsers.get(0); 599 if (item == null || item.info == null) return null; 600 if (item.isGuest) return context.getString(R.string.guest_nickname); 601 return item.info.name; 602 } 603 604 public void onDensityOrFontScaleChanged() { 605 refreshUsers(UserHandle.USER_ALL); 606 } 607 608 public static abstract class BaseUserAdapter extends BaseAdapter { 609 610 final UserSwitcherController mController; 611 612 protected BaseUserAdapter(UserSwitcherController controller) { 613 mController = controller; 614 controller.mAdapters.add(new WeakReference<>(this)); 615 } 616 617 @Override 618 public int getCount() { 619 boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing() 620 && mController.mKeyguardMonitor.isSecure() 621 && !mController.mKeyguardMonitor.canSkipBouncer(); 622 if (!secureKeyguardShowing) { 623 return mController.mUsers.size(); 624 } 625 // The lock screen is secure and showing. Filter out restricted records. 626 final int N = mController.mUsers.size(); 627 int count = 0; 628 for (int i = 0; i < N; i++) { 629 if (mController.mUsers.get(i).isRestricted) { 630 break; 631 } else { 632 count++; 633 } 634 } 635 return count; 636 } 637 638 @Override 639 public UserRecord getItem(int position) { 640 return mController.mUsers.get(position); 641 } 642 643 @Override 644 public long getItemId(int position) { 645 return position; 646 } 647 648 public void switchTo(UserRecord record) { 649 mController.switchTo(record); 650 } 651 652 public String getName(Context context, UserRecord item) { 653 if (item.isGuest) { 654 if (item.isCurrent) { 655 return context.getString(R.string.guest_exit_guest); 656 } else { 657 return context.getString( 658 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname); 659 } 660 } else if (item.isAddUser) { 661 return context.getString(R.string.user_add_user); 662 } else { 663 return item.info.name; 664 } 665 } 666 667 public Drawable getDrawable(Context context, UserRecord item) { 668 if (item.isAddUser) { 669 return context.getDrawable(R.drawable.ic_add_circle_qs); 670 } 671 return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true); 672 } 673 674 public void refresh() { 675 mController.refreshUsers(UserHandle.USER_NULL); 676 } 677 } 678 679 private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) { 680 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext, 681 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser()); 682 if (admin != null && !RestrictedLockUtils.hasBaseUserRestriction(mContext, 683 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser())) { 684 record.isDisabledByAdmin = true; 685 record.enforcedAdmin = admin; 686 } else { 687 record.isDisabledByAdmin = false; 688 record.enforcedAdmin = null; 689 } 690 } 691 692 public void startActivity(Intent intent) { 693 mActivityStarter.startActivity(intent, true); 694 } 695 696 public static final class UserRecord { 697 public final UserInfo info; 698 public final Bitmap picture; 699 public final boolean isGuest; 700 public final boolean isCurrent; 701 public final boolean isAddUser; 702 /** If true, the record is only visible to the owner and only when unlocked. */ 703 public final boolean isRestricted; 704 public boolean isDisabledByAdmin; 705 public EnforcedAdmin enforcedAdmin; 706 public boolean isSwitchToEnabled; 707 708 public UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, 709 boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled) { 710 this.info = info; 711 this.picture = picture; 712 this.isGuest = isGuest; 713 this.isCurrent = isCurrent; 714 this.isAddUser = isAddUser; 715 this.isRestricted = isRestricted; 716 this.isSwitchToEnabled = isSwitchToEnabled; 717 } 718 719 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 720 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, 721 isSwitchToEnabled); 722 } 723 724 public int resolveId() { 725 if (isGuest || info == null) { 726 return UserHandle.USER_NULL; 727 } 728 return info.id; 729 } 730 731 public String toString() { 732 StringBuilder sb = new StringBuilder(); 733 sb.append("UserRecord("); 734 if (info != null) { 735 sb.append("name=\"").append(info.name).append("\" id=").append(info.id); 736 } else { 737 if (isGuest) { 738 sb.append("<add guest placeholder>"); 739 } else if (isAddUser) { 740 sb.append("<add user placeholder>"); 741 } 742 } 743 if (isGuest) sb.append(" <isGuest>"); 744 if (isAddUser) sb.append(" <isAddUser>"); 745 if (isCurrent) sb.append(" <isCurrent>"); 746 if (picture != null) sb.append(" <hasPicture>"); 747 if (isRestricted) sb.append(" <isRestricted>"); 748 if (isDisabledByAdmin) { 749 sb.append(" <isDisabledByAdmin>"); 750 sb.append(" enforcedAdmin=").append(enforcedAdmin); 751 } 752 if (isSwitchToEnabled) { 753 sb.append(" <isSwitchToEnabled>"); 754 } 755 sb.append(')'); 756 return sb.toString(); 757 } 758 } 759 760 public final QSTile.DetailAdapter userDetailAdapter = new QSTile.DetailAdapter() { 761 private final Intent USER_SETTINGS_INTENT = new Intent(Settings.ACTION_USER_SETTINGS); 762 763 @Override 764 public CharSequence getTitle() { 765 return mContext.getString(R.string.quick_settings_user_title); 766 } 767 768 @Override 769 public View createDetailView(Context context, View convertView, ViewGroup parent) { 770 UserDetailView v; 771 if (!(convertView instanceof UserDetailView)) { 772 v = UserDetailView.inflate(context, parent, false); 773 v.createAndSetAdapter(UserSwitcherController.this); 774 } else { 775 v = (UserDetailView) convertView; 776 } 777 v.refreshAdapter(); 778 return v; 779 } 780 781 @Override 782 public Intent getSettingsIntent() { 783 return USER_SETTINGS_INTENT; 784 } 785 786 @Override 787 public Boolean getToggleState() { 788 return null; 789 } 790 791 @Override 792 public void setToggleState(boolean state) { 793 } 794 795 @Override 796 public int getMetricsCategory() { 797 return MetricsEvent.QS_USERDETAIL; 798 } 799 }; 800 801 private final KeyguardMonitor.Callback mCallback = new KeyguardMonitor.Callback() { 802 @Override 803 public void onKeyguardChanged() { 804 notifyAdapters(); 805 } 806 }; 807 808 private final class ExitGuestDialog extends SystemUIDialog implements 809 DialogInterface.OnClickListener { 810 811 private final int mGuestId; 812 813 public ExitGuestDialog(Context context, int guestId) { 814 super(context); 815 setTitle(R.string.guest_exit_guest_dialog_title); 816 setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); 817 setButton(DialogInterface.BUTTON_NEGATIVE, 818 context.getString(android.R.string.cancel), this); 819 setButton(DialogInterface.BUTTON_POSITIVE, 820 context.getString(R.string.guest_exit_guest_dialog_remove), this); 821 setCanceledOnTouchOutside(false); 822 mGuestId = guestId; 823 } 824 825 @Override 826 public void onClick(DialogInterface dialog, int which) { 827 if (which == BUTTON_NEGATIVE) { 828 cancel(); 829 } else { 830 dismiss(); 831 exitGuest(mGuestId); 832 } 833 } 834 } 835 836 private final class AddUserDialog extends SystemUIDialog implements 837 DialogInterface.OnClickListener { 838 839 public AddUserDialog(Context context) { 840 super(context); 841 setTitle(R.string.user_add_user_title); 842 setMessage(context.getString(R.string.user_add_user_message_short)); 843 setButton(DialogInterface.BUTTON_NEGATIVE, 844 context.getString(android.R.string.cancel), this); 845 setButton(DialogInterface.BUTTON_POSITIVE, 846 context.getString(android.R.string.ok), this); 847 } 848 849 @Override 850 public void onClick(DialogInterface dialog, int which) { 851 if (which == BUTTON_NEGATIVE) { 852 cancel(); 853 } else { 854 dismiss(); 855 if (ActivityManager.isUserAMonkey()) { 856 return; 857 } 858 UserInfo user = mUserManager.createUser( 859 mContext.getString(R.string.user_new_user_name), 0 /* flags */); 860 if (user == null) { 861 // Couldn't create user, most likely because there are too many, but we haven't 862 // been able to reload the list yet. 863 return; 864 } 865 int id = user.id; 866 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon( 867 id, /* light= */ false)); 868 mUserManager.setUserIcon(id, icon); 869 switchToUserId(id); 870 } 871 } 872 } 873 } 874