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 com.android.car.audio;
18 
19 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_ATTENUATION_CHANGED;
20 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED;
21 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_BLOCKED_CHANGED;
22 import static android.car.media.CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED;
23 import static android.media.AudioAttributes.USAGE_ALARM;
24 import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
25 import static android.media.AudioAttributes.USAGE_CALL_ASSISTANT;
26 import static android.media.AudioAttributes.USAGE_EMERGENCY;
27 import static android.media.AudioAttributes.USAGE_GAME;
28 import static android.media.AudioAttributes.USAGE_MEDIA;
29 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
30 import static android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT;
31 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
32 import static android.media.AudioAttributes.USAGE_SAFETY;
33 import static android.media.AudioAttributes.USAGE_UNKNOWN;
34 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
35 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
36 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
37 import static android.media.AudioDeviceInfo.TYPE_BUS;
38 
39 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
40 
41 import static org.junit.Assert.assertThrows;
42 import static org.mockito.Mockito.anyInt;
43 import static org.mockito.Mockito.never;
44 import static org.mockito.Mockito.times;
45 import static org.mockito.Mockito.when;
46 
47 import android.annotation.UserIdInt;
48 import android.car.feature.Flags;
49 import android.car.media.CarVolumeGroupInfo;
50 import android.car.test.AbstractExpectableTestCase;
51 import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
52 import android.hardware.automotive.audiocontrol.Reasons;
53 import android.media.AudioAttributes;
54 import android.media.AudioDeviceAttributes;
55 import android.os.UserHandle;
56 import android.platform.test.flag.junit.SetFlagsRule;
57 import android.util.ArraySet;
58 import android.util.SparseBooleanArray;
59 import android.util.SparseIntArray;
60 
61 import org.junit.Before;
62 import org.junit.Rule;
63 import org.junit.Test;
64 import org.junit.runner.RunWith;
65 import org.mockito.Mock;
66 import org.mockito.Mockito;
67 import org.mockito.junit.MockitoJUnitRunner;
68 
69 import java.util.ArrayList;
70 import java.util.List;
71 
72 @RunWith(MockitoJUnitRunner.class)
73 public class CarVolumeGroupUnitTest extends AbstractExpectableTestCase {
74     private static final int ZONE_ID = 0;
75     private static final int CONFIG_ID = 1;
76     private static final int GROUP_ID = 0;
77     private static final int DEFAULT_GAIN_INDEX = (TestCarAudioDeviceInfoBuilder.DEFAULT_GAIN
78             - TestCarAudioDeviceInfoBuilder.MIN_GAIN) / TestCarAudioDeviceInfoBuilder.STEP_VALUE;
79     private static final int MIN_GAIN_INDEX = 0;
80     private static final int MAX_GAIN_INDEX = (TestCarAudioDeviceInfoBuilder.MAX_GAIN
81             - TestCarAudioDeviceInfoBuilder.MIN_GAIN) / TestCarAudioDeviceInfoBuilder.STEP_VALUE;
82     private static final int MIN_ACTIVATION_GAIN_INDEX_PERCENTAGE = 20;
83     private static final int MAX_ACTIVATION_GAIN_INDEX_PERCENTAGE = 80;
84     private static final int SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE =
85             CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_SOURCE_CHANGED;
86     private static final int UNSUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE =
87             CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_PLAYBACK_CHANGED;
88     private static final int ACTIVATION_VOLUME_INVOCATION_TYPE =
89             CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_BOOT
90                     | CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_SOURCE_CHANGED;
91     private static final int MIN_ACTIVATION_GAIN_INDEX = MIN_GAIN_INDEX + (int) Math.round(
92             MIN_ACTIVATION_GAIN_INDEX_PERCENTAGE / 100.0 * (MAX_GAIN_INDEX - MIN_GAIN_INDEX));
93     private static final int MAX_ACTIVATION_GAIN_INDEX = MIN_GAIN_INDEX + (int) Math.round(
94             MAX_ACTIVATION_GAIN_INDEX_PERCENTAGE / 100.0 * (MAX_GAIN_INDEX - MIN_GAIN_INDEX));
95     private static final int TEST_GAIN_INDEX = 2;
96     private static final int TEST_USER_10 = 10;
97     private static final int TEST_USER_11 = 11;
98     private static final String GROUP_NAME = "group_0";
99     private static final String MEDIA_DEVICE_ADDRESS = "music";
100     private static final String NAVIGATION_DEVICE_ADDRESS = "navigation";
101     private static final String OTHER_ADDRESS = "other_address";
102     private static final int EVENT_TYPE_NONE = 0;
103 
104     private static final CarActivationVolumeConfig CAR_ACTIVATION_VOLUME_CONFIG =
105             new CarActivationVolumeConfig(ACTIVATION_VOLUME_INVOCATION_TYPE,
106                     MIN_ACTIVATION_GAIN_INDEX_PERCENTAGE, MAX_ACTIVATION_GAIN_INDEX_PERCENTAGE);
107 
108     private static final CarAudioContext TEST_CAR_AUDIO_CONTEXT =
109             new CarAudioContext(CarAudioContext.getAllContextsInfo(),
110                     /* useCoreAudioRouting= */ false);
111 
112     private static final @CarAudioContext.AudioContext int TEST_MEDIA_CONTEXT_ID =
113             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(
114                     CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA));
115     private static final @CarAudioContext.AudioContext int TEST_ALARM_CONTEXT_ID =
116             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(
117                     CarAudioContext.getAudioAttributeFromUsage(USAGE_ALARM));
118     private static final @CarAudioContext.AudioContext int TEST_CALL_CONTEXT_ID =
119             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(
120                     CarAudioContext.getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION));
121     private static final @CarAudioContext.AudioContext int TEST_CALL_RING_CONTEXT_ID =
122             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(
123                     CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_RINGTONE));
124     private static final @CarAudioContext.AudioContext int TEST_EMERGENCY_CONTEXT_ID =
125             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(
126                     CarAudioContext.getAudioAttributeFromUsage(USAGE_EMERGENCY));
127     private static final @CarAudioContext.AudioContext int TEST_NAVIGATION_CONTEXT_ID =
128             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(CarAudioContext
129                     .getAudioAttributeFromUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE));
130     private static final @CarAudioContext.AudioContext int TEST_NOTIFICATION_CONTEXT_ID =
131             TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(CarAudioContext
132                     .getAudioAttributeFromUsage(USAGE_NOTIFICATION));
133 
134     private CarAudioDeviceInfo mMediaDeviceInfo;
135     private CarAudioDeviceInfo mNavigationDeviceInfo;
136     private CarAudioDeviceInfo mInactiveMediaDeviceInfo;
137 
138     @Mock
139     CarAudioSettings mSettingsMock;
140     @Mock
141     AudioManagerWrapper mAudioManagerMock;
142 
143     @Rule
144     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
145 
146     @Before
setUp()147     public void setUp() {
148         mMediaDeviceInfo = new TestCarAudioDeviceInfoBuilder(MEDIA_DEVICE_ADDRESS).build();
149         mNavigationDeviceInfo = new TestCarAudioDeviceInfoBuilder(NAVIGATION_DEVICE_ADDRESS)
150                 .build();
151         mInactiveMediaDeviceInfo = new TestCarAudioDeviceInfoBuilder(MEDIA_DEVICE_ADDRESS)
152                 .setIsActive(false).setType(TYPE_BLUETOOTH_A2DP).build();
153     }
154 
155     @Test
getAddressForContext_withSupportedContext_returnsAddress()156     public void getAddressForContext_withSupportedContext_returnsAddress() {
157         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
158 
159         expectWithMessage("Supported context's address")
160                 .that(carVolumeGroup.getAddressForContext(TEST_MEDIA_CONTEXT_ID))
161                 .isEqualTo(mMediaDeviceInfo.getAddress());
162     }
163 
164     @Test
getAddressForContext_withUnsupportedContext_returnsNull()165     public void getAddressForContext_withUnsupportedContext_returnsNull() {
166         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
167 
168         expectWithMessage("Unsupported context's address")
169                 .that(carVolumeGroup.getAddressForContext(
170                         TEST_NAVIGATION_CONTEXT_ID)).isNull();
171     }
172 
173     @Test
setMuted_whenUnmuted_onActivation_returnsTrue()174     public void setMuted_whenUnmuted_onActivation_returnsTrue() {
175         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
176 
177         expectWithMessage("Status returned from set mute while unmuted")
178                 .that(carVolumeGroup.setMute(true)).isTrue();
179     }
180 
181     @Test
setMuted_whenUnmuted_onDeactivation_returnsFalse()182     public void setMuted_whenUnmuted_onDeactivation_returnsFalse() {
183         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
184 
185         expectWithMessage("Status returned from set unmute while unmuted")
186                 .that(carVolumeGroup.setMute(false)).isFalse();
187     }
188 
189 
190     @Test
setMuted_whenMuted_onDeactivation_returnsTrue()191     public void setMuted_whenMuted_onDeactivation_returnsTrue() {
192         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
193         carVolumeGroup.setMute(true);
194 
195         expectWithMessage("Status returned from set unmute while muted")
196                 .that(carVolumeGroup.setMute(false)).isTrue();
197     }
198 
199     @Test
setMuted_whenMuted_onActivation_returnsFalse()200     public void setMuted_whenMuted_onActivation_returnsFalse() {
201         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
202         carVolumeGroup.setMute(true);
203 
204         expectWithMessage("Status returned from set mute while muted")
205                 .that(carVolumeGroup.setMute(true)).isFalse();
206     }
207 
208     @Test
setMuted_whenHalMuted_onActivation_returnsTrue()209     public void setMuted_whenHalMuted_onActivation_returnsTrue() {
210         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
211         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
212         List<Integer> muteReasons = List.of(Reasons.TCU_MUTE);
213         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
214         musicGain.zoneId = ZONE_ID;
215         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
216         musicGain.volumeIndex = MIN_GAIN_INDEX;
217         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
218         carVolumeGroup.onAudioGainChanged(muteReasons, musicCarGain);
219 
220         expectWithMessage("Status returned from set mute while HAL muted")
221                 .that(carVolumeGroup.setMute(true)).isTrue();
222     }
223 
224     @Test
setMuted_whenHalMuted_onDeactivation_returnsFalse()225     public void setMuted_whenHalMuted_onDeactivation_returnsFalse() {
226         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
227         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
228         List<Integer> muteReasons = List.of(Reasons.TCU_MUTE);
229         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
230         musicGain.zoneId = ZONE_ID;
231         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
232         musicGain.volumeIndex = MIN_GAIN_INDEX;
233         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
234         carVolumeGroup.onAudioGainChanged(muteReasons, musicCarGain);
235 
236         expectWithMessage("Status returned from set unmute while HAL muted")
237                 .that(carVolumeGroup.setMute(false)).isFalse();
238     }
239 
240     @Test
isMuted_whenDefault_returnsFalse()241     public void isMuted_whenDefault_returnsFalse() {
242         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
243 
244         expectWithMessage("Default mute state")
245                 .that(carVolumeGroup.isMuted()).isFalse();
246     }
247 
248     @Test
isMuted_afterMuting_returnsTrue()249     public void isMuted_afterMuting_returnsTrue() {
250         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
251 
252         carVolumeGroup.setMute(true);
253 
254         expectWithMessage("Get mute state").that(carVolumeGroup.isMuted()).isTrue();
255     }
256 
257     @Test
isMuted_afterUnMuting_returnsFalse()258     public void isMuted_afterUnMuting_returnsFalse() {
259         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
260 
261         carVolumeGroup.setMute(false);
262 
263         expectWithMessage("Set mute state")
264                 .that(carVolumeGroup.isMuted()).isFalse();
265     }
266 
267     @Test
setMute_withMutedState_storesValueToSetting()268     public void setMute_withMutedState_storesValueToSetting() {
269         CarAudioSettings settings = new SettingsBuilder(ZONE_ID, CONFIG_ID, GROUP_ID)
270                 .setMuteForUser10(false).setIsPersistVolumeGroupEnabled(true).build();
271         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithNavigationBound(settings, true);
272         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_10);
273 
274         carVolumeGroup.setMute(true);
275 
276         verify(settings).storeVolumeGroupMuteForUser(TEST_USER_10, ZONE_ID, CONFIG_ID, GROUP_ID,
277                 /* isMuted= */ true);
278     }
279 
280     @Test
setMute_withUnMutedState_storesValueToSetting()281     public void setMute_withUnMutedState_storesValueToSetting() {
282         CarAudioSettings settings = new SettingsBuilder(ZONE_ID, CONFIG_ID, GROUP_ID)
283                 .setMuteForUser10(false).setIsPersistVolumeGroupEnabled(true).build();
284         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithNavigationBound(settings, true);
285         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_10);
286 
287         carVolumeGroup.setMute(false);
288 
289         verify(settings).storeVolumeGroupMuteForUser(TEST_USER_10, ZONE_ID, CONFIG_ID, GROUP_ID,
290                 /* isMuted= */ false);
291     }
292 
293     @Test
getContextsForAddress_returnsContextsBoundToThatAddress()294     public void getContextsForAddress_returnsContextsBoundToThatAddress() {
295         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
296 
297         List<Integer> contextsList = carVolumeGroup.getContextsForAddress(MEDIA_DEVICE_ADDRESS);
298 
299         expectWithMessage("Contexts for bounded address %s", MEDIA_DEVICE_ADDRESS)
300                 .that(contextsList).containsExactly(TEST_MEDIA_CONTEXT_ID,
301                         TEST_CALL_CONTEXT_ID, TEST_CALL_RING_CONTEXT_ID);
302     }
303 
304     @Test
getContextsForAddress_returnsEmptyArrayIfAddressNotBound()305     public void getContextsForAddress_returnsEmptyArrayIfAddressNotBound() {
306         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
307 
308         List<Integer> contextsList = carVolumeGroup.getContextsForAddress(OTHER_ADDRESS);
309 
310         expectWithMessage("Contexts for non-bounded address %s", OTHER_ADDRESS)
311                 .that(contextsList).isEmpty();
312     }
313 
314     @Test
getCarAudioDeviceInfoForAddress_returnsExpectedDevice()315     public void getCarAudioDeviceInfoForAddress_returnsExpectedDevice() {
316         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
317 
318         CarAudioDeviceInfo actualDevice = carVolumeGroup.getCarAudioDeviceInfoForAddress(
319                 MEDIA_DEVICE_ADDRESS);
320 
321         expectWithMessage("Device information for bounded address %s", MEDIA_DEVICE_ADDRESS)
322                 .that(actualDevice).isEqualTo(mMediaDeviceInfo);
323     }
324 
325     @Test
getCarAudioDeviceInfoForAddress_returnsNullIfAddressNotBound()326     public void getCarAudioDeviceInfoForAddress_returnsNullIfAddressNotBound() {
327         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
328 
329         CarAudioDeviceInfo actualDevice = carVolumeGroup.getCarAudioDeviceInfoForAddress(
330                 OTHER_ADDRESS);
331 
332         expectWithMessage("Device information for non-bounded address %s", OTHER_ADDRESS)
333                 .that(actualDevice).isNull();
334     }
335 
336     @Test
getAudioDeviceAttribute_returnsExpectedDevice()337     public void getAudioDeviceAttribute_returnsExpectedDevice() {
338         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
339         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
340         CarVolumeGroupInfo info = carVolumeGroup.getCarVolumeGroupInfo();
341 
342         List<AudioDeviceAttributes> devices = info.getAudioDeviceAttributes();
343 
344         expectWithMessage("Audio device attributes").that(devices).containsExactly(
345                 mMediaDeviceInfo.getAudioDevice(), mNavigationDeviceInfo.getAudioDevice());
346     }
347 
348     @Test
isHalMuted_withHalUnmuted()349     public void isHalMuted_withHalUnmuted() {
350         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
351 
352         expectWithMessage("Unmute status by HAL")
353                 .that(carVolumeGroup.isHalMuted()).isFalse();
354     }
355 
356     @Test
isHalMuted_withHalMuted()357     public void isHalMuted_withHalMuted() {
358         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
359         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
360         List<Integer> muteReasons = List.of(Reasons.TCU_MUTE);
361         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
362         musicGain.zoneId = ZONE_ID;
363         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
364         musicGain.volumeIndex = MIN_GAIN_INDEX;
365         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
366         carVolumeGroup.onAudioGainChanged(muteReasons, musicCarGain);
367 
368         expectWithMessage("Mute status by HAL")
369                 .that(carVolumeGroup.isHalMuted()).isTrue();
370     }
371 
372     @Test
getMutedBySystem_withHalUnmuted()373     public void getMutedBySystem_withHalUnmuted() {
374         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MUTE_AMBIGUITY);
375         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
376         CarVolumeGroupInfo info = carVolumeGroup.getCarVolumeGroupInfo();
377 
378         expectWithMessage("Mute by system status with HAL unmuted")
379                 .that(info.isMutedBySystem()).isFalse();
380     }
381 
382     @Test
getMutedBySystem_withHalMuted()383     public void getMutedBySystem_withHalMuted() {
384         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MUTE_AMBIGUITY);
385         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
386         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
387         List<Integer> muteReasons = List.of(Reasons.TCU_MUTE);
388         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
389         musicGain.zoneId = ZONE_ID;
390         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
391         musicGain.volumeIndex = MIN_GAIN_INDEX;
392         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
393         carVolumeGroup.onAudioGainChanged(muteReasons, musicCarGain);
394 
395         expectWithMessage("Mute by system status with HAL muted")
396                 .that(carVolumeGroup.getCarVolumeGroupInfo().isMutedBySystem()).isTrue();
397     }
398 
399     @Test
getMaxActivationGainIndex()400     public void getMaxActivationGainIndex() {
401         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
402         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
403 
404         int maxActivationGainIndex = carVolumeGroup.getMaxActivationGainIndex();
405 
406         expectWithMessage("Max activation gain index").that(maxActivationGainIndex)
407                 .isEqualTo(MAX_ACTIVATION_GAIN_INDEX);
408     }
409 
410     @Test
getMinActivationGainIndex()411     public void getMinActivationGainIndex() {
412         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
413         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
414 
415         int minActivationGainIndex = carVolumeGroup.getMinActivationGainIndex();
416 
417         expectWithMessage("Min activation gain index").that(minActivationGainIndex)
418                 .isEqualTo(MIN_ACTIVATION_GAIN_INDEX);
419     }
420 
421     @Test
getActivationVolumeInvocationType()422     public void getActivationVolumeInvocationType() {
423         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
424         CarVolumeGroup carVolumeGroup = testInactiveVolumeGroupSetup();
425 
426         expectWithMessage("Activation volume invocation types")
427                 .that(carVolumeGroup.getActivationVolumeInvocationType())
428                 .isEqualTo(ACTIVATION_VOLUME_INVOCATION_TYPE);
429     }
430 
431     @Test
setCurrentGainIndex_setsGainOnAllBoundDevices()432     public void setCurrentGainIndex_setsGainOnAllBoundDevices() {
433         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
434 
435         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
436 
437         verify(mMediaDeviceInfo).setCurrentGain(7);
438         verify(mNavigationDeviceInfo).setCurrentGain(7);
439     }
440 
441     @Test
setCurrentGainIndex_updatesCurrentGainIndex()442     public void setCurrentGainIndex_updatesCurrentGainIndex() {
443         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
444 
445         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
446 
447         expectWithMessage("Updated current gain index")
448                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(TEST_GAIN_INDEX);
449     }
450 
451     @Test
setCurrentGainIndex_checksNewGainIsAboveMin()452     public void setCurrentGainIndex_checksNewGainIsAboveMin() {
453         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
454 
455         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
456                 () -> carVolumeGroup.setCurrentGainIndex(MIN_GAIN_INDEX - 1));
457 
458         expectWithMessage("Set out of bound gain index failure")
459                 .that(thrown).hasMessageThat()
460                 .contains("Gain out of range (" + MIN_GAIN_INDEX + ":" + MAX_GAIN_INDEX + ")");
461     }
462 
463     @Test
setCurrentGainIndex_checksNewGainIsBelowMax()464     public void setCurrentGainIndex_checksNewGainIsBelowMax() {
465         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
466 
467         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
468                 () -> carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX + 1));
469 
470         expectWithMessage("Set out of bound gain index failure")
471                 .that(thrown).hasMessageThat()
472                 .contains("Gain out of range (" + MIN_GAIN_INDEX + ":" + MAX_GAIN_INDEX + ")");
473     }
474 
475     @Test
setCurrentGainIndex_setsCurrentGainIndexForUser()476     public void setCurrentGainIndex_setsCurrentGainIndexForUser() {
477         CarAudioSettings settings = new SettingsBuilder(ZONE_ID, CONFIG_ID, GROUP_ID)
478                 .setGainIndexForUser(TEST_USER_11).build();
479         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithNavigationBound(settings, false);
480         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_11);
481 
482         carVolumeGroup.setCurrentGainIndex(TestCarAudioDeviceInfoBuilder.MIN_GAIN);
483 
484         verify(settings).storeVolumeGainIndexForUser(TEST_USER_11, ZONE_ID, CONFIG_ID, GROUP_ID,
485                 TestCarAudioDeviceInfoBuilder.MIN_GAIN);
486     }
487 
488     @Test
setCurrentGainIndex_setsCurrentGainIndexForDefaultUser()489     public void setCurrentGainIndex_setsCurrentGainIndexForDefaultUser() {
490         CarAudioSettings settings = new SettingsBuilder(ZONE_ID, CONFIG_ID, GROUP_ID)
491                 .setGainIndexForUser(UserHandle.USER_CURRENT).build();
492         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithNavigationBound(settings, false);
493 
494         carVolumeGroup.setCurrentGainIndex(TestCarAudioDeviceInfoBuilder.MIN_GAIN);
495 
496         verify(settings).storeVolumeGainIndexForUser(UserHandle.USER_CURRENT, ZONE_ID, CONFIG_ID,
497                 GROUP_ID, TestCarAudioDeviceInfoBuilder.MIN_GAIN);
498     }
499 
500     @Test
handleActivationVolume_withActivationVolumeDisabled()501     public void handleActivationVolume_withActivationVolumeDisabled() {
502         mSetFlagsRule.disableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
503         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
504         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
505 
506         expectWithMessage("Adjustment to activation volume with activation volume disabled")
507                 .that(carVolumeGroup.handleActivationVolume(
508                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
509         expectWithMessage("Gain index with activation volume disabled")
510                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MAX_GAIN_INDEX);
511     }
512 
513     @Test
handleActivationVolume_withUnsupportedActivationInvocationType()514     public void handleActivationVolume_withUnsupportedActivationInvocationType() {
515         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
516         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
517         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
518 
519         expectWithMessage("Adjustment to activation volume with unsupported activation "
520                 + "invocation type").that(carVolumeGroup.handleActivationVolume(
521                 UNSUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
522         expectWithMessage("Gain index with unsupported activation invocation type")
523                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MAX_GAIN_INDEX);
524     }
525 
526     @Test
handleActivationVolume_withVolumeWithinActivationVolumeRange()527     public void handleActivationVolume_withVolumeWithinActivationVolumeRange() {
528         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
529         int currentGainIndex = MIN_ACTIVATION_GAIN_INDEX + 1;
530         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
531         carVolumeGroup.setCurrentGainIndex(currentGainIndex);
532 
533         expectWithMessage("Adjustment for activation volume")
534                 .that(carVolumeGroup.handleActivationVolume(
535                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
536         expectWithMessage("Gain index without activation volume adjustment")
537                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(currentGainIndex);
538     }
539 
540     @Test
handleActivationVolume_withVolumeBelowMinActivationVolume()541     public void handleActivationVolume_withVolumeBelowMinActivationVolume() {
542         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
543         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
544         carVolumeGroup.setCurrentGainIndex(MIN_ACTIVATION_GAIN_INDEX - 1);
545 
546         expectWithMessage("Success for adjusting to min activation volume")
547                 .that(carVolumeGroup.handleActivationVolume(
548                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isTrue();
549         expectWithMessage("Gain index for adjusting to min activation volume")
550                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MIN_ACTIVATION_GAIN_INDEX);
551     }
552 
553     @Test
handleActivationVolume_withVolumeAboveMaxActivationVolume()554     public void handleActivationVolume_withVolumeAboveMaxActivationVolume() {
555         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
556         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
557         carVolumeGroup.setCurrentGainIndex(MAX_ACTIVATION_GAIN_INDEX + 1);
558 
559         expectWithMessage("Success for adjusting to max activation volume")
560                 .that(carVolumeGroup.handleActivationVolume(
561                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isTrue();
562         expectWithMessage("Gain index for adjusting to max activation volume")
563                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MAX_ACTIVATION_GAIN_INDEX);
564     }
565 
566     @Test
handleActivationVolume_withMute()567     public void handleActivationVolume_withMute() {
568         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
569         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
570         carVolumeGroup.setCurrentGainIndex(MAX_ACTIVATION_GAIN_INDEX + 1);
571         carVolumeGroup.setMute(true);
572 
573         expectWithMessage("Failure for changing to activation volume with mute")
574                 .that(carVolumeGroup.handleActivationVolume(
575                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
576         expectWithMessage("Gain index with activation volume and mute")
577                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MIN_GAIN_INDEX);
578     }
579 
580     @Test
setMute_withFalseWhenMuteAndActivationVolume()581     public void setMute_withFalseWhenMuteAndActivationVolume() {
582         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
583         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
584         carVolumeGroup.setCurrentGainIndex(MAX_ACTIVATION_GAIN_INDEX + 1);
585         carVolumeGroup.setMute(true);
586         carVolumeGroup.handleActivationVolume(SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE);
587 
588         carVolumeGroup.setMute(false);
589 
590         expectWithMessage("Gain index with activation volume after unmute")
591                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MAX_ACTIVATION_GAIN_INDEX);
592     }
593 
594     @Test
handleActivationVolume_withBlock()595     public void handleActivationVolume_withBlock() {
596         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
597         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
598         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
599         int blockedIndex = MAX_ACTIVATION_GAIN_INDEX + 1;
600         carVolumeGroup.setBlocked(blockedIndex);
601 
602         expectWithMessage("Failure for changing to activation volume with block")
603                 .that(carVolumeGroup.handleActivationVolume(
604                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
605         expectWithMessage("Gain index with activation volume and block")
606                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(blockedIndex);
607     }
608 
609     @Test
resetBlocked_withActivationVolume()610     public void resetBlocked_withActivationVolume() {
611         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
612         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
613         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
614         int blockedIndex = MAX_ACTIVATION_GAIN_INDEX + 1;
615         carVolumeGroup.setBlocked(blockedIndex);
616         carVolumeGroup.handleActivationVolume(SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE);
617 
618         carVolumeGroup.resetBlocked();
619 
620         expectWithMessage("Gain index with activation volume after block reset")
621                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MAX_ACTIVATION_GAIN_INDEX);
622     }
623 
624     @Test
handleActivationVolume_withAttenuatedGain()625     public void handleActivationVolume_withAttenuatedGain() {
626         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
627         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
628         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
629         int attenuatedIndex = MAX_ACTIVATION_GAIN_INDEX + 1;
630         carVolumeGroup.setAttenuatedGain(attenuatedIndex);
631 
632         expectWithMessage("Changing to activation volume with attenuated gain")
633                 .that(carVolumeGroup.handleActivationVolume(
634                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
635         expectWithMessage("Gain index with activation volume and attenuated gain applied")
636                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(attenuatedIndex);
637     }
638 
639     @Test
resetAttenuation_withActivationVolume()640     public void resetAttenuation_withActivationVolume() {
641         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
642         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
643         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
644         int attenuatedIndex = MAX_ACTIVATION_GAIN_INDEX + 1;
645         carVolumeGroup.setAttenuatedGain(attenuatedIndex);
646         carVolumeGroup.handleActivationVolume(SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE);
647 
648         carVolumeGroup.resetAttenuation();
649 
650         expectWithMessage("Gain index with activation volume and attenuated gain applied")
651                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MAX_ACTIVATION_GAIN_INDEX);
652     }
653 
654     @Test
handleActivationVolume_withActivationVolumeOverLimitOverCurrentGain()655     public void handleActivationVolume_withActivationVolumeOverLimitOverCurrentGain() {
656         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
657         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
658         carVolumeGroup.setCurrentGainIndex(MIN_GAIN_INDEX);
659         int limitedGainIndex = MIN_ACTIVATION_GAIN_INDEX - 1;
660         carVolumeGroup.setLimit(limitedGainIndex);
661 
662         expectWithMessage("Volume changed due to activation volume over limit and limit "
663                 + "over current gain").that(carVolumeGroup.handleActivationVolume(
664                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isTrue();
665         expectWithMessage("Gain index with activation volume over limit and limit "
666                 + "over current gain").that(carVolumeGroup.getCurrentGainIndex())
667                 .isEqualTo(limitedGainIndex);
668     }
669 
670     @Test
resetLimit_withActivationVolumeOverLimitOverCurrentGainAndResetLimit()671     public void resetLimit_withActivationVolumeOverLimitOverCurrentGainAndResetLimit() {
672         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
673         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
674         carVolumeGroup.setCurrentGainIndex(MIN_ACTIVATION_GAIN_INDEX - 2);
675         int limitedGainIndex = MIN_ACTIVATION_GAIN_INDEX - 1;
676         carVolumeGroup.setLimit(limitedGainIndex);
677         carVolumeGroup.handleActivationVolume(SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE);
678 
679         carVolumeGroup.resetLimit();
680 
681         expectWithMessage("Gain index with activation volume over limit, limit over current "
682                 + " gain and limit reset").that(carVolumeGroup.getCurrentGainIndex())
683                 .isEqualTo(MIN_ACTIVATION_GAIN_INDEX);
684     }
685 
686     @Test
handleActivationVolume_withActivationVolumeOverCurrentGainOverLimit()687     public void handleActivationVolume_withActivationVolumeOverCurrentGainOverLimit() {
688         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
689         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
690         carVolumeGroup.setCurrentGainIndex(MIN_ACTIVATION_GAIN_INDEX - 1);
691         int limitedGainIndex = MIN_ACTIVATION_GAIN_INDEX - 2;
692         carVolumeGroup.setLimit(limitedGainIndex);
693 
694         expectWithMessage("Volume changed due to activation volume over current gain and "
695                 + "current gain over limit").that(carVolumeGroup.handleActivationVolume(
696                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isFalse();
697         expectWithMessage("Gain index with activation volume over current gain and "
698                 + "current gain over limit").that(carVolumeGroup.getCurrentGainIndex())
699                 .isEqualTo(limitedGainIndex);
700     }
701 
702     @Test
resetLimit_withActivationVolumeOverCurrentGainOverLimit()703     public void resetLimit_withActivationVolumeOverCurrentGainOverLimit() {
704         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
705         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
706         carVolumeGroup.setCurrentGainIndex(MIN_ACTIVATION_GAIN_INDEX - 1);
707         int limitedGainIndex = MIN_ACTIVATION_GAIN_INDEX - 2;
708         carVolumeGroup.setLimit(limitedGainIndex);
709         carVolumeGroup.handleActivationVolume(SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE);
710 
711         carVolumeGroup.resetLimit();
712         expectWithMessage("Gain index with activation volume over current gain, current gain "
713                 + "over limit and limit reset").that(carVolumeGroup.getCurrentGainIndex())
714                 .isEqualTo(MIN_ACTIVATION_GAIN_INDEX);
715     }
716 
717     @Test
handleActivationVolume_withActivationVolumeBelowLimit()718     public void handleActivationVolume_withActivationVolumeBelowLimit() {
719         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
720         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
721         carVolumeGroup.setCurrentGainIndex(MIN_GAIN_INDEX);
722         int limitedGainIndex = MIN_ACTIVATION_GAIN_INDEX + 1;
723         carVolumeGroup.setLimit(limitedGainIndex);
724 
725         expectWithMessage("Changing to activation volume below limit")
726                 .that(carVolumeGroup.handleActivationVolume(
727                         SUPPORTED_ACTIVATION_VOLUME_INVOCATION_TYPE)).isTrue();
728         expectWithMessage("Gain index with activation volume below limit")
729                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MIN_ACTIVATION_GAIN_INDEX);
730     }
731 
732     @Test
loadVolumesSettingsForUser_withMutedState_loadsMuteStateForUser()733     public void loadVolumesSettingsForUser_withMutedState_loadsMuteStateForUser() {
734         CarVolumeGroup carVolumeGroup = getVolumeGroupWithMuteAndNavBound(true, true, true);
735 
736         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_10);
737 
738         expectWithMessage("Saved mute state from settings")
739                 .that(carVolumeGroup.isMuted()).isTrue();
740     }
741 
742     @Test
loadVolumesSettingsForUser_withDisabledUseVolumeGroupMute_doesNotLoadMute()743     public void loadVolumesSettingsForUser_withDisabledUseVolumeGroupMute_doesNotLoadMute() {
744         CarVolumeGroup carVolumeGroup = getVolumeGroupWithMuteAndNavBound(true, true, false);
745 
746         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_10);
747 
748         expectWithMessage("Default mute state")
749                 .that(carVolumeGroup.isMuted()).isFalse();
750     }
751 
752     @Test
loadVolumesSettingsForUser_withUnMutedState_loadsMuteStateForUser()753     public void loadVolumesSettingsForUser_withUnMutedState_loadsMuteStateForUser() {
754         CarVolumeGroup carVolumeGroup = getVolumeGroupWithMuteAndNavBound(false, true, true);
755 
756         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_10);
757 
758         expectWithMessage("Saved mute state from settings").that(carVolumeGroup.isMuted())
759                 .isFalse();
760     }
761 
762     @Test
loadVolumesSettingsForUser_withMutedStateAndNoPersist_returnsDefaultMuteState()763     public void loadVolumesSettingsForUser_withMutedStateAndNoPersist_returnsDefaultMuteState() {
764         CarVolumeGroup carVolumeGroup = getVolumeGroupWithMuteAndNavBound(true, false, true);
765 
766         carVolumeGroup.loadVolumesSettingsForUser(TEST_USER_10);
767 
768         expectWithMessage("Default mute state").that(carVolumeGroup.isMuted()).isFalse();
769     }
770 
771     @Test
hasCriticalAudioContexts_withoutCriticalContexts_returnsFalse()772     public void hasCriticalAudioContexts_withoutCriticalContexts_returnsFalse() {
773         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
774 
775         expectWithMessage("Group without critical audio context")
776                 .that(carVolumeGroup.hasCriticalAudioContexts()).isFalse();
777     }
778 
779     @Test
hasCriticalAudioContexts_withCriticalContexts_returnsTrue()780     public void hasCriticalAudioContexts_withCriticalContexts_returnsTrue() {
781         CarVolumeGroupFactory factory = getFactory(/* useCarVolumeGroupMute= */ true);
782         factory.setDeviceInfoForContext(TEST_EMERGENCY_CONTEXT_ID, mMediaDeviceInfo);
783         CarVolumeGroup carVolumeGroup = factory.getCarVolumeGroup(/* useCoreAudioVolume= */ false);
784 
785         expectWithMessage("Group with critical audio context")
786                 .that(carVolumeGroup.hasCriticalAudioContexts()).isTrue();
787     }
788 
789     @Test
getCurrentGainIndex_whileMuted_returnsMinGain()790     public void getCurrentGainIndex_whileMuted_returnsMinGain() {
791         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
792         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
793 
794         carVolumeGroup.setMute(true);
795 
796         expectWithMessage("Muted current gain index")
797                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(MIN_GAIN_INDEX);
798     }
799 
800     @Test
getCurrentGainIndex_whileUnMuted_returnsLastSetGain()801     public void getCurrentGainIndex_whileUnMuted_returnsLastSetGain() {
802         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
803         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
804 
805         carVolumeGroup.setMute(false);
806 
807         expectWithMessage("Un-muted current gain index")
808                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(TEST_GAIN_INDEX);
809     }
810 
811     @Test
setCurrentGainIndex_whileMuted_unMutesVolumeGroup()812     public void setCurrentGainIndex_whileMuted_unMutesVolumeGroup() {
813         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
814         carVolumeGroup.setMute(true);
815         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
816 
817         expectWithMessage("Mute state after volume change")
818                 .that(carVolumeGroup.isMuted()).isEqualTo(false);
819     }
820 
821     @Test
setBlocked_withGain_thenBackToUninitializedGain()822     public void setBlocked_withGain_thenBackToUninitializedGain() {
823         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
824 
825         expectWithMessage("Default blocked state").that(carVolumeGroup.isBlocked()).isFalse();
826 
827         carVolumeGroup.setBlocked(10);
828 
829         expectWithMessage("Blocked state after blocked").that(carVolumeGroup.isBlocked())
830                 .isTrue();
831 
832         carVolumeGroup.resetBlocked();
833 
834         expectWithMessage("Blocked state after reset").that(carVolumeGroup.isBlocked())
835                 .isFalse();
836     }
837 
838     @Test
setLimited_withGain_thenBackToMaxGain()839     public void setLimited_withGain_thenBackToMaxGain() {
840         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
841 
842         expectWithMessage("Default limited state").that(carVolumeGroup.isLimited()).isFalse();
843 
844         carVolumeGroup.setLimit(carVolumeGroup.getMaxGainIndex() - 1);
845 
846         expectWithMessage("Limit state after set limit").that(carVolumeGroup.isLimited())
847                 .isTrue();
848 
849         carVolumeGroup.resetLimit();
850 
851         expectWithMessage("Limit state after reset").that(carVolumeGroup.isLimited())
852                 .isFalse();
853     }
854 
855     @Test
setAttenuatedGain_withGain_thenBackToUninitializedGain()856     public void setAttenuatedGain_withGain_thenBackToUninitializedGain() {
857         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
858 
859         expectWithMessage("Default attenuated state").that(carVolumeGroup.isAttenuated()).isFalse();
860 
861         carVolumeGroup.setAttenuatedGain(10);
862 
863         expectWithMessage("Attenuated state after set attenuated").that(carVolumeGroup
864                 .isAttenuated()).isTrue();
865 
866         carVolumeGroup.resetAttenuation();
867 
868         expectWithMessage("Attenuated state after reset").that(carVolumeGroup.isAttenuated())
869                 .isFalse();
870     }
871 
872     @Test
getCurrentGainIndex_whileBlocked_thenUnblocked()873     public void getCurrentGainIndex_whileBlocked_thenUnblocked() {
874         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
875         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
876 
877         expectWithMessage("Initial current gain index")
878                 .that(carVolumeGroup.getCurrentGainIndex())
879                 .isEqualTo(TEST_GAIN_INDEX);
880 
881         int blockedIndex = 10;
882         carVolumeGroup.setBlocked(blockedIndex);
883 
884         expectWithMessage("Blocked state after set blocked").that(carVolumeGroup.isBlocked())
885                 .isTrue();
886 
887         expectWithMessage("Blocked current gain index")
888                 .that(carVolumeGroup.getCurrentGainIndex())
889                 .isEqualTo(blockedIndex);
890 
891         carVolumeGroup.resetBlocked();
892 
893         expectWithMessage("Blocked state after reset").that(carVolumeGroup.isBlocked()).isFalse();
894 
895         expectWithMessage("Back to current gain index")
896                 .that(carVolumeGroup.getCurrentGainIndex())
897                 .isEqualTo(TEST_GAIN_INDEX);
898     }
899 
900     @Test
getCurrentGainIndex_whileLimited_thenUnlimited()901     public void getCurrentGainIndex_whileLimited_thenUnlimited() {
902         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
903         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
904         expectWithMessage("Initial current gain index")
905                 .that(carVolumeGroup.getCurrentGainIndex())
906                 .isEqualTo(TEST_GAIN_INDEX);
907         expectWithMessage("Default limit state").that(carVolumeGroup.isLimited()).isFalse();
908 
909         int limitedGainIndex = carVolumeGroup.getMinGainIndex() + 1;
910         carVolumeGroup.setLimit(limitedGainIndex);
911 
912         expectWithMessage("Limit state after set limit").that(carVolumeGroup.isLimited())
913                 .isTrue();
914         expectWithMessage("Limited current gain index")
915                 .that(carVolumeGroup.getCurrentGainIndex())
916                 .isEqualTo(limitedGainIndex);
917 
918         carVolumeGroup.resetLimit();
919 
920         expectWithMessage("Limit state after reset").that(carVolumeGroup.isLimited()).isFalse();
921         expectWithMessage("Back to current gain index")
922                 .that(carVolumeGroup.getCurrentGainIndex())
923                 .isEqualTo(TEST_GAIN_INDEX);
924     }
925 
926     @Test
getCurrentGainIndex_whileAttenuated_thenUnattenuated()927     public void getCurrentGainIndex_whileAttenuated_thenUnattenuated() {
928         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
929         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
930         expectWithMessage("Initial current gain index")
931                 .that(carVolumeGroup.getCurrentGainIndex())
932                 .isEqualTo(TEST_GAIN_INDEX);
933         expectWithMessage("Default attenuated state").that(carVolumeGroup.isAttenuated())
934                 .isFalse();
935 
936         int attenuatedIndex = TEST_GAIN_INDEX - 1;
937         carVolumeGroup.setAttenuatedGain(attenuatedIndex);
938 
939         expectWithMessage("Attenuated state after set attenuated").that(carVolumeGroup
940                 .isAttenuated()).isTrue();
941         expectWithMessage("Attenuated current gain index")
942                 .that(carVolumeGroup.getCurrentGainIndex())
943                 .isEqualTo(attenuatedIndex);
944 
945         carVolumeGroup.resetAttenuation();
946 
947         expectWithMessage("Attenuated state after reset").that(carVolumeGroup.isAttenuated())
948                 .isFalse();
949         expectWithMessage("Muted current gain index")
950                 .that(carVolumeGroup.getCurrentGainIndex())
951                 .isEqualTo(TEST_GAIN_INDEX);
952     }
953 
954     @Test
setCurrentGainIndex_whileBlocked_thenRemainsUnblocked()955     public void setCurrentGainIndex_whileBlocked_thenRemainsUnblocked() {
956         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
957         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
958 
959         expectWithMessage("Initial current gain index")
960                 .that(carVolumeGroup.getCurrentGainIndex())
961                 .isEqualTo(TEST_GAIN_INDEX);
962 
963         int blockedIndex = 1;
964         carVolumeGroup.setBlocked(blockedIndex);
965 
966         expectWithMessage("Blocked state after set blocked").that(carVolumeGroup.isBlocked())
967                 .isTrue();
968 
969         carVolumeGroup.setCurrentGainIndex(blockedIndex + 1);
970 
971         expectWithMessage("Over Blocked current gain index")
972                 .that(carVolumeGroup.getCurrentGainIndex())
973                 .isEqualTo(blockedIndex);
974 
975         carVolumeGroup.setCurrentGainIndex(blockedIndex - 1);
976 
977         expectWithMessage("Under Blocked current gain index")
978                 .that(carVolumeGroup.getCurrentGainIndex())
979                 .isEqualTo(blockedIndex);
980     }
981 
982     @Test
setCurrentGainIndex_whileLimited_under_then_over_limit()983     public void setCurrentGainIndex_whileLimited_under_then_over_limit() {
984         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
985         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
986         expectWithMessage("Initial current gain index")
987                 .that(carVolumeGroup.getCurrentGainIndex())
988                 .isEqualTo(MAX_GAIN_INDEX);
989         expectWithMessage("Default limit state").that(carVolumeGroup.isLimited()).isFalse();
990 
991         int limitedGainIndex = MAX_GAIN_INDEX - 1;
992         carVolumeGroup.setLimit(limitedGainIndex);
993 
994         expectWithMessage("Limit state after set limit").that(carVolumeGroup.isLimited())
995                 .isTrue();
996         expectWithMessage("Over limit state due to over limit gain").that(carVolumeGroup
997                 .isOverLimit()).isTrue();
998 
999         // Underlimit
1000         carVolumeGroup.setCurrentGainIndex(limitedGainIndex - 1);
1001 
1002         expectWithMessage("Under limit current gain index")
1003                 .that(carVolumeGroup.getCurrentGainIndex())
1004                 .isEqualTo(limitedGainIndex - 1);
1005 
1006         expectWithMessage("Limit state after set limit and setting gain under limit")
1007                 .that(carVolumeGroup.isLimited()).isTrue();
1008         expectWithMessage("Over limit state after set limit and setting gain under limit")
1009                 .that(carVolumeGroup.isOverLimit()).isFalse();
1010 
1011         // Overlimit
1012         carVolumeGroup.setCurrentGainIndex(limitedGainIndex + 1);
1013 
1014         expectWithMessage("Over limit current gain index")
1015                 .that(carVolumeGroup.getCurrentGainIndex())
1016                 .isEqualTo(limitedGainIndex);
1017 
1018         expectWithMessage("Limit state after set limit and fail to set gain over limit")
1019                 .that(carVolumeGroup.isLimited()).isTrue();
1020         // Limitation prevents to set over limited index
1021         expectWithMessage("Over limit state after set limit and fail to set gain over limit")
1022                 .that(carVolumeGroup.isOverLimit()).isFalse();
1023     }
1024 
1025     @Test
setCurrentGainIndex_whileAttenuated_thenUnattenuated()1026     public void setCurrentGainIndex_whileAttenuated_thenUnattenuated() {
1027         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
1028         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
1029         expectWithMessage("Initial current gain index")
1030                 .that(carVolumeGroup.getCurrentGainIndex())
1031                 .isEqualTo(TEST_GAIN_INDEX);
1032         expectWithMessage("Default attenuated state").that(carVolumeGroup.isAttenuated())
1033                 .isFalse();
1034 
1035         int attenuatedIndex = TEST_GAIN_INDEX - 2;
1036         carVolumeGroup.setAttenuatedGain(attenuatedIndex);
1037 
1038         expectWithMessage("Attenuated state after set attenuated").that(carVolumeGroup
1039                 .isAttenuated()).isTrue();
1040         expectWithMessage("Attenuated current gain index")
1041                 .that(carVolumeGroup.getCurrentGainIndex())
1042                 .isEqualTo(attenuatedIndex);
1043 
1044         carVolumeGroup.setCurrentGainIndex(attenuatedIndex + 1);
1045 
1046         expectWithMessage("Attenuated state after reset gain index").that(carVolumeGroup
1047                 .isAttenuated()).isFalse();
1048         expectWithMessage("new current gain index")
1049                 .that(carVolumeGroup.getCurrentGainIndex())
1050                 .isEqualTo(attenuatedIndex + 1);
1051     }
1052 
1053     @Test
isOverLimit_expectedTrue()1054     public void isOverLimit_expectedTrue() {
1055         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1056         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
1057 
1058         List<Integer> limitReasons = List.of(Reasons.THERMAL_LIMITATION);
1059 
1060         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1061         musicGain.zoneId = ZONE_ID;
1062         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1063         musicGain.volumeIndex = TEST_GAIN_INDEX;
1064         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1065 
1066         carVolumeGroup.onAudioGainChanged(limitReasons, musicCarGain);
1067         expectWithMessage("Limit state with thermal limitation")
1068                 .that(carVolumeGroup.isLimited()).isTrue();
1069         expectWithMessage("Over limit state with thermal limitation")
1070                 .that(carVolumeGroup.isOverLimit()).isTrue();
1071     }
1072 
1073     @Test
isOverLimit_expectedFalse()1074     public void isOverLimit_expectedFalse() {
1075         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1076         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX - 1);
1077 
1078         List<Integer> limitReasons = List.of(Reasons.THERMAL_LIMITATION);
1079 
1080         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1081         musicGain.zoneId = ZONE_ID;
1082         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1083         musicGain.volumeIndex = TEST_GAIN_INDEX;
1084         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1085 
1086         carVolumeGroup.onAudioGainChanged(limitReasons, musicCarGain);
1087 
1088         expectWithMessage("Limit state with thermal limitation while under limit")
1089                 .that(carVolumeGroup.isLimited()).isTrue();
1090         expectWithMessage("Over limit state with thermal limitation while under limit")
1091                 .that(carVolumeGroup.isOverLimit()).isFalse();
1092     }
1093 
1094     @Test
onAudioGainChanged_withOverLimit_thenEndsAndRestoresVolume()1095     public void onAudioGainChanged_withOverLimit_thenEndsAndRestoresVolume() {
1096         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1097         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
1098         List<Integer> limitReasons = List.of(Reasons.THERMAL_LIMITATION);
1099         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1100         musicGain.zoneId = ZONE_ID;
1101         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1102         musicGain.volumeIndex = DEFAULT_GAIN_INDEX;
1103         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1104 
1105         expectWithMessage("Audio gain changed with over limit")
1106                 .that(carVolumeGroup.onAudioGainChanged(limitReasons, musicCarGain))
1107                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1108         expectWithMessage("Over limit gain index")
1109                 .that(carVolumeGroup.getCurrentGainIndex())
1110                 .isEqualTo(DEFAULT_GAIN_INDEX);
1111         expectWithMessage("Attenuated state after set limited")
1112                 .that(carVolumeGroup.isAttenuated()).isFalse();
1113         expectWithMessage("Limit state after set limited")
1114                 .that(carVolumeGroup.isLimited()).isTrue();
1115         expectWithMessage("Over limit state after set limited")
1116                 .that(carVolumeGroup.isOverLimit()).isTrue();
1117         expectWithMessage("BLocked state after set limited")
1118                 .that(carVolumeGroup.isBlocked()).isFalse();
1119         expectWithMessage("Mute state after set limited")
1120                 .that(carVolumeGroup.isMuted()).isFalse();
1121 
1122         List<Integer> noReasons = new ArrayList<>(0);
1123         expectWithMessage("Audio gain changed with over limit")
1124                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1125                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1126         expectWithMessage("Attenuated state after reset limited")
1127                 .that(carVolumeGroup.isAttenuated()).isFalse();
1128         expectWithMessage("Limit state after reset limited")
1129                 .that(carVolumeGroup.isLimited()).isFalse();
1130         expectWithMessage("Over limit state after reset limited")
1131                 .that(carVolumeGroup.isOverLimit()).isFalse();
1132         expectWithMessage("BLocked state after reset limited")
1133                 .that(carVolumeGroup.isBlocked()).isFalse();
1134         expectWithMessage("Mute state after reset limited")
1135                 .that(carVolumeGroup.isMuted()).isFalse();
1136         expectWithMessage("Restored initial gain index")
1137                 .that(carVolumeGroup.getCurrentGainIndex())
1138                 .isEqualTo(MAX_GAIN_INDEX);
1139     }
1140 
1141     @Test
onAudioGainChanged_withUnderLimit_thenEndsWithVolumeUnchanged()1142     public void onAudioGainChanged_withUnderLimit_thenEndsWithVolumeUnchanged() {
1143         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1144         carVolumeGroup.setCurrentGainIndex(MIN_GAIN_INDEX);
1145         List<Integer> limitReasons = List.of(Reasons.THERMAL_LIMITATION);
1146         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1147         musicGain.zoneId = ZONE_ID;
1148         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1149         musicGain.volumeIndex = DEFAULT_GAIN_INDEX;
1150         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1151 
1152         expectWithMessage("Audio gain changed with under limit")
1153                 .that(carVolumeGroup.onAudioGainChanged(limitReasons, musicCarGain))
1154                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED);
1155         expectWithMessage("Under limit gain index")
1156                 .that(carVolumeGroup.getCurrentGainIndex())
1157                 .isEqualTo(MIN_GAIN_INDEX);
1158         expectWithMessage("Attenuated state after set limited")
1159                 .that(carVolumeGroup.isAttenuated()).isFalse();
1160         expectWithMessage("Limit state after set limited")
1161                 .that(carVolumeGroup.isLimited()).isTrue();
1162         expectWithMessage("Over limit state after set limited")
1163                 .that(carVolumeGroup.isOverLimit()).isFalse();
1164         expectWithMessage("Blocked state after set limited")
1165                 .that(carVolumeGroup.isBlocked()).isFalse();
1166         expectWithMessage("Mute state after set limited")
1167                 .that(carVolumeGroup.isMuted()).isFalse();
1168 
1169         List<Integer> noReasons = new ArrayList<>(0);
1170         expectWithMessage("Audio gain changed with under limit")
1171                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1172                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED);
1173         expectWithMessage("Attenuated state after reset limited")
1174                 .that(carVolumeGroup.isAttenuated()).isFalse();
1175         expectWithMessage("Limit state after reset limited")
1176                 .that(carVolumeGroup.isLimited()).isFalse();
1177         expectWithMessage("Over limit state after reset limited")
1178                 .that(carVolumeGroup.isOverLimit()).isFalse();
1179         expectWithMessage("Blocked state after reset limited")
1180                 .that(carVolumeGroup.isBlocked()).isFalse();
1181         expectWithMessage("Mute state after reset limited")
1182                 .that(carVolumeGroup.isMuted()).isFalse();
1183         expectWithMessage("Unchanged gain index")
1184                 .that(carVolumeGroup.getCurrentGainIndex())
1185                 .isEqualTo(MIN_GAIN_INDEX);
1186     }
1187 
1188     @Test
onAudioGainChanged_withBlockedGain_thenEndsAndRestoresVolume()1189     public void onAudioGainChanged_withBlockedGain_thenEndsAndRestoresVolume() {
1190         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1191         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
1192         List<Integer> blockReasons = List.of(Reasons.FORCED_MASTER_MUTE);
1193         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1194         musicGain.zoneId = ZONE_ID;
1195         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1196         musicGain.volumeIndex = MIN_GAIN_INDEX;
1197         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1198 
1199         expectWithMessage("Audio gain changed with blocked")
1200                 .that(carVolumeGroup.onAudioGainChanged(blockReasons, musicCarGain))
1201                 .isEqualTo(EVENT_TYPE_VOLUME_BLOCKED_CHANGED
1202                         | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1203         expectWithMessage("Attenuated state after set blocked")
1204                 .that(carVolumeGroup.isAttenuated()).isFalse();
1205         expectWithMessage("Limit state after set blocked")
1206                 .that(carVolumeGroup.isLimited()).isFalse();
1207         expectWithMessage("Over limit state after set blocked")
1208                 .that(carVolumeGroup.isOverLimit()).isFalse();
1209         expectWithMessage("Blocked state after set blocked")
1210                 .that(carVolumeGroup.isBlocked()).isTrue();
1211         expectWithMessage("Mute state after set blocked")
1212                 .that(carVolumeGroup.isMuted()).isFalse();
1213         expectWithMessage("Blocked gain index")
1214                 .that(carVolumeGroup.getCurrentGainIndex())
1215                 .isEqualTo(MIN_GAIN_INDEX);
1216 
1217         List<Integer> noReasons = new ArrayList<>(0);
1218         expectWithMessage("Audio gain changed with blocked")
1219                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1220                 .isEqualTo(EVENT_TYPE_VOLUME_BLOCKED_CHANGED
1221                         | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1222         expectWithMessage("Attenuated state after reset blocked")
1223                 .that(carVolumeGroup.isAttenuated()).isFalse();
1224         expectWithMessage("Limit state after reset blocked")
1225                 .that(carVolumeGroup.isLimited()).isFalse();
1226         expectWithMessage("Over limit state after reset blocked")
1227                 .that(carVolumeGroup.isOverLimit()).isFalse();
1228         expectWithMessage("BLocked state after reset blocked")
1229                 .that(carVolumeGroup.isBlocked()).isFalse();
1230         expectWithMessage("Mute state after reset blocked")
1231                 .that(carVolumeGroup.isMuted()).isFalse();
1232         expectWithMessage("Restored initial gain index")
1233                 .that(carVolumeGroup.getCurrentGainIndex())
1234                 .isEqualTo(DEFAULT_GAIN_INDEX);
1235     }
1236 
1237     @Test
onAudioGainChanged_withAttenuatedGain_thenEndsAndRestoresVolume()1238     public void onAudioGainChanged_withAttenuatedGain_thenEndsAndRestoresVolume() {
1239         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1240         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
1241         int attenuatedIndex = DEFAULT_GAIN_INDEX - 1;
1242         List<Integer> attenuateReasons = List.of(Reasons.ADAS_DUCKING);
1243         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1244         musicGain.zoneId = ZONE_ID;
1245         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1246         musicGain.volumeIndex = attenuatedIndex;
1247         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1248 
1249         expectWithMessage("Audio gain changed with attenuated gain")
1250                 .that(carVolumeGroup.onAudioGainChanged(attenuateReasons, musicCarGain))
1251                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1252         expectWithMessage("Attenuated state after set attenuated")
1253                 .that(carVolumeGroup.isAttenuated()).isTrue();
1254         expectWithMessage("Limit state after set attenuated")
1255                 .that(carVolumeGroup.isLimited()).isFalse();
1256         expectWithMessage("Over limit state after set attenuated")
1257                 .that(carVolumeGroup.isOverLimit()).isFalse();
1258         expectWithMessage("BLocked state after set attenuated")
1259                 .that(carVolumeGroup.isBlocked()).isFalse();
1260         expectWithMessage("Mute state after set attenuated")
1261                 .that(carVolumeGroup.isMuted()).isFalse();
1262         expectWithMessage("Attenuated gain index")
1263                 .that(carVolumeGroup.getCurrentGainIndex())
1264                 .isEqualTo(attenuatedIndex);
1265 
1266         List<Integer> noReasons = new ArrayList<>(0);
1267         expectWithMessage("Audio gain changed with attenuated gain")
1268                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1269                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1270         expectWithMessage("Attenuated state after reset attenuated")
1271                 .that(carVolumeGroup.isAttenuated()).isFalse();
1272         expectWithMessage("Limit state after reset attenuated")
1273                 .that(carVolumeGroup.isLimited()).isFalse();
1274         expectWithMessage("Over limit state after reset attenuated")
1275                 .that(carVolumeGroup.isOverLimit()).isFalse();
1276         expectWithMessage("BLocked state after reset attenuated")
1277                 .that(carVolumeGroup.isBlocked()).isFalse();
1278         expectWithMessage("Mute state after reset attenuated")
1279                 .that(carVolumeGroup.isMuted()).isFalse();
1280         expectWithMessage("Restored initial gain index")
1281                 .that(carVolumeGroup.getCurrentGainIndex())
1282                 .isEqualTo(DEFAULT_GAIN_INDEX);
1283     }
1284 
1285     @Test
onAudioGainChanged_withMutedGain_thenEndsAndRestoresVolume()1286     public void onAudioGainChanged_withMutedGain_thenEndsAndRestoresVolume() {
1287         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1288         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
1289         List<Integer> muteReasons = List.of(Reasons.TCU_MUTE);
1290         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1291         musicGain.zoneId = ZONE_ID;
1292         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1293         musicGain.volumeIndex = MIN_GAIN_INDEX;
1294         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1295 
1296         expectWithMessage("Audio gain changed with muted")
1297                 .that(carVolumeGroup.onAudioGainChanged(muteReasons, musicCarGain))
1298                 .isEqualTo(EVENT_TYPE_VOLUME_BLOCKED_CHANGED | EVENT_TYPE_MUTE_CHANGED
1299                         | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1300         expectWithMessage("Attenuated state after set muted")
1301                 .that(carVolumeGroup.isAttenuated()).isFalse();
1302         expectWithMessage("Limit state after set muted")
1303                 .that(carVolumeGroup.isLimited()).isFalse();
1304         expectWithMessage("Over limit state after set muted")
1305                 .that(carVolumeGroup.isOverLimit()).isFalse();
1306         expectWithMessage("Blocked state after set muted")
1307                 .that(carVolumeGroup.isBlocked()).isTrue();
1308         expectWithMessage("Mute state after set muted")
1309                 .that(carVolumeGroup.isMuted()).isTrue();
1310         expectWithMessage("Blocked gain index")
1311                 .that(carVolumeGroup.getCurrentGainIndex())
1312                 .isEqualTo(MIN_GAIN_INDEX);
1313 
1314         List<Integer> noReasons = new ArrayList<>(0);
1315         expectWithMessage("Audio gain changed with blocked")
1316                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1317                 .isEqualTo(EVENT_TYPE_VOLUME_BLOCKED_CHANGED | EVENT_TYPE_MUTE_CHANGED
1318                         | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1319         expectWithMessage("Attenuated state after reset muted")
1320                 .that(carVolumeGroup.isAttenuated()).isFalse();
1321         expectWithMessage("Limit state after reset muted")
1322                 .that(carVolumeGroup.isLimited()).isFalse();
1323         expectWithMessage("Over limit state after reset muted")
1324                 .that(carVolumeGroup.isOverLimit()).isFalse();
1325         expectWithMessage("BLocked state after reset muted")
1326                 .that(carVolumeGroup.isBlocked()).isFalse();
1327         expectWithMessage("Mute state after reset muted")
1328                 .that(carVolumeGroup.isMuted()).isFalse();
1329         expectWithMessage("Restored initial gain index")
1330                 .that(carVolumeGroup.getCurrentGainIndex())
1331                 .isEqualTo(DEFAULT_GAIN_INDEX);
1332     }
1333 
1334     @Test
onAudioGainChanged_withMutedGain_whenGroupMutingDisabled_doesNotSetMute()1335     public void onAudioGainChanged_withMutedGain_whenGroupMutingDisabled_doesNotSetMute() {
1336         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup(/* useCarVolumeGroupMute= */ false);
1337         carVolumeGroup.setCurrentGainIndex(DEFAULT_GAIN_INDEX);
1338         List<Integer> muteReasons = List.of(Reasons.TCU_MUTE);
1339         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1340         musicGain.zoneId = ZONE_ID;
1341         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1342         musicGain.volumeIndex = MIN_GAIN_INDEX;
1343         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1344 
1345         expectWithMessage("Audio gain changed with muted")
1346                 .that(carVolumeGroup.onAudioGainChanged(muteReasons, musicCarGain))
1347                 .isEqualTo(EVENT_TYPE_VOLUME_BLOCKED_CHANGED
1348                         | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1349         expectWithMessage("Mute state").that(carVolumeGroup.isMuted()).isFalse();
1350     }
1351 
1352     @Test
onAudioGainChanged_withVolumeFeedback()1353     public void onAudioGainChanged_withVolumeFeedback() {
1354         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1355         carVolumeGroup.setCurrentGainIndex(TEST_GAIN_INDEX);
1356         List<Integer> volFeedbackReasons = List.of(Reasons.EXTERNAL_AMP_VOL_FEEDBACK);
1357         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1358         musicGain.zoneId = ZONE_ID;
1359         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1360         musicGain.volumeIndex = TEST_GAIN_INDEX - 1;
1361         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1362 
1363         expectWithMessage("Audio gain changed with external amp vol feedback")
1364                 .that(carVolumeGroup.onAudioGainChanged(volFeedbackReasons, musicCarGain))
1365                 .isEqualTo(EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1366         expectWithMessage("Updated gain index")
1367                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(musicGain.volumeIndex);
1368         expectWithMessage("Attenuated state after external amp vol feedback")
1369                 .that(carVolumeGroup.isAttenuated()).isFalse();
1370         expectWithMessage("Limit state after external amp vol feedback")
1371                 .that(carVolumeGroup.isLimited()).isFalse();
1372         expectWithMessage("Over limit state after external amp vol feedback")
1373                 .that(carVolumeGroup.isOverLimit()).isFalse();
1374         expectWithMessage("Blocked state after external amp vol feedback")
1375                 .that(carVolumeGroup.isBlocked()).isFalse();
1376         expectWithMessage("Mute state after external amp vol feedback")
1377                 .that(carVolumeGroup.isMuted()).isFalse();
1378     }
1379 
1380     @Test
onAudioGainChanged_withBlockingLimitMuteAndAttenuation()1381     public void onAudioGainChanged_withBlockingLimitMuteAndAttenuation() {
1382         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1383         List<Integer> allReasons =
1384                 List.of(
1385                         -1,
1386                         -10,
1387                         666,
1388                         Reasons.FORCED_MASTER_MUTE,
1389                         Reasons.TCU_MUTE,
1390                         Reasons.REMOTE_MUTE,
1391                         Reasons.THERMAL_LIMITATION,
1392                         Reasons.SUSPEND_EXIT_VOL_LIMITATION,
1393                         Reasons.ADAS_DUCKING,
1394                         Reasons.ADAS_DUCKING);
1395 
1396         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1397         musicGain.zoneId = ZONE_ID;
1398         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1399         musicGain.volumeIndex = DEFAULT_GAIN_INDEX;
1400         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1401 
1402         expectWithMessage("Audio gain changed with blocked, limited, muted and attenuated")
1403                 .that(carVolumeGroup.onAudioGainChanged(allReasons, musicCarGain))
1404                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED | EVENT_TYPE_VOLUME_BLOCKED_CHANGED
1405                         | EVENT_TYPE_MUTE_CHANGED | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1406         expectWithMessage("Attenuated state while blocked, limited, muted and attenuated")
1407                 .that(carVolumeGroup.isAttenuated()).isTrue();
1408         expectWithMessage("Limit state while blocked, limited, muted and attenuated")
1409                 .that(carVolumeGroup.isLimited()).isTrue();
1410         expectWithMessage("Blocked state while blocked, limited, muted and attenuated")
1411                 .that(carVolumeGroup.isBlocked()).isTrue();
1412         expectWithMessage("Mute state while blocked, limited, muted and attenuated")
1413                 .that(carVolumeGroup.isMuted()).isTrue();
1414     }
1415 
1416     @Test
onAudioGainChanged_resettingBlockingLimitMuteAndAttenuation()1417     public void onAudioGainChanged_resettingBlockingLimitMuteAndAttenuation() {
1418         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1419         List<Integer> noReasons = new ArrayList<>(0);
1420         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1421         musicGain.zoneId = ZONE_ID;
1422         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1423         musicGain.volumeIndex = DEFAULT_GAIN_INDEX;
1424         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1425 
1426         expectWithMessage("Audio gain changed with no reasons")
1427                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1428                 .isEqualTo(EVENT_TYPE_NONE);
1429         expectWithMessage("Attenuated state after reset of blocked, limited, muted and attenuated")
1430                 .that(carVolumeGroup.isAttenuated()).isFalse();
1431         expectWithMessage("Limit state after reset of blocked, limited, muted and attenuated")
1432                 .that(carVolumeGroup.isLimited()).isFalse();
1433         expectWithMessage("Blocked state after reset of blocked, limited, muted and attenuated")
1434                 .that(carVolumeGroup.isBlocked()).isFalse();
1435         expectWithMessage("Muted state after reset of blocked, limited, muted and attenuated")
1436                 .that(carVolumeGroup.isMuted()).isFalse();
1437     }
1438 
1439     @Test
onAudioGainChanged_setResettingBlockingLimitMuteAndAttenuation()1440     public void onAudioGainChanged_setResettingBlockingLimitMuteAndAttenuation() {
1441         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1442         List<Integer> allReasons =
1443                 List.of(
1444                         Reasons.FORCED_MASTER_MUTE,
1445                         Reasons.TCU_MUTE,
1446                         Reasons.REMOTE_MUTE,
1447                         Reasons.THERMAL_LIMITATION,
1448                         Reasons.SUSPEND_EXIT_VOL_LIMITATION,
1449                         Reasons.ADAS_DUCKING,
1450                         Reasons.ADAS_DUCKING);
1451         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1452         musicGain.zoneId = ZONE_ID;
1453         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1454         musicGain.volumeIndex = DEFAULT_GAIN_INDEX;
1455         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1456         carVolumeGroup.onAudioGainChanged(allReasons, musicCarGain);
1457         List<Integer> noReasons = new ArrayList<>(0);
1458 
1459         expectWithMessage("Audio gain changed with reset of blocked, limited, muted and attenuated")
1460                 .that(carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain))
1461                 .isEqualTo(EVENT_TYPE_ATTENUATION_CHANGED | EVENT_TYPE_VOLUME_BLOCKED_CHANGED
1462                         | EVENT_TYPE_MUTE_CHANGED | EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
1463         expectWithMessage("Attenuated state after reset of blocked, limited, muted and attenuated")
1464                 .that(carVolumeGroup.isAttenuated()).isFalse();
1465         expectWithMessage("Limit state after reset of blocked, limited, muted and attenuated")
1466                 .that(carVolumeGroup.isLimited()).isFalse();
1467         expectWithMessage("Blocked state after reset of blocked, limited, muted and attenuated")
1468                 .that(carVolumeGroup.isBlocked()).isFalse();
1469         expectWithMessage("Muted state after reset of blocked, limited, muted and attenuated")
1470                 .that(carVolumeGroup.isMuted()).isFalse();
1471     }
1472 
1473     @Test
onAudioGainChanged_validGain()1474     public void onAudioGainChanged_validGain() {
1475         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1476 
1477         List<Integer> reasons = List.of(Reasons.FORCED_MASTER_MUTE, Reasons.NAV_DUCKING);
1478         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1479         musicGain.zoneId = ZONE_ID;
1480         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1481         musicGain.volumeIndex = DEFAULT_GAIN_INDEX;
1482         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1483 
1484         AudioGainConfigInfo navGain = new AudioGainConfigInfo();
1485         navGain.zoneId = ZONE_ID;
1486         navGain.devicePortAddress = NAVIGATION_DEVICE_ADDRESS;
1487         navGain.volumeIndex = DEFAULT_GAIN_INDEX;
1488         CarAudioGainConfigInfo navCarGain = new CarAudioGainConfigInfo(navGain);
1489 
1490         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1491         // Broadcasted to all CarAudioDeviceInfo
1492         verify(mMediaDeviceInfo).setCurrentGain(TestCarAudioDeviceInfoBuilder.DEFAULT_GAIN);
1493         verify(mNavigationDeviceInfo).setCurrentGain(TestCarAudioDeviceInfoBuilder.DEFAULT_GAIN);
1494 
1495         carVolumeGroup.onAudioGainChanged(reasons, navCarGain);
1496         // Broadcasted to all CarAudioDeviceInfo
1497         verify(mMediaDeviceInfo, times(2)).setCurrentGain(
1498                 TestCarAudioDeviceInfoBuilder.DEFAULT_GAIN);
1499         verify(mNavigationDeviceInfo, times(2)).setCurrentGain(
1500                 TestCarAudioDeviceInfoBuilder.DEFAULT_GAIN);
1501     }
1502 
1503     @Test
onAudioGainChanged_invalidGain()1504     public void onAudioGainChanged_invalidGain() {
1505         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1506         List<Integer> reasons = List.of(Reasons.REMOTE_MUTE, Reasons.NAV_DUCKING);
1507         AudioGainConfigInfo unknownGain = new AudioGainConfigInfo();
1508         unknownGain.zoneId = ZONE_ID;
1509         unknownGain.devicePortAddress = OTHER_ADDRESS;
1510         unknownGain.volumeIndex = 666;
1511         CarAudioGainConfigInfo unknownCarGain = new CarAudioGainConfigInfo(unknownGain);
1512 
1513         expectWithMessage("Audio gain changed with invalid gain")
1514                 .that(carVolumeGroup.onAudioGainChanged(reasons, unknownCarGain))
1515                 .isEqualTo(EVENT_TYPE_NONE);
1516         verify(mMediaDeviceInfo, never()).setCurrentGain(anyInt());
1517         verify(mNavigationDeviceInfo, never()).setCurrentGain(anyInt());
1518     }
1519 
1520     @Test
onAudioGainChanged_comboAttenuationLimitation_simultaneously()1521     public void onAudioGainChanged_comboAttenuationLimitation_simultaneously() {
1522         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1523         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1524         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
1525         List<Integer> reasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1526         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1527         musicGain.zoneId = ZONE_ID;
1528         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1529         musicGain.volumeIndex = comboLimitAttenuation;
1530         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1531 
1532         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1533 
1534         expectWithMessage("Attenuated state in combo limited / attenuated")
1535                 .that(carVolumeGroup.isAttenuated()).isTrue();
1536         expectWithMessage("Limit state in combo limited / attenuated")
1537                 .that(carVolumeGroup.isLimited()).isTrue();
1538         expectWithMessage("Attenuated gain index")
1539                 .that(carVolumeGroup.getCurrentGainIndex())
1540                 .isEqualTo(comboLimitAttenuation);
1541     }
1542 
1543     @Test
onAudioGainChanged_resetAttenuation_whileComboAttenuationLimitation()1544     public void onAudioGainChanged_resetAttenuation_whileComboAttenuationLimitation() {
1545         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1546         int comboLimitAttenuation = DEFAULT_GAIN_INDEX;
1547         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
1548         List<Integer> reasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1549         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1550         musicGain.zoneId = ZONE_ID;
1551         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1552         musicGain.volumeIndex = comboLimitAttenuation;
1553         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1554         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1555 
1556         // Set a gain under the limit
1557         carVolumeGroup.setCurrentGainIndex(comboLimitAttenuation - 1);
1558 
1559         expectWithMessage("Attenuation state after attempt to set the index")
1560                 .that(carVolumeGroup.isAttenuated()).isFalse();
1561         expectWithMessage("Limitation state after from attempt to set the gain")
1562                 .that(carVolumeGroup.isLimited()).isTrue();
1563         expectWithMessage("Limited gain index")
1564                 .that(carVolumeGroup.getCurrentGainIndex())
1565                 .isEqualTo(comboLimitAttenuation - 1);
1566     }
1567 
1568     @Test
onAudioGainChanged_withGainUpdate_whileComboAttenuationLimitation()1569     public void onAudioGainChanged_withGainUpdate_whileComboAttenuationLimitation() {
1570         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1571         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1572         carVolumeGroup.setCurrentGainIndex(MAX_GAIN_INDEX);
1573         List<Integer> reasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1574         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1575         musicGain.zoneId = ZONE_ID;
1576         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1577         musicGain.volumeIndex = comboLimitAttenuation;
1578         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1579         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1580 
1581         // any new callback will be interpreted as an update of the current reasons
1582         // (limit and attenuation)
1583         int updatedComboLimitAttenuation = DEFAULT_GAIN_INDEX + 1;
1584         musicGain.volumeIndex = updatedComboLimitAttenuation;
1585         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1586         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1587 
1588         expectWithMessage("Attenuated state in combo limited / attenuated")
1589                 .that(carVolumeGroup.isAttenuated()).isTrue();
1590         expectWithMessage("Limit state in combo limited / attenuated")
1591                 .that(carVolumeGroup.isLimited()).isTrue();
1592         expectWithMessage("Attenuated gain index")
1593                 .that(carVolumeGroup.getCurrentGainIndex())
1594                 .isEqualTo(updatedComboLimitAttenuation);
1595     }
1596 
1597     @Test
onAudioGainChanged_limitation_withLimitUpdate()1598     public void onAudioGainChanged_limitation_withLimitUpdate() {
1599         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1600         int initialLimit = DEFAULT_GAIN_INDEX - 1;
1601         int initialIndex = MAX_GAIN_INDEX;
1602         carVolumeGroup.setCurrentGainIndex(initialIndex);
1603         List<Integer> reasons = List.of(Reasons.THERMAL_LIMITATION);
1604         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1605         musicGain.zoneId = ZONE_ID;
1606         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1607         musicGain.volumeIndex = initialLimit;
1608         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1609         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1610 
1611         // any new callback will be interpreted as an update of the limitation, allowing higher
1612         // volume index
1613         int updatedLimit = DEFAULT_GAIN_INDEX;
1614         musicGain.volumeIndex = updatedLimit;
1615         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1616         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1617 
1618         expectWithMessage("Limitation state after limitation with less restrictive limit update")
1619                 .that(carVolumeGroup.isLimited()).isTrue();
1620         expectWithMessage("Gain index after limitation with less restrictive limit update")
1621                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(updatedLimit);
1622     }
1623 
1624     @Test
onAudioGainChanged_endOfRestrictions_afterLimitationWithLimitUpdate()1625     public void onAudioGainChanged_endOfRestrictions_afterLimitationWithLimitUpdate() {
1626         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1627         int initialLimit = DEFAULT_GAIN_INDEX - 1;
1628         int initialIndex = MAX_GAIN_INDEX;
1629         carVolumeGroup.setCurrentGainIndex(initialIndex);
1630         List<Integer> reasons = List.of(Reasons.THERMAL_LIMITATION);
1631         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1632         musicGain.zoneId = ZONE_ID;
1633         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1634         musicGain.volumeIndex = initialLimit;
1635         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1636         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1637         musicGain.volumeIndex = initialLimit + 1;
1638         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1639         carVolumeGroup.onAudioGainChanged(reasons, musicCarGain);
1640 
1641         // End of restrictions
1642         List<Integer> noReasons = new ArrayList<>(0);
1643         carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain);
1644 
1645         expectWithMessage("Limitation state after end of restrictions")
1646                 .that(carVolumeGroup.isLimited()).isFalse();
1647         expectWithMessage("Gain index after end of restrictions")
1648                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(initialIndex);
1649     }
1650 
1651     @Test
onAudioGainChanged_comboAttenuationLimitationHigherLimit_limitationStartFirst()1652     public void onAudioGainChanged_comboAttenuationLimitationHigherLimit_limitationStartFirst() {
1653         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1654         int limitation = DEFAULT_GAIN_INDEX;
1655         int initialIndex = MAX_GAIN_INDEX;
1656         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1657         carVolumeGroup.setCurrentGainIndex(initialIndex);
1658         // Limitation starts first, gain is the limited index
1659         List<Integer> limitationReasons = List.of(Reasons.THERMAL_LIMITATION);
1660         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1661         musicGain.zoneId = ZONE_ID;
1662         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1663         musicGain.volumeIndex = limitation;
1664         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1665         carVolumeGroup.onAudioGainChanged(limitationReasons, musicCarGain);
1666 
1667         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / new limit
1668         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1669         musicGain.volumeIndex = comboLimitAttenuation;
1670         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1671         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1672 
1673         expectWithMessage("Limitation state after combo").that(carVolumeGroup.isLimited()).isTrue();
1674         expectWithMessage("Attenuation state after combo")
1675                 .that(carVolumeGroup.isAttenuated()).isTrue();
1676         expectWithMessage("Gain index after combo")
1677                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(comboLimitAttenuation);
1678     }
1679 
1680     @Test
onAudioGainChanged_comboAttenuationLimitationWithHigerLimit_whilelimited()1681     public void onAudioGainChanged_comboAttenuationLimitationWithHigerLimit_whilelimited() {
1682         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1683         int limitation = DEFAULT_GAIN_INDEX;
1684         int initialIndex = MAX_GAIN_INDEX;
1685         int comboLimitAttenuation = limitation + 1;
1686         carVolumeGroup.setCurrentGainIndex(initialIndex);
1687         // Limitation starts first, gain is the limited index
1688         List<Integer> limitationReasons = List.of(Reasons.THERMAL_LIMITATION);
1689         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1690         musicGain.zoneId = ZONE_ID;
1691         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1692         musicGain.volumeIndex = limitation;
1693         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1694         carVolumeGroup.onAudioGainChanged(limitationReasons, musicCarGain);
1695 
1696         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / new limit
1697         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1698         musicGain.volumeIndex = comboLimitAttenuation;
1699         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1700         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1701 
1702         expectWithMessage("Limitation state after combo")
1703                 .that(carVolumeGroup.isLimited()).isTrue();
1704         expectWithMessage("Attenuation state after combo")
1705                 .that(carVolumeGroup.isAttenuated()).isTrue();
1706         expectWithMessage("Gain index after combo")
1707                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(comboLimitAttenuation);
1708     }
1709 
1710     @Test
onAudioGainChanged_comboAttenuationLimitationWithHigherLimit_whileAttenuated()1711     public void onAudioGainChanged_comboAttenuationLimitationWithHigherLimit_whileAttenuated() {
1712         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1713         int initialIndex = MAX_GAIN_INDEX;
1714         int attenuation = DEFAULT_GAIN_INDEX;
1715         int comboLimitAttenuation = DEFAULT_GAIN_INDEX + 1;
1716         carVolumeGroup.setCurrentGainIndex(initialIndex);
1717         List<Integer> attenuationReasons = List.of(Reasons.ADAS_DUCKING);
1718         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1719         musicGain.zoneId = ZONE_ID;
1720         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1721         musicGain.volumeIndex = attenuation;
1722         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1723         carVolumeGroup.onAudioGainChanged(attenuationReasons, musicCarGain);
1724 
1725         // COMBO ATTENUATION + LIMITATION, gain is the new attenuation / limit
1726         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1727         musicGain.volumeIndex = comboLimitAttenuation;
1728         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1729         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1730 
1731         expectWithMessage("Limitation state after combo")
1732                 .that(carVolumeGroup.isLimited()).isTrue();
1733         expectWithMessage("Attenuation state after combo")
1734                 .that(carVolumeGroup.isAttenuated()).isTrue();
1735         expectWithMessage("Gain index after combo")
1736                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(comboLimitAttenuation);
1737     }
1738 
1739     @Test
onAudioGainChanged_comboAttenuationLimitationWithLowerLimit_whileAttenuated()1740     public void onAudioGainChanged_comboAttenuationLimitationWithLowerLimit_whileAttenuated() {
1741         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1742         int initialIndex = MAX_GAIN_INDEX;
1743         int attenuation = DEFAULT_GAIN_INDEX;
1744         int comboLimitAttenuation = attenuation - 1;
1745         carVolumeGroup.setCurrentGainIndex(initialIndex);
1746         List<Integer> attenuationReasons = List.of(Reasons.ADAS_DUCKING);
1747         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1748         musicGain.zoneId = ZONE_ID;
1749         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1750         musicGain.volumeIndex = attenuation;
1751         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1752         carVolumeGroup.onAudioGainChanged(attenuationReasons, musicCarGain);
1753 
1754         // COMBO ATTENUATION + LIMITATION, gain is the new attenuation/ limit, lower than previous
1755         // attenuation
1756         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1757         musicGain.volumeIndex = comboLimitAttenuation;
1758         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1759         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1760 
1761         expectWithMessage("Limitation state after combo")
1762                 .that(carVolumeGroup.isLimited()).isTrue();
1763         expectWithMessage("Attenuation state after combo")
1764                 .that(carVolumeGroup.isAttenuated()).isTrue();
1765         expectWithMessage("Gain index after combo")
1766                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(comboLimitAttenuation);
1767     }
1768 
1769     @Test
onAudioGainChanged_comboAttenuationLimitation_withHigherLimitUpdate()1770     public void onAudioGainChanged_comboAttenuationLimitation_withHigherLimitUpdate() {
1771         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1772         int comboLimitAttenuation = DEFAULT_GAIN_INDEX;
1773         int comboLimitAttenuationUpdate = DEFAULT_GAIN_INDEX + 1;
1774         int initialIndex = MAX_GAIN_INDEX;
1775         carVolumeGroup.setCurrentGainIndex(initialIndex);
1776         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1777         musicGain.zoneId = ZONE_ID;
1778         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1779         musicGain.volumeIndex = comboLimitAttenuation;
1780         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1781         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / new limit
1782         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1783         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1784 
1785         // COMBO ATTENUATION + LIMITATION, gain is the new limit / new attenuation
1786         musicGain.volumeIndex = comboLimitAttenuationUpdate;
1787         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1788         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1789 
1790         expectWithMessage("Limitation state after combo with gain update")
1791                 .that(carVolumeGroup.isLimited()).isTrue();
1792         expectWithMessage("Attenuation state after combo with gain update")
1793                 .that(carVolumeGroup.isAttenuated()).isTrue();
1794         expectWithMessage("Gain index after combo with gain update")
1795                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(comboLimitAttenuationUpdate);
1796     }
1797 
1798     @Test
onAudioGainChanged_comboAttenuationLimitation_withLowerLimitUpdate()1799     public void onAudioGainChanged_comboAttenuationLimitation_withLowerLimitUpdate() {
1800         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1801         int comboLimitAttenuation = DEFAULT_GAIN_INDEX;
1802         int comboLimitAttenuationUpdate = DEFAULT_GAIN_INDEX - 1;
1803         int initialIndex = MAX_GAIN_INDEX;
1804         carVolumeGroup.setCurrentGainIndex(initialIndex);
1805         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1806         musicGain.zoneId = ZONE_ID;
1807         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1808         musicGain.volumeIndex = comboLimitAttenuation;
1809         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1810         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / limit
1811         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1812         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1813 
1814         // COMBO ATTENUATION + LIMITATION, gain is the new attenuation / new limit
1815         musicGain.volumeIndex = comboLimitAttenuationUpdate;
1816         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1817         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1818 
1819         expectWithMessage("Limitation state after combo with gain update")
1820                 .that(carVolumeGroup.isLimited()).isTrue();
1821         expectWithMessage("Attenuation state after combo with gain update")
1822                 .that(carVolumeGroup.isAttenuated()).isTrue();
1823         expectWithMessage("Gain index after combo with gain update")
1824                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(comboLimitAttenuationUpdate);
1825     }
1826 
1827     @Test
onAudioGainChanged_comboAttenuationLimitationHigerLimit_attenuationEndsFirst()1828     public void onAudioGainChanged_comboAttenuationLimitationHigerLimit_attenuationEndsFirst() {
1829         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1830         int limitation = DEFAULT_GAIN_INDEX;
1831         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1832         int initialIndex = MAX_GAIN_INDEX;
1833         carVolumeGroup.setCurrentGainIndex(initialIndex);
1834         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1835         musicGain.zoneId = ZONE_ID;
1836         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1837         musicGain.volumeIndex = comboLimitAttenuation;
1838         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1839         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / limit
1840         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1841         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1842 
1843         // End of Attenuation first
1844         musicGain.volumeIndex = limitation;
1845         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1846         List<Integer> limitationReasons = List.of(Reasons.THERMAL_LIMITATION);
1847         carVolumeGroup.onAudioGainChanged(limitationReasons, musicCarGain);
1848 
1849         expectWithMessage("Limitation state after end of combo")
1850                 .that(carVolumeGroup.isLimited()).isTrue();
1851         expectWithMessage("Attenuation state after end of combo")
1852                 .that(carVolumeGroup.isAttenuated()).isFalse();
1853         expectWithMessage("Gain index after end of combo")
1854                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(limitation);
1855     }
1856 
1857     @Test
onAudioGainChanged_comboAttenuationLimitationLowerLimit_attenuationEndsFirst()1858     public void onAudioGainChanged_comboAttenuationLimitationLowerLimit_attenuationEndsFirst() {
1859         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1860         int limitation = DEFAULT_GAIN_INDEX - 1;
1861         int comboLimitAttenuation = DEFAULT_GAIN_INDEX;
1862         int initialIndex = MAX_GAIN_INDEX;
1863         carVolumeGroup.setCurrentGainIndex(initialIndex);
1864         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1865         musicGain.zoneId = ZONE_ID;
1866         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1867         musicGain.volumeIndex = comboLimitAttenuation;
1868         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1869         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / limit
1870         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1871         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1872 
1873         // End of Attenuation first
1874         musicGain.volumeIndex = limitation;
1875         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1876         List<Integer> limitationReasons = List.of(Reasons.THERMAL_LIMITATION);
1877         carVolumeGroup.onAudioGainChanged(limitationReasons, musicCarGain);
1878 
1879         expectWithMessage("Limitation state after end of combo")
1880                 .that(carVolumeGroup.isLimited()).isTrue();
1881         expectWithMessage("Attenuation state after end of combo")
1882                 .that(carVolumeGroup.isAttenuated()).isFalse();
1883         expectWithMessage("Gain index after end of combo")
1884                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(limitation);
1885     }
1886 
1887     @Test
onAudioGainChanged_endOfRestrictions_whileComboAndattenuationEndsFirst()1888     public void onAudioGainChanged_endOfRestrictions_whileComboAndattenuationEndsFirst() {
1889         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1890         int limitation = DEFAULT_GAIN_INDEX;
1891         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1892         int initialIndex = MAX_GAIN_INDEX;
1893         carVolumeGroup.setCurrentGainIndex(initialIndex);
1894         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1895         musicGain.zoneId = ZONE_ID;
1896         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1897         musicGain.volumeIndex = comboLimitAttenuation;
1898         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1899         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / limit
1900         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1901         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1902         // End of Attenuation first
1903         musicGain.volumeIndex = limitation;
1904         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1905         List<Integer> limitationReasons = List.of(Reasons.THERMAL_LIMITATION);
1906         carVolumeGroup.onAudioGainChanged(limitationReasons, musicCarGain);
1907 
1908         // End of restrictions
1909         List<Integer> noReasons = new ArrayList<>(0);
1910         carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain);
1911 
1912         expectWithMessage("Attenuation state after end of restrictions")
1913                 .that(carVolumeGroup.isAttenuated()).isFalse();
1914         expectWithMessage("Limitation state after end of restrictions")
1915                 .that(carVolumeGroup.isLimited()).isFalse();
1916         expectWithMessage("Gain index after end of restrictions")
1917                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(initialIndex);
1918     }
1919 
1920     @Test
onAudioGainChanged_comboAttenuationLimitation_limitationEndsFirst()1921     public void onAudioGainChanged_comboAttenuationLimitation_limitationEndsFirst() {
1922         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1923         int initialIndex = MAX_GAIN_INDEX;
1924         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1925         carVolumeGroup.setCurrentGainIndex(initialIndex);
1926         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1927         musicGain.zoneId = ZONE_ID;
1928         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1929         musicGain.volumeIndex = comboLimitAttenuation;
1930         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1931         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / new limit
1932         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1933         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1934 
1935         // End of limitation first, lets change the attenuation also (higher than previous limit)
1936         int attenuation = comboLimitAttenuation + 1;
1937         musicGain.volumeIndex = attenuation;
1938         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1939         List<Integer> attenuationReasons = List.of(Reasons.ADAS_DUCKING);
1940         carVolumeGroup.onAudioGainChanged(attenuationReasons, musicCarGain);
1941 
1942         expectWithMessage("Limitation state after end of combo")
1943                 .that(carVolumeGroup.isLimited()).isFalse();
1944         expectWithMessage("Attenuation state after end of combo")
1945                 .that(carVolumeGroup.isAttenuated()).isTrue();
1946         expectWithMessage("Gain index after combo")
1947                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(attenuation);
1948     }
1949 
1950     @Test
onAudioGainChanged_endOfRestriction_afterComboAndlimitationEndsFirst()1951     public void onAudioGainChanged_endOfRestriction_afterComboAndlimitationEndsFirst() {
1952         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1953         int initialIndex = MAX_GAIN_INDEX;
1954         int comboLimitAttenuation = DEFAULT_GAIN_INDEX - 1;
1955         carVolumeGroup.setCurrentGainIndex(initialIndex);
1956         AudioGainConfigInfo musicGain = new AudioGainConfigInfo();
1957         musicGain.zoneId = ZONE_ID;
1958         musicGain.devicePortAddress = MEDIA_DEVICE_ADDRESS;
1959         musicGain.volumeIndex = comboLimitAttenuation;
1960         CarAudioGainConfigInfo musicCarGain = new CarAudioGainConfigInfo(musicGain);
1961         // COMBO ATTENUATION + LIMITATION, gain is the attenuation / new limit
1962         List<Integer> comboReasons = List.of(Reasons.THERMAL_LIMITATION, Reasons.ADAS_DUCKING);
1963         carVolumeGroup.onAudioGainChanged(comboReasons, musicCarGain);
1964         // End of limitation first, lets change the attenuation also (higher than previous limit)
1965         int attenuation = comboLimitAttenuation + 1;
1966         musicGain.volumeIndex = attenuation;
1967         musicCarGain = new CarAudioGainConfigInfo(musicGain);
1968         List<Integer> attenuationReasons = List.of(Reasons.ADAS_DUCKING);
1969         carVolumeGroup.onAudioGainChanged(attenuationReasons, musicCarGain);
1970 
1971         // End of restrictions
1972         List<Integer> noReasons = new ArrayList<>(0);
1973         carVolumeGroup.onAudioGainChanged(noReasons, musicCarGain);
1974 
1975         expectWithMessage("Attenuation state after end of restrictions")
1976                 .that(carVolumeGroup.isAttenuated()).isFalse();
1977         expectWithMessage("Limitation state after end of restrictions")
1978                 .that(carVolumeGroup.isLimited()).isFalse();
1979         expectWithMessage("Gain index after end of restrictions")
1980                 .that(carVolumeGroup.getCurrentGainIndex()).isEqualTo(initialIndex);
1981     }
1982 
1983     @Test
getCarVolumeGroupInfo()1984     public void getCarVolumeGroupInfo() {
1985         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
1986         carVolumeGroup.setCurrentGainIndex(0);
1987 
1988         CarVolumeGroupInfo info = carVolumeGroup.getCarVolumeGroupInfo();
1989 
1990         expectWithMessage("Car volume group info id")
1991                 .that(info.getId()).isEqualTo(ZONE_ID);
1992         expectWithMessage("Car volume group info zone id")
1993                 .that(info.getId()).isEqualTo(GROUP_ID);
1994         expectWithMessage("Car volume group info current gain")
1995                 .that(info.getVolumeGainIndex()).isEqualTo(carVolumeGroup.getCurrentGainIndex());
1996         expectWithMessage("Car volume group info max gain")
1997                 .that(info.getMaxVolumeGainIndex()).isEqualTo(carVolumeGroup.getMaxGainIndex());
1998         expectWithMessage("Car volume group info min gain")
1999                 .that(info.getMinVolumeGainIndex()).isEqualTo(carVolumeGroup.getMinGainIndex());
2000         expectWithMessage("Car volume group info muted state")
2001                 .that(info.isMuted()).isEqualTo(carVolumeGroup.isMuted());
2002         expectWithMessage("Car volume group info blocked state")
2003                 .that(info.isBlocked()).isEqualTo(carVolumeGroup.isBlocked());
2004         expectWithMessage("Car volume group info attenuated state")
2005                 .that(info.isAttenuated()).isEqualTo(carVolumeGroup.isAttenuated());
2006     }
2007 
2008     @Test
getCarVolumeGroupInfo_withMinMaxActivationVolumeEnabled()2009     public void getCarVolumeGroupInfo_withMinMaxActivationVolumeEnabled() {
2010         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
2011 
2012         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
2013         carVolumeGroup.setCurrentGainIndex(0);
2014 
2015         CarVolumeGroupInfo info = carVolumeGroup.getCarVolumeGroupInfo();
2016 
2017         expectWithMessage("Car volume group info min activation gain")
2018                 .that(info.getMinActivationVolumeGainIndex())
2019                 .isEqualTo(carVolumeGroup.getMinActivationGainIndex());
2020         expectWithMessage("Car volume group info max activation gain")
2021                 .that(info.getMaxActivationVolumeGainIndex())
2022                 .isEqualTo(carVolumeGroup.getMaxActivationGainIndex());
2023     }
2024 
2025     @Test
getAudioAttributes()2026     public void getAudioAttributes() {
2027         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
2028 
2029         List<AudioAttributes> audioAttributes = carVolumeGroup.getAudioAttributes();
2030 
2031         expectWithMessage("Group audio attributes").that(audioAttributes).containsExactly(
2032                 CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA),
2033                 CarAudioContext.getAudioAttributeFromUsage(USAGE_GAME),
2034                 CarAudioContext.getAudioAttributeFromUsage(USAGE_UNKNOWN));
2035     }
2036 
2037     @Test
hasAudioAttributes_withAttributesInVolumeGroup()2038     public void hasAudioAttributes_withAttributesInVolumeGroup() {
2039         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
2040 
2041         expectWithMessage("Game attributes in volume group").that(carVolumeGroup
2042                         .hasAudioAttributes(CarAudioContext.getAudioAttributeFromUsage(USAGE_GAME)))
2043                 .isTrue();
2044     }
2045 
2046     @Test
hasAudioAttributes_withAttributeUsageInVolumeGroup()2047     public void hasAudioAttributes_withAttributeUsageInVolumeGroup() {
2048         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
2049 
2050         expectWithMessage("Audio attributes with game usage in volume group")
2051                 .that(carVolumeGroup.hasAudioAttributes(new AudioAttributes.Builder()
2052                         .setUsage(USAGE_GAME).build())).isTrue();
2053     }
2054 
2055     @Test
hasAudioAttributes_withAttributesNotInVolumeGroup()2056     public void hasAudioAttributes_withAttributesNotInVolumeGroup() {
2057         CarVolumeGroup carVolumeGroup = getCarVolumeGroupWithMusicBound();
2058 
2059         expectWithMessage("Notification attributes not in volume group").that(carVolumeGroup
2060                 .hasAudioAttributes(CarAudioContext.getAudioAttributeFromUsage(
2061                         USAGE_NOTIFICATION))).isFalse();
2062     }
2063 
2064 
2065     @Test
getAllSupportedUsagesForAddress()2066     public void getAllSupportedUsagesForAddress() {
2067         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
2068 
2069         List<Integer> supportedUsagesForMediaAddress =
2070                 carVolumeGroup.getAllSupportedUsagesForAddress(mMediaDeviceInfo.getAddress());
2071 
2072         List<Integer> expectedUsagesForMediaAddress = List.of(USAGE_MEDIA, USAGE_GAME,
2073                 USAGE_UNKNOWN, USAGE_VOICE_COMMUNICATION, USAGE_CALL_ASSISTANT,
2074                 USAGE_VOICE_COMMUNICATION_SIGNALLING, USAGE_NOTIFICATION_RINGTONE);
2075         expectWithMessage("Usages for media (%s)", expectedUsagesForMediaAddress)
2076                 .that(supportedUsagesForMediaAddress)
2077                 .containsExactlyElementsIn(expectedUsagesForMediaAddress);
2078 
2079         List<Integer> supportedUsagesForNavAddress =
2080                 carVolumeGroup.getAllSupportedUsagesForAddress(mNavigationDeviceInfo.getAddress());
2081 
2082         List<Integer> expectedUsagesForNavAddress = List.of(
2083                 USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, USAGE_ALARM, USAGE_NOTIFICATION,
2084                 USAGE_NOTIFICATION_EVENT);
2085         expectWithMessage("Usages for nav (%s)", expectedUsagesForNavAddress)
2086                 .that(supportedUsagesForNavAddress)
2087                 .containsExactlyElementsIn(expectedUsagesForNavAddress);
2088     }
2089 
2090     @Test
isActive()2091     public void isActive() {
2092         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
2093 
2094         expectWithMessage("Active status").that(carVolumeGroup.isActive()).isTrue();
2095     }
2096 
2097     @Test
isActive_withInactiveGroup()2098     public void isActive_withInactiveGroup() {
2099         CarVolumeGroup carVolumeGroup = testDynamicVolumeGroupSetup();
2100 
2101         expectWithMessage("Active status, with inactive group")
2102                 .that(carVolumeGroup.isActive()).isFalse();
2103     }
2104 
2105     @Test
validateDeviceTypes_forGroupWithNoDynamicDevices()2106     public void validateDeviceTypes_forGroupWithNoDynamicDevices() {
2107         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
2108         ArraySet<Integer> existingDevices = new ArraySet<>();
2109 
2110         expectWithMessage("Status for valid volume group with BUS devices")
2111                 .that(carVolumeGroup.validateDeviceTypes(existingDevices)).isTrue();
2112         expectWithMessage("Dynamic devices for group").that(existingDevices).isEmpty();
2113     }
2114 
2115     @Test
validateDeviceTypes_forGroupWithNoDynamicDevices_andExistingBusDevice()2116     public void validateDeviceTypes_forGroupWithNoDynamicDevices_andExistingBusDevice() {
2117         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
2118         ArraySet<Integer> existingDevices = new ArraySet<>();
2119         existingDevices.add(TYPE_BUS);
2120 
2121         expectWithMessage("Status for valid volume group with BUS devices and existing BUS device")
2122                 .that(carVolumeGroup.validateDeviceTypes(existingDevices)).isTrue();
2123         expectWithMessage("Dynamic devices for group with BUS device").that(existingDevices)
2124                 .containsExactly(TYPE_BUS);
2125     }
2126 
2127     @Test
validateDeviceTypes_forGroupWithDynamicTypes()2128     public void validateDeviceTypes_forGroupWithDynamicTypes() {
2129         CarVolumeGroup carVolumeGroup = testDynamicVolumeGroupSetup();
2130         ArraySet<Integer> existingDevices = new ArraySet<>();
2131 
2132         expectWithMessage("Status for valid volume group with dynamic device")
2133                 .that(carVolumeGroup.validateDeviceTypes(existingDevices)).isTrue();
2134         expectWithMessage("Dynamic devices for group with dynamic device")
2135                 .that(existingDevices).containsExactly(TYPE_BLUETOOTH_A2DP);
2136     }
2137 
2138     @Test
validateDeviceTypes_forGroupWithDynamicDeviceAndBus()2139     public void validateDeviceTypes_forGroupWithDynamicDeviceAndBus() {
2140         CarVolumeGroup carVolumeGroup = testInactiveVolumeGroupSetup();
2141         ArraySet<Integer> existingDevices = new ArraySet<>();
2142 
2143         expectWithMessage("Status for invalid volume group with dynamic device")
2144                 .that(carVolumeGroup.validateDeviceTypes(existingDevices)).isFalse();
2145         expectWithMessage("Dynamic devices for group with dynamic device and bus device")
2146                 .that(existingDevices).containsExactly(TYPE_BLUETOOTH_A2DP);
2147     }
2148 
2149     @Test
validateDeviceTypes_forGroupWithAlreadyExistingDynamicType()2150     public void validateDeviceTypes_forGroupWithAlreadyExistingDynamicType() {
2151         CarVolumeGroup carVolumeGroup = testDynamicVolumeGroupSetup();
2152         ArraySet<Integer> existingDevices = new ArraySet<>();
2153         existingDevices.add(TYPE_BLUETOOTH_A2DP);
2154 
2155         expectWithMessage("Status for invalid volume group with existing dynamic device")
2156                 .that(carVolumeGroup.validateDeviceTypes(existingDevices)).isFalse();
2157         expectWithMessage("Existing dynamic devices for group with dynamic device")
2158                 .that(existingDevices).containsExactly(TYPE_BLUETOOTH_A2DP);
2159     }
2160 
2161     @Test
onAudioVolumeGroupChanged()2162     public void onAudioVolumeGroupChanged() {
2163         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup(/* useCarVolumeGroupMute= */ false);
2164 
2165         expectWithMessage("Flags for audio volume group change")
2166                 .that(carVolumeGroup.onAudioVolumeGroupChanged(/* flags= */ 0)).isEqualTo(0);
2167     }
2168 
2169     @Test
getAddressForContext_withoutContextFound()2170     public void getAddressForContext_withoutContextFound() {
2171         CarVolumeGroup carVolumeGroup = testVolumeGroupSetup();
2172         int safetyContextId = TEST_CAR_AUDIO_CONTEXT.getContextForAudioAttribute(
2173                 CarAudioContext.getAudioAttributeFromUsage(USAGE_SAFETY));
2174 
2175         expectWithMessage("Device address for context not found in volume group")
2176                 .that(carVolumeGroup.getAddressForContext(safetyContextId)).isNull();
2177     }
2178 
getCarVolumeGroupWithMusicBound()2179     private CarVolumeGroup getCarVolumeGroupWithMusicBound() {
2180         CarVolumeGroupFactory factory = getFactory(/* useCarVolumeGroupMute= */ true);
2181         factory.setDeviceInfoForContext(TEST_MEDIA_CONTEXT_ID, mMediaDeviceInfo);
2182         return factory.getCarVolumeGroup(/* useCoreAudioVolume= */ false);
2183     }
2184 
getCarVolumeGroupWithNavigationBound(CarAudioSettings settings, boolean useCarVolumeGroupMute)2185     private CarVolumeGroup getCarVolumeGroupWithNavigationBound(CarAudioSettings settings,
2186             boolean useCarVolumeGroupMute) {
2187         CarVolumeGroupFactory factory =  new CarVolumeGroupFactory(mAudioManagerMock, settings,
2188                 TEST_CAR_AUDIO_CONTEXT, ZONE_ID, CONFIG_ID, GROUP_ID, /* name= */ "0",
2189                 useCarVolumeGroupMute, CAR_ACTIVATION_VOLUME_CONFIG);
2190         factory.setDeviceInfoForContext(TEST_NAVIGATION_CONTEXT_ID, mNavigationDeviceInfo);
2191         return factory.getCarVolumeGroup(/* useCoreAudioVolume= */ false);
2192     }
2193 
getVolumeGroupWithMuteAndNavBound(boolean isMuted, boolean persistMute, boolean useCarVolumeGroupMute)2194     CarVolumeGroup getVolumeGroupWithMuteAndNavBound(boolean isMuted, boolean persistMute,
2195             boolean useCarVolumeGroupMute) {
2196         CarAudioSettings settings = new SettingsBuilder(ZONE_ID, CONFIG_ID, GROUP_ID)
2197                 .setMuteForUser10(isMuted)
2198                 .setIsPersistVolumeGroupEnabled(persistMute)
2199                 .build();
2200         return getCarVolumeGroupWithNavigationBound(settings, useCarVolumeGroupMute);
2201     }
2202 
testVolumeGroupSetup()2203     private CarVolumeGroup testVolumeGroupSetup() {
2204         return testVolumeGroupSetup(/* useCarVolumeGroupMute= */ true);
2205     }
2206 
testInactiveVolumeGroupSetup()2207     private CarVolumeGroup testInactiveVolumeGroupSetup() {
2208         CarVolumeGroupFactory factory = getFactory(/* useCarVolumeGroupMute= */ true);
2209 
2210         factory.setDeviceInfoForContext(TEST_MEDIA_CONTEXT_ID, mInactiveMediaDeviceInfo);
2211         factory.setDeviceInfoForContext(TEST_CALL_CONTEXT_ID, mInactiveMediaDeviceInfo);
2212         factory.setDeviceInfoForContext(TEST_CALL_RING_CONTEXT_ID, mInactiveMediaDeviceInfo);
2213 
2214         factory.setDeviceInfoForContext(TEST_NAVIGATION_CONTEXT_ID, mNavigationDeviceInfo);
2215         factory.setDeviceInfoForContext(TEST_ALARM_CONTEXT_ID, mNavigationDeviceInfo);
2216         factory.setDeviceInfoForContext(TEST_NOTIFICATION_CONTEXT_ID, mNavigationDeviceInfo);
2217 
2218         return factory.getCarVolumeGroup(/* useCoreAudioVolume= */ false);
2219     }
2220 
testDynamicVolumeGroupSetup()2221     private CarVolumeGroup testDynamicVolumeGroupSetup() {
2222         CarVolumeGroupFactory factory = getFactory(/* useCarVolumeGroupMute= */ true);
2223 
2224         factory.setDeviceInfoForContext(TEST_MEDIA_CONTEXT_ID, mInactiveMediaDeviceInfo);
2225         factory.setDeviceInfoForContext(TEST_CALL_CONTEXT_ID, mInactiveMediaDeviceInfo);
2226         factory.setDeviceInfoForContext(TEST_CALL_RING_CONTEXT_ID, mInactiveMediaDeviceInfo);
2227 
2228         return factory.getCarVolumeGroup(/* useCoreAudioVolume= */ false);
2229     }
2230 
testVolumeGroupSetup(boolean useCarVolumeGroupMute)2231     private CarVolumeGroup testVolumeGroupSetup(boolean useCarVolumeGroupMute) {
2232         CarVolumeGroupFactory factory = getFactory(useCarVolumeGroupMute);
2233 
2234         factory.setDeviceInfoForContext(TEST_MEDIA_CONTEXT_ID, mMediaDeviceInfo);
2235         factory.setDeviceInfoForContext(TEST_CALL_CONTEXT_ID, mMediaDeviceInfo);
2236         factory.setDeviceInfoForContext(TEST_CALL_RING_CONTEXT_ID, mMediaDeviceInfo);
2237 
2238         factory.setDeviceInfoForContext(TEST_NAVIGATION_CONTEXT_ID, mNavigationDeviceInfo);
2239         factory.setDeviceInfoForContext(TEST_ALARM_CONTEXT_ID, mNavigationDeviceInfo);
2240         factory.setDeviceInfoForContext(TEST_NOTIFICATION_CONTEXT_ID, mNavigationDeviceInfo);
2241 
2242         return factory.getCarVolumeGroup(/* useCoreAudioVolume= */ false);
2243     }
2244 
getFactory(boolean useCarVolumeGroupMute)2245     CarVolumeGroupFactory getFactory(boolean useCarVolumeGroupMute) {
2246         return new CarVolumeGroupFactory(mAudioManagerMock, mSettingsMock, TEST_CAR_AUDIO_CONTEXT,
2247                 ZONE_ID, CONFIG_ID, GROUP_ID, GROUP_NAME, useCarVolumeGroupMute,
2248                 CAR_ACTIVATION_VOLUME_CONFIG);
2249     }
2250 
2251     private static final class SettingsBuilder {
2252         private final SparseIntArray mStoredGainIndexes = new SparseIntArray();
2253         private final SparseBooleanArray mStoreMuteStates = new SparseBooleanArray();
2254         private final int mZoneId;
2255         private final int mConfigId;
2256         private final int mGroupId;
2257 
2258         private boolean mPersistMute;
2259 
SettingsBuilder(int zoneId, int configId, int groupId)2260         SettingsBuilder(int zoneId, int configId, int groupId) {
2261             mZoneId = zoneId;
2262             mConfigId = configId;
2263             mGroupId = groupId;
2264         }
2265 
setGainIndexForUser(@serIdInt int userId)2266         SettingsBuilder setGainIndexForUser(@UserIdInt int userId) {
2267             mStoredGainIndexes.put(userId, TEST_GAIN_INDEX);
2268             return this;
2269         }
2270 
setMuteForUser10(boolean mute)2271         SettingsBuilder setMuteForUser10(boolean mute) {
2272             mStoreMuteStates.put(CarVolumeGroupUnitTest.TEST_USER_10, mute);
2273             return this;
2274         }
2275 
setIsPersistVolumeGroupEnabled(boolean persistMute)2276         SettingsBuilder setIsPersistVolumeGroupEnabled(boolean persistMute) {
2277             mPersistMute = persistMute;
2278             return this;
2279         }
2280 
build()2281         CarAudioSettings build() {
2282             CarAudioSettings settingsMock = Mockito.mock(CarAudioSettings.class);
2283             for (int storeIndex = 0; storeIndex < mStoredGainIndexes.size(); storeIndex++) {
2284                 int gainUserId = mStoredGainIndexes.keyAt(storeIndex);
2285                 when(settingsMock
2286                         .getStoredVolumeGainIndexForUser(gainUserId, mZoneId, mConfigId,
2287                                 mGroupId)).thenReturn(mStoredGainIndexes.get(gainUserId,
2288                         TestCarAudioDeviceInfoBuilder.DEFAULT_GAIN));
2289             }
2290             for (int muteIndex = 0; muteIndex < mStoreMuteStates.size(); muteIndex++) {
2291                 int muteUserId = mStoreMuteStates.keyAt(muteIndex);
2292                 when(settingsMock.getVolumeGroupMuteForUser(muteUserId, mZoneId, mConfigId,
2293                         mGroupId)).thenReturn(mStoreMuteStates.get(muteUserId,
2294                         /* valueIfKeyNotFound= */ false));
2295                 when(settingsMock.isPersistVolumeGroupMuteEnabled(muteUserId))
2296                         .thenReturn(mPersistMute);
2297             }
2298             return settingsMock;
2299         }
2300     }
2301 }
2302