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 UserSwitcherController(Context context, KeyguardMonitor keyguardMonitor, Handler handler, ActivityStarter activityStarter)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") refreshUsers(int forcePictureLoadForId)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 pauseRefreshUsers()283 private void pauseRefreshUsers() { 284 if (!mPauseRefreshUsers) { 285 mHandler.postDelayed(mUnpauseRefreshUsers, PAUSE_REFRESH_USERS_TIMEOUT_MS); 286 mPauseRefreshUsers = true; 287 } 288 } 289 notifyAdapters()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 isSimpleUserSwitcher()301 public boolean isSimpleUserSwitcher() { 302 return mSimpleUserSwitcher; 303 } 304 useFullscreenUserSwitcher()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 logoutCurrentUser()321 public void logoutCurrentUser() { 322 int currentUser = ActivityManager.getCurrentUser(); 323 if (currentUser != UserHandle.USER_SYSTEM) { 324 pauseRefreshUsers(); 325 ActivityManager.logoutCurrentUser(); 326 } 327 } 328 removeUserId(int userId)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 switchTo(UserRecord record)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 switchTo(int userId)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 getSwitchableUserCount()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 switchToUserId(int id)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 showExitGuestDialog(int id)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 showAddUserDialog()413 private void showAddUserDialog() { 414 if (mAddUserDialog != null && mAddUserDialog.isShowing()) { 415 mAddUserDialog.cancel(); 416 } 417 mAddUserDialog = new AddUserDialog(mContext); 418 mAddUserDialog.show(); 419 } 420 exitGuest(int id)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 listenForCallState()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 showGuestNotification(int guestUserId)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 dump(FileDescriptor fd, PrintWriter pw, String[] args)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 getCurrentUserName(Context context)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 onDensityOrFontScaleChanged()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 BaseUserAdapter(UserSwitcherController controller)628 protected BaseUserAdapter(UserSwitcherController controller) { 629 mController = controller; 630 controller.mAdapters.add(new WeakReference<>(this)); 631 } 632 633 @Override getCount()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 getItem(int position)655 public UserRecord getItem(int position) { 656 return mController.mUsers.get(position); 657 } 658 659 @Override getItemId(int position)660 public long getItemId(int position) { 661 return position; 662 } 663 switchTo(UserRecord record)664 public void switchTo(UserRecord record) { 665 mController.switchTo(record); 666 } 667 getName(Context context, UserRecord item)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 getDrawable(Context context, UserRecord item)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 refresh()690 public void refresh() { 691 mController.refreshUsers(UserHandle.USER_NULL); 692 } 693 } 694 checkIfAddUserDisallowedByAdminOnly(UserRecord record)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 startActivity(Intent intent)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 UserRecord(UserInfo info, Bitmap picture, boolean isGuest, boolean isCurrent, boolean isAddUser, boolean isRestricted, boolean isSwitchToEnabled)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 copyWithIsCurrent(boolean _isCurrent)735 public UserRecord copyWithIsCurrent(boolean _isCurrent) { 736 return new UserRecord(info, picture, isGuest, _isCurrent, isAddUser, isRestricted, 737 isSwitchToEnabled); 738 } 739 resolveId()740 public int resolveId() { 741 if (isGuest || info == null) { 742 return UserHandle.USER_NULL; 743 } 744 return info.id; 745 } 746 toString()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 ExitGuestDialog(Context context, int guestId)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 onClick(DialogInterface dialog, int which)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 AddUserDialog(Context context)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 onClick(DialogInterface dialog, int which)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