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 android.annotation.IntDef; 19 import android.annotation.NonNull; 20 import android.annotation.Nullable; 21 import android.annotation.RequiresPermission; 22 import android.annotation.SystemApi; 23 import android.annotation.TestApi; 24 import android.car.Car; 25 import android.car.CarLibLog; 26 import android.car.CarManagerBase; 27 import android.car.annotation.AddedInOrBefore; 28 import android.car.annotation.ApiRequirements; 29 import android.media.AudioAttributes; 30 import android.media.AudioDeviceAttributes; 31 import android.media.AudioDeviceInfo; 32 import android.media.AudioManager; 33 import android.os.Bundle; 34 import android.os.Handler; 35 import android.os.IBinder; 36 import android.os.Looper; 37 import android.os.Message; 38 import android.os.RemoteException; 39 import android.util.Log; 40 41 import com.android.car.internal.annotation.AttributeUsage; 42 43 import java.lang.annotation.Retention; 44 import java.lang.annotation.RetentionPolicy; 45 import java.util.ArrayList; 46 import java.util.Collections; 47 import java.util.HashSet; 48 import java.util.List; 49 import java.util.Objects; 50 import java.util.Set; 51 import java.util.concurrent.CopyOnWriteArrayList; 52 53 /** 54 * APIs for handling audio in a car. 55 * 56 * In a car environment, we introduced the support to turn audio dynamic routing on /off by 57 * setting the "audioUseDynamicRouting" attribute in config.xml 58 * 59 * When audio dynamic routing is enabled: 60 * - Audio devices are grouped into zones 61 * - There is at least one primary zone, and extra secondary zones such as RSE 62 * (Reat Seat Entertainment) 63 * - Within each zone, audio devices are grouped into volume groups for volume control 64 * - Audio is assigned to an audio device based on its AudioAttributes usage 65 * 66 * When audio dynamic routing is disabled: 67 * - There is exactly one audio zone, which is the primary zone 68 * - Each volume group represents a controllable STREAM_TYPE, same as AudioManager 69 */ 70 public final class CarAudioManager extends CarManagerBase { 71 72 /** 73 * Zone id of the primary audio zone. 74 * @hide 75 */ 76 @SystemApi 77 @AddedInOrBefore(majorVersion = 33) 78 public static final int PRIMARY_AUDIO_ZONE = 0x0; 79 80 /** 81 * Zone id of the invalid audio zone. 82 * @hide 83 */ 84 @SystemApi 85 @AddedInOrBefore(majorVersion = 33) 86 public static final int INVALID_AUDIO_ZONE = 0xffffffff; 87 88 /** 89 * This is used to determine if dynamic routing is enabled via 90 * {@link #isAudioFeatureEnabled()} 91 */ 92 @AddedInOrBefore(majorVersion = 33) 93 public static final int AUDIO_FEATURE_DYNAMIC_ROUTING = 0x1; 94 95 /** 96 * This is used to determine if volume group muting is enabled via 97 * {@link #isAudioFeatureEnabled()} 98 * 99 * <p> 100 * If enabled, car volume group muting APIs can be used to mute each volume group, 101 * also car volume group muting changed callback will be called upon group mute changes. If 102 * disabled, car volume will toggle master mute instead. 103 */ 104 @AddedInOrBefore(majorVersion = 33) 105 public static final int AUDIO_FEATURE_VOLUME_GROUP_MUTING = 0x2; 106 107 /** @hide */ 108 @IntDef(flag = false, prefix = "AUDIO_FEATURE", value = { 109 AUDIO_FEATURE_DYNAMIC_ROUTING, 110 AUDIO_FEATURE_VOLUME_GROUP_MUTING 111 }) 112 @Retention(RetentionPolicy.SOURCE) 113 public @interface CarAudioFeature {} 114 115 /** 116 * Volume Group ID when volume group not found. 117 * @hide 118 */ 119 @AddedInOrBefore(majorVersion = 33) 120 public static final int INVALID_VOLUME_GROUP_ID = -1; 121 122 /** 123 * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an 124 * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus events, 125 * including {@link android.media.AudioManager#AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK}. 126 * The requester must hold {@link Car#PERMISSION_RECEIVE_CAR_AUDIO_DUCKING_EVENTS}; otherwise, 127 * this extra is ignored. 128 * 129 * @hide 130 */ 131 @SystemApi 132 @AddedInOrBefore(majorVersion = 33) 133 public static final String AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS = 134 "android.car.media.AUDIOFOCUS_EXTRA_RECEIVE_DUCKING_EVENTS"; 135 136 /** 137 * Extra for {@link android.media.AudioAttributes.Builder#addBundle(Bundle)}: when used in an 138 * {@link android.media.AudioFocusRequest}, the requester should receive all audio focus for the 139 * the zone. If the zone id is not defined: the audio focus request will default to the 140 * currently mapped zone for the requesting uid or {@link CarAudioManager.PRIMARY_AUDIO_ZONE} 141 * if no uid mapping currently exist. 142 * 143 * @hide 144 */ 145 @AddedInOrBefore(majorVersion = 33) 146 public static final String AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID = 147 "android.car.media.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID"; 148 149 private final ICarAudio mService; 150 private final CopyOnWriteArrayList<CarVolumeCallback> mCarVolumeCallbacks; 151 private final AudioManager mAudioManager; 152 153 private final EventHandler mEventHandler; 154 155 private final ICarVolumeCallback mCarVolumeCallbackImpl = 156 new android.car.media.ICarVolumeCallback.Stub() { 157 @Override 158 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) { 159 mEventHandler.dispatchOnGroupVolumeChanged(zoneId, groupId, flags); 160 } 161 162 @Override 163 public void onGroupMuteChanged(int zoneId, int groupId, int flags) { 164 mEventHandler.dispatchOnGroupMuteChanged(zoneId, groupId, flags); 165 } 166 167 @Override 168 public void onMasterMuteChanged(int zoneId, int flags) { 169 mEventHandler.dispatchOnMasterMuteChanged(zoneId, flags); 170 } 171 }; 172 173 /** 174 * @return Whether dynamic routing is enabled or not. 175 * 176 * @deprecated use {@link #isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)} instead. 177 * 178 * @hide 179 */ 180 @TestApi 181 @Deprecated 182 @AddedInOrBefore(majorVersion = 33) isDynamicRoutingEnabled()183 public boolean isDynamicRoutingEnabled() { 184 return isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING); 185 } 186 187 /** 188 * Determines if an audio feature is enabled. 189 * 190 * @param audioFeature audio feature to query, can be {@link #AUDIO_FEATURE_DYNAMIC_ROUTING} or 191 * {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} 192 * @return Returns {@code true} if the feature is enabled, {@code false} otherwise. 193 */ 194 @AddedInOrBefore(majorVersion = 33) isAudioFeatureEnabled(@arAudioFeature int audioFeature)195 public boolean isAudioFeatureEnabled(@CarAudioFeature int audioFeature) { 196 try { 197 return mService.isAudioFeatureEnabled(audioFeature); 198 } catch (RemoteException e) { 199 return handleRemoteExceptionFromCarService(e, false); 200 } 201 } 202 203 /** 204 * Sets the volume index for a volume group in primary zone. 205 * 206 * @see {@link #setGroupVolume(int, int, int, int)} 207 * @hide 208 */ 209 @SystemApi 210 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 211 @AddedInOrBefore(majorVersion = 33) setGroupVolume(int groupId, int index, int flags)212 public void setGroupVolume(int groupId, int index, int flags) { 213 setGroupVolume(PRIMARY_AUDIO_ZONE, groupId, index, flags); 214 } 215 216 /** 217 * Sets the volume index for a volume group. 218 * 219 * @param zoneId The zone id whose volume group is affected. 220 * @param groupId The volume group id whose volume index should be set. 221 * @param index The volume index to set. See 222 * {@link #getGroupMaxVolume(int, int)} for the largest valid value. 223 * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI}, 224 * {@link android.media.AudioManager#FLAG_PLAY_SOUND}) 225 * @hide 226 */ 227 @SystemApi 228 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 229 @AddedInOrBefore(majorVersion = 33) setGroupVolume(int zoneId, int groupId, int index, int flags)230 public void setGroupVolume(int zoneId, int groupId, int index, int flags) { 231 try { 232 mService.setGroupVolume(zoneId, groupId, index, flags); 233 } catch (RemoteException e) { 234 handleRemoteExceptionFromCarService(e); 235 } 236 } 237 238 /** 239 * Returns the maximum volume index for a volume group in primary zone. 240 * 241 * @see {@link #getGroupMaxVolume(int, int)} 242 * @hide 243 */ 244 @SystemApi 245 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 246 @AddedInOrBefore(majorVersion = 33) getGroupMaxVolume(int groupId)247 public int getGroupMaxVolume(int groupId) { 248 return getGroupMaxVolume(PRIMARY_AUDIO_ZONE, groupId); 249 } 250 251 /** 252 * Returns the maximum volume index for a volume group. 253 * 254 * @param zoneId The zone id whose volume group is queried. 255 * @param groupId The volume group id whose maximum volume index is returned. 256 * @return The maximum valid volume index for the given group. 257 * @hide 258 */ 259 @SystemApi 260 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 261 @AddedInOrBefore(majorVersion = 33) getGroupMaxVolume(int zoneId, int groupId)262 public int getGroupMaxVolume(int zoneId, int groupId) { 263 try { 264 return mService.getGroupMaxVolume(zoneId, groupId); 265 } catch (RemoteException e) { 266 return handleRemoteExceptionFromCarService(e, 0); 267 } 268 } 269 270 /** 271 * Returns the minimum volume index for a volume group in primary zone. 272 * 273 * @see {@link #getGroupMinVolume(int, int)} 274 * @hide 275 */ 276 @SystemApi 277 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 278 @AddedInOrBefore(majorVersion = 33) getGroupMinVolume(int groupId)279 public int getGroupMinVolume(int groupId) { 280 return getGroupMinVolume(PRIMARY_AUDIO_ZONE, groupId); 281 } 282 283 /** 284 * Returns the minimum volume index for a volume group. 285 * 286 * @param zoneId The zone id whose volume group is queried. 287 * @param groupId The volume group id whose minimum volume index is returned. 288 * @return The minimum valid volume index for the given group, non-negative 289 * @hide 290 */ 291 @SystemApi 292 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 293 @AddedInOrBefore(majorVersion = 33) getGroupMinVolume(int zoneId, int groupId)294 public int getGroupMinVolume(int zoneId, int groupId) { 295 try { 296 return mService.getGroupMinVolume(zoneId, groupId); 297 } catch (RemoteException e) { 298 return handleRemoteExceptionFromCarService(e, 0); 299 } 300 } 301 302 /** 303 * Returns the current volume index for a volume group in primary zone. 304 * 305 * @see {@link #getGroupVolume(int, int)} 306 * @hide 307 */ 308 @SystemApi 309 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 310 @AddedInOrBefore(majorVersion = 33) getGroupVolume(int groupId)311 public int getGroupVolume(int groupId) { 312 return getGroupVolume(PRIMARY_AUDIO_ZONE, groupId); 313 } 314 315 /** 316 * Returns the current volume index for a volume group. 317 * 318 * @param zoneId The zone id whose volume groups is queried. 319 * @param groupId The volume group id whose volume index is returned. 320 * @return The current volume index for the given group. 321 * 322 * @see #getGroupMaxVolume(int, int) 323 * @see #setGroupVolume(int, int, int, int) 324 * @hide 325 */ 326 @SystemApi 327 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 328 @AddedInOrBefore(majorVersion = 33) getGroupVolume(int zoneId, int groupId)329 public int getGroupVolume(int zoneId, int groupId) { 330 try { 331 return mService.getGroupVolume(zoneId, groupId); 332 } catch (RemoteException e) { 333 return handleRemoteExceptionFromCarService(e, 0); 334 } 335 } 336 337 /** 338 * Adjust the relative volume in the front vs back of the vehicle cabin. 339 * 340 * @param value in the range -1.0 to 1.0 for fully toward the back through 341 * fully toward the front. 0.0 means evenly balanced. 342 * 343 * @see #setBalanceTowardRight(float) 344 * @hide 345 */ 346 @SystemApi 347 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 348 @AddedInOrBefore(majorVersion = 33) setFadeTowardFront(float value)349 public void setFadeTowardFront(float value) { 350 try { 351 mService.setFadeTowardFront(value); 352 } catch (RemoteException e) { 353 handleRemoteExceptionFromCarService(e); 354 } 355 } 356 357 /** 358 * Adjust the relative volume on the left vs right side of the vehicle cabin. 359 * 360 * @param value in the range -1.0 to 1.0 for fully toward the left through 361 * fully toward the right. 0.0 means evenly balanced. 362 * 363 * @see #setFadeTowardFront(float) 364 * @hide 365 */ 366 @SystemApi 367 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 368 @AddedInOrBefore(majorVersion = 33) setBalanceTowardRight(float value)369 public void setBalanceTowardRight(float value) { 370 try { 371 mService.setBalanceTowardRight(value); 372 } catch (RemoteException e) { 373 handleRemoteExceptionFromCarService(e); 374 } 375 } 376 377 /** 378 * Queries the system configuration in order to report the available, non-microphone audio 379 * input devices. 380 * 381 * @return An array of strings representing the available input ports. 382 * Each port is identified by it's "address" tag in the audioPolicyConfiguration xml file. 383 * Empty array if we find nothing. 384 * 385 * @see #createAudioPatch(String, int, int) 386 * @see #releaseAudioPatch(CarAudioPatchHandle) 387 * 388 * @deprecated use {@link AudioManager#getDevices(int)} with 389 * {@link AudioManager#GET_DEVICES_INPUTS} instead 390 * @hide 391 */ 392 @SystemApi 393 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 394 @Deprecated 395 @AddedInOrBefore(majorVersion = 33) getExternalSources()396 public @NonNull String[] getExternalSources() { 397 try { 398 return mService.getExternalSources(); 399 } catch (RemoteException e) { 400 handleRemoteExceptionFromCarService(e); 401 return new String[0]; 402 } 403 } 404 405 /** 406 * Given an input port identified by getExternalSources(), request that it's audio signal 407 * be routed below the HAL to the output port associated with the given usage. For example, 408 * The output of a tuner might be routed directly to the output buss associated with 409 * AudioAttributes.USAGE_MEDIA while the tuner is playing. 410 * 411 * @param sourceAddress the input port name obtained from getExternalSources(). 412 * @param usage the type of audio represented by this source (usually USAGE_MEDIA). 413 * @param gainInMillibels How many steps above the minimum value defined for the source port to 414 * set the gain when creating the patch. 415 * This may be used for source balancing without affecting the user 416 * controlled volumes applied to the destination ports. A value of 417 * 0 indicates no gain change is requested. 418 * @return A handle for the created patch which can be used to later remove it. 419 * 420 * @see #getExternalSources() 421 * @see #releaseAudioPatch(CarAudioPatchHandle) 422 * 423 * @deprecated use {@link android.media.HwAudioSource} instead 424 * @hide 425 */ 426 @SystemApi 427 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 428 @Deprecated 429 @AddedInOrBefore(majorVersion = 33) createAudioPatch(String sourceAddress, @AttributeUsage int usage, int gainInMillibels)430 public CarAudioPatchHandle createAudioPatch(String sourceAddress, @AttributeUsage int usage, 431 int gainInMillibels) { 432 try { 433 return mService.createAudioPatch(sourceAddress, usage, gainInMillibels); 434 } catch (RemoteException e) { 435 return handleRemoteExceptionFromCarService(e, null); 436 } 437 } 438 439 /** 440 * Removes the association between an input port and an output port identified by the provided 441 * handle. 442 * 443 * @param patch CarAudioPatchHandle returned from createAudioPatch(). 444 * 445 * @see #getExternalSources() 446 * @see #createAudioPatch(String, int, int) 447 * 448 * @deprecated use {@link android.media.HwAudioSource} instead 449 * @hide 450 */ 451 @SystemApi 452 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 453 @Deprecated 454 @AddedInOrBefore(majorVersion = 33) releaseAudioPatch(CarAudioPatchHandle patch)455 public void releaseAudioPatch(CarAudioPatchHandle patch) { 456 try { 457 mService.releaseAudioPatch(patch); 458 } catch (RemoteException e) { 459 handleRemoteExceptionFromCarService(e); 460 } 461 } 462 463 /** 464 * Gets the count of available volume groups in primary zone. 465 * 466 * @see {@link #getVolumeGroupCount(int)} 467 * @hide 468 */ 469 @SystemApi 470 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 471 @AddedInOrBefore(majorVersion = 33) getVolumeGroupCount()472 public int getVolumeGroupCount() { 473 return getVolumeGroupCount(PRIMARY_AUDIO_ZONE); 474 } 475 476 /** 477 * Gets the count of available volume groups in the system. 478 * 479 * @param zoneId The zone id whois count of volume groups is queried. 480 * @return Count of volume groups 481 * @hide 482 */ 483 @SystemApi 484 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 485 @AddedInOrBefore(majorVersion = 33) getVolumeGroupCount(int zoneId)486 public int getVolumeGroupCount(int zoneId) { 487 try { 488 return mService.getVolumeGroupCount(zoneId); 489 } catch (RemoteException e) { 490 return handleRemoteExceptionFromCarService(e, 0); 491 } 492 } 493 494 /** 495 * Gets the volume group id for a given {@link AudioAttributes} usage in primary zone. 496 * 497 * @see {@link #getVolumeGroupIdForUsage(int, int)} 498 * @hide 499 */ 500 @SystemApi 501 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 502 @AddedInOrBefore(majorVersion = 33) getVolumeGroupIdForUsage(@ttributeUsage int usage)503 public int getVolumeGroupIdForUsage(@AttributeUsage int usage) { 504 return getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, usage); 505 } 506 507 /** 508 * Gets the volume group id for a given {@link AudioAttributes} usage. 509 * 510 * @param zoneId The zone id whose volume group is queried. 511 * @param usage The {@link AudioAttributes} usage to get a volume group from. 512 * @return The volume group id where the usage belongs to 513 * @hide 514 */ 515 @SystemApi 516 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 517 @AddedInOrBefore(majorVersion = 33) getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage)518 public int getVolumeGroupIdForUsage(int zoneId, @AttributeUsage int usage) { 519 try { 520 return mService.getVolumeGroupIdForUsage(zoneId, usage); 521 } catch (RemoteException e) { 522 return handleRemoteExceptionFromCarService(e, 0); 523 } 524 } 525 526 /** 527 * Gets array of {@link AudioAttributes} usages for a volume group in primary zone. 528 * 529 * @see {@link #getUsagesForVolumeGroupId(int, int)} 530 * @hide 531 */ 532 @SystemApi 533 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 534 @AddedInOrBefore(majorVersion = 33) getUsagesForVolumeGroupId(int groupId)535 public @NonNull int[] getUsagesForVolumeGroupId(int groupId) { 536 return getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE, groupId); 537 } 538 539 /** 540 * Returns the volume group info associated with the zone id and group id. 541 * 542 * <p>The volume information, including mute, blocked, limited state will reflect the state 543 * of the volume group at the time of query. 544 * 545 * @param zoneId zone id for the group to query 546 * @param groupId group id for the group to query 547 * @throws IllegalArgumentException if the audio zone or group id are invalid 548 * 549 * @return the current volume group info 550 * 551 * @hide 552 */ 553 @SystemApi 554 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 555 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 556 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 557 @Nullable getVolumeGroupInfo(int zoneId, int groupId)558 public CarVolumeGroupInfo getVolumeGroupInfo(int zoneId, int groupId) { 559 try { 560 return mService.getVolumeGroupInfo(zoneId, groupId); 561 } catch (RemoteException e) { 562 return handleRemoteExceptionFromCarService(e, null); 563 } 564 } 565 566 /** 567 * Returns a list of volume group info associated with the zone id. 568 * 569 * <p>The volume information, including mute, blocked, limited state will reflect the state 570 * of the volume group at the time of query. 571 * 572 * @param zoneId zone id for the group to query 573 * @throws IllegalArgumentException if the audio zone is invalid 574 * 575 * @return all the current volume group info's for the zone id 576 * 577 * @hide 578 */ 579 @SystemApi 580 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 581 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 582 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 583 @NonNull getVolumeGroupInfosForZone(int zoneId)584 public List<CarVolumeGroupInfo> getVolumeGroupInfosForZone(int zoneId) { 585 try { 586 return mService.getVolumeGroupInfosForZone(zoneId); 587 } catch (RemoteException e) { 588 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 589 } 590 } 591 592 /** 593 * Returns a list of audio attributes associated with the volume group info. 594 * 595 * @param groupInfo group info to query 596 * @throws NullPointerException if the volume group info is {@code null} 597 * 598 * @return list of audio attributes associated with the volume group info 599 * 600 * @hide 601 */ 602 @SystemApi 603 @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.TIRAMISU_3, 604 minPlatformVersion = ApiRequirements.PlatformVersion.TIRAMISU_0) 605 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 606 @NonNull getAudioAttributesForVolumeGroup( @onNull CarVolumeGroupInfo groupInfo)607 public List<AudioAttributes> getAudioAttributesForVolumeGroup( 608 @NonNull CarVolumeGroupInfo groupInfo) { 609 try { 610 return mService.getAudioAttributesForVolumeGroup(groupInfo); 611 } catch (RemoteException e) { 612 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 613 } 614 } 615 616 /** 617 * Gets array of {@link AudioAttributes} usages for a volume group in a zone. 618 * 619 * @param zoneId The zone id whose volume group is queried. 620 * @param groupId The volume group id whose associated audio usages is returned. 621 * @return Array of {@link AudioAttributes} usages for a given volume group id 622 * @hide 623 */ 624 @SystemApi 625 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 626 @AddedInOrBefore(majorVersion = 33) getUsagesForVolumeGroupId(int zoneId, int groupId)627 public @NonNull int[] getUsagesForVolumeGroupId(int zoneId, int groupId) { 628 try { 629 return mService.getUsagesForVolumeGroupId(zoneId, groupId); 630 } catch (RemoteException e) { 631 return handleRemoteExceptionFromCarService(e, new int[0]); 632 } 633 } 634 635 /** 636 * Determines if a particular volume group has any audio playback in a zone 637 * 638 * @param zoneId The zone id whose volume group is queried. 639 * @param groupId The volume group id whose associated audio usages is returned. 640 * @return {@code true} if the group has active playback, {@code false} otherwise 641 * 642 * @hide 643 */ 644 @SystemApi 645 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 646 @AddedInOrBefore(majorVersion = 33) isPlaybackOnVolumeGroupActive(int zoneId, int groupId)647 public boolean isPlaybackOnVolumeGroupActive(int zoneId, int groupId) { 648 try { 649 return mService.isPlaybackOnVolumeGroupActive(zoneId, groupId); 650 } catch (RemoteException e) { 651 return handleRemoteExceptionFromCarService(e, false); 652 } 653 } 654 655 /** 656 * Gets the audio zones currently available 657 * 658 * @return audio zone ids 659 * @hide 660 */ 661 @SystemApi 662 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 663 @AddedInOrBefore(majorVersion = 33) getAudioZoneIds()664 public @NonNull List<Integer> getAudioZoneIds() { 665 try { 666 int[] zoneIdArray = mService.getAudioZoneIds(); 667 List<Integer> zoneIdList = new ArrayList<Integer>(zoneIdArray.length); 668 for (int zoneIdValue : zoneIdArray) { 669 zoneIdList.add(zoneIdValue); 670 } 671 return zoneIdList; 672 } catch (RemoteException e) { 673 return handleRemoteExceptionFromCarService(e, Collections.emptyList()); 674 } 675 } 676 677 /** 678 * Gets the audio zone id currently mapped to uId, 679 * defaults to PRIMARY_AUDIO_ZONE if no mapping exist 680 * 681 * @param uid The uid to map 682 * @return zone id mapped to uid 683 * @hide 684 */ 685 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 686 @AddedInOrBefore(majorVersion = 33) getZoneIdForUid(int uid)687 public int getZoneIdForUid(int uid) { 688 try { 689 return mService.getZoneIdForUid(uid); 690 } catch (RemoteException e) { 691 return handleRemoteExceptionFromCarService(e, 0); 692 } 693 } 694 695 /** 696 * Maps the audio zone id to uid 697 * 698 * @param zoneId The audio zone id 699 * @param uid The uid to map 700 * @return true if the uid is successfully mapped 701 * @hide 702 */ 703 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 704 @AddedInOrBefore(majorVersion = 33) setZoneIdForUid(int zoneId, int uid)705 public boolean setZoneIdForUid(int zoneId, int uid) { 706 try { 707 return mService.setZoneIdForUid(zoneId, uid); 708 } catch (RemoteException e) { 709 return handleRemoteExceptionFromCarService(e, false); 710 } 711 } 712 713 /** 714 * Clears the current zone mapping of the uid 715 * 716 * @param uid The uid to clear 717 * @return true if the zone was successfully cleared 718 * @hide 719 */ 720 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 721 @AddedInOrBefore(majorVersion = 33) clearZoneIdForUid(int uid)722 public boolean clearZoneIdForUid(int uid) { 723 try { 724 return mService.clearZoneIdForUid(uid); 725 } catch (RemoteException e) { 726 return handleRemoteExceptionFromCarService(e, false); 727 } 728 } 729 730 /** 731 * Gets the output device for a given {@link AudioAttributes} usage in zoneId. 732 * 733 * <p><b>Note:</b> To be used for routing to a specific device. Most applications should 734 * use the regular routing mechanism, which is to set audio attribute usage to 735 * an audio track. 736 * 737 * @param zoneId zone id to query for device 738 * @param usage usage where audio is routed 739 * @return Audio device info, returns {@code null} if audio device usage fails to map to 740 * an active audio device. This is different from the using an invalid value for 741 * {@link AudioAttributes} usage. In the latter case the query will fail with a 742 * RuntimeException indicating the issue. 743 * 744 * @hide 745 */ 746 @SystemApi 747 @Nullable 748 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 749 @AddedInOrBefore(majorVersion = 33) getOutputDeviceForUsage(int zoneId, @AttributeUsage int usage)750 public AudioDeviceInfo getOutputDeviceForUsage(int zoneId, @AttributeUsage int usage) { 751 try { 752 String deviceAddress = mService.getOutputDeviceAddressForUsage(zoneId, usage); 753 if (deviceAddress == null) { 754 return null; 755 } 756 AudioDeviceInfo[] outputDevices = 757 mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS); 758 for (AudioDeviceInfo info : outputDevices) { 759 if (info.getAddress().equals(deviceAddress)) { 760 return info; 761 } 762 } 763 return null; 764 } catch (RemoteException e) { 765 return handleRemoteExceptionFromCarService(e, null); 766 } 767 } 768 769 /** 770 * Gets the input devices for an audio zone 771 * 772 * @return list of input devices 773 * @hide 774 */ 775 @SystemApi 776 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS) 777 @AddedInOrBefore(majorVersion = 33) getInputDevicesForZoneId(int zoneId)778 public @NonNull List<AudioDeviceInfo> getInputDevicesForZoneId(int zoneId) { 779 try { 780 return convertInputDevicesToDeviceInfos( 781 mService.getInputDevicesForZoneId(zoneId), 782 AudioManager.GET_DEVICES_INPUTS); 783 } catch (RemoteException e) { 784 return handleRemoteExceptionFromCarService(e, Collections.EMPTY_LIST); 785 } 786 } 787 788 /** @hide */ 789 @Override 790 @AddedInOrBefore(majorVersion = 33) onCarDisconnected()791 public void onCarDisconnected() { 792 if (mService != null && !mCarVolumeCallbacks.isEmpty()) { 793 unregisterVolumeCallback(); 794 } 795 } 796 797 /** @hide */ CarAudioManager(Car car, IBinder service)798 public CarAudioManager(Car car, IBinder service) { 799 super(car); 800 mService = ICarAudio.Stub.asInterface(service); 801 mAudioManager = getContext().getSystemService(AudioManager.class); 802 mCarVolumeCallbacks = new CopyOnWriteArrayList<>(); 803 mEventHandler = new EventHandler(getEventHandler().getLooper()); 804 } 805 806 /** 807 * Registers a {@link CarVolumeCallback} to receive volume change callbacks 808 * @param callback {@link CarVolumeCallback} instance, can not be null 809 * <p> 810 * Requires permission Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME 811 */ 812 @AddedInOrBefore(majorVersion = 33) registerCarVolumeCallback(@onNull CarVolumeCallback callback)813 public void registerCarVolumeCallback(@NonNull CarVolumeCallback callback) { 814 Objects.requireNonNull(callback); 815 816 if (mCarVolumeCallbacks.isEmpty()) { 817 registerVolumeCallback(); 818 } 819 820 mCarVolumeCallbacks.add(callback); 821 } 822 823 /** 824 * Unregisters a {@link CarVolumeCallback} from receiving volume change callbacks 825 * @param callback {@link CarVolumeCallback} instance previously registered, can not be null 826 * <p> 827 * Requires permission Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME 828 */ 829 @AddedInOrBefore(majorVersion = 33) unregisterCarVolumeCallback(@onNull CarVolumeCallback callback)830 public void unregisterCarVolumeCallback(@NonNull CarVolumeCallback callback) { 831 Objects.requireNonNull(callback); 832 if (mCarVolumeCallbacks.remove(callback) && mCarVolumeCallbacks.isEmpty()) { 833 unregisterVolumeCallback(); 834 } 835 } 836 registerVolumeCallback()837 private void registerVolumeCallback() { 838 try { 839 mService.registerVolumeCallback(mCarVolumeCallbackImpl.asBinder()); 840 } catch (RemoteException e) { 841 Log.e(CarLibLog.TAG_CAR, "registerVolumeCallback failed", e); 842 } 843 } 844 unregisterVolumeCallback()845 private void unregisterVolumeCallback() { 846 try { 847 mService.unregisterVolumeCallback(mCarVolumeCallbackImpl.asBinder()); 848 } catch (RemoteException e) { 849 handleRemoteExceptionFromCarService(e); 850 } 851 } 852 853 /** 854 * Returns the whether a volume group is muted 855 * 856 * <p><b>Note:<b/> If {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled this will always 857 * return {@code false} as group mute is disabled. 858 * 859 * @param zoneId The zone id whose volume groups is queried. 860 * @param groupId The volume group id whose mute state is returned. 861 * @return {@code true} if the volume group is muted, {@code false} 862 * otherwise 863 * 864 * @hide 865 */ 866 @SystemApi 867 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 868 @AddedInOrBefore(majorVersion = 33) isVolumeGroupMuted(int zoneId, int groupId)869 public boolean isVolumeGroupMuted(int zoneId, int groupId) { 870 try { 871 return mService.isVolumeGroupMuted(zoneId, groupId); 872 } catch (RemoteException e) { 873 return handleRemoteExceptionFromCarService(e, false); 874 } 875 } 876 877 /** 878 * Sets a volume group mute 879 * 880 * <p><b>Note:<b/> If {@link #AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled this will throw an 881 * error indicating the issue. 882 * 883 * @param zoneId The zone id whose volume groups will be changed. 884 * @param groupId The volume group id whose mute state will be changed. 885 * @param mute {@code true} to mute volume group, {@code false} otherwise 886 * @param flags One or more flags (e.g., {@link android.media.AudioManager#FLAG_SHOW_UI}, 887 * {@link android.media.AudioManager#FLAG_PLAY_SOUND}) 888 * 889 * @hide 890 */ 891 @SystemApi 892 @RequiresPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME) 893 @AddedInOrBefore(majorVersion = 33) setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags)894 public void setVolumeGroupMute(int zoneId, int groupId, boolean mute, int flags) { 895 try { 896 mService.setVolumeGroupMute(zoneId, groupId, mute, flags); 897 } catch (RemoteException e) { 898 handleRemoteExceptionFromCarService(e); 899 } 900 } 901 convertInputDevicesToDeviceInfos( List<AudioDeviceAttributes> devices, int flag)902 private List<AudioDeviceInfo> convertInputDevicesToDeviceInfos( 903 List<AudioDeviceAttributes> devices, int flag) { 904 int addressesSize = devices.size(); 905 Set<String> deviceAddressMap = new HashSet<>(addressesSize); 906 for (int i = 0; i < addressesSize; ++i) { 907 AudioDeviceAttributes device = devices.get(i); 908 deviceAddressMap.add(device.getAddress()); 909 } 910 List<AudioDeviceInfo> deviceInfoList = new ArrayList<>(devices.size()); 911 AudioDeviceInfo[] inputDevices = mAudioManager.getDevices(flag); 912 for (int i = 0; i < inputDevices.length; ++i) { 913 AudioDeviceInfo info = inputDevices[i]; 914 if (info.isSource() && deviceAddressMap.contains(info.getAddress())) { 915 deviceInfoList.add(info); 916 } 917 } 918 return deviceInfoList; 919 } 920 921 private final class EventHandler extends Handler { 922 private static final int MSG_GROUP_VOLUME_CHANGE = 1; 923 private static final int MSG_GROUP_MUTE_CHANGE = 2; 924 private static final int MSG_MASTER_MUTE_CHANGE = 3; 925 EventHandler(Looper looper)926 private EventHandler(Looper looper) { 927 super(looper); 928 } 929 930 @Override handleMessage(Message msg)931 public void handleMessage(Message msg) { 932 switch (msg.what) { 933 case MSG_GROUP_VOLUME_CHANGE: 934 VolumeGroupChangeInfo volumeInfo = (VolumeGroupChangeInfo) msg.obj; 935 handleOnGroupVolumeChanged(volumeInfo.mZoneId, volumeInfo.mGroupId, 936 volumeInfo.mFlags); 937 break; 938 case MSG_GROUP_MUTE_CHANGE: 939 VolumeGroupChangeInfo muteInfo = (VolumeGroupChangeInfo) msg.obj; 940 handleOnGroupMuteChanged(muteInfo.mZoneId, muteInfo.mGroupId, muteInfo.mFlags); 941 break; 942 case MSG_MASTER_MUTE_CHANGE: 943 handleOnMasterMuteChanged(msg.arg1, msg.arg2); 944 break; 945 default: 946 Log.e(CarLibLog.TAG_CAR, "Unknown nessage not handled:" + msg.what); 947 break; 948 } 949 } 950 dispatchOnGroupVolumeChanged(int zoneId, int groupId, int flags)951 private void dispatchOnGroupVolumeChanged(int zoneId, int groupId, int flags) { 952 VolumeGroupChangeInfo volumeInfo = new VolumeGroupChangeInfo(zoneId, groupId, flags); 953 sendMessage(obtainMessage(MSG_GROUP_VOLUME_CHANGE, volumeInfo)); 954 } 955 dispatchOnMasterMuteChanged(int zoneId, int flags)956 private void dispatchOnMasterMuteChanged(int zoneId, int flags) { 957 sendMessage(obtainMessage(MSG_MASTER_MUTE_CHANGE, zoneId, flags)); 958 } 959 dispatchOnGroupMuteChanged(int zoneId, int groupId, int flags)960 private void dispatchOnGroupMuteChanged(int zoneId, int groupId, int flags) { 961 VolumeGroupChangeInfo volumeInfo = new VolumeGroupChangeInfo(zoneId, groupId, flags); 962 sendMessage(obtainMessage(MSG_GROUP_MUTE_CHANGE, volumeInfo)); 963 } 964 965 private class VolumeGroupChangeInfo { 966 public int mZoneId; 967 public int mGroupId; 968 public int mFlags; 969 VolumeGroupChangeInfo(int zoneId, int groupId, int flags)970 VolumeGroupChangeInfo(int zoneId, int groupId, int flags) { 971 mZoneId = zoneId; 972 mGroupId = groupId; 973 mFlags = flags; 974 } 975 } 976 } 977 handleOnGroupVolumeChanged(int zoneId, int groupId, int flags)978 private void handleOnGroupVolumeChanged(int zoneId, int groupId, int flags) { 979 for (CarVolumeCallback callback : mCarVolumeCallbacks) { 980 callback.onGroupVolumeChanged(zoneId, groupId, flags); 981 } 982 } 983 handleOnMasterMuteChanged(int zoneId, int flags)984 private void handleOnMasterMuteChanged(int zoneId, int flags) { 985 for (CarVolumeCallback callback : mCarVolumeCallbacks) { 986 callback.onMasterMuteChanged(zoneId, flags); 987 } 988 } 989 handleOnGroupMuteChanged(int zoneId, int groupId, int flags)990 private void handleOnGroupMuteChanged(int zoneId, int groupId, int flags) { 991 for (CarVolumeCallback callback : mCarVolumeCallbacks) { 992 callback.onGroupMuteChanged(zoneId, groupId, flags); 993 } 994 } 995 996 /** 997 * Callback interface to receive volume change events in a car. 998 * Extend this class and register it with {@link #registerCarVolumeCallback(CarVolumeCallback)} 999 * and unregister it via {@link #unregisterCarVolumeCallback(CarVolumeCallback)} 1000 */ 1001 public abstract static class CarVolumeCallback { 1002 /** 1003 * This is called whenever a group volume is changed. 1004 * The changed-to volume index is not included, the caller is encouraged to 1005 * get the current group volume index via CarAudioManager. 1006 * 1007 * @param zoneId Id of the audio zone that volume change happens 1008 * @param groupId Id of the volume group that volume is changed 1009 * @param flags see {@link android.media.AudioManager} for flag definitions 1010 */ 1011 @AddedInOrBefore(majorVersion = 33) onGroupVolumeChanged(int zoneId, int groupId, int flags)1012 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {} 1013 1014 /** 1015 * This is called whenever the global mute state is changed. 1016 * The changed-to global mute state is not included, the caller is encouraged to 1017 * get the current global mute state via AudioManager. 1018 * 1019 * <p><b>Note:<b/> If {@link CarAudioManager#AUDIO_FEATURE_VOLUME_GROUP_MUTING} is disabled 1020 * this will be triggered on mute changes. Otherwise, car audio mute changes will trigger 1021 * {@link #onGroupMuteChanged(int, int, int)} 1022 * 1023 * @param zoneId Id of the audio zone that global mute state change happens 1024 * @param flags see {@link android.media.AudioManager} for flag definitions 1025 */ 1026 @AddedInOrBefore(majorVersion = 33) onMasterMuteChanged(int zoneId, int flags)1027 public void onMasterMuteChanged(int zoneId, int flags) {} 1028 1029 /** 1030 * This is called whenever a group mute state is changed. 1031 * The changed-to mute state is not included, the caller is encouraged to 1032 * get the current group mute state via CarAudioManager. 1033 * 1034 * <p><b>Note:<b/> If {@link CarAudioManager#AUDIO_FEATURE_VOLUME_GROUP_MUTING} is enabled 1035 * this will be triggered on mute changes. Otherwise, car audio mute changes will trigger 1036 * {@link #onMasterMuteChanged(int, int)} 1037 * 1038 * @param zoneId Id of the audio zone that volume change happens 1039 * @param groupId Id of the volume group that volume is changed 1040 * @param flags see {@link android.media.AudioManager} for flag definitions 1041 */ 1042 @AddedInOrBefore(majorVersion = 33) onGroupMuteChanged(int zoneId, int groupId, int flags)1043 public void onGroupMuteChanged(int zoneId, int groupId, int flags) {} 1044 } 1045 } 1046