• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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