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