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