1 /* 2 * Copyright (C) 2015 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 package android.car.media; 17 18 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.BOILERPLATE_CODE; 19 import static com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport.DEPRECATED_CODE; 20 import static com.android.car.internal.common.CommonConstants.EMPTY_INT_ARRAY; 21 22 import android.annotation.CallbackExecutor; 23 import android.annotation.FlaggedApi; 24 import android.annotation.IntDef; 25 import android.annotation.NonNull; 26 import android.annotation.Nullable; 27 import android.annotation.RequiresPermission; 28 import android.annotation.SystemApi; 29 import android.annotation.TestApi; 30 import android.car.Car; 31 import android.car.CarLibLog; 32 import android.car.CarManagerBase; 33 import android.car.CarOccupantZoneManager; 34 import android.car.CarOccupantZoneManager.OccupantZoneInfo; 35 import android.car.feature.Flags; 36 import android.media.AudioAttributes; 37 import android.media.AudioDeviceAttributes; 38 import android.media.AudioDeviceInfo; 39 import android.media.AudioManager; 40 import android.os.Binder; 41 import android.os.Bundle; 42 import android.os.Handler; 43 import android.os.IBinder; 44 import android.os.Looper; 45 import android.os.Message; 46 import android.os.RemoteException; 47 import android.util.Slog; 48 49 import com.android.car.internal.ExcludeFromCodeCoverageGeneratedReport; 50 import com.android.car.internal.ICarBase; 51 import com.android.car.internal.annotation.AttributeUsage; 52 import com.android.internal.annotations.GuardedBy; 53 54 import java.lang.annotation.Retention; 55 import java.lang.annotation.RetentionPolicy; 56 import java.util.ArrayList; 57 import java.util.Collections; 58 import java.util.HashSet; 59 import java.util.List; 60 import java.util.Objects; 61 import java.util.Set; 62 import java.util.concurrent.ConcurrentHashMap; 63 import java.util.concurrent.CopyOnWriteArrayList; 64 import java.util.concurrent.Executor; 65 66 /** 67 * APIs for handling audio in a car. 68 * 69 * <p>In a car environment, we introduced the support to turn audio dynamic routing on/off by 70 * setting the "audioUseDynamicRouting" attribute in config.xml</p> 71 * 72 * <p>When audio dynamic routing is enabled:</p> 73 * <ui> 74 * <li>Audio devices are grouped into zones</li> 75 * <li>There is at least one primary zone, and extra secondary zones such as RSE 76 * (Rear Seat Entertainment)</li> 77 * <li> Within each zone, audio devices are grouped into volume groups for volume control</li> 78 * <li> Audio is assigned to an audio device based on its AudioAttributes usage</li> 79 * </ui> 80 * 81 * 82 * <p>When audio dynamic routing is disabled:</p> 83 * <ui> 84 * <li>There is exactly one audio zone, which is the primary zone</li> 85 * <li>Each volume group represents a controllable STREAM_TYPE, same as AudioManager</li> 86 * </ui> 87 */ 88 public final class CarAudioManager extends CarManagerBase { 89 90 private static final String TAG = CarAudioManager.class.getSimpleName(); 91 92 /** 93 * Zone id of the primary audio zone. 94 * @hide 95 */ 96 @SystemApi 97 public static final int PRIMARY_AUDIO_ZONE = 0x0; 98 99 /** 100 * Zone id of the invalid audio zone. 101 * @hide 102 */ 103 @SystemApi 104 public static final int INVALID_AUDIO_ZONE = 0xffffffff; 105 106 /** 107 * This is used to determine if dynamic routing is enabled via 108 * {@link #isAudioFeatureEnabled(int)} 109 */ 110 public static final int AUDIO_FEATURE_DYNAMIC_ROUTING = 1; 111 112 /** 113 * This is used to determine if volume group muting is enabled via 114 * {@link #isAudioFeatureEnabled(int)} 115 * 116 * <p> 117 * If enabled, car volume group muting APIs can be used to mute each volume group, 118 * also car volume group muting changed callback will be called upon group mute changes. If 119 * disabled, car volume will toggle master mute instead. 120 */ 121 public static final int AUDIO_FEATURE_VOLUME_GROUP_MUTING = 2; 122 123 /** 124 * This is used to determine if the OEM audio service is enabled via 125 * {@link #isAudioFeatureEnabled(int)} 126 * 127 * <p>If enabled, car audio focus, car audio volume, and ducking control behaviour can change 128 * as it can be OEM dependent. 129 */ 130 public static final int AUDIO_FEATURE_OEM_AUDIO_SERVICE = 3; 131 132 /** 133 * This is used to determine if volume group events is supported via 134 * {@link #isAudioFeatureEnabled(int)} 135 * 136 * <p>If enabled, the car volume group event callback can be used to receive event changes 137 * to volume, mute, attenuation. 138 * If disabled, the register/unregister APIs will return {@code false}. 139 */ 140 public static final int AUDIO_FEATURE_VOLUME_GROUP_EVENTS = 4; 141 142 /** 143 * This is used to determine if audio mirroring is supported via 144 * {@link #isAudioFeatureEnabled(int)} 145 * 146 * <p>If enabled, audio mirroring can be managed by using the following APIs: 147 * {@link #setAudioZoneMirrorStatusCallback(Executor, AudioZonesMirrorStatusCallback)}, 148 * {@link #clearAudioZonesMirrorStatusCallback()}, {@link #canEnableAudioMirror()}, 149 * {@link #enableMirrorForAudioZones(List)}, {@link #extendAudioMirrorRequest(long, List)}, 150 * {@link #disableAudioMirrorForZone(int)}, {@link #disableAudioMirror(long)}, 151 * {@link #getMirrorAudioZonesForAudioZone(int)}, 152 * {@link #getMirrorAudioZonesForMirrorRequest(long)} 153 */ 154 public static final int AUDIO_FEATURE_AUDIO_MIRRORING = 5; 155 156 /** 157 * This is used to determine if min/max activation volume level is supported via 158 * {@link #isAudioFeatureEnabled(int)} 159 * 160 * <p>If enabled, the volume of the volume group with min/max activation volume setting 161 * will be set to min activation volume or max activation volume if volume during activation 162 * is lower than min activation volume or higher than max activation volume respectively. 163 */ 164 @FlaggedApi(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME) 165 public static final int AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME = 6; 166 167 /** 168 * This is used to determine if fade and balance values are persisted via 169 * {@link #isAudioFeatureEnabled(int)} 170 * 171 * <p>If this feature is enabled, the fade and balance values set using 172 * {@link #setFadeTowardFront(float)} and {@link #setBalanceTowardRight(float)} will be: 173 * <ul> 174 * <li>Persisted per user.</li> 175 * <li>Persistence occurs across ignition cycles.</li> 176 * <li>Persistence applies to the cabin zone or the primary audio zone (only).</li> 177 * </ul> 178 * 179 * <p>If this feature is disabled, the fade and balance settings will not be saved and will 180 * revert to defaults on the next power cycle or user change. 181 */ 182 @FlaggedApi(Flags.FLAG_AUDIO_FADE_BALANCE_GETTER_APIS) 183 public static final int AUDIO_FEATURE_PERSIST_FADE_BALANCE_VALUES = 7; 184 185 /** @hide */ 186 @IntDef(flag = false, prefix = "AUDIO_FEATURE", value = { 187 AUDIO_FEATURE_DYNAMIC_ROUTING, 188 AUDIO_FEATURE_VOLUME_GROUP_MUTING, 189 AUDIO_FEATURE_OEM_AUDIO_SERVICE, 190 AUDIO_FEATURE_VOLUME_GROUP_EVENTS, 191 AUDIO_FEATURE_AUDIO_MIRRORING, 192 AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME, 193 AUDIO_FEATURE_PERSIST_FADE_BALANCE_VALUES 194 }) 195 @Retention(RetentionPolicy.SOURCE) 196 public @interface CarAudioFeature {} 197 198 /** 199 * Volume Group ID when volume group not found. 200 * @hide 201 */ 202 public static final int INVALID_VOLUME_GROUP_ID = -1; 203 204 /** 205 * Use to identify if the request from {@link #requestMediaAudioOnPrimaryZone} is invalid 206 * 207 * @hide 208 */ 209 @SystemApi 210 public static final long INVALID_REQUEST_ID = -1; 211 212 /** 213 * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an 214 * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus events, 215 * including {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. 216 * The requester must hold {@link Car#PERMISSION_RECEIVE_CAR_AUDIO_DUCKING_EVENTS}; otherwise, 217 * this extra is ignored. 218 * 219 * @hide 220 */ 221 @SystemApi 222 public static final String AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS = 223 "android.car.media.AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS"; 224 225 /** 226 * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an 227 * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus for the 228 * the zone. If the zone id is not defined: the audio focus request will default to the 229 * currently mapped zone for the requesting uid or {@link CarAudioManager#PRIMARY_AUDIO_ZONE} 230 * if no uid mapping currently exist. 231 * 232 * @hide 233 */ 234 public static final String AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID = 235 "android.car.media.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID"; 236 237 /** 238 * Use to inform media request callbacks about approval of a media request 239 * 240 * @hide 241 */ 242 @SystemApi 243 public static final int AUDIO_REQUEST_STATUS_APPROVED = 1; 244 245 /** 246 * Use to inform media request callbacks about rejection of a media request 247 * 248 * @hide 249 */ 250 @SystemApi 251 public static final int AUDIO_REQUEST_STATUS_REJECTED = 2; 252 253 /** 254 * Use to inform media request callbacks about cancellation of a pending request 255 * 256 * @hide 257 */ 258 @SystemApi 259 public static final int AUDIO_REQUEST_STATUS_CANCELLED = 3; 260 261 /** 262 * Use to inform media request callbacks about the stop of a media request 263 * 264 * @hide 265 */ 266 @SystemApi 267 public static final int AUDIO_REQUEST_STATUS_STOPPED = 4; 268 269 /** @hide */ 270 @IntDef(flag = false, prefix = "AUDIO_REQUEST_STATUS", value = { 271 AUDIO_REQUEST_STATUS_APPROVED, 272 AUDIO_REQUEST_STATUS_REJECTED, 273 AUDIO_REQUEST_STATUS_CANCELLED, 274 AUDIO_REQUEST_STATUS_STOPPED 275 }) 276 @Retention(RetentionPolicy.SOURCE) 277 public @interface MediaAudioRequestStatus {} 278 279 /** 280 * This will be returned by {@link #canEnableAudioMirror()} in case there is an error when 281 * calling the car audio service 282 * 283 * @hide 284 */ 285 @SystemApi 286 public static final int AUDIO_MIRROR_INTERNAL_ERROR = -1; 287 288 /** 289 * This will be returned by {@link #canEnableAudioMirror()} and determines that it is possible 290 * to enable audio mirroring using the {@link #enableMirrorForAudioZones(List)} 291 * 292 * @hide 293 */ 294 @SystemApi 295 public static final int AUDIO_MIRROR_CAN_ENABLE = 1; 296 297 /** 298 * This will be returned by {@link #canEnableAudioMirror()} and determines that it is not 299 * possible to enable audio mirroring using the {@link #enableMirrorForAudioZones(List)}. 300 * This informs that there are no more audio mirror output devices available to route audio. 301 * 302 * @hide 303 */ 304 @SystemApi 305 public static final int AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES = 2; 306 307 /** @hide */ 308 @IntDef(flag = false, prefix = "AUDIO_MIRROR_", value = { 309 AUDIO_MIRROR_INTERNAL_ERROR, 310 AUDIO_MIRROR_CAN_ENABLE, 311 AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES, 312 }) 313 @Retention(RetentionPolicy.SOURCE) 314 public @interface AudioMirrorStatus {} 315 316 /** 317 * Status indicating the dynamic audio configurations info have been updated. 318 * 319 * <p><b>Note</b> The list of devices on audio 320 * {@link AudioZoneConfigurationsChangeCallback#onAudioZoneConfigurationsChanged(List, int)}, 321 * will contain all the configuration and each configuration can be perused to find 322 * availability status. For an active configuration becoming disabled due to device 323 * availability, the {@link #CONFIG_STATUS_AUTO_SWITCHED} will be triggered instead. 324 * 325 * <p><b>Note</b> This API will only be triggered when a configuration's active status has 326 * changed due to a device connection state changing. 327 * 328 * @hide 329 */ 330 @SystemApi 331 @FlaggedApi(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES) 332 public static final int CONFIG_STATUS_CHANGED = 1; 333 334 /** 335 * Status indicating the dynamic audio config info has auto switched. 336 * 337 * <p><b>Note</b> The list of devices on audio 338 * {@link AudioZoneConfigurationsChangeCallback#onAudioZoneConfigurationsChanged(List, int)}, 339 * will contain the previously selected configuration and the newly selected configuration only. 340 * 341 * @hide 342 */ 343 @SystemApi 344 @FlaggedApi(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES) 345 public static final int CONFIG_STATUS_AUTO_SWITCHED = 2; 346 347 /** @hide */ 348 @IntDef(flag = false, prefix = "CONFIG_STATUS_", value = { 349 CONFIG_STATUS_CHANGED, 350 CONFIG_STATUS_AUTO_SWITCHED, 351 }) 352 @Retention(RetentionPolicy.SOURCE) 353 public @interface AudioConfigStatus {} 354 355 private final ICarAudio mService; 356 private final CopyOnWriteArrayList<CarVolumeCallback> mCarVolumeCallbacks; 357 private final CopyOnWriteArrayList<CarVolumeGroupEventCallbackWrapper> 358 mCarVolumeEventCallbacks = new CopyOnWriteArrayList<>(); 359 private final AudioManager mAudioManager; 360 361 private final EventHandler mEventHandler; 362 363 private final Object mLock = new Object(); 364 @GuardedBy("mLock") 365 private PrimaryZoneMediaAudioRequestCallback mPrimaryZoneMediaAudioRequestCallback; 366 @GuardedBy("mLock") 367 private Executor mPrimaryZoneMediaAudioRequestCallbackExecutor; 368 369 @GuardedBy("mLock") 370 private AudioZonesMirrorStatusCallbackWrapper mAudioZonesMirrorStatusCallbackWrapper; 371 372 @GuardedBy("mLock") 373 private AudioZoneConfigurationsChangeCallbackWrapper mZoneConfigurationsChangeCallbackWrapper; 374 375 private final ConcurrentHashMap<Long, MediaAudioRequestStatusCallbackWrapper> 376 mRequestIdToMediaAudioRequestStatusCallbacks = new ConcurrentHashMap<>(); 377 378 private final IPrimaryZoneMediaAudioRequestCallback mIPrimaryZoneMediaAudioRequestCallback = 379 new IPrimaryZoneMediaAudioRequestCallback.Stub() { 380 @Override 381 public void onRequestMediaOnPrimaryZone(OccupantZoneInfo info, 382 long requestId) { 383 runOnExecutor((callback) -> 384 callback.onRequestMediaOnPrimaryZone(info, requestId)); 385 } 386 387 @Override 388 public void onMediaAudioRequestStatusChanged( 389 @NonNull CarOccupantZoneManager.OccupantZoneInfo info, 390 long requestId, int status) throws RemoteException { 391 runOnExecutor((callback) -> 392 callback.onMediaAudioRequestStatusChanged(info, requestId, status)); 393 } 394 395 private void runOnExecutor(PrimaryZoneMediaAudioRequestCallbackRunner runner) { 396 PrimaryZoneMediaAudioRequestCallback callback; 397 Executor executor; 398 synchronized (mLock) { 399 if (mPrimaryZoneMediaAudioRequestCallbackExecutor == null 400 || mPrimaryZoneMediaAudioRequestCallback == null) { 401 Slog.w(TAG, "Media request removed before change dispatched"); 402 return; 403 } 404 callback = mPrimaryZoneMediaAudioRequestCallback; 405 executor = mPrimaryZoneMediaAudioRequestCallbackExecutor; 406 } 407 408 long identity = Binder.clearCallingIdentity(); 409 try { 410 executor.execute(() -> runner.runOnCallback(callback)); 411 } finally { 412 Binder.restoreCallingIdentity(identity); 413 } 414 } 415 }; 416 417 private interface PrimaryZoneMediaAudioRequestCallbackRunner { runOnCallback(PrimaryZoneMediaAudioRequestCallback callback)418 void runOnCallback(PrimaryZoneMediaAudioRequestCallback callback); 419 } 420 421 private final ICarVolumeCallback mCarVolumeCallbackImpl = 422 new android.car.media.ICarVolumeCallback.Stub() { 423 @Override 424 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { 425 mEventHandler.dispatchOnGroupVolumeChanged(zoneId, groupId, flags); 426 } 427 428 @Override 429 public void onGroupMuteChanged(int zoneId, int groupId, int flags) { 430 mEventHandler.dispatchOnGroupMuteChanged(zoneId, groupId, flags); 431 } 432 433 @Override 434 public void onMasterMuteChanged(int zoneId, int flags) { 435 mEventHandler.dispatchOnMasterMuteChanged(zoneId, flags); 436 } 437 }; 438 439 private final ICarVolumeEventCallback mCarVolumeEventCallbackImpl = 440 new android.car.media.ICarVolumeEventCallback.Stub() { 441 @Override 442 public void onVolumeGroupEvent(@NonNull List<CarVolumeGroupEvent> events) { 443 mEventHandler.dispatchOnVolumeGroupEvent(events); 444 } 445 446 @Override 447 public void onMasterMuteChanged(int zoneId, int flags) { 448 mEventHandler.dispatchOnMasterMuteChanged(zoneId, flags); 449 } 450 }; 451 452 /** 453 * @return Whether dynamic routing is enabled or not. 454 * 455 * @deprecated use {@link #isAudioFeatureEnabled(int AUDIO_FEATURE_DYNAMIC_ROUTING)} instead. 456 * 457 * @hide 458 */ 459 @TestApi 460 @Deprecated 461 @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE) isDynamicRoutingEnabled()462 public boolean isDynamicRoutingEnabled() { 463 return isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING); 464 } 465 466 /** 467 * Determines if an audio feature is enabled. 468 * 469 * @param audioFeature audio feature to query, can be any of: 470 * {@link #AUDIO_FEATURE_DYNAMIC_ROUTING}, 471 * {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING}, 472 * {@link #AUDIO_FEATURE_VOLUME_GROUP_EVENTS}, 473 * {@link #AUDIO_FEATURE_AUDIO_MIRRORING}, 474 * {@link #AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME} or 475 * {@link #AUDIO_FEATURE_PERSIST_FADE_BALANCE_VALUES} 476 * @return Returns {@code true} if the feature is enabled, {@code false} otherwise. 477 */ isAudioFeatureEnabled(@arAudioFeature int audioFeature)478 public boolean isAudioFeatureEnabled(@CarAudioFeature int audioFeature) { 479 try { 480 return mService.isAudioFeatureEnabled(audioFeature); 481 } catch (RemoteException e) { 482 return handleRemoteExceptionFromCarService(e, false); 483 } 484 } 485 486 /** 487 * Sets the volume index for a volume group in primary zone. 488 * 489 * @see #setGroupVolume(int, int, int, int) 490 * @hide 491 */ 492 @SystemApi 493 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) setGroupVolume(int groupId, int index, int flags)494 public void setGroupVolume(int groupId, int index, int flags) { 495 setGroupVolume(PRIMARY_AUDIO_ZONE, groupId, index, flags); 496 } 497 498 /** 499 * Sets the volume index for a volume group. 500 * 501 * @param zoneId The zone id whose volume group is affected. 502 * @param groupId The volume group id whose volume index should be set. 503 * @param index The volume index to set. See 504 * {@link #getGroupMaxVolume(int, int)} for the largest valid value. 505 * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI}, 506 * {@link android.media.AudioManager#FLAG_PLAY_SOUND}) 507 * @hide 508 */ 509 @SystemApi 510 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) setGroupVolume(int zoneId, int groupId, int index, int flags)511 public void setGroupVolume(int zoneId, int groupId, int index, int flags) { 512 try { 513 mService.setGroupVolume(zoneId, groupId, index, flags); 514 } catch (RemoteException e) { 515 handleRemoteExceptionFromCarService(e); 516 } 517 } 518 519 /** 520 * Returns the maximum volume index for a volume group in primary zone. 521 * 522 * @see #getGroupMaxVolume(int, int) 523 * @hide 524 */ 525 @SystemApi 526 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getGroupMaxVolume(int groupId)527 public int getGroupMaxVolume(int groupId) { 528 return getGroupMaxVolume(PRIMARY_AUDIO_ZONE, groupId); 529 } 530 531 /** 532 * Returns the maximum volume index for a volume group. 533 * 534 * @param zoneId The zone id whose volume group is queried. 535 * @param groupId The volume group id whose maximum volume index is returned. 536 * @return The maximum valid volume index for the given group. 537 * @hide 538 */ 539 @SystemApi 540 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getGroupMaxVolume(int zoneId, int groupId)541 public int getGroupMaxVolume(int zoneId, int groupId) { 542 try { 543 return mService.getGroupMaxVolume(zoneId, groupId); 544 } catch (RemoteException e) { 545 return handleRemoteExceptionFromCarService(e, 0); 546 } 547 } 548 549 /** 550 * Returns the minimum volume index for a volume group in primary zone. 551 * 552 * @see #getGroupMinVolume(int, int) 553 * @hide 554 */ 555 @SystemApi 556 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getGroupMinVolume(int groupId)557 public int getGroupMinVolume(int groupId) { 558 return getGroupMinVolume(PRIMARY_AUDIO_ZONE, groupId); 559 } 560 561 /** 562 * Returns the minimum volume index for a volume group. 563 * 564 * @param zoneId The zone id whose volume group is queried. 565 * @param groupId The volume group id whose minimum volume index is returned. 566 * @return The minimum valid volume index for the given group, non-negative 567 * @hide 568 */ 569 @SystemApi 570 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getGroupMinVolume(int zoneId, int groupId)571 public int getGroupMinVolume(int zoneId, int groupId) { 572 try { 573 return mService.getGroupMinVolume(zoneId, groupId); 574 } catch (RemoteException e) { 575 return handleRemoteExceptionFromCarService(e, 0); 576 } 577 } 578 579 /** 580 * Returns the current volume index for a volume group in primary zone. 581 * 582 * @see #getGroupVolume(int, int) 583 * @hide 584 */ 585 @SystemApi 586 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getGroupVolume(int groupId)587 public int getGroupVolume(int groupId) { 588 return getGroupVolume(PRIMARY_AUDIO_ZONE, groupId); 589 } 590 591 /** 592 * Returns the current volume index for a volume group. 593 * 594 * @param zoneId The zone id whose volume groups is queried. 595 * @param groupId The volume group id whose volume index is returned. 596 * @return The current volume index for the given group. 597 * 598 * @see #getGroupMaxVolume(int, int) 599 * @see #setGroupVolume(int, int, int, int) 600 * @hide 601 */ 602 @SystemApi 603 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getGroupVolume(int zoneId, int groupId)604 public int getGroupVolume(int zoneId, int groupId) { 605 try { 606 return mService.getGroupVolume(zoneId, groupId); 607 } catch (RemoteException e) { 608 return handleRemoteExceptionFromCarService(e, 0); 609 } 610 } 611 612 /** 613 * Adjust the relative volume in the front vs back of the vehicle cabin. 614 * 615 * @param value in the range -1.0 to 1.0 for fully toward the back through 616 * fully toward the front. 0.0 means evenly balanced. 617 * 618 * @throws IllegalArgumentException if {@code value} is less than -1.0 or 619 * greater than 1.0 620 * @see #setBalanceTowardRight(float) 621 * @hide 622 */ 623 @SystemApi 624 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) setFadeTowardFront(float value)625 public void setFadeTowardFront(float value) { 626 try { 627 mService.setFadeTowardFront(value); 628 } catch (RemoteException e) { 629 handleRemoteExceptionFromCarService(e); 630 } 631 } 632 633 /** 634 * Adjust the relative volume on the left vs right side of the vehicle cabin. 635 * 636 * @param value in the range -1.0 to 1.0 for fully toward the left through 637 * fully toward the right. 0.0 means evenly balanced. 638 * 639 * @throws IllegalArgumentException if {@code value} is less than -1.0 or 640 * greater than 1.0 641 * @see #setFadeTowardFront(float) 642 * @hide 643 */ 644 @SystemApi 645 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) setBalanceTowardRight(float value)646 public void setBalanceTowardRight(float value) { 647 try { 648 mService.setBalanceTowardRight(value); 649 } catch (RemoteException e) { 650 handleRemoteExceptionFromCarService(e); 651 } 652 } 653 654 /** 655 * Queries the system configuration in order to report the available, non-microphone audio 656 * input devices. 657 * 658 * @return An array of strings representing the available input ports. 659 * Each port is identified by it's "address" tag in the audioPolicyConfiguration xml file. 660 * Empty array if we find nothing. 661 * 662 * @see #createAudioPatch(String, int, int) 663 * @see #releaseAudioPatch(CarAudioPatchHandle) 664 * 665 * @deprecated use {@link AudioManager#getDevices(int)} with 666 * {@link AudioManager#GET_DEVICES_INPUTS} instead 667 * @hide 668 */ 669 @SystemApi 670 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 671 @Deprecated 672 @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE) getExternalSources()673 public @NonNull String[] getExternalSources() { 674 try { 675 return mService.getExternalSources(); 676 } catch (RemoteException e) { 677 handleRemoteExceptionFromCarService(e); 678 return new String[0]; 679 } 680 } 681 682 /** 683 * Given an input port identified by getExternalSources(), request that it's audio signal 684 * be routed below the HAL to the output port associated with the given usage. For example, 685 * The output of a tuner might be routed directly to the output buss associated with 686 * AudioAttributes.USAGE_MEDIA while the tuner is playing. 687 * 688 * @param sourceAddress the input port name obtained from getExternalSources(). 689 * @param usage the type of audio represented by this source (usually USAGE_MEDIA). 690 * @param gainInMillibels How many steps above the minimum value defined for the source port to 691 * set the gain when creating the patch. 692 * This may be used for source balancing without affecting the user 693 * controlled volumes applied to the destination ports. A value of 694 * 0 indicates no gain change is requested. 695 * @return A handle for the created patch which can be used to later remove it. 696 * 697 * @see #getExternalSources() 698 * @see #releaseAudioPatch(CarAudioPatchHandle) 699 * 700 * @deprecated use {@link android.media.HwAudioSource} instead 701 * @hide 702 */ 703 @SystemApi 704 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 705 @Deprecated 706 @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE) createAudioPatch(String sourceAddress, @AttributeUsage int usage, int gainInMillibels)707 public CarAudioPatchHandle createAudioPatch(String sourceAddress, @AttributeUsage int usage, 708 int gainInMillibels) { 709 try { 710 return mService.createAudioPatch(sourceAddress, usage, gainInMillibels); 711 } catch (RemoteException e) { 712 return handleRemoteExceptionFromCarService(e, null); 713 } 714 } 715 716 /** 717 * Removes the association between an input port and an output port identified by the provided 718 * handle. 719 * 720 * @param patch CarAudioPatchHandle returned from createAudioPatch(). 721 * 722 * @see #getExternalSources() 723 * @see #createAudioPatch(String, int, int) 724 * 725 * @deprecated use {@link android.media.HwAudioSource} instead 726 * @hide 727 */ 728 @SystemApi 729 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 730 @Deprecated 731 @ExcludeFromCodeCoverageGeneratedReport(reason = DEPRECATED_CODE) releaseAudioPatch(CarAudioPatchHandle patch)732 public void releaseAudioPatch(CarAudioPatchHandle patch) { 733 try { 734 mService.releaseAudioPatch(patch); 735 } catch (RemoteException e) { 736 handleRemoteExceptionFromCarService(e); 737 } 738 } 739 740 /** 741 * Gets the count of available volume groups in primary zone. 742 * 743 * @see #getVolumeGroupCount(int) 744 * @hide 745 */ 746 @SystemApi 747 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getVolumeGroupCount()748 public int getVolumeGroupCount() { 749 return getVolumeGroupCount(PRIMARY_AUDIO_ZONE); 750 } 751 752 /** 753 * Gets the count of available volume groups in the system. 754 * 755 * @param zoneId The zone id whois count of volume groups is queried. 756 * @return Count of volume groups 757 * @hide 758 */ 759 @SystemApi 760 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getVolumeGroupCount(int zoneId)761 public int getVolumeGroupCount(int zoneId) { 762 try { 763 return mService.getVolumeGroupCount(zoneId); 764 } catch (RemoteException e) { 765 return handleRemoteExceptionFromCarService(e, 0); 766 } 767 } 768 769 /** 770 * Gets the volume group id for a given {@link AudioAttributes} usage in primary zone. 771 * 772 * @see #getVolumeGroupIdForUsage(int, int) 773 * @hide 774 */ 775 @SystemApi 776 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getVolumeGroupIdForUsage(@ttributeUsage int usage)777 public int getVolumeGroupIdForUsage(@AttributeUsage int usage) { 778 return getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, usage); 779 } 780 781 /** 782 * Gets the volume group id for a given {@link AudioAttributes} usage. 783 * 784 * @param zoneId The zone id whose volume group is queried. 785 * @param usage The {@link AudioAttributes} usage to get a volume group from. 786 * @return The volume group id where the usage belongs to 787 * @hide 788 */ 789 @SystemApi 790 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage)791 public int getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage) { 792 try { 793 return mService.getVolumeGroupIdForUsage(zoneId, usage); 794 } catch (RemoteException e) { 795 return handleRemoteExceptionFromCarService(e, 0); 796 } 797 } 798 799 /** 800 * Gets array of {@link AudioAttributes} usages for a volume group in primary zone. 801 * 802 * @see #getUsagesForVolumeGroupId(int, int) 803 * @hide 804 */ 805 @SystemApi 806 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getUsagesForVolumeGroupId(int groupId)807 public @NonNull int[] getUsagesForVolumeGroupId(int groupId) { 808 return getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE, groupId); 809 } 810 811 /** 812 * Returns the volume group info associated with the zone id and group id. 813 * 814 * <p>The volume information, including mute, blocked, limited state will reflect the state 815 * of the volume group at the time of query. 816 * 817 * @param zoneId zone id for the group to query 818 * @param groupId group id for the group to query 819 * @throws IllegalArgumentException if the audio zone or group id are invalid 820 * 821 * @return the current volume group info 822 * 823 * @hide 824 */ 825 @SystemApi 826 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 827 @Nullable getVolumeGroupInfo(int zoneId, int groupId)828 public CarVolumeGroupInfo getVolumeGroupInfo(int zoneId, int groupId) { 829 try { 830 return mService.getVolumeGroupInfo(zoneId, groupId); 831 } catch (RemoteException e) { 832 return handleRemoteExceptionFromCarService(e, null); 833 } 834 } 835 836 /** 837 * Returns a list of volume group info associated with the zone id. 838 * 839 * <p>The volume information, including mute, blocked, limited state will reflect the state 840 * of the volume group at the time of query. 841 * 842 * @param zoneId zone id for the group to query 843 * @throws IllegalArgumentException if the audio zone is invalid 844 * 845 * @return all the current volume group info's for the zone id 846 * 847 * @hide 848 */ 849 @SystemApi 850 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 851 @NonNull getVolumeGroupInfosForZone(int zoneId)852 public List<CarVolumeGroupInfo> getVolumeGroupInfosForZone(int zoneId) { 853 try { 854 return mService.getVolumeGroupInfosForZone(zoneId); 855 } catch (RemoteException e) { 856 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 857 } 858 } 859 860 /** 861 * Returns a list of audio attributes associated with the volume group info. 862 * 863 * @param groupInfo group info to query 864 * @throws NullPointerException if the volume group info is {@code null} 865 * 866 * @return list of audio attributes associated with the volume group info 867 * 868 * @hide 869 */ 870 @SystemApi 871 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 872 @NonNull getAudioAttributesForVolumeGroup( @onNull CarVolumeGroupInfo groupInfo)873 public List<AudioAttributes> getAudioAttributesForVolumeGroup( 874 @NonNull CarVolumeGroupInfo groupInfo) { 875 try { 876 return mService.getAudioAttributesForVolumeGroup(groupInfo); 877 } catch (RemoteException e) { 878 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 879 } 880 } 881 882 /** 883 * Gets array of {@link AudioAttributes} usages for a volume group in a zone. 884 * 885 * @param zoneId The zone id whose volume group is queried. 886 * @param groupId The volume group id whose associated audio usages is returned. 887 * @return Array of {@link AudioAttributes} usages for a given volume group id 888 * @hide 889 */ 890 @SystemApi 891 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) getUsagesForVolumeGroupId(int zoneId, int groupId)892 public @NonNull int[] getUsagesForVolumeGroupId(int zoneId, int groupId) { 893 try { 894 return mService.getUsagesForVolumeGroupId(zoneId, groupId); 895 } catch (RemoteException e) { 896 return handleRemoteExceptionFromCarService(e, EMPTY_INT_ARRAY); 897 } 898 } 899 900 /** 901 * Determines if a particular volume group has any audio playback in a zone 902 * 903 * @param zoneId The zone id whose volume group is queried. 904 * @param groupId The volume group id whose associated audio usages is returned. 905 * @return {@code true} if the group has active playback, {@code false} otherwise 906 * 907 * @hide 908 */ 909 @SystemApi 910 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) isPlaybackOnVolumeGroupActive(int zoneId, int groupId)911 public boolean isPlaybackOnVolumeGroupActive(int zoneId, int groupId) { 912 try { 913 return mService.isPlaybackOnVolumeGroupActive(zoneId, groupId); 914 } catch (RemoteException e) { 915 return handleRemoteExceptionFromCarService(e, false); 916 } 917 } 918 919 /** 920 * Returns the current car audio zone configuration info associated with the zone id 921 * 922 * <p>If the car audio configuration does not include zone configurations, a default 923 * configuration consisting current output devices for the zone is returned. 924 * 925 * @param zoneId Zone id for the configuration to query 926 * @return the current car audio zone configuration info, or {@code null} if car audio service 927 * throws {@link RemoteException} 928 * @throws IllegalStateException if dynamic audio routing is not enabled 929 * @throws IllegalArgumentException if the audio zone id is invalid 930 * 931 * @hide 932 */ 933 @SystemApi 934 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 935 @Nullable getCurrentAudioZoneConfigInfo(int zoneId)936 public CarAudioZoneConfigInfo getCurrentAudioZoneConfigInfo(int zoneId) { 937 try { 938 return mService.getCurrentAudioZoneConfigInfo(zoneId); 939 } catch (RemoteException e) { 940 return handleRemoteExceptionFromCarService(e, null); 941 } 942 } 943 944 /** 945 * Returns a list of car audio zone configuration info associated with the zone id 946 * 947 * <p>If the car audio configuration does not include zone configurations, a default 948 * configuration consisting current output devices for each zone is returned. 949 * 950 * <p>There exists exactly one zone configuration in primary zone. 951 * 952 * @param zoneId Zone id for the configuration to query 953 * @return all the car audio zone configuration info for the zone id 954 * @throws IllegalStateException if dynamic audio routing is not enabled 955 * @throws IllegalArgumentException if the audio zone id is invalid 956 * 957 * @hide 958 */ 959 @SystemApi 960 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 961 @NonNull getAudioZoneConfigInfos(int zoneId)962 public List<CarAudioZoneConfigInfo> getAudioZoneConfigInfos(int zoneId) { 963 try { 964 return mService.getAudioZoneConfigInfos(zoneId); 965 } catch (RemoteException e) { 966 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 967 } 968 } 969 970 /** 971 * Switches the car audio zone configuration 972 * 973 * <p>To receive the volume group change after configuration is changed, a 974 * {@link CarVolumeGroupEventCallback} must be registered through 975 * {@link #registerCarVolumeGroupEventCallback(Executor, CarVolumeGroupEventCallback)} first. 976 * 977 * @param zoneConfig Audio zone configuration to switch to 978 * @param executor Executor on which callback will be invoked 979 * @param callback Callback that will report the result of switching car audio zone 980 * configuration 981 * @throws NullPointerException if either executor or callback are {@code null} 982 * @throws IllegalStateException if dynamic audio routing is not enabled 983 * @throws IllegalStateException if no user is assigned to the audio zone 984 * @throws IllegalStateException if the audio zone is currently in a mirroring configuration 985 * or sharing audio with primary audio zone 986 * @throws IllegalArgumentException if the audio zone configuration is invalid 987 * 988 * @hide 989 */ 990 @SystemApi 991 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) switchAudioZoneToConfig(@onNull CarAudioZoneConfigInfo zoneConfig, @NonNull @CallbackExecutor Executor executor, @NonNull SwitchAudioZoneConfigCallback callback)992 public void switchAudioZoneToConfig(@NonNull CarAudioZoneConfigInfo zoneConfig, 993 @NonNull @CallbackExecutor Executor executor, 994 @NonNull SwitchAudioZoneConfigCallback callback) { 995 Objects.requireNonNull(zoneConfig, "Audio zone configuration can not be null"); 996 Objects.requireNonNull(executor, "Executor can not be null"); 997 Objects.requireNonNull(callback, 998 "Switching audio zone configuration result callback can not be null"); 999 SwitchAudioZoneConfigCallbackWrapper wrapper = 1000 new SwitchAudioZoneConfigCallbackWrapper(executor, callback); 1001 try { 1002 mService.switchZoneToConfig(zoneConfig, wrapper); 1003 } catch (RemoteException e) { 1004 handleRemoteExceptionFromCarService(e); 1005 } 1006 } 1007 1008 /** 1009 * Sets the audio zone configurations change callback 1010 * 1011 * <p><b>Note:</b> There are two types on configuration changes. 1012 * 1013 * <p>Config active status changes, signaled by status {@link #CONFIG_STATUS_CHANGED}, 1014 * represent changes to the configurations due to a configuration becoming active or inactive as 1015 * a result of a dynamic device being connected or disconnected respectively. 1016 * 1017 * <p>Config auto switch changes, signaled by status {@link #CONFIG_STATUS_AUTO_SWITCHED}, 1018 * represent changes to the configurations due a currently selected configuration becoming 1019 * inactive as a result of a dynamic device being disconnected. 1020 * 1021 * @param executor Executor on which callback will be invoked 1022 * @param callback Callback that will be triggered on audio configuration changes 1023 * @return {@code true} if the callback is successfully registered, {@code false} otherwise 1024 * @throws NullPointerException if either executor or callback are {@code null} 1025 * @throws IllegalStateException if dynamic audio routing is not enabled 1026 * @throws IllegalStateException if there is a callback already set 1027 * 1028 * @hide 1029 */ 1030 @SystemApi 1031 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 1032 @FlaggedApi(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES) setAudioZoneConfigsChangeCallback(@onNull @allbackExecutor Executor executor, @NonNull AudioZoneConfigurationsChangeCallback callback)1033 public boolean setAudioZoneConfigsChangeCallback(@NonNull @CallbackExecutor Executor executor, 1034 @NonNull AudioZoneConfigurationsChangeCallback callback) { 1035 Objects.requireNonNull(executor, "Executor can not be null"); 1036 Objects.requireNonNull(callback, "Audio zone configs change callback can not be null"); 1037 1038 synchronized (mLock) { 1039 if (mZoneConfigurationsChangeCallbackWrapper != null) { 1040 throw new IllegalStateException("Audio zone configs change " 1041 + "callback is already set"); 1042 } 1043 } 1044 AudioZoneConfigurationsChangeCallbackWrapper wrapper = 1045 new AudioZoneConfigurationsChangeCallbackWrapper(executor, callback); 1046 1047 boolean succeeded; 1048 try { 1049 succeeded = mService.registerAudioZoneConfigsChangeCallback(wrapper); 1050 } catch (RemoteException e) { 1051 return handleRemoteExceptionFromCarService(e, false); 1052 } 1053 1054 if (!succeeded) { 1055 return false; 1056 } 1057 boolean error; 1058 synchronized (mLock) { 1059 error = mZoneConfigurationsChangeCallbackWrapper != null; 1060 if (!error) { 1061 mZoneConfigurationsChangeCallbackWrapper = wrapper; 1062 } 1063 } 1064 1065 // In case there was an error, unregister the listener and throw an exception 1066 if (error) { 1067 try { 1068 mService.unregisterAudioZoneConfigsChangeCallback(wrapper); 1069 } catch (RemoteException e) { 1070 handleRemoteExceptionFromCarService(e); 1071 } 1072 1073 throw new IllegalStateException("Audio zone config change callback is already set"); 1074 } 1075 return true; 1076 } 1077 1078 /** 1079 * Clears the currently set {@link AudioZoneConfigurationsChangeCallback} 1080 * 1081 * @throws IllegalStateException if dynamic audio routing is not enabled 1082 * 1083 * @hide 1084 */ 1085 @SystemApi 1086 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 1087 @FlaggedApi(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES) clearAudioZoneConfigsCallback()1088 public void clearAudioZoneConfigsCallback() { 1089 AudioZoneConfigurationsChangeCallbackWrapper wrapper; 1090 1091 synchronized (mLock) { 1092 if (mZoneConfigurationsChangeCallbackWrapper == null) { 1093 Slog.w(TAG, "Audio zone configs callback was already cleared"); 1094 return; 1095 } 1096 wrapper = mZoneConfigurationsChangeCallbackWrapper; 1097 mZoneConfigurationsChangeCallbackWrapper = null; 1098 } 1099 1100 try { 1101 mService.unregisterAudioZoneConfigsChangeCallback(wrapper); 1102 } catch (RemoteException e) { 1103 handleRemoteExceptionFromCarService(e); 1104 } 1105 } 1106 1107 /** 1108 * Gets the audio zones currently available 1109 * 1110 * @return audio zone ids 1111 * @hide 1112 */ 1113 @SystemApi 1114 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) getAudioZoneIds()1115 public @NonNull List<Integer> getAudioZoneIds() { 1116 try { 1117 return asList(mService.getAudioZoneIds()); 1118 } catch (RemoteException e) { 1119 return handleRemoteExceptionFromCarService(e, Collections.emptyList()); 1120 } 1121 } 1122 1123 /** 1124 * Gets the audio zone id currently mapped to uId, 1125 * defaults to PRIMARY_AUDIO_ZONE if no mapping exist 1126 * 1127 * @param uid The uid to map 1128 * @return zone id mapped to uid 1129 * @hide 1130 */ 1131 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) getZoneIdForUid(int uid)1132 public int getZoneIdForUid(int uid) { 1133 try { 1134 return mService.getZoneIdForUid(uid); 1135 } catch (RemoteException e) { 1136 return handleRemoteExceptionFromCarService(e, 0); 1137 } 1138 } 1139 1140 /** 1141 * Maps the audio zone id to uid 1142 * 1143 * @param zoneId The audio zone id 1144 * @param uid The uid to map 1145 * @return true if the uid is successfully mapped 1146 * @hide 1147 */ 1148 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) setZoneIdForUid(int zoneId, int uid)1149 public boolean setZoneIdForUid(int zoneId, int uid) { 1150 try { 1151 return mService.setZoneIdForUid(zoneId, uid); 1152 } catch (RemoteException e) { 1153 return handleRemoteExceptionFromCarService(e, false); 1154 } 1155 } 1156 1157 /** 1158 * Clears the current zone mapping of the uid 1159 * 1160 * @param uid The uid to clear 1161 * @return true if the zone was successfully cleared 1162 * 1163 * @hide 1164 */ 1165 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) clearZoneIdForUid(int uid)1166 public boolean clearZoneIdForUid(int uid) { 1167 try { 1168 return mService.clearZoneIdForUid(uid); 1169 } catch (RemoteException e) { 1170 return handleRemoteExceptionFromCarService(e, false); 1171 } 1172 } 1173 1174 /** 1175 * Sets a {@link PrimaryZoneMediaAudioRequestCallback} to listen for request to play 1176 * media audio in primary audio zone 1177 * 1178 * @param executor Executor on which callback will be invoked 1179 * @param callback Media audio request callback to monitor for audio requests 1180 * @return {@code true} if the callback is successfully registered, {@code false} otherwise 1181 * @throws NullPointerException if either executor or callback are {@code null} 1182 * @throws IllegalStateException if dynamic audio routing is not enabled 1183 * @throws IllegalStateException if there is a callback already set 1184 * 1185 * @hide 1186 */ 1187 @SystemApi 1188 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) setPrimaryZoneMediaAudioRequestCallback( @onNull @allbackExecutor Executor executor, @NonNull PrimaryZoneMediaAudioRequestCallback callback)1189 public boolean setPrimaryZoneMediaAudioRequestCallback( 1190 @NonNull @CallbackExecutor Executor executor, 1191 @NonNull PrimaryZoneMediaAudioRequestCallback callback) { 1192 Objects.requireNonNull(executor, "Executor can not be null"); 1193 Objects.requireNonNull(callback, "Audio media request callback can not be null"); 1194 synchronized (mLock) { 1195 if (mPrimaryZoneMediaAudioRequestCallback != null) { 1196 throw new IllegalStateException("Primary zone media audio request is already set"); 1197 } 1198 } 1199 1200 try { 1201 if (!mService.registerPrimaryZoneMediaAudioRequestCallback( 1202 mIPrimaryZoneMediaAudioRequestCallback)) { 1203 return false; 1204 } 1205 } catch (RemoteException e) { 1206 return handleRemoteExceptionFromCarService(e, /* returnValue= */ false); 1207 } 1208 1209 synchronized (mLock) { 1210 mPrimaryZoneMediaAudioRequestCallback = callback; 1211 mPrimaryZoneMediaAudioRequestCallbackExecutor = executor; 1212 } 1213 1214 return true; 1215 } 1216 1217 /** 1218 * Clears the currently set {@link PrimaryZoneMediaAudioRequestCallback} 1219 * 1220 * @throws IllegalStateException if dynamic audio routing is not enabled 1221 * 1222 * @hide 1223 */ 1224 @SystemApi 1225 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) clearPrimaryZoneMediaAudioRequestCallback()1226 public void clearPrimaryZoneMediaAudioRequestCallback() { 1227 synchronized (mLock) { 1228 if (mPrimaryZoneMediaAudioRequestCallback == null) { 1229 return; 1230 } 1231 } 1232 1233 try { 1234 mService.unregisterPrimaryZoneMediaAudioRequestCallback( 1235 mIPrimaryZoneMediaAudioRequestCallback); 1236 } catch (RemoteException e) { 1237 handleRemoteExceptionFromCarService(e); 1238 } 1239 1240 synchronized (mLock) { 1241 mPrimaryZoneMediaAudioRequestCallback = null; 1242 mPrimaryZoneMediaAudioRequestCallbackExecutor = null; 1243 } 1244 } 1245 1246 /** 1247 * Cancels a request set by {@link #requestMediaAudioOnPrimaryZone} 1248 * 1249 * @param requestId Request id to cancel 1250 * @return {@code true} if request is successfully cancelled 1251 * @throws IllegalStateException if dynamic audio routing is not enabled 1252 * 1253 * @hide 1254 */ 1255 @SystemApi 1256 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) cancelMediaAudioOnPrimaryZone(long requestId)1257 public boolean cancelMediaAudioOnPrimaryZone(long requestId) { 1258 try { 1259 if (removeMediaRequestCallback(requestId)) { 1260 return mService.cancelMediaAudioOnPrimaryZone(requestId); 1261 } 1262 } catch (RemoteException e) { 1263 return handleRemoteExceptionFromCarService(e, /* returnValue= */ false); 1264 } 1265 1266 return true; 1267 } 1268 removeMediaRequestCallback(long requestId)1269 private boolean removeMediaRequestCallback(long requestId) { 1270 return mRequestIdToMediaAudioRequestStatusCallbacks.remove(requestId) != null; 1271 } 1272 1273 /** 1274 * Requests to play audio in primary zone with information contained in {@code request} 1275 * 1276 * @param info Occupant zone info whose media audio should be shared to primary zone 1277 * @param executor Executor on which callback will be invoked 1278 * @param callback Callback that will report the status changes of the request 1279 * @return returns a valid request id if successful or {@code INVALID_REQUEST_ID} otherwise 1280 * @throws NullPointerException if any of info, executor, or callback parameters are 1281 * {@code null} 1282 * @throws IllegalStateException if dynamic audio routing is not enabled, or if audio mirroring 1283 * is currently enabled for the audio zone owned by the occupant as configured by 1284 * {@link #enableMirrorForAudioZones(List)} 1285 * 1286 * @hide 1287 */ 1288 @SystemApi 1289 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) requestMediaAudioOnPrimaryZone(@onNull OccupantZoneInfo info, @NonNull @CallbackExecutor Executor executor, @NonNull MediaAudioRequestStatusCallback callback)1290 public long requestMediaAudioOnPrimaryZone(@NonNull OccupantZoneInfo info, 1291 @NonNull @CallbackExecutor Executor executor, 1292 @NonNull MediaAudioRequestStatusCallback callback) { 1293 Objects.requireNonNull(info, "Occupant zone info can not be null"); 1294 Objects.requireNonNull(executor, "Executor can not be null"); 1295 Objects.requireNonNull(callback, "Media audio request status callback can not be null"); 1296 1297 MediaAudioRequestStatusCallbackWrapper wrapper = 1298 new MediaAudioRequestStatusCallbackWrapper(executor, callback); 1299 1300 long requestId; 1301 try { 1302 requestId = mService.requestMediaAudioOnPrimaryZone(wrapper, info); 1303 } catch (RemoteException e) { 1304 return handleRemoteExceptionFromCarService(e, INVALID_REQUEST_ID); 1305 } 1306 1307 if (requestId == INVALID_REQUEST_ID) { 1308 return requestId; 1309 } 1310 1311 mRequestIdToMediaAudioRequestStatusCallbacks.put(requestId, wrapper); 1312 return requestId; 1313 } 1314 1315 /** 1316 * Allow/rejects audio to play for a request 1317 * {@link #requestMediaAudioOnPrimaryZone(OccupantZoneInfo, Executor, 1318 * MediaAudioRequestStatusCallback)} 1319 * 1320 * @param requestId Request id to approve 1321 * @param allow Boolean indicating to allow or reject, {@code true} to allow audio 1322 * playback on primary zone, {@code false} otherwise 1323 * @return {@code false} if media is not successfully allowed/rejected for the request, 1324 * including the case when the request id is {@link #INVALID_REQUEST_ID} 1325 * @throws IllegalStateException if no {@link PrimaryZoneMediaAudioRequestCallback} is 1326 * registered prior to calling this method. 1327 * @throws IllegalStateException if dynamic audio routing is not enabled, or if audio mirroring 1328 * is currently enabled for the audio zone owned by the occupant as configured by 1329 * {@link #enableMirrorForAudioZones(List)} 1330 * 1331 * @hide 1332 */ 1333 @SystemApi 1334 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) allowMediaAudioOnPrimaryZone(long requestId, boolean allow)1335 public boolean allowMediaAudioOnPrimaryZone(long requestId, boolean allow) { 1336 synchronized (mLock) { 1337 if (mPrimaryZoneMediaAudioRequestCallback == null) { 1338 throw new IllegalStateException("Primary zone media audio request callback must be " 1339 + "registered to allow/reject playback"); 1340 } 1341 } 1342 1343 try { 1344 return mService.allowMediaAudioOnPrimaryZone( 1345 mIPrimaryZoneMediaAudioRequestCallback.asBinder(), requestId, allow); 1346 } catch (RemoteException e) { 1347 return handleRemoteExceptionFromCarService(e, /* returnValue= */ false); 1348 } 1349 } 1350 1351 /** 1352 * Resets the media audio playback in primary zone from occupant 1353 * 1354 * @param info Occupant's audio to reset in primary zone 1355 * @return {@code true} if audio is successfully reset, {@code false} otherwise including case 1356 * where audio is not currently assigned 1357 * @throws IllegalStateException if dynamic audio routing is not enabled 1358 * 1359 * @hide 1360 */ 1361 @SystemApi 1362 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) resetMediaAudioOnPrimaryZone(@onNull OccupantZoneInfo info)1363 public boolean resetMediaAudioOnPrimaryZone(@NonNull OccupantZoneInfo info) { 1364 try { 1365 return mService.resetMediaAudioOnPrimaryZone(info); 1366 } catch (RemoteException e) { 1367 return handleRemoteExceptionFromCarService(e, /* returnValue= */ false); 1368 } 1369 } 1370 1371 /** 1372 * Determines if audio from occupant is allowed in primary zone 1373 * 1374 * @param info Occupant zone info to query 1375 * @return {@code true} if audio playback from occupant is allowed in primary zone 1376 * @throws IllegalStateException if dynamic audio routing is not enabled 1377 * 1378 * @hide 1379 */ 1380 @SystemApi 1381 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) isMediaAudioAllowedInPrimaryZone(@onNull OccupantZoneInfo info)1382 public boolean isMediaAudioAllowedInPrimaryZone(@NonNull OccupantZoneInfo info) { 1383 try { 1384 return mService.isMediaAudioAllowedInPrimaryZone(info); 1385 } catch (RemoteException e) { 1386 return handleRemoteExceptionFromCarService(e, /* returnValue= */ false); 1387 } 1388 } 1389 1390 /** 1391 * Registers audio mirror status callback 1392 * 1393 * @param executor Executor on which the callback will be invoked 1394 * @param callback Callback to inform about audio mirror status changes 1395 * @return {@code true} if audio zones mirror status is set successfully, or {@code false} 1396 * otherwise 1397 * @throws NullPointerException if {@link AudioZonesMirrorStatusCallback} or {@link Executor} 1398 * passed in are {@code null} 1399 * @throws IllegalStateException if dynamic audio routing is not enabled, also if 1400 * there is a callback already set 1401 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1402 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1403 * feature flag 1404 * 1405 * @hide 1406 */ 1407 @SystemApi 1408 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) setAudioZoneMirrorStatusCallback(@onNull @allbackExecutor Executor executor, @NonNull AudioZonesMirrorStatusCallback callback)1409 public boolean setAudioZoneMirrorStatusCallback(@NonNull @CallbackExecutor Executor executor, 1410 @NonNull AudioZonesMirrorStatusCallback callback) { 1411 Objects.requireNonNull(executor, "Executor can not be null"); 1412 Objects.requireNonNull(callback, "Audio zones mirror status callback can not be null"); 1413 1414 synchronized (mLock) { 1415 if (mAudioZonesMirrorStatusCallbackWrapper != null) { 1416 throw new IllegalStateException("Audio zones mirror status " 1417 + "callback is already set"); 1418 } 1419 } 1420 AudioZonesMirrorStatusCallbackWrapper wrapper = 1421 new AudioZonesMirrorStatusCallbackWrapper(executor, callback); 1422 1423 boolean succeeded; 1424 try { 1425 succeeded = mService.registerAudioZonesMirrorStatusCallback(wrapper); 1426 } catch (RemoteException e) { 1427 return handleRemoteExceptionFromCarService(e, false); 1428 } 1429 1430 if (!succeeded) { 1431 return false; 1432 } 1433 boolean error; 1434 synchronized (mLock) { 1435 // Unless there is a race condition mAudioZonesMirrorStatusCallbackWrapper 1436 // should not be set 1437 error = mAudioZonesMirrorStatusCallbackWrapper != null; 1438 if (!error) { 1439 mAudioZonesMirrorStatusCallbackWrapper = wrapper; 1440 } 1441 } 1442 1443 // In case there was an error, unregister the listener and throw an exception 1444 if (error) { 1445 try { 1446 mService.unregisterAudioZonesMirrorStatusCallback(wrapper); 1447 } catch (RemoteException e) { 1448 handleRemoteExceptionFromCarService(e); 1449 } 1450 1451 throw new IllegalStateException("Audio zones mirror status callback is already set"); 1452 } 1453 return true; 1454 } 1455 1456 /** 1457 * Clears the currently set {@link AudioZonesMirrorStatusCallback} 1458 * 1459 * @throws IllegalStateException if dynamic audio routing is not enabled 1460 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1461 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1462 * feature flag 1463 * 1464 * @hide 1465 */ 1466 @SystemApi 1467 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) clearAudioZonesMirrorStatusCallback()1468 public void clearAudioZonesMirrorStatusCallback() { 1469 AudioZonesMirrorStatusCallbackWrapper wrapper; 1470 1471 synchronized (mLock) { 1472 if (mAudioZonesMirrorStatusCallbackWrapper == null) { 1473 return; 1474 } 1475 wrapper = mAudioZonesMirrorStatusCallbackWrapper; 1476 mAudioZonesMirrorStatusCallbackWrapper = null; 1477 } 1478 1479 try { 1480 mService.unregisterAudioZonesMirrorStatusCallback(wrapper); 1481 } catch (RemoteException e) { 1482 handleRemoteExceptionFromCarService(e); 1483 } 1484 } 1485 1486 /** 1487 * Determines if it is possible to enable audio mirror 1488 * 1489 * @return returns status to determine if it is possible to enable audio mirror using the 1490 * {@link #enableMirrorForAudioZones(List)} API, if audio mirror can be enabled this will 1491 * return {@link #AUDIO_MIRROR_CAN_ENABLE}, or {@link #AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES} if 1492 * there are no more output devices currently available to mirror. 1493 * {@link #AUDIO_MIRROR_INTERNAL_ERROR} can also be returned in case there is an error when 1494 * communicating with the car audio service 1495 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1496 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1497 * feature flag 1498 * 1499 * @hide 1500 */ 1501 @SystemApi 1502 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) canEnableAudioMirror()1503 public @AudioMirrorStatus int canEnableAudioMirror() { 1504 try { 1505 return mService.canEnableAudioMirror(); 1506 } catch (RemoteException e) { 1507 return handleRemoteExceptionFromCarService(e, AUDIO_MIRROR_INTERNAL_ERROR); 1508 } 1509 } 1510 1511 /** 1512 * Enables audio mirror for a set of audio zones 1513 * 1514 * <p><b>Note:</b> The results will be notified in the {@link AudioZonesMirrorStatusCallback} 1515 * set via {@link #setAudioZoneMirrorStatusCallback(Executor, AudioZonesMirrorStatusCallback)} 1516 * 1517 * @param audioZonesToMirror List of audio zones that should have audio mirror enabled, 1518 * a minimum of two audio zones are needed to enable mirroring 1519 * @return returns a valid mirror request id if successful or {@code INVALID_REQUEST_ID} 1520 * otherwise 1521 * @throws NullPointerException if the audio mirror list is {@code null} 1522 * @throws IllegalArgumentException if the audio mirror list size is less than 2, if a zone id 1523 * repeats within the list, or if the list contains the {@link #PRIMARY_AUDIO_ZONE} 1524 * @throws IllegalStateException if dynamic audio routing is not enabled, or there is an 1525 * attempt to merge zones from two different mirroring request, or any of the zone ids 1526 * are currently sharing audio to primary zone as allowed via 1527 * {@link #allowMediaAudioOnPrimaryZone(long, boolean)} 1528 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1529 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1530 * feature flag 1531 * 1532 * @hide 1533 */ 1534 @SystemApi 1535 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) enableMirrorForAudioZones(@onNull List<Integer> audioZonesToMirror)1536 public long enableMirrorForAudioZones(@NonNull List<Integer> audioZonesToMirror) { 1537 Objects.requireNonNull(audioZonesToMirror, "Audio zones to mirror should not be null"); 1538 1539 try { 1540 return mService.enableMirrorForAudioZones(toIntArray(audioZonesToMirror)); 1541 } catch (RemoteException e) { 1542 return handleRemoteExceptionFromCarService(e, INVALID_REQUEST_ID); 1543 } 1544 } 1545 1546 /** 1547 * Extends the audio zone mirroring request by appending new zones to the mirroring 1548 * configuration. The zones previously mirroring in the audio mirroring configuration, will 1549 * continue to mirror and the mirroring will be further extended to the new zones. 1550 * 1551 * <p><b>Note:</b> The results will be notified in the {@link AudioZonesMirrorStatusCallback} 1552 * set via {@link #setAudioZoneMirrorStatusCallback(Executor, AudioZonesMirrorStatusCallback)}. 1553 * For example, to further extend a mirroring request currently containing zones 1 and 2, with 1554 * a new zone (3) Simply call the API with zone 3 in the list, after the completion of audio 1555 * mirroring extension, zones 1, 2, and 3 will now have mirroring enabled. 1556 * 1557 * @param audioZonesToMirror List of audio zones that will be added to the mirroring request 1558 * @param mirrorId Audio mirroring request to expand with more audio zones 1559 * @throws NullPointerException if the audio mirror list is {@code null} 1560 * @throws IllegalArgumentException if a zone id repeats within the list, or if the list 1561 * contains the {@link #PRIMARY_AUDIO_ZONE}, or if the request id to expand is no longer valid 1562 * @throws IllegalStateException if dynamic audio routing is not enabled, or there is an 1563 * attempt to merge zones from two different mirroring request, or any of the zone ids 1564 * are currently sharing audio to primary zone as allowed via 1565 * {@link #allowMediaAudioOnPrimaryZone(long, boolean)} 1566 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1567 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1568 * feature flag 1569 * 1570 * @hide 1571 */ 1572 @SystemApi 1573 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) extendAudioMirrorRequest(long mirrorId, @NonNull List<Integer> audioZonesToMirror)1574 public void extendAudioMirrorRequest(long mirrorId, @NonNull List<Integer> audioZonesToMirror) { 1575 Objects.requireNonNull(audioZonesToMirror, "Audio zones to mirror should not be null"); 1576 1577 try { 1578 mService.extendAudioMirrorRequest(mirrorId, toIntArray(audioZonesToMirror)); 1579 } catch (RemoteException e) { 1580 handleRemoteExceptionFromCarService(e); 1581 } 1582 } 1583 1584 /** 1585 * Disables audio mirror for a particular audio zone 1586 * 1587 * <p><b>Note:</b> The results will be notified in the {@link AudioZonesMirrorStatusCallback} 1588 * set via {@link #setAudioZoneMirrorStatusCallback(Executor, AudioZonesMirrorStatusCallback)}. 1589 * The results will contain the information for the audio zones whose mirror was cancelled. 1590 * For example, if the mirror configuration only has two zones, mirroring will be undone for 1591 * both zones and the callback will have both zones. On the other hand, if the mirroring 1592 * configuration contains three zones, then this API will only cancel mirroring for one zone 1593 * and the other two zone will continue mirroring. In this case, the callback will only have 1594 * information about the cancelled zone 1595 * 1596 * @param zoneId Zone id where audio mirror should be disabled 1597 * @throws IllegalArgumentException if the zoneId is invalid 1598 * @throws IllegalStateException if dynamic audio routing is not enabled 1599 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1600 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1601 * feature flag 1602 * 1603 * @hide 1604 */ 1605 @SystemApi 1606 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) disableAudioMirrorForZone(int zoneId)1607 public void disableAudioMirrorForZone(int zoneId) { 1608 try { 1609 mService.disableAudioMirrorForZone(zoneId); 1610 } catch (RemoteException e) { 1611 handleRemoteExceptionFromCarService(e); 1612 } 1613 } 1614 1615 /** 1616 * Disables audio mirror for all the zones mirroring in a particular request 1617 * 1618 * <p><b>Note:</b> The results will be notified in the {@link AudioZonesMirrorStatusCallback} 1619 * set via {@link #setAudioZoneMirrorStatusCallback(Executor, AudioZonesMirrorStatusCallback)} 1620 * 1621 * @param mirrorId Whose audio mirroring should be disabled as obtained via 1622 * {@link #enableMirrorForAudioZones(List)} 1623 * @throws IllegalArgumentException if the request id is no longer valid 1624 * @throws IllegalStateException if dynamic audio routing is not enabled 1625 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1626 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1627 * feature flag 1628 * 1629 * @hide 1630 */ 1631 @SystemApi 1632 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) disableAudioMirror(long mirrorId)1633 public void disableAudioMirror(long mirrorId) { 1634 try { 1635 mService.disableAudioMirror(mirrorId); 1636 } catch (RemoteException e) { 1637 handleRemoteExceptionFromCarService(e); 1638 } 1639 } 1640 1641 /** 1642 * Determines the current mirror configuration for an audio zone as set by 1643 * {@link #enableMirrorForAudioZones(List)} or extended via 1644 * {@link #extendAudioMirrorRequest(long, List)} 1645 * 1646 * @param zoneId The audio zone id where mirror audio should be queried 1647 * @return A list of audio zones where the queried audio zone is mirroring or empty if the 1648 * audio zone is not mirroring with any other audio zone. The list of zones will contain the 1649 * queried zone if audio mirroring is enabled for that zone. 1650 * @throws IllegalArgumentException if the audio zone id is invalid 1651 * @throws IllegalStateException if dynamic audio routing is not enabled 1652 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1653 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1654 * feature flag 1655 * 1656 * @hide 1657 */ 1658 @SystemApi 1659 @NonNull 1660 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) getMirrorAudioZonesForAudioZone(int zoneId)1661 public List<Integer> getMirrorAudioZonesForAudioZone(int zoneId) { 1662 try { 1663 return asList(mService.getMirrorAudioZonesForAudioZone(zoneId)); 1664 } catch (RemoteException e) { 1665 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 1666 } 1667 } 1668 1669 /** 1670 * Determines the current mirror configuration for a mirror id 1671 * 1672 * @param mirrorId The request id that should be queried 1673 * @return A list of audio zones where the queried audio zone is mirroring or empty if the 1674 * request id is no longer valid. 1675 * @throws IllegalArgumentException if mirror request id is {@link #INVALID_REQUEST_ID} 1676 * @throws IllegalStateException if dynamic audio routing is not enabled 1677 * @throws IllegalStateException if audio mirroring feature is disabled, which can be verified 1678 * using {@link #isAudioFeatureEnabled(int)} with the {@link #AUDIO_FEATURE_AUDIO_MIRRORING} 1679 * feature flag 1680 * 1681 * @hide 1682 */ 1683 @SystemApi 1684 @NonNull 1685 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) getMirrorAudioZonesForMirrorRequest(long mirrorId)1686 public List<Integer> getMirrorAudioZonesForMirrorRequest(long mirrorId) { 1687 try { 1688 return asList(mService.getMirrorAudioZonesForMirrorRequest(mirrorId)); 1689 } catch (RemoteException e) { 1690 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 1691 } 1692 } 1693 1694 /** 1695 * Gets the output device for a given {@link AudioAttributes} usage in zoneId. 1696 * 1697 * <p><b>Note:</b> To be used for routing to a specific device. Most applications should 1698 * use the regular routing mechanism, which is to set audio attribute usage to 1699 * an audio track. 1700 * 1701 * @param zoneId zone id to query for device 1702 * @param usage usage where audio is routed 1703 * @return Audio device info, returns {@code null} if audio device usage fails to map to 1704 * an active audio device. This is different from the using an invalid value for 1705 * {@link AudioAttributes} usage. In the latter case the query will fail with a 1706 * RuntimeException indicating the issue. 1707 * 1708 * @hide 1709 */ 1710 @SystemApi 1711 @Nullable 1712 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) getOutputDeviceForUsage(int zoneId, @AttributeUsage int usage)1713 public AudioDeviceInfo getOutputDeviceForUsage(int zoneId, @AttributeUsage int usage) { 1714 try { 1715 String deviceAddress = mService.getOutputDeviceAddressForUsage(zoneId, usage); 1716 if (deviceAddress == null) { 1717 return null; 1718 } 1719 AudioDeviceInfo[] outputDevices = 1720 mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 1721 for (AudioDeviceInfo info : outputDevices) { 1722 if (info.getAddress().equals(deviceAddress)) { 1723 return info; 1724 } 1725 } 1726 return null; 1727 } catch (RemoteException e) { 1728 return handleRemoteExceptionFromCarService(e, null); 1729 } 1730 } 1731 1732 /** 1733 * Gets the input devices for an audio zone 1734 * 1735 * @return list of input devices 1736 * @hide 1737 */ 1738 @SystemApi 1739 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) getInputDevicesForZoneId(int zoneId)1740 public @NonNull List<AudioDeviceInfo> getInputDevicesForZoneId(int zoneId) { 1741 try { 1742 return convertInputDevicesToDeviceInfos( 1743 mService.getInputDevicesForZoneId(zoneId), 1744 AudioManager.GET_DEVICES_INPUTS); 1745 } catch (RemoteException e) { 1746 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 1747 } 1748 } 1749 1750 /** @hide */ 1751 @Override onCarDisconnected()1752 public void onCarDisconnected() { 1753 if (mService == null) { 1754 return; 1755 } 1756 1757 if (!mCarVolumeCallbacks.isEmpty()) { 1758 unregisterVolumeCallback(); 1759 } 1760 if (!mCarVolumeEventCallbacks.isEmpty()) { 1761 unregisterVolumeGroupEventCallback(); 1762 } 1763 } 1764 1765 /** @hide */ CarAudioManager(ICarBase car, IBinder service)1766 public CarAudioManager(ICarBase car, IBinder service) { 1767 super(car); 1768 mService = ICarAudio.Stub.asInterface(service); 1769 mAudioManager = getContext().getSystemService(AudioManager.class); 1770 mCarVolumeCallbacks = new CopyOnWriteArrayList<>(); 1771 mEventHandler = new EventHandler(getEventHandler().getLooper()); 1772 } 1773 1774 /** 1775 * Registers a {@link CarVolumeCallback} to receive volume change callbacks 1776 * @param callback {@link CarVolumeCallback} instance, can not be null 1777 * <p> 1778 * Requires permission Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME 1779 */ registerCarVolumeCallback(@onNull CarVolumeCallback callback)1780 public void registerCarVolumeCallback(@NonNull CarVolumeCallback callback) { 1781 Objects.requireNonNull(callback); 1782 1783 if (mCarVolumeCallbacks.isEmpty()) { 1784 registerVolumeCallback(); 1785 } 1786 1787 mCarVolumeCallbacks.add(callback); 1788 } 1789 1790 /** 1791 * Unregisters a {@link CarVolumeCallback} from receiving volume change callbacks 1792 * @param callback {@link CarVolumeCallback} instance previously registered, can not be null 1793 * <p> 1794 * Requires permission Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME 1795 */ unregisterCarVolumeCallback(@onNull CarVolumeCallback callback)1796 public void unregisterCarVolumeCallback(@NonNull CarVolumeCallback callback) { 1797 Objects.requireNonNull(callback); 1798 if (mCarVolumeCallbacks.contains(callback) && (mCarVolumeCallbacks.size() == 1)) { 1799 unregisterVolumeCallback(); 1800 } 1801 1802 mCarVolumeCallbacks.remove(callback); 1803 } 1804 registerVolumeCallback()1805 private void registerVolumeCallback() { 1806 try { 1807 mService.registerVolumeCallback(mCarVolumeCallbackImpl.asBinder()); 1808 } catch (RemoteException e) { 1809 Slog.e(CarLibLog.TAG_CAR, "registerVolumeCallback failed", e); 1810 } 1811 } 1812 unregisterVolumeCallback()1813 private void unregisterVolumeCallback() { 1814 try { 1815 mService.unregisterVolumeCallback(mCarVolumeCallbackImpl.asBinder()); 1816 } catch (RemoteException e) { 1817 handleRemoteExceptionFromCarService(e); 1818 } 1819 } 1820 1821 /** 1822 * Registers a {@link CarVolumeGroupEventCallback} to receive volume group event callbacks 1823 * 1824 * @param executor Executor on which callback will be invoked 1825 * @param callback Callback that will report volume group events 1826 * @return {@code true} if the callback is successfully registered, {@code false} otherwise 1827 * @throws NullPointerException if executor or callback parameters is {@code null} 1828 * @throws IllegalStateException if dynamic audio routing is not enabled 1829 * @throws IllegalStateException if volume group events are not enabled 1830 * 1831 * @hide 1832 */ 1833 @SystemApi 1834 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) registerCarVolumeGroupEventCallback( @onNull @allbackExecutor Executor executor, @NonNull CarVolumeGroupEventCallback callback)1835 public boolean registerCarVolumeGroupEventCallback( 1836 @NonNull @CallbackExecutor Executor executor, 1837 @NonNull CarVolumeGroupEventCallback callback) { 1838 Objects.requireNonNull(executor, "Executor can not be null"); 1839 Objects.requireNonNull(callback, "Car volume event callback can not be null"); 1840 1841 if (mCarVolumeEventCallbacks.isEmpty()) { 1842 if (!registerVolumeGroupEventCallback()) { 1843 return false; 1844 } 1845 } 1846 1847 return mCarVolumeEventCallbacks.addIfAbsent( 1848 new CarVolumeGroupEventCallbackWrapper(executor, callback)); 1849 } 1850 registerVolumeGroupEventCallback()1851 private boolean registerVolumeGroupEventCallback() { 1852 try { 1853 if (!mService.registerCarVolumeEventCallback(mCarVolumeEventCallbackImpl)) { 1854 return false; 1855 } 1856 } catch (RemoteException e) { 1857 Slog.e(CarLibLog.TAG_CAR, "registerCarVolumeEventCallback failed", e); 1858 return handleRemoteExceptionFromCarService(e, /* returnValue= */ false); 1859 } 1860 1861 return true; 1862 } 1863 1864 /** 1865 * Unregisters a {@link CarVolumeGroupEventCallback} registered via 1866 * {@link #registerCarVolumeGroupEventCallback} 1867 * 1868 * @param callback The callback to be removed 1869 * @throws NullPointerException if callback is {@code null} 1870 * @throws IllegalStateException if dynamic audio routing is not enabled 1871 * @throws IllegalStateException if volume group events are not enabled 1872 * 1873 * @hide 1874 */ 1875 @SystemApi 1876 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) unregisterCarVolumeGroupEventCallback( @onNull CarVolumeGroupEventCallback callback)1877 public void unregisterCarVolumeGroupEventCallback( 1878 @NonNull CarVolumeGroupEventCallback callback) { 1879 Objects.requireNonNull(callback, "Car volume event callback can not be null"); 1880 1881 CarVolumeGroupEventCallbackWrapper callbackWrapper = 1882 new CarVolumeGroupEventCallbackWrapper(/* executor= */ null, callback); 1883 if (mCarVolumeEventCallbacks.contains(callbackWrapper) 1884 && (mCarVolumeEventCallbacks.size() == 1)) { 1885 unregisterVolumeGroupEventCallback(); 1886 } 1887 1888 mCarVolumeEventCallbacks.remove(callbackWrapper); 1889 } 1890 unregisterVolumeGroupEventCallback()1891 private boolean unregisterVolumeGroupEventCallback() { 1892 try { 1893 if (!mService.unregisterCarVolumeEventCallback(mCarVolumeEventCallbackImpl)) { 1894 Slog.e(CarLibLog.TAG_CAR, 1895 "unregisterCarVolumeEventCallback failed with service"); 1896 return false; 1897 } 1898 } catch (RemoteException e) { 1899 Slog.e(CarLibLog.TAG_CAR, 1900 "unregisterCarVolumeEventCallback failed with exception", e); 1901 handleRemoteExceptionFromCarService(e); 1902 } 1903 1904 return true; 1905 } 1906 1907 /** 1908 * Returns the whether a volume group is muted 1909 * 1910 * <p><b>Note:</b> If {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled this will always 1911 * return {@code false} as group mute is disabled. 1912 * 1913 * @param zoneId The zone id whose volume groups is queried. 1914 * @param groupId The volume group id whose mute state is returned. 1915 * @return {@code true} if the volume group is muted, {@code false} 1916 * otherwise 1917 * 1918 * @hide 1919 */ 1920 @SystemApi 1921 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) isVolumeGroupMuted(int zoneId, int groupId)1922 public boolean isVolumeGroupMuted(int zoneId, int groupId) { 1923 try { 1924 return mService.isVolumeGroupMuted(zoneId, groupId); 1925 } catch (RemoteException e) { 1926 return handleRemoteExceptionFromCarService(e, false); 1927 } 1928 } 1929 1930 /** 1931 * Sets a volume group mute 1932 * 1933 * <p><b>Note:</b> If {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled this will throw an 1934 * error indicating the issue. 1935 * 1936 * @param zoneId The zone id whose volume groups will be changed. 1937 * @param groupId The volume group id whose mute state will be changed. 1938 * @param mute {@code true} to mute volume group, {@code false} otherwise 1939 * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI}, 1940 * {@link android.media.AudioManager#FLAG_PLAY_SOUND}) 1941 * 1942 * @hide 1943 */ 1944 @SystemApi 1945 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags)1946 public void setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags) { 1947 try { 1948 mService.setVolumeGroupMute(zoneId, groupId, mute, flags); 1949 } catch (RemoteException e) { 1950 handleRemoteExceptionFromCarService(e); 1951 } 1952 } 1953 convertInputDevicesToDeviceInfos( List<AudioDeviceAttributes> devices, int flag)1954 private List<AudioDeviceInfo> convertInputDevicesToDeviceInfos( 1955 List<AudioDeviceAttributes> devices, int flag) { 1956 int addressesSize = devices.size(); 1957 Set<String> deviceAddressMap = new HashSet<>(addressesSize); 1958 for (int i = 0; i < addressesSize; ++i) { 1959 AudioDeviceAttributes device = devices.get(i); 1960 deviceAddressMap.add(device.getAddress()); 1961 } 1962 List<AudioDeviceInfo> deviceInfoList = new ArrayList<>(devices.size()); 1963 AudioDeviceInfo[] inputDevices = mAudioManager.getDevices(flag); 1964 for (int i = 0; i < inputDevices.length; ++i) { 1965 AudioDeviceInfo info = inputDevices[i]; 1966 if (info.isSource() && deviceAddressMap.contains(info.getAddress())) { 1967 deviceInfoList.add(info); 1968 } 1969 } 1970 return deviceInfoList; 1971 } 1972 1973 private final class EventHandler extends Handler { 1974 private static final int MSG_GROUP_VOLUME_CHANGE = 1; 1975 private static final int MSG_GROUP_MUTE_CHANGE = 2; 1976 private static final int MSG_MASTER_MUTE_CHANGE = 3; 1977 private static final int MSG_VOLUME_GROUP_EVENT = 4; 1978 EventHandler(Looper looper)1979 private EventHandler(Looper looper) { 1980 super(looper); 1981 } 1982 1983 @Override handleMessage(Message msg)1984 public void handleMessage(Message msg) { 1985 switch (msg.what) { 1986 case MSG_GROUP_VOLUME_CHANGE: 1987 VolumeGroupChangeInfo volumeInfo = (VolumeGroupChangeInfo) msg.obj; 1988 handleOnGroupVolumeChanged(volumeInfo.mZoneId, volumeInfo.mGroupId, 1989 volumeInfo.mFlags); 1990 break; 1991 case MSG_GROUP_MUTE_CHANGE: 1992 VolumeGroupChangeInfo muteInfo = (VolumeGroupChangeInfo) msg.obj; 1993 handleOnGroupMuteChanged(muteInfo.mZoneId, muteInfo.mGroupId, muteInfo.mFlags); 1994 break; 1995 case MSG_MASTER_MUTE_CHANGE: 1996 handleOnMasterMuteChanged(msg.arg1, msg.arg2); 1997 break; 1998 case MSG_VOLUME_GROUP_EVENT: 1999 List<CarVolumeGroupEvent> events = (List<CarVolumeGroupEvent>) msg.obj; 2000 handleOnVolumeGroupEvent(events); 2001 default: 2002 Slog.e(CarLibLog.TAG_CAR, "Unknown message not handled:" + msg.what); 2003 break; 2004 } 2005 } 2006 dispatchOnGroupVolumeChanged(int zoneId, int groupId, int flags)2007 private void dispatchOnGroupVolumeChanged(int zoneId, int groupId, int flags) { 2008 VolumeGroupChangeInfo volumeInfo = new VolumeGroupChangeInfo(zoneId, groupId, flags); 2009 sendMessage(obtainMessage(MSG_GROUP_VOLUME_CHANGE, volumeInfo)); 2010 } 2011 dispatchOnMasterMuteChanged(int zoneId, int flags)2012 private void dispatchOnMasterMuteChanged(int zoneId, int flags) { 2013 sendMessage(obtainMessage(MSG_MASTER_MUTE_CHANGE, zoneId, flags)); 2014 } 2015 dispatchOnGroupMuteChanged(int zoneId, int groupId, int flags)2016 private void dispatchOnGroupMuteChanged(int zoneId, int groupId, int flags) { 2017 VolumeGroupChangeInfo volumeInfo = new VolumeGroupChangeInfo(zoneId, groupId, flags); 2018 sendMessage(obtainMessage(MSG_GROUP_MUTE_CHANGE, volumeInfo)); 2019 } 2020 dispatchOnVolumeGroupEvent(List<CarVolumeGroupEvent> events)2021 private void dispatchOnVolumeGroupEvent(List<CarVolumeGroupEvent> events) { 2022 sendMessage(obtainMessage(MSG_VOLUME_GROUP_EVENT, events)); 2023 } 2024 2025 private class VolumeGroupChangeInfo { 2026 public int mZoneId; 2027 public int mGroupId; 2028 public int mFlags; 2029 VolumeGroupChangeInfo(int zoneId, int groupId, int flags)2030 VolumeGroupChangeInfo(int zoneId, int groupId, int flags) { 2031 mZoneId = zoneId; 2032 mGroupId = groupId; 2033 mFlags = flags; 2034 } 2035 } 2036 } 2037 handleOnGroupVolumeChanged(int zoneId, int groupId, int flags)2038 private void handleOnGroupVolumeChanged(int zoneId, int groupId, int flags) { 2039 for (CarVolumeCallback callback : mCarVolumeCallbacks) { 2040 callback.onGroupVolumeChanged(zoneId, groupId, flags); 2041 } 2042 } 2043 handleOnMasterMuteChanged(int zoneId, int flags)2044 private void handleOnMasterMuteChanged(int zoneId, int flags) { 2045 for (CarVolumeCallback callback : mCarVolumeCallbacks) { 2046 callback.onMasterMuteChanged(zoneId, flags); 2047 } 2048 } 2049 handleOnGroupMuteChanged(int zoneId, int groupId, int flags)2050 private void handleOnGroupMuteChanged(int zoneId, int groupId, int flags) { 2051 for (CarVolumeCallback callback : mCarVolumeCallbacks) { 2052 callback.onGroupMuteChanged(zoneId, groupId, flags); 2053 } 2054 } 2055 2056 handleOnVolumeGroupEvent(List<CarVolumeGroupEvent> events)2057 private void handleOnVolumeGroupEvent(List<CarVolumeGroupEvent> events) { 2058 for (CarVolumeGroupEventCallbackWrapper wr : mCarVolumeEventCallbacks) { 2059 wr.mExecutor.execute(() -> wr.mCallback.onVolumeGroupEvent(events)); 2060 } 2061 } 2062 toIntArray(List<Integer> list)2063 private static int[] toIntArray(List<Integer> list) { 2064 int size = list.size(); 2065 int[] array = new int[size]; 2066 for (int i = 0; i < size; ++i) { 2067 array[i] = list.get(i); 2068 } 2069 return array; 2070 } 2071 asList(int[] intArray)2072 private static List<Integer> asList(int[] intArray) { 2073 List<Integer> zoneIdList = new ArrayList<Integer>(intArray.length); 2074 for (int index = 0; index < intArray.length; index++) { 2075 zoneIdList.add(intArray[index]); 2076 } 2077 return zoneIdList; 2078 } 2079 2080 /** 2081 * Callback interface to receive volume change events in a car. 2082 * Extend this class and register it with {@link #registerCarVolumeCallback(CarVolumeCallback)} 2083 * and unregister it via {@link #unregisterCarVolumeCallback(CarVolumeCallback)} 2084 */ 2085 public abstract static class CarVolumeCallback { 2086 /** 2087 * This is called whenever a group volume is changed. 2088 * 2089 * The changed-to volume index is not included, the caller is encouraged to 2090 * get the current group volume index via CarAudioManager. 2091 * 2092 * <p><b>Notes:</b> 2093 * <ul> 2094 * <li>If both {@link CarVolumeCallback} and {@code CarVolumeGroupEventCallback} 2095 * are registered by the same app, then volume group index changes are <b>only</b> 2096 * propagated through {@code CarVolumeGroupEventCallback} 2097 * (until it is unregistered)</li> 2098 * <li>Apps are encouraged to migrate to the new callback 2099 * {@code CarVolumeGroupEventCallback}</li> 2100 * </ul> 2101 * 2102 * @param zoneId Id of the audio zone that volume change happens 2103 * @param groupId Id of the volume group that volume is changed 2104 * @param flags see {@link android.media.AudioManager} for flag definitions 2105 */ 2106 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) onGroupVolumeChanged(int zoneId, int groupId, int flags)2107 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {} 2108 2109 /** 2110 * This is called whenever the global mute state is changed. 2111 * The changed-to global mute state is not included, the caller is encouraged to 2112 * get the current global mute state via AudioManager. 2113 * 2114 * <p><b>Note:</b> If {@link CarAudioManager#AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled 2115 * this will be triggered on mute changes. Otherwise, car audio mute changes will trigger 2116 * {@link #onGroupMuteChanged(int, int, int)} 2117 * 2118 * @param zoneId Id of the audio zone that global mute state change happens 2119 * @param flags see {@link android.media.AudioManager} for flag definitions 2120 */ 2121 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) onMasterMuteChanged(int zoneId, int flags)2122 public void onMasterMuteChanged(int zoneId, int flags) {} 2123 2124 /** 2125 * This is called whenever a group mute state is changed. 2126 * 2127 * The changed-to mute state is not included, the caller is encouraged to 2128 * get the current group mute state via CarAudioManager. 2129 * 2130 * <p><b>Notes:</b> 2131 * <ul> 2132 * <li>If {@link CarAudioManager#AUDIO_FEATURE_VOLUME_GROUP_MUTING} is enabled 2133 * this will be triggered on mute changes. Otherwise, car audio mute changes will 2134 * trigger {@link #onMasterMuteChanged(int, int)}</li> 2135 * <li>If both {@link CarVolumeCallback} and {@code CarVolumeGroupEventCallback} 2136 * are registered by the same app, then volume group mute changes are <b>only</b> 2137 * propagated through {@code CarVolumeGroupEventCallback} 2138 * (until it is unregistered)</li> 2139 * <li>Apps are encouraged to migrate to the new callback 2140 * {@code CarVolumeGroupEventCallback}</li> 2141 * </ul> 2142 * 2143 * @param zoneId Id of the audio zone that volume change happens 2144 * @param groupId Id of the volume group that volume is changed 2145 * @param flags see {@link android.media.AudioManager} for flag definitions 2146 */ 2147 @ExcludeFromCodeCoverageGeneratedReport(reason = BOILERPLATE_CODE) onGroupMuteChanged(int zoneId, int groupId, int flags)2148 public void onGroupMuteChanged(int zoneId, int groupId, int flags) {} 2149 } 2150 2151 private static final class MediaAudioRequestStatusCallbackWrapper 2152 extends IMediaAudioRequestStatusCallback.Stub { 2153 2154 private final Executor mExecutor; 2155 private final MediaAudioRequestStatusCallback mCallback; 2156 MediaAudioRequestStatusCallbackWrapper(Executor executor, MediaAudioRequestStatusCallback callback)2157 MediaAudioRequestStatusCallbackWrapper(Executor executor, 2158 MediaAudioRequestStatusCallback callback) { 2159 mExecutor = executor; 2160 mCallback = callback; 2161 } 2162 2163 @Override onMediaAudioRequestStatusChanged(CarOccupantZoneManager.OccupantZoneInfo info, long requestId, @CarAudioManager.MediaAudioRequestStatus int status)2164 public void onMediaAudioRequestStatusChanged(CarOccupantZoneManager.OccupantZoneInfo info, 2165 long requestId, 2166 @CarAudioManager.MediaAudioRequestStatus int status) throws RemoteException { 2167 long identity = Binder.clearCallingIdentity(); 2168 try { 2169 mExecutor.execute(() -> 2170 mCallback.onMediaAudioRequestStatusChanged(info, requestId, status)); 2171 } finally { 2172 Binder.restoreCallingIdentity(identity); 2173 } 2174 } 2175 } 2176 2177 private static final class SwitchAudioZoneConfigCallbackWrapper 2178 extends ISwitchAudioZoneConfigCallback.Stub { 2179 private final Executor mExecutor; 2180 private final SwitchAudioZoneConfigCallback mCallback; 2181 SwitchAudioZoneConfigCallbackWrapper(Executor executor, SwitchAudioZoneConfigCallback callback)2182 SwitchAudioZoneConfigCallbackWrapper(Executor executor, 2183 SwitchAudioZoneConfigCallback callback) { 2184 mExecutor = executor; 2185 mCallback = callback; 2186 } 2187 2188 @Override onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfig, boolean isSuccessful)2189 public void onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfig, 2190 boolean isSuccessful) { 2191 long identity = Binder.clearCallingIdentity(); 2192 try { 2193 mExecutor.execute(() -> 2194 mCallback.onAudioZoneConfigSwitched(zoneConfig, isSuccessful)); 2195 } finally { 2196 Binder.restoreCallingIdentity(identity); 2197 } 2198 } 2199 } 2200 2201 private static final class CarVolumeGroupEventCallbackWrapper { 2202 private final Executor mExecutor; 2203 private final CarVolumeGroupEventCallback mCallback; 2204 CarVolumeGroupEventCallbackWrapper(Executor executor, CarVolumeGroupEventCallback callback)2205 CarVolumeGroupEventCallbackWrapper(Executor executor, 2206 CarVolumeGroupEventCallback callback) { 2207 mExecutor = executor; 2208 mCallback = callback; 2209 } 2210 2211 @Override equals(Object o)2212 public boolean equals(Object o) { 2213 if (this == o) { 2214 return true; 2215 } 2216 2217 if (!(o instanceof CarVolumeGroupEventCallbackWrapper)) { 2218 return false; 2219 } 2220 2221 CarVolumeGroupEventCallbackWrapper rhs = (CarVolumeGroupEventCallbackWrapper) o; 2222 return mCallback == rhs.mCallback; 2223 } 2224 2225 @Override hashCode()2226 public int hashCode() { 2227 return mCallback.hashCode(); 2228 } 2229 } 2230 2231 private static final class AudioZonesMirrorStatusCallbackWrapper 2232 extends IAudioZonesMirrorStatusCallback.Stub { 2233 2234 private final Executor mExecutor; 2235 private final AudioZonesMirrorStatusCallback mCallback; 2236 AudioZonesMirrorStatusCallbackWrapper(Executor executor, AudioZonesMirrorStatusCallback callback)2237 AudioZonesMirrorStatusCallbackWrapper(Executor executor, 2238 AudioZonesMirrorStatusCallback callback) { 2239 mExecutor = executor; 2240 mCallback = callback; 2241 } 2242 onAudioZonesMirrorStatusChanged(int[] mirroredAudioZones, int status)2243 public void onAudioZonesMirrorStatusChanged(int[] mirroredAudioZones, 2244 int status) { 2245 long identity = Binder.clearCallingIdentity(); 2246 try { 2247 mExecutor.execute(() -> mCallback.onAudioZonesMirrorStatusChanged( 2248 asList(mirroredAudioZones), status)); 2249 } finally { 2250 Binder.restoreCallingIdentity(identity); 2251 } 2252 } 2253 } 2254 2255 private static final class AudioZoneConfigurationsChangeCallbackWrapper extends 2256 IAudioZoneConfigurationsChangeCallback.Stub { 2257 2258 private final Executor mExecutor; 2259 private final AudioZoneConfigurationsChangeCallback mCallback; 2260 AudioZoneConfigurationsChangeCallbackWrapper(Executor executor, AudioZoneConfigurationsChangeCallback callback)2261 private AudioZoneConfigurationsChangeCallbackWrapper(Executor executor, 2262 AudioZoneConfigurationsChangeCallback callback) { 2263 mExecutor = executor; 2264 mCallback = callback; 2265 } 2266 2267 @Override onAudioZoneConfigurationsChanged(List<CarAudioZoneConfigInfo> configs, int status)2268 public void onAudioZoneConfigurationsChanged(List<CarAudioZoneConfigInfo> configs, 2269 int status) { 2270 long identity = Binder.clearCallingIdentity(); 2271 try { 2272 mExecutor.execute(() -> mCallback.onAudioZoneConfigurationsChanged(configs, 2273 status)); 2274 } finally { 2275 Binder.restoreCallingIdentity(identity); 2276 } 2277 } 2278 } 2279 } 2280