1 /* 2 * Copyright (C) 2019 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 android.car.user; 18 19 import static android.Manifest.permission.INTERACT_ACROSS_USERS; 20 import static android.Manifest.permission.INTERACT_ACROSS_USERS_FULL; 21 import static android.os.Process.myUid; 22 23 import static com.android.car.internal.util.FunctionalUtils.getLambdaName; 24 25 import android.annotation.CallbackExecutor; 26 import android.annotation.IntDef; 27 import android.annotation.NonNull; 28 import android.annotation.Nullable; 29 import android.annotation.RequiresPermission; 30 import android.annotation.SystemApi; 31 import android.annotation.TestApi; 32 import android.annotation.UserIdInt; 33 import android.car.Car; 34 import android.car.CarManagerBase; 35 import android.car.ICarResultReceiver; 36 import android.car.ICarUserService; 37 import android.car.annotation.AddedInOrBefore; 38 import android.car.annotation.ApiRequirements; 39 import android.car.builtin.os.UserManagerHelper; 40 import android.car.builtin.util.EventLogHelper; 41 import android.car.util.concurrent.AndroidAsyncFuture; 42 import android.car.util.concurrent.AndroidFuture; 43 import android.car.util.concurrent.AsyncFuture; 44 import android.os.Bundle; 45 import android.os.IBinder; 46 import android.os.RemoteException; 47 import android.os.UserHandle; 48 import android.os.UserManager; 49 import android.util.ArrayMap; 50 import android.util.Dumpable; 51 import android.util.Log; 52 import android.util.Pair; 53 54 import com.android.car.internal.common.CommonConstants; 55 import com.android.car.internal.common.CommonConstants.UserLifecycleEventType; 56 import com.android.car.internal.common.UserHelperLite; 57 import com.android.car.internal.os.CarSystemProperties; 58 import com.android.car.internal.util.ArrayUtils; 59 import com.android.internal.annotations.GuardedBy; 60 import com.android.internal.annotations.VisibleForTesting; 61 import com.android.internal.util.Preconditions; 62 63 import java.io.PrintWriter; 64 import java.lang.annotation.Retention; 65 import java.lang.annotation.RetentionPolicy; 66 import java.util.ArrayList; 67 import java.util.Arrays; 68 import java.util.List; 69 import java.util.Objects; 70 import java.util.concurrent.ExecutionException; 71 import java.util.concurrent.Executor; 72 import java.util.concurrent.TimeUnit; 73 import java.util.concurrent.TimeoutException; 74 import java.util.stream.Collectors; 75 76 /** 77 * API to manage users related to car. 78 * 79 * @hide 80 */ 81 @SystemApi 82 @TestApi 83 public final class CarUserManager extends CarManagerBase { 84 85 /** @hide */ 86 @AddedInOrBefore(majorVersion = 33) 87 public static final String TAG = CarUserManager.class.getSimpleName(); 88 89 private static final int HAL_TIMEOUT_MS = CarSystemProperties.getUserHalTimeout().orElse(5_000); 90 private static final int REMOVE_USER_CALL_TIMEOUT_MS = 60_000; 91 92 private static final boolean DBG = Log.isLoggable(TAG, Log.DEBUG); 93 private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE); 94 95 /** 96 * {@link UserLifecycleEvent} called when the user is starting, for components to initialize 97 * any per-user state they maintain for running users. 98 * 99 * @hide 100 */ 101 @SystemApi 102 @TestApi 103 @AddedInOrBefore(majorVersion = 33) 104 public static final int USER_LIFECYCLE_EVENT_TYPE_STARTING = 105 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STARTING; 106 107 /** 108 * {@link UserLifecycleEvent} called when switching to a different foreground user, for 109 * components that have special behavior for whichever user is currently in the foreground. 110 * 111 * <p>This is called before any application processes are aware of the new user. 112 * 113 * <p>Notice that internal system services might not have handled user switching yet, so be 114 * careful with interaction with them. 115 * 116 * @hide 117 */ 118 @SystemApi 119 @TestApi 120 @AddedInOrBefore(majorVersion = 33) 121 public static final int USER_LIFECYCLE_EVENT_TYPE_SWITCHING = 122 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_SWITCHING; 123 124 /** 125 * {@link UserLifecycleEvent} called when an existing user is in the process of being unlocked. 126 * 127 * <p>This means the credential-encrypted storage for that user is now available, and 128 * encryption-aware component filtering is no longer in effect. 129 * 130 * <p>Notice that internal system services might not have handled unlock yet, so most components 131 * should ignore this callback and rely on {@link #USER_LIFECYCLE_EVENT_TYPE_UNLOCKED} instead. 132 * 133 * @hide 134 */ 135 @SystemApi 136 @TestApi 137 @AddedInOrBefore(majorVersion = 33) 138 public static final int USER_LIFECYCLE_EVENT_TYPE_UNLOCKING = 139 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_UNLOCKING; 140 141 /** 142 * {@link UserLifecycleEvent} called after an existing user is unlocked. 143 * 144 * @hide 145 */ 146 @SystemApi 147 @TestApi 148 @AddedInOrBefore(majorVersion = 33) 149 public static final int USER_LIFECYCLE_EVENT_TYPE_UNLOCKED = 150 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_UNLOCKED; 151 152 /** 153 * {@link UserLifecycleEvent} called after an existing user is unlocked for components to 154 * perform non-urgent tasks for user unlocked. 155 * 156 * <p>Note: This event type is intended only for internal system services. Application listeners 157 * should not use this event type and will not receive any events of this type. 158 * 159 * @hide 160 */ 161 @AddedInOrBefore(majorVersion = 33) 162 public static final int USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED = 163 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED; 164 165 /** 166 * {@link UserLifecycleEvent} called when an existing user is stopping, for components to 167 * finalize any per-user state they maintain for running users. 168 * 169 * <p>This is called prior to sending the {@code SHUTDOWN} broadcast to the user; it is a good 170 * place to stop making use of any resources of that user (such as binding to a service running 171 * in the user). 172 * 173 * <p><b>Note:</b> this is the last callback where the callee may access the target user's CE 174 * storage. 175 * 176 * @hide 177 */ 178 @SystemApi 179 @TestApi 180 @AddedInOrBefore(majorVersion = 33) 181 public static final int USER_LIFECYCLE_EVENT_TYPE_STOPPING = 182 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPING; 183 184 /** 185 * {@link UserLifecycleEvent} called after an existing user is stopped. 186 * 187 * <p>This is called after all application process teardown of the user is complete. 188 * 189 * @hide 190 */ 191 @SystemApi 192 @TestApi 193 @AddedInOrBefore(majorVersion = 33) 194 public static final int USER_LIFECYCLE_EVENT_TYPE_STOPPED = 195 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_STOPPED; 196 197 /** 198 * {@link UserLifecycleEvent} called after an existing user is created. 199 * 200 * @hide 201 */ 202 @SystemApi 203 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1, 204 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_1) 205 public static final int USER_LIFECYCLE_EVENT_TYPE_CREATED = 206 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_CREATED; 207 208 /** 209 * {@link UserLifecycleEvent} called after an existing user is removed. 210 * 211 * @hide 212 */ 213 @SystemApi 214 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_1, 215 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_1) 216 public static final int USER_LIFECYCLE_EVENT_TYPE_REMOVED = 217 CommonConstants.USER_LIFECYCLE_EVENT_TYPE_REMOVED; 218 219 /** @hide */ 220 @AddedInOrBefore(majorVersion = 33) 221 public static final String BUNDLE_PARAM_ACTION = "action"; 222 /** @hide */ 223 @AddedInOrBefore(majorVersion = 33) 224 public static final String BUNDLE_PARAM_PREVIOUS_USER_ID = "previous_user"; 225 226 /** 227 * {@link UserIdentificationAssociationType} for key fob. 228 * 229 * @hide 230 */ 231 @AddedInOrBefore(majorVersion = 33) 232 public static final int USER_IDENTIFICATION_ASSOCIATION_TYPE_KEY_FOB = 1; 233 234 /** 235 * {@link UserIdentificationAssociationType} for custom type 1. 236 * 237 * @hide 238 */ 239 @AddedInOrBefore(majorVersion = 33) 240 public static final int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_1 = 101; 241 242 /** 243 * {@link UserIdentificationAssociationType} for custom type 2. 244 * 245 * @hide 246 */ 247 @AddedInOrBefore(majorVersion = 33) 248 public static final int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_2 = 102; 249 250 /** 251 * {@link UserIdentificationAssociationType} for custom type 3. 252 * 253 * @hide 254 */ 255 @AddedInOrBefore(majorVersion = 33) 256 public static final int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_3 = 103; 257 258 /** 259 * {@link UserIdentificationAssociationType} for custom type 4. 260 * 261 * @hide 262 */ 263 @AddedInOrBefore(majorVersion = 33) 264 public static final int USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_4 = 104; 265 266 /** 267 * User HAL's user identification association types 268 * 269 * @hide 270 */ 271 @IntDef(prefix = { "USER_IDENTIFICATION_ASSOCIATION_TYPE_" }, value = { 272 USER_IDENTIFICATION_ASSOCIATION_TYPE_KEY_FOB, 273 USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_1, 274 USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_2, 275 USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_3, 276 USER_IDENTIFICATION_ASSOCIATION_TYPE_CUSTOM_4, 277 }) 278 @Retention(RetentionPolicy.SOURCE) 279 public @interface UserIdentificationAssociationType{} 280 281 /** 282 * {@link UserIdentificationAssociationSetValue} to associate the identification type with the 283 * current foreground Android user. 284 * 285 * @hide 286 */ 287 @AddedInOrBefore(majorVersion = 33) 288 public static final int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_ASSOCIATE_CURRENT_USER = 1; 289 290 /** 291 * {@link UserIdentificationAssociationSetValue} to disassociate the identification type from 292 * the current foreground Android user. 293 * 294 * @hide 295 */ 296 @AddedInOrBefore(majorVersion = 33) 297 public static final int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_CURRENT_USER = 2; 298 299 /** 300 * {@link UserIdentificationAssociationSetValue} to disassociate the identification type from 301 * all Android users. 302 * 303 * @hide 304 */ 305 @AddedInOrBefore(majorVersion = 33) 306 public static final int USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_ALL_USERS = 3; 307 308 /** 309 * User HAL's user identification association types 310 * 311 * @hide 312 */ 313 @IntDef(prefix = { "USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_" }, value = { 314 USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_ASSOCIATE_CURRENT_USER, 315 USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_CURRENT_USER, 316 USER_IDENTIFICATION_ASSOCIATION_SET_VALUE_DISASSOCIATE_ALL_USERS, 317 }) 318 @Retention(RetentionPolicy.SOURCE) 319 public @interface UserIdentificationAssociationSetValue{} 320 321 /** 322 * {@link UserIdentificationAssociationValue} when the status of an association could not be 323 * determined. 324 * 325 * @hide 326 */ 327 @AddedInOrBefore(majorVersion = 33) 328 public static final int USER_IDENTIFICATION_ASSOCIATION_VALUE_UNKNOWN = 1; 329 330 /** 331 * {@link UserIdentificationAssociationValue} when the identification type is associated with 332 * the current foreground Android user. 333 * 334 * @hide 335 */ 336 @AddedInOrBefore(majorVersion = 33) 337 public static final int USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATE_CURRENT_USER = 2; 338 339 /** 340 * {@link UserIdentificationAssociationValue} when the identification type is associated with 341 * another Android user. 342 * 343 * @hide 344 */ 345 @AddedInOrBefore(majorVersion = 33) 346 public static final int USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATED_ANOTHER_USER = 3; 347 348 /** 349 * {@link UserIdentificationAssociationValue} when the identification type is not associated 350 * with any Android user. 351 * 352 * @hide 353 */ 354 @AddedInOrBefore(majorVersion = 33) 355 public static final int USER_IDENTIFICATION_ASSOCIATION_VALUE_NOT_ASSOCIATED_ANY_USER = 4; 356 357 /** 358 * User HAL's user identification association types 359 * 360 * @hide 361 */ 362 @IntDef(prefix = { "USER_IDENTIFICATION_ASSOCIATION_VALUE_" }, value = { 363 USER_IDENTIFICATION_ASSOCIATION_VALUE_UNKNOWN, 364 USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATE_CURRENT_USER, 365 USER_IDENTIFICATION_ASSOCIATION_VALUE_ASSOCIATED_ANOTHER_USER, 366 USER_IDENTIFICATION_ASSOCIATION_VALUE_NOT_ASSOCIATED_ANY_USER, 367 }) 368 @Retention(RetentionPolicy.SOURCE) 369 public @interface UserIdentificationAssociationValue{} 370 371 private final Object mLock = new Object(); 372 373 private final ICarUserService mService; 374 private final UserManager mUserManager; 375 376 /** 377 * Map of listeners registers by the app. 378 */ 379 @Nullable 380 @GuardedBy("mLock") 381 private ArrayMap<UserLifecycleListener, Pair<UserLifecycleEventFilter, Executor>> mListeners; 382 383 /** 384 * Receiver used to receive user-lifecycle callbacks from the service. 385 */ 386 @Nullable 387 @GuardedBy("mLock") 388 private LifecycleResultReceiver mReceiver; 389 390 private final Dumper mDumper; 391 392 /** 393 * Logs the number of received events so it's shown on {@code Dumper.dump()}. 394 */ 395 private int mNumberReceivedEvents; 396 397 /** 398 * Logs the received events so they're shown on {@code Dumper.dump()}. 399 * 400 * <p><b>Note</b>: these events are only logged when {@link #VERBOSE} is {@code true}. 401 */ 402 @Nullable 403 private List<UserLifecycleEvent> mEvents; 404 405 /** 406 * @hide 407 */ CarUserManager(@onNull Car car, @NonNull IBinder service)408 public CarUserManager(@NonNull Car car, @NonNull IBinder service) { 409 this(car, ICarUserService.Stub.asInterface(service), 410 car.getContext().getSystemService(UserManager.class)); 411 } 412 413 /** 414 * @hide 415 */ 416 @VisibleForTesting CarUserManager(@onNull Car car, @NonNull ICarUserService service, @NonNull UserManager userManager)417 public CarUserManager(@NonNull Car car, @NonNull ICarUserService service, 418 @NonNull UserManager userManager) { 419 super(car); 420 421 mDumper = addDumpable(car.getContext(), () -> new Dumper()); 422 Log.d(TAG, "CarUserManager(): DBG= " + DBG + ", mDumper=" + mDumper); 423 424 mService = service; 425 mUserManager = userManager; 426 } 427 428 /** 429 * Switches the foreground user to the given target user. 430 * 431 * @hide 432 */ 433 @TestApi 434 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 435 android.Manifest.permission.CREATE_USERS}) 436 @AddedInOrBefore(majorVersion = 33) switchUser(@serIdInt int targetUserId)437 public AsyncFuture<UserSwitchResult> switchUser(@UserIdInt int targetUserId) { 438 int uid = myUid(); 439 try { 440 AndroidFuture<UserSwitchResult> future = new AndroidFuture<UserSwitchResult>() { 441 @Override 442 protected void onCompleted(UserSwitchResult result, Throwable err) { 443 if (result != null) { 444 EventLogHelper.writeCarUserManagerSwitchUserResp(uid, 445 result.getStatus(), result.getErrorMessage()); 446 } else { 447 Log.w(TAG, "switchUser(" + targetUserId + ") failed: " + err); 448 } 449 super.onCompleted(result, err); 450 } 451 }; 452 EventLogHelper.writeCarUserManagerSwitchUserReq(uid, targetUserId); 453 mService.switchUser(targetUserId, HAL_TIMEOUT_MS, future); 454 return new AndroidAsyncFuture<>(future); 455 } catch (SecurityException e) { 456 throw e; 457 } catch (RemoteException | RuntimeException e) { 458 AsyncFuture<UserSwitchResult> future = 459 newSwitchResuiltForFailure(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE); 460 return handleExceptionFromCarService(e, future); 461 } 462 } 463 464 /** 465 * Logouts the current user (if it was switched to by a device admin). 466 * 467 * @hide 468 */ 469 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 470 android.Manifest.permission.CREATE_USERS}) 471 @AddedInOrBefore(majorVersion = 33) logoutUser()472 public AsyncFuture<UserSwitchResult> logoutUser() { 473 int uid = myUid(); 474 try { 475 AndroidFuture<UserSwitchResult> future = new AndroidFuture<UserSwitchResult>() { 476 @Override 477 protected void onCompleted(UserSwitchResult result, Throwable err) { 478 if (result != null) { 479 EventLogHelper.writeCarUserManagerLogoutUserResp(uid, 480 result.getStatus(), result.getErrorMessage()); 481 } else { 482 Log.w(TAG, "logoutUser() failed: " + err); 483 } 484 super.onCompleted(result, err); 485 } 486 }; 487 EventLogHelper.writeCarUserManagerLogoutUserReq(uid); 488 mService.logoutUser(HAL_TIMEOUT_MS, future); 489 return new AndroidAsyncFuture<>(future); 490 } catch (SecurityException e) { 491 throw e; 492 } catch (RemoteException | RuntimeException e) { 493 AsyncFuture<UserSwitchResult> future = 494 newSwitchResuiltForFailure(UserSwitchResult.STATUS_HAL_INTERNAL_FAILURE); 495 return handleExceptionFromCarService(e, future); 496 } 497 } 498 newSwitchResuiltForFailure( @serSwitchResult.Status int status)499 private AndroidAsyncFuture<UserSwitchResult> newSwitchResuiltForFailure( 500 @UserSwitchResult.Status int status) { 501 AndroidFuture<UserSwitchResult> future = new AndroidFuture<>(); 502 future.complete(new UserSwitchResult(status, null)); 503 return new AndroidAsyncFuture<>(future); 504 } 505 createUser(@ullable String name, @NonNull String userType, int flags)506 private AsyncFuture<UserCreationResult> createUser(@Nullable String name, 507 @NonNull String userType, int flags) { 508 int uid = myUid(); 509 try { 510 AndroidFuture<UserCreationResult> future = new AndroidFuture<UserCreationResult>() { 511 @Override 512 protected void onCompleted(UserCreationResult result, Throwable err) { 513 if (result != null) { 514 EventLogHelper.writeCarUserManagerCreateUserResp(uid, 515 result.getStatus(), result.getErrorMessage()); 516 } else { 517 Log.w(TAG, "createUser(" + userType + "," + flags 518 + ") failed: " + err); 519 } 520 super.onCompleted(result, err); 521 }; 522 }; 523 EventLogHelper.writeCarUserManagerCreateUserReq(uid, 524 UserHelperLite.safeName(name), userType, flags); 525 mService.createUser(name, userType, flags, HAL_TIMEOUT_MS, future); 526 return new AndroidAsyncFuture<>(future); 527 } catch (SecurityException e) { 528 throw e; 529 } catch (RemoteException | RuntimeException e) { 530 AndroidFuture<UserCreationResult> future = new AndroidFuture<>(); 531 future.complete(new UserCreationResult(UserCreationResult.STATUS_HAL_INTERNAL_FAILURE)); 532 return handleExceptionFromCarService(e, new AndroidAsyncFuture<>(future)); 533 } 534 } 535 536 /** 537 * Creates a new guest Android user. 538 * 539 * @hide 540 */ 541 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 542 android.Manifest.permission.CREATE_USERS}) 543 @AddedInOrBefore(majorVersion = 33) createGuest(@ullable String name)544 public AsyncFuture<UserCreationResult> createGuest(@Nullable String name) { 545 return createUser(name, UserManager.USER_TYPE_FULL_GUEST, /* flags= */ 0); 546 } 547 548 /** 549 * Creates a new Android user. 550 * 551 * @hide 552 */ 553 @AddedInOrBefore(majorVersion = 33) 554 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 555 android.Manifest.permission.CREATE_USERS}) createUser(@ullable String name, int flags)556 public AsyncFuture<UserCreationResult> createUser(@Nullable String name, 557 int flags) { 558 return createUser(name, UserManager.USER_TYPE_FULL_SECONDARY, flags); 559 } 560 561 /** 562 * Updates pre-created users. 563 * <p> 564 * Updates pre-created users based on the car properties defined 565 * {@code CarProperties.number_pre_created_guests} and (@code 566 * CarProperties.number_pre_created_users}. 567 * 568 * @hide 569 */ 570 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 571 android.Manifest.permission.CREATE_USERS}) 572 @AddedInOrBefore(majorVersion = 33) updatePreCreatedUsers()573 public void updatePreCreatedUsers() { 574 int uid = myUid(); 575 EventLogHelper.writeCarUserManagerPreCreateUserReq(uid); 576 try { 577 mService.updatePreCreatedUsers(); 578 } catch (SecurityException e) { 579 throw e; 580 } catch (RemoteException | RuntimeException e) { 581 handleExceptionFromCarService(e, null); 582 } 583 } 584 585 /** 586 * Removes the given user. 587 * 588 * @param userId identification of the user to be removed. 589 * 590 * @return whether the user was successfully removed. 591 * 592 * @hide 593 */ 594 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 595 android.Manifest.permission.CREATE_USERS}) 596 @NonNull 597 @AddedInOrBefore(majorVersion = 33) removeUser(@serIdInt int userId)598 public UserRemovalResult removeUser(@UserIdInt int userId) { 599 int uid = myUid(); 600 EventLogHelper.writeCarUserManagerRemoveUserReq(uid, userId); 601 int status = UserRemovalResult.STATUS_ANDROID_FAILURE; 602 try { 603 AndroidFuture<UserRemovalResult> future = new AndroidFuture<UserRemovalResult>(); 604 mService.removeUser(userId, future); 605 UserRemovalResult result = future.get(REMOVE_USER_CALL_TIMEOUT_MS, 606 TimeUnit.MILLISECONDS); 607 status = result.getStatus(); 608 return result; 609 } catch (SecurityException e) { 610 throw e; 611 } catch (InterruptedException e) { 612 Thread.currentThread().interrupt(); 613 return new UserRemovalResult(UserRemovalResult.STATUS_ANDROID_FAILURE); 614 } catch (ExecutionException | TimeoutException e) { 615 return new UserRemovalResult(UserRemovalResult.STATUS_ANDROID_FAILURE); 616 } catch (RemoteException | RuntimeException e) { 617 return handleExceptionFromCarService(e, 618 new UserRemovalResult(UserRemovalResult.STATUS_ANDROID_FAILURE)); 619 } finally { 620 EventLogHelper.writeCarUserManagerRemoveUserResp(uid, status); 621 } 622 } 623 624 /** 625 * Adds a listener for {@link UserLifecycleEvent user lifecycle events}. 626 * 627 * @throws IllegalStateException if the listener was already added. 628 * 629 * @hide 630 */ 631 @SystemApi 632 @TestApi 633 @RequiresPermission(anyOf = {INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL}) 634 @AddedInOrBefore(majorVersion = 33) addListener(@onNull @allbackExecutor Executor executor, @NonNull UserLifecycleListener listener)635 public void addListener(@NonNull @CallbackExecutor Executor executor, 636 @NonNull UserLifecycleListener listener) { 637 addListenerInternal(executor, /* filter= */null, listener); 638 } 639 640 /** 641 * Adds a listener for {@link UserLifecycleEvent user lifecycle events} with a filter that can 642 * specify a specific event type or a user id. 643 * 644 * @throws IllegalStateException if the listener was already added. 645 * 646 * @hide 647 */ 648 @SystemApi 649 @TestApi 650 @RequiresPermission(anyOf = {INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL}) 651 @AddedInOrBefore(majorVersion = 33) addListener(@onNull @allbackExecutor Executor executor, @NonNull UserLifecycleEventFilter filter, @NonNull UserLifecycleListener listener)652 public void addListener(@NonNull @CallbackExecutor Executor executor, 653 @NonNull UserLifecycleEventFilter filter, @NonNull UserLifecycleListener listener) { 654 Objects.requireNonNull(filter, "filter cannot be null"); 655 656 addListenerInternal(executor, filter, listener); 657 } 658 addListenerInternal(@allbackExecutor Executor executor, @Nullable UserLifecycleEventFilter filter, UserLifecycleListener listener)659 private void addListenerInternal(@CallbackExecutor Executor executor, 660 @Nullable UserLifecycleEventFilter filter, UserLifecycleListener listener) { 661 Objects.requireNonNull(executor, "executor cannot be null"); 662 Objects.requireNonNull(listener, "listener cannot be null"); 663 664 int uid = myUid(); 665 String packageName = getContext().getPackageName(); 666 if (DBG) { 667 Log.d(TAG, "addListener(): uid=" + uid + ", pkg=" + packageName 668 + ", listener=" + listener + ", filter= " + filter); 669 } 670 synchronized (mLock) { 671 Preconditions.checkState(mListeners == null || !mListeners.containsKey(listener), 672 "already called for this listener"); 673 if (mReceiver == null) { 674 mReceiver = new LifecycleResultReceiver(); 675 if (DBG) { 676 Log.d(TAG, "Setting lifecycle receiver with filter " + filter 677 + " for uid " + uid + " and package " + packageName); 678 } 679 } else { 680 if (DBG) { 681 Log.d(TAG, "Already set receiver for uid " + uid + " and package " 682 + packageName + " adding new filter " + filter); 683 } 684 } 685 try { 686 boolean hasFilter = filter != null; 687 EventLogHelper.writeCarUserManagerAddListener(uid, packageName, hasFilter); 688 mService.setLifecycleListenerForApp(packageName, filter, mReceiver); 689 } catch (RemoteException e) { 690 handleRemoteExceptionFromCarService(e); 691 } 692 693 if (mListeners == null) { 694 mListeners = new ArrayMap<>(1); // Most likely app will have just one listener 695 } else if (DBG) { 696 Log.d(TAG, "addListener(" + getLambdaName(listener) + "): context " + getContext() 697 + " already has " + mListeners.size() + " listeners: " 698 + mListeners.keySet().stream() 699 .map((l) -> getLambdaName(l)) 700 .collect(Collectors.toList()), new Exception("caller's stack")); 701 } 702 if (DBG) Log.d(TAG, "Adding listener: " + listener + " with filter " + filter); 703 mListeners.put(listener, Pair.create(filter, executor)); 704 } 705 } 706 707 /** 708 * Removes a listener for {@link UserLifecycleEvent user lifecycle events}. 709 * 710 * @throws IllegalStateException if the listener was not added beforehand. 711 * 712 * @hide 713 */ 714 @SystemApi 715 @TestApi 716 @RequiresPermission(anyOf = {INTERACT_ACROSS_USERS, INTERACT_ACROSS_USERS_FULL}) 717 @AddedInOrBefore(majorVersion = 33) removeListener(@onNull UserLifecycleListener listener)718 public void removeListener(@NonNull UserLifecycleListener listener) { 719 Objects.requireNonNull(listener, "listener cannot be null"); 720 721 int uid = myUid(); 722 String packageName = getContext().getPackageName(); 723 if (DBG) { 724 Log.d(TAG, "removeListener(): uid=" + uid + ", pkg=" + packageName 725 + ", listener=" + listener); 726 } 727 synchronized (mLock) { 728 Preconditions.checkState(mListeners != null && mListeners.containsKey(listener), 729 "not called for this listener yet"); 730 mListeners.remove(listener); 731 732 // Note that there can be some rare corner cases that a listener is removed but its 733 // corresponding filter remains in the service side. This may cause slight inefficiency 734 // due to unnecessary receiver calls. It will still be functionally correct, because the 735 // removed listener will no longer be invoked. 736 if (!mListeners.isEmpty()) { 737 if (DBG) Log.d(TAG, "removeListeners(): still " + mListeners.size() + " left"); 738 return; 739 } 740 mListeners = null; 741 742 if (mReceiver == null) { 743 Log.wtf(TAG, "removeListener(): receiver already null"); 744 return; 745 } 746 747 EventLogHelper.writeCarUserManagerRemoveListener(uid, packageName); 748 if (DBG) { 749 Log.d(TAG, "Removing lifecycle receiver for uid=" + uid + " and package " 750 + packageName); 751 } 752 try { 753 mService.resetLifecycleListenerForApp(mReceiver); 754 mReceiver = null; 755 } catch (RemoteException e) { 756 handleRemoteExceptionFromCarService(e); 757 } 758 } 759 } 760 761 /** 762 * Check if user hal supports user association. 763 * 764 * @hide 765 */ 766 @AddedInOrBefore(majorVersion = 33) isUserHalUserAssociationSupported()767 public boolean isUserHalUserAssociationSupported() { 768 try { 769 return mService.isUserHalUserAssociationSupported(); 770 } catch (RemoteException | RuntimeException e) { 771 return handleExceptionFromCarService(e, false); 772 } 773 } 774 775 /** 776 * Gets the user authentication types associated with this manager's user. 777 * 778 * @hide 779 */ 780 @NonNull 781 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 782 android.Manifest.permission.CREATE_USERS}) 783 @AddedInOrBefore(majorVersion = 33) getUserIdentificationAssociation( @serIdentificationAssociationType int... types)784 public UserIdentificationAssociationResponse getUserIdentificationAssociation( 785 @UserIdentificationAssociationType int... types) { 786 Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type"); 787 EventLogHelper.writeCarUserManagerGetUserAuthReq(convertToObjectArray(types)); 788 try { 789 UserIdentificationAssociationResponse response = 790 mService.getUserIdentificationAssociation(types); 791 if (response != null) { 792 int[] values = response.getValues(); 793 EventLogHelper.writeCarUserManagerGetUserAuthResp(convertToObjectArray(values)); 794 } 795 return response; 796 } catch (SecurityException e) { 797 throw e; 798 } catch (RemoteException | RuntimeException e) { 799 return handleExceptionFromCarService(e, 800 UserIdentificationAssociationResponse.forFailure(e.getMessage())); 801 } 802 } 803 convertToObjectArray(int[] input)804 private Object[] convertToObjectArray(int[] input) { 805 if (input == null) return null; 806 Object[] output = new Object[input.length]; 807 for (int i = 0; i < input.length; i++) { 808 output[i] = input[i]; 809 } 810 return output; 811 } 812 813 /** 814 * Sets the user authentication types associated with this manager's user. 815 * 816 * @hide 817 */ 818 @NonNull 819 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 820 android.Manifest.permission.CREATE_USERS}) 821 @AddedInOrBefore(majorVersion = 33) setUserIdentificationAssociation( @serIdentificationAssociationType int[] types, @UserIdentificationAssociationSetValue int[] values)822 public AsyncFuture<UserIdentificationAssociationResponse> setUserIdentificationAssociation( 823 @UserIdentificationAssociationType int[] types, 824 @UserIdentificationAssociationSetValue int[] values) { 825 Preconditions.checkArgument(!ArrayUtils.isEmpty(types), "must have at least one type"); 826 Preconditions.checkArgument(!ArrayUtils.isEmpty(values), "must have at least one value"); 827 if (types.length != values.length) { 828 throw new IllegalArgumentException("types (" + Arrays.toString(types) + ") and values (" 829 + Arrays.toString(values) + ") should have the same length"); 830 } 831 // TODO(b/153900032): move this logic to a common helper 832 Object[] loggedValues = new Integer[types.length * 2]; 833 for (int i = 0; i < types.length; i++) { 834 loggedValues[i * 2] = types[i]; 835 loggedValues[i * 2 + 1 ] = values[i]; 836 } 837 EventLogHelper.writeCarUserManagerSetUserAuthReq(loggedValues); 838 839 try { 840 AndroidFuture<UserIdentificationAssociationResponse> future = 841 new AndroidFuture<UserIdentificationAssociationResponse>() { 842 @Override 843 protected void onCompleted(UserIdentificationAssociationResponse result, 844 Throwable err) { 845 if (result != null) { 846 int[] rawValues = result.getValues(); 847 // TODO(b/153900032): move this logic to a common helper 848 if (rawValues != null) { 849 Object[] loggedValues = new Object[rawValues.length]; 850 for (int i = 0; i < rawValues.length; i++) { 851 loggedValues[i] = rawValues[i]; 852 } 853 EventLogHelper.writeCarUserManagerSetUserAuthResp(loggedValues); 854 } 855 } else { 856 Log.w(TAG, "setUserIdentificationAssociation(" + Arrays.toString(types) 857 + ", " + Arrays.toString(values) + ") failed: " + err); 858 } 859 super.onCompleted(result, err); 860 }; 861 }; 862 mService.setUserIdentificationAssociation(HAL_TIMEOUT_MS, types, values, future); 863 return new AndroidAsyncFuture<>(future); 864 } catch (SecurityException e) { 865 throw e; 866 } catch (RemoteException | RuntimeException e) { 867 AndroidFuture<UserIdentificationAssociationResponse> future = new AndroidFuture<>(); 868 future.complete(UserIdentificationAssociationResponse.forFailure()); 869 return handleExceptionFromCarService(e, new AndroidAsyncFuture<>(future)); 870 } 871 } 872 873 /** 874 * Sets a callback to be notified before user switch. It should only be used by Car System UI. 875 * 876 * @hide 877 */ 878 @RequiresPermission(android.Manifest.permission.MANAGE_USERS) 879 @AddedInOrBefore(majorVersion = 33) setUserSwitchUiCallback(@onNull UserSwitchUiCallback callback)880 public void setUserSwitchUiCallback(@NonNull UserSwitchUiCallback callback) { 881 Preconditions.checkArgument(callback != null, "Null callback"); 882 UserSwitchUiCallbackReceiver userSwitchUiCallbackReceiver = 883 new UserSwitchUiCallbackReceiver(callback); 884 try { 885 mService.setUserSwitchUiCallback(userSwitchUiCallbackReceiver); 886 } catch (RemoteException e) { 887 handleRemoteExceptionFromCarService(e); 888 } 889 } 890 891 /** 892 * {@code ICarResultReceiver} used to receive user switch UI Callback. 893 */ 894 // TODO(b/154958003): use mReceiver instead as now there are two binder objects 895 private final class UserSwitchUiCallbackReceiver extends ICarResultReceiver.Stub { 896 897 private final UserSwitchUiCallback mUserSwitchUiCallback; 898 UserSwitchUiCallbackReceiver(UserSwitchUiCallback callback)899 UserSwitchUiCallbackReceiver(UserSwitchUiCallback callback) { 900 mUserSwitchUiCallback = callback; 901 } 902 903 @Override send(int userId, Bundle unused)904 public void send(int userId, Bundle unused) throws RemoteException { 905 mUserSwitchUiCallback.showUserSwitchDialog(userId); 906 } 907 } 908 909 /** 910 * {@code ICarResultReceiver} used to receive lifecycle events and dispatch to the proper 911 * listener. 912 */ 913 private class LifecycleResultReceiver extends ICarResultReceiver.Stub { 914 @Override send(int resultCode, Bundle resultData)915 public void send(int resultCode, Bundle resultData) { 916 if (resultData == null) { 917 Log.w(TAG, "Received result (" + resultCode + ") without data"); 918 return; 919 } 920 int from = resultData.getInt(BUNDLE_PARAM_PREVIOUS_USER_ID, 921 UserManagerHelper.USER_NULL); 922 int to = resultCode; 923 int eventType = resultData.getInt(BUNDLE_PARAM_ACTION); 924 UserLifecycleEvent event = new UserLifecycleEvent(eventType, from, to); 925 ArrayMap<UserLifecycleListener, Pair<UserLifecycleEventFilter, Executor>> listeners; 926 synchronized (mLock) { 927 if (mListeners == null) { 928 Log.w(TAG, "No listeners for event " + event); 929 return; 930 } 931 listeners = new ArrayMap<>(mListeners); 932 } 933 int size = listeners.size(); 934 EventLogHelper.writeCarUserManagerNotifyLifecycleListener(size, eventType, from, to); 935 for (int i = 0; i < size; i++) { 936 UserLifecycleListener listener = listeners.keyAt(i); 937 UserLifecycleEventFilter filter = listeners.valueAt(i).first; 938 if (filter != null && !filter.apply(event)) { 939 if (DBG) { 940 Log.d(TAG, "Listener " + getLambdaName(listener) 941 + " is skipped for the event " + event + " due to the filter " 942 + filter); 943 } 944 continue; 945 } 946 Executor executor = listeners.valueAt(i).second; 947 if (DBG) { 948 Log.d(TAG, "Calling " + getLambdaName(listener) + " for event " + event); 949 } 950 executor.execute(() -> listener.onEvent(event)); 951 } 952 mNumberReceivedEvents++; 953 if (VERBOSE) { 954 if (mEvents == null) { 955 mEvents = new ArrayList<>(); 956 } 957 mEvents.add(event); 958 } 959 } 960 } 961 962 /** @hide */ 963 @Override 964 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()965 public void onCarDisconnected() { 966 // nothing to do 967 } 968 969 private final class Dumper implements Dumpable { 970 @Override dump(PrintWriter pw, String[] args)971 public void dump(PrintWriter pw, String[] args) { 972 String prefix = " "; 973 974 pw.printf("DBG=%b, VERBOSE=%b\n", DBG, VERBOSE); 975 int listenersSize = 0; 976 synchronized (mLock) { 977 pw.printf("mReceiver: %s\n", mReceiver); 978 if (mListeners == null) { 979 pw.println("no listeners"); 980 } else { 981 listenersSize = mListeners.size(); 982 pw.printf("%d listeners\n", listenersSize); 983 } 984 if (DBG) { 985 for (int i = 0; i < listenersSize; i++) { 986 pw.printf("%s%d: %s\n", prefix, i + 1, mListeners.keyAt(i)); 987 } 988 } 989 } 990 pw.printf("mNumberReceivedEvents: %d\n", mNumberReceivedEvents); 991 if (VERBOSE && mEvents != null) { 992 for (int i = 0; i < mEvents.size(); i++) { 993 pw.printf("%s%d: %s\n", prefix, i + 1, mEvents.get(i)); 994 } 995 } 996 } 997 998 @Override getDumpableName()999 public String getDumpableName() { 1000 return CarUserManager.class.getSimpleName(); 1001 } 1002 } 1003 1004 /** 1005 * @hide 1006 */ 1007 @TestApi 1008 @AddedInOrBefore(majorVersion = 33) lifecycleEventTypeToString(@serLifecycleEventType int type)1009 public static String lifecycleEventTypeToString(@UserLifecycleEventType int type) { 1010 switch (type) { 1011 case USER_LIFECYCLE_EVENT_TYPE_STARTING: 1012 return "STARTING"; 1013 case USER_LIFECYCLE_EVENT_TYPE_SWITCHING: 1014 return "SWITCHING"; 1015 case USER_LIFECYCLE_EVENT_TYPE_UNLOCKING: 1016 return "UNLOCKING"; 1017 case USER_LIFECYCLE_EVENT_TYPE_UNLOCKED: 1018 return "UNLOCKED"; 1019 case USER_LIFECYCLE_EVENT_TYPE_STOPPING: 1020 return "STOPPING"; 1021 case USER_LIFECYCLE_EVENT_TYPE_STOPPED: 1022 return "STOPPED"; 1023 case USER_LIFECYCLE_EVENT_TYPE_POST_UNLOCKED: 1024 return "POST_UNLOCKED"; 1025 case USER_LIFECYCLE_EVENT_TYPE_CREATED: 1026 return "CREATED"; 1027 case USER_LIFECYCLE_EVENT_TYPE_REMOVED: 1028 return "REMOVED"; 1029 default: 1030 return "UNKNOWN-" + type; 1031 } 1032 } 1033 1034 /** 1035 * Checks if the given {@code userId} represents a valid user. 1036 * 1037 * <p>A "valid" user: 1038 * 1039 * <ul> 1040 * <li>Must exist in the device. 1041 * <li>Is not in the process of being deleted. 1042 * <li>Cannot be the {@link UserHandle#isSystem() system} user on devices that use 1043 * {@link UserManager#isHeadlessSystemUserMode() headless system mode}. 1044 * </ul> 1045 * 1046 * @hide 1047 */ 1048 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 1049 android.Manifest.permission.CREATE_USERS}) 1050 @AddedInOrBefore(majorVersion = 33) isValidUser(@serIdInt int userId)1051 public boolean isValidUser(@UserIdInt int userId) { 1052 List<UserHandle> allUsers = mUserManager.getUserHandles(/* excludeDying=*/ true); 1053 for (int i = 0; i < allUsers.size(); i++) { 1054 UserHandle user = allUsers.get(i); 1055 if (user.getIdentifier() == userId && (userId != UserHandle.SYSTEM.getIdentifier() 1056 || !UserManager.isHeadlessSystemUserMode())) { 1057 return true; 1058 } 1059 } 1060 return false; 1061 } 1062 1063 /** 1064 * Defines a lifecycle event for an Android user. 1065 * 1066 * @hide 1067 */ 1068 @SystemApi 1069 @TestApi 1070 public static final class UserLifecycleEvent { 1071 private final @UserLifecycleEventType int mEventType; 1072 private final @UserIdInt int mUserId; 1073 private final @UserIdInt int mPreviousUserId; 1074 1075 /** @hide */ UserLifecycleEvent(@serLifecycleEventType int eventType, @UserIdInt int from, @UserIdInt int to)1076 public UserLifecycleEvent(@UserLifecycleEventType int eventType, 1077 @UserIdInt int from, @UserIdInt int to) { 1078 mEventType = eventType; 1079 mPreviousUserId = from; 1080 mUserId = to; 1081 } 1082 1083 /** @hide */ UserLifecycleEvent(@serLifecycleEventType int eventType, @UserIdInt int to)1084 public UserLifecycleEvent(@UserLifecycleEventType int eventType, @UserIdInt int to) { 1085 this(eventType, UserManagerHelper.USER_NULL, to); 1086 } 1087 1088 /** 1089 * Gets the event type. 1090 * 1091 * @return either {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_STARTING}, 1092 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING}, 1093 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKING}, 1094 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_UNLOCKED}, 1095 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_STOPPING} or 1096 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_STOPPED} for all apps; 1097 * for apps {@link CarPackageManager#getTargetCarVersion() targeting car version} 1098 * {@link CarVersion.VERSION_CODES#TIRAMISU_1} or higher, it could be new types 1099 * added on later releases, such as 1100 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_CREATED}, 1101 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_REMOVED} and possibly others. 1102 * 1103 */ 1104 @UserLifecycleEventType 1105 @AddedInOrBefore(majorVersion = 33) getEventType()1106 public int getEventType() { 1107 return mEventType; 1108 } 1109 1110 /** 1111 * Gets the id of the user whose event is being reported. 1112 * 1113 * @hide 1114 */ 1115 @UserIdInt 1116 @AddedInOrBefore(majorVersion = 33) getUserId()1117 public int getUserId() { 1118 return mUserId; 1119 } 1120 1121 /** 1122 * Gets the handle of the user whose event is being reported. 1123 */ 1124 @NonNull 1125 @AddedInOrBefore(majorVersion = 33) getUserHandle()1126 public UserHandle getUserHandle() { 1127 return UserHandle.of(mUserId); 1128 } 1129 1130 /** 1131 * Gets the id of the user being switched from. 1132 * 1133 * <p>This method returns {@link UserHandle#USER_NULL} for all event types but 1134 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING}. 1135 * 1136 * @hide 1137 */ 1138 @UserIdInt 1139 @AddedInOrBefore(majorVersion = 33) getPreviousUserId()1140 public int getPreviousUserId() { 1141 return mPreviousUserId; 1142 } 1143 1144 /** 1145 * Gets the handle of the user being switched from. 1146 * 1147 * <p>This method returns {@code null} for all event types but 1148 * {@link CarUserManager#USER_LIFECYCLE_EVENT_TYPE_SWITCHING}. 1149 */ 1150 @Nullable 1151 @AddedInOrBefore(majorVersion = 33) getPreviousUserHandle()1152 public UserHandle getPreviousUserHandle() { 1153 return mPreviousUserId == UserManagerHelper.USER_NULL ? null 1154 : UserHandle.of(mPreviousUserId); 1155 } 1156 1157 @Override 1158 @AddedInOrBefore(majorVersion = 33) toString()1159 public String toString() { 1160 StringBuilder builder = new StringBuilder("Event[type=") 1161 .append(lifecycleEventTypeToString(mEventType)); 1162 if (mPreviousUserId != UserManagerHelper.USER_NULL) { 1163 builder 1164 .append(",from=").append(mPreviousUserId) 1165 .append(",to=").append(mUserId); 1166 } else { 1167 builder.append(",user=").append(mUserId); 1168 } 1169 1170 return builder.append(']').toString(); 1171 } 1172 1173 @Override 1174 @AddedInOrBefore(majorVersion = 33) equals(@ullable Object o)1175 public boolean equals(@Nullable Object o) { 1176 if (this == o) return true; 1177 if (o == null || getClass() != o.getClass()) return false; 1178 1179 UserLifecycleEvent that = (UserLifecycleEvent) o; 1180 return mEventType == that.mEventType && mUserId == that.mUserId 1181 && mPreviousUserId == that.mPreviousUserId; 1182 } 1183 1184 @Override 1185 @AddedInOrBefore(majorVersion = 33) hashCode()1186 public int hashCode() { 1187 int hash = 23; 1188 hash = 17 * hash + mEventType; 1189 hash = 17 * hash + mUserId; 1190 hash = 17 * hash + mPreviousUserId; 1191 1192 return hash; 1193 } 1194 } 1195 1196 /** 1197 * Listener for Android User lifecycle events. 1198 * 1199 * <p>Must be registered using {@link CarUserManager#addListener(UserLifecycleListener)} and 1200 * unregistered through {@link CarUserManager#removeListener(UserLifecycleListener)}. 1201 * 1202 * @hide 1203 */ 1204 @SystemApi 1205 @TestApi 1206 public interface UserLifecycleListener { 1207 1208 /** 1209 * Called to notify the given {@code event}. 1210 */ 1211 @AddedInOrBefore(majorVersion = 33) onEvent(@onNull UserLifecycleEvent event)1212 void onEvent(@NonNull UserLifecycleEvent event); 1213 } 1214 1215 /** 1216 * Callback for notifying user switch before switch started. 1217 * 1218 * <p> It should only be user by Car System UI. The purpose of this callback is notify the 1219 * Car System UI to display the user switch UI. 1220 * 1221 * @hide 1222 */ 1223 public interface UserSwitchUiCallback { 1224 1225 /** 1226 * Called to notify that user switch dialog should be shown now. 1227 */ 1228 @AddedInOrBefore(majorVersion = 33) showUserSwitchDialog(@serIdInt int userId)1229 void showUserSwitchDialog(@UserIdInt int userId); 1230 } 1231 } 1232