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