1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.car.audio.hal; 18 19 import static android.media.AudioAttributes.USAGE_MEDIA; 20 import static android.media.AudioAttributes.USAGE_NOTIFICATION; 21 import static android.media.audio.common.AudioDeviceDescription.CONNECTION_BUS; 22 import static android.media.audio.common.AudioDeviceType.OUT_DEVICE; 23 import static android.media.audio.common.AudioGainMode.JOINT; 24 import static android.os.IBinder.DeathRecipient; 25 26 import static com.android.car.audio.CarHalAudioUtils.usageToMetadata; 27 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION; 28 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_DUCKING; 29 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS; 30 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA; 31 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK; 32 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING; 33 import static com.android.car.audio.hal.AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK; 34 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; 35 36 import static com.google.common.truth.Truth.assertThat; 37 import static com.google.common.truth.Truth.assertWithMessage; 38 39 import static org.junit.Assert.assertThrows; 40 import static org.mockito.ArgumentMatchers.any; 41 import static org.mockito.ArgumentMatchers.anyInt; 42 import static org.mockito.ArgumentMatchers.anyString; 43 import static org.mockito.ArgumentMatchers.eq; 44 import static org.mockito.Mockito.after; 45 import static org.mockito.Mockito.doThrow; 46 import static org.mockito.Mockito.mock; 47 import static org.mockito.Mockito.never; 48 import static org.mockito.Mockito.timeout; 49 import static org.mockito.Mockito.times; 50 import static org.mockito.Mockito.verify; 51 import static org.mockito.Mockito.when; 52 53 import android.audio.policy.configuration.V7_0.AudioUsage; 54 import android.car.feature.Flags; 55 import android.car.test.mocks.AbstractExtendedMockitoTestCase; 56 import android.hardware.audio.common.PlaybackTrackMetadata; 57 import android.hardware.automotive.audiocontrol.AudioDeviceConfiguration; 58 import android.hardware.automotive.audiocontrol.AudioGainConfigInfo; 59 import android.hardware.automotive.audiocontrol.AudioZone; 60 import android.hardware.automotive.audiocontrol.DuckingInfo; 61 import android.hardware.automotive.audiocontrol.IAudioControl; 62 import android.hardware.automotive.audiocontrol.IAudioGainCallback; 63 import android.hardware.automotive.audiocontrol.IFocusListener; 64 import android.hardware.automotive.audiocontrol.IModuleChangeCallback; 65 import android.hardware.automotive.audiocontrol.MutingInfo; 66 import android.hardware.automotive.audiocontrol.Reasons; 67 import android.hardware.automotive.audiocontrol.RoutingDeviceConfiguration; 68 import android.media.AudioAttributes; 69 import android.media.AudioManager; 70 import android.media.audio.common.AudioGain; 71 import android.media.audio.common.AudioPort; 72 import android.media.audio.common.AudioPortDeviceExt; 73 import android.os.IBinder; 74 import android.os.RemoteException; 75 import android.platform.test.annotations.DisableFlags; 76 import android.platform.test.annotations.EnableFlags; 77 import android.platform.test.flag.junit.SetFlagsRule; 78 79 import androidx.test.ext.junit.runners.AndroidJUnit4; 80 81 import com.android.car.audio.CarAudioContext; 82 import com.android.car.audio.CarAudioGainConfigInfo; 83 import com.android.car.audio.CarAudioTestUtils; 84 import com.android.car.audio.CarAudioZone; 85 import com.android.car.audio.CarDuckingInfo; 86 import com.android.car.audio.CarHalAudioUtils; 87 import com.android.car.audio.hal.AudioControlWrapper.AudioControlDeathRecipient; 88 import com.android.dx.mockito.inline.extended.ExtendedMockito; 89 90 import com.google.common.collect.ImmutableList; 91 92 import org.junit.Before; 93 import org.junit.Rule; 94 import org.junit.Test; 95 import org.junit.runner.RunWith; 96 import org.mockito.ArgumentCaptor; 97 import org.mockito.Mock; 98 99 import java.util.ArrayList; 100 import java.util.Arrays; 101 import java.util.List; 102 import java.util.stream.Collectors; 103 104 @RunWith(AndroidJUnit4.class) 105 public final class AudioControlWrapperAidlTest extends AbstractExtendedMockitoTestCase { 106 private static final long TEST_CALLBACK_TIMEOUT_MS = 100; 107 private static final float FADE_VALUE = 5; 108 private static final float BALANCE_VALUE = 6; 109 private static final int USAGE = USAGE_MEDIA; 110 private static final String USAGE_NAME = AudioUsage.AUDIO_USAGE_MEDIA.toString(); 111 private static final int ZONE_ID = 2; 112 private static final int PRIMARY_ZONE_ID = 0; 113 private static final int SECONDARY_ZONE_ID = 1; 114 private static final int FOCUS_GAIN = AudioManager.AUDIOFOCUS_GAIN; 115 private static final String PRIMARY_MUSIC_ADDRESS = "primary music"; 116 private static final String PRIMARY_NAVIGATION_ADDRESS = "primary navigation"; 117 private static final String PRIMARY_CALL_ADDRESS = "primary call"; 118 private static final String PRIMARY_NOTIFICATION_ADDRESS = "primary notification"; 119 private static final String SECONDARY_MUSIC_ADDRESS = "secondary music"; 120 private static final String SECONDARY_NAVIGATION_ADDRESS = "secondary navigation"; 121 private static final String SECONDARY_CALL_ADDRESS = "secondary call"; 122 private static final String SECONDARY_NOTIFICATION_ADDRESS = "secondary notification"; 123 private static final PlaybackTrackMetadata METADATA = usageToMetadata(USAGE); 124 125 private static final int PORT_ID_MEDIA = 0; 126 private static final String PORT_NAME_MEDIA = "Media Port"; 127 private static final String ADDRESS_BUS_MEDIA = "BUS100_MEDIA"; 128 private static final int PORT_ID_NAV = 1; 129 private static final String PORT_NAME_NAV = "Nav Port"; 130 private static final String ADDRESS_BUS_NAV = "BUS101_NAV"; 131 private static final AudioGain[] GAINS = new AudioGain[] { 132 new AudioGain() {{ 133 mode = JOINT; 134 minValue = 0; 135 maxValue = 100; 136 defaultValue = 50; 137 stepValue = 2; 138 }} 139 }; 140 141 private static final int AIDL_AUDIO_CONTROL_VERSION_1 = 1; 142 private static final int AIDL_AUDIO_CONTROL_VERSION_2 = 2; 143 private static final int AIDL_AUDIO_CONTROL_VERSION_3 = 3; 144 private static final int AIDL_AUDIO_CONTROL_VERSION_5 = 5; 145 private static final CarAudioContext TEST_CAR_AUDIO_CONTEXT = 146 new CarAudioContext(CarAudioContext.getAllContextsInfo(), 147 /* useCoreAudioRouting= */ false); 148 149 public static final AudioAttributes TEST_MEDIA_ATTRIBUTE = 150 CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA); 151 public static final AudioAttributes TEST_NOTIFICATION_ATTRIBUTE = 152 CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION); 153 154 public static final int TEST_MEDIA_CONTEXT_ID = 155 TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(TEST_MEDIA_ATTRIBUTE); 156 public static final int TEST_NOTIFICATION_CONTEXT_ID = 157 TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(TEST_NOTIFICATION_ATTRIBUTE); 158 159 @Mock 160 private IBinder mBinder; 161 162 @Mock 163 private IAudioControl mAudioControl; 164 165 @Mock 166 private HalAudioGainCallback mHalAudioGainCallback; 167 168 @Mock 169 private AudioControlDeathRecipient mDeathRecipient; 170 @Mock 171 HalAudioModuleChangeCallback mHalAudioModuleChangeCallback; 172 173 private AudioControlWrapperAidl mAudioControlWrapperAidl; 174 private MutingInfo mPrimaryZoneMutingInfo; 175 private MutingInfo mSecondaryZoneMutingInfo; 176 177 @Rule 178 public final SetFlagsRule mSetFlagsRule = new SetFlagsRule(); 179 AudioControlWrapperAidlTest()180 public AudioControlWrapperAidlTest() { 181 super(AudioControlWrapperAidl.TAG); 182 } 183 184 @Override onSessionBuilder(CustomMockitoSessionBuilder session)185 protected void onSessionBuilder(CustomMockitoSessionBuilder session) { 186 session.spyStatic(AudioControlWrapperAidl.class); 187 } 188 189 @Before setUp()190 public void setUp() throws RemoteException { 191 mAudioControlWrapperAidl = createAudioControlWrapperAidl(); 192 mPrimaryZoneMutingInfo = new MutingInfoBuilder(PRIMARY_ZONE_ID) 193 .setMutedAddresses(PRIMARY_MUSIC_ADDRESS, PRIMARY_NAVIGATION_ADDRESS) 194 .setUnMutedAddresses(PRIMARY_CALL_ADDRESS, PRIMARY_NOTIFICATION_ADDRESS) 195 .build(); 196 197 mSecondaryZoneMutingInfo = new MutingInfoBuilder(SECONDARY_ZONE_ID) 198 .setMutedAddresses(SECONDARY_MUSIC_ADDRESS, SECONDARY_NAVIGATION_ADDRESS) 199 .setUnMutedAddresses(SECONDARY_CALL_ADDRESS, SECONDARY_NOTIFICATION_ADDRESS) 200 .build(); 201 } 202 createAudioControlWrapperAidl()203 private AudioControlWrapperAidl createAudioControlWrapperAidl() throws RemoteException { 204 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_3); 205 when(mBinder.queryLocalInterface(anyString())).thenReturn(mAudioControl); 206 doReturn(mBinder).when(AudioControlWrapperAidl::getService); 207 return new AudioControlWrapperAidl(mBinder); 208 } 209 210 @Test setFadeTowardFront_succeeds()211 public void setFadeTowardFront_succeeds() throws Exception { 212 mAudioControlWrapperAidl.setFadeTowardFront(FADE_VALUE); 213 214 verify(mAudioControl).setFadeTowardFront(FADE_VALUE); 215 } 216 217 @Test setBalanceTowardRight_succeeds()218 public void setBalanceTowardRight_succeeds() throws Exception { 219 mAudioControlWrapperAidl.setBalanceTowardRight(BALANCE_VALUE); 220 221 verify(mAudioControl).setBalanceTowardRight(BALANCE_VALUE); 222 } 223 224 @Test supportsFeature_forAudioFocus_returnsTrue()225 public void supportsFeature_forAudioFocus_returnsTrue() { 226 assertThat(mAudioControlWrapperAidl.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_FOCUS)) 227 .isTrue(); 228 } 229 230 @Test supportsFeature_forAudioDucking_returnsTrue()231 public void supportsFeature_forAudioDucking_returnsTrue() { 232 assertThat(mAudioControlWrapperAidl.supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_DUCKING)) 233 .isTrue(); 234 } 235 236 @Test supportsFeature_forAudioMuting_returnsTrue()237 public void supportsFeature_forAudioMuting_returnsTrue() { 238 assertThat(mAudioControlWrapperAidl 239 .supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING)).isTrue(); 240 } 241 242 @Test supportsFeature_forUnknownFeature_returnsFalse()243 public void supportsFeature_forUnknownFeature_returnsFalse() { 244 assertThat(mAudioControlWrapperAidl.supportsFeature(-1)).isFalse(); 245 } 246 247 @Test supportsFeature_forGainCallbackEithRemoteException_returnsTrue()248 public void supportsFeature_forGainCallbackEithRemoteException_returnsTrue() throws Exception { 249 doThrow(new RemoteException()).when(mAudioControl).getInterfaceVersion(); 250 251 assertWithMessage("Gain callback support with failure for getting version") 252 .that(mAudioControlWrapperAidl.supportsFeature( 253 AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)).isFalse(); 254 } 255 256 @Test supportsFeature_forModuleCallbackEithRemoteException_returnsTrue()257 public void supportsFeature_forModuleCallbackEithRemoteException_returnsTrue() 258 throws Exception { 259 doThrow(new RemoteException()).when(mAudioControl).getInterfaceVersion(); 260 261 assertWithMessage("Module callback support with failure for getting version") 262 .that(mAudioControlWrapperAidl.supportsFeature( 263 AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)).isFalse(); 264 } 265 266 @Test 267 @DisableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) supportsFeature_forAudioConfiguration_withFlagDisabled()268 public void supportsFeature_forAudioConfiguration_withFlagDisabled() throws Exception { 269 assertWithMessage("Audio device configuration with flag disabled") 270 .that(mAudioControlWrapperAidl.supportsFeature( 271 AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION)).isFalse(); 272 } 273 274 @Test 275 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) supportsFeature_forAudioConfiguration_withUnsupportedVersion()276 public void supportsFeature_forAudioConfiguration_withUnsupportedVersion() throws Exception { 277 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_3); 278 279 assertWithMessage("Audio device configuration with unsupported version") 280 .that(mAudioControlWrapperAidl.supportsFeature( 281 AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION)).isFalse(); 282 } 283 284 @Test 285 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) supportsFeature_forAudioConfiguration_withRemoteException()286 public void supportsFeature_forAudioConfiguration_withRemoteException() throws Exception { 287 when(mAudioControl.getInterfaceVersion()) 288 .thenThrow(new RemoteException("Remote exception")); 289 290 assertWithMessage("Audio device configuration with remote exception") 291 .that(mAudioControlWrapperAidl.supportsFeature( 292 AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION)).isFalse(); 293 } 294 295 @Test 296 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) supportsFeature_forAudioConfiguration_withSupportedVersion()297 public void supportsFeature_forAudioConfiguration_withSupportedVersion() throws Exception { 298 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_5); 299 300 assertWithMessage("Audio device configuration with supported version") 301 .that(mAudioControlWrapperAidl.supportsFeature( 302 AUDIOCONTROL_FEATURE_AUDIO_CONFIGURATION)).isTrue(); 303 } 304 305 @Test registerFocusListener_succeeds()306 public void registerFocusListener_succeeds() throws Exception { 307 HalFocusListener mockListener = mock(HalFocusListener.class); 308 mAudioControlWrapperAidl.registerFocusListener(mockListener); 309 310 verify(mAudioControl).registerFocusListener(any(IFocusListener.class)); 311 } 312 313 @Test registerFocusListener_throws()314 public void registerFocusListener_throws() throws Exception { 315 doThrow(new RemoteException()).when(mAudioControl) 316 .registerFocusListener(any(IFocusListener.class)); 317 HalFocusListener mockListener = mock(HalFocusListener.class); 318 319 IllegalStateException thrown = assertThrows(IllegalStateException.class, 320 () -> mAudioControlWrapperAidl.registerFocusListener(mockListener)); 321 322 assertWithMessage("Exception thrown when registerFocusListener failed") 323 .that(thrown).hasMessageThat() 324 .contains("IAudioControl#registerFocusListener failed"); 325 } 326 327 @Test requestAudioFocus_forFocusListenerWrapper_succeeds()328 public void requestAudioFocus_forFocusListenerWrapper_succeeds() throws Exception { 329 HalFocusListener mockListener = mock(HalFocusListener.class); 330 ArgumentCaptor<IFocusListener.Stub> captor = 331 ArgumentCaptor.forClass(IFocusListener.Stub.class); 332 mAudioControlWrapperAidl.registerFocusListener(mockListener); 333 verify(mAudioControl).registerFocusListener(captor.capture()); 334 335 captor.getValue().requestAudioFocus(USAGE_NAME, ZONE_ID, FOCUS_GAIN); 336 337 verify(mockListener).requestAudioFocus(METADATA, ZONE_ID, FOCUS_GAIN); 338 } 339 340 @Test requestAudioFocusWithMetaData_forFocusListenerWrapper_succeeds()341 public void requestAudioFocusWithMetaData_forFocusListenerWrapper_succeeds() throws Exception { 342 HalFocusListener mockListener = mock(HalFocusListener.class); 343 ArgumentCaptor<IFocusListener.Stub> captor = 344 ArgumentCaptor.forClass(IFocusListener.Stub.class); 345 mAudioControlWrapperAidl.registerFocusListener(mockListener); 346 verify(mAudioControl).registerFocusListener(captor.capture()); 347 348 captor.getValue().requestAudioFocusWithMetaData(METADATA, ZONE_ID, FOCUS_GAIN); 349 350 verify(mockListener).requestAudioFocus(METADATA, ZONE_ID, FOCUS_GAIN); 351 } 352 353 @Test abandonAudioFocus_forFocusListenerWrapper_succeeds()354 public void abandonAudioFocus_forFocusListenerWrapper_succeeds() throws Exception { 355 HalFocusListener mockListener = mock(HalFocusListener.class); 356 ArgumentCaptor<IFocusListener.Stub> captor = 357 ArgumentCaptor.forClass(IFocusListener.Stub.class); 358 mAudioControlWrapperAidl.registerFocusListener(mockListener); 359 verify(mAudioControl).registerFocusListener(captor.capture()); 360 361 captor.getValue().abandonAudioFocus(USAGE_NAME, ZONE_ID); 362 363 verify(mockListener).abandonAudioFocus(METADATA, ZONE_ID); 364 } 365 366 @Test abandonAudioFocusWithMetaData_forFocusListenerWrapper_succeeds()367 public void abandonAudioFocusWithMetaData_forFocusListenerWrapper_succeeds() throws Exception { 368 HalFocusListener mockListener = mock(HalFocusListener.class); 369 ArgumentCaptor<IFocusListener.Stub> captor = 370 ArgumentCaptor.forClass(IFocusListener.Stub.class); 371 mAudioControlWrapperAidl.registerFocusListener(mockListener); 372 verify(mAudioControl).registerFocusListener(captor.capture()); 373 374 captor.getValue().abandonAudioFocusWithMetaData(METADATA, ZONE_ID); 375 376 verify(mockListener).abandonAudioFocus(METADATA, ZONE_ID); 377 } 378 379 @Test onAudioFocusChange_succeeds()380 public void onAudioFocusChange_succeeds() throws Exception { 381 mAudioControlWrapperAidl.onAudioFocusChange(METADATA, ZONE_ID, FOCUS_GAIN); 382 383 verify(mAudioControl).onAudioFocusChangeWithMetaData(METADATA, ZONE_ID, FOCUS_GAIN); 384 } 385 386 @Test onAudioFocusChange_throws()387 public void onAudioFocusChange_throws() throws Exception { 388 doThrow(new RemoteException()).when(mAudioControl) 389 .onAudioFocusChange(anyString(), anyInt(), anyInt()); 390 doThrow(new RemoteException()).when(mAudioControl) 391 .onAudioFocusChangeWithMetaData(any(), anyInt(), anyInt()); 392 393 IllegalStateException thrown = assertThrows(IllegalStateException.class, 394 () -> mAudioControlWrapperAidl.onAudioFocusChange(METADATA, ZONE_ID, FOCUS_GAIN)); 395 396 assertWithMessage("Exception thrown when onAudioFocusChange failed") 397 .that(thrown).hasMessageThat() 398 .contains("Failed to query IAudioControl#onAudioFocusChange"); 399 } 400 401 @Test onAudioFocusChange_whenMetaDataApiUnimplemented_fallbacksOnUsageApi()402 public void onAudioFocusChange_whenMetaDataApiUnimplemented_fallbacksOnUsageApi() 403 throws Exception { 404 doThrow(new RemoteException()).when(mAudioControl).onAudioFocusChangeWithMetaData(any(), 405 anyInt(), anyInt()); 406 407 mAudioControlWrapperAidl.onAudioFocusChange(METADATA, ZONE_ID, FOCUS_GAIN); 408 409 verify(mAudioControl).onAudioFocusChange(USAGE_NAME, ZONE_ID, FOCUS_GAIN); 410 } 411 412 @Test onDevicesToDuckChange_withNullDuckingInfo_throws()413 public void onDevicesToDuckChange_withNullDuckingInfo_throws() { 414 assertThrows(NullPointerException.class, 415 () -> mAudioControlWrapperAidl.onDevicesToDuckChange(null)); 416 } 417 418 @Test onDevicesToDuckChange_callsHalWithDuckingInfo()419 public void onDevicesToDuckChange_callsHalWithDuckingInfo() throws Exception { 420 CarDuckingInfo carDuckingInfo = 421 new CarDuckingInfo( 422 ZONE_ID, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); 423 424 mAudioControlWrapperAidl.onDevicesToDuckChange(List.of(carDuckingInfo)); 425 426 ArgumentCaptor<DuckingInfo[]> captor = ArgumentCaptor.forClass(DuckingInfo[].class); 427 verify(mAudioControl).onDevicesToDuckChange(captor.capture()); 428 DuckingInfo[] duckingInfos = captor.getValue(); 429 assertThat(duckingInfos).hasLength(1); 430 } 431 432 @Test onDevicesToDuckChange_convertsUsagesToXsdStrings()433 public void onDevicesToDuckChange_convertsUsagesToXsdStrings() throws Exception { 434 List<AudioAttributes> audioAttributes = List.of( 435 TEST_MEDIA_ATTRIBUTE, TEST_NOTIFICATION_ATTRIBUTE); 436 CarDuckingInfo carDuckingInfo = 437 new CarDuckingInfo( 438 ZONE_ID, 439 new ArrayList<>(), 440 new ArrayList<>(), 441 CarHalAudioUtils.audioAttributesToMetadatas(audioAttributes, 442 generateAudioZoneMock())); 443 444 mAudioControlWrapperAidl.onDevicesToDuckChange(List.of(carDuckingInfo)); 445 446 ArgumentCaptor<DuckingInfo[]> captor = ArgumentCaptor.forClass(DuckingInfo[].class); 447 verify(mAudioControl).onDevicesToDuckChange(captor.capture()); 448 DuckingInfo duckingInfo = captor.getValue()[0]; 449 assertThat(duckingInfo.usagesHoldingFocus).asList() 450 .containsExactly(AudioUsage.AUDIO_USAGE_MEDIA.toString(), 451 AudioUsage.AUDIO_USAGE_NOTIFICATION.toString()); 452 } 453 454 @Test onDevicesToDuckChange_passesAlongAddressesToDuck()455 public void onDevicesToDuckChange_passesAlongAddressesToDuck() throws Exception { 456 String mediaAddress = "media_bus"; 457 String navigationAddress = "navigation_bus"; 458 CarDuckingInfo carDuckingInfo = 459 new CarDuckingInfo( 460 ZONE_ID, 461 Arrays.asList(mediaAddress, navigationAddress), 462 new ArrayList<>(), 463 new ArrayList<>()); 464 465 mAudioControlWrapperAidl.onDevicesToDuckChange(List.of(carDuckingInfo)); 466 467 ArgumentCaptor<DuckingInfo[]> captor = ArgumentCaptor.forClass(DuckingInfo[].class); 468 verify(mAudioControl).onDevicesToDuckChange(captor.capture()); 469 DuckingInfo duckingInfo = captor.getValue()[0]; 470 assertThat(duckingInfo.deviceAddressesToDuck).asList() 471 .containsExactly(mediaAddress, navigationAddress); 472 } 473 474 @Test onDevicesToDuckChange_passesAlongAddressesToUnduck()475 public void onDevicesToDuckChange_passesAlongAddressesToUnduck() throws Exception { 476 String notificationAddress = "notification_bus"; 477 String callAddress = "call_address"; 478 CarDuckingInfo carDuckingInfo = 479 new CarDuckingInfo( 480 ZONE_ID, 481 new ArrayList<>(), 482 Arrays.asList(notificationAddress, callAddress), 483 new ArrayList<>()); 484 485 mAudioControlWrapperAidl.onDevicesToDuckChange(List.of(carDuckingInfo)); 486 487 ArgumentCaptor<DuckingInfo[]> captor = ArgumentCaptor.forClass(DuckingInfo[].class); 488 verify(mAudioControl).onDevicesToDuckChange(captor.capture()); 489 DuckingInfo duckingInfo = captor.getValue()[0]; 490 assertThat(duckingInfo.deviceAddressesToUnduck).asList() 491 .containsExactly(notificationAddress, callAddress); 492 } 493 494 @Test onDevicesToDuckChange_passesAlongZoneId()495 public void onDevicesToDuckChange_passesAlongZoneId() throws Exception { 496 CarDuckingInfo carDuckingInfo = 497 new CarDuckingInfo( 498 ZONE_ID, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); 499 500 mAudioControlWrapperAidl.onDevicesToDuckChange(List.of(carDuckingInfo)); 501 502 ArgumentCaptor<DuckingInfo[]> captor = ArgumentCaptor.forClass(DuckingInfo[].class); 503 verify(mAudioControl).onDevicesToDuckChange(captor.capture()); 504 DuckingInfo duckingInfo = captor.getValue()[0]; 505 assertThat(duckingInfo.zoneId).isEqualTo(ZONE_ID); 506 } 507 508 @Test onDevicesToDuckChange_multipleZones_passesADuckingInfoPerZone()509 public void onDevicesToDuckChange_multipleZones_passesADuckingInfoPerZone() throws Exception { 510 CarDuckingInfo carDuckingInfo = 511 new CarDuckingInfo( 512 ZONE_ID, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); 513 CarDuckingInfo secondaryCarDuckingInfo = 514 new CarDuckingInfo( 515 SECONDARY_ZONE_ID, new ArrayList<>(), new ArrayList<>(), new ArrayList<>()); 516 517 mAudioControlWrapperAidl.onDevicesToDuckChange(List.of(carDuckingInfo, 518 secondaryCarDuckingInfo)); 519 520 ArgumentCaptor<DuckingInfo[]> captor = ArgumentCaptor.forClass(DuckingInfo[].class); 521 verify(mAudioControl).onDevicesToDuckChange(captor.capture()); 522 assertWithMessage("Number of ducking infos passed along") 523 .that(captor.getValue().length).isEqualTo(2); 524 } 525 526 @Test linkToDeath_callsBinder()527 public void linkToDeath_callsBinder() throws Exception { 528 mAudioControlWrapperAidl.linkToDeath(null); 529 530 verify(mBinder).linkToDeath(any(DeathRecipient.class), eq(0)); 531 } 532 533 @Test linkToDeath_throws()534 public void linkToDeath_throws() throws Exception { 535 doThrow(new RemoteException()).when(mBinder) 536 .linkToDeath(any(DeathRecipient.class), anyInt()); 537 538 IllegalStateException thrown = assertThrows(IllegalStateException.class, 539 () -> mAudioControlWrapperAidl.linkToDeath(null)); 540 541 assertWithMessage("Exception thrown when linkToDeath failed") 542 .that(thrown).hasMessageThat() 543 .contains("Call to IAudioControl#linkToDeath failed"); 544 } 545 546 @Test unlinkToDeath_callsBinder()547 public void unlinkToDeath_callsBinder() { 548 mAudioControlWrapperAidl.linkToDeath(null); 549 550 mAudioControlWrapperAidl.unlinkToDeath(); 551 552 verify(mBinder).unlinkToDeath(any(DeathRecipient.class), eq(0)); 553 } 554 555 @Test binderDied_fetchesNewBinder()556 public void binderDied_fetchesNewBinder() throws Exception { 557 mAudioControlWrapperAidl.linkToDeath(null); 558 559 ArgumentCaptor<DeathRecipient> captor = ArgumentCaptor.forClass(DeathRecipient.class); 560 verify(mBinder).linkToDeath(captor.capture(), eq(0)); 561 IBinder.DeathRecipient deathRecipient = captor.getValue(); 562 563 deathRecipient.binderDied(); 564 565 ExtendedMockito.verify(() -> AudioControlWrapperAidl.getService()); 566 } 567 568 @Test binderDied_relinksToDeath()569 public void binderDied_relinksToDeath() throws Exception { 570 mAudioControlWrapperAidl.linkToDeath(null); 571 572 ArgumentCaptor<DeathRecipient> captor = ArgumentCaptor.forClass(DeathRecipient.class); 573 verify(mBinder).linkToDeath(captor.capture(), eq(0)); 574 IBinder.DeathRecipient deathRecipient = captor.getValue(); 575 576 deathRecipient.binderDied(); 577 578 verify(mBinder, times(2)).linkToDeath(any(DeathRecipient.class), eq(0)); 579 } 580 581 @Test binderDied_callsDeathRecipient()582 public void binderDied_callsDeathRecipient() throws Exception { 583 mAudioControlWrapperAidl.linkToDeath(mDeathRecipient); 584 585 ArgumentCaptor<DeathRecipient> captor = ArgumentCaptor.forClass( 586 IBinder.DeathRecipient.class); 587 verify(mBinder).linkToDeath(captor.capture(), eq(0)); 588 IBinder.DeathRecipient deathRecipient = captor.getValue(); 589 590 deathRecipient.binderDied(); 591 592 verify(mDeathRecipient).serviceDied(); 593 } 594 595 @Test onDevicesToMuteChange_withNullMutingInformation_Throws()596 public void onDevicesToMuteChange_withNullMutingInformation_Throws() { 597 NullPointerException thrown = assertThrows(NullPointerException.class, 598 () -> mAudioControlWrapperAidl.onDevicesToMuteChange(null)); 599 600 assertWithMessage("NullPointerException thrown by onDevicesToMuteChange") 601 .that(thrown).hasMessageThat().contains("not be null"); 602 } 603 604 @Test onDevicesToMuteChange_withEmptyMutingInformation_Throws()605 public void onDevicesToMuteChange_withEmptyMutingInformation_Throws() { 606 IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, 607 () -> mAudioControlWrapperAidl.onDevicesToMuteChange(new ArrayList<>())); 608 609 assertWithMessage("IllegalArgumentException thrown by onDevicesToMuteChange") 610 .that(thrown).hasMessageThat().contains("not be empty"); 611 } 612 613 @Test onDevicesToMuteChange_passesAlongZoneId()614 public void onDevicesToMuteChange_passesAlongZoneId() throws Exception { 615 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo)); 616 617 ArgumentCaptor<MutingInfo[]> captor = ArgumentCaptor.forClass(MutingInfo[].class); 618 verify(mAudioControl).onDevicesToMuteChange(captor.capture()); 619 MutingInfo mutingInfo = captor.getValue()[0]; 620 assertWithMessage("Audio Zone Id") 621 .that(mutingInfo.zoneId).isEqualTo(PRIMARY_ZONE_ID); 622 } 623 624 @Test onDevicesToMuteChange_passesAlongAddressesToMute()625 public void onDevicesToMuteChange_passesAlongAddressesToMute() throws Exception { 626 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo)); 627 628 ArgumentCaptor<MutingInfo[]> captor = ArgumentCaptor.forClass(MutingInfo[].class); 629 verify(mAudioControl).onDevicesToMuteChange(captor.capture()); 630 MutingInfo mutingInfo = captor.getValue()[0]; 631 assertWithMessage("Device Addresses to mute") 632 .that(mutingInfo.deviceAddressesToMute).asList() 633 .containsExactly(PRIMARY_MUSIC_ADDRESS, PRIMARY_NAVIGATION_ADDRESS); 634 } 635 636 @Test onDevicesToMuteChange_passesAlongAddressesToUnMute()637 public void onDevicesToMuteChange_passesAlongAddressesToUnMute() throws Exception { 638 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo)); 639 640 ArgumentCaptor<MutingInfo[]> captor = ArgumentCaptor.forClass(MutingInfo[].class); 641 verify(mAudioControl).onDevicesToMuteChange(captor.capture()); 642 MutingInfo mutingInfo = captor.getValue()[0]; 643 assertWithMessage("Device Address to Unmute") 644 .that(mutingInfo.deviceAddressesToUnmute).asList() 645 .containsExactly(PRIMARY_CALL_ADDRESS, PRIMARY_NOTIFICATION_ADDRESS); 646 } 647 648 @Test onDevicesToMuteChange_withMultipleZones_passesAlongCorrectSizeInfo()649 public void onDevicesToMuteChange_withMultipleZones_passesAlongCorrectSizeInfo() 650 throws Exception { 651 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo, 652 mSecondaryZoneMutingInfo)); 653 654 ArgumentCaptor<MutingInfo[]> captor = ArgumentCaptor.forClass(MutingInfo[].class); 655 verify(mAudioControl).onDevicesToMuteChange(captor.capture()); 656 assertWithMessage("Muting Info size") 657 .that(captor.getValue()).asList().hasSize(2); 658 } 659 660 @Test onDevicesToMuteChange_withMultipleZones_passesAlongCorrectZoneInfo()661 public void onDevicesToMuteChange_withMultipleZones_passesAlongCorrectZoneInfo() 662 throws Exception { 663 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo, 664 mSecondaryZoneMutingInfo)); 665 666 ArgumentCaptor<MutingInfo[]> captor = ArgumentCaptor.forClass(MutingInfo[].class); 667 verify(mAudioControl).onDevicesToMuteChange(captor.capture()); 668 List<Integer> zoneIds = Arrays.stream(captor.getValue()) 669 .map(info -> info.zoneId).collect(Collectors.toList()); 670 assertWithMessage("Muting Zone Ids").that(zoneIds) 671 .containsExactly(PRIMARY_ZONE_ID, SECONDARY_ZONE_ID); 672 } 673 674 @Test 675 public void onDevicesToMuteChange_withMultipleZones_passesAlongCorrectAddressToMuteForPrimaryZone()676 onDevicesToMuteChange_withMultipleZones_passesAlongCorrectAddressToMuteForPrimaryZone() 677 throws Exception { 678 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo, 679 mSecondaryZoneMutingInfo)); 680 681 MutingInfo info = verifyOnDevicesToMuteChangeCalled(PRIMARY_ZONE_ID); 682 assertWithMessage("Primary Zone Device Addresses to mute") 683 .that(info.deviceAddressesToMute).asList() 684 .containsExactly(PRIMARY_MUSIC_ADDRESS, PRIMARY_NAVIGATION_ADDRESS); 685 } 686 687 @Test 688 public void onDevicesToMuteChange_withMultiZones_passesAlongCorrectAddressToMuteForSecondaryZone()689 onDevicesToMuteChange_withMultiZones_passesAlongCorrectAddressToMuteForSecondaryZone() 690 throws Exception { 691 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo, 692 mSecondaryZoneMutingInfo)); 693 694 MutingInfo info = verifyOnDevicesToMuteChangeCalled(SECONDARY_ZONE_ID); 695 assertWithMessage("Secondary Zone Device Addresses to mute") 696 .that(info.deviceAddressesToMute).asList() 697 .containsExactly(SECONDARY_MUSIC_ADDRESS, SECONDARY_NAVIGATION_ADDRESS); 698 } 699 700 @Test 701 public void onDevicesToMuteChange_witMultipleZones_passesAlongCorrectAddressToUnMuteForPrimaryZone()702 onDevicesToMuteChange_witMultipleZones_passesAlongCorrectAddressToUnMuteForPrimaryZone() 703 throws Exception { 704 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo, 705 mSecondaryZoneMutingInfo)); 706 707 MutingInfo info = verifyOnDevicesToMuteChangeCalled(PRIMARY_ZONE_ID); 708 assertWithMessage("Primary Zone Device Addresses to un-mute") 709 .that(info.deviceAddressesToUnmute).asList() 710 .containsExactly(PRIMARY_CALL_ADDRESS, PRIMARY_NOTIFICATION_ADDRESS); 711 } 712 713 @Test 714 public void onDevicesToMuteChange_witMultiZones_passesAlongCorrectAddressToUnMuteForSecondaryZone()715 onDevicesToMuteChange_witMultiZones_passesAlongCorrectAddressToUnMuteForSecondaryZone() 716 throws Exception { 717 mAudioControlWrapperAidl.onDevicesToMuteChange(ImmutableList.of(mPrimaryZoneMutingInfo, 718 mSecondaryZoneMutingInfo)); 719 720 MutingInfo info = verifyOnDevicesToMuteChangeCalled(SECONDARY_ZONE_ID); 721 722 assertWithMessage("Secondary Zone Device Addresses to un-mute") 723 .that(info.deviceAddressesToUnmute).asList() 724 .containsExactly(SECONDARY_CALL_ADDRESS, SECONDARY_NOTIFICATION_ADDRESS); 725 } 726 727 @Test supportsFeature_forAudioGainCallback_returnsTrue()728 public void supportsFeature_forAudioGainCallback_returnsTrue() throws Exception { 729 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_1 + 1); 730 assertThat( 731 mAudioControlWrapperAidl.supportsFeature( 732 AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)) 733 .isTrue(); 734 735 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_1 + 4); 736 assertThat( 737 mAudioControlWrapperAidl.supportsFeature( 738 AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)) 739 .isTrue(); 740 } 741 742 @Test supportsFeature_forAudioGainCallback_returnsFalse()743 public void supportsFeature_forAudioGainCallback_returnsFalse() throws Exception { 744 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_1); 745 assertThat( 746 mAudioControlWrapperAidl.supportsFeature( 747 AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)) 748 .isFalse(); 749 750 when(mAudioControl.getInterfaceVersion()).thenReturn(0); 751 assertThat( 752 mAudioControlWrapperAidl.supportsFeature( 753 AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)) 754 .isFalse(); 755 } 756 757 @Test supportsFeature_forAudioFocusWithMetadataThrowsException_returnFalse()758 public void supportsFeature_forAudioFocusWithMetadataThrowsException_returnFalse() 759 throws Exception { 760 doThrow(new RemoteException()).when(mAudioControl).getInterfaceVersion(); 761 762 assertThat(mAudioControlWrapperAidl 763 .supportsFeature(AUDIOCONTROL_FEATURE_AUDIO_FOCUS_WITH_METADATA)).isFalse(); 764 } 765 766 @Test registerAudioGainCallback_succeeds()767 public void registerAudioGainCallback_succeeds() throws Exception { 768 mAudioControlWrapperAidl.registerAudioGainCallback(mHalAudioGainCallback); 769 verify(mAudioControl).registerGainCallback(any()); 770 } 771 772 @Test registerAudioGainCallback_throws()773 public void registerAudioGainCallback_throws() throws Exception { 774 doThrow(new RemoteException("D'OH!")).when(mAudioControl).registerGainCallback(any()); 775 776 IllegalStateException thrown = 777 assertThrows( 778 IllegalStateException.class, 779 () -> 780 mAudioControlWrapperAidl.registerAudioGainCallback( 781 mHalAudioGainCallback)); 782 783 assertWithMessage("IllegalStateException thrown by registerAudioGainCallback") 784 .that(thrown) 785 .hasMessageThat() 786 .contains("IAudioControl#registerAudioGainCallback failed"); 787 } 788 789 @Test registerAudioGainCallback_nullcallback_Throws()790 public void registerAudioGainCallback_nullcallback_Throws() { 791 NullPointerException thrown = 792 assertThrows( 793 NullPointerException.class, 794 () -> 795 mAudioControlWrapperAidl.registerAudioGainCallback( 796 /* gainCallback= */ null)); 797 798 assertWithMessage("NullPointerException thrown by registerAudioGainCallback") 799 .that(thrown) 800 .hasMessageThat() 801 .contains("Audio Gain Callback can not be null"); 802 } 803 804 @Test registerAudioGainCallback_withLowerVersion()805 public void registerAudioGainCallback_withLowerVersion() throws Exception { 806 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_1); 807 808 mAudioControlWrapperAidl.registerAudioGainCallback(mHalAudioGainCallback); 809 810 verify(mAudioControl, never()).registerGainCallback(any()); 811 } 812 813 @Test onAudioDeviceGainsChanged_succeeds()814 public void onAudioDeviceGainsChanged_succeeds() throws Exception { 815 ArgumentCaptor<IAudioGainCallback.Stub> captor = 816 ArgumentCaptor.forClass(IAudioGainCallback.Stub.class); 817 818 mAudioControlWrapperAidl.registerAudioGainCallback(mHalAudioGainCallback); 819 verify(mAudioControl).registerGainCallback(captor.capture()); 820 821 int[] halReasons = {Reasons.REMOTE_MUTE, Reasons.NAV_DUCKING}; 822 List<Integer> reasons = Arrays.stream(halReasons).boxed().collect(Collectors.toList()); 823 824 AudioGainConfigInfo gainInfo1 = new AudioGainConfigInfo(); 825 gainInfo1.zoneId = PRIMARY_ZONE_ID; 826 gainInfo1.devicePortAddress = PRIMARY_MUSIC_ADDRESS; 827 gainInfo1.volumeIndex = 666; 828 CarAudioGainConfigInfo carGainInfo1 = new CarAudioGainConfigInfo(gainInfo1); 829 AudioGainConfigInfo gainInfo2 = new AudioGainConfigInfo(); 830 gainInfo2.zoneId = SECONDARY_ZONE_ID; 831 gainInfo2.devicePortAddress = SECONDARY_NAVIGATION_ADDRESS; 832 gainInfo2.volumeIndex = 999; 833 CarAudioGainConfigInfo carGainInfo2 = new CarAudioGainConfigInfo(gainInfo2); 834 835 AudioGainConfigInfo[] gains = new AudioGainConfigInfo[] {gainInfo1, gainInfo2}; 836 837 List<CarAudioGainConfigInfo> carGains = List.of(carGainInfo1, carGainInfo2); 838 839 captor.getValue().onAudioDeviceGainsChanged(halReasons, gains); 840 841 ArgumentCaptor<List<CarAudioGainConfigInfo>> captorGains = 842 ArgumentCaptor.forClass(List.class); 843 verify(mHalAudioGainCallback).onAudioDeviceGainsChanged(eq(reasons), captorGains.capture()); 844 845 assertThat(captorGains.getValue()).containsExactlyElementsIn(carGains); 846 } 847 848 @Test onAudioDeviceGainsChanged_invalidReasons()849 public void onAudioDeviceGainsChanged_invalidReasons() throws Exception { 850 ArgumentCaptor<IAudioGainCallback.Stub> captor = 851 ArgumentCaptor.forClass(IAudioGainCallback.Stub.class); 852 853 mAudioControlWrapperAidl.registerAudioGainCallback(mHalAudioGainCallback); 854 verify(mAudioControl).registerGainCallback(captor.capture()); 855 856 int[] halReasons = {-1, 1999, 666}; 857 List<Integer> emptyReasons = new ArrayList<>(); 858 859 AudioGainConfigInfo gainInfo1 = new AudioGainConfigInfo(); 860 gainInfo1.zoneId = PRIMARY_ZONE_ID; 861 gainInfo1.devicePortAddress = PRIMARY_MUSIC_ADDRESS; 862 gainInfo1.volumeIndex = 666; 863 CarAudioGainConfigInfo carGainInfo1 = new CarAudioGainConfigInfo(gainInfo1); 864 AudioGainConfigInfo gainInfo2 = new AudioGainConfigInfo(); 865 gainInfo2.zoneId = SECONDARY_ZONE_ID; 866 gainInfo2.devicePortAddress = SECONDARY_NAVIGATION_ADDRESS; 867 gainInfo2.volumeIndex = 999; 868 CarAudioGainConfigInfo carGainInfo2 = new CarAudioGainConfigInfo(gainInfo2); 869 870 AudioGainConfigInfo[] gains = new AudioGainConfigInfo[] {gainInfo1, gainInfo2}; 871 872 List<CarAudioGainConfigInfo> carGains = List.of(carGainInfo1, carGainInfo2); 873 874 captor.getValue().onAudioDeviceGainsChanged(halReasons, gains); 875 876 ArgumentCaptor<List<CarAudioGainConfigInfo>> captorGains = 877 ArgumentCaptor.forClass(List.class); 878 verify(mHalAudioGainCallback) 879 .onAudioDeviceGainsChanged(eq(emptyReasons), captorGains.capture()); 880 881 assertThat(captorGains.getValue()).containsExactlyElementsIn(carGains); 882 } 883 884 @Test onAudioDeviceGainsChanged_invalidReasonsAmongValidReasons()885 public void onAudioDeviceGainsChanged_invalidReasonsAmongValidReasons() throws Exception { 886 ArgumentCaptor<IAudioGainCallback.Stub> captor = 887 ArgumentCaptor.forClass(IAudioGainCallback.Stub.class); 888 889 mAudioControlWrapperAidl.registerAudioGainCallback(mHalAudioGainCallback); 890 verify(mAudioControl).registerGainCallback(captor.capture()); 891 892 int[] halReasons = {-1, Reasons.REMOTE_MUTE, 1999, 666, Reasons.NAV_DUCKING}; 893 List<Integer> validReasons = List.of(Reasons.REMOTE_MUTE, Reasons.NAV_DUCKING); 894 895 AudioGainConfigInfo gainInfo1 = new AudioGainConfigInfo(); 896 gainInfo1.zoneId = PRIMARY_ZONE_ID; 897 gainInfo1.devicePortAddress = PRIMARY_MUSIC_ADDRESS; 898 gainInfo1.volumeIndex = 666; 899 CarAudioGainConfigInfo carGainInfo1 = new CarAudioGainConfigInfo(gainInfo1); 900 AudioGainConfigInfo gainInfo2 = new AudioGainConfigInfo(); 901 gainInfo2.zoneId = SECONDARY_ZONE_ID; 902 gainInfo2.devicePortAddress = SECONDARY_NAVIGATION_ADDRESS; 903 gainInfo2.volumeIndex = 999; 904 CarAudioGainConfigInfo carGainInfo2 = new CarAudioGainConfigInfo(gainInfo2); 905 906 AudioGainConfigInfo[] gains = new AudioGainConfigInfo[] {gainInfo1, gainInfo2}; 907 908 List<CarAudioGainConfigInfo> carGains = List.of(carGainInfo1, carGainInfo2); 909 910 captor.getValue().onAudioDeviceGainsChanged(halReasons, gains); 911 912 ArgumentCaptor<List<CarAudioGainConfigInfo>> captorGains = 913 ArgumentCaptor.forClass(List.class); 914 verify(mHalAudioGainCallback) 915 .onAudioDeviceGainsChanged(eq(validReasons), captorGains.capture()); 916 917 assertThat(captorGains.getValue()).containsExactlyElementsIn(carGains); 918 } 919 920 @Test setModuleChangeCallback()921 public void setModuleChangeCallback() throws Exception { 922 mAudioControlWrapperAidl.setModuleChangeCallback(mHalAudioModuleChangeCallback); 923 924 verify(mAudioControl, timeout(TEST_CALLBACK_TIMEOUT_MS)).setModuleChangeCallback(any()); 925 } 926 927 @Test setModuleChangeCallback_withLowerVersion()928 public void setModuleChangeCallback_withLowerVersion() throws Exception { 929 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_2); 930 931 mAudioControlWrapperAidl.setModuleChangeCallback(mHalAudioModuleChangeCallback); 932 933 verify(mAudioControl, after(TEST_CALLBACK_TIMEOUT_MS).never()) 934 .setModuleChangeCallback(any()); 935 } 936 937 @Test setModuleChangeCallback_withIllegalStateException_retries()938 public void setModuleChangeCallback_withIllegalStateException_retries() throws Exception { 939 doThrow(new IllegalStateException()).when(mAudioControl).setModuleChangeCallback(any()); 940 941 mAudioControlWrapperAidl.setModuleChangeCallback(mHalAudioModuleChangeCallback); 942 943 verify(mAudioControl, timeout(TEST_CALLBACK_TIMEOUT_MS)).clearModuleChangeCallback(); 944 } 945 946 @Test onAudioPortsChanged()947 public void onAudioPortsChanged() throws Exception { 948 AudioPortDeviceExt mediaDeviceExt = CarAudioTestUtils.createAudioPortDeviceExt( 949 OUT_DEVICE, CONNECTION_BUS, ADDRESS_BUS_MEDIA); 950 AudioPort mediaAudioPort = CarAudioTestUtils.createAudioPort(PORT_ID_MEDIA, 951 PORT_NAME_MEDIA, GAINS, mediaDeviceExt); 952 AudioPortDeviceExt navDeviceExt = CarAudioTestUtils.createAudioPortDeviceExt( 953 OUT_DEVICE, CONNECTION_BUS, ADDRESS_BUS_NAV); 954 AudioPort navAudioPort = CarAudioTestUtils.createAudioPort(PORT_ID_NAV, 955 PORT_NAME_NAV, GAINS, navDeviceExt); 956 HalAudioDeviceInfo mediaDeviceInfo = new HalAudioDeviceInfo(mediaAudioPort); 957 HalAudioDeviceInfo navDeviceInfo = new HalAudioDeviceInfo(navAudioPort); 958 mAudioControlWrapperAidl.setModuleChangeCallback(mHalAudioModuleChangeCallback); 959 ArgumentCaptor<IModuleChangeCallback> callbackCaptor = 960 ArgumentCaptor.forClass(IModuleChangeCallback.class); 961 verify(mAudioControl, timeout(TEST_CALLBACK_TIMEOUT_MS)).setModuleChangeCallback( 962 callbackCaptor.capture()); 963 IModuleChangeCallback moduleChangeCallback = callbackCaptor.getValue(); 964 965 moduleChangeCallback.onAudioPortsChanged(new AudioPort[]{mediaAudioPort, navAudioPort}); 966 967 ArgumentCaptor<List<HalAudioDeviceInfo>> audioDeviceInfoCaptor = ArgumentCaptor.forClass( 968 List.class); 969 verify(mHalAudioModuleChangeCallback).onAudioPortsChanged(audioDeviceInfoCaptor.capture()); 970 assertWithMessage("Hal audio device info changed").that(audioDeviceInfoCaptor.getValue()) 971 .containsExactly(mediaDeviceInfo, navDeviceInfo); 972 } 973 974 @Test clearModuleChangeCallback()975 public void clearModuleChangeCallback() throws Exception { 976 mAudioControlWrapperAidl.clearModuleChangeCallback(); 977 978 verify(mAudioControl, timeout(TEST_CALLBACK_TIMEOUT_MS)).clearModuleChangeCallback(); 979 } 980 981 @Test clearModuleChangeCallback_withLowerVersion()982 public void clearModuleChangeCallback_withLowerVersion() throws Exception { 983 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_2); 984 985 mAudioControlWrapperAidl.clearModuleChangeCallback(); 986 987 verify(mAudioControl, after(TEST_CALLBACK_TIMEOUT_MS).never()) 988 .clearModuleChangeCallback(); 989 } 990 991 @Test 992 @DisableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getAudioDeviceConfiguration_withFlagDisabled()993 public void getAudioDeviceConfiguration_withFlagDisabled() throws Exception { 994 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_5); 995 996 AudioDeviceConfiguration configuration = 997 mAudioControlWrapperAidl.getAudioDeviceConfiguration(); 998 999 assertWithMessage("Audio device configuration with flag disabled") 1000 .that(configuration.routingConfig) 1001 .isEqualTo(RoutingDeviceConfiguration.DEFAULT_AUDIO_ROUTING); 1002 } 1003 1004 @Test 1005 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getAudioDeviceConfiguration_withUnsupportedVersion()1006 public void getAudioDeviceConfiguration_withUnsupportedVersion() throws Exception { 1007 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_3); 1008 1009 AudioDeviceConfiguration configuration = 1010 mAudioControlWrapperAidl.getAudioDeviceConfiguration(); 1011 1012 assertWithMessage("Audio device configuration with unsupported version") 1013 .that(configuration.routingConfig) 1014 .isEqualTo(RoutingDeviceConfiguration.DEFAULT_AUDIO_ROUTING); 1015 } 1016 1017 @Test 1018 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getAudioDeviceConfiguration()1019 public void getAudioDeviceConfiguration() throws Exception { 1020 AudioDeviceConfiguration testConfiguration = 1021 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1022 1023 AudioDeviceConfiguration configuration = 1024 mAudioControlWrapperAidl.getAudioDeviceConfiguration(); 1025 1026 assertWithMessage("Audio device configuration with valid configuration") 1027 .that(configuration).isEqualTo(testConfiguration); 1028 } 1029 1030 @Test 1031 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getAudioDeviceConfiguration_withUnsupportedException_throws()1032 public void getAudioDeviceConfiguration_withUnsupportedException_throws() throws Exception { 1033 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1034 when(mAudioControl.getAudioDeviceConfiguration()) 1035 .thenThrow(new UnsupportedOperationException("Unsupported exception")); 1036 1037 AudioDeviceConfiguration configuration = 1038 mAudioControlWrapperAidl.getAudioDeviceConfiguration(); 1039 1040 assertWithMessage("Audio device config with unsupported exception") 1041 .that(configuration.routingConfig) 1042 .isEqualTo(RoutingDeviceConfiguration.DEFAULT_AUDIO_ROUTING); 1043 } 1044 1045 @Test 1046 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getAudioDeviceConfiguration_withRemoteException_throws()1047 public void getAudioDeviceConfiguration_withRemoteException_throws() throws Exception { 1048 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1049 when(mAudioControl.getAudioDeviceConfiguration()) 1050 .thenThrow(new RemoteException("Remote exception")); 1051 1052 AudioDeviceConfiguration configuration = 1053 mAudioControlWrapperAidl.getAudioDeviceConfiguration(); 1054 1055 assertWithMessage("Audio device config with remote exception") 1056 .that(configuration.routingConfig) 1057 .isEqualTo(RoutingDeviceConfiguration.DEFAULT_AUDIO_ROUTING); 1058 } 1059 1060 @Test 1061 @DisableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getOutputMirroringDevices_withFlagDisabled()1062 public void getOutputMirroringDevices_withFlagDisabled() throws Exception { 1063 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_5); 1064 1065 assertWithMessage("Audio mirroring devices with flag disabled") 1066 .that(mAudioControlWrapperAidl.getOutputMirroringDevices()).isEmpty(); 1067 } 1068 1069 @Test 1070 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getOutputMirroringDevices_withUnsupportedVersion()1071 public void getOutputMirroringDevices_withUnsupportedVersion() throws Exception { 1072 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_3); 1073 1074 assertWithMessage("Audio mirroring devices with unsupported version") 1075 .that(mAudioControlWrapperAidl.getOutputMirroringDevices()).isEmpty(); 1076 } 1077 1078 @Test 1079 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getOutputMirroringDevices_withRemoteException_throws()1080 public void getOutputMirroringDevices_withRemoteException_throws() throws Exception { 1081 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1082 when(mAudioControl.getOutputMirroringDevices()) 1083 .thenThrow(new RemoteException("Remote exception")); 1084 1085 assertWithMessage("Audio mirroring devices with remote exception") 1086 .that(mAudioControlWrapperAidl.getOutputMirroringDevices()).isEmpty(); 1087 } 1088 1089 @Test 1090 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getOutputMirroringDevices_withUnsupportedException_throws()1091 public void getOutputMirroringDevices_withUnsupportedException_throws() throws Exception { 1092 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1093 when(mAudioControl.getOutputMirroringDevices()) 1094 .thenThrow(new UnsupportedOperationException("Unsupported exception")); 1095 1096 assertWithMessage("Audio mirroring devices with unsupported exception") 1097 .that(mAudioControlWrapperAidl.getOutputMirroringDevices()).isEmpty(); 1098 } 1099 1100 @Test 1101 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getOutputMirroringDevices()1102 public void getOutputMirroringDevices() throws Exception { 1103 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1104 AudioPortDeviceExt mediaDeviceExt = CarAudioTestUtils.createAudioPortDeviceExt( 1105 OUT_DEVICE, CONNECTION_BUS, ADDRESS_BUS_MEDIA); 1106 AudioPort mediaAudioPort = CarAudioTestUtils.createAudioPort(PORT_ID_MEDIA, 1107 PORT_NAME_MEDIA, GAINS, mediaDeviceExt); 1108 AudioPortDeviceExt navDeviceExt = CarAudioTestUtils.createAudioPortDeviceExt( 1109 OUT_DEVICE, CONNECTION_BUS, ADDRESS_BUS_NAV); 1110 AudioPort navAudioPort = CarAudioTestUtils.createAudioPort(PORT_ID_NAV, 1111 PORT_NAME_NAV, GAINS, navDeviceExt); 1112 when(mAudioControl.getOutputMirroringDevices()) 1113 .thenReturn(List.of(mediaAudioPort, navAudioPort)); 1114 1115 var infos = mAudioControlWrapperAidl.getOutputMirroringDevices(); 1116 1117 assertWithMessage("Audio mirroring addresses with valid devices") 1118 .that(infos) 1119 .containsExactly(mediaAudioPort, navAudioPort); 1120 } 1121 1122 @Test 1123 @DisableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getCarAudioZones_withFlagDisabled()1124 public void getCarAudioZones_withFlagDisabled() throws Exception { 1125 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1126 1127 assertWithMessage("Audio zones with flag disabled") 1128 .that(mAudioControlWrapperAidl.getCarAudioZones()).isEmpty(); 1129 } 1130 1131 @Test 1132 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getCarAudioZones_withUnsupportedVersion()1133 public void getCarAudioZones_withUnsupportedVersion() throws Exception { 1134 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_3); 1135 1136 assertWithMessage("Audio zones exception with unsupported version") 1137 .that(mAudioControlWrapperAidl.getCarAudioZones()).isEmpty(); 1138 } 1139 1140 @Test 1141 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getCarAudioZones_withRemoteException()1142 public void getCarAudioZones_withRemoteException() throws Exception { 1143 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1144 when(mAudioControl.getOutputMirroringDevices()) 1145 .thenThrow(new RemoteException("Remote exception")); 1146 1147 assertWithMessage("Audio zones with remote exception") 1148 .that(mAudioControlWrapperAidl.getCarAudioZones()).isEmpty(); 1149 } 1150 1151 @Test 1152 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getCarAudioZones_withUnsupportedException()1153 public void getCarAudioZones_withUnsupportedException() throws Exception { 1154 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1155 when(mAudioControl.getOutputMirroringDevices()) 1156 .thenThrow(new UnsupportedOperationException("Unsupported exception")); 1157 1158 assertWithMessage("Audio zones with unsupported exception") 1159 .that(mAudioControlWrapperAidl.getCarAudioZones()).isEmpty(); 1160 } 1161 1162 @Test 1163 @EnableFlags(Flags.FLAG_AUDIO_CONTROL_HAL_CONFIGURATION) getCarAudioZones()1164 public void getCarAudioZones() throws Exception { 1165 setUpAudioDeviceConfiguration(RoutingDeviceConfiguration.DYNAMIC_AUDIO_ROUTING); 1166 AudioZone primaryZone = new AudioZone(); 1167 primaryZone.id = PRIMARY_ZONE_ID; 1168 primaryZone.name = "Primary zone"; 1169 AudioZone secondaryZone = new AudioZone(); 1170 primaryZone.id = SECONDARY_ZONE_ID; 1171 primaryZone.name = "Secondary zone"; 1172 when(mAudioControl.getCarAudioZones()).thenReturn(List.of(primaryZone, secondaryZone)); 1173 1174 assertWithMessage("Audio zones with valid zones") 1175 .that(mAudioControlWrapperAidl.getCarAudioZones()) 1176 .containsExactly(primaryZone, secondaryZone); 1177 } 1178 setUpAudioDeviceConfiguration( @outingDeviceConfiguration byte routingConfig)1179 private AudioDeviceConfiguration setUpAudioDeviceConfiguration( 1180 @RoutingDeviceConfiguration byte routingConfig) throws Exception { 1181 when(mAudioControl.getInterfaceVersion()).thenReturn(AIDL_AUDIO_CONTROL_VERSION_5); 1182 AudioDeviceConfiguration testConfiguration = new AudioDeviceConfiguration(); 1183 testConfiguration.routingConfig = routingConfig; 1184 when(mAudioControl.getAudioDeviceConfiguration()).thenReturn(testConfiguration); 1185 return testConfiguration; 1186 } 1187 generateAudioZoneMock()1188 private static CarAudioZone generateAudioZoneMock() { 1189 CarAudioZone mockZone = mock(CarAudioZone.class); 1190 when(mockZone.getAddressForContext(TEST_MEDIA_CONTEXT_ID)) 1191 .thenReturn(PRIMARY_MUSIC_ADDRESS); 1192 when(mockZone.getAddressForContext(TEST_NOTIFICATION_CONTEXT_ID)) 1193 .thenReturn(PRIMARY_NOTIFICATION_ADDRESS); 1194 1195 when(mockZone.getCarAudioContext()).thenReturn(TEST_CAR_AUDIO_CONTEXT); 1196 1197 return mockZone; 1198 } 1199 verifyOnDevicesToMuteChangeCalled(int audioZoneId)1200 private MutingInfo verifyOnDevicesToMuteChangeCalled(int audioZoneId) throws Exception { 1201 ArgumentCaptor<MutingInfo[]> captor = ArgumentCaptor.forClass(MutingInfo[].class); 1202 verify(mAudioControl).onDevicesToMuteChange(captor.capture()); 1203 for (MutingInfo info : captor.getValue()) { 1204 if (info.zoneId != audioZoneId) { 1205 continue; 1206 } 1207 return info; 1208 } 1209 return null; 1210 } 1211 1212 private static final class MutingInfoBuilder { 1213 private final int mZoneId; 1214 private String[] mAddressesToMute; 1215 private String[] mAddressesToUnMute; 1216 MutingInfoBuilder(int zoneId)1217 MutingInfoBuilder(int zoneId) { 1218 mZoneId = zoneId; 1219 } 1220 setMutedAddresses(String... addressesToMute)1221 MutingInfoBuilder setMutedAddresses(String... addressesToMute) { 1222 mAddressesToMute = addressesToMute; 1223 return this; 1224 } 1225 setUnMutedAddresses(String... addressesToUnMute)1226 MutingInfoBuilder setUnMutedAddresses(String... addressesToUnMute) { 1227 mAddressesToUnMute = addressesToUnMute; 1228 return this; 1229 } 1230 build()1231 MutingInfo build() { 1232 MutingInfo info = new MutingInfo(); 1233 info.zoneId = mZoneId; 1234 info.deviceAddressesToMute = mAddressesToMute; 1235 info.deviceAddressesToUnmute = mAddressesToUnMute; 1236 return info; 1237 } 1238 } 1239 } 1240