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.messages.SystemMessageProto.SystemMessage; 51 import com.android.internal.util.UserIcons; 52 import com.android.settingslib.RestrictedLockUtils; 53 import com.android.systemui.GuestResumeSessionReceiver; 54 import com.android.systemui.R; 55 import com.android.systemui.SystemUI; 56 import com.android.systemui.SystemUISecondaryUserService; 57 import com.android.systemui.qs.QSTile; 58 import com.android.systemui.qs.tiles.UserDetailView; 59 import com.android.systemui.statusbar.phone.ActivityStarter; 60 import com.android.systemui.statusbar.phone.SystemUIDialog; 61 62 import java.io.FileDescriptor; 63 import java.io.PrintWriter; 64 import java.lang.ref.WeakReference; 65 import java.util.ArrayList; 66 import java.util.List; 67 68 import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin; 69 70 /** 71 * Keeps a list of all users on the device for user switching. 72 */ 73 public 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 UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, Handler handler, ActivityStarter activityStarter)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") refreshUsers(int forcePictureLoadForId)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 pauseRefreshUsers()282 private void pauseRefreshUsers() { 283 if (!mPauseRefreshUsers) { 284 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS); 285 mPauseRefreshUsers = true; 286 } 287 } 288 notifyAdapters()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 isSimpleUserSwitcher()300 public boolean isSimpleUserSwitcher() { 301 return mSimpleUserSwitcher; 302 } 303 useFullscreenUserSwitcher()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 logoutCurrentUser()320 public void logoutCurrentUser() { 321 int currentUser = ActivityManager.getCurrentUser(); 322 if (currentUser != UserHandle.USER_SYSTEM) { 323 pauseRefreshUsers(); 324 ActivityManager.logoutCurrentUser(); 325 } 326 } 327 removeUserId(int userId)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 switchTo(UserRecord record)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 switchTo(int userId)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 getSwitchableUserCount()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 switchToUserId(int id)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 showExitGuestDialog(int id)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 showAddUserDialog()412 private void showAddUserDialog() { 413 if (mAddUserDialog != null && mAddUserDialog.isShowing()) { 414 mAddUserDialog.cancel(); 415 } 416 mAddUserDialog = new AddUserDialog(mContext); 417 mAddUserDialog.show(); 418 } 419 exitGuest(int id)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 listenForCallState()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 showGuestNotification(int guestUserId)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 dump(FileDescriptor fd, PrintWriter pw, String[] args)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 getCurrentUserName(Context context)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 onDensityOrFontScaleChanged()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 BaseUserAdapter(UserSwitcherController controller)627 protected BaseUserAdapter(UserSwitcherController controller) { 628 mController = controller; 629 controller.mAdapters.add(new WeakReference<>(this)); 630 } 631 632 @Override getCount()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 getItem(int position)654 public UserRecord getItem(int position) { 655 return mController.mUsers.get(position); 656 } 657 658 @Override getItemId(int position)659 public long getItemId(int position) { 660 return position; 661 } 662 switchTo(UserRecord record)663 public void switchTo(UserRecord record) { 664 mController.switchTo(record); 665 } 666 getName(Context context, UserRecord item)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 getDrawable(Context context, UserRecord item)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 refresh()689 public void refresh() { 690 mController.refreshUsers(UserHandle.USER_NULL); 691 } 692 } 693 checkIfAddUserDisallowedByAdminOnly(UserRecord record)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 startActivity(Intent intent)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 UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled)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 copyWithIsCurrent(boolean _isCurrent)734 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 735 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, 736 isSwitchToEnabled); 737 } 738 resolveId()739 public int resolveId() { 740 if (isGuest || info == null) { 741 return UserHandle.USER_NULL; 742 } 743 return info.id; 744 } 745 toString()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 ExitGuestDialog(Context context, int guestId)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 onClick(DialogInterface dialog, int which)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 AddUserDialog(Context context)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 onClick(DialogInterface dialog, int which)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