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 public int getSwitchableUserCount() { 385 int count = 0; 386 final int N = mUsers.size(); 387 for (int i = 0; i < N; ++i) { 388 UserRecord record = mUsers.get(i); 389 if (record.info != null && record.info.supportsSwitchTo()) { 390 count++; 391 } 392 } 393 return count; 394 } 395 396 private void switchToUserId(int id) { 397 try { 398 pauseRefreshUsers(); 399 ActivityManagerNative.getDefault().switchUser(id); 400 } catch (RemoteException e) { 401 Log.e(TAG, "Couldn't switch user.", e); 402 } 403 } 404 405 private void showExitGuestDialog(int id) { 406 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 407 mExitGuestDialog.cancel(); 408 } 409 mExitGuestDialog = new ExitGuestDialog(mContext, id); 410 mExitGuestDialog.show(); 411 } 412 413 private void showAddUserDialog() { 414 if (mAddUserDialog != null && mAddUserDialog.isShowing()) { 415 mAddUserDialog.cancel(); 416 } 417 mAddUserDialog = new AddUserDialog(mContext); 418 mAddUserDialog.show(); 419 } 420 421 private void exitGuest(int id) { 422 int newId = UserHandle.USER_SYSTEM; 423 if (mLastNonGuestUser != UserHandle.USER_SYSTEM) { 424 UserInfo info = mUserManager.getUserInfo(mLastNonGuestUser); 425 if (info != null && info.isEnabled() && info.supportsSwitchToByUser()) { 426 newId = info.id; 427 } 428 } 429 switchToUserId(newId); 430 mUserManager.removeUser(id); 431 } 432 433 private void listenForCallState() { 434 TelephonyManager.from(mContext).listen(mPhoneStateListener, 435 PhoneStateListener.LISTEN_CALL_STATE); 436 } 437 438 private final PhoneStateListener mPhoneStateListener = new PhoneStateListener() { 439 private int mCallState; 440 441 @Override 442 public void onCallStateChanged(int state, String incomingNumber) { 443 if (mCallState == state) return; 444 if (DEBUG) Log.v(TAG, "Call state changed: " + state); 445 mCallState = state; 446 int currentUserId = ActivityManager.getCurrentUser(); 447 UserInfo userInfo = mUserManager.getUserInfo(currentUserId); 448 if (userInfo != null && userInfo.isGuest()) { 449 showGuestNotification(currentUserId); 450 } 451 refreshUsers(UserHandle.USER_NULL); 452 } 453 }; 454 455 private BroadcastReceiver mReceiver = new BroadcastReceiver() { 456 @Override 457 public void onReceive(Context context, Intent intent) { 458 if (DEBUG) { 459 Log.v(TAG, "Broadcast: a=" + intent.getAction() 460 + " user=" + intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1)); 461 } 462 463 boolean unpauseRefreshUsers = false; 464 int forcePictureLoadForId = UserHandle.USER_NULL; 465 466 if (ACTION_REMOVE_GUEST.equals(intent.getAction())) { 467 int currentUser = ActivityManager.getCurrentUser(); 468 UserInfo userInfo = mUserManager.getUserInfo(currentUser); 469 if (userInfo != null && userInfo.isGuest()) { 470 showExitGuestDialog(currentUser); 471 } 472 return; 473 } else if (ACTION_LOGOUT_USER.equals(intent.getAction())) { 474 logoutCurrentUser(); 475 } else if (Intent.ACTION_USER_SWITCHED.equals(intent.getAction())) { 476 if (mExitGuestDialog != null && mExitGuestDialog.isShowing()) { 477 mExitGuestDialog.cancel(); 478 mExitGuestDialog = null; 479 } 480 481 final int currentId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, -1); 482 final UserInfo userInfo = mUserManager.getUserInfo(currentId); 483 final int N = mUsers.size(); 484 for (int i = 0; i < N; i++) { 485 UserRecord record = mUsers.get(i); 486 if (record.info == null) continue; 487 boolean shouldBeCurrent = record.info.id == currentId; 488 if (record.isCurrent != shouldBeCurrent) { 489 mUsers.set(i, record.copyWithIsCurrent(shouldBeCurrent)); 490 } 491 if (shouldBeCurrent && !record.isGuest) { 492 mLastNonGuestUser = record.info.id; 493 } 494 if ((userInfo == null || !userInfo.isAdmin()) && record.isRestricted) { 495 // Immediately remove restricted records in case the AsyncTask is too slow. 496 mUsers.remove(i); 497 i--; 498 } 499 } 500 notifyAdapters(); 501 502 // Disconnect from the old secondary user's service 503 if (mSecondaryUser != UserHandle.USER_NULL) { 504 context.stopServiceAsUser(mSecondaryUserServiceIntent, 505 UserHandle.of(mSecondaryUser)); 506 mSecondaryUser = UserHandle.USER_NULL; 507 } 508 // Connect to the new secondary user's service (purely to ensure that a persistent 509 // SystemUI application is created for that user) 510 if (userInfo != null && !userInfo.isPrimary()) { 511 context.startServiceAsUser(mSecondaryUserServiceIntent, 512 UserHandle.of(userInfo.id)); 513 mSecondaryUser = userInfo.id; 514 } 515 516 if (UserManager.isSplitSystemUser() && userInfo != null && !userInfo.isGuest() 517 && userInfo.id != UserHandle.USER_SYSTEM) { 518 showLogoutNotification(currentId); 519 } 520 if (userInfo != null && userInfo.isGuest()) { 521 showGuestNotification(currentId); 522 } 523 unpauseRefreshUsers = true; 524 } else if (Intent.ACTION_USER_INFO_CHANGED.equals(intent.getAction())) { 525 forcePictureLoadForId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, 526 UserHandle.USER_NULL); 527 } else if (Intent.ACTION_USER_UNLOCKED.equals(intent.getAction())) { 528 // Unlocking the system user may require a refresh 529 int userId = intent.getIntExtra(Intent.EXTRA_USER_HANDLE, UserHandle.USER_NULL); 530 if (userId != UserHandle.USER_SYSTEM) { 531 return; 532 } 533 } 534 refreshUsers(forcePictureLoadForId); 535 if (unpauseRefreshUsers) { 536 mUnpauseRefreshUsers.run(); 537 } 538 } 539 540 private void showLogoutNotification(int userId) { 541 PendingIntent logoutPI = PendingIntent.getBroadcastAsUser(mContext, 542 0, new Intent(ACTION_LOGOUT_USER), 0, UserHandle.SYSTEM); 543 Notification.Builder builder = new Notification.Builder(mContext) 544 .setVisibility(Notification.VISIBILITY_SECRET) 545 .setPriority(Notification.PRIORITY_MIN) 546 .setSmallIcon(R.drawable.ic_person) 547 .setContentTitle(mContext.getString(R.string.user_logout_notification_title)) 548 .setContentText(mContext.getString(R.string.user_logout_notification_text)) 549 .setContentIntent(logoutPI) 550 .setOngoing(true) 551 .setShowWhen(false) 552 .addAction(R.drawable.ic_delete, 553 mContext.getString(R.string.user_logout_notification_action), 554 logoutPI); 555 SystemUI.overrideNotificationAppName(mContext, builder); 556 NotificationManager.from(mContext).notifyAsUser(TAG_LOGOUT_USER, ID_LOGOUT_USER, 557 builder.build(), new UserHandle(userId)); 558 } 559 }; 560 561 private void showGuestNotification(int guestUserId) { 562 boolean canSwitchUsers = mUserManager.canSwitchUsers(); 563 // Disable 'Remove guest' action if cannot switch users right now 564 PendingIntent removeGuestPI = canSwitchUsers ? PendingIntent.getBroadcastAsUser(mContext, 565 0, new Intent(ACTION_REMOVE_GUEST), 0, UserHandle.SYSTEM) : null; 566 567 Notification.Builder builder = new Notification.Builder(mContext) 568 .setVisibility(Notification.VISIBILITY_SECRET) 569 .setPriority(Notification.PRIORITY_MIN) 570 .setSmallIcon(R.drawable.ic_person) 571 .setContentTitle(mContext.getString(R.string.guest_notification_title)) 572 .setContentText(mContext.getString(R.string.guest_notification_text)) 573 .setContentIntent(removeGuestPI) 574 .setShowWhen(false) 575 .addAction(R.drawable.ic_delete, 576 mContext.getString(R.string.guest_notification_remove_action), 577 removeGuestPI); 578 SystemUI.overrideNotificationAppName(mContext, builder); 579 NotificationManager.from(mContext).notifyAsUser(TAG_REMOVE_GUEST, ID_REMOVE_GUEST, 580 builder.build(), new UserHandle(guestUserId)); 581 } 582 583 private final Runnable mUnpauseRefreshUsers = new Runnable() { 584 @Override 585 public void run() { 586 mHandler.removeCallbacks(this); 587 mPauseRefreshUsers = false; 588 refreshUsers(UserHandle.USER_NULL); 589 } 590 }; 591 592 private final ContentObserver mSettingsObserver = new ContentObserver(new Handler()) { 593 public void onChange(boolean selfChange) { 594 mSimpleUserSwitcher = Settings.Global.getInt(mContext.getContentResolver(), 595 SIMPLE_USER_SWITCHER_GLOBAL_SETTING, 0) != 0; 596 mAddUsersWhenLocked = Settings.Global.getInt(mContext.getContentResolver(), 597 Settings.Global.ADD_USERS_WHEN_LOCKED, 0) != 0; 598 refreshUsers(UserHandle.USER_NULL); 599 }; 600 }; 601 602 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) { 603 pw.println("UserSwitcherController state:"); 604 pw.println(" mLastNonGuestUser=" + mLastNonGuestUser); 605 pw.print(" mUsers.size="); pw.println(mUsers.size()); 606 for (int i = 0; i < mUsers.size(); i++) { 607 final UserRecord u = mUsers.get(i); 608 pw.print(" "); pw.println(u.toString()); 609 } 610 } 611 612 public String getCurrentUserName(Context context) { 613 if (mUsers.isEmpty()) return null; 614 UserRecord item = mUsers.get(0); 615 if (item == null || item.info == null) return null; 616 if (item.isGuest) return context.getString(R.string.guest_nickname); 617 return item.info.name; 618 } 619 620 public void onDensityOrFontScaleChanged() { 621 refreshUsers(UserHandle.USER_ALL); 622 } 623 624 public static abstract class BaseUserAdapter extends BaseAdapter { 625 626 final UserSwitcherController mController; 627 628 protected BaseUserAdapter(UserSwitcherController controller) { 629 mController = controller; 630 controller.mAdapters.add(new WeakReference<>(this)); 631 } 632 633 @Override 634 public int getCount() { 635 boolean secureKeyguardShowing = mController.mKeyguardMonitor.isShowing() 636 && mController.mKeyguardMonitor.isSecure() 637 && !mController.mKeyguardMonitor.canSkipBouncer(); 638 if (!secureKeyguardShowing) { 639 return mController.mUsers.size(); 640 } 641 // The lock screen is secure and showing. Filter out restricted records. 642 final int N = mController.mUsers.size(); 643 int count = 0; 644 for (int i = 0; i < N; i++) { 645 if (mController.mUsers.get(i).isRestricted) { 646 break; 647 } else { 648 count++; 649 } 650 } 651 return count; 652 } 653 654 @Override 655 public UserRecord getItem(int position) { 656 return mController.mUsers.get(position); 657 } 658 659 @Override 660 public long getItemId(int position) { 661 return position; 662 } 663 664 public void switchTo(UserRecord record) { 665 mController.switchTo(record); 666 } 667 668 public String getName(Context context, UserRecord item) { 669 if (item.isGuest) { 670 if (item.isCurrent) { 671 return context.getString(R.string.guest_exit_guest); 672 } else { 673 return context.getString( 674 item.info == null ? R.string.guest_new_guest : R.string.guest_nickname); 675 } 676 } else if (item.isAddUser) { 677 return context.getString(R.string.user_add_user); 678 } else { 679 return item.info.name; 680 } 681 } 682 683 public Drawable getDrawable(Context context, UserRecord item) { 684 if (item.isAddUser) { 685 return context.getDrawable(R.drawable.ic_add_circle_qs); 686 } 687 return UserIcons.getDefaultUserIcon(item.resolveId(), /* light= */ true); 688 } 689 690 public void refresh() { 691 mController.refreshUsers(UserHandle.USER_NULL); 692 } 693 } 694 695 private void checkIfAddUserDisallowedByAdminOnly(UserRecord record) { 696 EnforcedAdmin admin = RestrictedLockUtils.checkIfRestrictionEnforced(mContext, 697 UserManager.DISALLOW_ADD_USER, ActivityManager.getCurrentUser()); 698 if (admin != null && !RestrictedLockUtils.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 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 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 735 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 736 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, 737 isSwitchToEnabled); 738 } 739 740 public int resolveId() { 741 if (isGuest || info == null) { 742 return UserHandle.USER_NULL; 743 } 744 return info.id; 745 } 746 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 QSTile.DetailAdapter userDetailAdapter = new QSTile.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 onKeyguardChanged() { 820 notifyAdapters(); 821 } 822 }; 823 824 private final class ExitGuestDialog extends SystemUIDialog implements 825 DialogInterface.OnClickListener { 826 827 private final int mGuestId; 828 829 public ExitGuestDialog(Context context, int guestId) { 830 super(context); 831 setTitle(R.string.guest_exit_guest_dialog_title); 832 setMessage(context.getString(R.string.guest_exit_guest_dialog_message)); 833 setButton(DialogInterface.BUTTON_NEGATIVE, 834 context.getString(android.R.string.cancel), this); 835 setButton(DialogInterface.BUTTON_POSITIVE, 836 context.getString(R.string.guest_exit_guest_dialog_remove), this); 837 setCanceledOnTouchOutside(false); 838 mGuestId = guestId; 839 } 840 841 @Override 842 public void onClick(DialogInterface dialog, int which) { 843 if (which == BUTTON_NEGATIVE) { 844 cancel(); 845 } else { 846 dismiss(); 847 exitGuest(mGuestId); 848 } 849 } 850 } 851 852 private final class AddUserDialog extends SystemUIDialog implements 853 DialogInterface.OnClickListener { 854 855 public AddUserDialog(Context context) { 856 super(context); 857 setTitle(R.string.user_add_user_title); 858 setMessage(context.getString(R.string.user_add_user_message_short)); 859 setButton(DialogInterface.BUTTON_NEGATIVE, 860 context.getString(android.R.string.cancel), this); 861 setButton(DialogInterface.BUTTON_POSITIVE, 862 context.getString(android.R.string.ok), this); 863 } 864 865 @Override 866 public void onClick(DialogInterface dialog, int which) { 867 if (which == BUTTON_NEGATIVE) { 868 cancel(); 869 } else { 870 dismiss(); 871 if (ActivityManager.isUserAMonkey()) { 872 return; 873 } 874 UserInfo user = mUserManager.createUser( 875 mContext.getString(R.string.user_new_user_name), 0 /* flags */); 876 if (user == null) { 877 // Couldn't create user, most likely because there are too many, but we haven't 878 // been able to reload the list yet. 879 return; 880 } 881 int id = user.id; 882 Bitmap icon = UserIcons.convertToBitmap(UserIcons.getDefaultUserIcon( 883 id, /* light= */ false)); 884 mUserManager.setUserIcon(id, icon); 885 switchToUserId(id); 886 } 887 } 888 } 889 } 890