1 /*
2  * Copyright (C) 2023 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 android.car.media;
18 
19 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING;
20 import static android.car.media.CarAudioManager.CONFIG_STATUS_CHANGED;
21 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
22 import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
23 import static android.media.AudioAttributes.USAGE_GAME;
24 import static android.media.AudioAttributes.USAGE_MEDIA;
25 import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC;
26 import static android.media.AudioDeviceInfo.TYPE_FM_TUNER;
27 
28 import static org.junit.Assert.assertThrows;
29 import static org.mockito.ArgumentMatchers.any;
30 import static org.mockito.ArgumentMatchers.anyString;
31 import static org.mockito.ArgumentMatchers.eq;
32 import static org.mockito.Mockito.clearInvocations;
33 import static org.mockito.Mockito.doAnswer;
34 import static org.mockito.Mockito.doThrow;
35 import static org.mockito.Mockito.mock;
36 import static org.mockito.Mockito.never;
37 import static org.mockito.Mockito.timeout;
38 import static org.mockito.Mockito.verify;
39 import static org.mockito.Mockito.when;
40 
41 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
42 import android.car.test.AbstractExpectableTestCase;
43 import android.content.Context;
44 import android.media.AudioAttributes;
45 import android.media.AudioDeviceAttributes;
46 import android.media.AudioDeviceInfo;
47 import android.media.AudioManager;
48 import android.os.Handler;
49 import android.os.HandlerThread;
50 import android.os.IBinder;
51 import android.os.RemoteException;
52 import android.platform.test.ravenwood.RavenwoodRule;
53 
54 import com.android.car.audio.AudioDeviceInfoBuilder;
55 import com.android.car.internal.ICarBase;
56 
57 import org.junit.After;
58 import org.junit.Before;
59 import org.junit.Rule;
60 import org.junit.Test;
61 import org.junit.runner.RunWith;
62 import org.mockito.ArgumentCaptor;
63 import org.mockito.Mock;
64 import org.mockito.junit.MockitoJUnitRunner;
65 
66 import java.util.List;
67 import java.util.concurrent.CountDownLatch;
68 import java.util.concurrent.Executor;
69 import java.util.concurrent.TimeUnit;
70 
71 @RunWith(MockitoJUnitRunner.class)
72 public final class CarAudioManagerUnitTest extends AbstractExpectableTestCase {
73     // Required to set the process ID and set the "main" thread for this test, otherwise
74     // getMainLooper will return null.
75     @Rule
76     public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().setProcessApp()
77             .setProvideMainThread(true).build();
78 
79     private static final String Car_AUDIO_MANAGER_TEST_THREAD_NAME = "CarAudioManagerUnitTest";
80     private static final String MICROPHONE_ADDRESS = "Built-In Mic";
81     private static final String FM_TUNER_ADDRESS = "FM Tuner";
82     private static final String MEDIA_TEST_DEVICE = "media_bus_device";
83     private static final String NAVIGATION_TEST_DEVICE = "navigation_bus_device";
84     private static final int TEST_REAR_LEFT_ZONE_ID = 1;
85     private static final int TEST_REAR_RIGHT_ZONE_ID = 2;
86     private static final int TEST_FRONT_ZONE_ID = 3;
87     private static final int TEST_VOLUME_GROUP_ID = 1;
88     private static final int TEST_VOLUME_GROUP_INDEX = 10;
89     private static final int TEST_FLAGS = 0;
90     private static final int TEST_GAIN_MIN_VALUE = -3000;
91     private static final int TEST_GAIN_MAX_VALUE = -1000;
92     private static final int TEST_GAIN_DEFAULT_VALUE = -2000;
93     private static final int TEST_VOLUME_GROUP_COUNT = 2;
94     private static final int TEST_USAGE = USAGE_MEDIA;
95     private static final int TEST_UID = 15;
96     private static final long TEST_TIME_OUT_MS = 500;
97     private static final long TEST_REQUEST_ID = 1;
98 
99     private static final AudioDeviceInfo  TEST_MICROPHONE_INPUT_DEVICE = new
100             AudioDeviceInfoBuilder().setAddressName(MICROPHONE_ADDRESS)
101             .setType(TYPE_BUILTIN_MIC).setIsSource(true).build();
102     private static final AudioDeviceInfo TEST_TUNER_INPUT_DEVICE = new AudioDeviceInfoBuilder()
103                 .setAddressName(FM_TUNER_ADDRESS).setType(TYPE_FM_TUNER).setIsSource(true).build();
104     private static final AudioDeviceInfo TEST_MEDIA_OUTPUT_DEVICE = new AudioDeviceInfoBuilder()
105             .setAddressName(MEDIA_TEST_DEVICE).build();
106     private static final AudioDeviceInfo TEST_NAV_OUTPUT_DEVICE = new AudioDeviceInfoBuilder()
107             .setAddressName(NAVIGATION_TEST_DEVICE).build();
108 
109     private static final Executor DIRECT_EXECUTOR = Runnable::run;
110 
111     private HandlerThread mHandlerThread;
112     private Handler mHandler;
113     private final RemoteException mRemoteException = new RemoteException();
114 
115     @Mock
116     private ICarBase mCar;
117     @Mock
118     private IBinder mBinderMock;
119     @Mock
120     private ICarAudio mServiceMock;
121     @Mock
122     private AudioManager mAudioManagerMock;
123     @Mock
124     private Context mContextMock;
125     @Mock
126     private CarVolumeGroupInfo mCarVolumeGroupInfoMock1;
127     @Mock
128     private CarVolumeGroupInfo mCarVolumeGroupInfoMock2;
129     @Mock
130     private AudioAttributes mAudioAttributesMock1;
131     @Mock
132     private AudioAttributes mAudioAttributesMock2;
133     @Mock
134     private CarAudioManager.CarVolumeCallback mVolumeCallbackMock1;
135     @Mock
136     private CarAudioManager.CarVolumeCallback mVolumeCallbackMock2;
137     @Mock
138     private CarAudioZoneConfigInfo mZoneConfigInfoMock1;
139     @Mock
140     private CarAudioZoneConfigInfo mZoneConfigInfoMock2;
141     @Mock
142     private OccupantZoneInfo mOccupantZoneInfoMock;
143     @Mock
144     private SwitchAudioZoneConfigCallback mSwitchCallbackMock;
145     @Mock
146     private CarVolumeGroupEventCallback mVolumeGroupEventCallbackMock1;
147     @Mock
148     private CarVolumeGroupEventCallback mVolumeGroupEventCallbackMock2;
149     @Mock
150     private PrimaryZoneMediaAudioRequestCallback mPrimaryZoneMediaAudioRequestCallbackMock;
151     @Mock
152     private MediaAudioRequestStatusCallback mMediaAudioRequestStatusCallbackMock;
153     @Mock
154     private AudioZonesMirrorStatusCallback mAudioZonesMirrorStatusCallback;
155 
156     private CarAudioManager mCarAudioManager;
157     private TestAudioZoneConfigurationsChangeCallback mTestConfigCallback;
158 
159     @Before
setUp()160     public void setUp() throws Exception {
161         mHandlerThread = new HandlerThread(Car_AUDIO_MANAGER_TEST_THREAD_NAME);
162         mHandlerThread.start();
163         mHandler = new Handler(mHandlerThread.getLooper());
164 
165         when(mBinderMock.queryLocalInterface(anyString())).thenReturn(mServiceMock);
166         when(mCar.getContext()).thenReturn(mContextMock);
167         when(mCar.getEventHandler()).thenReturn(mHandler);
168         when(mContextMock.getSystemService(AudioManager.class)).thenReturn(mAudioManagerMock);
169         mCarAudioManager = new CarAudioManager(mCar, mBinderMock);
170         doAnswer(invocation -> invocation.getArgument(1)).when(mCar)
171                 .handleRemoteExceptionFromCarService(any(RemoteException.class), any());
172         mTestConfigCallback = new TestAudioZoneConfigurationsChangeCallback();
173         when(mServiceMock.registerAudioZoneConfigsChangeCallback(any())).thenReturn(true);
174     }
175 
176     @After
tearDown()177     public void tearDown() throws Exception {
178         mHandlerThread.quitSafely();
179         mHandlerThread.join();
180     }
181 
182     @Test
isAudioFeatureEnabled()183     public void isAudioFeatureEnabled() throws Exception {
184         when(mServiceMock.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)).thenReturn(true);
185 
186         expectWithMessage("Dynamic audio routing enabled")
187                 .that(mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING))
188                 .isTrue();
189     }
190 
191     @Test
isAudioFeatureEnabled_withServiceRemoteException_returnsFalse()192     public void isAudioFeatureEnabled_withServiceRemoteException_returnsFalse() throws Exception {
193         doThrow(mRemoteException).when(mServiceMock).isAudioFeatureEnabled(
194                 AUDIO_FEATURE_DYNAMIC_ROUTING);
195 
196         expectWithMessage("Dynamic audio routing disabled when service throws remote exception")
197                 .that(mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING))
198                 .isFalse();
199     }
200 
201     @Test
setGroupVolume_withoutZoneId()202     public void setGroupVolume_withoutZoneId() throws Exception {
203         mCarAudioManager.setGroupVolume(TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS);
204 
205         verify(mServiceMock).setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID,
206                 TEST_VOLUME_GROUP_INDEX, TEST_FLAGS);
207     }
208 
209     @Test
setGroupVolume_withZoneId()210     public void setGroupVolume_withZoneId() throws Exception {
211         mCarAudioManager.setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID,
212                 TEST_VOLUME_GROUP_INDEX, TEST_FLAGS);
213 
214         verify(mServiceMock).setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID,
215                 TEST_VOLUME_GROUP_INDEX, TEST_FLAGS);
216     }
217 
218     @Test
setGroupVolume_withServiceRemoteException()219     public void setGroupVolume_withServiceRemoteException() throws Exception {
220         doThrow(mRemoteException).when(mServiceMock).setGroupVolume(TEST_REAR_RIGHT_ZONE_ID,
221                 TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS);
222 
223         mCarAudioManager.setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID,
224                 TEST_VOLUME_GROUP_INDEX, TEST_FLAGS);
225 
226         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
227     }
228 
229     @Test
getGroupMaxVolume_withoutZoneId()230     public void getGroupMaxVolume_withoutZoneId() throws Exception {
231         when(mServiceMock.getGroupMaxVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID))
232                 .thenReturn(TEST_GAIN_MAX_VALUE);
233 
234         expectWithMessage("Primary zone max volume")
235                 .that(mCarAudioManager.getGroupMaxVolume(TEST_VOLUME_GROUP_ID))
236                 .isEqualTo(TEST_GAIN_MAX_VALUE);
237     }
238 
239     @Test
getGroupMaxVolume_withZoneId()240     public void getGroupMaxVolume_withZoneId() throws Exception {
241         when(mServiceMock.getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
242                 .thenReturn(TEST_GAIN_MAX_VALUE);
243 
244         expectWithMessage("Rear right zone max volume").that(mCarAudioManager
245                 .getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
246                 .isEqualTo(TEST_GAIN_MAX_VALUE);
247     }
248 
249     @Test
getGroupMaxVolume_withServiceRemoteException()250     public void getGroupMaxVolume_withServiceRemoteException() throws Exception {
251         doThrow(mRemoteException).when(mServiceMock).getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID,
252                 TEST_VOLUME_GROUP_ID);
253 
254         expectWithMessage("Rear right zone max volume when service throws remote exception")
255                 .that(mCarAudioManager.getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID,
256                         TEST_VOLUME_GROUP_ID)).isEqualTo(0);
257     }
258 
259     @Test
getGroupMinVolume_withoutZoneId()260     public void getGroupMinVolume_withoutZoneId() throws Exception {
261         when(mServiceMock.getGroupMinVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID))
262                 .thenReturn(TEST_GAIN_MIN_VALUE);
263 
264         expectWithMessage("Primary zone min volume")
265                 .that(mCarAudioManager.getGroupMinVolume(TEST_VOLUME_GROUP_ID))
266                 .isEqualTo(TEST_GAIN_MIN_VALUE);
267     }
268 
269     @Test
getGroupMinVolume_withZoneId()270     public void getGroupMinVolume_withZoneId() throws Exception {
271         when(mServiceMock.getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
272                 .thenReturn(TEST_GAIN_MIN_VALUE);
273 
274         expectWithMessage("Rear right zone min volume").that(mCarAudioManager
275                 .getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
276                 .isEqualTo(TEST_GAIN_MIN_VALUE);
277     }
278 
279     @Test
getGroupMinVolume_withServiceRemoteException()280     public void getGroupMinVolume_withServiceRemoteException() throws Exception {
281         doThrow(mRemoteException).when(mServiceMock).getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID,
282                 TEST_VOLUME_GROUP_ID);
283 
284         expectWithMessage("Rear right zone min volume when service throws remote exception")
285                 .that(mCarAudioManager.getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID,
286                         TEST_VOLUME_GROUP_ID)).isEqualTo(0);
287     }
288 
289     @Test
getGroupVolume_withoutZoneId()290     public void getGroupVolume_withoutZoneId() throws Exception {
291         when(mServiceMock.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID))
292                 .thenReturn(TEST_GAIN_DEFAULT_VALUE);
293 
294         expectWithMessage("Primary zone volume")
295                 .that(mCarAudioManager.getGroupVolume(TEST_VOLUME_GROUP_ID))
296                 .isEqualTo(TEST_GAIN_DEFAULT_VALUE);
297     }
298 
299     @Test
getGroupVolume_withZoneId()300     public void getGroupVolume_withZoneId() throws Exception {
301         when(mServiceMock.getGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
302                 .thenReturn(TEST_GAIN_DEFAULT_VALUE);
303 
304         expectWithMessage("Rear right zone volume").that(mCarAudioManager.getGroupVolume(
305                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(TEST_GAIN_DEFAULT_VALUE);
306     }
307 
308     @Test
getGroupVolume_withServiceRemoteException()309     public void getGroupVolume_withServiceRemoteException() throws Exception {
310         doThrow(mRemoteException).when(mServiceMock).getGroupVolume(TEST_REAR_RIGHT_ZONE_ID,
311                 TEST_VOLUME_GROUP_ID);
312 
313         expectWithMessage("Rear right zone volume when service throws remote exception")
314                 .that(mCarAudioManager.getGroupVolume(TEST_REAR_RIGHT_ZONE_ID,
315                         TEST_VOLUME_GROUP_ID)).isEqualTo(0);
316     }
317 
318     @Test
setFadeTowardFront()319     public void setFadeTowardFront() throws Exception {
320         float value = 0.3f;
321 
322         mCarAudioManager.setFadeTowardFront(value);
323 
324         verify(mServiceMock).setFadeTowardFront(value);
325     }
326 
327     @Test
setFadeTowardFront_withServiceRemoteException()328     public void setFadeTowardFront_withServiceRemoteException() throws Exception {
329         float value = 0.3f;
330         doThrow(mRemoteException).when(mServiceMock).setFadeTowardFront(value);
331 
332         mCarAudioManager.setFadeTowardFront(value);
333 
334         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
335     }
336 
337     @Test
setBalanceTowardRight()338     public void setBalanceTowardRight() throws Exception {
339         float value = 0.4f;
340 
341         mCarAudioManager.setBalanceTowardRight(value);
342 
343         verify(mServiceMock).setBalanceTowardRight(value);
344     }
345 
346     @Test
setBalanceTowardRight_withServiceRemoteException()347     public void setBalanceTowardRight_withServiceRemoteException() throws Exception {
348         float value = 0.4f;
349         doThrow(mRemoteException).when(mServiceMock).setBalanceTowardRight(value);
350 
351         mCarAudioManager.setBalanceTowardRight(value);
352 
353         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
354     }
355 
356     @Test
getVolumeGroupCount_withoutZoneId()357     public void getVolumeGroupCount_withoutZoneId() throws Exception {
358         when(mServiceMock.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
359                 .thenReturn(TEST_VOLUME_GROUP_COUNT);
360 
361         expectWithMessage("Primary zone volume group count")
362                 .that(mCarAudioManager.getVolumeGroupCount()).isEqualTo(TEST_VOLUME_GROUP_COUNT);
363     }
364 
365     @Test
getVolumeGroupCount_withZoneId()366     public void getVolumeGroupCount_withZoneId() throws Exception {
367         when(mServiceMock.getVolumeGroupCount(TEST_REAR_RIGHT_ZONE_ID))
368                 .thenReturn(TEST_VOLUME_GROUP_COUNT);
369 
370         expectWithMessage("Rear right zone volume group count")
371                 .that(mCarAudioManager.getVolumeGroupCount(TEST_REAR_RIGHT_ZONE_ID))
372                 .isEqualTo(TEST_VOLUME_GROUP_COUNT);
373     }
374 
375     @Test
getVolumeGroupCount_withServiceRemoteException()376     public void getVolumeGroupCount_withServiceRemoteException() throws Exception {
377         doThrow(mRemoteException).when(mServiceMock).getVolumeGroupCount(
378                 TEST_REAR_RIGHT_ZONE_ID);
379 
380         expectWithMessage("Rear right zone volume group count when service throws remote exception")
381                 .that(mCarAudioManager.getVolumeGroupCount(TEST_REAR_RIGHT_ZONE_ID)).isEqualTo(0);
382     }
383 
384     @Test
getVolumeGroupIdForUsage_withoutZoneId()385     public void getVolumeGroupIdForUsage_withoutZoneId() throws Exception {
386         when(mServiceMock.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, TEST_USAGE))
387                 .thenReturn(TEST_VOLUME_GROUP_ID);
388 
389         expectWithMessage("Volume group id for media usage in primary zone")
390                 .that(mCarAudioManager.getVolumeGroupIdForUsage(TEST_USAGE))
391                 .isEqualTo(TEST_VOLUME_GROUP_ID);
392     }
393 
394     @Test
getVolumeGroupIdForUsage_withZoneId()395     public void getVolumeGroupIdForUsage_withZoneId() throws Exception {
396         when(mServiceMock.getVolumeGroupIdForUsage(TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE))
397                 .thenReturn(TEST_VOLUME_GROUP_ID);
398 
399         expectWithMessage("Volume group id for media usage in rear right zone")
400                 .that(mCarAudioManager.getVolumeGroupIdForUsage(TEST_REAR_RIGHT_ZONE_ID,
401                         TEST_USAGE)).isEqualTo(TEST_VOLUME_GROUP_ID);
402     }
403 
404     @Test
getVolumeGroupIdForUsage_withServiceRemoteException()405     public void getVolumeGroupIdForUsage_withServiceRemoteException() throws Exception {
406         doThrow(mRemoteException).when(mServiceMock).getVolumeGroupIdForUsage(
407                 TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE);
408 
409         expectWithMessage("Volume group id for media usage in rear right zone when service "
410                 + "throws remote exception").that(mCarAudioManager.getVolumeGroupIdForUsage(
411                         TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE)).isEqualTo(0);
412     }
413 
414     @Test
getUsagesForVolumeGroupId_withoutZoneId()415     public void getUsagesForVolumeGroupId_withoutZoneId() throws Exception {
416         int[] volumeGroupUsages = new int[]{USAGE_MEDIA, USAGE_GAME};
417         when(mServiceMock.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID))
418                 .thenReturn(volumeGroupUsages);
419 
420         expectWithMessage("Group %s usages in primary zone", TEST_VOLUME_GROUP_ID)
421                 .that(mCarAudioManager.getUsagesForVolumeGroupId(TEST_VOLUME_GROUP_ID))
422                 .isEqualTo(volumeGroupUsages);
423     }
424 
425     @Test
getUsagesForVolumeGroupId_withZoneId()426     public void getUsagesForVolumeGroupId_withZoneId() throws Exception {
427         int[] volumeGroupUsages = new int[]{USAGE_MEDIA, USAGE_GAME};
428         when(mServiceMock.getUsagesForVolumeGroupId(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
429                 .thenReturn(volumeGroupUsages);
430 
431         expectWithMessage("Group %s usages in rear right zone", TEST_VOLUME_GROUP_ID)
432                 .that(mCarAudioManager.getUsagesForVolumeGroupId(TEST_REAR_RIGHT_ZONE_ID,
433                         TEST_VOLUME_GROUP_ID)).isEqualTo(volumeGroupUsages);
434     }
435 
436     @Test
getUsagesForVolumeGroupId_withServiceRemoteException_returnsEmptyList()437     public void getUsagesForVolumeGroupId_withServiceRemoteException_returnsEmptyList()
438             throws Exception {
439         doThrow(mRemoteException).when(mServiceMock).getUsagesForVolumeGroupId(
440                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID);
441 
442         expectWithMessage("Group %s usages in rear right zone when service throws remote exception",
443                 TEST_VOLUME_GROUP_ID).that(mCarAudioManager.getUsagesForVolumeGroupId(
444                         TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).asList().isEmpty();
445     }
446 
447     @Test
getVolumeGroupInfo()448     public void getVolumeGroupInfo() throws Exception {
449         when(mServiceMock.getVolumeGroupInfo(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
450                 .thenReturn(mCarVolumeGroupInfoMock1);
451 
452         expectWithMessage("Volume group info with id %s in rear right zone")
453                 .that(mCarAudioManager.getVolumeGroupInfo(TEST_REAR_RIGHT_ZONE_ID,
454                         TEST_VOLUME_GROUP_ID)).isEqualTo(mCarVolumeGroupInfoMock1);
455     }
456 
457     @Test
getVolumeGroupInfo_withServiceRemoteException_returnsNull()458     public void getVolumeGroupInfo_withServiceRemoteException_returnsNull() throws Exception {
459         doThrow(mRemoteException).when(mServiceMock).getVolumeGroupInfo(
460                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID);
461 
462         expectWithMessage("Volume group info with id %s in rear right zone when service throws "
463                 + "remote exception").that(mCarAudioManager.getVolumeGroupInfo(
464                         TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isNull();
465     }
466 
467     @Test
getVolumeGroupInfosForZone()468     public void getVolumeGroupInfosForZone() throws Exception {
469         when(mServiceMock.getVolumeGroupInfosForZone(TEST_REAR_RIGHT_ZONE_ID))
470                 .thenReturn(List.of(mCarVolumeGroupInfoMock1, mCarVolumeGroupInfoMock2));
471 
472         expectWithMessage("Volume group infos in rear right zone")
473                 .that(mCarAudioManager.getVolumeGroupInfosForZone(TEST_REAR_RIGHT_ZONE_ID))
474                 .containsExactly(mCarVolumeGroupInfoMock1, mCarVolumeGroupInfoMock2);
475     }
476 
477     @Test
getVolumeGroupInfosForZone_withServiceRemoteException_returnsEmptyList()478     public void getVolumeGroupInfosForZone_withServiceRemoteException_returnsEmptyList()
479             throws Exception {
480         doThrow(mRemoteException).when(mServiceMock).getVolumeGroupInfosForZone(
481                 TEST_REAR_RIGHT_ZONE_ID);
482 
483         expectWithMessage("Volume group infos in rear right zone when service throws remote "
484                 + "exception").that(mCarAudioManager.getVolumeGroupInfosForZone(
485                         TEST_REAR_RIGHT_ZONE_ID)).isEmpty();
486     }
487 
488     @Test
getAudioAttributesForVolumeGroup()489     public void getAudioAttributesForVolumeGroup() throws Exception {
490         List<AudioAttributes> attributesList = List.of(mAudioAttributesMock1,
491                 mAudioAttributesMock2);
492         when(mServiceMock.getAudioAttributesForVolumeGroup(mCarVolumeGroupInfoMock1))
493                 .thenReturn(attributesList);
494 
495         expectWithMessage("Audio attributes for volume group").that(mCarAudioManager
496                 .getAudioAttributesForVolumeGroup(mCarVolumeGroupInfoMock1))
497                 .isEqualTo(attributesList);
498     }
499 
500     @Test
getAudioAttributesForVolumeGroup_withServiceRemoteException_returnsEmptyList()501     public void getAudioAttributesForVolumeGroup_withServiceRemoteException_returnsEmptyList()
502             throws Exception {
503         doThrow(mRemoteException).when(mServiceMock).getAudioAttributesForVolumeGroup(
504                 mCarVolumeGroupInfoMock1);
505 
506         expectWithMessage("Audio attributes for volume group when service throws remote exception")
507                 .that(mCarAudioManager.getAudioAttributesForVolumeGroup(mCarVolumeGroupInfoMock1))
508                 .isEmpty();
509     }
510 
511     @Test
isPlaybackOnVolumeGroupActive()512     public void isPlaybackOnVolumeGroupActive() throws Exception {
513         when(mServiceMock.isPlaybackOnVolumeGroupActive(TEST_REAR_RIGHT_ZONE_ID,
514                 TEST_VOLUME_GROUP_ID)).thenReturn(true);
515 
516         expectWithMessage("Enabled playback on volume group %s in rear right zone",
517                 TEST_VOLUME_GROUP_ID).that(mCarAudioManager.isPlaybackOnVolumeGroupActive(
518                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isTrue();
519     }
520 
521     @Test
isPlaybackOnVolumeGroupActive_withServiceRemoteException_returnsFalse()522     public void isPlaybackOnVolumeGroupActive_withServiceRemoteException_returnsFalse()
523             throws Exception {
524         doThrow(mRemoteException).when(mServiceMock).isPlaybackOnVolumeGroupActive(
525                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID);
526 
527         expectWithMessage("Enabled playback on volume group %s in rear right zone when service "
528                         + "throws remote exception", TEST_VOLUME_GROUP_ID).that(mCarAudioManager
529                 .isPlaybackOnVolumeGroupActive(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
530                 .isFalse();
531     }
532 
533     @Test
getAudioZoneIds()534     public void getAudioZoneIds() throws Exception {
535         int[] expectedZoneIds = new int[]{PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
536                 TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID};
537         when(mServiceMock.getAudioZoneIds()).thenReturn(expectedZoneIds);
538 
539         expectWithMessage("Zone ids").that(mCarAudioManager.getAudioZoneIds())
540                 .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
541                         TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID).inOrder();
542     }
543 
544     @Test
getAudioZoneIds_withServiceRemoteException_returnsEmptyList()545     public void getAudioZoneIds_withServiceRemoteException_returnsEmptyList() throws Exception {
546         doThrow(mRemoteException).when(mServiceMock).getAudioZoneIds();
547 
548         expectWithMessage("Zone ids when service throws remote exception")
549                 .that(mCarAudioManager.getAudioZoneIds()).isEmpty();
550     }
551 
552     @Test
getZoneIdForUid()553     public void getZoneIdForUid() throws Exception {
554         when(mServiceMock.getZoneIdForUid(TEST_UID)).thenReturn(TEST_REAR_LEFT_ZONE_ID);
555 
556         expectWithMessage("Zone id for uid %s", TEST_UID).that(mCarAudioManager
557                 .getZoneIdForUid(TEST_UID)).isEqualTo(TEST_REAR_LEFT_ZONE_ID);
558     }
559 
560     @Test
getZoneIdForUid_withServiceRemoteException()561     public void getZoneIdForUid_withServiceRemoteException() throws Exception {
562         doThrow(mRemoteException).when(mServiceMock).getZoneIdForUid(TEST_UID);
563 
564         expectWithMessage("Zone id for uid %s when service throws remote exception", TEST_UID)
565                 .that(mCarAudioManager.getZoneIdForUid(TEST_UID)).isEqualTo(0);
566     }
567 
568     @Test
setZoneIdForUid()569     public void setZoneIdForUid() throws Exception {
570         when(mServiceMock.setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID, TEST_UID)).thenReturn(true);
571 
572         expectWithMessage("Status for setting uid %s to rear right zone", TEST_UID)
573                 .that(mCarAudioManager.setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID, TEST_UID)).isTrue();
574     }
575 
576     @Test
setZoneIdForUid_withServiceRemoteException_returnsFalse()577     public void setZoneIdForUid_withServiceRemoteException_returnsFalse() throws Exception {
578         doThrow(mRemoteException).when(mServiceMock).setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID,
579                 TEST_UID);
580 
581         expectWithMessage("Status for setting uid %s to rear right zone when service throws "
582                 + "remote exception").that(mCarAudioManager.setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID,
583                 TEST_UID)).isFalse();
584     }
585 
586     @Test
clearZoneIdForUid()587     public void clearZoneIdForUid() throws Exception {
588         when(mServiceMock.clearZoneIdForUid(TEST_UID)).thenReturn(true);
589 
590         expectWithMessage("Status for clearing zone id for uid %s", TEST_UID)
591                 .that(mCarAudioManager.clearZoneIdForUid(TEST_UID)).isTrue();
592     }
593 
594     @Test
clearZoneIdForUid_withServiceRemoteException_returnsFalse()595     public void clearZoneIdForUid_withServiceRemoteException_returnsFalse() throws Exception {
596         doThrow(mRemoteException).when(mServiceMock).clearZoneIdForUid(TEST_UID);
597 
598         expectWithMessage("Status for clearing zone id for uid when service throws remote "
599                 + "exception").that(mCarAudioManager.clearZoneIdForUid(TEST_UID)).isFalse();
600     }
601 
602     @Test
registerCarVolumeGroupEventCallback()603     public void registerCarVolumeGroupEventCallback() throws Exception {
604         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
605 
606         expectWithMessage("Status for registering car volume group event callback")
607                 .that(mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
608                         mVolumeGroupEventCallbackMock1)).isTrue();
609     }
610 
611     @Test
registerCarVolumeGroupEventCallback_withMultipleCallbacks()612     public void registerCarVolumeGroupEventCallback_withMultipleCallbacks() throws Exception {
613         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
614         mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
615                 mVolumeGroupEventCallbackMock1);
616         verify(mServiceMock).registerCarVolumeEventCallback(any());
617         clearInvocations(mServiceMock);
618 
619         expectWithMessage("Status for registering second car volume group event callback")
620                 .that(mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
621                         mVolumeGroupEventCallbackMock2)).isTrue();
622         verify(mServiceMock, never()).registerCarVolumeEventCallback(any());
623     }
624 
625     @Test
registerCarVolumeGroupEventCallback_withServiceReturnsFalse_returnsFalse()626     public void registerCarVolumeGroupEventCallback_withServiceReturnsFalse_returnsFalse()
627             throws Exception {
628         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(false);
629 
630         expectWithMessage("Failure for registering car volume group event callback")
631                 .that(mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
632                         mVolumeGroupEventCallbackMock1)).isFalse();
633     }
634 
635     @Test
registerCarVolumeGroupEventCallback_withSameCallbackforMultipleTimes_returnsFalse()636     public void registerCarVolumeGroupEventCallback_withSameCallbackforMultipleTimes_returnsFalse()
637             throws Exception {
638         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
639         mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
640                 mVolumeGroupEventCallbackMock1);
641 
642         expectWithMessage("Failure for registering car volume group event callback "
643                 + "for multiple times").that(mCarAudioManager.registerCarVolumeGroupEventCallback(
644                         DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1)).isFalse();
645     }
646 
647     @Test
registerCarVolumeGroupEventCallback_withNullExecutor_fails()648     public void registerCarVolumeGroupEventCallback_withNullExecutor_fails() {
649         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
650                 mCarAudioManager.registerCarVolumeGroupEventCallback(/* executor= */ null,
651                         mVolumeGroupEventCallbackMock1));
652 
653         expectWithMessage("Exception for registering car volume group event callback with "
654                 + "null executor").that(thrown).hasMessageThat()
655                 .contains("Executor can not be null");
656     }
657 
658     @Test
registerCarVolumeGroupEventCallback_withNullCallback_fails()659     public void registerCarVolumeGroupEventCallback_withNullCallback_fails() {
660         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
661                 mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
662                         /* callback= */ null));
663 
664         expectWithMessage("Exception for registering null car volume group event callback")
665                 .that(thrown).hasMessageThat()
666                 .contains("Car volume event callback can not be null");
667     }
668 
669     @Test
registerCarVolumeGroupEventCallback_withServiceRemoteException_returnsFalse()670     public void registerCarVolumeGroupEventCallback_withServiceRemoteException_returnsFalse()
671             throws Exception {
672         doThrow(mRemoteException).when(mServiceMock).registerCarVolumeEventCallback(any());
673 
674         expectWithMessage("Failure for registering car volume group event callback when service "
675                 + "throws remote exception").that(mCarAudioManager
676                 .registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
677                         mVolumeGroupEventCallbackMock1)).isFalse();
678     }
679 
680     @Test
unregisterCarVolumeGroupEventCallback_withPartOfCallbacks()681     public void unregisterCarVolumeGroupEventCallback_withPartOfCallbacks() throws Exception {
682         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
683         ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl(
684                 mVolumeGroupEventCallbackMock1);
685         mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
686                 mVolumeGroupEventCallbackMock2);
687 
688         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock1);
689 
690         verify(mServiceMock, never()).unregisterCarVolumeEventCallback(serviceCallback);
691     }
692 
693     @Test
unregisterCarVolumeGroupEventCallback_withAllCallbacks()694     public void unregisterCarVolumeGroupEventCallback_withAllCallbacks() throws Exception {
695         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
696         ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl(
697                 mVolumeGroupEventCallbackMock1);
698         mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
699                 mVolumeGroupEventCallbackMock2);
700         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock1);
701         verify(mServiceMock, never()).unregisterCarVolumeEventCallback(serviceCallback);
702 
703         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock2);
704 
705         verify(mServiceMock).unregisterCarVolumeEventCallback(serviceCallback);
706     }
707 
708     @Test
unregisterCarVolumeGroupEventCallback_withNullCallback_fails()709     public void unregisterCarVolumeGroupEventCallback_withNullCallback_fails() {
710         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
711                 mCarAudioManager.unregisterCarVolumeGroupEventCallback(/* callback= */ null));
712 
713         expectWithMessage("Exception for unregistering null car volume group event callback")
714                 .that(thrown).hasMessageThat()
715                 .contains("Car volume event callback can not be null");
716     }
717 
718     @Test
unregisterCarVolumeGroupEventCallback_withServiceRemoteException()719     public void unregisterCarVolumeGroupEventCallback_withServiceRemoteException()
720             throws Exception {
721         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
722         ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl(
723                 mVolumeGroupEventCallbackMock1);
724         doThrow(mRemoteException).when(mServiceMock).unregisterCarVolumeEventCallback(
725                 serviceCallback);
726 
727         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock1);
728     }
729 
730     @Test
onVolumeGroupEvent_forICarVolumeEventCallback()731     public void onVolumeGroupEvent_forICarVolumeEventCallback() throws Exception {
732         List<CarVolumeGroupEvent> groupEvents = List.of(new CarVolumeGroupEvent.Builder(
733                 List.of(mCarVolumeGroupInfoMock1), CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED)
734                         .build(), new CarVolumeGroupEvent.Builder(
735                 List.of(mCarVolumeGroupInfoMock2),
736                 CarVolumeGroupEvent.EVENT_TYPE_VOLUME_BLOCKED_CHANGED).build());
737         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
738         ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl(
739                 mVolumeGroupEventCallbackMock1);
740         mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR,
741                 mVolumeGroupEventCallbackMock2);
742 
743         serviceCallback.onVolumeGroupEvent(groupEvents);
744 
745         verify(mVolumeGroupEventCallbackMock1, timeout(TEST_TIME_OUT_MS)).onVolumeGroupEvent(
746                 groupEvents);
747         verify(mVolumeGroupEventCallbackMock2, timeout(TEST_TIME_OUT_MS)).onVolumeGroupEvent(
748                 groupEvents);
749     }
750 
751     @Test
onMasterMuteChanged_forICarVolumeEventCallback()752     public void onMasterMuteChanged_forICarVolumeEventCallback() throws Exception {
753         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
754         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
755         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2);
756         ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl(
757                 mVolumeGroupEventCallbackMock1);
758 
759         serviceCallback.onMasterMuteChanged(TEST_REAR_RIGHT_ZONE_ID, TEST_FLAGS);
760 
761         verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onMasterMuteChanged(
762                 TEST_REAR_RIGHT_ZONE_ID, TEST_FLAGS);
763         verify(mVolumeCallbackMock2, timeout(TEST_TIME_OUT_MS)).onMasterMuteChanged(
764                 TEST_REAR_RIGHT_ZONE_ID, TEST_FLAGS);
765     }
766 
767     @Test
isVolumeGroupMuted()768     public void isVolumeGroupMuted() throws Exception {
769         when(mServiceMock.isVolumeGroupMuted(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID))
770                 .thenReturn(true);
771 
772         expectWithMessage("Muted volume group").that(mCarAudioManager.isVolumeGroupMuted(
773                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isTrue();
774     }
775 
776     @Test
isVolumeGroupMuted_withServiceRemoteException_returnsFalse()777     public void isVolumeGroupMuted_withServiceRemoteException_returnsFalse() throws Exception {
778         doThrow(mRemoteException).when(mServiceMock).isVolumeGroupMuted(
779                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID);
780 
781         expectWithMessage("Muted volume group when service throws remote exception")
782                 .that(mCarAudioManager.isVolumeGroupMuted(TEST_REAR_RIGHT_ZONE_ID,
783                         TEST_VOLUME_GROUP_ID)).isFalse();
784     }
785 
786     @Test
setVolumeGroupMute()787     public void setVolumeGroupMute() throws Exception {
788         mCarAudioManager.setVolumeGroupMute(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID,
789                 /* mute= */ true, TEST_FLAGS);
790 
791         verify(mServiceMock).setVolumeGroupMute(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID,
792                 /* mute= */ true, TEST_FLAGS);
793     }
794 
795     @Test
setVolumeGroupMute_withServiceRemoteException()796     public void setVolumeGroupMute_withServiceRemoteException() throws Exception {
797         doThrow(mRemoteException).when(mServiceMock).setVolumeGroupMute(
798                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, /* mute= */ true, TEST_FLAGS);
799 
800         mCarAudioManager.setVolumeGroupMute(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID,
801                 /* mute= */ true, TEST_FLAGS);
802 
803         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
804     }
805 
806     @Test
registerCarVolumeCallback()807     public void registerCarVolumeCallback() throws Exception {
808         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
809 
810         verify(mServiceMock).registerVolumeCallback(any(IBinder.class));
811     }
812 
813     @Test
registerCarVolumeCallback_withMultipleCallbacks()814     public void registerCarVolumeCallback_withMultipleCallbacks() throws Exception {
815         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
816         verify(mServiceMock).registerVolumeCallback(any(IBinder.class));
817         clearInvocations(mServiceMock);
818 
819         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2);
820 
821         verify(mServiceMock, never()).registerVolumeCallback(any(IBinder.class));
822     }
823 
824     @Test
unregisterCarVolumeCallback_withPartOfCallbacks()825     public void unregisterCarVolumeCallback_withPartOfCallbacks() throws Exception {
826         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
827         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2);
828 
829         mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock1);
830 
831         verify(mServiceMock, never()).unregisterVolumeCallback(any(IBinder.class));
832     }
833 
834     @Test
unregisterCarVolumeCallback_withAllCallbacks()835     public void unregisterCarVolumeCallback_withAllCallbacks() throws Exception {
836         ArgumentCaptor<IBinder> captor = ArgumentCaptor.forClass(IBinder.class);
837         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
838         verify(mServiceMock).registerVolumeCallback(captor.capture());
839         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2);
840 
841         mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock1);
842         mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock2);
843 
844         verify(mServiceMock).unregisterVolumeCallback(captor.getValue());
845     }
846 
847     @Test
unregisterCarVolumeCallback_withServiceRemoteException()848     public void unregisterCarVolumeCallback_withServiceRemoteException() throws Exception {
849         mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1);
850         verify(mServiceMock).registerVolumeCallback(any());
851         doThrow(mRemoteException).when(mServiceMock).unregisterVolumeCallback(any());
852 
853         mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock1);
854 
855         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
856     }
857 
858     @Test
onCarDisconnected()859     public void onCarDisconnected() throws Exception {
860         ICarVolumeCallback serviceVolCallback = getCarVolumeCallbackImpl(mVolumeCallbackMock1);
861         ICarVolumeEventCallback serviceVolEventCallback = getCarVolumeEventCallbackImpl(
862                 mVolumeGroupEventCallbackMock1);
863 
864         mCarAudioManager.onCarDisconnected();
865 
866         verify(mServiceMock).unregisterVolumeCallback(serviceVolCallback.asBinder());
867         verify(mServiceMock).unregisterCarVolumeEventCallback(serviceVolEventCallback);
868     }
869 
870     @Test
onGroupMuteChanged_forCarVolumeCallback()871     public void onGroupMuteChanged_forCarVolumeCallback() throws Exception {
872         ICarVolumeCallback callbackImpl = getCarVolumeCallbackImpl(mVolumeCallbackMock1);
873 
874         callbackImpl.onGroupMuteChanged(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_FLAGS);
875 
876         verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onGroupMuteChanged(
877                 TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_FLAGS);
878     }
879 
880     @Test
onGroupVolumeChanged_forCarVolumeCallback()881     public void onGroupVolumeChanged_forCarVolumeCallback() throws Exception {
882         ICarVolumeCallback callbackImpl = getCarVolumeCallbackImpl(mVolumeCallbackMock1);
883 
884         callbackImpl.onGroupVolumeChanged(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID, TEST_FLAGS);
885 
886         verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onGroupVolumeChanged(
887                 PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID, TEST_FLAGS);
888     }
889 
890     @Test
onMasterMuteChanged_forCarVolumeCallback()891     public void onMasterMuteChanged_forCarVolumeCallback() throws Exception {
892         ICarVolumeCallback callbackImpl = getCarVolumeCallbackImpl(mVolumeCallbackMock1);
893 
894         callbackImpl.onMasterMuteChanged(TEST_REAR_LEFT_ZONE_ID, TEST_FLAGS);
895 
896         verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onMasterMuteChanged(
897                 TEST_REAR_LEFT_ZONE_ID, TEST_FLAGS);
898     }
899 
900     @Test
getAudioZoneConfigInfos()901     public void getAudioZoneConfigInfos() throws Exception {
902         List<CarAudioZoneConfigInfo> zoneConfigInfos = List.of(mZoneConfigInfoMock1,
903                 mZoneConfigInfoMock2);
904         when(mServiceMock.getAudioZoneConfigInfos(TEST_REAR_RIGHT_ZONE_ID))
905                 .thenReturn(zoneConfigInfos);
906 
907         expectWithMessage("All configuration infos for rear right zone")
908                 .that(mCarAudioManager.getAudioZoneConfigInfos(TEST_REAR_RIGHT_ZONE_ID))
909                 .isEqualTo(zoneConfigInfos);
910     }
911 
912     @Test
getAudioZoneConfigInfos_withServiceRemoteException_returnsEmptyList()913     public void getAudioZoneConfigInfos_withServiceRemoteException_returnsEmptyList()
914             throws Exception {
915         doThrow(mRemoteException).when(mServiceMock).getAudioZoneConfigInfos(
916                 TEST_REAR_RIGHT_ZONE_ID);
917 
918         expectWithMessage("All configuration infos for rear right zone when service throws"
919                 + " remote exception").that(mCarAudioManager.getAudioZoneConfigInfos(
920                         TEST_REAR_RIGHT_ZONE_ID)).isEmpty();
921     }
922 
923     @Test
getCurrentAudioZoneConfigInfo()924     public void getCurrentAudioZoneConfigInfo() throws Exception {
925         when(mServiceMock.getCurrentAudioZoneConfigInfo(TEST_REAR_RIGHT_ZONE_ID))
926                 .thenReturn(mZoneConfigInfoMock1);
927 
928         expectWithMessage("Current configuration info for rear right zone")
929                 .that(mCarAudioManager.getCurrentAudioZoneConfigInfo(TEST_REAR_RIGHT_ZONE_ID))
930                 .isEqualTo(mZoneConfigInfoMock1);
931     }
932 
933     @Test
getCurrentAudioZoneConfigInfo_whenServiceThrowsRemoteException_returnsNull()934     public void getCurrentAudioZoneConfigInfo_whenServiceThrowsRemoteException_returnsNull()
935             throws Exception {
936         doThrow(mRemoteException).when(mServiceMock).getCurrentAudioZoneConfigInfo(
937                 TEST_REAR_RIGHT_ZONE_ID);
938 
939         expectWithMessage("Current configuration info for rear right zone when service throws "
940                 + "remote exception").that(mCarAudioManager.getCurrentAudioZoneConfigInfo(
941                         TEST_REAR_RIGHT_ZONE_ID)).isNull();
942     }
943 
944     @Test
switchAudioZoneToConfig()945     public void switchAudioZoneToConfig() throws Exception {
946         doAnswer(invocation -> {
947             CarAudioZoneConfigInfo configInfoToSwitch = invocation.getArgument(0);
948             ISwitchAudioZoneConfigCallback serviceCallback = invocation.getArgument(1);
949             serviceCallback.onAudioZoneConfigSwitched(configInfoToSwitch, /* isSuccessful= */ true);
950             return null;
951         }).when(mServiceMock).switchZoneToConfig(any(CarAudioZoneConfigInfo.class),
952                 any(ISwitchAudioZoneConfigCallback.class));
953 
954         mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, DIRECT_EXECUTOR,
955                 mSwitchCallbackMock);
956 
957         verify(mSwitchCallbackMock).onAudioZoneConfigSwitched(mZoneConfigInfoMock1,
958                 /* isSuccessful= */ true);
959     }
960 
961     @Test
switchAudioZoneToConfig_withNullConfig_fails()962     public void switchAudioZoneToConfig_withNullConfig_fails() {
963         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
964                 mCarAudioManager.switchAudioZoneToConfig(/* zoneConfig= */ null, DIRECT_EXECUTOR,
965                         mSwitchCallbackMock));
966 
967         expectWithMessage("Exception for switching zone configuration with null executor")
968                 .that(thrown).hasMessageThat()
969                 .contains("Audio zone configuration can not be null");
970     }
971 
972     @Test
switchAudioZoneToConfig_withNullExecutor_fails()973     public void switchAudioZoneToConfig_withNullExecutor_fails() {
974         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
975                 mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, /* executor= */ null,
976                         mSwitchCallbackMock));
977 
978         expectWithMessage("Exception for switching zone configuration with null executor")
979                 .that(thrown).hasMessageThat()
980                 .contains("Executor can not be null");
981     }
982 
983     @Test
switchAudioZoneToConfig_withNullCallback_fails()984     public void switchAudioZoneToConfig_withNullCallback_fails() {
985         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
986                 mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, DIRECT_EXECUTOR,
987                         /* callback= */ null));
988 
989         expectWithMessage("Exception for switching zone configuration with null callback")
990                 .that(thrown).hasMessageThat()
991                 .contains("Switching audio zone configuration result callback can not be null");
992     }
993 
994     @Test
switchAudioZoneToConfig_whenServiceThrowsRemoteException()995     public void switchAudioZoneToConfig_whenServiceThrowsRemoteException() throws Exception {
996         doThrow(mRemoteException).when(mServiceMock).switchZoneToConfig(any(
997                 CarAudioZoneConfigInfo.class), any(ISwitchAudioZoneConfigCallback.class));
998 
999         mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, DIRECT_EXECUTOR,
1000                 mSwitchCallbackMock);
1001 
1002         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
1003     }
1004 
1005     @Test
setAudioZoneConfigsChangeCallback_withNullExecutor_fails()1006     public void setAudioZoneConfigsChangeCallback_withNullExecutor_fails() {
1007         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1008                 mCarAudioManager.setAudioZoneConfigsChangeCallback(/* executor= */ null,
1009                         mTestConfigCallback));
1010 
1011         expectWithMessage("Null audio zone config callback executor exception").that(thrown)
1012                 .hasMessageThat().contains("Executor");
1013     }
1014 
1015     @Test
setAudioZoneConfigsChangeCallback_withNullCallback_fails()1016     public void setAudioZoneConfigsChangeCallback_withNullCallback_fails() {
1017         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1018                 mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR,
1019                         /* callback= */ null));
1020 
1021         expectWithMessage("Null audio zone config callback exception").that(thrown)
1022                 .hasMessageThat().contains("Audio zone configs change callback");
1023     }
1024 
1025     @Test
setAudioZoneConfigsChangeCallback()1026     public void setAudioZoneConfigsChangeCallback() throws Exception {
1027         boolean registered = mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR,
1028                 mTestConfigCallback);
1029 
1030         expectWithMessage("Audio zone configs callback registered status").that(registered)
1031                 .isTrue();
1032         verify(mServiceMock).registerAudioZoneConfigsChangeCallback(any());
1033     }
1034 
1035     @Test
setAudioZoneConfigsChangeCallback_multipleTimes_fails()1036     public void setAudioZoneConfigsChangeCallback_multipleTimes_fails() {
1037         mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback);
1038 
1039         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
1040                 mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR,
1041                         mTestConfigCallback));
1042 
1043         expectWithMessage("Audio zone configs callback re-registered exception").that(thrown)
1044                 .hasMessageThat().contains("already set");
1045     }
1046 
1047     @Test
onAudioZoneConfigurationsChanged_onRegisteredCallback()1048     public void onAudioZoneConfigurationsChanged_onRegisteredCallback() throws Exception {
1049         mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR,
1050                 mTestConfigCallback);
1051         ArgumentCaptor<IAudioZoneConfigurationsChangeCallback> captor =
1052                 ArgumentCaptor.forClass(IAudioZoneConfigurationsChangeCallback.class);
1053         verify(mServiceMock).registerAudioZoneConfigsChangeCallback(captor.capture());
1054         IAudioZoneConfigurationsChangeCallback remoteCallback = captor.getValue();
1055 
1056         remoteCallback.onAudioZoneConfigurationsChanged(
1057                 List.of(mZoneConfigInfoMock1, mZoneConfigInfoMock2), CONFIG_STATUS_CHANGED);
1058 
1059         mTestConfigCallback.waitForCallback();
1060         expectWithMessage("Triggered callback configs").that(mTestConfigCallback.mInfos)
1061                 .containsExactly(mZoneConfigInfoMock1, mZoneConfigInfoMock2);
1062         expectWithMessage("Triggered callback status").that(mTestConfigCallback.mStatus)
1063                 .isEqualTo(CONFIG_STATUS_CHANGED);
1064     }
1065 
1066     @Test
clearAudioZoneConfigsCallback()1067     public void clearAudioZoneConfigsCallback() throws Exception {
1068         mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR,
1069                 mTestConfigCallback);
1070 
1071         mCarAudioManager.clearAudioZoneConfigsCallback();
1072 
1073         verify(mServiceMock).unregisterAudioZoneConfigsChangeCallback(any());
1074     }
1075 
1076     @Test
clearAudioZoneConfigsCallback_withNoRegisteredCallback()1077     public void clearAudioZoneConfigsCallback_withNoRegisteredCallback() throws Exception {
1078         mCarAudioManager.clearAudioZoneConfigsCallback();
1079 
1080         verify(mServiceMock, never()).unregisterAudioZoneConfigsChangeCallback(any());
1081     }
1082 
1083     @Test
setAudioZoneConfigsChangeCallback_withServiceException_fails()1084     public void setAudioZoneConfigsChangeCallback_withServiceException_fails() throws Exception {
1085         RemoteException remoteException = new RemoteException("Register failed!");
1086         when(mServiceMock.registerAudioZoneConfigsChangeCallback(any())).thenThrow(remoteException);
1087 
1088         boolean registered = mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR,
1089                 mTestConfigCallback);
1090 
1091         expectWithMessage("Audio zone configs callback registered status after service exception")
1092                 .that(registered).isFalse();
1093     }
1094 
1095     @Test
setPrimaryZoneMediaAudioRequestCallback()1096     public void setPrimaryZoneMediaAudioRequestCallback() throws Exception {
1097         when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true);
1098 
1099         expectWithMessage("Status for setting primary zone media audio request callback")
1100                 .that(mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1101                         mPrimaryZoneMediaAudioRequestCallbackMock)).isTrue();
1102     }
1103 
1104     @Test
setPrimaryZoneMediaAudioRequestCallback_fails()1105     public void setPrimaryZoneMediaAudioRequestCallback_fails() throws Exception {
1106         when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(false);
1107         mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1108                 mPrimaryZoneMediaAudioRequestCallbackMock);
1109 
1110         expectWithMessage("Failure for setting primary zone media audio request callback")
1111                 .that(mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1112                         mPrimaryZoneMediaAudioRequestCallbackMock)).isFalse();
1113     }
1114 
1115     @Test
setPrimaryZoneMediaAudioRequestCallback_withNullExecutor_fails()1116     public void setPrimaryZoneMediaAudioRequestCallback_withNullExecutor_fails() {
1117         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1118                 mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(/* executor= */ null,
1119                         mPrimaryZoneMediaAudioRequestCallbackMock));
1120 
1121         expectWithMessage("Exception for setting primary zone media audio request callback with"
1122                 + "null executor").that(thrown).hasMessageThat()
1123                 .contains("Executor can not be null");
1124     }
1125 
1126     @Test
setPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails()1127     public void setPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails() {
1128         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1129                 mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1130                         /* callback= */ null));
1131 
1132         expectWithMessage("Exception for setting null primary zone media audio request callback")
1133                 .that(thrown).hasMessageThat()
1134                 .contains("Audio media request callback can not be null");
1135     }
1136 
1137     @Test
setPrimaryZoneMediaAudioRequestCallback_forMultipleTimes_fails()1138     public void setPrimaryZoneMediaAudioRequestCallback_forMultipleTimes_fails() throws Exception {
1139         when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true);
1140         mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1141                 mPrimaryZoneMediaAudioRequestCallbackMock);
1142 
1143         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
1144                 mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1145                         mock(PrimaryZoneMediaAudioRequestCallback.class)));
1146 
1147         expectWithMessage("Exception for setting primary zone media audio request callback"
1148                 + " for multiple times").that(thrown).hasMessageThat()
1149                 .contains("Primary zone media audio request is already set");
1150     }
1151 
1152     @Test
setPrimaryZoneMediaAudioRequestCallback_afterClearingPreviousCallbacks()1153     public void setPrimaryZoneMediaAudioRequestCallback_afterClearingPreviousCallbacks()
1154             throws Exception {
1155         when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true);
1156         mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1157                 mPrimaryZoneMediaAudioRequestCallbackMock);
1158         mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
1159 
1160         expectWithMessage("Status for setting primary zone media audio request callback"
1161                 + " again after clearing previous callbacks").that(mCarAudioManager
1162                 .setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1163                         mock(PrimaryZoneMediaAudioRequestCallback.class))).isTrue();
1164     }
1165 
1166     @Test
setPrimaryZoneMediaAudioRequestCallback_whenServiceThrowsRemoteException()1167     public void setPrimaryZoneMediaAudioRequestCallback_whenServiceThrowsRemoteException()
1168             throws Exception {
1169         doThrow(mRemoteException).when(mServiceMock)
1170                 .registerPrimaryZoneMediaAudioRequestCallback(any());
1171 
1172         expectWithMessage("Status for setting primary zone media audio request callback "
1173                 + "when service throws remote exception").that(mCarAudioManager
1174                 .setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR,
1175                         mPrimaryZoneMediaAudioRequestCallbackMock)).isFalse();
1176     }
1177 
1178     @Test
clearPrimaryZoneMediaAudioRequestCallback()1179     public void clearPrimaryZoneMediaAudioRequestCallback() throws Exception {
1180         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1181                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1182                         mPrimaryZoneMediaAudioRequestCallbackMock);
1183 
1184         mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
1185 
1186         verify(mServiceMock).unregisterPrimaryZoneMediaAudioRequestCallback(serviceCallback);
1187     }
1188 
1189     @Test
clearPrimaryZoneMediaAudioRequestCallback_beforeRegisteringCallback()1190     public void clearPrimaryZoneMediaAudioRequestCallback_beforeRegisteringCallback()
1191             throws Exception {
1192         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1193                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1194                         mPrimaryZoneMediaAudioRequestCallbackMock);
1195         mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
1196         verify(mServiceMock).unregisterPrimaryZoneMediaAudioRequestCallback(serviceCallback);
1197         clearInvocations(mServiceMock);
1198 
1199         mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
1200 
1201         verify(mServiceMock, never()).unregisterPrimaryZoneMediaAudioRequestCallback(
1202                 serviceCallback);
1203     }
1204 
1205     @Test
clearPrimaryZoneMediaAudioRequestCallback_whenServiceThrowsRemoteException()1206     public void clearPrimaryZoneMediaAudioRequestCallback_whenServiceThrowsRemoteException()
1207             throws Exception {
1208         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1209                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1210                         mPrimaryZoneMediaAudioRequestCallbackMock);
1211         doThrow(mRemoteException).when(mServiceMock)
1212                 .unregisterPrimaryZoneMediaAudioRequestCallback(serviceCallback);
1213 
1214         mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
1215 
1216         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
1217     }
1218 
1219     @Test
onRequestMediaOnPrimaryZone_forIPrimaryZoneMediaAudioRequestCallback()1220     public void onRequestMediaOnPrimaryZone_forIPrimaryZoneMediaAudioRequestCallback()
1221             throws Exception {
1222         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1223                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1224                         mPrimaryZoneMediaAudioRequestCallbackMock);
1225 
1226         serviceCallback.onRequestMediaOnPrimaryZone(mOccupantZoneInfoMock, TEST_REQUEST_ID);
1227 
1228         verify(mPrimaryZoneMediaAudioRequestCallbackMock).onRequestMediaOnPrimaryZone(
1229                 mOccupantZoneInfoMock, TEST_REQUEST_ID);
1230     }
1231 
1232     @Test
onRequestMediaOnPrimaryZone_afterClearPrimaryZoneMediaAudioRequestCallback()1233     public void onRequestMediaOnPrimaryZone_afterClearPrimaryZoneMediaAudioRequestCallback()
1234             throws Exception {
1235         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1236                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1237                         mPrimaryZoneMediaAudioRequestCallbackMock);
1238         mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
1239 
1240         serviceCallback.onRequestMediaOnPrimaryZone(mOccupantZoneInfoMock, TEST_REQUEST_ID);
1241 
1242         verify(mPrimaryZoneMediaAudioRequestCallbackMock, never()).onRequestMediaOnPrimaryZone(
1243                 mOccupantZoneInfoMock, TEST_REQUEST_ID);
1244     }
1245 
1246     @Test
onMediaAudioRequestStatusChanged_forIPrimaryZoneMediaAudioRequestCallback()1247     public void onMediaAudioRequestStatusChanged_forIPrimaryZoneMediaAudioRequestCallback()
1248             throws Exception {
1249         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1250                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1251                         mPrimaryZoneMediaAudioRequestCallbackMock);
1252 
1253         serviceCallback.onMediaAudioRequestStatusChanged(mOccupantZoneInfoMock, TEST_REQUEST_ID,
1254                 CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
1255 
1256         verify(mPrimaryZoneMediaAudioRequestCallbackMock).onMediaAudioRequestStatusChanged(
1257                 mOccupantZoneInfoMock, TEST_REQUEST_ID,
1258                 CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
1259     }
1260 
1261     @Test
requestMediaAudioOnPrimaryZone()1262     public void requestMediaAudioOnPrimaryZone() throws Exception {
1263         int expectedRequestStatus = CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED;
1264         doAnswer(invocation -> {
1265             IMediaAudioRequestStatusCallback callback = invocation.getArgument(0);
1266             OccupantZoneInfo info = invocation.getArgument(1);
1267             callback.onMediaAudioRequestStatusChanged(info, TEST_REQUEST_ID, expectedRequestStatus);
1268             return TEST_REQUEST_ID;
1269         }).when(mServiceMock).requestMediaAudioOnPrimaryZone(any(
1270                 IMediaAudioRequestStatusCallback.class), any(OccupantZoneInfo.class));
1271 
1272         long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock,
1273                 DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock);
1274 
1275         expectWithMessage("Request id for media audio on primary zone").that(requestId)
1276                 .isEqualTo(TEST_REQUEST_ID);
1277         verify(mMediaAudioRequestStatusCallbackMock).onMediaAudioRequestStatusChanged(
1278                 mOccupantZoneInfoMock, TEST_REQUEST_ID, expectedRequestStatus);
1279     }
1280 
1281     @Test
requestMediaAudioOnPrimaryZone_withNullOccupantZoneInfo()1282     public void requestMediaAudioOnPrimaryZone_withNullOccupantZoneInfo() {
1283         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1284                 mCarAudioManager.requestMediaAudioOnPrimaryZone(/* info= */ null,
1285                         DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock));
1286 
1287         expectWithMessage("Exception for requesting media audio on primary zone with null info")
1288                 .that(thrown).hasMessageThat().contains("Occupant zone info can not be null");
1289     }
1290 
1291     @Test
requestMediaAudioOnPrimaryZone_withNullExecutor()1292     public void requestMediaAudioOnPrimaryZone_withNullExecutor() {
1293         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1294                 mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock,
1295                         /* executor= */ null, mMediaAudioRequestStatusCallbackMock));
1296 
1297         expectWithMessage("Exception for requesting media audio on primary zone with null executor")
1298                 .that(thrown).hasMessageThat().contains("Executor can not be null");
1299     }
1300 
1301     @Test
requestMediaAudioOnPrimaryZone_withNullCallback()1302     public void requestMediaAudioOnPrimaryZone_withNullCallback() {
1303         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1304                 mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock,
1305                         DIRECT_EXECUTOR, /* callback= */ null));
1306 
1307         expectWithMessage("Exception for requesting media audio on primary zone with "
1308                 + "null callback").that(thrown).hasMessageThat()
1309                 .contains("Media audio request status callback can not be null");
1310     }
1311 
1312     @Test
requestMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsInvalidId()1313     public void requestMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsInvalidId()
1314             throws Exception {
1315         doThrow(mRemoteException).when(mServiceMock).requestMediaAudioOnPrimaryZone(any(
1316                 IMediaAudioRequestStatusCallback.class), any(OccupantZoneInfo.class));
1317 
1318         expectWithMessage("Request id for media audio on primary zone when service throws "
1319                 + "remote exception").that(mCarAudioManager.requestMediaAudioOnPrimaryZone(
1320                         mOccupantZoneInfoMock, DIRECT_EXECUTOR,
1321                 mMediaAudioRequestStatusCallbackMock)).isEqualTo(
1322                         CarAudioManager.INVALID_REQUEST_ID);
1323     }
1324 
1325     @Test
cancelMediaAudioOnPrimaryZone()1326     public void cancelMediaAudioOnPrimaryZone() throws Exception {
1327         when(mServiceMock.requestMediaAudioOnPrimaryZone(any(), any())).thenReturn(TEST_REQUEST_ID);
1328         long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock,
1329                 DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock);
1330         when(mServiceMock.cancelMediaAudioOnPrimaryZone(requestId)).thenReturn(true);
1331 
1332         expectWithMessage("Status for canceling pending media audio on primary zone")
1333                 .that(mCarAudioManager.cancelMediaAudioOnPrimaryZone(requestId)).isTrue();
1334     }
1335 
1336     @Test
cancelMediaAudioOnPrimaryZone_withInvalidId()1337     public void cancelMediaAudioOnPrimaryZone_withInvalidId() throws Exception {
1338         when(mServiceMock.requestMediaAudioOnPrimaryZone(any(), any()))
1339                 .thenReturn(CarAudioManager.INVALID_REQUEST_ID);
1340         long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock,
1341                 DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock);
1342 
1343         expectWithMessage("Status for canceling media audio request with invalid id")
1344                 .that(mCarAudioManager.cancelMediaAudioOnPrimaryZone(requestId)).isTrue();
1345         verify(mServiceMock, never()).cancelMediaAudioOnPrimaryZone(requestId);
1346     }
1347 
1348     @Test
cancelMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()1349     public void cancelMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()
1350             throws Exception {
1351         when(mServiceMock.requestMediaAudioOnPrimaryZone(any(), any())).thenReturn(TEST_REQUEST_ID);
1352         long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock,
1353                 DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock);
1354         doThrow(mRemoteException).when(mServiceMock).cancelMediaAudioOnPrimaryZone(requestId);
1355 
1356         expectWithMessage("Status for canceling pending media audio on primary zone "
1357                 + "throws remote exception").that(mCarAudioManager.cancelMediaAudioOnPrimaryZone(
1358                         requestId)).isFalse();
1359     }
1360 
1361     @Test
allowMediaAudioOnPrimaryZone()1362     public void allowMediaAudioOnPrimaryZone() throws Exception {
1363         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1364                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1365                         mPrimaryZoneMediaAudioRequestCallbackMock);
1366         when(mServiceMock.allowMediaAudioOnPrimaryZone(serviceCallback.asBinder(), TEST_REQUEST_ID,
1367                 /* allow= */ true)).thenReturn(true);
1368 
1369         expectWithMessage("Status for allowing media audio on primary zone")
1370                 .that(mCarAudioManager.allowMediaAudioOnPrimaryZone(TEST_REQUEST_ID,
1371                         /* allow= */ true)).isTrue();
1372     }
1373 
1374     @Test
allowMediaAudioOnPrimaryZone_beforeSettingCallback()1375     public void allowMediaAudioOnPrimaryZone_beforeSettingCallback() {
1376         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
1377                 mCarAudioManager.allowMediaAudioOnPrimaryZone(TEST_REQUEST_ID, /* allow= */ true));
1378 
1379         expectWithMessage("Exception for allowing media audio on primary zone before "
1380                 + "setting primary zone media audio request callback").that(thrown)
1381                 .hasMessageThat().contains("Primary zone media audio request callback");
1382     }
1383 
1384     @Test
allowMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()1385     public void allowMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()
1386             throws Exception {
1387         IPrimaryZoneMediaAudioRequestCallback serviceCallback =
1388                 getPrimaryZoneMediaAudioRequestCallbackImpl(
1389                         mPrimaryZoneMediaAudioRequestCallbackMock);
1390         doThrow(mRemoteException).when(mServiceMock).allowMediaAudioOnPrimaryZone(
1391                 serviceCallback.asBinder(), TEST_REQUEST_ID, /* allow= */ true);
1392 
1393         expectWithMessage("Status for allowing media audio on primary zone when service throws"
1394                 + "remote exception").that(mCarAudioManager.allowMediaAudioOnPrimaryZone(
1395                         TEST_REQUEST_ID, /* allow= */ true)).isFalse();
1396     }
1397 
1398     @Test
resetMediaAudioOnPrimaryZone()1399     public void resetMediaAudioOnPrimaryZone() throws Exception {
1400         when(mServiceMock.resetMediaAudioOnPrimaryZone(mOccupantZoneInfoMock)).thenReturn(true);
1401 
1402         expectWithMessage("Status for resetting media audio on primary zone")
1403                 .that(mCarAudioManager.resetMediaAudioOnPrimaryZone(mOccupantZoneInfoMock))
1404                 .isTrue();
1405     }
1406 
1407     @Test
resetMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()1408     public void resetMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()
1409             throws Exception {
1410         doThrow(mRemoteException).when(mServiceMock).resetMediaAudioOnPrimaryZone(
1411                 mOccupantZoneInfoMock);
1412 
1413         expectWithMessage("Status for resetting media audio on primary zone when service "
1414                 + "throws remote exception").that(mCarAudioManager.resetMediaAudioOnPrimaryZone(
1415                         mOccupantZoneInfoMock)).isFalse();
1416     }
1417 
1418     @Test
isMediaAudioAllowedInPrimaryZone()1419     public void isMediaAudioAllowedInPrimaryZone() throws Exception {
1420         when(mServiceMock.isMediaAudioAllowedInPrimaryZone(mOccupantZoneInfoMock)).thenReturn(true);
1421 
1422         expectWithMessage("Media audio allowed in primary zone").that(mCarAudioManager
1423                 .isMediaAudioAllowedInPrimaryZone(mOccupantZoneInfoMock)).isTrue();
1424     }
1425 
1426     @Test
isMediaAudioAllowedInPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()1427     public void isMediaAudioAllowedInPrimaryZone_whenServiceThrowsRemoteException_returnsFalse()
1428             throws Exception {
1429         doThrow(mRemoteException).when(mServiceMock).isMediaAudioAllowedInPrimaryZone(
1430                 mOccupantZoneInfoMock);
1431 
1432         expectWithMessage("Media audio allowed in primary zone when service "
1433                 + "throws remote exception").that(mCarAudioManager.isMediaAudioAllowedInPrimaryZone(
1434                         mOccupantZoneInfoMock)).isFalse();
1435     }
1436 
1437     @Test
setAudioZoneMirrorStatusCallback_succeeds()1438     public void setAudioZoneMirrorStatusCallback_succeeds() throws Exception {
1439         when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true);
1440 
1441         expectWithMessage("Status for setting audio zone mirror status callback")
1442                 .that(mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1443                         mAudioZonesMirrorStatusCallback)).isTrue();
1444     }
1445 
1446     @Test
setAudioZoneMirrorStatusCallback_withServiceReturnsFalse_fails()1447     public void setAudioZoneMirrorStatusCallback_withServiceReturnsFalse_fails() throws Exception {
1448         when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(false);
1449 
1450         expectWithMessage("Status for setting audio zone mirror status callback")
1451                 .that(mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1452                         mAudioZonesMirrorStatusCallback)).isFalse();
1453     }
1454 
1455     @Test
setAudioZoneMirrorStatusCallback_forMultipleTimes_fails()1456     public void setAudioZoneMirrorStatusCallback_forMultipleTimes_fails() throws Exception {
1457         when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true);
1458         mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1459                 mAudioZonesMirrorStatusCallback);
1460 
1461         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
1462                 mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1463                         mock(AudioZonesMirrorStatusCallback.class)));
1464 
1465         expectWithMessage("Exception for setting audio zone mirror status callback multiple times")
1466                 .that(thrown).hasMessageThat().contains("callback is already set");
1467     }
1468 
1469     @Test
setAudioZoneMirrorStatusCallback_withNullExecutor()1470     public void setAudioZoneMirrorStatusCallback_withNullExecutor() {
1471         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1472                 mCarAudioManager.setAudioZoneMirrorStatusCallback(/* executor= */ null,
1473                         mAudioZonesMirrorStatusCallback));
1474 
1475         expectWithMessage("Exception for setting audio zone mirror status callback with"
1476                 + " null executor").that(thrown).hasMessageThat()
1477                 .contains("Executor can not be null");
1478     }
1479 
1480     @Test
setAudioZoneMirrorStatusCallback_withNullCallback()1481     public void setAudioZoneMirrorStatusCallback_withNullCallback() {
1482         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1483                 mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1484                         /* callback= */ null));
1485 
1486         expectWithMessage("Exception for setting audio zone mirror status callback with "
1487                 + "null callback").that(thrown).hasMessageThat()
1488                 .contains("Audio zones mirror status callback can not be null");
1489     }
1490 
1491     @Test
setAudioZoneMirrorStatusCallback_whenServiceThrowsRemoteException_returnsFalse()1492     public void setAudioZoneMirrorStatusCallback_whenServiceThrowsRemoteException_returnsFalse()
1493             throws Exception {
1494         doThrow(mRemoteException).when(mServiceMock).registerAudioZonesMirrorStatusCallback(
1495                 any());
1496 
1497         expectWithMessage("Status for setting audio zone mirror status callback when service "
1498                 + "throws remote exception").that(mCarAudioManager.setAudioZoneMirrorStatusCallback(
1499                         DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback)).isFalse();
1500     }
1501 
1502     @Test
clearAudioZonesMirrorStatusCallback()1503     public void clearAudioZonesMirrorStatusCallback() throws Exception {
1504         when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true);
1505         ArgumentCaptor<IAudioZonesMirrorStatusCallback> captor = ArgumentCaptor.forClass(
1506                 IAudioZonesMirrorStatusCallback.class);
1507         mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1508                 mAudioZonesMirrorStatusCallback);
1509         verify(mServiceMock).registerAudioZonesMirrorStatusCallback(captor.capture());
1510 
1511         mCarAudioManager.clearAudioZonesMirrorStatusCallback();
1512 
1513         verify(mServiceMock).unregisterAudioZonesMirrorStatusCallback(captor.getValue());
1514     }
1515 
1516     @Test
clearAudioZonesMirrorStatusCallback_withoutCallbackSet()1517     public void clearAudioZonesMirrorStatusCallback_withoutCallbackSet() throws Exception {
1518         mCarAudioManager.clearAudioZonesMirrorStatusCallback();
1519 
1520         verify(mServiceMock, never()).unregisterAudioZonesMirrorStatusCallback(any());
1521     }
1522 
1523     @Test
clearAudioZonesMirrorStatusCallback_whenServiceThrowsRemoteException()1524     public void clearAudioZonesMirrorStatusCallback_whenServiceThrowsRemoteException()
1525             throws Exception {
1526         when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true);
1527         ArgumentCaptor<IAudioZonesMirrorStatusCallback> captor = ArgumentCaptor.forClass(
1528                 IAudioZonesMirrorStatusCallback.class);
1529         mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1530                 mAudioZonesMirrorStatusCallback);
1531         verify(mServiceMock).registerAudioZonesMirrorStatusCallback(captor.capture());
1532         doThrow(mRemoteException).when(mServiceMock).unregisterAudioZonesMirrorStatusCallback(
1533                 captor.getValue());
1534 
1535         mCarAudioManager.clearAudioZonesMirrorStatusCallback();
1536 
1537         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
1538     }
1539 
1540     @Test
canEnableAudioMirror()1541     public void canEnableAudioMirror() throws Exception {
1542         when(mServiceMock.canEnableAudioMirror()).thenReturn(
1543                 CarAudioManager.AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES);
1544 
1545         expectWithMessage("Audio mirror status").that(mCarAudioManager.canEnableAudioMirror())
1546                 .isEqualTo(CarAudioManager.AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES);
1547     }
1548 
1549     @Test
canEnableAudioMirror_whenServiceThrowsRemoteException_fails()1550     public void canEnableAudioMirror_whenServiceThrowsRemoteException_fails() throws Exception {
1551         doThrow(mRemoteException).when(mServiceMock).canEnableAudioMirror();
1552 
1553         expectWithMessage("Audio mirror status when service throws remote exception")
1554                 .that(mCarAudioManager.canEnableAudioMirror()).isEqualTo(
1555                         CarAudioManager.AUDIO_MIRROR_INTERNAL_ERROR);
1556     }
1557 
1558     @Test
enableMirrorForAudioZones()1559     public void enableMirrorForAudioZones() throws Exception {
1560         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
1561         IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper();
1562         doAnswer(invocation -> {
1563             int[] zones = invocation.getArgument(0);
1564             callback.onAudioZonesMirrorStatusChanged(zones,
1565                     CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
1566             return TEST_REQUEST_ID;
1567         }).when(mServiceMock).enableMirrorForAudioZones(any(int[].class));
1568 
1569         long requestId = mCarAudioManager.enableMirrorForAudioZones(zonesToMirrorList);
1570 
1571         expectWithMessage("Request id for enabling mirror for audio zones").that(requestId)
1572                 .isEqualTo(TEST_REQUEST_ID);
1573         verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList,
1574                 CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
1575     }
1576 
1577     @Test
enableMirrorForAudioZones_withNullZoneList_fails()1578     public void enableMirrorForAudioZones_withNullZoneList_fails() {
1579         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1580                 mCarAudioManager.enableMirrorForAudioZones(/* audioZonesToMirror= */ null));
1581 
1582         expectWithMessage("Exception for enabling mirror on null zone list").that(thrown)
1583                 .hasMessageThat().contains("Audio zones to mirror should not be null");
1584     }
1585 
1586     @Test
enableMirrorForAudioZones_whenServiceThrowsRemoteException_returnsInvalidId()1587     public void enableMirrorForAudioZones_whenServiceThrowsRemoteException_returnsInvalidId()
1588             throws Exception {
1589         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
1590         getAudioZonesMirrorStatusCallbackWrapper();
1591         doThrow(mRemoteException).when(mServiceMock).enableMirrorForAudioZones(any(
1592                 int[].class));
1593 
1594         expectWithMessage("Request id for enabling mirror for audio zones when service throws "
1595                 + "remote exception").that(mCarAudioManager.enableMirrorForAudioZones(
1596                         zonesToMirrorList)).isEqualTo(CarAudioManager.INVALID_REQUEST_ID);
1597     }
1598 
1599     @Test
extendAudioMirrorRequest()1600     public void extendAudioMirrorRequest() throws Exception {
1601         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID);
1602         IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper();
1603         doAnswer(invocation -> {
1604             int[] zones = invocation.getArgument(1);
1605             callback.onAudioZonesMirrorStatusChanged(zones,
1606                     CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
1607             return null;
1608         }).when(mServiceMock).extendAudioMirrorRequest(eq(TEST_REQUEST_ID), any(int[].class));
1609 
1610         mCarAudioManager.extendAudioMirrorRequest(TEST_REQUEST_ID, zonesToMirrorList);
1611 
1612         verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList,
1613                 CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
1614     }
1615 
1616     @Test
extendAudioMirrorRequest_withNullZoneList_fails()1617     public void extendAudioMirrorRequest_withNullZoneList_fails() {
1618         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1619                 mCarAudioManager.extendAudioMirrorRequest(TEST_REQUEST_ID,
1620                         /* audioZonesToMirror= */ null));
1621 
1622         expectWithMessage("Exception for extending mirror on null zone list").that(thrown)
1623                 .hasMessageThat().contains("Audio zones to mirror should not be null");
1624     }
1625 
1626     @Test
extendAudioMirrorRequest_whenServiceThrowsRemoteException()1627     public void extendAudioMirrorRequest_whenServiceThrowsRemoteException() throws Exception {
1628         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID);
1629         getAudioZonesMirrorStatusCallbackWrapper();
1630         doThrow(mRemoteException).when(mServiceMock).extendAudioMirrorRequest(
1631                 eq(TEST_REQUEST_ID), any(int[].class));
1632 
1633         mCarAudioManager.extendAudioMirrorRequest(TEST_REQUEST_ID, zonesToMirrorList);
1634 
1635         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
1636     }
1637 
1638     @Test
disableAudioMirror()1639     public void disableAudioMirror() throws Exception {
1640         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
1641         IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper();
1642         long requestId = mCarAudioManager.enableMirrorForAudioZones(zonesToMirrorList);
1643         doAnswer(invocation -> {
1644             callback.onAudioZonesMirrorStatusChanged(new int[]{TEST_REAR_LEFT_ZONE_ID,
1645                     TEST_REAR_RIGHT_ZONE_ID}, CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
1646             return null;
1647         }).when(mServiceMock).disableAudioMirror(requestId);
1648 
1649         mCarAudioManager.disableAudioMirror(requestId);
1650 
1651         verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList,
1652                 CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
1653     }
1654 
1655     @Test
disableAudioMirror_whenServiceThrowsRemoteException()1656     public void disableAudioMirror_whenServiceThrowsRemoteException() throws Exception {
1657         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
1658         getAudioZonesMirrorStatusCallbackWrapper();
1659         long requestId = mCarAudioManager.enableMirrorForAudioZones(zonesToMirrorList);
1660         doThrow(mRemoteException).when(mServiceMock).disableAudioMirror(requestId);
1661 
1662         mCarAudioManager.disableAudioMirror(requestId);
1663 
1664         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
1665     }
1666 
1667     @Test
disableAudioMirrorForZone()1668     public void disableAudioMirrorForZone() throws Exception {
1669         List<Integer> zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
1670         IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper();
1671         doAnswer(invocation -> {
1672             callback.onAudioZonesMirrorStatusChanged(new int[]{TEST_REAR_LEFT_ZONE_ID,
1673                     TEST_REAR_RIGHT_ZONE_ID}, CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
1674             return null;
1675         }).when(mServiceMock).disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID);
1676 
1677         mCarAudioManager.disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID);
1678 
1679         verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList,
1680                 CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
1681     }
1682 
1683     @Test
disableAudioMirrorForZone_whenServiceThrowsRemoteException()1684     public void disableAudioMirrorForZone_whenServiceThrowsRemoteException() throws Exception {
1685         getAudioZonesMirrorStatusCallbackWrapper();
1686         doThrow(mRemoteException).when(mServiceMock).disableAudioMirrorForZone(
1687                 TEST_REAR_RIGHT_ZONE_ID);
1688 
1689         mCarAudioManager.disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID);
1690 
1691         verify(mCar).handleRemoteExceptionFromCarService(mRemoteException);
1692     }
1693 
1694     @Test
getMirrorAudioZonesForAudioZone()1695     public void getMirrorAudioZonesForAudioZone() throws Exception {
1696         when(mServiceMock.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID)).thenReturn(
1697                 new int[]{TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID});
1698 
1699         expectWithMessage("Mirror zones for rear right zone").that(mCarAudioManager
1700                 .getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID)).containsExactly(
1701                 TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID).inOrder();
1702     }
1703 
1704     @Test
getMirrorAudioZonesForAudioZone_whenServiceThrowsRemoteException_returnsEmptyList()1705     public void getMirrorAudioZonesForAudioZone_whenServiceThrowsRemoteException_returnsEmptyList()
1706             throws Exception {
1707         doThrow(mRemoteException).when(mServiceMock).getMirrorAudioZonesForAudioZone(
1708                 TEST_REAR_RIGHT_ZONE_ID);
1709 
1710         expectWithMessage("Mirror zones for rear right zone when service throws remote exception")
1711                 .that(mCarAudioManager.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID))
1712                 .isEmpty();
1713     }
1714 
1715     @Test
getMirrorAudioZonesForMirrorRequest()1716     public void getMirrorAudioZonesForMirrorRequest() throws Exception {
1717         when(mServiceMock.getMirrorAudioZonesForMirrorRequest(TEST_REQUEST_ID)).thenReturn(
1718                 new int[]{TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID});
1719 
1720         expectWithMessage("Mirror zones for request id").that(mCarAudioManager
1721                 .getMirrorAudioZonesForMirrorRequest(TEST_REQUEST_ID)).containsExactly(
1722                 TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID).inOrder();
1723     }
1724 
1725     @Test
getMirrorAudioZonesForMirrorRequest_whenServiceThrowsRemoteException()1726     public void getMirrorAudioZonesForMirrorRequest_whenServiceThrowsRemoteException()
1727             throws Exception {
1728         doThrow(mRemoteException).when(mServiceMock).getMirrorAudioZonesForMirrorRequest(
1729                 TEST_REQUEST_ID);
1730 
1731         expectWithMessage("Mirror zones for request id when service throws remote exception")
1732                 .that(mCarAudioManager.getMirrorAudioZonesForMirrorRequest(TEST_REQUEST_ID))
1733                 .isEmpty();
1734     }
1735 
1736     @Test
getOutputDeviceForUsage()1737     public void getOutputDeviceForUsage() throws Exception {
1738         when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(
1739                 new AudioDeviceInfo[]{TEST_MEDIA_OUTPUT_DEVICE, TEST_NAV_OUTPUT_DEVICE});
1740         when(mServiceMock.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1741                 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)).thenReturn(NAVIGATION_TEST_DEVICE);
1742 
1743         AudioDeviceInfo outputDevice = mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE,
1744                 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
1745 
1746         expectWithMessage("Navigation output device").that(outputDevice)
1747                 .isEqualTo(TEST_NAV_OUTPUT_DEVICE);
1748     }
1749 
1750     @Test
getOutputDeviceForUsage_withDeviceAddressNotFound_returnsNull()1751     public void getOutputDeviceForUsage_withDeviceAddressNotFound_returnsNull() throws Exception {
1752         String invalidDeviceAddress = "invalid_bus";
1753         when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(
1754                 new AudioDeviceInfo[]{TEST_MEDIA_OUTPUT_DEVICE, TEST_NAV_OUTPUT_DEVICE});
1755         when(mServiceMock.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1756                 USAGE_GAME)).thenReturn(invalidDeviceAddress);
1757 
1758         expectWithMessage("Null output device").that(mCarAudioManager.getOutputDeviceForUsage(
1759                 PRIMARY_AUDIO_ZONE, USAGE_GAME)).isNull();
1760     }
1761 
1762     @Test
getOutputDeviceForUsage_whenServiceThrowsRemoteException_returnsNull()1763     public void getOutputDeviceForUsage_whenServiceThrowsRemoteException_returnsNull()
1764             throws Exception {
1765         doThrow(mRemoteException).when(mServiceMock).getOutputDeviceAddressForUsage(
1766                 PRIMARY_AUDIO_ZONE, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
1767 
1768         expectWithMessage("Navigation output device when service throws remote exception")
1769                 .that(mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE,
1770                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)).isNull();
1771     }
1772 
1773     @Test
getInputDevicesForZoneId()1774     public void getInputDevicesForZoneId() throws Exception {
1775         AudioDeviceAttributes microphoneAttr = mock(AudioDeviceAttributes.class);
1776         when(microphoneAttr.getAddress()).thenReturn(MICROPHONE_ADDRESS);
1777         AudioDeviceAttributes tunerAttr = mock(AudioDeviceAttributes.class);
1778         when(tunerAttr.getAddress()).thenReturn(FM_TUNER_ADDRESS);
1779         List<AudioDeviceAttributes> inputDevicesFromService = List.of(microphoneAttr, tunerAttr);
1780         when(mServiceMock.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE)).thenReturn(
1781                 inputDevicesFromService);
1782         when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
1783                 new AudioDeviceInfo[]{TEST_MICROPHONE_INPUT_DEVICE, TEST_TUNER_INPUT_DEVICE});
1784 
1785         List<AudioDeviceInfo> inputDevices = mCarAudioManager.getInputDevicesForZoneId(
1786                 PRIMARY_AUDIO_ZONE);
1787 
1788         expectWithMessage("Input devices for primary zone").that(inputDevices)
1789                 .containsExactly(TEST_MICROPHONE_INPUT_DEVICE, TEST_TUNER_INPUT_DEVICE);
1790     }
1791 
1792     @Test
getInputDevicesForZoneId_withInvalidDevicesInAudioManager()1793     public void getInputDevicesForZoneId_withInvalidDevicesInAudioManager() throws Exception {
1794         AudioDeviceAttributes microphoneAttr = mock(AudioDeviceAttributes.class);
1795         when(microphoneAttr.getAddress()).thenReturn(MICROPHONE_ADDRESS);
1796         when(mServiceMock.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE)).thenReturn(
1797                 List.of(microphoneAttr));
1798         when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn(
1799                 new AudioDeviceInfo[]{TEST_MEDIA_OUTPUT_DEVICE, TEST_TUNER_INPUT_DEVICE});
1800 
1801         List<AudioDeviceInfo> inputDevices = mCarAudioManager.getInputDevicesForZoneId(
1802                 PRIMARY_AUDIO_ZONE);
1803 
1804         expectWithMessage("No input devices found for primary zone").that(inputDevices).isEmpty();
1805     }
1806 
1807     @Test
getInputDevicesForZoneId_whenServiceThrowsRemoteException_returnsEmptyList()1808     public void getInputDevicesForZoneId_whenServiceThrowsRemoteException_returnsEmptyList()
1809             throws Exception {
1810         doThrow(mRemoteException).when(mServiceMock).getInputDevicesForZoneId(
1811                 PRIMARY_AUDIO_ZONE);
1812 
1813         expectWithMessage("Input devices for primary zone when service throws remote exception")
1814                 .that(mCarAudioManager.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE)).isEmpty();
1815     }
1816 
getCarVolumeCallbackImpl(CarAudioManager.CarVolumeCallback callbackMock)1817     private ICarVolumeCallback getCarVolumeCallbackImpl(CarAudioManager.CarVolumeCallback
1818             callbackMock) throws Exception {
1819         ArgumentCaptor<IBinder> captor = ArgumentCaptor.forClass(IBinder.class);
1820         mCarAudioManager.registerCarVolumeCallback(callbackMock);
1821         verify(mServiceMock).registerVolumeCallback(captor.capture());
1822         return ICarVolumeCallback.Stub.asInterface(captor.getValue());
1823     }
1824 
getCarVolumeEventCallbackImpl(CarVolumeGroupEventCallback callbackMock)1825     private ICarVolumeEventCallback getCarVolumeEventCallbackImpl(CarVolumeGroupEventCallback
1826             callbackMock) throws Exception {
1827         when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true);
1828         mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, callbackMock);
1829         ArgumentCaptor<ICarVolumeEventCallback> captor = ArgumentCaptor.forClass(
1830                 ICarVolumeEventCallback.class);
1831         verify(mServiceMock).registerCarVolumeEventCallback(captor.capture());
1832         return captor.getValue();
1833     }
1834 
getPrimaryZoneMediaAudioRequestCallbackImpl( PrimaryZoneMediaAudioRequestCallback callbackMock)1835     private IPrimaryZoneMediaAudioRequestCallback getPrimaryZoneMediaAudioRequestCallbackImpl(
1836             PrimaryZoneMediaAudioRequestCallback callbackMock) throws Exception {
1837         when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true);
1838         ArgumentCaptor<IPrimaryZoneMediaAudioRequestCallback> captor = ArgumentCaptor.forClass(
1839                 IPrimaryZoneMediaAudioRequestCallback.class);
1840         mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, callbackMock);
1841         verify(mServiceMock).registerPrimaryZoneMediaAudioRequestCallback(captor.capture());
1842         return captor.getValue();
1843     }
1844 
getAudioZonesMirrorStatusCallbackWrapper()1845     private IAudioZonesMirrorStatusCallback getAudioZonesMirrorStatusCallbackWrapper()
1846             throws Exception {
1847         ArgumentCaptor<IAudioZonesMirrorStatusCallback> captor = ArgumentCaptor.forClass(
1848                 IAudioZonesMirrorStatusCallback.class);
1849         when(mServiceMock.registerAudioZonesMirrorStatusCallback(
1850                 any(IAudioZonesMirrorStatusCallback.class))).thenReturn(true);
1851         mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR,
1852                 mAudioZonesMirrorStatusCallback);
1853         verify(mServiceMock).registerAudioZonesMirrorStatusCallback(captor.capture());
1854         return captor.getValue();
1855     }
1856 
1857     private static final class  TestAudioZoneConfigurationsChangeCallback
1858             implements AudioZoneConfigurationsChangeCallback {
1859 
1860         private static final long TEST_CALLBACK_TIMEOUT_MS = 500;
1861         private List<CarAudioZoneConfigInfo> mInfos;
1862         private int mStatus;
1863 
1864         private CountDownLatch mStatusLatch = new CountDownLatch(1);
1865         @Override
onAudioZoneConfigurationsChanged(List<CarAudioZoneConfigInfo> configs, int status)1866         public void onAudioZoneConfigurationsChanged(List<CarAudioZoneConfigInfo> configs,
1867                 int status) {
1868             mInfos = configs;
1869             mStatus = status;
1870             mStatusLatch.countDown();
1871         }
1872 
waitForCallback()1873         private void waitForCallback() throws Exception {
1874             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
1875         }
1876     }
1877 }
1878