1 /* 2 * Copyright (C) 2023 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.car.userpicker; 18 19 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_CREATED; 20 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_INVISIBLE; 21 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_REMOVED; 22 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STARTING; 23 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPED; 24 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_STOPPING; 25 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 26 import static android.car.user.CarUserManager.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 27 import static android.car.user.CarUserManager.lifecycleEventTypeToString; 28 import static android.os.UserHandle.USER_ALL; 29 import static android.os.UserHandle.USER_SYSTEM; 30 import static android.os.UserManager.SWITCHABILITY_STATUS_OK; 31 import static android.os.UserManager.isHeadlessSystemUserMode; 32 33 import android.annotation.MainThread; 34 import android.annotation.Nullable; 35 import android.annotation.UserIdInt; 36 import android.app.ActivityManager; 37 import android.car.SyncResultCallback; 38 import android.car.user.CarUserManager; 39 import android.car.user.CarUserManager.UserLifecycleListener; 40 import android.car.user.UserCreationResult; 41 import android.car.user.UserLifecycleEventFilter; 42 import android.car.user.UserStartRequest; 43 import android.car.user.UserStartResponse; 44 import android.car.user.UserStopRequest; 45 import android.car.user.UserStopResponse; 46 import android.car.user.UserSwitchRequest; 47 import android.car.user.UserSwitchResult; 48 import android.car.util.concurrent.AsyncFuture; 49 import android.content.BroadcastReceiver; 50 import android.content.Context; 51 import android.content.Intent; 52 import android.content.IntentFilter; 53 import android.content.pm.UserInfo; 54 import android.os.Handler; 55 import android.os.Looper; 56 import android.os.UserHandle; 57 import android.os.UserManager; 58 import android.util.Log; 59 import android.util.Slog; 60 import android.util.SparseArray; 61 62 import androidx.annotation.VisibleForTesting; 63 64 import com.android.systemui.R; 65 66 import java.util.List; 67 import java.util.concurrent.CountDownLatch; 68 import java.util.concurrent.ExecutorService; 69 import java.util.concurrent.Executors; 70 import java.util.concurrent.TimeUnit; 71 72 import javax.inject.Inject; 73 74 /** 75 * Helper class for {@link UserManager}, this is meant to be used by builds that support 76 * {@link UserManager#isVisibleBackgroundUsersEnabled() Multi-user model with Concurrent Multi 77 * User Feature.} 78 * 79 * <p>This class handles user event such as creating, removing, unlocking, stopped, and so on. 80 * Also, it provides methods for creating, stopping, starting users. 81 */ 82 @UserPickerScope 83 public final class UserEventManager { 84 private static final String TAG = UserEventManager.class.getSimpleName(); 85 private static final boolean DEBUG = Log.isLoggable(TAG, Log.DEBUG); 86 87 private static final long USER_TIMEOUT_MS = 10_000; 88 89 private final UserLifecycleEventFilter mFilter = new UserLifecycleEventFilter.Builder() 90 .addEventType(USER_LIFECYCLE_EVENT_TYPE_SWITCHING) 91 .addEventType(USER_LIFECYCLE_EVENT_TYPE_INVISIBLE) 92 .addEventType(USER_LIFECYCLE_EVENT_TYPE_CREATED) 93 .addEventType(USER_LIFECYCLE_EVENT_TYPE_REMOVED) 94 .addEventType(USER_LIFECYCLE_EVENT_TYPE_UNLOCKED) 95 .addEventType(USER_LIFECYCLE_EVENT_TYPE_STARTING) 96 .addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPING) 97 .addEventType(USER_LIFECYCLE_EVENT_TYPE_STOPPED).build(); 98 99 private final Context mContext; 100 private final UserManager mUserManager; 101 private final CarServiceMediator mCarServiceMediator; 102 private final UserPickerSharedState mUserPickerSharedState; 103 104 /** 105 * {@link UserPickerController} is per-display object. It adds listener to UserEventManager to 106 * update user information, and UserEventManager will call listeners whenever user event occurs. 107 * mUpdateListeners is used only on main thread. 108 */ 109 private final SparseArray<OnUpdateUsersListener> mUpdateListeners; 110 111 private final Handler mMainHandler; 112 113 /** 114 * This is used to wait until previous user is in invisible state. 115 * When changing user, previous user is stopped, and new user is started. But new user can not 116 * be started if occupant zone is not unassigned for previous user yet, and occupant zone 117 * unassignment is processed on user invisible event. In this reason, we should wait until 118 * previous user is in invisible state for stable user starting. <b/275973135> 119 */ 120 private final UserInvisibleWaiter mUserInvisibleWaiter = new UserInvisibleWaiter(); 121 122 /** 123 * We don't use the main thread for UX responsiveness when handling user events. 124 */ 125 private final ExecutorService mUserLifecycleReceiver; 126 127 @VisibleForTesting 128 final UserLifecycleListener mUserLifecycleListener = event -> { 129 onUserEvent(event); 130 }; 131 132 private final BroadcastReceiver mUserUpdateReceiver = new BroadcastReceiver() { 133 @Override 134 public void onReceive(Context context, Intent intent) { 135 runUpdateUsersOnMainThread(); 136 } 137 }; 138 139 @Inject UserEventManager(Context context, CarServiceMediator carServiceMediator, UserPickerSharedState userPickerSharedState)140 UserEventManager(Context context, CarServiceMediator carServiceMediator, 141 UserPickerSharedState userPickerSharedState) { 142 mUpdateListeners = new SparseArray<>(); 143 mContext = context.getApplicationContext(); 144 mUserLifecycleReceiver = Executors.newSingleThreadExecutor(); 145 mMainHandler = new Handler(Looper.getMainLooper()); 146 mUserManager = mContext.getSystemService(UserManager.class); 147 mUserPickerSharedState = userPickerSharedState; 148 mCarServiceMediator = carServiceMediator; 149 mCarServiceMediator.registerUserChangeEventsListener(mUserLifecycleReceiver, mFilter, 150 mUserLifecycleListener); 151 registerUserInfoChangedReceiver(); 152 } 153 154 /** 155 * Unregisters all the listeners when the owners is being destroyed 156 */ onDestroy()157 void onDestroy() { 158 mCarServiceMediator.onDestroy(); 159 mContext.unregisterReceiver(mUserUpdateReceiver); 160 } 161 onUserEvent(CarUserManager.UserLifecycleEvent event)162 private void onUserEvent(CarUserManager.UserLifecycleEvent event) { 163 int eventType = event.getEventType(); 164 int userId = event.getUserId(); 165 if (DEBUG) { 166 Slog.d(TAG, "event=" + lifecycleEventTypeToString(eventType) + " userId=" + userId); 167 } 168 if (eventType == USER_LIFECYCLE_EVENT_TYPE_STOPPING) { 169 mUserPickerSharedState.addStoppingUserId(userId); 170 } else if (eventType == USER_LIFECYCLE_EVENT_TYPE_STOPPED) { 171 if (mUserPickerSharedState.isStoppingUser(userId)) { 172 mUserPickerSharedState.removeStoppingUserId(userId); 173 } 174 } else if (eventType == USER_LIFECYCLE_EVENT_TYPE_INVISIBLE) { 175 mUserInvisibleWaiter.onUserInvisible(userId); 176 } 177 runUpdateUsersOnMainThread(userId, eventType); 178 } 179 registerUserInfoChangedReceiver()180 private void registerUserInfoChangedReceiver() { 181 IntentFilter filter = new IntentFilter(); 182 filter.addAction(Intent.ACTION_USER_INFO_CHANGED); 183 mContext.registerReceiverAsUser(mUserUpdateReceiver, UserHandle.ALL, filter, null, null); 184 } 185 registerOnUpdateUsersListener(OnUpdateUsersListener listener, int displayId)186 void registerOnUpdateUsersListener(OnUpdateUsersListener listener, int displayId) { 187 if (listener == null) { 188 return; 189 } 190 mUpdateListeners.put(displayId, listener); 191 } 192 unregisterOnUpdateUsersListener(int displayId)193 void unregisterOnUpdateUsersListener(int displayId) { 194 mUpdateListeners.remove(displayId); 195 } 196 197 @MainThread updateUsers(@serIdInt int userId, int userEvent)198 private void updateUsers(@UserIdInt int userId, int userEvent) { 199 for (int i = 0; i < mUpdateListeners.size(); i++) { 200 OnUpdateUsersListener listener = mUpdateListeners.valueAt(i); 201 if (listener != null) { 202 listener.onUpdateUsers(userId, userEvent); 203 } 204 } 205 } 206 runUpdateUsersOnMainThread()207 void runUpdateUsersOnMainThread() { 208 runUpdateUsersOnMainThread(USER_ALL, 0); 209 } 210 runUpdateUsersOnMainThread(@serIdInt int userId, int userEvent)211 void runUpdateUsersOnMainThread(@UserIdInt int userId, int userEvent) { 212 if (Looper.myLooper() != Looper.getMainLooper()) { 213 mMainHandler.post(() -> updateUsers(userId, userEvent)); 214 } else { 215 updateUsers(userId, userEvent); 216 } 217 } 218 getMaxSupportedUsers()219 static int getMaxSupportedUsers() { 220 int maxSupportedUsers = UserManager.getMaxSupportedUsers(); 221 if (isHeadlessSystemUserMode()) { 222 maxSupportedUsers -= 1; 223 } 224 return maxSupportedUsers; 225 } 226 getUserInfo(@serIdInt int userId)227 UserInfo getUserInfo(@UserIdInt int userId) { 228 return mUserManager.getUserInfo(userId); 229 } 230 getCurrentForegroundUserInfo()231 UserInfo getCurrentForegroundUserInfo() { 232 return mUserManager.getUserInfo(ActivityManager.getCurrentUser()); 233 } 234 235 /** 236 * Gets alive users from user manager except guest users to create user records. 237 * If it is headless system user mode, removes system user info from the list by 238 * {@link UserManager#getAliveUsers}. 239 * 240 * @return the list of users that were created except guest users. 241 */ getAliveUsers()242 List<UserInfo> getAliveUsers() { 243 List<UserInfo> aliveUsers = mUserManager.getAliveUsers(); 244 for (int i = aliveUsers.size() - 1; i >= 0; i--) { 245 UserInfo userInfo = aliveUsers.get(i); 246 if ((isHeadlessSystemUserMode() && userInfo.id == USER_SYSTEM) 247 || userInfo.isGuest()) { 248 aliveUsers.remove(i); 249 } 250 } 251 return aliveUsers; 252 } 253 isUserLimitReached()254 boolean isUserLimitReached() { 255 int countNonGuestUsers = getAliveUsers().size(); 256 int maxSupportedUsers = getMaxSupportedUsers(); 257 258 if (countNonGuestUsers > maxSupportedUsers) { 259 Slog.e(TAG, "There are more users on the device than allowed."); 260 return true; 261 } 262 return countNonGuestUsers == maxSupportedUsers; 263 } 264 canForegroundUserAddUsers()265 boolean canForegroundUserAddUsers() { 266 return !mUserManager.hasUserRestrictionForUser(UserManager.DISALLOW_ADD_USER, 267 UserHandle.of(ActivityManager.getCurrentUser())); 268 } 269 isForegroundUserNotSwitchable(UserHandle fgUserHandle)270 boolean isForegroundUserNotSwitchable(UserHandle fgUserHandle) { 271 return mUserManager.getUserSwitchability(fgUserHandle) != SWITCHABILITY_STATUS_OK; 272 } 273 274 @Nullable createNewUser()275 UserCreationResult createNewUser() { 276 CarUserManager carUserManager = mCarServiceMediator.getCarUserManager(); 277 AsyncFuture<UserCreationResult> future = carUserManager.createUser( 278 mContext.getString(R.string.car_new_user), 0); 279 return getUserCreationResult(future); 280 } 281 282 @Nullable createGuest()283 UserCreationResult createGuest() { 284 CarUserManager carUserManager = mCarServiceMediator.getCarUserManager(); 285 AsyncFuture<UserCreationResult> future = carUserManager.createGuest( 286 mContext.getString(R.string.car_guest)); 287 return getUserCreationResult(future); 288 } 289 290 @Nullable getUserCreationResult(AsyncFuture<UserCreationResult> future)291 private UserCreationResult getUserCreationResult(AsyncFuture<UserCreationResult> future) { 292 UserCreationResult result = null; 293 try { 294 result = future.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS); 295 if (result == null) { 296 Slog.e(TAG, "Timed out creating guest after " + USER_TIMEOUT_MS + "ms..."); 297 return null; 298 } 299 } catch (InterruptedException e) { 300 Slog.w(TAG, "Interrupted waiting for future " + future, e); 301 Thread.currentThread().interrupt(); 302 return null; 303 } catch (Exception e) { 304 Slog.w(TAG, "Exception getting future " + future, e); 305 return null; 306 } 307 308 return result; 309 } 310 isUserRunningUnlocked(@serIdInt int userId)311 boolean isUserRunningUnlocked(@UserIdInt int userId) { 312 return mUserManager.isUserRunning(userId) && mUserManager.isUserUnlocked(userId); 313 } 314 isUserRunning(@serIdInt int userId)315 boolean isUserRunning(@UserIdInt int userId) { 316 return mUserManager.isUserRunning(userId); 317 } 318 startUserForDisplay(@serIdInt int prevCurrentUser, @UserIdInt int userId, int displayId, boolean isFgUserStart)319 boolean startUserForDisplay(@UserIdInt int prevCurrentUser, @UserIdInt int userId, 320 int displayId, boolean isFgUserStart) { 321 if (DEBUG) { 322 Slog.d(TAG, "switchToUserForDisplay " + userId + " State : Running " 323 + mUserManager.isUserRunning(userId) + " Unlocked " 324 + mUserManager.isUserUnlocked(userId) + " displayId=" + displayId 325 + " prevCurrentUser=" + prevCurrentUser + " isFgUserStart=" + isFgUserStart); 326 } 327 UserHandle userHandle = UserHandle.of(userId); 328 CarUserManager carUserManager = mCarServiceMediator.getCarUserManager(); 329 if (carUserManager == null) { 330 Slog.w(TAG, "car user manager is not available when starting user " + userId); 331 return false; 332 } 333 if (isFgUserStart) { 334 // Old user will be stopped by {@link UserController} after user switching 335 // completed. In the case of user switching, to avoid clicking stopping user, we can 336 // block previous current user immediately here by adding to the list of stopping 337 // users. 338 mUserPickerSharedState.addStoppingUserId(prevCurrentUser); 339 try { 340 SyncResultCallback<UserSwitchResult> userSwitchCallback = 341 new SyncResultCallback<>(); 342 carUserManager.switchUser(new UserSwitchRequest.Builder( 343 userHandle).build(), Runnable::run, userSwitchCallback); 344 UserSwitchResult userSwitchResult = 345 userSwitchCallback.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS); 346 if (userSwitchResult.isSuccess()) { 347 Slog.i(TAG, "Successful switchUser from " + prevCurrentUser + " to " + userId 348 + ". Result: " + userSwitchResult); 349 return true; 350 } 351 Slog.w(TAG, "Failed to switchUser from " + prevCurrentUser + " to " + userId 352 + ". Result: " + userSwitchResult); 353 } catch (Exception e) { 354 Slog.e(TAG, "Exception during switchUser from " + prevCurrentUser + " to " 355 + userId, e); 356 return false; 357 } 358 } 359 360 try { 361 SyncResultCallback<UserStartResponse> userStartCallback = new SyncResultCallback<>(); 362 carUserManager.startUser( 363 new UserStartRequest.Builder(UserHandle.of(userId)) 364 .setDisplayId(displayId).build(), 365 Runnable::run, userStartCallback); 366 UserStartResponse userStartResponse = 367 userStartCallback.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS); 368 if (userStartResponse.isSuccess()) { 369 Slog.i(TAG, "Successful startUser for user " + userId + " on display " 370 + displayId + ". Result: " + userStartResponse); 371 return true; 372 } 373 Slog.w(TAG, "startUser failed for " + userId + " on display " + displayId 374 + ". Result: " + userStartResponse); 375 } catch (Exception e) { 376 Slog.e(TAG, "Exception during startUser for user " + userId + " on display " 377 + displayId, e); 378 } 379 380 return false; 381 } 382 stopUserUnchecked(@serIdInt int userId, int displayId)383 boolean stopUserUnchecked(@UserIdInt int userId, int displayId) { 384 if (DEBUG) { 385 Slog.d(TAG, "stop user:" + userId); 386 } 387 388 mUserPickerSharedState.addStoppingUserId(userId); 389 390 CarUserManager carUserManager = mCarServiceMediator.getCarUserManager(); 391 if (carUserManager == null) { 392 Slog.w(TAG, "car user manager is not available when stopping user " + userId); 393 return false; 394 } 395 396 // We do not need to unassign the user from the occupant zone, because it is handled by 397 // CarUserService#onUserInvisible(). 398 try { 399 mUserInvisibleWaiter.init(userId); 400 SyncResultCallback<UserStopResponse> userStopCallback = new SyncResultCallback<>(); 401 carUserManager.stopUser(new UserStopRequest.Builder(UserHandle.of(userId)).build(), 402 Runnable::run, userStopCallback); 403 UserStopResponse userStopResponse = 404 userStopCallback.get(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS); 405 if (userStopResponse.isSuccess()) { 406 Slog.i(TAG, "Successful stopUser for user " + userId + " on display " + displayId 407 + ". Result: " + userStopResponse); 408 return mUserInvisibleWaiter.waitUserInvisible(); 409 } 410 Slog.w(TAG, "stopUser failed for user " + userId + " on display " + displayId 411 + ". Result: " + userStopResponse); 412 } catch (Exception e) { 413 Slog.e(TAG, "Exception during stopUser for user " + userId + " on display " 414 + displayId, e); 415 } 416 417 mUserPickerSharedState.removeStoppingUserId(userId); 418 return false; 419 } 420 421 private static class UserInvisibleWaiter { 422 private @UserIdInt int mUserId; 423 private CountDownLatch mWaiter; 424 init(@serIdInt int userId)425 void init(@UserIdInt int userId) { 426 mUserId = userId; 427 mWaiter = new CountDownLatch(1); 428 } 429 waitUserInvisible()430 boolean waitUserInvisible() { 431 if (mWaiter != null) { 432 try { 433 // This method returns false when timeout occurs so that user can re-try to 434 // login. A timeout means that stopUser() has been called successfully, but 435 // the user hasn't changed to invisible yet. 436 return mWaiter.await(USER_TIMEOUT_MS, TimeUnit.MILLISECONDS); 437 } catch (InterruptedException e) { 438 mWaiter = null; 439 } 440 } 441 return true; 442 } 443 onUserInvisible(@serIdInt int userId)444 void onUserInvisible(@UserIdInt int userId) { 445 if (userId == mUserId && mWaiter != null) { 446 mWaiter.countDown(); 447 mWaiter = null; 448 } 449 } 450 } 451 452 /** 453 * Interface for listeners that want to register for receiving updates to changes to the users 454 * on the system including removing and adding users, and changing user info. 455 */ 456 public interface OnUpdateUsersListener { 457 /** 458 * Method that will get called when users list has been changed. 459 */ onUpdateUsers(@serIdInt int userId, int userEvent)460 void onUpdateUsers(@UserIdInt int userId, int userEvent); 461 } 462 } 463