1 /*
2  * Copyright (C) 2021 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.cts;
18 
19 import static android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME;
20 import static android.car.media.CarAudioManager.AUDIO_FEATURE_AUDIO_MIRRORING;
21 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING;
22 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_EVENTS;
23 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING;
24 import static android.car.media.CarAudioManager.AUDIO_MIRROR_CAN_ENABLE;
25 import static android.car.media.CarAudioManager.INVALID_AUDIO_ZONE;
26 import static android.car.media.CarAudioManager.INVALID_REQUEST_ID;
27 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
28 import static android.car.test.mocks.JavaMockitoHelper.await;
29 import static android.media.AudioAttributes.USAGE_MEDIA;
30 
31 import static com.google.common.truth.Truth.assertThat;
32 import static com.google.common.truth.Truth.assertWithMessage;
33 
34 import static org.junit.Assert.assertThrows;
35 import static org.junit.Assume.assumeFalse;
36 import static org.junit.Assume.assumeTrue;
37 
38 import android.app.UiAutomation;
39 import android.car.Car;
40 import android.car.CarOccupantZoneManager;
41 import android.car.CarOccupantZoneManager.OccupantZoneInfo;
42 import android.car.PlatformVersion;
43 import android.car.cts.utils.ProtoDumpUtils;
44 import android.car.feature.Flags;
45 import android.car.media.AudioZonesMirrorStatusCallback;
46 import android.car.media.CarAudioManager;
47 import android.car.media.CarAudioZoneConfigInfo;
48 import android.car.media.CarVolumeGroupInfo;
49 import android.car.media.MediaAudioRequestStatusCallback;
50 import android.car.media.PrimaryZoneMediaAudioRequestCallback;
51 import android.car.media.SwitchAudioZoneConfigCallback;
52 import android.car.test.PermissionsCheckerRule;
53 import android.car.test.PermissionsCheckerRule.EnsureHasPermission;
54 import android.car.test.util.CarAudioManagerTestUtils;
55 import android.media.AudioDeviceAttributes;
56 import android.media.AudioDeviceInfo;
57 import android.os.SystemClock;
58 import android.platform.test.annotations.AppModeFull;
59 import android.platform.test.annotations.RequiresFlagsDisabled;
60 import android.platform.test.annotations.RequiresFlagsEnabled;
61 import android.util.Log;
62 import android.util.SparseArray;
63 import android.view.KeyEvent;
64 
65 import androidx.test.ext.junit.runners.AndroidJUnit4;
66 import androidx.test.platform.app.InstrumentationRegistry;
67 
68 import com.android.car.audio.CarAudioDumpProto;
69 import com.android.car.audio.CarAudioDumpProto.CarAudioZoneConfigProto;
70 import com.android.car.audio.CarAudioDumpProto.CarAudioZoneProto;
71 import com.android.car.audio.CarAudioDumpProto.CarVolumeGroupProto;
72 import com.android.compatibility.common.util.ApiTest;
73 import com.android.compatibility.common.util.ShellUtils;
74 
75 import org.junit.After;
76 import org.junit.Before;
77 import org.junit.Rule;
78 import org.junit.Test;
79 import org.junit.runner.RunWith;
80 
81 import java.util.ArrayList;
82 import java.util.List;
83 import java.util.concurrent.CountDownLatch;
84 import java.util.concurrent.Executor;
85 import java.util.concurrent.Executors;
86 import java.util.regex.Matcher;
87 import java.util.regex.Pattern;
88 import java.util.stream.Collectors;
89 
90 @RunWith(AndroidJUnit4.class)
91 @AppModeFull(reason = "Instant Apps cannot get car related permissions")
92 public final class CarAudioManagerTest extends AbstractCarTestCase {
93 
94     private static final String TAG = CarAudioManagerTest.class.getSimpleName();
95 
96     private static final long WAIT_TIMEOUT_MS = 5_000;
97 
98     private static final Pattern ZONE_PATTERN = Pattern.compile(
99             "CarAudioZone\\(.*:(\\d?)\\) isPrimary\\? (.*?)\n.*Current Config Id: (\\d?)");
100     private static final Pattern VOLUME_GROUP_PATTERN = Pattern.compile(
101             "CarVolumeGroup\\((\\d?)\\)\n.*Name\\((.*?)\\)\n.*Zone Id\\((\\d?)\\)\n"
102                     + ".*Configuration Id\\((\\d?)\\)");
103     private static final Pattern ZONE_CONFIG_PATTERN = Pattern.compile(
104             "CarAudioZoneConfig\\((.*?):(\\d?)\\) of zone (\\d?) isDefault\\? (.*?)");
105     private static final Pattern PRIMARY_ZONE_MEDIA_REQUEST_APPROVERS_PATTERN =
106             Pattern.compile("Media request callbacks\\[(\\d+)\\]:");
107 
108     private static final int USAGE_INVALID = -1;
109     private static final int VOLUME_FLAGS = 0;
110     private static final int INVALID_VOLUME_GROUP_ID = -1;
111     private static final int LEGACY_ZONE_ID = 0;
112     private static final int LEGACY_VOLUME_GROUP_ID = 0;
113     private static final int LEGACY_GROUP_VOLUME_COUNT = 3;
114 
115     @Rule
116     public final PermissionsCheckerRule mPermissionsCheckerRule = new PermissionsCheckerRule();
117 
118     private static final UiAutomation UI_AUTOMATION =
119             InstrumentationRegistry.getInstrumentation().getUiAutomation();
120 
121     private CarAudioManager mCarAudioManager;
122     private CarAudioManagerTestUtils.SyncCarVolumeCallback mCallback;
123     private int mZoneId = LEGACY_ZONE_ID;
124     private int mConfigId = -1;
125     private int mVolumeGroupId = -1;
126     private int mVolumeGroupCount = LEGACY_GROUP_VOLUME_COUNT;
127     private CarOccupantZoneManager mCarOccupantZoneManager;
128     private TestPrimaryZoneMediaAudioRequestStatusCallback mRequestCallback;
129     private long mMediaRequestId = INVALID_REQUEST_ID;
130     private String mCarAudioServiceDump;
131     private CarAudioDumpProto mCarAudioServiceProtoDump;
132     private TestAudioZonesMirrorStatusCallback mAudioZonesMirrorCallback;
133     private long mMirrorRequestId = INVALID_REQUEST_ID;
134     private CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback mEventCallback;
135 
136     @Before
setUp()137     public void setUp() throws Exception {
138         mCarAudioManager = getCar().getCarManager(CarAudioManager.class);
139         mCarOccupantZoneManager = getCar().getCarManager(CarOccupantZoneManager.class);
140         if (Flags.carDumpToProto()) {
141             mCarAudioServiceProtoDump = CarAudioDumpProto.parseFrom(
142                     ProtoDumpUtils.executeProtoDumpShellCommand("CarAudioService"));
143         } else {
144             mCarAudioServiceDump = ShellUtils.runShellCommand(
145                     "dumpsys car_service --services CarAudioService");
146         }
147     }
148 
149     @After
cleanUp()150     public void cleanUp() {
151         if (mCallback != null) {
152             // Unregistering the last callback requires PERMISSION_CAR_CONTROL_AUDIO_VOLUME
153             runWithCarControlAudioVolumePermission(
154                     () -> mCarAudioManager.unregisterCarVolumeCallback(mCallback));
155         }
156 
157         if (mMediaRequestId != INVALID_REQUEST_ID) {
158             Log.w(TAG, "Cancelling media request " +  mMediaRequestId);
159             mCarAudioManager.cancelMediaAudioOnPrimaryZone(mMediaRequestId);
160         }
161 
162         if (mRequestCallback != null) {
163             Log.w(TAG, "Releasing media request callback");
164             mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback();
165         }
166 
167         if (mAudioZonesMirrorCallback != null) {
168             Log.i(TAG, "Releasing audio mirror request callback");
169             mCarAudioManager.clearAudioZonesMirrorStatusCallback();
170         }
171 
172         if (mMirrorRequestId != INVALID_REQUEST_ID) {
173             Log.i(TAG, "Disabling audio mirror for request: " + mMirrorRequestId);
174             mCarAudioManager.disableAudioMirror(mMirrorRequestId);
175         }
176 
177         if (mEventCallback != null && Car.getPlatformVersion().isAtLeast(
178                 PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0)) {
179             runWithCarControlAudioVolumePermission(
180                     () -> mCarAudioManager.unregisterCarVolumeGroupEventCallback(mEventCallback));
181         }
182     }
183 
184     @Test
185     @ApiTest(apis = {"android.car.media.CarAudioManager#isAudioFeatureEnabled"})
isAudioFeatureEnabled_withVolumeGroupMuteFeature_succeeds()186     public void isAudioFeatureEnabled_withVolumeGroupMuteFeature_succeeds() {
187         boolean volumeGroupMutingEnabled = mCarAudioManager.isAudioFeatureEnabled(
188                         AUDIO_FEATURE_VOLUME_GROUP_MUTING);
189 
190         assertThat(volumeGroupMutingEnabled).isAnyOf(true, false);
191     }
192 
193     @Test
194     @ApiTest(apis = {"android.car.media.CarAudioManager#isAudioFeatureEnabled"})
isAudioFeatureEnabled_withDynamicRoutingFeature_succeeds()195     public void isAudioFeatureEnabled_withDynamicRoutingFeature_succeeds() {
196         boolean dynamicRoutingEnabled = mCarAudioManager.isAudioFeatureEnabled(
197                         AUDIO_FEATURE_DYNAMIC_ROUTING);
198 
199         assertThat(dynamicRoutingEnabled).isAnyOf(true, false);
200     }
201 
202     @Test
203     @ApiTest(apis = {"android.car.media.CarAudioManager#isAudioFeatureEnabled"})
isAudioFeatureEnabled_withVolumeGroupEventsFeature_succeeds()204     public void isAudioFeatureEnabled_withVolumeGroupEventsFeature_succeeds() {
205         boolean volumeGroupEventsEnabled = mCarAudioManager.isAudioFeatureEnabled(
206                 AUDIO_FEATURE_VOLUME_GROUP_EVENTS);
207 
208         assertWithMessage("Car volume group events feature").that(volumeGroupEventsEnabled)
209                 .isAnyOf(true, false);
210     }
211 
212     @Test
213     @ApiTest(apis = {"android.car.media.CarAudioManager#isAudioFeatureEnabled"})
isAudioFeatureEnabled_withNonAudioFeature_fails()214     public void isAudioFeatureEnabled_withNonAudioFeature_fails() {
215         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
216                 () -> mCarAudioManager.isAudioFeatureEnabled(-1));
217 
218         assertThat(exception).hasMessageThat().contains("Unknown Audio Feature");
219     }
220 
221     @Test
222     @ApiTest(apis = {"android.car.media.CarAudioManager#isAudioFeatureEnabled(int)",
223             "android.car.media.CarAudioManager#AUDIO_FEATURE_AUDIO_MIRRORING"})
isAudioFeatureEnabled_withAudioMirrorFeature_succeeds()224     public void isAudioFeatureEnabled_withAudioMirrorFeature_succeeds() {
225         boolean audioMirroringEnabled = mCarAudioManager.isAudioFeatureEnabled(
226                 AUDIO_FEATURE_AUDIO_MIRRORING);
227 
228         assertThat(audioMirroringEnabled).isAnyOf(true, false);
229     }
230 
231     @Test
232     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
233     @ApiTest(apis = {"android.car.media.CarAudioManager#canEnableAudioMirror"})
canEnableAudioMirror()234     public void canEnableAudioMirror() {
235         assumeAudioMirrorEnabled();
236 
237         int status = mCarAudioManager.canEnableAudioMirror();
238 
239         assertWithMessage("Can enable audio mirror status with feature supported")
240                 .that(status).isEqualTo(AUDIO_MIRROR_CAN_ENABLE);
241     }
242 
243     @Test
244     @ApiTest(apis = {
245             "android.car.media.CarAudioManager#registerCarVolumeCallback"})
registerCarVolumeCallback_nullCallback_throwsNPE()246     public void registerCarVolumeCallback_nullCallback_throwsNPE() {
247         assertThrows(NullPointerException.class,
248                 () -> mCarAudioManager.registerCarVolumeCallback(null));
249     }
250 
251     @Test
252     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
253     @ApiTest(apis = {
254             "android.car.media.CarAudioManager#registerCarVolumeCallback"})
registerCarVolumeCallback_onGroupVolumeChanged()255     public void registerCarVolumeCallback_onGroupVolumeChanged() throws Exception {
256         assumeDynamicRoutingIsEnabled();
257         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
258 
259         mCarAudioManager.registerCarVolumeCallback(mCallback);
260 
261         injectVolumeDownKeyEvent();
262         assertWithMessage("CarVolumeCallback#onGroupVolumeChanged should be called")
263                 .that(mCallback.receivedGroupVolumeChanged())
264                 .isTrue();
265     }
266 
267     @Test
268     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
269     @ApiTest(apis = {
270             "android.car.media.CarAudioManager#registerCarVolumeCallback"})
registerCarVolumeCallback_onMasterMuteChanged()271     public void registerCarVolumeCallback_onMasterMuteChanged() throws Exception {
272         assumeDynamicRoutingIsEnabled();
273         assumeVolumeGroupMutingIsDisabled();
274         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
275 
276         mCarAudioManager.registerCarVolumeCallback(mCallback);
277 
278         injectVolumeMuteKeyEvent();
279         try {
280             assertWithMessage("CarVolumeCallback#onMasterMuteChanged should be called")
281                 .that(mCallback.receivedMasterMuteChanged())
282                 .isTrue();
283         } finally {
284             injectVolumeMuteKeyEvent();
285         }
286     }
287 
288     @Test
289     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
290     @ApiTest(apis = {
291             "android.car.media.CarAudioManager#registerCarVolumeCallback"})
registerCarVolumeCallback_onGroupMuteChanged()292     public void registerCarVolumeCallback_onGroupMuteChanged() throws Exception {
293         assumeDynamicRoutingIsEnabled();
294         assumeVolumeGroupMutingIsEnabled();
295         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
296 
297         readFirstZoneAndVolumeGroup();
298         mCarAudioManager.registerCarVolumeCallback(mCallback);
299         setVolumeGroupMute(mZoneId, mVolumeGroupId, /* mute= */ true);
300         setVolumeGroupMute(mZoneId, mVolumeGroupId, /* mute= */ false);
301 
302         assertWithMessage("CarVolumeCallback#onGroupMuteChanged should be called")
303             .that(mCallback.receivedGroupMuteChanged()).isTrue();
304         assertWithMessage("CarVolumeCallback#onGroupMuteChanged wrong zoneId")
305             .that(mCallback.zoneId).isEqualTo(mZoneId);
306         assertWithMessage("CarVolumeCallback#onGroupMuteChanged wrong groupId")
307             .that(mCallback.groupId).isEqualTo(mVolumeGroupId);
308     }
309 
310     @Test
311     @ApiTest(apis = {
312             "android.car.media.CarAudioManager#unregisterCarVolumeCallback"})
unregisterCarVolumeCallback_nullCallback_throws()313     public void unregisterCarVolumeCallback_nullCallback_throws() {
314         assertThrows(NullPointerException.class,
315                 () -> mCarAudioManager.unregisterCarVolumeCallback(null));
316     }
317 
318     @Test
319     @ApiTest(apis = {
320             "android.car.media.CarAudioManager#unregisterCarVolumeCallback"})
unregisterCarVolumeCallback_unregisteredCallback_doesNotReceiveCallback()321     public void unregisterCarVolumeCallback_unregisteredCallback_doesNotReceiveCallback()
322             throws Exception {
323         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
324 
325         mCarAudioManager.unregisterCarVolumeCallback(mCallback);
326 
327         assertWithMessage("CarVolumeCallback#onGroupVolumeChanged should not be called")
328                 .that(mCallback.receivedGroupVolumeChanged())
329                 .isFalse();
330     }
331 
332     @Test
333     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
334     @ApiTest(apis = {
335             "android.car.media.CarAudioManager#unregisterCarVolumeCallback"})
unregisterCarVolumeCallback_noLongerReceivesCallback()336     public void unregisterCarVolumeCallback_noLongerReceivesCallback() throws Exception {
337         assumeDynamicRoutingIsEnabled();
338         CarAudioManagerTestUtils.SyncCarVolumeCallback callback =
339                 new CarAudioManagerTestUtils.SyncCarVolumeCallback();
340         mCarAudioManager.registerCarVolumeCallback(callback);
341         mCarAudioManager.unregisterCarVolumeCallback(callback);
342 
343         injectVolumeDownKeyEvent();
344 
345         assertWithMessage("CarVolumeCallback#onGroupVolumeChanged should not be called")
346                 .that(callback.receivedGroupVolumeChanged())
347                 .isFalse();
348     }
349 
350     @Test
351     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
352     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupInfo"})
353     @RequiresFlagsDisabled(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME)
getVolumeGroupInfo_withMinMaxActivationVolumeFlagDisabled()354     public void getVolumeGroupInfo_withMinMaxActivationVolumeFlagDisabled() {
355         assumeDynamicRoutingIsEnabled();
356         int groupCount = mCarAudioManager.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
357 
358         for (int index = 0; index < groupCount; index++) {
359             int minIndex = mCarAudioManager.getGroupMinVolume(PRIMARY_AUDIO_ZONE, index);
360             int maxIndex = mCarAudioManager.getGroupMaxVolume(PRIMARY_AUDIO_ZONE, index);
361             CarVolumeGroupInfo info =
362                     mCarAudioManager.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, index);
363             expectWithMessage("Car volume group id for info %s and group %s", info, index)
364                     .that(info.getId()).isEqualTo(index);
365             expectWithMessage("Car volume group info zone for info %s and group %s",
366                     info, index).that(info.getZoneId()).isEqualTo(PRIMARY_AUDIO_ZONE);
367             expectWithMessage("Car volume group info max index for info %s and group %s",
368                     info, index).that(info.getMaxVolumeGainIndex()).isEqualTo(maxIndex);
369             expectWithMessage("Car volume group info min index for info %s and group %s",
370                     info, index).that(info.getMinVolumeGainIndex()).isEqualTo(minIndex);
371         }
372     }
373 
374     @Test
375     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
376     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupInfo",
377             "android.car.media.CarVolumeGroupInfo#getMaxActivationVolumeGainIndex",
378             "android.car.media.CarVolumeGroupInfo#getMinActivationVolumeGainIndex"})
379     @RequiresFlagsEnabled(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME)
getVolumeGroupInfo_withMinMaxActivationVolumeFlagEnabled()380     public void getVolumeGroupInfo_withMinMaxActivationVolumeFlagEnabled() {
381         assumeDynamicRoutingIsEnabled();
382         int groupCount = mCarAudioManager.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
383 
384         for (int index = 0; index < groupCount; index++) {
385             int minIndex = mCarAudioManager.getGroupMinVolume(PRIMARY_AUDIO_ZONE, index);
386             int maxIndex = mCarAudioManager.getGroupMaxVolume(PRIMARY_AUDIO_ZONE, index);
387             CarVolumeGroupInfo info =
388                     mCarAudioManager.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, index);
389             int minActivationIndex = info.getMinActivationVolumeGainIndex();
390             int maxActivationIndex = info.getMaxActivationVolumeGainIndex();
391 
392             expectWithMessage("Car volume group id for info %s and group %s", info, index)
393                     .that(info.getId()).isEqualTo(index);
394             expectWithMessage("Car volume group info zone for info %s and group %s",
395                     info, index).that(info.getZoneId()).isEqualTo(PRIMARY_AUDIO_ZONE);
396             expectWithMessage("Car volume group info max index for info %s and group %s",
397                     info, index).that(info.getMaxVolumeGainIndex()).isEqualTo(maxIndex);
398             expectWithMessage("Car volume group info min index for info %s and group %s",
399                     info, index).that(info.getMinVolumeGainIndex()).isEqualTo(minIndex);
400             expectWithMessage("Car volume group info min activation volume for info %s and"
401                     + " group %s", info, index).that(minActivationIndex).isAtLeast(minIndex);
402             expectWithMessage("Car volume group info max activation volume for info %s and"
403                     + " group %s", info, index).that(maxActivationIndex).isAtMost(maxIndex);
404             expectWithMessage("Max activation volume and min activation volume for info %s"
405                     + " and group %s", info, index).that(maxActivationIndex)
406                     .isGreaterThan(minActivationIndex);
407         }
408     }
409 
410     @Test
411     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
412     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupInfosForZone"})
getVolumeGroupInfosForZone()413     public void getVolumeGroupInfosForZone() {
414         assumeDynamicRoutingIsEnabled();
415         int groupCount = mCarAudioManager.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
416 
417         List<CarVolumeGroupInfo> infos =
418                 mCarAudioManager.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
419 
420         expectWithMessage("Car volume group infos for primary zone")
421                 .that(infos).hasSize(groupCount);
422         for (int index = 0; index < groupCount; index++) {
423             CarVolumeGroupInfo info =
424                     mCarAudioManager.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, index);
425             expectWithMessage("Car volume group infos for info %s and group %s", info, index)
426                     .that(infos).contains(info);
427         }
428     }
429 
430     @Test
431     @EnsureHasPermission({Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME,
432             Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS})
433     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupInfosForZone",
434             "android.car.media.CarVolumeGroupInfo#getAudioDeviceAttributes",
435             "android.car.media.CarVolumeGroupInfo#isDefault"})
436     @RequiresFlagsEnabled(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES)
getVolumeGroupInfosForZone_forPrimaryZone_withAudioDeviceAttributes()437     public void getVolumeGroupInfosForZone_forPrimaryZone_withAudioDeviceAttributes()
438             throws Exception {
439         assumeDynamicRoutingIsEnabled();
440         setUpDefaultCarAudioConfigurationForZone(PRIMARY_AUDIO_ZONE);
441 
442         List<CarVolumeGroupInfo> infos =
443                 mCarAudioManager.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
444 
445         expectWithMessage("Primary zone's car volume groups").that(infos).isNotEmpty();
446         for (int count = 0; count < infos.size(); count++) {
447             CarVolumeGroupInfo info = infos.get(count);
448             List<Integer> types = info.getAudioDeviceAttributes().stream()
449                     .map(AudioDeviceAttributes::getType).collect(Collectors.toList());
450             expectWithMessage("Primary zone's car volume group %s types", info.getId())
451                     .that(types).isNotEmpty();
452             for (int index = 0; index < types.size(); index++)  {
453                 expectWithMessage("Primary zone's car volume group device type "
454                     + " for group %s device index %s", info.getId(), index)
455                         .that(types.get(index)).isEqualTo(AudioDeviceInfo.TYPE_BUS);
456             }
457         }
458     }
459 
460     @Test
461     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
462     @ApiTest(apis = {"android.car.media.CarAudioManager"
463             + "#getAudioAttributesForVolumeGroup"})
getAudioAttributesForVolumeGroup()464     public void getAudioAttributesForVolumeGroup() {
465         assumeDynamicRoutingIsEnabled();
466         CarVolumeGroupInfo info =
467                 mCarAudioManager.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, /* groupId= */ 0);
468 
469         expectWithMessage("Car volume audio attributes")
470                 .that(mCarAudioManager.getAudioAttributesForVolumeGroup(info))
471                 .isNotEmpty();
472     }
473 
474     @Test
475     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
476     @ApiTest(apis = {"android.car.media.CarAudioManager#getGroupMaxVolume",
477             "android.car.media.CarAudioManager#getGroupMinVolume",
478             "android.car.media.CarAudioManager#getGroupVolume"})
getGroupVolume()479     public void getGroupVolume() {
480         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
481             assumePrimaryZone();
482         }
483         int maxIndex = mCarAudioManager.getGroupMaxVolume(mVolumeGroupId);
484         int minIndex = mCarAudioManager.getGroupMinVolume(mVolumeGroupId);
485 
486         int currentIndex = mCarAudioManager.getGroupVolume(mVolumeGroupId);
487 
488         assertWithMessage("Current maximum volume for primary zone")
489                 .that(currentIndex).isAtMost(maxIndex);
490         assertWithMessage("Current minimum volume for primary zone")
491                 .that(currentIndex).isAtLeast(minIndex);
492     }
493 
494     @Test
495     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
496     @ApiTest(apis = {"android.car.media.CarAudioManager#getGroupMaxVolume",
497             "android.car.media.CarAudioManager#getGroupMinVolume",
498             "android.car.media.CarAudioManager#getGroupVolume"})
getGroupVolume_withZoneId()499     public void getGroupVolume_withZoneId() {
500         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
501             readFirstZoneAndVolumeGroup();
502         }
503         int maxIndex = mCarAudioManager.getGroupMaxVolume(mZoneId, mVolumeGroupId);
504         int minIndex = mCarAudioManager.getGroupMinVolume(mZoneId, mVolumeGroupId);
505 
506         int currentIndex = mCarAudioManager.getGroupVolume(mZoneId, mVolumeGroupId);
507 
508         assertWithMessage("Current maximum volume for zone %s volume group %s", mZoneId,
509                 mVolumeGroupId).that(currentIndex).isAtMost(maxIndex);
510         assertWithMessage("Current minimum volume for zone %s volume group %s", mZoneId,
511                 mVolumeGroupId).that(currentIndex).isAtLeast(minIndex);
512     }
513 
514     @Test
515     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
516     @ApiTest(apis = {"android.car.media.CarAudioManager#setGroupVolume"})
setGroupVolume_toMax_succeeds()517     public void setGroupVolume_toMax_succeeds() {
518         assumeDynamicRoutingIsEnabled();
519         assumePrimaryZone();
520         int prevIndex = mCarAudioManager.getGroupVolume(mZoneId, mVolumeGroupId);
521         int maxIndex = mCarAudioManager.getGroupMaxVolume(mVolumeGroupId);
522 
523         mCarAudioManager.setGroupVolume(mVolumeGroupId, maxIndex, VOLUME_FLAGS);
524 
525         try {
526             assertWithMessage("Current volume after setting to max for primary zone volume group"
527                     + " %s", mVolumeGroupId).that(mCarAudioManager.getGroupVolume(mVolumeGroupId))
528                     .isEqualTo(maxIndex);
529         } finally {
530             mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, prevIndex, VOLUME_FLAGS);
531         }
532     }
533 
534     @Test
535     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
536     @ApiTest(apis = {"android.car.media.CarAudioManager#setGroupVolume"})
setGroupVolume_toMin_succeeds()537     public void setGroupVolume_toMin_succeeds() {
538         assumeDynamicRoutingIsEnabled();
539         assumePrimaryZone();
540         int prevIndex = mCarAudioManager.getGroupVolume(mZoneId, mVolumeGroupId);
541         int minIndex = mCarAudioManager.getGroupMinVolume(mVolumeGroupId);
542 
543         mCarAudioManager.setGroupVolume(mVolumeGroupId, minIndex, VOLUME_FLAGS);
544 
545         try {
546             assertWithMessage("Current volume after setting to min for primary zone volume group"
547                     + " %s", mVolumeGroupId).that(mCarAudioManager.getGroupVolume(mVolumeGroupId))
548                     .isEqualTo(minIndex);
549         } finally {
550             mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, prevIndex, VOLUME_FLAGS);
551         }
552     }
553 
554     @Test
555     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
556     @ApiTest(apis = {"android.car.media.CarAudioManager#setGroupVolume"})
setGroupVolume_withZone_toMax_succeeds()557     public void setGroupVolume_withZone_toMax_succeeds() {
558         assumeDynamicRoutingIsEnabled();
559         readFirstZoneAndVolumeGroup();
560         int prevIndex = mCarAudioManager.getGroupVolume(mZoneId, mVolumeGroupId);
561         int maxIndex = mCarAudioManager.getGroupMaxVolume(mZoneId, mVolumeGroupId);
562 
563         mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, maxIndex, VOLUME_FLAGS);
564 
565         try {
566             assertWithMessage("Current volume after setting to max for zone %s volume group %s",
567                     mZoneId, mVolumeGroupId).that(mCarAudioManager.getGroupVolume(mZoneId,
568                     mVolumeGroupId)).isEqualTo(maxIndex);
569         } finally {
570             mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, prevIndex, VOLUME_FLAGS);
571         }
572     }
573 
574     @Test
575     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
576     @ApiTest(apis = {"android.car.media.CarAudioManager#setGroupVolume"})
setGroupVolume_withZone_toMin_succeeds()577     public void setGroupVolume_withZone_toMin_succeeds() {
578         assumeDynamicRoutingIsEnabled();
579         readFirstZoneAndVolumeGroup();
580         int prevIndex = mCarAudioManager.getGroupVolume(mZoneId, mVolumeGroupId);
581         int minIndex = mCarAudioManager.getGroupMinVolume(mZoneId, mVolumeGroupId);
582 
583         mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, minIndex, VOLUME_FLAGS);
584 
585         try {
586             assertWithMessage("Current volume after setting to min for zone %s volume group %s",
587                     mZoneId, mVolumeGroupId).that(mCarAudioManager.getGroupVolume(mZoneId,
588                     mVolumeGroupId)).isEqualTo(minIndex);
589         } finally {
590             mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, prevIndex, VOLUME_FLAGS);
591         }
592     }
593 
594     @Test
595     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
596     @ApiTest(apis = {"android.car.media.CarAudioManager#setGroupVolume"})
setGroupVolume_aboveMax_throwsException()597     public void setGroupVolume_aboveMax_throwsException() {
598         assumeDynamicRoutingIsEnabled();
599         readFirstZoneAndVolumeGroup();
600         int maxIndex = mCarAudioManager.getGroupMaxVolume(mZoneId, mVolumeGroupId);
601 
602         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
603                 () -> mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, maxIndex + 1,
604                         VOLUME_FLAGS));
605 
606         assertWithMessage("Exception for setting volume above max")
607                 .that(exception).hasMessageThat().contains("Gain out of range");
608     }
609 
610     @Test
611     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
612     @ApiTest(apis = {"android.car.media.CarAudioManager#setGroupVolume"})
setGroupVolume_belowMin_throwsException()613     public void setGroupVolume_belowMin_throwsException() {
614         assumeDynamicRoutingIsEnabled();
615         readFirstZoneAndVolumeGroup();
616         int minIndex = mCarAudioManager.getGroupMinVolume(mZoneId, mVolumeGroupId);
617 
618         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
619                 () -> mCarAudioManager.setGroupVolume(mZoneId, mVolumeGroupId, minIndex - 1,
620                         VOLUME_FLAGS));
621 
622         assertWithMessage("Exception for setting volume below min")
623                 .that(exception).hasMessageThat().contains("Gain out of range");
624     }
625 
626     @Test
627     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
628     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupCount"})
getVolumeGroupCount()629     public void getVolumeGroupCount() {
630         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
631             assumePrimaryZone();
632         }
633 
634         assertWithMessage("Primary zone volume group count")
635                 .that(mCarAudioManager.getVolumeGroupCount()).isEqualTo(mVolumeGroupCount);
636     }
637 
638     @Test
639     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
640     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupCount"})
getVolumeGroupCount_withZoneIdAndDynamicRouting()641     public void getVolumeGroupCount_withZoneIdAndDynamicRouting() {
642         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
643             readFirstZoneAndVolumeGroup();
644         }
645 
646         assertWithMessage("Zone %s volume group count", mZoneId)
647                 .that(mCarAudioManager.getVolumeGroupCount(mZoneId)).isEqualTo(mVolumeGroupCount);
648     }
649 
650     @Test
651     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
652     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupCount"})
getVolumeGroupCount_withInvalidZoneId_throwsException()653     public void getVolumeGroupCount_withInvalidZoneId_throwsException() {
654         assumeDynamicRoutingIsEnabled();
655         assumePrimaryZone();
656 
657         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
658                 () -> mCarAudioManager.getVolumeGroupCount(INVALID_AUDIO_ZONE));
659 
660         assertWithMessage("Invalid zone volume group count exception").that(exception)
661                 .hasMessageThat().contains("Invalid audio zone Id");
662     }
663 
664     @Test
665     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
666     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupIdForUsage"})
getVolumeGroupIdForUsage_forInvalidUsage_returnsInvalidId()667     public void getVolumeGroupIdForUsage_forInvalidUsage_returnsInvalidId() {
668         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
669             assumePrimaryZone();
670         }
671 
672         int volumeGroupId = mCarAudioManager.getVolumeGroupIdForUsage(USAGE_INVALID);
673 
674         assertWithMessage("Volume group id for invalid usage").that(volumeGroupId)
675                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
676     }
677 
678     @Test
679     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
680     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupIdForUsage"})
getVolumeGroupIdForUsage_withInvalidZoneId_throwsException()681     public void getVolumeGroupIdForUsage_withInvalidZoneId_throwsException() {
682         assumeDynamicRoutingIsEnabled();
683 
684         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
685                 () -> mCarAudioManager.getVolumeGroupIdForUsage(INVALID_AUDIO_ZONE, USAGE_MEDIA));
686 
687         assertWithMessage("Invalid zone volume group for media usage exception").that(exception)
688                 .hasMessageThat().contains("Invalid audio zone Id");
689     }
690 
691     @Test
692     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
693     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupIdForUsage"})
getVolumeGroupIdForUsage_returnsValidId()694     public void getVolumeGroupIdForUsage_returnsValidId() {
695         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) {
696             assumePrimaryZone();
697         }
698 
699         int volumeGroupId = mCarAudioManager.getVolumeGroupIdForUsage(USAGE_MEDIA);
700 
701         assertWithMessage("Valid volume group id in primary zone")
702                 .that(volumeGroupId).isNotEqualTo(INVALID_VOLUME_GROUP_ID);
703     }
704 
705     @Test
706     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
707     @ApiTest(apis = {"android.car.media.CarAudioManager#getVolumeGroupIdForUsage"})
getVolumeGroupIdForUsage_withZoneId_returnsValidId()708     public void getVolumeGroupIdForUsage_withZoneId_returnsValidId() {
709         assumeDynamicRoutingIsEnabled();
710         readFirstZoneAndVolumeGroup();
711 
712         int volumeGroupId = mCarAudioManager.getVolumeGroupIdForUsage(mZoneId, USAGE_MEDIA);
713 
714         assertWithMessage("Valid volume group id for media usage in zone %s", mZoneId)
715                 .that(volumeGroupId).isNotEqualTo(INVALID_VOLUME_GROUP_ID);
716     }
717 
718     @Test
719     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
720     @ApiTest(apis = {"android.car.media.CarAudioManager#getUsagesForVolumeGroupId"})
getUsagesForVolumeGroupId_returnsAtLeastOneUsage()721     public void getUsagesForVolumeGroupId_returnsAtLeastOneUsage() {
722         assumeDynamicRoutingIsEnabled();
723         assumePrimaryZone();
724 
725         int[] usages = mCarAudioManager.getUsagesForVolumeGroupId(mVolumeGroupId);
726 
727         assertVolumeGroupIdForUsages(PRIMARY_AUDIO_ZONE, mVolumeGroupId, usages,
728                 "with dynamic routing");
729     }
730 
731     @Test
732     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
733     @ApiTest(apis = {"android.car.media.CarAudioManager#getUsagesForVolumeGroupId"})
getUsagesForVolumeGroupId_withoutDynamicRouting_returnsAtLeastOneUsage()734     public void getUsagesForVolumeGroupId_withoutDynamicRouting_returnsAtLeastOneUsage() {
735         assumeDynamicRoutingIsDisabled();
736 
737         int[] usages = mCarAudioManager.getUsagesForVolumeGroupId(LEGACY_VOLUME_GROUP_ID);
738 
739         assertVolumeGroupIdForUsages(PRIMARY_AUDIO_ZONE, LEGACY_VOLUME_GROUP_ID,
740                 usages, "with dynamic routing disabled");
741     }
742 
743     @Test
744     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
745     @ApiTest(apis = {"android.car.media.CarAudioManager#getUsagesForVolumeGroupId"})
getUsagesForVolumeGroupId_withZoneId_returnsAtLeastOneUsage()746     public void getUsagesForVolumeGroupId_withZoneId_returnsAtLeastOneUsage() {
747         assumeDynamicRoutingIsEnabled();
748         readFirstZoneAndVolumeGroup();
749 
750         int[] usages = mCarAudioManager.getUsagesForVolumeGroupId(mVolumeGroupId);
751 
752         assertVolumeGroupIdForUsages(mZoneId, mVolumeGroupId, usages,
753                 "with id and dynamic routing");
754     }
755 
756     @Test
757     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
758     @ApiTest(apis = {"android.car.media.CarAudioManager#getUsagesForVolumeGroupId"})
getUsagesForVolumeGroupId_withZoneIdWithoutDynamicRouting_returnsAtLeastOneUsage()759     public void getUsagesForVolumeGroupId_withZoneIdWithoutDynamicRouting_returnsAtLeastOneUsage() {
760         assumeDynamicRoutingIsDisabled();
761 
762         int[] usages = mCarAudioManager.getUsagesForVolumeGroupId(LEGACY_VOLUME_GROUP_ID);
763 
764         assertVolumeGroupIdForUsages(LEGACY_ZONE_ID, LEGACY_VOLUME_GROUP_ID, usages,
765                 "with id and with dynamic routing disabled");
766     }
767 
768     @Test
769     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
770     @ApiTest(apis = {"android.car.media.CarAudioManager#getUsagesForVolumeGroupId"})
getUsagesForVolumeGroupId_withInvalidZone_throwsException()771     public void getUsagesForVolumeGroupId_withInvalidZone_throwsException() {
772         assumeDynamicRoutingIsEnabled();
773         assumePrimaryZone();
774 
775         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
776                 () -> mCarAudioManager.getUsagesForVolumeGroupId(INVALID_AUDIO_ZONE,
777                         mVolumeGroupId));
778 
779         assertWithMessage("Exception for getting usage for volume group in invalid zone")
780                 .that(exception).hasMessageThat().contains("Invalid audio zone Id");
781     }
782 
783     @Test
784     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
785     @ApiTest(apis = {"android.car.media.CarAudioManager#getOutputDeviceForUsage"})
getOutputDeviceForUsage_withoutDynamicRouting_throws()786     public void getOutputDeviceForUsage_withoutDynamicRouting_throws() {
787         assumeDynamicRoutingIsDisabled();
788 
789         IllegalStateException exception = assertThrows(IllegalStateException.class,
790                 () -> mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA));
791 
792         assertWithMessage("Exception for getting media output device with dynamic routing disabled")
793                 .that(exception).hasMessageThat().contains("Non legacy routing");
794     }
795 
796     @Test
797     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
798     @ApiTest(apis = {"android.car.media.CarAudioManager#getOutputDeviceForUsage"})
getOutputDeviceForUsage_withInvalidZoneId_throws()799     public void getOutputDeviceForUsage_withInvalidZoneId_throws() {
800         assumeDynamicRoutingIsEnabled();
801 
802         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
803                 () -> mCarAudioManager.getOutputDeviceForUsage(INVALID_AUDIO_ZONE, USAGE_MEDIA));
804 
805         assertWithMessage("Exception for getting invalid zone media output device")
806                 .that(exception).hasMessageThat().contains("Invalid audio zone Id");
807     }
808 
809     @Test
810     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
811     @ApiTest(apis = {"android.car.media.CarAudioManager#getOutputDeviceForUsage"})
getOutputDeviceForUsage_withInvalidUsage_throws()812     public void getOutputDeviceForUsage_withInvalidUsage_throws() {
813         assumeDynamicRoutingIsEnabled();
814 
815         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
816                 () -> mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE, USAGE_INVALID));
817 
818         assertWithMessage("Invalid usage output device exception")
819                 .that(exception).hasMessageThat().contains("Invalid audio attribute");
820     }
821 
822     @Test
823     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
824     @ApiTest(apis = {"android.car.media.CarAudioManager#getOutputDeviceForUsage"})
getOutputDeviceForUsage_succeeds()825     public void getOutputDeviceForUsage_succeeds() {
826         assumeDynamicRoutingIsEnabled();
827 
828         AudioDeviceInfo deviceInfo = mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE,
829                 USAGE_MEDIA);
830 
831         assertWithMessage("Device for media in primary zone")
832                 .that(deviceInfo).isNotNull();
833         assertWithMessage("Sink device for media in primary zone")
834                 .that(deviceInfo.isSink()).isTrue();
835     }
836 
837     @Test
838     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
839     @ApiTest(apis = {"android.car.media.CarAudioManager#getInputDevicesForZoneId"})
getInputDevicesForZoneId_withoutDynamicRouting_throws()840     public void getInputDevicesForZoneId_withoutDynamicRouting_throws() {
841         assumeDynamicRoutingIsDisabled();
842 
843         IllegalStateException exception = assertThrows(IllegalStateException.class,
844                 () -> mCarAudioManager.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE));
845 
846         assertWithMessage("Exception for getting input devices with dynamic routing disabled")
847                 .that(exception).hasMessageThat().contains("Non legacy routing");
848     }
849 
850     @Test
851     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
852     @ApiTest(apis = {"android.car.media.CarAudioManager#getInputDevicesForZoneId"})
getInputDevicesForZoneId_withInvalidZoneId_throws()853     public void getInputDevicesForZoneId_withInvalidZoneId_throws() {
854         assumeDynamicRoutingIsEnabled();
855 
856         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
857                 () -> mCarAudioManager.getInputDevicesForZoneId(INVALID_AUDIO_ZONE));
858 
859         assertWithMessage("Exception for getting input devices with invalid volume group id")
860                 .that(exception).hasMessageThat().contains("Invalid audio zone Id");
861     }
862 
863     @Test
864     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
865     @ApiTest(apis = {"android.car.media.CarAudioManager#getInputDevicesForZoneId"})
getInputDevicesForZoneId_succeeds()866     public void getInputDevicesForZoneId_succeeds() {
867         assumeDynamicRoutingIsEnabled();
868 
869         List<AudioDeviceInfo> audioDeviceInfos = mCarAudioManager.getInputDevicesForZoneId(
870                 PRIMARY_AUDIO_ZONE);
871 
872         assertWithMessage("Input devices for primary zone")
873                 .that(audioDeviceInfos).isNotNull();
874         for (int i = 0; i < audioDeviceInfos.size(); i++) {
875             assertWithMessage("Source device %s in primary zone", audioDeviceInfos.get(i)
876                     .getAddress()).that(audioDeviceInfos.get(i).isSource()).isTrue();
877         }
878     }
879 
880     @Test
881     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
882     @ApiTest(apis = {"android.car.media.CarAudioManager"
883             + "#isMediaAudioAllowedInPrimaryZone"})
isMediaAudioAllowedInPrimaryZone_byDefault()884     public void isMediaAudioAllowedInPrimaryZone_byDefault() {
885         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
886         OccupantZoneInfo info =
887                         mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
888 
889         assertWithMessage("Default media allowed status")
890                 .that(mCarAudioManager.isMediaAudioAllowedInPrimaryZone(info)).isFalse();
891     }
892 
893     @Test
894     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
895     @ApiTest(apis = {"android.car.media.CarAudioManager"
896             + "#isMediaAudioAllowedInPrimaryZone"})
isMediaAudioAllowedInPrimaryZone_afterAllowed()897     public void isMediaAudioAllowedInPrimaryZone_afterAllowed() throws Exception {
898         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
899         setupMediaAudioRequestCallback();
900         OccupantZoneInfo info =
901                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
902         requestToPlayMediaInPrimaryZone(info);
903         mRequestCallback.receivedMediaRequest();
904         mCarAudioManager.allowMediaAudioOnPrimaryZone(mMediaRequestId, /* allow= */ true);
905 
906         boolean approved = mCarAudioManager.isMediaAudioAllowedInPrimaryZone(info);
907 
908         assertWithMessage("Approved media allowed status")
909                 .that(approved).isTrue();
910     }
911 
912     @Test
913     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
914     @ApiTest(apis = {"android.car.media.CarAudioManager"
915             + "#isMediaAudioAllowedInPrimaryZone"})
isMediaAudioAllowedInPrimaryZone_afterRejected()916     public void isMediaAudioAllowedInPrimaryZone_afterRejected() throws Exception {
917         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
918         setupMediaAudioRequestCallback();
919         OccupantZoneInfo info =
920                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
921         requestToPlayMediaInPrimaryZone(info);
922         mRequestCallback.receivedMediaRequest();
923         mCarAudioManager.allowMediaAudioOnPrimaryZone(mMediaRequestId, /* allow= */ false);
924 
925         boolean approved = mCarAudioManager.isMediaAudioAllowedInPrimaryZone(info);
926 
927         assertWithMessage("Unapproved media allowed status")
928                 .that(approved).isFalse();
929     }
930 
931     @Test
932     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
933     @ApiTest(apis = {"android.car.media.CarAudioManager"
934             + "#setPrimaryZoneMediaAudioRequestCallback"})
setPrimaryZoneMediaAudioRequestCallback()935     public void setPrimaryZoneMediaAudioRequestCallback() {
936         assumePassengerWithValidAudioZone();
937         Executor executor = Executors.newFixedThreadPool(1);
938         TestPrimaryZoneMediaAudioRequestStatusCallback
939                 requestCallback = new TestPrimaryZoneMediaAudioRequestStatusCallback();
940 
941         boolean registered =
942                 mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(executor, requestCallback);
943 
944         assertWithMessage("Set status of media request callback")
945                 .that(registered).isTrue();
946     }
947 
948     @Test
949     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
950     @ApiTest(apis = {"android.car.media.CarAudioManager"
951             + "#requestMediaAudioOnPrimaryZone"})
requestMediaAudioOnPrimaryZone()952     public void requestMediaAudioOnPrimaryZone() throws Exception {
953         assumeDynamicRoutingIsEnabled();
954         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
955         setupMediaAudioRequestCallback();
956         OccupantZoneInfo info =
957                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
958         Executor callbackExecutor = Executors.newFixedThreadPool(1);
959         TestMediaAudioRequestStatusCallback callback = new TestMediaAudioRequestStatusCallback();
960 
961         mMediaRequestId =
962                 mCarAudioManager.requestMediaAudioOnPrimaryZone(info, callbackExecutor, callback);
963 
964         mRequestCallback.receivedMediaRequest();
965         assertWithMessage("Received request id").that(mRequestCallback.mRequestId)
966                 .isEqualTo(mMediaRequestId);
967         assertWithMessage("Received occupant info").that(mRequestCallback.mOccupantZoneInfo)
968                 .isEqualTo(info);
969     }
970 
971     @Test
972     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
973     @ApiTest(apis = {"android.car.media.CarAudioManager"
974             + "#requestMediaAudioOnPrimaryZone"})
requestMediaAudioOnPrimaryZone_withoutApprover()975     public void requestMediaAudioOnPrimaryZone_withoutApprover() throws Exception {
976         assumeDynamicRoutingIsEnabled();
977         assumeNoPrimaryZoneAudioMediaApprovers();
978         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
979         OccupantZoneInfo info =
980                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
981         Executor callbackExecutor = Executors.newFixedThreadPool(1);
982         TestMediaAudioRequestStatusCallback callback = new TestMediaAudioRequestStatusCallback();
983 
984         mMediaRequestId =
985                 mCarAudioManager.requestMediaAudioOnPrimaryZone(info, callbackExecutor, callback);
986 
987         callback.receivedApproval();
988         assertWithMessage("Request id for rejected request")
989                 .that(callback.mRequestId).isEqualTo(mMediaRequestId);
990         assertWithMessage("Rejected request status").that(callback.mStatus)
991                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
992     }
993 
994     @Test
995     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
996     @ApiTest(apis = {"android.car.media.CarAudioManager"
997             + "#allowMediaAudioOnPrimaryZone"})
allowMediaAudioOnPrimaryZone()998     public void allowMediaAudioOnPrimaryZone() throws Exception {
999         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
1000         setupMediaAudioRequestCallback();
1001         OccupantZoneInfo info =
1002                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
1003         TestMediaAudioRequestStatusCallback callback =
1004                 requestToPlayMediaInPrimaryZone(info);
1005         mRequestCallback.receivedMediaRequest();
1006 
1007         boolean succeeded = mCarAudioManager.allowMediaAudioOnPrimaryZone(mMediaRequestId,
1008                 /* allow= */ true);
1009 
1010         callback.receivedApproval();
1011         assertWithMessage("Approved results for request in primary zone")
1012                 .that(succeeded).isTrue();
1013         assertWithMessage("Approved request id in primary zone")
1014                 .that(callback.mRequestId).isEqualTo(mMediaRequestId);
1015         assertWithMessage("Approved request occupant in primary zone")
1016                 .that(callback.mOccupantZoneInfo).isEqualTo(info);
1017         assertWithMessage("Audio status in primary zone").that(callback.mStatus)
1018                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
1019     }
1020 
1021     @Test
1022     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1023     @ApiTest(apis = {"android.car.media.CarAudioManager"
1024             + "#allowMediaAudioOnPrimaryZone"})
allowMediaAudioOnPrimaryZone_withReject()1025     public void allowMediaAudioOnPrimaryZone_withReject() throws Exception {
1026         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
1027         setupMediaAudioRequestCallback();
1028         OccupantZoneInfo info =
1029                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
1030         TestMediaAudioRequestStatusCallback callback = requestToPlayMediaInPrimaryZone(info);
1031         mRequestCallback.receivedMediaRequest();
1032 
1033         boolean succeeded = mCarAudioManager.allowMediaAudioOnPrimaryZone(mMediaRequestId,
1034                 /* allow= */ false);
1035 
1036         callback.receivedApproval();
1037         long tempRequestId = mMediaRequestId;
1038         mMediaRequestId = INVALID_REQUEST_ID;
1039         assertWithMessage("Unapproved results for request in primary zone")
1040                 .that(succeeded).isTrue();
1041         assertWithMessage("Unapproved request id in primary zone")
1042                 .that(callback.mRequestId).isEqualTo(tempRequestId);
1043         assertWithMessage("Unapproved audio status in primary zone").that(callback.mStatus)
1044                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
1045     }
1046 
1047     @Test
1048     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1049     @ApiTest(apis = {"android.car.media.CarAudioManager"
1050             + "#allowMediaAudioOnPrimaryZone"})
allowMediaAudioOnPrimaryZone_withInvalidRequestId()1051     public void allowMediaAudioOnPrimaryZone_withInvalidRequestId() throws Exception {
1052         assumePassengerWithValidAudioZone();
1053         setupMediaAudioRequestCallback();
1054 
1055         boolean succeeded = mCarAudioManager
1056                         .allowMediaAudioOnPrimaryZone(INVALID_REQUEST_ID, /* allow= */ true);
1057 
1058         assertWithMessage("Invalid request id allowed results")
1059                 .that(succeeded).isFalse();
1060     }
1061 
1062     @Test
1063     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1064     @ApiTest(apis = {"android.car.media.CarAudioManager"
1065             + "#cancelMediaAudioOnPrimaryZone"})
cancelMediaAudioOnPrimaryZone()1066     public void cancelMediaAudioOnPrimaryZone() throws Exception {
1067         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
1068         setupMediaAudioRequestCallback();
1069         OccupantZoneInfo info =
1070                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
1071         TestMediaAudioRequestStatusCallback callback = requestToPlayMediaInPrimaryZone(info);
1072         mRequestCallback.receivedMediaRequest();
1073 
1074         mCarAudioManager.cancelMediaAudioOnPrimaryZone(mMediaRequestId);
1075 
1076         mMediaRequestId = INVALID_REQUEST_ID;
1077         callback.receivedApproval();
1078         assertWithMessage("Cancelled audio status in primary zone")
1079                 .that(callback.mStatus).isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
1080     }
1081 
1082     @Test
1083     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1084     @ApiTest(apis = {"android.car.media.CarAudioManager"
1085             + "#resetMediaAudioOnPrimaryZone"})
resetMediaAudioOnPrimaryZone()1086     public void resetMediaAudioOnPrimaryZone() throws Exception {
1087         int passengerAudioZoneId = assumePassengerWithValidAudioZone();
1088         setupMediaAudioRequestCallback();
1089         OccupantZoneInfo info =
1090                 mCarOccupantZoneManager.getOccupantForAudioZoneId(passengerAudioZoneId);
1091         TestMediaAudioRequestStatusCallback callback = requestToPlayMediaInPrimaryZone(info);
1092         mRequestCallback.receivedMediaRequest();
1093         mCarAudioManager.allowMediaAudioOnPrimaryZone(mMediaRequestId, /* allow= */ true);
1094         callback.receivedApproval();
1095         callback.reset();
1096 
1097         mCarAudioManager.resetMediaAudioOnPrimaryZone(info);
1098 
1099         long tempRequestId = mMediaRequestId;
1100         mMediaRequestId = INVALID_REQUEST_ID;
1101         callback.receivedApproval();
1102         assertWithMessage("Reset request id in primary zone")
1103                 .that(callback.mRequestId).isEqualTo(tempRequestId);
1104         assertWithMessage("Reset audio status in primary zone")
1105                 .that(callback.mStatus).isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
1106     }
1107 
1108     @Test
1109     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1110     @ApiTest(apis = {"android.car.media.CarAudioManager#getCurrentAudioZoneConfigInfo"})
getCurrentAudioZoneConfigInfo()1111     public void getCurrentAudioZoneConfigInfo() {
1112         assumeDynamicRoutingIsEnabled();
1113         List<TestZoneConfigIdInfo> zoneConfigs = assumeSecondaryZoneConfigs();
1114 
1115         CarAudioZoneConfigInfo currentZoneConfigInfo =
1116                 mCarAudioManager.getCurrentAudioZoneConfigInfo(mZoneId);
1117 
1118         assertWithMessage("Current zone config info")
1119                 .that(TestZoneConfigIdInfo.getZoneConfigFromInfo(currentZoneConfigInfo))
1120                 .isIn(zoneConfigs);
1121     }
1122 
1123     @Test
1124     @EnsureHasPermission({Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS,
1125             Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME})
1126     @ApiTest(apis = {"android.car.media.CarAudioManager#getCurrentAudioZoneConfigInfo",
1127             "android.car.media.CarAudioZoneConfigInfo#isActive",
1128             "android.car.media.CarAudioZoneConfigInfo#isSelected",
1129             "android.car.media.CarAudioZoneConfigInfo#isDefault",
1130             "android.car.media.CarAudioZoneConfigInfo#getConfigVolumeGroups"})
1131     @RequiresFlagsEnabled(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES)
getCurrentAudioZoneConfigInfo_withDynamicDevicesEnabled()1132     public void getCurrentAudioZoneConfigInfo_withDynamicDevicesEnabled() throws Exception {
1133         assumeDynamicRoutingIsEnabled();
1134         List<TestZoneConfigIdInfo> zoneConfigs = assumeSecondaryZoneConfigs();
1135         setUpDefaultCarAudioConfigurationForZone(mZoneId);
1136 
1137         CarAudioZoneConfigInfo currentZoneConfigInfo =
1138                 mCarAudioManager.getCurrentAudioZoneConfigInfo(mZoneId);
1139 
1140         assertWithMessage("Current zone config info, with dynamic device enabled")
1141                 .that(TestZoneConfigIdInfo.getZoneConfigFromInfo(currentZoneConfigInfo))
1142                 .isIn(zoneConfigs);
1143         assertWithMessage("Current zone config info active status")
1144                 .that(currentZoneConfigInfo.isActive()).isTrue();
1145         assertWithMessage("Current zone config info selected status")
1146                 .that(currentZoneConfigInfo.isSelected()).isTrue();
1147         assertWithMessage("Current zone config info default indicator")
1148                 .that(currentZoneConfigInfo.isDefault()).isFalse();
1149         List<CarVolumeGroupInfo> currentInfos =
1150                 mCarAudioManager.getVolumeGroupInfosForZone(mZoneId);
1151         for (CarVolumeGroupInfo info : currentInfos) {
1152             assertWithMessage("Current zone config info volume group %s", info.getId())
1153                     .that(currentZoneConfigInfo.getConfigVolumeGroups()).contains(info);
1154         }
1155     }
1156 
1157     @Test
1158     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1159     @ApiTest(apis = {"android.car.media.CarAudioManager#getCurrentAudioZoneConfigInfo"})
getCurrentAudioZoneConfigInfo_withInvalidZoneId_fails()1160     public void getCurrentAudioZoneConfigInfo_withInvalidZoneId_fails() {
1161         assumeDynamicRoutingIsEnabled();
1162 
1163         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
1164                 () -> mCarAudioManager.getCurrentAudioZoneConfigInfo(INVALID_AUDIO_ZONE));
1165 
1166         assertThat(exception).hasMessageThat().contains("Invalid audio zone Id");
1167     }
1168 
1169     @Test
1170     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1171     @ApiTest(apis = {"android.car.media.CarAudioManager#getAudioZoneConfigInfos"})
getAudioZoneConfigInfos_forPrimaryZone()1172     public void getAudioZoneConfigInfos_forPrimaryZone() {
1173         assumeDynamicRoutingIsEnabled();
1174         List<TestZoneConfigIdInfo> primaryZoneConfigsFromDump = parsePrimaryZoneConfigs();
1175 
1176         List<CarAudioZoneConfigInfo> zoneConfigInfos =
1177                 mCarAudioManager.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
1178 
1179         assertWithMessage("Primary audio zone config")
1180                 .that(TestZoneConfigIdInfo.getZoneConfigListFromInfoList(zoneConfigInfos))
1181                 .containsExactlyElementsIn(primaryZoneConfigsFromDump);
1182     }
1183 
1184     @Test
1185     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1186     @ApiTest(apis = {"android.car.media.CarAudioManager#getAudioZoneConfigInfos"})
getAudioZoneConfigInfos_forSecondaryZone()1187     public void getAudioZoneConfigInfos_forSecondaryZone() {
1188         assumeDynamicRoutingIsEnabled();
1189         List<TestZoneConfigIdInfo> zoneConfigs = assumeSecondaryZoneConfigs();
1190 
1191         List<CarAudioZoneConfigInfo> zoneConfigInfosFromDump =
1192                 mCarAudioManager.getAudioZoneConfigInfos(mZoneId);
1193 
1194         assertWithMessage("All zone config infos")
1195                 .that(TestZoneConfigIdInfo.getZoneConfigListFromInfoList(zoneConfigInfosFromDump))
1196                 .containsExactlyElementsIn(zoneConfigs);
1197     }
1198 
1199     @Test
1200     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1201     @ApiTest(apis = {"android.car.media.CarAudioManager#getAudioZoneConfigInfos"})
getAudioZoneConfigInfos_withInvalidZoneId_fails()1202     public void getAudioZoneConfigInfos_withInvalidZoneId_fails() {
1203         assumeDynamicRoutingIsEnabled();
1204 
1205         IllegalArgumentException exception = assertThrows(IllegalArgumentException.class,
1206                 () -> mCarAudioManager.getAudioZoneConfigInfos(INVALID_AUDIO_ZONE));
1207 
1208         assertThat(exception).hasMessageThat().contains("Invalid audio zone Id");
1209     }
1210 
1211     @Test
1212     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1213     @ApiTest(apis = {"android.car.media.CarAudioManager#switchAudioZoneToConfig"})
1214     @RequiresFlagsDisabled(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES)
switchAudioZoneToConfig()1215     public void switchAudioZoneToConfig() throws Exception {
1216         assumeDynamicRoutingIsEnabled();
1217         assumeSecondaryZoneConfigs();
1218         CarAudioZoneConfigInfo zoneConfigInfoSaved =
1219                 mCarAudioManager.getCurrentAudioZoneConfigInfo(mZoneId);
1220         CarAudioZoneConfigInfo zoneConfigInfoSwitchedTo = getNonCurrentZoneConfig(mZoneId);
1221         Executor callbackExecutor = Executors.newFixedThreadPool(1);
1222         TestSwitchAudioZoneConfigCallback callback = new TestSwitchAudioZoneConfigCallback();
1223 
1224         mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSwitchedTo, callbackExecutor,
1225                 callback);
1226 
1227         callback.receivedApproval();
1228         assertWithMessage("Zone configuration switching status")
1229                 .that(callback.mIsSuccessful).isTrue();
1230         assertWithMessage("Updated zone configuration")
1231                 .that(callback.mZoneConfigInfo).isEqualTo(zoneConfigInfoSwitchedTo);
1232         callback.reset();
1233         mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSaved, callbackExecutor, callback);
1234         callback.receivedApproval();
1235     }
1236 
1237     @Test
1238     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1239     @ApiTest(apis = {"android.car.media.CarAudioManager#switchAudioZoneToConfig",
1240             "android.car.media.CarAudioZoneConfigInfo#isActive",
1241             "android.car.media.CarAudioZoneConfigInfo#isSelected",
1242             "android.car.media.CarAudioZoneConfigInfo#getConfigVolumeGroups"})
1243     @RequiresFlagsEnabled(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES)
switchAudioZoneToConfig_withDynamicDevicesEnabled()1244     public void switchAudioZoneToConfig_withDynamicDevicesEnabled() throws Exception {
1245         assumeDynamicRoutingIsEnabled();
1246         assumeSecondaryZoneConfigs();
1247         CarAudioZoneConfigInfo zoneConfigInfoSaved =
1248                 mCarAudioManager.getCurrentAudioZoneConfigInfo(mZoneId);
1249         CarAudioZoneConfigInfo zoneConfigInfoSwitchedTo = getNonCurrentZoneConfig(mZoneId);
1250         Executor callbackExecutor = Executors.newFixedThreadPool(1);
1251         TestSwitchAudioZoneConfigCallback callback = new TestSwitchAudioZoneConfigCallback();
1252 
1253         mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSwitchedTo, callbackExecutor,
1254                 callback);
1255 
1256         callback.receivedApproval();
1257         assertWithMessage("Zone configuration switching status with dynamic devices enabled")
1258                 .that(callback.mIsSuccessful).isTrue();
1259         assertWithMessage("Updated zone configuration with dynamic devices enabled")
1260                 .that(callback.mZoneConfigInfo.hasSameConfigInfo(zoneConfigInfoSwitchedTo))
1261                 .isTrue();
1262         assertWithMessage("Switched configuration active status with dynamic devices enable")
1263                 .that(callback.mZoneConfigInfo.isActive()).isTrue();
1264         assertWithMessage("Switched configuration selected status with dynamic devices enable")
1265                 .that(callback.mZoneConfigInfo.isSelected()).isTrue();
1266         CarAudioZoneConfigInfo updatedPreviousConfig =
1267                 mCarAudioManager.getAudioZoneConfigInfos(mZoneId).stream()
1268                         .filter(info -> info.getConfigId() == zoneConfigInfoSaved.getConfigId())
1269                         .findFirst().orElseThrow();
1270         assertWithMessage("Previous configuration selected status with dynamic devices enable")
1271                 .that(updatedPreviousConfig.isSelected()).isFalse();
1272         callback.reset();
1273         mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSaved, callbackExecutor, callback);
1274         callback.receivedApproval();
1275     }
1276 
1277     @Test
1278     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1279     @ApiTest(apis = {"android.car.media.CarAudioManager#switchAudioZoneToConfig"})
switchAudioZoneToConfig_withNullConfig_fails()1280     public void switchAudioZoneToConfig_withNullConfig_fails() throws Exception {
1281         assumeDynamicRoutingIsEnabled();
1282         Executor callbackExecutor = Executors.newFixedThreadPool(1);
1283         TestSwitchAudioZoneConfigCallback callback = new TestSwitchAudioZoneConfigCallback();
1284 
1285         NullPointerException exception = assertThrows(NullPointerException.class,
1286                 () -> mCarAudioManager.switchAudioZoneToConfig(/* zoneConfig= */ null,
1287                         callbackExecutor, callback));
1288 
1289         assertThat(exception).hasMessageThat().contains("Audio zone configuration can not be null");
1290     }
1291 
1292     @Test
1293     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1294     @ApiTest(apis = {"android.car.media.CarAudioManager#switchAudioZoneToConfig"})
switchAudioZoneToConfig_withNullExecutor_fails()1295     public void switchAudioZoneToConfig_withNullExecutor_fails() throws Exception {
1296         assumeDynamicRoutingIsEnabled();
1297         assumeSecondaryZoneConfigs();
1298         CarAudioZoneConfigInfo zoneConfigInfoSwitchedTo = getNonCurrentZoneConfig(mZoneId);
1299         TestSwitchAudioZoneConfigCallback callback = new TestSwitchAudioZoneConfigCallback();
1300 
1301         NullPointerException exception = assertThrows(NullPointerException.class,
1302                 () -> mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSwitchedTo,
1303                         /* executor= */ null, callback));
1304 
1305         assertThat(exception).hasMessageThat().contains("Executor can not be null");
1306     }
1307 
1308     @Test
1309     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1310     @ApiTest(apis = {"android.car.media.CarAudioManager#switchAudioZoneToConfig"})
switchAudioZoneToConfig_withNullCallback_fails()1311     public void switchAudioZoneToConfig_withNullCallback_fails() throws Exception {
1312         assumeDynamicRoutingIsEnabled();
1313         assumeSecondaryZoneConfigs();
1314         CarAudioZoneConfigInfo zoneConfigInfoSwitchedTo = getNonCurrentZoneConfig(mZoneId);
1315         Executor callbackExecutor = Executors.newFixedThreadPool(1);
1316 
1317         NullPointerException exception = assertThrows(NullPointerException.class,
1318                 () -> mCarAudioManager.switchAudioZoneToConfig(zoneConfigInfoSwitchedTo,
1319                         callbackExecutor, /* callback= */ null));
1320 
1321         assertThat(exception).hasMessageThat().contains("callback can not be null");
1322     }
1323 
1324     @Test
1325     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1326     @ApiTest(apis = {"android.car.media.CarAudioManager"
1327             + "#setAudioZoneMirrorStatusCallback"})
setAudioZoneMirrorStatusCallback()1328     public void setAudioZoneMirrorStatusCallback() {
1329         assumeAudioMirrorEnabled();
1330         assumePassengersForAudioMirror();
1331         Executor executor = Executors.newFixedThreadPool(1);
1332         TestAudioZonesMirrorStatusCallback callback;
1333         boolean registered = false;
1334 
1335         try {
1336             callback = new TestAudioZonesMirrorStatusCallback();
1337             registered = mCarAudioManager.setAudioZoneMirrorStatusCallback(executor, callback);
1338         } finally {
1339             if (registered) {
1340                 mCarAudioManager.clearAudioZonesMirrorStatusCallback();
1341             }
1342         }
1343 
1344         assertWithMessage("Audio zone mirror status callback registered status")
1345                 .that(registered).isTrue();
1346     }
1347 
1348     @Test
1349     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1350     @ApiTest(apis = {"android.car.media.CarAudioManager#enableMirrorForAudioZones"})
enableMirrorForAudioZones()1351     public void enableMirrorForAudioZones() throws Exception {
1352         assumeAudioMirrorEnabled();
1353         List<Integer> audioZones = assumePassengersForAudioMirror();
1354         setupAudioMirrorStatusCallback();
1355 
1356         mMirrorRequestId = mCarAudioManager.enableMirrorForAudioZones(audioZones);
1357 
1358         mAudioZonesMirrorCallback.waitForCallback();
1359         assertWithMessage("Enabled mirror for audio zone status")
1360                 .that(mAudioZonesMirrorCallback.mStatus).isEqualTo(
1361                         CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
1362         assertWithMessage("Enabled mirror audio zones")
1363                 .that(mAudioZonesMirrorCallback.mAudioZones).containsExactlyElementsIn(audioZones);
1364     }
1365 
1366     @Test
1367     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1368     @ApiTest(apis = {"android.car.media.CarAudioManager#enableMirrorForAudioZones"})
enableMirrorForAudioZones_withNullList()1369     public void enableMirrorForAudioZones_withNullList() throws Exception {
1370         assumeAudioMirrorEnabled();
1371         setupAudioMirrorStatusCallback();
1372 
1373         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
1374                 mMirrorRequestId = mCarAudioManager
1375                         .enableMirrorForAudioZones(/* audioZonesToMirror= */ null));
1376 
1377         assertWithMessage("Null enable mirror audio zones exception")
1378                 .that(thrown).hasMessageThat().contains("Audio zones to mirror");
1379     }
1380 
1381     @Test
1382     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1383     @ApiTest(apis = {"android.car.media.CarAudioManager#disableAudioMirrorForZone"})
disableAudioMirrorForZone()1384     public void disableAudioMirrorForZone() throws Exception {
1385         assumeAudioMirrorEnabled();
1386         List<Integer> audioZones = assumePassengersForAudioMirror();
1387         int zoneToDisable = audioZones.get(0);
1388         setupAudioMirrorStatusCallback();
1389         mMirrorRequestId = mCarAudioManager.enableMirrorForAudioZones(audioZones);
1390         mAudioZonesMirrorCallback.waitForCallback();
1391         mAudioZonesMirrorCallback.reset();
1392 
1393         mCarAudioManager.disableAudioMirrorForZone(zoneToDisable);
1394 
1395         mMirrorRequestId = INVALID_REQUEST_ID;
1396         mAudioZonesMirrorCallback.waitForCallback();
1397         assertWithMessage("Disable mirror status for audio zone %s", zoneToDisable)
1398                 .that(mAudioZonesMirrorCallback.mStatus).isEqualTo(
1399                         CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
1400         assertWithMessage("Disable mirror zones for audio zone %s", zoneToDisable)
1401                 .that(mAudioZonesMirrorCallback.mAudioZones).containsExactlyElementsIn(audioZones);
1402     }
1403 
1404     @Test
1405     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1406     @ApiTest(apis = {"android.car.media.CarAudioManager#disableAudioMirror"})
disableAudioMirror()1407     public void disableAudioMirror() throws Exception {
1408         assumeAudioMirrorEnabled();
1409         List<Integer> audioZones = assumePassengersForAudioMirror();
1410         setupAudioMirrorStatusCallback();
1411         mMirrorRequestId = mCarAudioManager.enableMirrorForAudioZones(audioZones);
1412         mAudioZonesMirrorCallback.waitForCallback();
1413         mAudioZonesMirrorCallback.reset();
1414 
1415         mCarAudioManager.disableAudioMirror(mMirrorRequestId);
1416 
1417         mMirrorRequestId = INVALID_REQUEST_ID;
1418         mAudioZonesMirrorCallback.waitForCallback();
1419         assertWithMessage("Disable mirror status for audio zones")
1420                 .that(mAudioZonesMirrorCallback.mStatus).isEqualTo(
1421                         CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
1422         assertWithMessage("Disable mirror zones for audio zones")
1423                 .that(mAudioZonesMirrorCallback.mAudioZones).containsExactlyElementsIn(audioZones);
1424     }
1425 
1426     @Test
1427     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1428     @ApiTest(apis = {"android.car.media.CarAudioManager#getMirrorAudioZonesForAudioZone"})
getMirrorAudioZonesForAudioZone()1429     public void getMirrorAudioZonesForAudioZone() throws Exception {
1430         assumeAudioMirrorEnabled();
1431         List<Integer> audioZones = assumePassengersForAudioMirror();
1432         int zoneToQuery = audioZones.get(0);
1433         setupAudioMirrorStatusCallback();
1434         mMirrorRequestId = mCarAudioManager.enableMirrorForAudioZones(audioZones);
1435         mAudioZonesMirrorCallback.waitForCallback();
1436 
1437         List<Integer> queriedZones = mCarAudioManager.getMirrorAudioZonesForAudioZone(zoneToQuery);
1438 
1439         assertWithMessage("Queried audio zones").that(queriedZones)
1440                 .containsExactlyElementsIn(audioZones);
1441     }
1442 
1443     @Test
1444     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS)
1445     @ApiTest(apis = {"android.car.media.CarAudioManager#getMirrorAudioZonesForMirrorRequest"})
getMirrorAudioZonesForMirrorRequest()1446     public void getMirrorAudioZonesForMirrorRequest() throws Exception {
1447         assumeAudioMirrorEnabled();
1448         List<Integer> audioZones = assumePassengersForAudioMirror();
1449         setupAudioMirrorStatusCallback();
1450         mMirrorRequestId = mCarAudioManager.enableMirrorForAudioZones(audioZones);
1451         mAudioZonesMirrorCallback.waitForCallback();
1452 
1453         List<Integer> queriedZones = mCarAudioManager.getMirrorAudioZonesForMirrorRequest(
1454                 mMirrorRequestId);
1455 
1456         assertWithMessage("Queried audio zones for request %s", mMirrorRequestId).that(queriedZones)
1457                 .containsExactlyElementsIn(audioZones);
1458     }
1459 
requestToPlayMediaInPrimaryZone( OccupantZoneInfo info)1460     private TestMediaAudioRequestStatusCallback requestToPlayMediaInPrimaryZone(
1461             OccupantZoneInfo info) {
1462         Executor callbackExecutor = Executors.newFixedThreadPool(1);
1463         TestMediaAudioRequestStatusCallback callback = new TestMediaAudioRequestStatusCallback();
1464         mMediaRequestId =
1465                 mCarAudioManager.requestMediaAudioOnPrimaryZone(info, callbackExecutor, callback);
1466         return callback;
1467     }
1468 
setupMediaAudioRequestCallback()1469     private void setupMediaAudioRequestCallback() {
1470         Executor requestExecutor = Executors.newFixedThreadPool(1);
1471         mRequestCallback = new TestPrimaryZoneMediaAudioRequestStatusCallback();
1472         mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(requestExecutor, mRequestCallback);
1473     }
1474 
setupAudioMirrorStatusCallback()1475     private void setupAudioMirrorStatusCallback() {
1476         Executor executor = Executors.newFixedThreadPool(1);
1477         mAudioZonesMirrorCallback = new TestAudioZonesMirrorStatusCallback();
1478         mCarAudioManager.setAudioZoneMirrorStatusCallback(executor, mAudioZonesMirrorCallback);
1479     }
1480 
assumePassengerWithValidAudioZone()1481     private int assumePassengerWithValidAudioZone() {
1482         List<Integer> audioZonesWithPassengers = assumePassengersWithValidAudioZones(
1483                 /* count= */ 1, "Audio share to primary zone");
1484 
1485         return audioZonesWithPassengers.get(0);
1486     }
1487 
assumePassengersWithValidAudioZones(int minimumCount, String message)1488     private List<Integer> assumePassengersWithValidAudioZones(int minimumCount, String message) {
1489         List<Integer> audioZonesWithPassengers = getAvailablePassengerAudioZone();
1490         assumeTrue(message + ": Need at least " + minimumCount
1491                         + " passenger(s) with valid audio zone id",
1492                 audioZonesWithPassengers.size() >= minimumCount);
1493 
1494         return audioZonesWithPassengers;
1495     }
1496 
assumePassengersForAudioMirror()1497     private List<Integer> assumePassengersForAudioMirror() {
1498         List<Integer> audioZonesWithPassengers = assumePassengersWithValidAudioZones(/* count= */ 2,
1499                 "Passenger audio mirror");
1500 
1501         return audioZonesWithPassengers.subList(/* fromIndex= */ 0, /* toIndex= */ 2);
1502     }
1503 
getAvailablePassengerAudioZone()1504     private List<Integer> getAvailablePassengerAudioZone() {
1505         return mCarOccupantZoneManager.getAllOccupantZones().stream()
1506                 .filter(occupant -> mCarOccupantZoneManager.getUserForOccupant(occupant)
1507                         != CarOccupantZoneManager.INVALID_USER_ID)
1508                 .map(occupant -> mCarOccupantZoneManager.getAudioZoneIdForOccupant(occupant))
1509                 .filter(audioZoneId -> audioZoneId != INVALID_AUDIO_ZONE
1510                         && audioZoneId != PRIMARY_AUDIO_ZONE)
1511                 .collect(Collectors.toList());
1512     }
1513 
assumeSecondaryZoneConfigs()1514     private List<TestZoneConfigIdInfo> assumeSecondaryZoneConfigs() {
1515         List<Integer> audioZonesWithPassengers = getAvailablePassengerAudioZone();
1516         assumeFalse("Requires a zone with a passenger/user", audioZonesWithPassengers.isEmpty());
1517         SparseArray<List<TestZoneConfigIdInfo>> zoneConfigs = parseAudioZoneConfigs();
1518         List<TestZoneConfigIdInfo> secondaryZoneConfigs = null;
1519         for (int index = 0; index < audioZonesWithPassengers.size(); index++) {
1520             int zoneId = audioZonesWithPassengers.get(index);
1521             if (zoneConfigs.contains(zoneId) && zoneConfigs.get(zoneId).size() > 1) {
1522                 mZoneId = zoneId;
1523                 secondaryZoneConfigs = zoneConfigs.get(zoneId);
1524                 break;
1525             }
1526         }
1527         assumeTrue("Secondary zones requires multiple zone configurations",
1528                 secondaryZoneConfigs != null);
1529         return secondaryZoneConfigs;
1530     }
1531 
parsePrimaryZoneConfigs()1532     private List<TestZoneConfigIdInfo> parsePrimaryZoneConfigs() {
1533         SparseArray<List<TestZoneConfigIdInfo>> zoneConfigs = parseAudioZoneConfigs();
1534         List<TestZoneConfigIdInfo> primaryZoneConfigs = zoneConfigs.get(PRIMARY_AUDIO_ZONE);
1535         assertWithMessage("Dumped primary audio zone configuration")
1536                 .that(primaryZoneConfigs).isNotNull();
1537         return primaryZoneConfigs;
1538     }
1539 
parseAudioZoneConfigs()1540     private SparseArray<List<TestZoneConfigIdInfo>> parseAudioZoneConfigs() {
1541         SparseArray<List<TestZoneConfigIdInfo>> zoneConfigs = new SparseArray<>();
1542         if (Flags.carDumpToProto()) {
1543             List<CarAudioZoneProto> zoneProtoList = mCarAudioServiceProtoDump
1544                     .getCarAudioZonesList();
1545             for (int zoneIndex = 0; zoneIndex < zoneProtoList.size(); zoneIndex++) {
1546                 int zoneId = zoneProtoList.get(zoneIndex).getId();
1547                 List<CarAudioZoneConfigProto> zoneConfigProtoList = zoneProtoList.get(zoneIndex)
1548                         .getZoneConfigsList();
1549                 for (int configIndex = 0; configIndex < zoneConfigProtoList.size(); configIndex++) {
1550                     if (!zoneConfigs.contains(zoneId)) {
1551                         zoneConfigs.put(zoneId, new ArrayList<>());
1552                     }
1553                     int zoneConfigId = zoneConfigProtoList.get(configIndex).getId();
1554                     String configName = zoneConfigProtoList.get(configIndex).getName();
1555                     zoneConfigs.get(zoneId).add(new TestZoneConfigIdInfo(zoneId, zoneConfigId,
1556                             configName));
1557                 }
1558             }
1559         } else {
1560             Matcher zoneConfigMatcher = ZONE_CONFIG_PATTERN.matcher(mCarAudioServiceDump);
1561             while (zoneConfigMatcher.find()) {
1562                 int zoneId = Integer.parseInt(zoneConfigMatcher.group(3));
1563                 int zoneConfigId = Integer.parseInt(zoneConfigMatcher.group(2));
1564                 String configName = zoneConfigMatcher.group(1);
1565                 if (!zoneConfigs.contains(zoneId)) {
1566                     zoneConfigs.put(zoneId, new ArrayList<>());
1567                 }
1568                 zoneConfigs.get(zoneId).add(new TestZoneConfigIdInfo(zoneId, zoneConfigId,
1569                         configName));
1570             }
1571         }
1572         return zoneConfigs;
1573     }
1574 
getNonCurrentZoneConfig(int zoneId)1575     private CarAudioZoneConfigInfo getNonCurrentZoneConfig(int zoneId) {
1576         List<CarAudioZoneConfigInfo> zoneConfigInfos =
1577                 mCarAudioManager.getAudioZoneConfigInfos(zoneId);
1578         CarAudioZoneConfigInfo currentZoneConfigInfo =
1579                 mCarAudioManager.getCurrentAudioZoneConfigInfo(zoneId);
1580 
1581         CarAudioZoneConfigInfo differentZoneConfig = null;
1582         for (int index = 0; index < zoneConfigInfos.size(); index++) {
1583             if (!currentZoneConfigInfo.equals(zoneConfigInfos.get(index))) {
1584                 differentZoneConfig = zoneConfigInfos.get(index);
1585                 break;
1586             }
1587         }
1588 
1589         return differentZoneConfig;
1590     }
1591 
1592     @Test
1593     @ApiTest(apis = {"android.car.media.CarAudioManager"
1594             + "#registerCarVolumeGroupEventCallback"})
registerCarVolumeGroupEventCallback_nullCallback_throwsNPE()1595     public void registerCarVolumeGroupEventCallback_nullCallback_throwsNPE() {
1596         Executor executor = Executors.newFixedThreadPool(1);
1597 
1598         NullPointerException exception = assertThrows(NullPointerException.class,
1599                 () -> mCarAudioManager.registerCarVolumeGroupEventCallback(executor,
1600                         /* callback= */ null));
1601 
1602         assertWithMessage("Register car volume group event with null callback exception")
1603                 .that(exception).hasMessageThat()
1604                 .contains("Car volume event callback can not be null");
1605     }
1606 
1607     @Test
1608     @ApiTest(apis = {"android.car.media.CarAudioManager"
1609             + "#registerCarVolumeGroupEventCallback"})
registerCarVolumeGroupEventCallback_nullExecutor_throwsNPE()1610     public void registerCarVolumeGroupEventCallback_nullExecutor_throwsNPE() {
1611         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1612 
1613         NullPointerException exception = assertThrows(NullPointerException.class,
1614                 () -> mCarAudioManager.registerCarVolumeGroupEventCallback(/* executor= */ null,
1615                         mEventCallback));
1616 
1617         mEventCallback = null;
1618         assertWithMessage("Register car volume group event with null executor exception")
1619                 .that(exception).hasMessageThat().contains("Executor can not be null");
1620     }
1621 
1622     @Test
1623     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1624     @ApiTest(apis = {"android.car.media.CarAudioManager"
1625             + "#registerCarVolumeGroupEventCallback"})
registerCarVolumeGroupEventCallback_volumeGroupEventsDisabled()1626     public void registerCarVolumeGroupEventCallback_volumeGroupEventsDisabled() throws Exception {
1627         assumeDynamicRoutingIsEnabled();
1628         assumeVolumeGroupEventsIsDisabled();
1629         Executor executor = Executors.newFixedThreadPool(1);
1630         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1631 
1632         Exception exception = assertThrows(IllegalStateException.class,
1633                 () -> mCarAudioManager.registerCarVolumeGroupEventCallback(executor,
1634                         mEventCallback));
1635 
1636         mEventCallback = null;
1637         assertWithMessage("Register car volume group event with feature disabled")
1638                 .that(exception).hasMessageThat().contains("Car Volume Group Event is required");
1639     }
1640 
1641     @Test
1642     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1643     @ApiTest(apis = {
1644             "android.car.media.CarAudioManager#registerCarVolumeGroupEventCallback"})
registerCarVolumeGroupEventCallback_onVolumeGroupEvent()1645     public void registerCarVolumeGroupEventCallback_onVolumeGroupEvent() throws Exception {
1646         assumeDynamicRoutingIsEnabled();
1647         assumeVolumeGroupEventsIsEnabled();
1648         Executor executor = Executors.newFixedThreadPool(1);
1649         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1650 
1651         boolean status = mCarAudioManager.registerCarVolumeGroupEventCallback(executor,
1652                 mEventCallback);
1653 
1654         injectVolumeUpKeyEvent();
1655         assertWithMessage("Car volume group event callback")
1656                 .that(mEventCallback.receivedVolumeGroupEvents()).isTrue();
1657     }
1658 
1659     @Test
1660     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1661     @ApiTest(apis = {"android.car.media.CarAudioManager"
1662             + "#registerCarVolumeGroupEventCallback"})
registerCarVolumeGroupEventCallback_registerCarVolumeCallback_onVolumeGroupEvent()1663     public void registerCarVolumeGroupEventCallback_registerCarVolumeCallback_onVolumeGroupEvent()
1664             throws Exception {
1665         assumeDynamicRoutingIsEnabled();
1666         assumeVolumeGroupEventsIsEnabled();
1667         Executor executor = Executors.newFixedThreadPool(1);
1668         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1669         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
1670         mCarAudioManager.registerCarVolumeGroupEventCallback(executor, mEventCallback);
1671 
1672         mCarAudioManager.registerCarVolumeCallback(mCallback);
1673 
1674         injectVolumeDownKeyEvent();
1675         assertWithMessage("Car volume group event for registered callback")
1676             .that(mEventCallback.receivedVolumeGroupEvents()).isTrue();
1677         assertWithMessage("Car group volume changed for deprioritized callback")
1678             .that(mCallback.receivedGroupVolumeChanged()).isFalse();
1679     }
1680 
1681     @Test
1682     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1683     @ApiTest(apis = {"android.car.media.CarAudioManager"
1684             + "#registerCarVolumeGroupEventCallback",
1685             "android.car.media.CarAudioManager"
1686                     + "#unregisterCarVolumeGroupEventCallback"})
unregisterCarVolumeGroupEventCallback_onGroupVolumeChanged()1687     public void unregisterCarVolumeGroupEventCallback_onGroupVolumeChanged()
1688             throws Exception {
1689         assumeDynamicRoutingIsEnabled();
1690         assumeVolumeGroupEventsIsEnabled();
1691         Executor executor = Executors.newFixedThreadPool(1);
1692         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1693         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
1694         mCarAudioManager.registerCarVolumeGroupEventCallback(executor, mEventCallback);
1695         mCarAudioManager.registerCarVolumeCallback(mCallback);
1696 
1697         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mEventCallback);
1698 
1699         injectVolumeUpKeyEvent();
1700         assertWithMessage("Car volume group event for unregistered callback")
1701             .that(mEventCallback.receivedVolumeGroupEvents()).isFalse();
1702         assertWithMessage("Car group volume changed for reprioritized callback")
1703             .that(mCallback.receivedGroupVolumeChanged()).isTrue();
1704     }
1705 
1706     @Test
1707     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1708     @ApiTest(apis = {"android.car.media.CarAudioManager"
1709             + "#registerCarVolumeGroupEventCallback",
1710             "android.car.media.CarAudioManager"
1711                     + "#unregisterCarVolumeGroupEventCallback"})
reRegisterCarVolumeGroupEventCallback_eventCallbackReprioritized()1712     public void reRegisterCarVolumeGroupEventCallback_eventCallbackReprioritized()
1713             throws Exception {
1714         assumeDynamicRoutingIsEnabled();
1715         assumeVolumeGroupEventsIsEnabled();
1716         Executor executor = Executors.newFixedThreadPool(1);
1717         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1718         mCallback = new CarAudioManagerTestUtils.SyncCarVolumeCallback();
1719         mCarAudioManager.registerCarVolumeGroupEventCallback(executor, mEventCallback);
1720         mCarAudioManager.registerCarVolumeCallback(mCallback);
1721         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mEventCallback);
1722 
1723         mCarAudioManager.registerCarVolumeGroupEventCallback(executor, mEventCallback);
1724 
1725         injectVolumeDownKeyEvent();
1726         assertWithMessage("Car volume group event for re-registered callback")
1727             .that(mEventCallback.receivedVolumeGroupEvents()).isTrue();
1728         assertWithMessage("Car group volume changed for deprioritized callback")
1729             .that(mCallback.receivedGroupVolumeChanged()).isFalse();
1730     }
1731 
1732     @Test
1733     @ApiTest(apis = {"android.car.media.CarAudioManager"
1734             + "#unregisterCarVolumeGroupEventCallback"})
unregisterCarVolumeGroupEventCallback_nullCallback_throwsNPE()1735     public void unregisterCarVolumeGroupEventCallback_nullCallback_throwsNPE() {
1736         NullPointerException exception = assertThrows(NullPointerException.class,
1737                 () -> mCarAudioManager.unregisterCarVolumeGroupEventCallback(/* callback= */ null));
1738 
1739         assertWithMessage("Unregister car volume group event with null callback exception")
1740                 .that(exception).hasMessageThat()
1741                 .contains("Car volume event callback can not be null");
1742     }
1743 
1744     @Test
1745     @ApiTest(apis = {"android.car.media.CarAudioManager"
1746             + "#unregisterCarVolumeGroupEventCallback"})
unregisterCarVolumeGroupEventCallback_withoutPermission_throws()1747     public void unregisterCarVolumeGroupEventCallback_withoutPermission_throws()
1748             throws Exception {
1749         assumeDynamicRoutingIsEnabled();
1750         assumeVolumeGroupEventsIsEnabled();
1751         Executor executor = Executors.newFixedThreadPool(1);
1752         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1753         runWithCarControlAudioVolumePermission(
1754                 () -> mCarAudioManager.registerCarVolumeGroupEventCallback(executor,
1755                         mEventCallback));
1756 
1757         Exception exception = assertThrows(SecurityException.class,
1758                 () -> mCarAudioManager.unregisterCarVolumeGroupEventCallback(mEventCallback));
1759 
1760         assertWithMessage("Unregister car volume group event callback without permission exception")
1761                 .that(exception).hasMessageThat().contains(PERMISSION_CAR_CONTROL_AUDIO_VOLUME);
1762 
1763     }
1764 
1765     @Test
1766     @ApiTest(apis = {"android.car.media.CarAudioManager"
1767             + "#unregisterCarVolumeGroupEventCallback"})
unregisterCarVolumeGroupEventCallback_withoutPermission_receivesCallback()1768     public void unregisterCarVolumeGroupEventCallback_withoutPermission_receivesCallback()
1769             throws Exception {
1770         assumeDynamicRoutingIsEnabled();
1771         assumeVolumeGroupEventsIsEnabled();
1772         Executor executor = Executors.newFixedThreadPool(1);
1773         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1774         runWithCarControlAudioVolumePermission(
1775                 () -> mCarAudioManager.registerCarVolumeGroupEventCallback(executor,
1776                         mEventCallback));
1777 
1778         Exception exception = assertThrows(SecurityException.class,
1779                 () -> mCarAudioManager.unregisterCarVolumeGroupEventCallback(mEventCallback));
1780 
1781         injectVolumeDownKeyEvent();
1782         assertWithMessage("Car volume group event after unregister security exception")
1783                 .that(mEventCallback.receivedVolumeGroupEvents()).isTrue();
1784     }
1785 
1786     @Test
1787     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1788     @ApiTest(apis = {"android.car.media.CarAudioManager"
1789             + "#registerCarVolumeGroupEventCallback",
1790             "android.car.media.CarAudioManager"
1791                     + "#unregisterCarVolumeGroupEventCallback"})
unregisterCarVolumeGroupEventCallback_noLongerReceivesEventCallback()1792     public void unregisterCarVolumeGroupEventCallback_noLongerReceivesEventCallback()
1793             throws Exception {
1794         assumeDynamicRoutingIsEnabled();
1795         assumeVolumeGroupEventsIsEnabled();
1796         Executor executor = Executors.newFixedThreadPool(1);
1797         mEventCallback = new CarAudioManagerTestUtils.TestCarVolumeGroupEventCallback();
1798         mCarAudioManager.registerCarVolumeGroupEventCallback(executor, mEventCallback);
1799 
1800         mCarAudioManager.unregisterCarVolumeGroupEventCallback(mEventCallback);
1801 
1802         injectVolumeUpKeyEvent();
1803         assertWithMessage("Car volume group event for unregistered callback")
1804             .that(mEventCallback.receivedVolumeGroupEvents()).isFalse();
1805     }
1806 
1807     @Test
1808     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1809     @ApiTest(apis = {"android.car.media.CarAudioManager#isVolumeGroupMuted",
1810             "android.car.media.CarVolumeGroupInfo#isMuted"})
isVolumeGroupMuted()1811     public void isVolumeGroupMuted() {
1812         assumeDynamicRoutingIsEnabled();
1813         readFirstZoneAndVolumeGroup();
1814         CarVolumeGroupInfo volumeGroupInfo = mCarAudioManager.getVolumeGroupInfo(mZoneId,
1815                 mVolumeGroupId);
1816 
1817         boolean isMuted = mCarAudioManager.isVolumeGroupMuted(mZoneId, mVolumeGroupId);
1818 
1819         assertWithMessage("Mute state for zone %s group %s", mZoneId, mVolumeGroupId)
1820                 .that(isMuted).isEqualTo(volumeGroupInfo.isMuted());
1821     }
1822 
1823     @Test
1824     @EnsureHasPermission(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME)
1825     @ApiTest(apis = {"android.car.media.CarAudioManager#setVolumeGroupMute",
1826             "android.car.media.CarAudioManager#isVolumeGroupMuted",
1827             "android.car.media.CarVolumeGroupInfo#isMutedBySystem"})
1828     @RequiresFlagsEnabled(Flags.FLAG_CAR_AUDIO_MUTE_AMBIGUITY)
setVolumeGroupMute_withMuteAmbiguityFlagEnabled()1829     public void setVolumeGroupMute_withMuteAmbiguityFlagEnabled() {
1830         assumeDynamicRoutingIsEnabled();
1831         assumeVolumeGroupMutingIsEnabled();
1832         readFirstZoneAndVolumeGroup();
1833         boolean isMuted = mCarAudioManager.isVolumeGroupMuted(mZoneId, mVolumeGroupId);
1834         boolean isMutedBySystem = mCarAudioManager.getVolumeGroupInfo(mZoneId,
1835                 mVolumeGroupId).isMutedBySystem();
1836         String muteOperationStr = isMuted ? "unmuting" : "muting";
1837 
1838         try {
1839             mCarAudioManager.setVolumeGroupMute(mZoneId, mVolumeGroupId, !isMuted, /* flags= */ 0);
1840 
1841             assertWithMessage("Mute state for zone %s group %s after %s", mZoneId, mVolumeGroupId,
1842                     muteOperationStr)
1843                     .that(mCarAudioManager.isVolumeGroupMuted(mZoneId, mVolumeGroupId))
1844                     .isEqualTo(mCarAudioManager.getVolumeGroupInfo(mZoneId, mVolumeGroupId)
1845                             .isMuted());
1846             assertWithMessage("Mute by system state for zone %s group %s after %s", mZoneId,
1847                     mVolumeGroupId, muteOperationStr)
1848                     .that(mCarAudioManager.getVolumeGroupInfo(mZoneId, mVolumeGroupId)
1849                             .isMutedBySystem()).isEqualTo(isMutedBySystem);
1850         } finally {
1851             mCarAudioManager.setVolumeGroupMute(mZoneId, mVolumeGroupId, isMuted, /* flags= */ 0);
1852         }
1853     }
1854 
getNumberOfPrimaryZoneAudioMediaCallbacks()1855     private int getNumberOfPrimaryZoneAudioMediaCallbacks() {
1856         if (Flags.carDumpToProto()) {
1857             return mCarAudioServiceProtoDump.getMediaRequestHandler().hasMediaRequestCallbackCount()
1858                     ? mCarAudioServiceProtoDump.getMediaRequestHandler()
1859                     .getMediaRequestCallbackCount() : 0;
1860         }
1861         Matcher matchCount = PRIMARY_ZONE_MEDIA_REQUEST_APPROVERS_PATTERN
1862                 .matcher(mCarAudioServiceDump);
1863         assertWithMessage("No Car Audio Media in dump").that(matchCount.find()).isTrue();
1864         return Integer.parseInt(matchCount.group(1));
1865     }
1866 
assumeNoPrimaryZoneAudioMediaApprovers()1867     private void assumeNoPrimaryZoneAudioMediaApprovers() {
1868         assumeTrue("Primary zone audio media approvers must be empty",
1869                 getNumberOfPrimaryZoneAudioMediaCallbacks() == 0);
1870     }
1871 
setUpDefaultCarAudioConfigurationForZone(int zoneId)1872     private void setUpDefaultCarAudioConfigurationForZone(int zoneId) throws Exception {
1873         CarAudioZoneConfigInfo current =
1874                 mCarAudioManager.getCurrentAudioZoneConfigInfo(zoneId);
1875         if (current != null && current.isDefault()) {
1876             return;
1877         }
1878 
1879         CarAudioZoneConfigInfo defaultConfig =
1880                 mCarAudioManager.getAudioZoneConfigInfos(zoneId).stream()
1881                         .filter(CarAudioZoneConfigInfo::isDefault).findFirst().orElseThrow();
1882         Executor callbackExecutor = Executors.newFixedThreadPool(1);
1883         TestSwitchAudioZoneConfigCallback callback = new TestSwitchAudioZoneConfigCallback();
1884         mCarAudioManager.switchAudioZoneToConfig(defaultConfig, callbackExecutor, callback);
1885         callback.receivedApproval();
1886     }
1887 
assumeDynamicRoutingIsEnabled()1888     private void assumeDynamicRoutingIsEnabled() {
1889         assumeTrue("Requires dynamic audio routing",
1890                 mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING));
1891     }
1892 
assumeDynamicRoutingIsDisabled()1893     private void assumeDynamicRoutingIsDisabled() {
1894         assumeFalse("Requires dynamic audio routing",
1895                 mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING));
1896     }
1897 
assumeVolumeGroupMutingIsEnabled()1898     private void assumeVolumeGroupMutingIsEnabled() {
1899         assumeTrue("Requires volume group muting",
1900                 mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING));
1901     }
1902 
assumeVolumeGroupMutingIsDisabled()1903     private void assumeVolumeGroupMutingIsDisabled() {
1904         assumeFalse("Requires volume group muting disabled",
1905                 mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING));
1906     }
1907 
assumeAudioMirrorEnabled()1908     private void assumeAudioMirrorEnabled() {
1909         assumeTrue("Requires audio mirroring",
1910                 mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_AUDIO_MIRRORING));
1911     }
1912 
assumeVolumeGroupEventsIsEnabled()1913     private void assumeVolumeGroupEventsIsEnabled() {
1914         assumeTrue(mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS));
1915     }
1916 
assumeVolumeGroupEventsIsDisabled()1917     private void assumeVolumeGroupEventsIsDisabled() {
1918         assumeFalse(mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS));
1919     }
1920 
runWithCarControlAudioVolumePermission(Runnable runnable)1921     private void runWithCarControlAudioVolumePermission(Runnable runnable) {
1922         UI_AUTOMATION.adoptShellPermissionIdentity(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME);
1923         try {
1924             runnable.run();
1925         } finally {
1926             UI_AUTOMATION.dropShellPermissionIdentity();
1927         }
1928     }
1929 
injectKeyEvent(int keyCode)1930     private void injectKeyEvent(int keyCode) {
1931         long downTime = SystemClock.uptimeMillis();
1932         KeyEvent volumeDown = new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN,
1933             keyCode, 0);
1934         UI_AUTOMATION.injectInputEvent(volumeDown, true);
1935     }
1936 
injectVolumeDownKeyEvent()1937     private void injectVolumeDownKeyEvent() {
1938         injectKeyEvent(KeyEvent.KEYCODE_VOLUME_DOWN);
1939     }
1940 
injectVolumeUpKeyEvent()1941     private void injectVolumeUpKeyEvent() {
1942         injectKeyEvent(KeyEvent.KEYCODE_VOLUME_UP);
1943     }
1944 
injectVolumeMuteKeyEvent()1945     private void injectVolumeMuteKeyEvent() {
1946         injectKeyEvent(KeyEvent.KEYCODE_VOLUME_MUTE);
1947     }
1948 
readFirstZoneAndVolumeGroup()1949     private void readFirstZoneAndVolumeGroup() {
1950         if (Flags.carDumpToProto()) {
1951             List<CarAudioZoneProto> zoneProtoList = mCarAudioServiceProtoDump
1952                     .getCarAudioZonesList();
1953             assertWithMessage("No CarAudioZone in proto dump").that(zoneProtoList)
1954                     .isNotEmpty();
1955             mZoneId = zoneProtoList.get(0).getId();
1956             readFirstVolumeGroupAndVolumeGroupCountFromProto(zoneProtoList.get(0));
1957             return;
1958         }
1959         Matcher matchZone = ZONE_PATTERN.matcher(mCarAudioServiceDump);
1960         assertWithMessage("No CarAudioZone in dump").that(matchZone.find()).isTrue();
1961         mZoneId = Integer.parseInt(matchZone.group(1));
1962         mConfigId = Integer.parseInt(matchZone.group(3));
1963         readFirstVolumeGroupAndVolumeGroupCount(mZoneId, mConfigId);
1964     }
1965 
assumePrimaryZone()1966     private void assumePrimaryZone() {
1967         if (Flags.carDumpToProto()) {
1968             List<CarAudioZoneProto> zoneProtoList = mCarAudioServiceProtoDump
1969                     .getCarAudioZonesList();
1970             for (int zoneIndex = 0; zoneIndex < zoneProtoList.size(); zoneIndex++) {
1971                 int zoneId = zoneProtoList.get(zoneIndex).getId();
1972                 if (zoneId == PRIMARY_AUDIO_ZONE) {
1973                     mZoneId = zoneId;
1974                     readFirstVolumeGroupAndVolumeGroupCountFromProto(zoneProtoList.get(zoneIndex));
1975                     break;
1976                 }
1977             }
1978         } else {
1979             Matcher matchZone = ZONE_PATTERN.matcher(mCarAudioServiceDump);
1980             while (matchZone.find()) {
1981                 if (Integer.parseInt(matchZone.group(1)) == PRIMARY_AUDIO_ZONE) {
1982                     mZoneId = PRIMARY_AUDIO_ZONE;
1983                     mConfigId = Integer.parseInt(matchZone.group(3));
1984                     readFirstVolumeGroupAndVolumeGroupCount(mZoneId, mConfigId);
1985                     break;
1986                 }
1987             }
1988         }
1989         assumeTrue("Primary zone exists", mZoneId == PRIMARY_AUDIO_ZONE);
1990     }
1991 
readFirstVolumeGroupAndVolumeGroupCount(int zoneId, int currentConfigId)1992     private void readFirstVolumeGroupAndVolumeGroupCount(int zoneId, int currentConfigId) {
1993         Matcher matchGroup = VOLUME_GROUP_PATTERN.matcher(mCarAudioServiceDump);
1994         boolean findVolumeGroup = false;
1995         mVolumeGroupCount = 0;
1996         while (matchGroup.find()) {
1997             if (Integer.parseInt(matchGroup.group(3)) == zoneId
1998                     && Integer.parseInt(matchGroup.group(4)) == currentConfigId) {
1999                 if (!findVolumeGroup) {
2000                     mVolumeGroupId = Integer.parseInt(matchGroup.group(1));
2001                     findVolumeGroup = true;
2002                 }
2003                 mVolumeGroupCount++;
2004             }
2005         }
2006         assertWithMessage("No CarVolumeGroup in dump").that(findVolumeGroup).isTrue();
2007     }
2008 
readFirstVolumeGroupAndVolumeGroupCountFromProto(CarAudioZoneProto zoneProto)2009     private void readFirstVolumeGroupAndVolumeGroupCountFromProto(CarAudioZoneProto zoneProto) {
2010         mConfigId = zoneProto.getCurrentZoneConfigId();
2011         List<CarAudioZoneConfigProto> zoneConfigProtoList = zoneProto.getZoneConfigsList();
2012         for (int configIndex = 0; configIndex < zoneConfigProtoList.size(); configIndex++) {
2013             if (zoneConfigProtoList.get(configIndex).getId() != mConfigId) {
2014                 continue;
2015             }
2016             List<CarVolumeGroupProto> volumeGroupProtoList = zoneConfigProtoList
2017                     .get(configIndex).getVolumeGroupsList();
2018             assertWithMessage("No CarVolumeGroup in dump").that(volumeGroupProtoList)
2019                     .isNotEmpty();
2020             mVolumeGroupCount = volumeGroupProtoList.size();
2021             mVolumeGroupId = volumeGroupProtoList.get(0).getId();
2022         }
2023     }
2024 
setVolumeGroupMute(int zoneId, int groupId, boolean mute)2025     private void setVolumeGroupMute(int zoneId, int groupId, boolean mute) {
2026         ShellUtils.runShellCommand("cmd car_service set-mute-car-volume-group %d %d %s",
2027                 zoneId, groupId, mute ? "mute" : "unmute");
2028     }
2029 
assertVolumeGroupIdForUsages(int zoneId, int volumeGroupId, int[] usages, String message)2030     private void assertVolumeGroupIdForUsages(int zoneId, int volumeGroupId, int[] usages,
2031             String message) {
2032         assertWithMessage("Usage for volume group %s in zone %s %s", volumeGroupId, zoneId,
2033                 message).that(usages).isNotEmpty();
2034         for (int i = 0; i < usages.length; i++) {
2035             assertWithMessage("Usage %s in volume group %s in zone %s %s", usages[i], volumeGroupId,
2036                     zoneId, message).that(mCarAudioManager.getVolumeGroupIdForUsage(zoneId,
2037                     usages[i])).isEqualTo(volumeGroupId);
2038         }
2039     }
2040 
2041     private static final class TestZoneConfigIdInfo {
2042         private final int mZoneId;
2043         private final int mConfigId;
2044         private final String mConfigName;
2045 
TestZoneConfigIdInfo(int zoneId, int configId, String configName)2046         TestZoneConfigIdInfo(int zoneId, int configId, String configName) {
2047             mZoneId = zoneId;
2048             mConfigId = configId;
2049             mConfigName = configName;
2050         }
2051 
2052         @Override
equals(Object o)2053         public boolean equals(Object o) {
2054             if (this == o) {
2055                 return true;
2056             }
2057 
2058             if (!(o instanceof TestZoneConfigIdInfo)) {
2059                 return false;
2060             }
2061 
2062             TestZoneConfigIdInfo that = (TestZoneConfigIdInfo) o;
2063 
2064             return mZoneId == that.mZoneId && mConfigId == that.mConfigId
2065                     && mConfigName.equals(that.mConfigName);
2066         }
2067 
getZoneConfigFromInfo(CarAudioZoneConfigInfo configInfo)2068         static TestZoneConfigIdInfo getZoneConfigFromInfo(CarAudioZoneConfigInfo configInfo) {
2069             return new TestZoneConfigIdInfo(configInfo.getZoneId(), configInfo.getConfigId(),
2070                     configInfo.getName());
2071         }
2072 
getZoneConfigListFromInfoList( List<CarAudioZoneConfigInfo> configInfo)2073         static List<TestZoneConfigIdInfo> getZoneConfigListFromInfoList(
2074                 List<CarAudioZoneConfigInfo> configInfo) {
2075             List<TestZoneConfigIdInfo> zoneConfigs = new ArrayList<>(configInfo.size());
2076             Log.e(TAG, "getZoneConfigListFromInfoList " + configInfo.size());
2077             for (int index = 0; index < configInfo.size(); index++) {
2078                 zoneConfigs.add(getZoneConfigFromInfo(configInfo.get(index)));
2079             }
2080             return zoneConfigs;
2081         }
2082     }
2083 
2084     private static final class TestPrimaryZoneMediaAudioRequestStatusCallback implements
2085             PrimaryZoneMediaAudioRequestCallback {
2086 
2087         private final CountDownLatch mRequestAudioLatch = new CountDownLatch(1);
2088         private long mRequestId;
2089         private OccupantZoneInfo mOccupantZoneInfo;
2090 
2091         @Override
onRequestMediaOnPrimaryZone(OccupantZoneInfo info, long requestId)2092         public void onRequestMediaOnPrimaryZone(OccupantZoneInfo info, long requestId) {
2093             mOccupantZoneInfo = info;
2094             mRequestId = requestId;
2095             Log.i(TAG, "onRequestMediaOnPrimaryZone info " + info + " request id " + requestId);
2096             mRequestAudioLatch.countDown();
2097         }
2098 
2099         @Override
onMediaAudioRequestStatusChanged(OccupantZoneInfo info, long requestId, int status)2100         public void onMediaAudioRequestStatusChanged(OccupantZoneInfo info,
2101                 long requestId, int status) {
2102         }
2103 
receivedMediaRequest()2104         void receivedMediaRequest() throws InterruptedException {
2105             await(mRequestAudioLatch, WAIT_TIMEOUT_MS);
2106         }
2107     }
2108 
2109     private static final class TestMediaAudioRequestStatusCallback implements
2110             MediaAudioRequestStatusCallback {
2111 
2112         private CountDownLatch mRequestAudioLatch = new CountDownLatch(1);
2113         private long mRequestId;
2114         private OccupantZoneInfo mOccupantZoneInfo;
2115         private int mStatus;
2116 
receivedApproval()2117         void receivedApproval() throws InterruptedException {
2118             await(mRequestAudioLatch, WAIT_TIMEOUT_MS);
2119         }
2120 
reset()2121         void reset() {
2122             mRequestAudioLatch = new CountDownLatch(1);
2123             mRequestId = INVALID_REQUEST_ID;
2124             mOccupantZoneInfo = null;
2125             mStatus = 0;
2126         }
2127 
2128         @Override
onMediaAudioRequestStatusChanged(OccupantZoneInfo info, long requestId, int status)2129         public void onMediaAudioRequestStatusChanged(OccupantZoneInfo info,
2130                 long requestId, int status) {
2131             mOccupantZoneInfo = info;
2132             mRequestId = requestId;
2133             mStatus = status;
2134             Log.i(TAG, "onMediaAudioRequestStatusChanged info " + info + " request id "
2135                     + requestId + " status " + status);
2136             mRequestAudioLatch.countDown();
2137         }
2138     }
2139 
2140     private static final class TestSwitchAudioZoneConfigCallback implements
2141             SwitchAudioZoneConfigCallback {
2142 
2143         private CountDownLatch mRequestAudioLatch = new CountDownLatch(1);
2144         private CarAudioZoneConfigInfo mZoneConfigInfo;
2145         private boolean mIsSuccessful;
2146 
receivedApproval()2147         void receivedApproval() throws InterruptedException {
2148             await(mRequestAudioLatch, WAIT_TIMEOUT_MS);
2149         }
2150 
reset()2151         void reset() {
2152             mRequestAudioLatch = new CountDownLatch(1);
2153             mZoneConfigInfo = null;
2154             mIsSuccessful = false;
2155         }
2156 
2157         @Override
onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfigInfo, boolean isSuccessful)2158         public void onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfigInfo,
2159                 boolean isSuccessful) {
2160             mZoneConfigInfo = zoneConfigInfo;
2161             mIsSuccessful = isSuccessful;
2162             Log.i(TAG, "onAudioZoneConfigSwitched zoneConfig " + zoneConfigInfo + " is successful? "
2163                     + isSuccessful);
2164             mRequestAudioLatch.countDown();
2165         }
2166     }
2167 
2168     private static final class TestAudioZonesMirrorStatusCallback implements
2169             AudioZonesMirrorStatusCallback {
2170 
2171         private CountDownLatch mRequestAudioLatch = new CountDownLatch(1);
2172         public List<Integer> mAudioZones;
2173         public int mStatus;
2174 
2175         @Override
onAudioZonesMirrorStatusChanged(List<Integer> mirroredAudioZones, int status)2176         public void onAudioZonesMirrorStatusChanged(List<Integer> mirroredAudioZones, int status) {
2177             mAudioZones = mirroredAudioZones;
2178             mStatus = status;
2179             Log.i(TAG, "onAudioZonesMirrorStatusChanged: audio zones " + mirroredAudioZones
2180                     + " status " + status);
2181             mRequestAudioLatch.countDown();
2182         }
2183 
waitForCallback()2184         private void waitForCallback() throws InterruptedException {
2185             await(mRequestAudioLatch, WAIT_TIMEOUT_MS);
2186         }
2187 
reset()2188         public void reset() {
2189             mRequestAudioLatch = new CountDownLatch(1);
2190         }
2191     }
2192 
2193 }
2194