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