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; 18 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 20 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeastU; 21 22 import android.annotation.IntDef; 23 import android.annotation.NonNull; 24 import android.annotation.Nullable; 25 import android.annotation.RequiresPermission; 26 import android.annotation.SystemApi; 27 import android.annotation.UserIdInt; 28 import android.car.annotation.AddedInOrBefore; 29 import android.car.annotation.ApiRequirements; 30 import android.hardware.display.DisplayManager; 31 import android.os.Handler; 32 import android.os.IBinder; 33 import android.os.Looper; 34 import android.os.Message; 35 import android.os.Parcel; 36 import android.os.Parcelable; 37 import android.os.RemoteException; 38 import android.os.UserHandle; 39 import android.os.UserManager; 40 import android.util.Log; 41 import android.view.Display; 42 43 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 44 import com.android.car.internal.util.Lists; 45 46 import java.lang.annotation.ElementType; 47 import java.lang.annotation.Retention; 48 import java.lang.annotation.RetentionPolicy; 49 import java.lang.annotation.Target; 50 import java.lang.ref.WeakReference; 51 import java.util.ArrayList; 52 import java.util.Collections; 53 import java.util.List; 54 import java.util.concurrent.CopyOnWriteArrayList; 55 56 /** 57 * API to get information on displays and users in the car. 58 * 59 * <p>From car version {@link CarVersion.VERSION_CODES#UPSIDE_DOWN_CAKE_0}, system without the 60 * driver zone is allowed and the current user will not be the driver. 61 */ 62 public class CarOccupantZoneManager extends CarManagerBase { 63 64 private static final String TAG = CarOccupantZoneManager.class.getSimpleName(); 65 66 /** 67 * Display type is not known. In some system, some displays may be just public display without 68 * any additional information and such displays will be treated as unknown. 69 */ 70 @AddedInOrBefore(majorVersion = 33) 71 public static final int DISPLAY_TYPE_UNKNOWN = 0; 72 73 /** 74 * Main display users are interacting with. UI for the user will be launched to this display by 75 * default. {@link Display#DEFAULT_DISPLAY} will be always have this type. But there can be 76 * multiple of this type as each passenger can have their own main display. 77 */ 78 @AddedInOrBefore(majorVersion = 33) 79 public static final int DISPLAY_TYPE_MAIN = 1; 80 81 /** Instrument cluster display. This may exist only for driver. */ 82 @AddedInOrBefore(majorVersion = 33) 83 public static final int DISPLAY_TYPE_INSTRUMENT_CLUSTER = 2; 84 85 /** Head Up Display. This may exist only for driver. */ 86 @AddedInOrBefore(majorVersion = 33) 87 public static final int DISPLAY_TYPE_HUD = 3; 88 89 /** Dedicated display for showing IME for {@link #DISPLAY_TYPE_MAIN} */ 90 @AddedInOrBefore(majorVersion = 33) 91 public static final int DISPLAY_TYPE_INPUT = 4; 92 93 /** 94 * Auxiliary display which can provide additional screen for {@link #DISPLAY_TYPE_MAIN}. 95 * Activity running in {@link #DISPLAY_TYPE_MAIN} may use {@link android.app.Presentation} to 96 * show additional information. 97 */ 98 @AddedInOrBefore(majorVersion = 33) 99 public static final int DISPLAY_TYPE_AUXILIARY = 5; 100 101 /** @hide */ 102 @Retention(RetentionPolicy.SOURCE) 103 @IntDef(prefix = "DISPLAY_TYPE_", value = { 104 DISPLAY_TYPE_UNKNOWN, 105 DISPLAY_TYPE_MAIN, 106 DISPLAY_TYPE_INSTRUMENT_CLUSTER, 107 DISPLAY_TYPE_HUD, 108 DISPLAY_TYPE_INPUT, 109 DISPLAY_TYPE_AUXILIARY, 110 }) 111 @Target({ElementType.TYPE_USE}) 112 public @interface DisplayTypeEnum {} 113 114 /** @hide */ 115 @AddedInOrBefore(majorVersion = 33) 116 public static final int OCCUPANT_TYPE_INVALID = -1; 117 118 /** 119 * Represents the driver. There can be one or zero driver for the system. Zero driver situation 120 * can happen if the system is configured to support only passengers. 121 */ 122 @AddedInOrBefore(majorVersion = 33) 123 public static final int OCCUPANT_TYPE_DRIVER = 0; 124 125 /** 126 * Represents front passengers who sit in front side of car. Most cars will have only 127 * one passenger of this type but this can be multiple. 128 */ 129 @AddedInOrBefore(majorVersion = 33) 130 public static final int OCCUPANT_TYPE_FRONT_PASSENGER = 1; 131 132 /** Represents passengers in rear seats. There can be multiple passengers of this type. */ 133 @AddedInOrBefore(majorVersion = 33) 134 public static final int OCCUPANT_TYPE_REAR_PASSENGER = 2; 135 136 /** @hide */ 137 @Retention(RetentionPolicy.SOURCE) 138 @IntDef(prefix = "OCCUPANT_TYPE_", value = { 139 OCCUPANT_TYPE_DRIVER, 140 OCCUPANT_TYPE_FRONT_PASSENGER, 141 OCCUPANT_TYPE_REAR_PASSENGER, 142 }) 143 @Target({ElementType.TYPE_USE}) 144 public @interface OccupantTypeEnum {} 145 146 /** 147 * Represents an occupant zone in a car. 148 * 149 * <p>Each occupant does not necessarily represent single person but it is for mapping to one 150 * set of displays. For example, for display located in center rear seat, both left and right 151 * side passengers may use it but it is abstracted as a single occupant zone.</p> 152 */ 153 public static final class OccupantZoneInfo implements Parcelable { 154 /** @hide */ 155 @AddedInOrBefore(majorVersion = 33) 156 public static final int INVALID_ZONE_ID = -1; 157 158 /** 159 * This is an unique id to distinguish each occupant zone. 160 * 161 * <p>This can be helpful to distinguish different zones when {@link #occupantType} and 162 * {@link #seat} are the same for multiple occupant / passenger zones.</p> 163 * 164 * <p>This id will remain the same for the same zone across configuration changes like 165 * user switching or display changes</p> 166 */ 167 @AddedInOrBefore(majorVersion = 33) 168 public int zoneId; 169 /** Represents type of passenger */ 170 @OccupantTypeEnum 171 @AddedInOrBefore(majorVersion = 33) 172 public final int occupantType; 173 /** 174 * Represents seat assigned for the occupant. In some system, this can have value of 175 * {@link VehicleAreaSeat#SEAT_UNKNOWN}. 176 */ 177 @VehicleAreaSeat.Enum 178 @AddedInOrBefore(majorVersion = 33) 179 public final int seat; 180 181 /** @hide */ OccupantZoneInfo(int zoneId, @OccupantTypeEnum int occupantType, @VehicleAreaSeat.Enum int seat)182 public OccupantZoneInfo(int zoneId, @OccupantTypeEnum int occupantType, 183 @VehicleAreaSeat.Enum int seat) { 184 this.zoneId = zoneId; 185 this.occupantType = occupantType; 186 this.seat = seat; 187 } 188 189 /** @hide */ OccupantZoneInfo(Parcel in)190 public OccupantZoneInfo(Parcel in) { 191 zoneId = in.readInt(); 192 occupantType = in.readInt(); 193 seat = in.readInt(); 194 } 195 196 @Override 197 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) 198 @AddedInOrBefore(majorVersion = 33) describeContents()199 public int describeContents() { 200 return 0; 201 } 202 203 @Override 204 @AddedInOrBefore(majorVersion = 33) writeToParcel(Parcel dest, int flags)205 public void writeToParcel(Parcel dest, int flags) { 206 dest.writeInt(zoneId); 207 dest.writeInt(occupantType); 208 dest.writeInt(seat); 209 } 210 211 @Override equals(Object other)212 public boolean equals(Object other) { 213 if (this == other) { 214 return true; 215 } 216 if (!(other instanceof OccupantZoneInfo)) { 217 return false; 218 } 219 OccupantZoneInfo that = (OccupantZoneInfo) other; 220 return zoneId == that.zoneId && occupantType == that.occupantType 221 && seat == that.seat; 222 } 223 224 @Override hashCode()225 public int hashCode() { 226 int hash = 23; 227 hash = hash * 17 + zoneId; 228 hash = hash * 17 + occupantType; 229 hash = hash * 17 + seat; 230 return hash; 231 } 232 233 @AddedInOrBefore(majorVersion = 33) 234 public static final Parcelable.Creator<OccupantZoneInfo> CREATOR = 235 new Parcelable.Creator<>() { 236 public OccupantZoneInfo createFromParcel(Parcel in) { 237 return new OccupantZoneInfo(in); 238 } 239 240 public OccupantZoneInfo[] newArray(int size) { 241 return new OccupantZoneInfo[size]; 242 } 243 }; 244 245 @Override toString()246 public String toString() { 247 StringBuilder b = new StringBuilder(64); 248 b.append("OccupantZoneInfo{zoneId="); 249 b.append(zoneId); 250 b.append(" type="); 251 b.append(occupantType); 252 b.append(" seat="); 253 b.append(Integer.toHexString(seat)); 254 b.append("}"); 255 return b.toString(); 256 } 257 } 258 259 /** 260 * Zone config change caused by display changes. A display could have been added / removed. 261 * Besides change in display itself. this can lead into removal / addition of passenger zones. 262 */ 263 @AddedInOrBefore(majorVersion = 33) 264 public static final int ZONE_CONFIG_CHANGE_FLAG_DISPLAY = 0x1; 265 266 /** Zone config change caused by user change. Assigned user for passenger zones have changed. */ 267 @AddedInOrBefore(majorVersion = 33) 268 public static final int ZONE_CONFIG_CHANGE_FLAG_USER = 0x2; 269 270 /** 271 * Zone config change caused by audio zone change. 272 * Assigned audio zone for passenger zones have changed. 273 **/ 274 @AddedInOrBefore(majorVersion = 33) 275 public static final int ZONE_CONFIG_CHANGE_FLAG_AUDIO = 0x4; 276 277 /** @hide */ 278 @IntDef(flag = true, prefix = {"ZONE_CONFIG_CHANGE_FLAG_"}, value = { 279 ZONE_CONFIG_CHANGE_FLAG_DISPLAY, 280 ZONE_CONFIG_CHANGE_FLAG_USER, 281 ZONE_CONFIG_CHANGE_FLAG_AUDIO, 282 }) 283 @Retention(RetentionPolicy.SOURCE) 284 @interface ZoneConfigChangeFlags {} 285 286 /** 287 * The assignment was successful. 288 * 289 * @hide 290 */ 291 @SystemApi 292 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 293 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 294 public static final int USER_ASSIGNMENT_RESULT_OK = 0; 295 296 /** 297 * The operation has failed as the user is already assigned to other zone. If the goal is to 298 * move the user, the current zone should be unassigned first. 299 * 300 * @hide 301 */ 302 @SystemApi 303 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 304 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 305 public static final int USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED = 1; 306 307 /** 308 * The assigned user is not a {@link UserManager#isUserVisible() visible user}. 309 * 310 * @hide 311 */ 312 @SystemApi 313 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 314 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 315 public static final int USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER = 2; 316 317 /** 318 * Assigning non-current user to driver zone or un-assigning driver zone will fail with this 319 * error. 320 * 321 * @hide 322 */ 323 @SystemApi 324 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 325 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 326 public static final int USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE = 3; 327 328 /** @hide */ 329 @IntDef(flag = false, prefix = {"USER_ASSIGNMENT_RESULT_"}, value = { 330 USER_ASSIGNMENT_RESULT_OK, 331 USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED, 332 USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER, 333 USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE, 334 }) 335 @Retention(RetentionPolicy.SOURCE) 336 @interface UserAssignmentResult {} 337 338 /** 339 * Invalid user ID. Zone with this user ID has no allocated user. Should have the same value 340 * with {@link UserHandle#USER_NULL}. 341 */ 342 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 343 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 344 public static final @UserIdInt int INVALID_USER_ID = -10000; 345 346 /** 347 * Listener to monitor any Occupant Zone configuration change. The configuration change can 348 * involve some displays removed or new displays added. Also it can happen when assigned user 349 * for any zone changes. 350 */ 351 public interface OccupantZoneConfigChangeListener { 352 353 /** 354 * Configuration for occupant zones has changed. Apps should re-check all 355 * occupant zone configs. This can be caused by events like user switching and 356 * display addition / removal. 357 * 358 * @param changeFlags Reason for the zone change. 359 */ 360 @AddedInOrBefore(majorVersion = 33) onOccupantZoneConfigChanged(@oneConfigChangeFlags int changeFlags)361 void onOccupantZoneConfigChanged(@ZoneConfigChangeFlags int changeFlags); 362 } 363 364 private final DisplayManager mDisplayManager; 365 private final EventHandler mEventHandler; 366 367 private final ICarOccupantZone mService; 368 369 private final ICarOccupantZoneCallbackImpl mBinderCallback; 370 371 private final CopyOnWriteArrayList<OccupantZoneConfigChangeListener> mListeners = 372 new CopyOnWriteArrayList<>(); 373 374 /** 375 * Gets an instance of the CarOccupantZoneManager. 376 * 377 * <p>Should not be obtained directly by clients, use {@link Car#getCarManager(String)} instead. 378 * 379 * @hide 380 */ CarOccupantZoneManager(Car car, IBinder service)381 public CarOccupantZoneManager(Car car, IBinder service) { 382 super(car); 383 mService = ICarOccupantZone.Stub.asInterface(service); 384 mBinderCallback = new ICarOccupantZoneCallbackImpl(this); 385 mDisplayManager = getContext().getSystemService(DisplayManager.class); 386 mEventHandler = new EventHandler(getEventHandler().getLooper()); 387 } 388 389 /** 390 * Returns all available occupants in the system. If no occupant zone is defined in the system 391 * or none is available at the moment, it will return empty list. 392 */ 393 @NonNull 394 @AddedInOrBefore(majorVersion = 33) getAllOccupantZones()395 public List<OccupantZoneInfo> getAllOccupantZones() { 396 try { 397 return mService.getAllOccupantZones(); 398 } catch (RemoteException e) { 399 return handleRemoteExceptionFromCarService(e, Collections.emptyList()); 400 } 401 } 402 403 /** 404 * Returns all displays assigned for the given occupant zone. If no display is available for 405 * the passenger, it will return empty list. 406 */ 407 @NonNull 408 @AddedInOrBefore(majorVersion = 33) getAllDisplaysForOccupant(@onNull OccupantZoneInfo occupantZone)409 public List<Display> getAllDisplaysForOccupant(@NonNull OccupantZoneInfo occupantZone) { 410 assertNonNullOccupant(occupantZone); 411 try { 412 int[] displayIds = mService.getAllDisplaysForOccupantZone(occupantZone.zoneId); 413 ArrayList<Display> displays = new ArrayList<>(displayIds.length); 414 for (int i = 0; i < displayIds.length; i++) { 415 // quick confidence check while getDisplay can still handle invalid display 416 if (displayIds[i] == Display.INVALID_DISPLAY) { 417 continue; 418 } 419 Display display = mDisplayManager.getDisplay(displayIds[i]); 420 if (display != null) { // necessary as display list could have changed. 421 displays.add(display); 422 } 423 } 424 return displays; 425 } catch (RemoteException e) { 426 return handleRemoteExceptionFromCarService(e, Collections.emptyList()); 427 } 428 } 429 430 /** 431 * Gets the display for the occupant for the specified display type, or returns {@code null} 432 * if no display matches the requirements. 433 * 434 * @param displayType This should be a valid display type and passing 435 * {@link #DISPLAY_TYPE_UNKNOWN} will always lead into {@code null} return. 436 */ 437 @Nullable 438 @AddedInOrBefore(majorVersion = 33) getDisplayForOccupant(@onNull OccupantZoneInfo occupantZone, @DisplayTypeEnum int displayType)439 public Display getDisplayForOccupant(@NonNull OccupantZoneInfo occupantZone, 440 @DisplayTypeEnum int displayType) { 441 assertNonNullOccupant(occupantZone); 442 try { 443 int displayId = mService.getDisplayForOccupant(occupantZone.zoneId, displayType); 444 // quick confidence check while getDisplay can still handle invalid display 445 if (displayId == Display.INVALID_DISPLAY) { 446 return null; 447 } 448 return mDisplayManager.getDisplay(displayId); 449 } catch (RemoteException e) { 450 return handleRemoteExceptionFromCarService(e, null); 451 } 452 } 453 454 /** 455 * Returns the display id for the driver. 456 * 457 * <p>This method just returns the display id for the requested type. The returned display id 458 * may correspond to a private display and the client may not have access to it. 459 * 460 * @param displayType the display type 461 * @return the driver's display id or {@link Display#INVALID_DISPLAY} when no such display 462 * exists or if the driver zone does not exist. 463 * 464 * @hide 465 */ 466 @SystemApi 467 @RequiresPermission(Car.ACCESS_PRIVATE_DISPLAY_ID) 468 @AddedInOrBefore(majorVersion = 33) getDisplayIdForDriver(@isplayTypeEnum int displayType)469 public int getDisplayIdForDriver(@DisplayTypeEnum int displayType) { 470 try { 471 return mService.getDisplayIdForDriver(displayType); 472 } catch (RemoteException e) { 473 return handleRemoteExceptionFromCarService(e, Display.INVALID_DISPLAY); 474 } 475 } 476 477 /** 478 * Gets the audio zone id for the occupant, or returns 479 * {@code CarAudioManager.INVALID_AUDIO_ZONE} if no audio zone matches the requirements. 480 * throws InvalidArgumentException if occupantZone does not exist. 481 * 482 * @hide 483 */ 484 @SystemApi 485 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 486 @AddedInOrBefore(majorVersion = 33) getAudioZoneIdForOccupant(@onNull OccupantZoneInfo occupantZone)487 public int getAudioZoneIdForOccupant(@NonNull OccupantZoneInfo occupantZone) { 488 assertNonNullOccupant(occupantZone); 489 try { 490 return mService.getAudioZoneIdForOccupant(occupantZone.zoneId); 491 } catch (RemoteException e) { 492 return handleRemoteExceptionFromCarService(e, null); 493 } 494 } 495 496 /** 497 * Gets occupant for the audio zone id, or returns {@code null} 498 * if no audio zone matches the requirements. 499 * 500 * @hide 501 */ 502 @Nullable 503 @SystemApi 504 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 505 @AddedInOrBefore(majorVersion = 33) getOccupantForAudioZoneId(int audioZoneId)506 public OccupantZoneInfo getOccupantForAudioZoneId(int audioZoneId) { 507 try { 508 return mService.getOccupantForAudioZoneId(audioZoneId); 509 } catch (RemoteException e) { 510 return handleRemoteExceptionFromCarService(e, null); 511 } 512 } 513 514 /** 515 * Returns assigned display type for the display. It will return {@link #DISPLAY_TYPE_UNKNOWN} 516 * if type is not specified or if display is no longer available. 517 */ 518 @DisplayTypeEnum 519 @AddedInOrBefore(majorVersion = 33) getDisplayType(@onNull Display display)520 public int getDisplayType(@NonNull Display display) { 521 assertNonNullDisplay(display); 522 try { 523 return mService.getDisplayType(display.getDisplayId()); 524 } catch (RemoteException e) { 525 return handleRemoteExceptionFromCarService(e, DISPLAY_TYPE_UNKNOWN); 526 } 527 } 528 529 /** 530 * Returns android user id assigned for the given zone. It will return 531 * {@link #INVALID_USER_ID} if user is not assigned or if zone is not available. 532 */ 533 @UserIdInt 534 @AddedInOrBefore(majorVersion = 33) getUserForOccupant(@onNull OccupantZoneInfo occupantZone)535 public int getUserForOccupant(@NonNull OccupantZoneInfo occupantZone) { 536 assertNonNullOccupant(occupantZone); 537 try { 538 return mService.getUserForOccupant(occupantZone.zoneId); 539 } catch (RemoteException e) { 540 return handleRemoteExceptionFromCarService(e, INVALID_USER_ID); 541 } 542 } 543 544 /** 545 * Returns assigned user id for the given display id. 546 * 547 * @param displayId Should be valid display id. Passing invalid display id will lead into 548 * getting {@link #INVALID_USER_ID} result. 549 * @return Valid user id or {@link #INVALID_USER_ID} if no user is assigned for the display. 550 */ 551 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 552 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 553 @UserIdInt getUserForDisplayId(int displayId)554 public int getUserForDisplayId(int displayId) { 555 try { 556 return mService.getUserForDisplayId(displayId); 557 } catch (RemoteException e) { 558 return handleRemoteExceptionFromCarService(e, INVALID_USER_ID); 559 } 560 } 561 562 /** 563 * Returns the info for the occupant zone that has the display identified by the given 564 * {@code displayId}. 565 * 566 * @param displayId Should be valid display id. Passing in invalid display id will lead into 567 * getting {@code null} occupant zone info result. 568 * @return Occupant zone info or {@code null} if no occupant zone is found which has the given 569 * display. 570 * 571 * @hide 572 */ 573 @SystemApi 574 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 575 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 576 @Nullable getOccupantZoneForDisplayId(int displayId)577 public OccupantZoneInfo getOccupantZoneForDisplayId(int displayId) { 578 try { 579 return mService.getOccupantZoneForDisplayId(displayId); 580 } catch (RemoteException e) { 581 return handleRemoteExceptionFromCarService(e, null); 582 } 583 } 584 585 /** 586 * Assigns the given profile {@code userId} to the {@code occupantZone}. Returns true when the 587 * request succeeds. 588 * 589 * <p>Note that only non-driver zone can be assigned with this call. Calling this for driver 590 * zone will lead into {@code IllegalArgumentException}. 591 * 592 * @param occupantZone Zone to assign user. 593 * @param userId profile user id to assign. Passing {@link #INVALID_USER_ID} leads into 594 * removing the current user assignment. 595 * @return true if the request succeeds or if the user is already assigned to the zone. 596 * @deprecated Use {@link #assignVisibleUserToOccupantZone(OccupantZoneInfo, UserHandle)} 597 * instead. 598 * 599 * @hide 600 */ 601 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 602 Car.PERMISSION_MANAGE_OCCUPANT_ZONE}) 603 @AddedInOrBefore(majorVersion = 33) 604 @Deprecated assignProfileUserToOccupantZone(@onNull OccupantZoneInfo occupantZone, @UserIdInt int userId)605 public boolean assignProfileUserToOccupantZone(@NonNull OccupantZoneInfo occupantZone, 606 @UserIdInt int userId) { 607 assertNonNullOccupant(occupantZone); 608 try { 609 return mService.assignProfileUserToOccupantZone(occupantZone.zoneId, userId); 610 } catch (RemoteException e) { 611 return handleRemoteExceptionFromCarService(e, false); 612 } 613 } 614 615 /** 616 * Assign a visible user, which gets {@code true} from ({@link UserManager#isUserVisible()}, 617 * to the specified occupant zone. 618 * 619 * <p>This API handles occupant zone change. 620 * 621 * <p>This API can take a long time, so it is recommended to call this from non-main thread. 622 * 623 * <p> The return value is {@link #USER_ASSIGNMENT_RESULT_OK} when the assignment succeeds or if 624 * the user is already allocated to the zone. Note that new error code can be added in the 625 * future. For now, following error codes will be returned for a Failure: 626 * <ul> 627 * <li>{@link #USER_ASSIGNMENT_RESULT_FAIL_NON_VISIBLE_USER} for non-visible user. 628 * <li>{@link #USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED} if the user is already assigned 629 * to other zone. New error code can be added in future. 630 * <li>{@link #USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE} if non-current user is assigned to the 631 * driver zone. 632 * </ul> 633 * 634 * <p>The system requires one user to have one zone and moving user from one zone to another 635 * requires unassigning the zone using {@link #unassignOccupantZone(OccupantZoneInfo)} 636 * first. 637 * 638 * @param occupantZone the occupant zone to change user allocation 639 * @param user the user to allocate. {@code null} user removes the allocation for the zone 640 * {@link UserHandle#CURRENT} will assign the current user to the zone 641 * @return Check the above 642 * 643 * @hide 644 */ 645 @SystemApi 646 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 647 Car.PERMISSION_MANAGE_OCCUPANT_ZONE}) 648 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 649 minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) 650 @UserAssignmentResult assignVisibleUserToOccupantZone(@onNull OccupantZoneInfo occupantZone, @NonNull UserHandle user)651 public int assignVisibleUserToOccupantZone(@NonNull OccupantZoneInfo occupantZone, 652 @NonNull UserHandle user) { 653 assertPlatformVersionAtLeastU(); 654 assertNonNullOccupant(occupantZone); 655 try { 656 return mService.assignVisibleUserToOccupantZone(occupantZone.zoneId, user); 657 } catch (RemoteException e) { 658 // Return any error code if car service is gone. 659 return handleRemoteExceptionFromCarService(e, 660 USER_ASSIGNMENT_RESULT_FAIL_ALREADY_ASSIGNED); 661 } 662 } 663 664 /** 665 * Un-assign user from the specified occupant zone. The zone will return 666 * {@link #INVALID_USER_ID} for {@link #getUserForOccupant(OccupantZoneInfo)} after this call. 667 * 668 * @param occupantZone Zone to unassign. 669 * @return {@link #USER_ASSIGNMENT_RESULT_OK} if the zone is unassigned by the call or was 670 * already unassigned. Error code of {@link #USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE} 671 * will be returned if driver zone is asked. 672 * 673 * @hide 674 */ 675 @SystemApi 676 @RequiresPermission(anyOf = {android.Manifest.permission.MANAGE_USERS, 677 Car.PERMISSION_MANAGE_OCCUPANT_ZONE}) 678 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 679 minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0) 680 @UserAssignmentResult unassignOccupantZone(@onNull OccupantZoneInfo occupantZone)681 public int unassignOccupantZone(@NonNull OccupantZoneInfo occupantZone) { 682 assertPlatformVersionAtLeastU(); 683 try { 684 return mService.unassignOccupantZone(occupantZone.zoneId); 685 } catch (RemoteException e) { 686 // Return any error code if car service is gone. 687 return handleRemoteExceptionFromCarService(e, USER_ASSIGNMENT_RESULT_FAIL_DRIVER_ZONE); 688 } 689 } 690 assertNonNullOccupant(OccupantZoneInfo occupantZone)691 private void assertNonNullOccupant(OccupantZoneInfo occupantZone) { 692 if (occupantZone == null) { 693 throw new IllegalArgumentException("null OccupantZoneInfo"); 694 } 695 } 696 assertNonNullDisplay(Display display)697 private void assertNonNullDisplay(Display display) { 698 if (display == null) { 699 throw new IllegalArgumentException("null Display"); 700 } 701 } 702 703 /** 704 * Registers the listener for occupant zone config change. Registering multiple listeners are 705 * allowed. 706 */ 707 @AddedInOrBefore(majorVersion = 33) registerOccupantZoneConfigChangeListener( @onNull OccupantZoneConfigChangeListener listener)708 public void registerOccupantZoneConfigChangeListener( 709 @NonNull OccupantZoneConfigChangeListener listener) { 710 if (mListeners.addIfAbsent(listener)) { 711 if (mListeners.size() == 1) { 712 try { 713 mService.registerCallback(mBinderCallback); 714 } catch (RemoteException e) { 715 handleRemoteExceptionFromCarService(e); 716 } 717 } 718 } 719 } 720 721 /** 722 * Unregisters the listener. Listeners not registered before will be ignored. 723 */ 724 @AddedInOrBefore(majorVersion = 33) unregisterOccupantZoneConfigChangeListener( @onNull OccupantZoneConfigChangeListener listener)725 public void unregisterOccupantZoneConfigChangeListener( 726 @NonNull OccupantZoneConfigChangeListener listener) { 727 if (mListeners.remove(listener)) { 728 if (mListeners.size() == 0) { 729 try { 730 mService.unregisterCallback(mBinderCallback); 731 } catch (RemoteException ignored) { 732 // ignore for unregistering 733 } 734 } 735 } 736 } 737 738 /** 739 * Returns {@link OccupantZoneInfo} for the calling process's android user. 740 * It will return {@code null} if there is no occupant zone assigned for the user. 741 * 742 * <p>When there is no occupant zone allocated for the user, most likely the user is not allowed 743 * to run Activity or play audio, which are the main use cases to get the zone. So apps should 744 * not try such tasks when {@code null} {@code OccupantZoneInfo} is returned. There can be an 745 * exception for system user running under 746 * {@link UserManager#isHeadlessSystemUserMode() Headless System User Mode}: The system user 747 * apps may show UI even if there is no zone allocated. 748 */ 749 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 750 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 751 @Nullable getMyOccupantZone()752 public OccupantZoneInfo getMyOccupantZone() { 753 try { 754 return mService.getMyOccupantZone(); 755 } catch (RemoteException e) { 756 return handleRemoteExceptionFromCarService(e, null); 757 } 758 } 759 760 /** 761 * Returns {@code OccupantZoneInfo} associated with the given {@code UserHandle}. In the case 762 * that the user is associated with multiple zones, this API returns the first matched zone. 763 * 764 * @param user The user to find. 765 * @return Matching occupant zone or {@code null} if the user is not assigned or user has a 766 * userId of {@code UserHandle#USER_NULL}. 767 */ 768 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 769 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 770 @SuppressWarnings("UserHandle") 771 @Nullable getOccupantZoneForUser(@onNull UserHandle user)772 public OccupantZoneInfo getOccupantZoneForUser(@NonNull UserHandle user) { 773 try { 774 return mService.getOccupantZoneForUser(user); 775 } catch (RemoteException e) { 776 return handleRemoteExceptionFromCarService(e, /* returnValue= */null); 777 } 778 } 779 780 /** 781 * Finds {@code OccupantZoneInfo} for the given occupant type and seat. 782 * <p>For{@link #OCCUPANT_TYPE_DRIVER} and {@link #OCCUPANT_TYPE_FRONT_PASSENGER}, {@code seat} 783 * argument will be ignored. 784 * 785 * @param occupantType should be one of {@link #OCCUPANT_TYPE_DRIVER}, 786 * {@link #OCCUPANT_TYPE_FRONT_PASSENGER}, 787 * {@link #OCCUPANT_TYPE_FRONT_PASSENGER} 788 * @param seat Seat of the occupant. This is necessary for 789 * {@link #OCCUPANT_TYPE_REAR_PASSENGER}. 790 * @return Matching occupant zone or {@code null} if such zone does not exist. 791 */ 792 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 793 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 794 @Nullable getOccupantZone(@ccupantTypeEnum int occupantType, @VehicleAreaSeat.Enum int seat)795 public OccupantZoneInfo getOccupantZone(@OccupantTypeEnum int occupantType, 796 @VehicleAreaSeat.Enum int seat) { 797 try { 798 return mService.getOccupantZone(occupantType, seat); 799 } catch (RemoteException e) { 800 return handleRemoteExceptionFromCarService(e, null); 801 } 802 803 } 804 805 /** 806 * Returns {@code true} if the system has a driver zone. It will return false for system with 807 * only passenger zones. 808 * 809 * <p> Note that at least one zone must be present and following system configurations are 810 * possible: 811 * <ul> 812 * <li>One driver zone only. 813 * <li>One driver zone with at least one passenger zone. 814 * <li>At least one passenger zone. 815 * </ul> 816 */ 817 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 818 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) hasDriverZone()819 public boolean hasDriverZone() { 820 try { 821 return mService.hasDriverZone(); 822 } catch (RemoteException e) { 823 return handleRemoteExceptionFromCarService(e, false); 824 } 825 } 826 827 /** 828 * Returns {@code true} if the system has front or rear passenger zones. Check 829 * {@link #hasDriverZone()} for possible system configurations. 830 */ 831 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 832 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) hasPassengerZones()833 public boolean hasPassengerZones() { 834 try { 835 return mService.hasPassengerZones(); 836 } catch (RemoteException e) { 837 return handleRemoteExceptionFromCarService(e, false); 838 } 839 } 840 handleOnOccupantZoneConfigChanged(int flags)841 private void handleOnOccupantZoneConfigChanged(int flags) { 842 for (OccupantZoneConfigChangeListener listener : mListeners) { 843 listener.onOccupantZoneConfigChanged(flags); 844 } 845 } 846 847 private final class EventHandler extends Handler { 848 private static final int MSG_ZONE_CHANGE = 1; 849 EventHandler(Looper looper)850 private EventHandler(Looper looper) { 851 super(looper); 852 } 853 854 @Override handleMessage(Message msg)855 public void handleMessage(Message msg) { 856 switch (msg.what) { 857 case MSG_ZONE_CHANGE: 858 handleOnOccupantZoneConfigChanged(msg.arg1); 859 break; 860 default: 861 Log.e(TAG, "Unknown msg not handdled:" + msg.what); 862 break; 863 } 864 } 865 dispatchOnOccupantZoneConfigChanged(int flags)866 private void dispatchOnOccupantZoneConfigChanged(int flags) { 867 sendMessage(obtainMessage(MSG_ZONE_CHANGE, flags, 0)); 868 } 869 } 870 871 private static class ICarOccupantZoneCallbackImpl extends ICarOccupantZoneCallback.Stub { 872 private final WeakReference<CarOccupantZoneManager> mManager; 873 ICarOccupantZoneCallbackImpl(CarOccupantZoneManager manager)874 private ICarOccupantZoneCallbackImpl(CarOccupantZoneManager manager) { 875 mManager = new WeakReference<>(manager); 876 } 877 878 @Override onOccupantZoneConfigChanged(int flags)879 public void onOccupantZoneConfigChanged(int flags) { 880 CarOccupantZoneManager manager = mManager.get(); 881 if (manager != null) { 882 manager.mEventHandler.dispatchOnOccupantZoneConfigChanged(flags); 883 } 884 } 885 } 886 887 /** @hide */ 888 @Override 889 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()890 public void onCarDisconnected() { 891 // nothing to do 892 } 893 894 /** 895 * Returns the supported input types for the occupant zone info and display type passed as 896 * the argument. 897 * 898 * <p>It returns an empty list if the input type is unknown. Starting in Android 899 * {@link android.os.Build.VERSION_CODES#UPSIDE_DOWN_CAKE}, all associated occupant zones and 900 * display types in {@code config_occupant_display_mapping} must define at least one input type. 901 * 902 * <p>If the display doesn't have any input type associated, then it should return a list 903 * containing {@link android.car.input.CarInputManager#INPUT_TYPE_NONE} only. 904 * 905 * <p>This is the list of all available input types this method may return: 906 * <ul> 907 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_ROTARY_NAVIGATION}</li> 908 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_ROTARY_VOLUME}</li> 909 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_DPAD_KEYS}</li> 910 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_NAVIGATE_KEYS}</li> 911 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_SYSTEM_NAVIGATE_KEYS}</li> 912 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_CUSTOM_INPUT_EVENT}</li> 913 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_TOUCH_SCREEN}</li> 914 * <li>{@link android.car.input.CarInputManager#INPUT_TYPE_NONE}</li> 915 * </ul> 916 * 917 * @param occupantZoneInfo the occupant zone info of the supported input types to find 918 * @param displayType the display type of the supported input types to find 919 * @return the supported input types for the occupant zone info and display type passed in as 920 * the argument (see the full list of supported input types in the above) 921 */ 922 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0, 923 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 924 @NonNull getSupportedInputTypes(@onNull OccupantZoneInfo occupantZoneInfo, @DisplayTypeEnum int displayType)925 public List<Integer> getSupportedInputTypes(@NonNull OccupantZoneInfo occupantZoneInfo, 926 @DisplayTypeEnum int displayType) { 927 assertNonNullOccupant(occupantZoneInfo); 928 List<Integer> inputTypes; 929 try { 930 int[] ints = mService.getSupportedInputTypes(occupantZoneInfo.zoneId, displayType); 931 inputTypes = Lists.asImmutableList(ints); 932 } catch (RemoteException e) { 933 inputTypes = handleRemoteExceptionFromCarService(e, Collections.emptyList()); 934 } 935 return inputTypes; 936 } 937 } 938