1 /*
2  * Copyright (C) 2022 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.Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS;
20 import static android.car.Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME;
21 import static android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0;
22 import static android.car.media.CarAudioManager.AUDIO_FEATURE_AUDIO_MIRRORING;
23 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING;
24 import static android.car.media.CarAudioManager.AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME;
25 import static android.car.media.CarAudioManager.AUDIO_FEATURE_OEM_AUDIO_SERVICE;
26 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_EVENTS;
27 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING;
28 import static android.car.media.CarAudioManager.AUDIO_MIRROR_CAN_ENABLE;
29 import static android.car.media.CarAudioManager.AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES;
30 import static android.car.media.CarAudioManager.CONFIG_STATUS_AUTO_SWITCHED;
31 import static android.car.media.CarAudioManager.CONFIG_STATUS_CHANGED;
32 import static android.car.media.CarAudioManager.INVALID_AUDIO_ZONE;
33 import static android.car.media.CarAudioManager.INVALID_REQUEST_ID;
34 import static android.car.media.CarAudioManager.INVALID_VOLUME_GROUP_ID;
35 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
36 import static android.car.test.mocks.AndroidMockitoHelper.mockCarGetPlatformVersion;
37 import static android.car.test.mocks.AndroidMockitoHelper.mockContextCheckCallingOrSelfPermission;
38 import static android.content.pm.PackageManager.PERMISSION_DENIED;
39 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
40 import static android.media.AudioAttributes.USAGE_ALARM;
41 import static android.media.AudioAttributes.USAGE_ANNOUNCEMENT;
42 import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
43 import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
44 import static android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
45 import static android.media.AudioAttributes.USAGE_ASSISTANT;
46 import static android.media.AudioAttributes.USAGE_CALL_ASSISTANT;
47 import static android.media.AudioAttributes.USAGE_EMERGENCY;
48 import static android.media.AudioAttributes.USAGE_GAME;
49 import static android.media.AudioAttributes.USAGE_MEDIA;
50 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
51 import static android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT;
52 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
53 import static android.media.AudioAttributes.USAGE_SAFETY;
54 import static android.media.AudioAttributes.USAGE_UNKNOWN;
55 import static android.media.AudioAttributes.USAGE_VEHICLE_STATUS;
56 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
57 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
58 import static android.media.AudioDeviceInfo.TYPE_BLUETOOTH_A2DP;
59 import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC;
60 import static android.media.AudioDeviceInfo.TYPE_FM_TUNER;
61 import static android.media.AudioManager.AUDIOFOCUS_GAIN;
62 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
63 import static android.media.AudioManager.AUDIOFOCUS_LOSS;
64 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
65 import static android.media.AudioManager.AUDIOFOCUS_NONE;
66 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
67 import static android.media.AudioManager.ERROR;
68 import static android.media.AudioManager.EXTRA_VOLUME_STREAM_TYPE;
69 import static android.media.AudioManager.FLAG_FROM_KEY;
70 import static android.media.AudioManager.FLAG_PLAY_SOUND;
71 import static android.media.AudioManager.FLAG_SHOW_UI;
72 import static android.media.AudioManager.MASTER_MUTE_CHANGED_ACTION;
73 import static android.media.AudioManager.STREAM_MUSIC;
74 import static android.media.AudioManager.SUCCESS;
75 import static android.media.AudioManager.VOLUME_CHANGED_ACTION;
76 import static android.media.audio.common.AudioDeviceDescription.CONNECTION_BUS;
77 import static android.media.audio.common.AudioDeviceType.OUT_DEVICE;
78 import static android.media.audio.common.AudioGainMode.JOINT;
79 import static android.media.audiopolicy.Flags.FLAG_ENABLE_FADE_MANAGER_CONFIGURATION;
80 import static android.os.Build.VERSION.SDK_INT;
81 import static android.view.KeyEvent.ACTION_DOWN;
82 import static android.view.KeyEvent.ACTION_UP;
83 import static android.view.KeyEvent.KEYCODE_UNKNOWN;
84 import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
85 import static android.view.KeyEvent.KEYCODE_VOLUME_MUTE;
86 import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
87 
88 import static com.android.car.R.bool.audioEnableVolumeKeyEventsToDynamicDevices;
89 import static com.android.car.R.bool.audioPersistMasterMuteState;
90 import static com.android.car.R.bool.audioUseCarVolumeGroupEvent;
91 import static com.android.car.R.bool.audioUseCarVolumeGroupMuting;
92 import static com.android.car.R.bool.audioUseCoreRouting;
93 import static com.android.car.R.bool.audioUseCoreVolume;
94 import static com.android.car.R.bool.audioUseDynamicRouting;
95 import static com.android.car.R.bool.audioUseFadeManagerConfiguration;
96 import static com.android.car.R.bool.audioUseHalDuckingSignals;
97 import static com.android.car.R.bool.audioUseMinMaxActivationVolume;
98 import static com.android.car.R.integer.audioVolumeAdjustmentContextsVersion;
99 import static com.android.car.R.integer.audioVolumeKeyEventTimeoutMs;
100 import static com.android.car.audio.CarAudioService.CAR_DEFAULT_AUDIO_ATTRIBUTE;
101 import static com.android.car.audio.CarHalAudioUtils.usageToMetadata;
102 import static com.android.car.audio.GainBuilder.DEFAULT_GAIN;
103 import static com.android.car.audio.GainBuilder.MAX_GAIN;
104 import static com.android.car.audio.GainBuilder.MIN_GAIN;
105 import static com.android.car.audio.GainBuilder.STEP_SIZE;
106 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
107 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
108 
109 import static org.junit.Assert.assertThrows;
110 import static org.mockito.ArgumentMatchers.any;
111 import static org.mockito.ArgumentMatchers.anyBoolean;
112 import static org.mockito.ArgumentMatchers.anyInt;
113 import static org.mockito.ArgumentMatchers.anyString;
114 import static org.mockito.ArgumentMatchers.eq;
115 import static org.mockito.Mockito.atLeastOnce;
116 import static org.mockito.Mockito.mock;
117 import static org.mockito.Mockito.never;
118 import static org.mockito.Mockito.reset;
119 import static org.mockito.Mockito.times;
120 import static org.mockito.Mockito.when;
121 
122 import android.annotation.NonNull;
123 import android.car.Car;
124 import android.car.CarOccupantZoneManager;
125 import android.car.ICarOccupantZoneCallback;
126 import android.car.VehicleAreaSeat;
127 import android.car.builtin.media.AudioManagerHelper;
128 import android.car.builtin.media.AudioManagerHelper.AudioPatchInfo;
129 import android.car.builtin.os.UserManagerHelper;
130 import android.car.feature.Flags;
131 import android.car.media.CarAudioManager;
132 import android.car.media.CarAudioPatchHandle;
133 import android.car.media.CarAudioZoneConfigInfo;
134 import android.car.media.CarVolumeGroupEvent;
135 import android.car.media.CarVolumeGroupInfo;
136 import android.car.media.IAudioZoneConfigurationsChangeCallback;
137 import android.car.media.IAudioZonesMirrorStatusCallback;
138 import android.car.media.IMediaAudioRequestStatusCallback;
139 import android.car.media.IPrimaryZoneMediaAudioRequestCallback;
140 import android.car.media.ISwitchAudioZoneConfigCallback;
141 import android.car.settings.CarSettings;
142 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
143 import android.car.test.mocks.MockSettings;
144 import android.car.test.util.TemporaryFile;
145 import android.content.AttributionSource;
146 import android.content.BroadcastReceiver;
147 import android.content.ContentResolver;
148 import android.content.Context;
149 import android.content.Intent;
150 import android.content.pm.PackageManager;
151 import android.content.res.Resources;
152 import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
153 import android.hardware.automotive.audiocontrol.IAudioControl;
154 import android.hardware.automotive.audiocontrol.Reasons;
155 import android.media.AudioAttributes;
156 import android.media.AudioDeviceAttributes;
157 import android.media.AudioDeviceCallback;
158 import android.media.AudioDeviceInfo;
159 import android.media.AudioFocusInfo;
160 import android.media.AudioGain;
161 import android.media.AudioManager;
162 import android.media.AudioManager.AudioPlaybackCallback;
163 import android.media.AudioManager.AudioServerStateCallback;
164 import android.media.AudioPlaybackConfiguration;
165 import android.media.IAudioService;
166 import android.media.audio.common.AudioDevice;
167 import android.media.audio.common.AudioDeviceAddress;
168 import android.media.audio.common.AudioDeviceDescription;
169 import android.media.audio.common.AudioPort;
170 import android.media.audio.common.AudioPortDeviceExt;
171 import android.media.audio.common.AudioPortExt;
172 import android.media.audiopolicy.AudioPolicy;
173 import android.net.Uri;
174 import android.os.Binder;
175 import android.os.Build;
176 import android.os.Handler;
177 import android.os.HandlerThread;
178 import android.os.IBinder;
179 import android.os.RemoteException;
180 import android.os.ServiceManager;
181 import android.os.SystemProperties;
182 import android.os.UserHandle;
183 import android.platform.test.flag.junit.SetFlagsRule;
184 import android.provider.Settings;
185 import android.telephony.SubscriptionManager;
186 import android.telephony.TelephonyCallback;
187 import android.telephony.TelephonyManager;
188 import android.util.Log;
189 import android.util.NoSuchPropertyException;
190 import android.view.KeyEvent;
191 
192 import androidx.test.core.app.ApplicationProvider;
193 
194 import com.android.car.CarInputService;
195 import com.android.car.CarInputService.KeyEventListener;
196 import com.android.car.CarLocalServices;
197 import com.android.car.CarOccupantZoneService;
198 import com.android.car.CarServiceUtils;
199 import com.android.car.R;
200 import com.android.car.audio.hal.AudioControlFactory;
201 import com.android.car.audio.hal.AudioControlWrapper;
202 import com.android.car.audio.hal.AudioControlWrapper.AudioControlDeathRecipient;
203 import com.android.car.audio.hal.AudioControlWrapperAidl;
204 import com.android.car.audio.hal.HalAudioDeviceInfo;
205 import com.android.car.audio.hal.HalAudioGainCallback;
206 import com.android.car.audio.hal.HalAudioModuleChangeCallback;
207 import com.android.car.audio.hal.HalFocusListener;
208 import com.android.car.oem.CarOemAudioDuckingProxyService;
209 import com.android.car.oem.CarOemAudioFocusProxyService;
210 import com.android.car.oem.CarOemAudioVolumeProxyService;
211 import com.android.car.oem.CarOemProxyService;
212 import com.android.car.power.CarPowerManagementService;
213 
214 import org.junit.After;
215 import org.junit.Before;
216 import org.junit.Rule;
217 import org.junit.Test;
218 import org.mockito.ArgumentCaptor;
219 import org.mockito.Captor;
220 import org.mockito.Mock;
221 
222 import java.io.InputStream;
223 import java.util.ArrayList;
224 import java.util.Arrays;
225 import java.util.Collections;
226 import java.util.List;
227 import java.util.concurrent.CountDownLatch;
228 import java.util.concurrent.TimeUnit;
229 
230 public final class CarAudioServiceUnitTest extends AbstractExtendedMockitoTestCase {
231     private static final String TAG = CarAudioServiceUnitTest.class.getSimpleName();
232     private static final long TEST_CALLBACK_TIMEOUT_MS = 100;
233     private static final long TEST_ZONE_CONFIG_CALLBACK_TIMEOUT_MS = 500;
234     private static final int VOLUME_KEY_EVENT_TIMEOUT_MS = 3000;
235     private static final int AUDIO_CONTEXT_PRIORITY_LIST_VERSION_ONE = 1;
236     private static final int AUDIO_CONTEXT_PRIORITY_LIST_VERSION_TWO = 2;
237     private static final String MEDIA_TEST_DEVICE = "media_bus_device";
238     private static final String OEM_TEST_DEVICE = "oem_bus_device";
239     private static final String MIRROR_TEST_DEVICE = "mirror_bus_device";
240     private static final String NAVIGATION_TEST_DEVICE = "navigation_bus_device";
241     private static final String CALL_TEST_DEVICE = "call_bus_device";
242     private static final String NOTIFICATION_TEST_DEVICE = "notification_bus_device";
243     private static final String VOICE_TEST_DEVICE = "voice_bus_device";
244     private static final String RING_TEST_DEVICE = "ring_bus_device";
245     private static final String ALARM_TEST_DEVICE = "alarm_bus_device";
246     private static final String SYSTEM_BUS_DEVICE = "system_bus_device";
247     private static final String SECONDARY_TEST_DEVICE_CONFIG_0 = "secondary_zone_bus_100";
248     private static final String SECONDARY_TEST_DEVICE_CONFIG_1_0 = "secondary_zone_bus_200";
249     private static final String SECONDARY_TEST_DEVICE_CONFIG_1_1 = "secondary_zone_bus_201";
250     private static final String TEST_BT_DEVICE = "08:67:53:09";
251     private static final String TERTIARY_TEST_DEVICE_1 = "tertiary_zone_bus_100";
252     private static final String TERTIARY_TEST_DEVICE_2 = "tertiary_zone_bus_200";
253     private static final String QUATERNARY_TEST_DEVICE_1 = "quaternary_zone_bus_1";
254     private static final String TEST_REAR_ROW_3_DEVICE = "rear_row_three_zone_bus_1";
255     private static final String PRIMARY_ZONE_MICROPHONE_ADDRESS = "Built-In Mic";
256     private static final String PRIMARY_ZONE_FM_TUNER_ADDRESS = "FM Tuner";
257     private static final String SECONDARY_ZONE_CONFIG_NAME_1 = "secondary zone config 1";
258     private static final String SECONDARY_ZONE_CONFIG_NAME_2 = "secondary zone config 2";
259     public static final String SECONDARY_ZONE_BT_CONFIG_NAME = "secondary BT zone config 0";
260     private static final String DEFAULT_CONFIG_NAME_DYNAMIC_DEVICES = "primary zone config 0";
261     private static final String PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES = "primary zone BT media";
262     private static final String TERTIARY_CONFIG_NAME_DYNAMIC_DEVICES =
263             "primary zone headphones media";
264     private static final String MIRROR_OFF_SIGNAL = "mirroring=off";
265     // From the car audio configuration file in /res/raw/car_audio_configuration.xml
266     private static final int TEST_REAR_LEFT_ZONE_ID = 1;
267     private static final int TEST_REAR_RIGHT_ZONE_ID = 2;
268     private static final int TEST_FRONT_ZONE_ID = 3;
269     private static final int TEST_REAR_ROW_3_ZONE_ID = 4;
270     public static final int[] TEST_MIRROR_AUDIO_ZONES = new int[]{TEST_REAR_LEFT_ZONE_ID,
271             TEST_REAR_RIGHT_ZONE_ID};
272     private static final int OUT_OF_RANGE_ZONE = TEST_REAR_ROW_3_ZONE_ID + 1;
273     private static final int PRIMARY_ZONE_VOLUME_GROUP_COUNT = 4;
274     private static final int SECONDARY_ZONE_VOLUME_GROUP_COUNT = 1;
275     private static final int SECONDARY_ZONE_VOLUME_GROUP_ID = SECONDARY_ZONE_VOLUME_GROUP_COUNT - 1;
276     private static final int TEST_PRIMARY_ZONE_GROUP_0 = 0;
277     private static final int TEST_PRIMARY_ZONE_GROUP_1 = 1;
278     private static final int TEST_PRIMARY_ZONE_GROUP_2 = 2;
279     private static final int TEST_SECONDARY_ZONE_GROUP_0 = 0;
280     private static final int TEST_SECONDARY_ZONE_GROUP_1 = 1;
281     private static final int TEST_FLAGS = 0;
282     private static final float TEST_VALUE = -.75f;
283     private static final float INVALID_TEST_VALUE = -1.5f;
284     private static final int TEST_DISPLAY_TYPE = 2;
285     private static final int TEST_SEAT = 2;
286     private static final int PRIMARY_OCCUPANT_ZONE = 0;
287     private static final int INVALID_STATUS = 0;
288 
289     private static final int TEST_DRIVER_OCCUPANT_ZONE_ID = 1;
290     private static final int TEST_REAR_LEFT_OCCUPANT_ZONE_ID = 2;
291     private static final int TEST_REAR_RIGHT_OCCUPANT_ZONE_ID = 3;
292     private static final int TEST_FRONT_OCCUPANT_ZONE_ID = 4;
293     private static final int TEST_REAR_ROW_3_OCCUPANT_ZONE_ID = 5;
294     private static final int TEST_UNASSIGNED_OCCUPANT_ZONE_ID = 6;
295 
296     private static final int TEST_MEDIA_PORT_ID = 0;
297     private static final int TEST_NAV_PORT_ID = 1;
298     private static final String TEST_MEDIA_PORT_NAME = "Media bus";
299     private static final String TEST_NAV_PORT_NAME = "Nav bus";
300     private static final int TEST_GAIN_MIN_VALUE = -3000;
301     private static final int TEST_GAIN_MAX_VALUE = -1000;
302     private static final int TEST_GAIN_DEFAULT_VALUE = -2000;
303     private static final int TEST_GAIN_STEP_VALUE = 2;
304 
305     private static final int TEST_PLAYBACK_UID = 10101;
306 
307     private static final CarOccupantZoneManager.OccupantZoneInfo TEST_DRIVER_OCCUPANT =
308             getOccupantInfo(TEST_DRIVER_OCCUPANT_ZONE_ID,
309                     CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
310                     VehicleAreaSeat.SEAT_ROW_1_LEFT);
311     private static final CarOccupantZoneManager.OccupantZoneInfo
312             TEST_REAR_RIGHT_PASSENGER_OCCUPANT =
313             getOccupantInfo(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID,
314                     CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
315                     VehicleAreaSeat.SEAT_ROW_2_RIGHT);
316     private static final CarOccupantZoneManager.OccupantZoneInfo
317             TEST_FRONT_PASSENGER_OCCUPANT =
318             getOccupantInfo(TEST_FRONT_OCCUPANT_ZONE_ID,
319                     CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER,
320                     VehicleAreaSeat.SEAT_ROW_1_RIGHT);
321     private static final CarOccupantZoneManager.OccupantZoneInfo
322             TEST_REAR_LEFT_PASSENGER_OCCUPANT =
323             getOccupantInfo(TEST_REAR_LEFT_OCCUPANT_ZONE_ID,
324                     CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
325                     VehicleAreaSeat.SEAT_ROW_2_LEFT);
326 
327     private static final CarOccupantZoneManager.OccupantZoneInfo
328             TEST_REAR_ROW_3_PASSENGER_OCCUPANT =
329             getOccupantInfo(TEST_REAR_ROW_3_OCCUPANT_ZONE_ID,
330                     CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
331                     VehicleAreaSeat.SEAT_ROW_3_LEFT);
332 
333     private static final String PROPERTY_RO_ENABLE_AUDIO_PATCH =
334             "ro.android.car.audio.enableaudiopatch";
335 
336     private static final int MEDIA_APP_UID = 1086753;
337     private static final int TEST_REAR_RIGHT_UID = 1286753;
338     private static final String MEDIA_CLIENT_ID = "media-client-id";
339     private static final String MEDIA_PACKAGE_NAME = "com.android.car.audio";
340     private static final int MEDIA_EMPTY_FLAG = 0;
341     private static final String REGISTRATION_ID = "meh";
342     private static final int MEDIA_VOLUME_GROUP_ID = 0;
343     private static final int NAVIGATION_VOLUME_GROUP_ID = 1;
344     private static final int INVALID_USAGE = -1;
345     private static final int INVALID_AUDIO_FEATURE = -1;
346     private static final int TEST_DRIVER_USER_ID = 10;
347     private static final int TEST_REAR_LEFT_USER_ID = 11;
348     private static final int TEST_REAR_RIGHT_USER_ID = 12;
349     private static final int TEST_FRONT_PASSENGER_USER_ID = 13;
350     private static final int TEST_REAR_ROW_3_PASSENGER_USER_ID = 14;
351     private static final int TEST_GAIN_INDEX = 4;
352 
353     // TODO(b/273800524): create a utility test class for audio attributes.
354     private static final AudioAttributes ATTRIBUTES_UNKNOWN =
355             CarAudioContext.getAudioAttributeFromUsage(USAGE_UNKNOWN);
356     private static final AudioAttributes ATTRIBUTES_GAME =
357             CarAudioContext.getAudioAttributeFromUsage(USAGE_GAME);
358     private static final AudioAttributes ATTRIBUTES_MEDIA =
359             CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA);
360     private static final AudioAttributes ATTRIBUTES_NOTIFICATION =
361             CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION);
362     private static final AudioAttributes ATTRIBUTES_NOTIFICATION_EVENT =
363             CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_EVENT);
364     private static final AudioAttributes ATTRIBUTES_ANNOUNCEMENT =
365             CarAudioContext.getAudioAttributeFromUsage(USAGE_ANNOUNCEMENT);
366     private static final AudioAttributes ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE =
367             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
368     private static final AudioAttributes ATTRIBUTES_ASSISTANCE_ACCESSIBILITY =
369             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANCE_ACCESSIBILITY);
370     private static final AudioAttributes ATTRIBUTES_ASSISTANT =
371             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANT);
372     private static final AudioAttributes ATTRIBUTES_NOTIFICATION_RINGTONE =
373             CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_RINGTONE);
374     private static final AudioAttributes ATTRIBUTES_VOICE_COMMUNICATION =
375             CarAudioContext.getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION);
376     private static final AudioAttributes ATTRIBUTES_CALL_ASSISTANT =
377             CarAudioContext.getAudioAttributeFromUsage(USAGE_CALL_ASSISTANT);
378     private static final AudioAttributes ATTRIBUTES_VOICE_COMMUNICATION_SIGNALLING =
379             CarAudioContext.getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION_SIGNALLING);
380     private static final AudioAttributes ATTRIBUTES_ALARM =
381             CarAudioContext.getAudioAttributeFromUsage(USAGE_ALARM);
382     private static final AudioAttributes ATTRIBUTES_ASSISTANCE_SONIFICATION =
383             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANCE_SONIFICATION);
384     private static final AudioAttributes ATTRIBUTES_EMERGENCY =
385             CarAudioContext.getAudioAttributeFromUsage(USAGE_EMERGENCY);
386     private static final AudioAttributes ATTRIBUTES_SAFETY =
387             CarAudioContext.getAudioAttributeFromUsage(USAGE_SAFETY);
388     private static final AudioAttributes ATTRIBUTES_VEHICLE_STATUS =
389             CarAudioContext.getAudioAttributeFromUsage(USAGE_VEHICLE_STATUS);
390 
391     private static final List<AudioAttributes> TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0 = List.of(
392             ATTRIBUTES_UNKNOWN, ATTRIBUTES_GAME, ATTRIBUTES_MEDIA, ATTRIBUTES_NOTIFICATION,
393             ATTRIBUTES_NOTIFICATION_EVENT, ATTRIBUTES_ANNOUNCEMENT);
394 
395     private static final List<AudioAttributes> TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_1 = List.of(
396             ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE, ATTRIBUTES_ASSISTANCE_ACCESSIBILITY,
397             ATTRIBUTES_ASSISTANT);
398 
399     private static final List<AudioAttributes> TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_DEFAULT =
400             List.of(ATTRIBUTES_UNKNOWN, ATTRIBUTES_GAME, ATTRIBUTES_MEDIA,
401                     ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE, ATTRIBUTES_ASSISTANCE_ACCESSIBILITY,
402                     ATTRIBUTES_ASSISTANT, ATTRIBUTES_NOTIFICATION_RINGTONE,
403                     ATTRIBUTES_VOICE_COMMUNICATION, ATTRIBUTES_CALL_ASSISTANT,
404                     ATTRIBUTES_VOICE_COMMUNICATION_SIGNALLING, ATTRIBUTES_ALARM,
405                     ATTRIBUTES_NOTIFICATION, ATTRIBUTES_NOTIFICATION_EVENT,
406                     ATTRIBUTES_ASSISTANCE_SONIFICATION, ATTRIBUTES_EMERGENCY, ATTRIBUTES_SAFETY,
407                     ATTRIBUTES_VEHICLE_STATUS, ATTRIBUTES_ANNOUNCEMENT);
408 
409     private static final List<AudioAttributes> TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_0 = List.of(
410             ATTRIBUTES_UNKNOWN, ATTRIBUTES_GAME, ATTRIBUTES_MEDIA,
411             ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE, ATTRIBUTES_ASSISTANCE_ACCESSIBILITY,
412             ATTRIBUTES_ASSISTANT, ATTRIBUTES_NOTIFICATION, ATTRIBUTES_NOTIFICATION_EVENT,
413             ATTRIBUTES_ANNOUNCEMENT);
414 
415     private static final List<AudioAttributes> TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_1 = List.of(
416             ATTRIBUTES_NOTIFICATION_RINGTONE, ATTRIBUTES_VOICE_COMMUNICATION,
417             ATTRIBUTES_CALL_ASSISTANT, ATTRIBUTES_VOICE_COMMUNICATION_SIGNALLING, ATTRIBUTES_ALARM,
418             ATTRIBUTES_ASSISTANCE_SONIFICATION, ATTRIBUTES_EMERGENCY, ATTRIBUTES_SAFETY,
419             ATTRIBUTES_VEHICLE_STATUS);
420 
421     private static final AudioFocusInfo TEST_AUDIO_FOCUS_INFO =
422             new AudioFocusInfo(CarAudioContext
423                     .getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION), MEDIA_APP_UID,
424             MEDIA_CLIENT_ID, "com.android.car.audio",
425             AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, AUDIOFOCUS_NONE, /* flags= */ 0,
426             Build.VERSION.SDK_INT);
427 
428     private static final AudioFocusInfo TEST_REAR_RIGHT_AUDIO_FOCUS_INFO =
429             new AudioFocusInfo(CarAudioContext
430             .getAudioAttributeFromUsage(USAGE_MEDIA), TEST_REAR_RIGHT_UID,
431             MEDIA_CLIENT_ID, "com.android.car.audio",
432             AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, AUDIOFOCUS_NONE, /* flags= */ 0,
433             Build.VERSION.SDK_INT);
434 
435     private static final int AUDIO_SERVICE_POLICY_REGISTRATIONS = 3;
436     private static final int AUDIO_SERVICE_POLICY_REGISTRATIONS_WITH_FADE_MANAGER = 4;
437     private static final int AUDIO_SERVICE_CALLBACKS_REGISTRATION = 1;
438 
439     private HandlerThread mHandlerThread;
440     private Handler mHandler;
441 
442     @Mock
443     private Context mMockContext;
444     @Mock
445     private TelephonyManager mMockTelephonyManagerWithoutSubscriptionId;
446     @Mock
447     private TelephonyManager mMockTelephonyManager;
448     @Mock
449     private AudioManagerWrapper mAudioManager;
450     @Mock
451     private Resources mMockResources;
452     @Mock
453     private ContentResolver mMockContentResolver;
454     @Mock
455     private AttributionSource mMockAttributionSource;
456     @Mock
457     IBinder mBinder;
458     @Mock
459     IBinder mVolumeCallbackBinder;
460     @Mock
461     IAudioControl mAudioControl;
462     @Mock
463     private PackageManager mMockPackageManager;
464     @Mock
465     private CarOccupantZoneService mMockOccupantZoneService;
466     @Mock
467     private CarOemProxyService mMockCarOemProxyService;
468     @Mock
469     private IAudioService mMockAudioService;
470     @Mock
471     private Uri mNavSettingUri;
472     @Mock
473     private AudioControlWrapperAidl mAudioControlWrapperAidl;
474     @Mock
475     private CarVolumeCallbackHandler mCarVolumeCallbackHandler;
476     @Mock
477     private CarInputService mMockCarInputService;
478     @Mock
479     private CarPowerManagementService mMockPowerService;
480 
481     // Not used directly, but sets proper mockStatic() expectations on Settings
482     @SuppressWarnings("UnusedVariable")
483     private MockSettings mMockSettings;
484 
485     private boolean mPersistMasterMute = true;
486     private boolean mUseDynamicRouting = true;
487     private boolean mUseHalAudioDucking = true;
488     private boolean mUseCarVolumeGroupMuting = true;
489     private boolean mUseCarVolumeGroupEvents = true;
490     private boolean mUseMinMaxActivationVolume = true;
491     private boolean mEnableVolumeKeyEventsToDynamicDevices = false;
492 
493 
494     private TemporaryFile mTempCarAudioConfigFile;
495     private TemporaryFile mTempCarAudioFadeConfigFile;
496 
497     private Context mContext;
498     private AudioDeviceInfo mMicrophoneInputDevice;
499     private AudioDeviceInfo mFmTunerInputDevice;
500     private AudioDeviceInfo mMediaOutputDevice;
501     private AudioDeviceInfo mNotificationOutpuBus;
502     private AudioDeviceInfo mNavOutputDevice;
503     private AudioDeviceInfo mVoiceOutpuBus;
504     private AudioDeviceInfo mSecondaryConfig0Group0Device;
505     private AudioDeviceInfo mSecondaryConfig1Group0Device;
506     private AudioDeviceInfo mSecondaryConfig1Group1Device;
507 
508     private AudioDeviceInfo mBTAudioDeviceInfo;
509 
510     private CarVolumeGroupInfo mTestPrimaryZoneVolumeInfo0;
511     private CarVolumeGroupInfo mTestPrimaryZoneUmMutedVolueInfo0;
512     private CarVolumeGroupInfo mTestPrimaryZoneVolumeInfo1;
513     private CarVolumeGroupInfo mTestSecondaryConfig0VolumeGroup0Info;
514     private CarVolumeGroupInfo mTestSecondaryZoneConfig1VolumeInfo0;
515     private CarVolumeGroupInfo mTestSecondaryZoneConfig1VolumeInfo1;
516 
517     private CarVolumeGroupEvent mTestCarVolumeGroupEvent;
518     private CarVolumeGroupEvent mTestCarMuteGroupEvent;
519     private CarVolumeGroupEvent mTestCarZoneReconfigurationEvent;
520 
521     @Captor
522     private ArgumentCaptor<BroadcastReceiver> mVolumeReceiverCaptor;
523 
524     private int mRegistrationCount = 0;
525     private List<Integer> mAudioPolicyRegistrationStatus = new ArrayList<>();
526 
527     @Rule
528     public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
529 
CarAudioServiceUnitTest()530     public CarAudioServiceUnitTest() {
531         super(CarAudioService.TAG);
532     }
533 
534     @Override
onSessionBuilder(CustomMockitoSessionBuilder session)535     protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
536         mMockSettings = new MockSettings(session);
537         session
538                 .spyStatic(SubscriptionManager.class)
539                 .spyStatic(AudioManagerWrapper.class)
540                 .spyStatic(AudioManagerHelper.class)
541                 .spyStatic(AudioControlWrapperAidl.class)
542                 .spyStatic(CoreAudioHelper.class)
543                 .spyStatic(AudioControlFactory.class)
544                 .spyStatic(SystemProperties.class)
545                 .spyStatic(ServiceManager.class)
546                 .spyStatic(Car.class);
547     }
548 
549     @Before
setUp()550     public void setUp() throws Exception {
551         mHandlerThread = CarServiceUtils.getHandlerThread(CarAudioService.class.getSimpleName());
552         mHandler = new Handler(mHandlerThread.getLooper());
553         mContext = ApplicationProvider.getApplicationContext();
554 
555         mockCarGetPlatformVersion(UPSIDE_DOWN_CAKE_0);
556 
557         mockCoreAudioRoutingAndVolume();
558         mockGrantCarControlAudioSettingsPermission();
559 
560         setUpAudioControlHAL();
561         setUpService();
562 
563         when(Settings.Secure.getUriFor(
564                 CarSettings.Secure.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL))
565                 .thenReturn(mNavSettingUri);
566     }
567 
568     @After
tearDown()569     public void tearDown() throws Exception {
570         if (mTempCarAudioConfigFile != null) {
571             mTempCarAudioConfigFile.close();
572         }
573         if (mTempCarAudioFadeConfigFile != null) {
574             mTempCarAudioFadeConfigFile.close();
575         }
576         CarLocalServices.removeServiceForTest(CarOemProxyService.class);
577         CarLocalServices.removeServiceForTest(CarOccupantZoneService.class);
578         CarLocalServices.removeServiceForTest(CarPowerManagementService.class);
579     }
580 
setUpAudioControlHAL()581     private void setUpAudioControlHAL() {
582         when(mBinder.queryLocalInterface(anyString())).thenReturn(mAudioControl);
583         doReturn(mBinder).when(AudioControlWrapperAidl::getService);
584         when(mAudioControlWrapperAidl.supportsFeature(
585                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_DUCKING)).thenReturn(true);
586         when(mAudioControlWrapperAidl.supportsFeature(
587                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS)).thenReturn(true);
588         when(mAudioControlWrapperAidl.supportsFeature(
589                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)).thenReturn(true);
590         when(mAudioControlWrapperAidl.supportsFeature(
591                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING)).thenReturn(true);
592         when(mAudioControlWrapperAidl.supportsFeature(
593                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)).thenReturn(true);
594         doReturn(mAudioControlWrapperAidl)
595                 .when(AudioControlFactory::newAudioControl);
596     }
597 
setUpService()598     private void setUpService() throws Exception {
599         doReturn(0).when(() -> SubscriptionManager.getDefaultDataSubscriptionId());
600         when(mMockContext.getSystemService(TelephonyManager.class))
601                 .thenReturn(mMockTelephonyManagerWithoutSubscriptionId);
602         when(mMockTelephonyManagerWithoutSubscriptionId.createForSubscriptionId(anyInt()))
603                 .thenReturn(mMockTelephonyManager);
604 
605         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
606         when(mMockContext.getAttributionSource()).thenReturn(mMockAttributionSource);
607         doReturn(true)
608                 .when(() -> AudioManagerHelper
609                         .setAudioDeviceGain(any(), any(), anyInt(), anyBoolean()));
610         doReturn(true)
611                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, false));
612 
613         when(mMockOccupantZoneService.getUserForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
614                 .thenReturn(TEST_DRIVER_USER_ID);
615         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
616                 .thenReturn(PRIMARY_AUDIO_ZONE);
617         when(mMockOccupantZoneService.getOccupantZoneForUser(UserHandle.of(TEST_DRIVER_USER_ID)))
618                 .thenReturn(TEST_DRIVER_OCCUPANT);
619 
620         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
621                 .thenReturn(TEST_REAR_LEFT_USER_ID);
622         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
623                 .thenReturn(TEST_REAR_RIGHT_USER_ID);
624         when(mMockOccupantZoneService.getUserForOccupant(TEST_FRONT_OCCUPANT_ZONE_ID))
625                 .thenReturn(TEST_FRONT_PASSENGER_USER_ID);
626         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_ROW_3_OCCUPANT_ZONE_ID))
627                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_USER_ID);
628         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
629                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
630         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
631                 .thenReturn(TEST_REAR_RIGHT_ZONE_ID);
632         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_FRONT_OCCUPANT_ZONE_ID))
633                 .thenReturn(TEST_FRONT_PASSENGER_USER_ID);
634         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_REAR_ROW_3_ZONE_ID))
635                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_USER_ID);
636         when(mMockOccupantZoneService.getOccupantZoneForUser(
637                 UserHandle.of(TEST_REAR_RIGHT_USER_ID))).thenReturn(
638                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
639         when(mMockOccupantZoneService.getOccupantZoneForUser(
640                 UserHandle.of(TEST_FRONT_PASSENGER_USER_ID))).thenReturn(
641                 TEST_FRONT_PASSENGER_OCCUPANT);
642         when(mMockOccupantZoneService.getOccupantZoneForUser(
643                 UserHandle.of(TEST_REAR_LEFT_USER_ID))).thenReturn(
644                 TEST_REAR_LEFT_PASSENGER_OCCUPANT);
645         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_ROW_3_ZONE_ID))
646                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_OCCUPANT);
647         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_RIGHT_ZONE_ID))
648                 .thenReturn(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
649         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_FRONT_ZONE_ID))
650                 .thenReturn(TEST_FRONT_PASSENGER_OCCUPANT);
651         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_LEFT_ZONE_ID))
652                 .thenReturn(TEST_REAR_LEFT_PASSENGER_OCCUPANT);
653         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_ROW_3_ZONE_ID))
654                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_OCCUPANT);
655 
656         // Initially set occupant zone service at uninitialized
657         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(UserHandle.USER_SYSTEM);
658 
659         CarLocalServices.removeServiceForTest(CarOccupantZoneService.class);
660         CarLocalServices.addService(CarOccupantZoneService.class, mMockOccupantZoneService);
661         CarLocalServices.removeServiceForTest(CarInputService.class);
662         CarLocalServices.addService(CarInputService.class, mMockCarInputService);
663         CarLocalServices.removeServiceForTest(CarPowerManagementService.class);
664         CarLocalServices.addService(CarPowerManagementService.class, mMockPowerService);
665 
666         CarLocalServices.removeServiceForTest(CarOemProxyService.class);
667         CarLocalServices.addService(CarOemProxyService.class, mMockCarOemProxyService);
668 
669         setUpAudioManager();
670 
671         setUpResources();
672     }
673 
setUpAudioManager()674     private void setUpAudioManager() throws Exception {
675         AudioDeviceInfo[] outputDevices = generateOutputDeviceInfos();
676         AudioDeviceInfo[] inputDevices = generateInputDeviceInfos();
677         mTestPrimaryZoneVolumeInfo0 =
678                 new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_0,
679                         PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0).setMuted(true)
680                         .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
681                         .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
682                         .setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0)
683                         .setAudioDeviceAttributes(List.of(
684                                 new AudioDeviceAttributes(mNotificationOutpuBus),
685                                 new AudioDeviceAttributes(mMediaOutputDevice)))
686                         .setMinActivationVolumeGainIndex(0)
687                         .setMaxActivationVolumeGainIndex(MAX_GAIN / STEP_SIZE).build();
688         mTestPrimaryZoneUmMutedVolueInfo0 =
689                 new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_0,
690                         PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0).setMuted(false)
691                         .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
692                         .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
693                         .setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0)
694                         .setAudioDeviceAttributes(List.of(
695                                 new AudioDeviceAttributes(mNotificationOutpuBus),
696                                 new AudioDeviceAttributes(mMediaOutputDevice)))
697                         .setMinActivationVolumeGainIndex(0)
698                         .setMaxActivationVolumeGainIndex(MAX_GAIN / STEP_SIZE).build();
699         mTestPrimaryZoneVolumeInfo1 =
700                 new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_1,
701                         PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1).setMuted(true)
702                         .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
703                         .setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_1)
704                         .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
705                         .setAudioDeviceAttributes(List.of(
706                                 new AudioDeviceAttributes(mVoiceOutpuBus),
707                                 new AudioDeviceAttributes(mNavOutputDevice)))
708                         .setMinActivationVolumeGainIndex(0)
709                         .setMaxActivationVolumeGainIndex(MAX_GAIN / STEP_SIZE).build();
710         mTestSecondaryConfig0VolumeGroup0Info =
711                 new CarVolumeGroupInfo.Builder("config 0 group " + TEST_SECONDARY_ZONE_GROUP_0,
712                         TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0)
713                         .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
714                         .setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_DEFAULT)
715                         .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
716                         .setAudioDeviceAttributes(List.of(
717                                 new AudioDeviceAttributes(mSecondaryConfig0Group0Device)))
718                         .setMinActivationVolumeGainIndex(0)
719                         .setMaxActivationVolumeGainIndex(MAX_GAIN / STEP_SIZE).build();
720         mTestSecondaryZoneConfig1VolumeInfo0 =
721                 new CarVolumeGroupInfo.Builder("config 1 group " + TEST_SECONDARY_ZONE_GROUP_0,
722                         TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0)
723                         .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
724                         .setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_0)
725                         .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
726                         .setAudioDeviceAttributes(List.of(new AudioDeviceAttributes(
727                                 mSecondaryConfig1Group0Device)))
728                         .setMinActivationVolumeGainIndex(0)
729                         .setMaxActivationVolumeGainIndex(MAX_GAIN / STEP_SIZE).build();
730         mTestSecondaryZoneConfig1VolumeInfo1 =
731                 new CarVolumeGroupInfo.Builder("config 1 group " + TEST_SECONDARY_ZONE_GROUP_1,
732                         TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_1)
733                         .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
734                         .setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_1)
735                         .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
736                         .setAudioDeviceAttributes(List.of(new AudioDeviceAttributes(
737                                 mSecondaryConfig1Group1Device)))
738                         .setMinActivationVolumeGainIndex(0)
739                         .setMaxActivationVolumeGainIndex(MAX_GAIN / STEP_SIZE).build();
740         mTestCarVolumeGroupEvent =
741                 new CarVolumeGroupEvent.Builder(List.of(mTestPrimaryZoneUmMutedVolueInfo0),
742                         CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED,
743                         List.of(CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_UI)).build();
744         mTestCarMuteGroupEvent =
745                 new CarVolumeGroupEvent.Builder(List.of(mTestPrimaryZoneUmMutedVolueInfo0),
746                         CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED,
747                         List.of(CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_UI)).build();
748         mTestCarZoneReconfigurationEvent =
749                 new CarVolumeGroupEvent.Builder(List.of(mTestPrimaryZoneUmMutedVolueInfo0),
750                         CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED,
751                         List.of(CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_UI)).build();
752         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
753                 .thenReturn(outputDevices);
754         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
755                .thenReturn(inputDevices);
756 
757         when(mAudioManager.registerAudioPolicy(any())).thenAnswer(invocation -> {
758             AudioPolicy policy = (AudioPolicy) invocation.getArguments()[0];
759             policy.setRegistration(REGISTRATION_ID);
760 
761             // Only return an specific result if testing failures at different phases.
762             return mAudioPolicyRegistrationStatus.isEmpty()
763                     ? SUCCESS : mAudioPolicyRegistrationStatus.get(mRegistrationCount++);
764         });
765 
766         when(mAudioManager.isAudioServerRunning()).thenReturn(true);
767 
768         // Needed by audio policy when setting UID device affinity
769         IBinder mockBinder = mock(IBinder.class);
770         when(mockBinder.queryLocalInterface(any())).thenReturn(mMockAudioService);
771         doReturn(mockBinder).when(() -> ServiceManager.getService(Context.AUDIO_SERVICE));
772     }
773 
setUpResources()774     private void setUpResources() {
775         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
776         when(mMockContext.createContextAsUser(any(), anyInt())).thenReturn(mMockContext);
777         when(mMockContext.getResources()).thenReturn(mMockResources);
778         when(mMockResources.getBoolean(audioUseDynamicRouting)).thenReturn(mUseDynamicRouting);
779         when(mMockResources.getInteger(audioVolumeKeyEventTimeoutMs))
780                 .thenReturn(VOLUME_KEY_EVENT_TIMEOUT_MS);
781         when(mMockResources.getBoolean(audioUseHalDuckingSignals)).thenReturn(mUseHalAudioDucking);
782         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting))
783                 .thenReturn(mUseCarVolumeGroupMuting);
784         when(mMockResources.getBoolean(audioUseCarVolumeGroupEvent))
785                 .thenReturn(mUseCarVolumeGroupEvents);
786         when(mMockResources.getBoolean(audioUseMinMaxActivationVolume))
787                 .thenReturn(mUseMinMaxActivationVolume);
788         when(mMockResources.getInteger(audioVolumeAdjustmentContextsVersion))
789                 .thenReturn(AUDIO_CONTEXT_PRIORITY_LIST_VERSION_ONE);
790         when(mMockResources.getBoolean(audioPersistMasterMuteState)).thenReturn(mPersistMasterMute);
791         enableVolumeKeyEventsToDynamicDevices(mEnableVolumeKeyEventsToDynamicDevices);
792     }
793 
enableVolumeKeyEventsToDynamicDevices(boolean enableVolumeKeyEvents)794     private void enableVolumeKeyEventsToDynamicDevices(boolean enableVolumeKeyEvents) {
795         when(mMockResources.getBoolean(audioEnableVolumeKeyEventsToDynamicDevices))
796                 .thenReturn(enableVolumeKeyEvents);
797     }
798 
799     @Test
constructor_withValidContext()800     public void constructor_withValidContext() {
801         AudioManager manager = mock(AudioManager.class);
802         when(mMockContext.getSystemService(AudioManager.class)).thenReturn(manager);
803 
804         new CarAudioService(mMockContext);
805 
806         verify(mMockContext).getSystemService(AudioManager.class);
807         verify(mMockContext).getSystemService(TelephonyManager.class);
808     }
809 
810     @Test
constructor_withNullContext_fails()811     public void constructor_withNullContext_fails() {
812         NullPointerException thrown =
813                 assertThrows(NullPointerException.class, () -> new CarAudioService(null));
814 
815         expectWithMessage("Car Audio Service Construction Exception")
816                 .that(thrown).hasMessageThat().contains("Context");
817     }
818 
819     @Test
constructor_withNullContextAndNullPath_fails()820     public void constructor_withNullContextAndNullPath_fails() {
821         NullPointerException thrown =
822                 assertThrows(NullPointerException.class,
823                         () -> new CarAudioService(/* context= */null,
824                                 /* audioManagerWrapper= */ null,
825                                 /* audioConfigurationPath= */ null,
826                                 /* carVolumeCallbackHandler= */ null,
827                                 /* audioFadeConfigurationPath= */ null));
828 
829         expectWithMessage("Car Audio Service Construction")
830                 .that(thrown).hasMessageThat().contains("Context");
831     }
832 
833     @Test
constructor_withLegacyMode_enableFadeManagerConfiguration_fails()834     public void constructor_withLegacyMode_enableFadeManagerConfiguration_fails()
835             throws Exception {
836         mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
837         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_FADE_MANAGER_CONFIGURATION);
838         when(mMockResources.getBoolean(audioUseDynamicRouting)).thenReturn(false);
839         when(mMockResources.getBoolean(audioUseFadeManagerConfiguration)).thenReturn(true);
840 
841         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
842                 () -> setUpAudioServiceWithoutInit());
843 
844         expectWithMessage("Car audio service construction").that(thrown).hasMessageThat()
845                 .containsMatch("Fade manager configuration feature can not");
846     }
847 
848     @Test
init_withVolumeControlPolicyRegistrationError_fails()849     public void init_withVolumeControlPolicyRegistrationError_fails() throws Exception {
850         mAudioPolicyRegistrationStatus.add(ERROR);
851         CarAudioService service = setUpAudioServiceWithoutInit();
852 
853         IllegalStateException thrown =
854                 assertThrows(IllegalStateException.class, () -> service.init());
855 
856         expectWithMessage("Audio control policy registration exception").that(thrown)
857                 .hasMessageThat().containsMatch("car audio service's volume control audio policy");
858     }
859 
860     @Test
init_withRepeatedDynamicDevicesInConfig_fails()861     public void init_withRepeatedDynamicDevicesInConfig_fails() throws Exception {
862         setUpTempFileForAudioConfiguration(
863                 R.raw.car_audio_configuration_repeated_dynamic_devices_in_config);
864         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
865         CarAudioService service = setUpAudioServiceWithDynamicDevices(mTempCarAudioConfigFile,
866                 mTempCarAudioFadeConfigFile);
867 
868         RuntimeException thrown =
869                 assertThrows(RuntimeException.class, () -> service.init());
870 
871         expectWithMessage("Car audio zone config with multiple dynamic devices exception")
872                 .that(thrown).hasMessageThat()
873                 .containsMatch("Invalid zone configurations for zone");
874     }
875 
876     @Test
init_withFocusControlPolicyRegistrationError_fails()877     public void init_withFocusControlPolicyRegistrationError_fails() throws Exception {
878         mAudioPolicyRegistrationStatus.add(SUCCESS);
879         mAudioPolicyRegistrationStatus.add(ERROR);
880         CarAudioService service = setUpAudioServiceWithoutInit();
881 
882         IllegalStateException thrown =
883                 assertThrows(IllegalStateException.class, () -> service.init());
884 
885         expectWithMessage("Audio control policy registration exception").that(thrown)
886                 .hasMessageThat().containsMatch("car audio service's focus control audio policy");
887     }
888 
889     @Test
init_withAudioRoutingPolicyRegistrationError_fails()890     public void init_withAudioRoutingPolicyRegistrationError_fails() throws Exception {
891         mAudioPolicyRegistrationStatus.add(SUCCESS);
892         mAudioPolicyRegistrationStatus.add(SUCCESS);
893         mAudioPolicyRegistrationStatus.add(ERROR);
894         CarAudioService service = setUpAudioServiceWithoutInit();
895 
896         IllegalStateException thrown =
897                 assertThrows(IllegalStateException.class, () -> service.init());
898 
899         expectWithMessage("Audio routing policy registration exception").that(thrown)
900                 .hasMessageThat().containsMatch("Audio routing policy registration");
901     }
902 
903     @Test
init_withFadeManagerConfigPolicyRegistrationError_fails()904     public void init_withFadeManagerConfigPolicyRegistrationError_fails() throws Exception {
905         mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
906         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_FADE_MANAGER_CONFIGURATION);
907         when(mMockResources.getBoolean(audioUseFadeManagerConfiguration)).thenReturn(true);
908         mAudioPolicyRegistrationStatus.add(SUCCESS);
909         mAudioPolicyRegistrationStatus.add(SUCCESS);
910         mAudioPolicyRegistrationStatus.add(SUCCESS);
911         mAudioPolicyRegistrationStatus.add(ERROR);
912         CarAudioService service = setUpAudioServiceWithoutInit();
913 
914         IllegalStateException thrown =
915                 assertThrows(IllegalStateException.class, () -> service.init());
916 
917         expectWithMessage("Audio fade policy registration exception").that(thrown).hasMessageThat()
918                 .containsMatch("car audio service's fade configuration audio policy");
919     }
920 
921     @Test
init_initializesAudioServiceCallbacks()922     public void init_initializesAudioServiceCallbacks() throws Exception {
923         mSetFlagsRule.disableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
924         CarAudioService service = setUpAudioServiceWithoutInit();
925 
926         service.init();
927 
928         verify(mAudioManager).setAudioServerStateCallback(any(), any());
929         verify(mAudioManager, never()).registerAudioDeviceCallback(any(), any());
930     }
931 
932     @Test
init_initializesAudioServiceCallbacks_withDynamicDevices()933     public void init_initializesAudioServiceCallbacks_withDynamicDevices() throws Exception {
934         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
935         CarAudioService service = setUpAudioServiceWithDynamicDevices();
936 
937         service.init();
938 
939         verify(mAudioManager).setAudioServerStateCallback(any(), any());
940         verify(mAudioManager).registerAudioDeviceCallback(any(), any());
941     }
942 
943     @Test
init_withDynamicDevices()944     public void init_withDynamicDevices() throws Exception {
945         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
946         CarAudioService audioServiceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
947 
948         audioServiceWithDynamicDevices.init();
949 
950         List<CarAudioZoneConfigInfo> zoneConfigInfos =
951                 audioServiceWithDynamicDevices.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
952         List<String> names = zoneConfigInfos.stream().map(config -> config.getName()).toList();
953         expectWithMessage("Dynamic configuration names").that(names).containsExactly(
954                 DEFAULT_CONFIG_NAME_DYNAMIC_DEVICES, PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES,
955                 TERTIARY_CONFIG_NAME_DYNAMIC_DEVICES);
956         CarAudioZoneConfigInfo btConfig = zoneConfigInfos.stream()
957                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
958                 .findFirst().orElseThrow();
959         expectWithMessage("Bluetooth configuration by default active status")
960                 .that(btConfig.isActive()).isFalse();
961     }
962 
963     @Test
init_withAudioServerDown()964     public void init_withAudioServerDown() throws Exception {
965         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
966         when(mAudioManager.isAudioServerRunning()).thenReturn(false);
967         CarAudioService service = setUpAudioServiceWithDynamicDevices();
968 
969         service.init();
970 
971         verify(mAudioManager).setAudioServerStateCallback(any(), any());
972         verify(mAudioManager, never()).registerAudioDeviceCallback(any(), any());
973     }
974 
975     @Test
release_releasesAudioServiceCallbacks()976     public void release_releasesAudioServiceCallbacks() throws Exception {
977         mSetFlagsRule.disableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
978         CarAudioService service = setUpAudioService();
979 
980         service.release();
981 
982         verify(mAudioManager, never()).unregisterAudioDeviceCallback(any());
983         verify(mAudioManager).clearAudioServerStateCallback();
984         verify(mAudioControlWrapperAidl).clearModuleChangeCallback();
985     }
986 
987     @Test
release_releasesAudioServiceCallbacks_withDynamicDevices()988     public void release_releasesAudioServiceCallbacks_withDynamicDevices() throws Exception {
989         CarAudioService service = setUpAudioServiceWithDynamicDevices();
990         service.init();
991 
992         service.release();
993 
994         verify(mAudioManager).unregisterAudioDeviceCallback(any());
995         verify(mAudioManager).clearAudioServerStateCallback();
996         verify(mAudioControlWrapperAidl).clearModuleChangeCallback();
997     }
998 
999     @Test
release_withoutModuleChangeCallback()1000     public void release_withoutModuleChangeCallback() throws Exception {
1001         when(mAudioControlWrapperAidl.supportsFeature(
1002                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)).thenReturn(false);
1003         CarAudioService service = setUpAudioService();
1004 
1005         service.release();
1006 
1007         verify(mAudioControlWrapperAidl, never()).clearModuleChangeCallback();
1008     }
1009 
1010     @Test
getAudioZoneIds_withBaseConfiguration_returnAllTheZones()1011     public void getAudioZoneIds_withBaseConfiguration_returnAllTheZones() throws Exception {
1012         CarAudioService service = setUpAudioService();
1013 
1014         expectWithMessage("Car Audio Service Zones")
1015                 .that(service.getAudioZoneIds()).asList()
1016                 .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
1017                         TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID);
1018     }
1019 
1020     @Test
getVolumeGroupCount_onPrimaryZone_returnsAllGroups()1021     public void getVolumeGroupCount_onPrimaryZone_returnsAllGroups() throws Exception {
1022         CarAudioService service = setUpAudioService();
1023 
1024         expectWithMessage("Primary zone car volume group count")
1025                 .that(service.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
1026                 .isEqualTo(PRIMARY_ZONE_VOLUME_GROUP_COUNT);
1027     }
1028 
1029     @Test
getVolumeGroupCount_onPrimaryZone_withNonDynamicRouting_returnsAllGroups()1030     public void getVolumeGroupCount_onPrimaryZone_withNonDynamicRouting_returnsAllGroups()
1031             throws Exception {
1032         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1033 
1034         expectWithMessage("Non dynamic routing primary zone car volume group count")
1035                 .that(nonDynamicAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
1036                 .isEqualTo(CarAudioDynamicRouting.STREAM_TYPES.length);
1037     }
1038 
1039     @Test
getVolumeGroupIdForUsage_forMusicUsage()1040     public void getVolumeGroupIdForUsage_forMusicUsage() throws Exception {
1041         CarAudioService service = setUpAudioService();
1042 
1043         expectWithMessage("Primary zone's media car volume group id")
1044                 .that(service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA))
1045                 .isEqualTo(MEDIA_VOLUME_GROUP_ID);
1046     }
1047 
1048     @Test
getVolumeGroupIdForUsage_withNonDynamicRouting_forMusicUsage()1049     public void getVolumeGroupIdForUsage_withNonDynamicRouting_forMusicUsage() throws Exception {
1050         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1051 
1052         expectWithMessage("Non dynamic routing primary zone's media car volume group id")
1053                 .that(nonDynamicAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
1054                         USAGE_MEDIA)).isEqualTo(MEDIA_VOLUME_GROUP_ID);
1055     }
1056 
1057     @Test
getVolumeGroupIdForUsage_forNavigationUsage()1058     public void getVolumeGroupIdForUsage_forNavigationUsage() throws Exception {
1059         CarAudioService service = setUpAudioService();
1060 
1061         expectWithMessage("Primary zone's navigation car volume group id")
1062                 .that(service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
1063                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE))
1064                 .isEqualTo(NAVIGATION_VOLUME_GROUP_ID);
1065     }
1066 
1067     @Test
getVolumeGroupIdForUsage_withNonDynamicRouting_forNavigationUsage()1068     public void getVolumeGroupIdForUsage_withNonDynamicRouting_forNavigationUsage()
1069             throws Exception {
1070         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1071 
1072         expectWithMessage("Non dynamic routing primary zone's navigation car volume group id")
1073                 .that(nonDynamicAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
1074                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE))
1075                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
1076     }
1077 
1078     @Test
getVolumeGroupIdForUsage_forInvalidUsage_returnsInvalidGroupId()1079     public void getVolumeGroupIdForUsage_forInvalidUsage_returnsInvalidGroupId() throws Exception {
1080         CarAudioService service = setUpAudioService();
1081 
1082         expectWithMessage("Primary zone's invalid car volume group id")
1083                 .that(service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, INVALID_USAGE))
1084                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
1085     }
1086 
1087     @Test
1088     public void
getVolumeGroupIdForUsage_forInvalidUsage_withNonDynamicRouting_returnsInvalidGroupId()1089             getVolumeGroupIdForUsage_forInvalidUsage_withNonDynamicRouting_returnsInvalidGroupId()
1090             throws Exception {
1091         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1092 
1093         expectWithMessage("Non dynamic routing primary zone's invalid car volume group id")
1094                 .that(nonDynamicAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
1095                         INVALID_USAGE)).isEqualTo(INVALID_VOLUME_GROUP_ID);
1096     }
1097 
1098     @Test
getVolumeGroupIdForUsage_forUnknownUsage_returnsMediaGroupId()1099     public void getVolumeGroupIdForUsage_forUnknownUsage_returnsMediaGroupId() throws Exception {
1100         CarAudioService service = setUpAudioService();
1101 
1102         expectWithMessage("Primary zone's unknown car volume group id")
1103                 .that(service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, USAGE_UNKNOWN))
1104                 .isEqualTo(MEDIA_VOLUME_GROUP_ID);
1105     }
1106 
1107     @Test
getVolumeGroupIdForUsage_forVirtualUsage_returnsInvalidGroupId()1108     public void getVolumeGroupIdForUsage_forVirtualUsage_returnsInvalidGroupId() throws Exception {
1109         CarAudioService service = setUpAudioService();
1110 
1111         expectWithMessage("Primary zone's virtual car volume group id")
1112                 .that(service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
1113                         AudioManagerHelper.getUsageVirtualSource()))
1114                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
1115     }
1116 
1117     @Test
getVolumeGroupCount_onSecondaryZone_returnsAllGroups()1118     public void getVolumeGroupCount_onSecondaryZone_returnsAllGroups() throws Exception {
1119         CarAudioService service = setUpAudioService();
1120 
1121         expectWithMessage("Secondary Zone car volume group count")
1122                 .that(service.getVolumeGroupCount(TEST_REAR_LEFT_ZONE_ID))
1123                 .isEqualTo(SECONDARY_ZONE_VOLUME_GROUP_COUNT);
1124     }
1125 
1126     @Test
getUsagesForVolumeGroupId_forMusicContext()1127     public void getUsagesForVolumeGroupId_forMusicContext() throws Exception {
1128         CarAudioService service = setUpAudioService();
1129 
1130 
1131         expectWithMessage("Primary zone's music car volume group id usages")
1132                 .that(service.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE,
1133                         MEDIA_VOLUME_GROUP_ID)).asList()
1134                 .containsExactly(USAGE_UNKNOWN, USAGE_GAME, USAGE_MEDIA, USAGE_ANNOUNCEMENT,
1135                         USAGE_NOTIFICATION, USAGE_NOTIFICATION_EVENT);
1136     }
1137 
1138     @Test
getUsagesForVolumeGroupId_forSystemContext()1139     public void getUsagesForVolumeGroupId_forSystemContext() throws Exception {
1140         CarAudioService service = setUpAudioService();
1141         int systemVolumeGroup =
1142                 service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, USAGE_EMERGENCY);
1143 
1144         expectWithMessage("Primary zone's system car volume group id usages")
1145                 .that(service.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE,
1146                         systemVolumeGroup)).asList().containsExactly(USAGE_ALARM, USAGE_EMERGENCY,
1147                         USAGE_SAFETY, USAGE_VEHICLE_STATUS, USAGE_ASSISTANCE_SONIFICATION);
1148     }
1149 
1150     @Test
getUsagesForVolumeGroupId_onSecondaryZone_forSingleVolumeGroupId_returnAllUsages()1151     public void getUsagesForVolumeGroupId_onSecondaryZone_forSingleVolumeGroupId_returnAllUsages()
1152             throws Exception {
1153         CarAudioService service = setUpAudioService();
1154 
1155         expectWithMessage("Secondary Zone's car volume group id usages")
1156                 .that(service.getUsagesForVolumeGroupId(TEST_REAR_LEFT_ZONE_ID,
1157                         SECONDARY_ZONE_VOLUME_GROUP_ID))
1158                 .asList().containsExactly(USAGE_UNKNOWN, USAGE_MEDIA,
1159                         USAGE_VOICE_COMMUNICATION, USAGE_VOICE_COMMUNICATION_SIGNALLING,
1160                         USAGE_ALARM, USAGE_NOTIFICATION, USAGE_NOTIFICATION_RINGTONE,
1161                         USAGE_NOTIFICATION_EVENT, USAGE_ASSISTANCE_ACCESSIBILITY,
1162                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, USAGE_ASSISTANCE_SONIFICATION,
1163                         USAGE_GAME, USAGE_ASSISTANT, USAGE_CALL_ASSISTANT, USAGE_EMERGENCY,
1164                         USAGE_ANNOUNCEMENT, USAGE_SAFETY, USAGE_VEHICLE_STATUS);
1165     }
1166 
1167     @Test
getUsagesForVolumeGroupId_withoutDynamicRouting()1168     public void getUsagesForVolumeGroupId_withoutDynamicRouting() throws Exception {
1169         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1170 
1171         expectWithMessage("Media car volume group id without dynamic routing").that(
1172                 nonDynamicAudioService.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE,
1173                 MEDIA_VOLUME_GROUP_ID)).asList()
1174                 .containsExactly(CarAudioDynamicRouting.STREAM_TYPE_USAGES[MEDIA_VOLUME_GROUP_ID]);
1175     }
1176 
1177     @Test
createAudioPatch_onMediaOutputDevice_failsForConfigurationMissing()1178     public void createAudioPatch_onMediaOutputDevice_failsForConfigurationMissing()
1179             throws Exception {
1180         CarAudioService service = setUpAudioService();
1181 
1182         doReturn(false)
1183                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, false));
1184 
1185         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
1186                 service.createAudioPatch(PRIMARY_ZONE_FM_TUNER_ADDRESS, USAGE_MEDIA, DEFAULT_GAIN));
1187 
1188         expectWithMessage("FM and Media Audio Patch Exception")
1189                 .that(thrown).hasMessageThat().contains("Audio Patch APIs not enabled");
1190     }
1191 
1192     @Test
createAudioPatch_onMediaOutputDevice_failsForMissingPermission()1193     public void createAudioPatch_onMediaOutputDevice_failsForMissingPermission() throws Exception {
1194         CarAudioService service = setUpAudioService();
1195 
1196         mockDenyCarControlAudioSettingsPermission();
1197 
1198         SecurityException thrown = assertThrows(SecurityException.class,
1199                 () -> service
1200                         .createAudioPatch(PRIMARY_ZONE_FM_TUNER_ADDRESS,
1201                                 USAGE_MEDIA, DEFAULT_GAIN));
1202 
1203         expectWithMessage("FM and Media Audio Patch Permission Exception")
1204                 .that(thrown).hasMessageThat().contains(PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1205     }
1206 
1207     @Test
createAudioPatch_onMediaOutputDevice_succeeds()1208     public void createAudioPatch_onMediaOutputDevice_succeeds() throws Exception {
1209         CarAudioService service = setUpAudioService();
1210 
1211         mockGrantCarControlAudioSettingsPermission();
1212         doReturn(false)
1213                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, true));
1214         doReturn(new AudioPatchInfo(PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE, 0))
1215                 .when(() -> AudioManagerHelper
1216                         .createAudioPatch(mFmTunerInputDevice, mMediaOutputDevice, DEFAULT_GAIN));
1217 
1218         CarAudioPatchHandle audioPatch = service
1219                 .createAudioPatch(PRIMARY_ZONE_FM_TUNER_ADDRESS, USAGE_MEDIA, DEFAULT_GAIN);
1220 
1221         expectWithMessage("Audio Patch Sink Address")
1222                 .that(audioPatch.getSinkAddress()).isEqualTo(MEDIA_TEST_DEVICE);
1223         expectWithMessage("Audio Patch Source Address")
1224                 .that(audioPatch.getSourceAddress()).isEqualTo(PRIMARY_ZONE_FM_TUNER_ADDRESS);
1225         expectWithMessage("Audio Patch Handle")
1226                 .that(audioPatch.getHandleId()).isEqualTo(0);
1227     }
1228 
1229     @Test
releaseAudioPatch_failsForConfigurationMissing()1230     public void releaseAudioPatch_failsForConfigurationMissing() throws Exception {
1231         CarAudioService service = setUpAudioService();
1232 
1233         doReturn(false)
1234                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, false));
1235         CarAudioPatchHandle carAudioPatchHandle =
1236                 new CarAudioPatchHandle(0, PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE);
1237 
1238         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1239                 () -> service.releaseAudioPatch(carAudioPatchHandle));
1240 
1241         expectWithMessage("Release FM and Media Audio Patch Exception")
1242                 .that(thrown).hasMessageThat().contains("Audio Patch APIs not enabled");
1243     }
1244 
1245     @Test
releaseAudioPatch_failsForMissingPermission()1246     public void releaseAudioPatch_failsForMissingPermission() throws Exception {
1247         CarAudioService service = setUpAudioService();
1248 
1249         mockDenyCarControlAudioSettingsPermission();
1250         CarAudioPatchHandle carAudioPatchHandle =
1251                 new CarAudioPatchHandle(0, PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE);
1252 
1253         SecurityException thrown = assertThrows(SecurityException.class,
1254                 () -> service.releaseAudioPatch(carAudioPatchHandle));
1255 
1256         expectWithMessage("FM and Media Audio Patch Permission Exception")
1257                 .that(thrown).hasMessageThat().contains(PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1258     }
1259 
1260     @Test
releaseAudioPatch_forNullSourceAddress_throwsNullPointerException()1261     public void releaseAudioPatch_forNullSourceAddress_throwsNullPointerException()
1262             throws Exception {
1263         CarAudioService service = setUpAudioService();
1264         mockGrantCarControlAudioSettingsPermission();
1265         doReturn(new AudioPatchInfo(PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE, 0))
1266                 .when(() -> AudioManagerHelper
1267                         .createAudioPatch(mFmTunerInputDevice, mMediaOutputDevice, DEFAULT_GAIN));
1268 
1269         CarAudioPatchHandle audioPatch = mock(CarAudioPatchHandle.class);
1270         when(audioPatch.getSourceAddress()).thenReturn(null);
1271 
1272         NullPointerException thrown = assertThrows(NullPointerException.class,
1273                 () -> service.releaseAudioPatch(audioPatch));
1274 
1275         expectWithMessage("Release audio patch for null source address "
1276                 + "and sink address Null Exception")
1277                 .that(thrown).hasMessageThat()
1278                 .contains("Source Address can not be null for patch id 0");
1279     }
1280 
1281     @Test
releaseAudioPatch_failsForNullPatch()1282     public void releaseAudioPatch_failsForNullPatch() throws Exception {
1283         CarAudioService service = setUpAudioService();
1284 
1285         assertThrows(NullPointerException.class,
1286                 () -> service.releaseAudioPatch(null));
1287     }
1288 
1289     @Test
setZoneIdForUid_withoutRoutingPermission_fails()1290     public void setZoneIdForUid_withoutRoutingPermission_fails() throws Exception {
1291         CarAudioService service = setUpAudioService();
1292 
1293         mockDenyCarControlAudioSettingsPermission();
1294 
1295         SecurityException thrown = assertThrows(SecurityException.class,
1296                 () -> service.setZoneIdForUid(OUT_OF_RANGE_ZONE, MEDIA_APP_UID));
1297 
1298         expectWithMessage("Set Zone for UID Permission Exception")
1299                 .that(thrown).hasMessageThat()
1300                 .contains(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1301     }
1302 
1303     @Test
setZoneIdForUid_withoutDynamicRouting_fails()1304     public void setZoneIdForUid_withoutDynamicRouting_fails() throws Exception {
1305         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1306 
1307         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1308                 () -> nonDynamicAudioService.setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID));
1309 
1310         expectWithMessage("Set Zone for UID Dynamic Configuration Exception")
1311                 .that(thrown).hasMessageThat()
1312                 .contains("Non legacy routing is required");
1313     }
1314 
1315     @Test
setZoneIdForUid_withInvalidZone_fails()1316     public void setZoneIdForUid_withInvalidZone_fails() throws Exception {
1317         CarAudioService service = setUpAudioService();
1318 
1319         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
1320                 () -> service.setZoneIdForUid(INVALID_AUDIO_ZONE, MEDIA_APP_UID));
1321 
1322         expectWithMessage("Set Zone for UID Invalid Zone Exception")
1323                 .that(thrown).hasMessageThat()
1324                 .contains("Invalid audio zone Id " + INVALID_AUDIO_ZONE);
1325     }
1326 
1327     @Test
setZoneIdForUid_withOutOfRangeZone_fails()1328     public void setZoneIdForUid_withOutOfRangeZone_fails() throws Exception {
1329         CarAudioService service = setUpAudioService();
1330 
1331         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
1332                 () -> service.setZoneIdForUid(OUT_OF_RANGE_ZONE, MEDIA_APP_UID));
1333 
1334         expectWithMessage("Set Zone for UID Zone Out of Range Exception")
1335                 .that(thrown).hasMessageThat()
1336                 .contains("Invalid audio zone Id " + OUT_OF_RANGE_ZONE);
1337     }
1338 
1339     @Test
setZoneIdForUid_withZoneAudioMapping_fails()1340     public void setZoneIdForUid_withZoneAudioMapping_fails() throws Exception {
1341         CarAudioService service = setUpAudioService();
1342 
1343         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1344                 () -> service.setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID));
1345 
1346         expectWithMessage("Set Zone for UID With Audio Zone Mapping Exception")
1347                 .that(thrown).hasMessageThat()
1348                 .contains("UID based routing is not supported while using occupant zone mapping");
1349     }
1350 
1351     @Test
setZoneIdForUid_withValidZone_succeeds()1352     public void setZoneIdForUid_withValidZone_succeeds() throws Exception {
1353         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1354 
1355         boolean results = noZoneMappingAudioService
1356                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1357 
1358         expectWithMessage("Set Zone for UID Status").that(results).isTrue();
1359     }
1360 
1361     @Test
setZoneIdForUid_onDifferentZones_succeeds()1362     public void setZoneIdForUid_onDifferentZones_succeeds() throws Exception {
1363         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1364 
1365         noZoneMappingAudioService
1366                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1367 
1368         boolean results = noZoneMappingAudioService
1369                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1370 
1371         expectWithMessage("Set Zone for UID For Different Zone")
1372                 .that(results).isTrue();
1373     }
1374 
1375     @Test
setZoneIdForUid_onDifferentZones_withAudioFocus_succeeds()1376     public void setZoneIdForUid_onDifferentZones_withAudioFocus_succeeds() throws Exception {
1377         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1378         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia();
1379 
1380         noZoneMappingAudioService
1381                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1382 
1383         noZoneMappingAudioService
1384                 .requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
1385 
1386         boolean results = noZoneMappingAudioService
1387                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1388 
1389         expectWithMessage("Set Zone for UID For Different Zone with Audio Focus")
1390                 .that(results).isTrue();
1391     }
1392 
1393     @Test
getZoneIdForUid_withoutMappedUid_succeeds()1394     public void getZoneIdForUid_withoutMappedUid_succeeds() throws Exception {
1395         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1396 
1397         int zoneId = noZoneMappingAudioService
1398                 .getZoneIdForUid(MEDIA_APP_UID);
1399 
1400         expectWithMessage("Get Zone for Non Mapped UID")
1401                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1402     }
1403 
1404     @Test
getZoneIdForUid_succeeds()1405     public void getZoneIdForUid_succeeds() throws Exception {
1406         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1407 
1408         noZoneMappingAudioService
1409                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1410 
1411         int zoneId = noZoneMappingAudioService
1412                 .getZoneIdForUid(MEDIA_APP_UID);
1413 
1414         expectWithMessage("Get Zone for UID Zone Id")
1415                 .that(zoneId).isEqualTo(TEST_REAR_LEFT_ZONE_ID);
1416     }
1417 
1418     @Test
getZoneIdForUid_afterSwitchingZones_succeeds()1419     public void getZoneIdForUid_afterSwitchingZones_succeeds() throws Exception {
1420         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1421 
1422         noZoneMappingAudioService
1423                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1424 
1425         noZoneMappingAudioService
1426                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1427 
1428         int zoneId = noZoneMappingAudioService
1429                 .getZoneIdForUid(MEDIA_APP_UID);
1430 
1431         expectWithMessage("Get Zone for UID Zone Id")
1432                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1433     }
1434 
1435     @Test
clearZoneIdForUid_withoutRoutingPermission_fails()1436     public void clearZoneIdForUid_withoutRoutingPermission_fails() throws Exception {
1437         CarAudioService service = setUpAudioService();
1438 
1439         mockDenyCarControlAudioSettingsPermission();
1440 
1441         SecurityException thrown = assertThrows(SecurityException.class,
1442                 () -> service.clearZoneIdForUid(MEDIA_APP_UID));
1443 
1444         expectWithMessage("Clear Zone for UID Permission Exception")
1445                 .that(thrown).hasMessageThat()
1446                 .contains(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1447     }
1448 
1449     @Test
clearZoneIdForUid_withoutDynamicRouting_fails()1450     public void clearZoneIdForUid_withoutDynamicRouting_fails() throws Exception {
1451         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1452 
1453         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1454                 () -> nonDynamicAudioService.clearZoneIdForUid(MEDIA_APP_UID));
1455 
1456         expectWithMessage("Clear Zone for UID Dynamic Configuration Exception")
1457                 .that(thrown).hasMessageThat()
1458                 .contains("Non legacy routing is required");
1459     }
1460 
1461     @Test
clearZoneIdForUid_withZoneAudioMapping_fails()1462     public void clearZoneIdForUid_withZoneAudioMapping_fails() throws Exception {
1463         CarAudioService service = setUpAudioService();
1464 
1465         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1466                 () -> service.clearZoneIdForUid(MEDIA_APP_UID));
1467 
1468         expectWithMessage("Clear Zone for UID Audio Zone Mapping Exception")
1469                 .that(thrown).hasMessageThat()
1470                 .contains("UID based routing is not supported while using occupant zone mapping");
1471     }
1472 
1473     @Test
clearZoneIdForUid_forNonMappedUid_succeeds()1474     public void clearZoneIdForUid_forNonMappedUid_succeeds() throws Exception {
1475         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1476 
1477         boolean status = noZoneMappingAudioService
1478                 .clearZoneIdForUid(MEDIA_APP_UID);
1479 
1480         expectWithMessage("Clear Zone for UID Audio Zone without Mapping")
1481                 .that(status).isTrue();
1482     }
1483 
1484     @Test
clearZoneIdForUid_forMappedUid_succeeds()1485     public void clearZoneIdForUid_forMappedUid_succeeds() throws Exception {
1486         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1487 
1488         noZoneMappingAudioService
1489                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1490 
1491         boolean status = noZoneMappingAudioService.clearZoneIdForUid(MEDIA_APP_UID);
1492 
1493         expectWithMessage("Clear Zone for UID Audio Zone with Mapping")
1494                 .that(status).isTrue();
1495     }
1496 
1497     @Test
getZoneIdForUid_afterClearedUidMapping_returnsDefaultZone()1498     public void getZoneIdForUid_afterClearedUidMapping_returnsDefaultZone() throws Exception {
1499         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1500 
1501         noZoneMappingAudioService
1502                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1503 
1504         noZoneMappingAudioService.clearZoneIdForUid(MEDIA_APP_UID);
1505 
1506         int zoneId = noZoneMappingAudioService.getZoneIdForUid(MEDIA_APP_UID);
1507 
1508         expectWithMessage("Get Zone for UID Audio Zone with Cleared Mapping")
1509                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1510     }
1511 
1512     @Test
getZoneIdForAudioFocusInfo_withoutMappedUid_succeeds()1513     public void getZoneIdForAudioFocusInfo_withoutMappedUid_succeeds() throws Exception {
1514         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1515 
1516         int zoneId = noZoneMappingAudioService
1517                 .getZoneIdForAudioFocusInfo(TEST_AUDIO_FOCUS_INFO);
1518 
1519         expectWithMessage("Mapped audio focus info's zone")
1520                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1521     }
1522 
1523     @Test
getZoneIdForAudioFocusInfo_succeeds()1524     public void getZoneIdForAudioFocusInfo_succeeds() throws Exception {
1525         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1526 
1527         noZoneMappingAudioService
1528                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1529 
1530         int zoneId = noZoneMappingAudioService
1531                 .getZoneIdForAudioFocusInfo(TEST_AUDIO_FOCUS_INFO);
1532 
1533         expectWithMessage("Mapped audio focus info's zone")
1534                 .that(zoneId).isEqualTo(TEST_REAR_LEFT_ZONE_ID);
1535     }
1536 
1537     @Test
getZoneIdForAudioFocusInfo_afterSwitchingZones_succeeds()1538     public void getZoneIdForAudioFocusInfo_afterSwitchingZones_succeeds() throws Exception {
1539         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
1540         noZoneMappingAudioService
1541                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1542         noZoneMappingAudioService
1543                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1544 
1545         int zoneId = noZoneMappingAudioService
1546                 .getZoneIdForAudioFocusInfo(TEST_AUDIO_FOCUS_INFO);
1547 
1548         expectWithMessage("Remapped audio focus info's zone")
1549                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1550     }
1551 
1552     @Test
setGroupVolume_withoutPermission_fails()1553     public void setGroupVolume_withoutPermission_fails() throws Exception {
1554         CarAudioService service = setUpAudioService();
1555 
1556         mockDenyCarControlAudioVolumePermission();
1557 
1558         SecurityException thrown = assertThrows(SecurityException.class,
1559                 () -> service.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1560                         TEST_GAIN_INDEX, TEST_FLAGS));
1561 
1562         expectWithMessage("Set Volume Group Permission Exception")
1563                 .that(thrown).hasMessageThat()
1564                 .contains(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME);
1565     }
1566 
1567     @Test
setGroupVolume_withDynamicRoutingDisabled()1568     public void setGroupVolume_withDynamicRoutingDisabled() throws Exception {
1569         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1570 
1571         nonDynamicAudioService.setGroupVolume(
1572                 PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0, TEST_GAIN_INDEX, TEST_FLAGS);
1573 
1574         verify(mAudioManager).setStreamVolume(
1575                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0],
1576                 TEST_GAIN_INDEX,
1577                 TEST_FLAGS);
1578     }
1579 
1580     @Test
setGroupVolume_verifyNoCallbacks()1581     public void setGroupVolume_verifyNoCallbacks() throws Exception {
1582         CarAudioService service = setUpAudioService();
1583         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1584                 /* mute= */ false, TEST_FLAGS);
1585         reset(mCarVolumeCallbackHandler);
1586 
1587         service.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1588                 TEST_GAIN_INDEX, TEST_FLAGS);
1589 
1590         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
1591     }
1592 
1593     @Test
setGroupVolume_afterSetVolumeGroupMute()1594     public void setGroupVolume_afterSetVolumeGroupMute() throws Exception {
1595         CarAudioService service = setUpAudioService();
1596         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1597                 /* mute= */ true, TEST_FLAGS);
1598         reset(mCarVolumeCallbackHandler);
1599 
1600         service.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1601                 TEST_GAIN_INDEX, TEST_FLAGS);
1602 
1603         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
1604                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
1605     }
1606 
1607     @Test
setGroupVolume_withVolumeGroupMutingDisabled_doesnotThrowException()1608     public void setGroupVolume_withVolumeGroupMutingDisabled_doesnotThrowException()
1609             throws Exception {
1610         CarAudioService nonVolumeGroupMutingAudioService =
1611                 setUpAudioServiceWithDisabledResource(audioUseCarVolumeGroupMuting);
1612         HalAudioGainCallback callback = getHalAudioGainCallback();
1613         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(PRIMARY_AUDIO_ZONE,
1614                 MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
1615         callback.onAudioDeviceGainsChanged(List.of(Reasons.TCU_MUTE), List.of(carGain));
1616         reset(mCarVolumeCallbackHandler);
1617 
1618         nonVolumeGroupMutingAudioService.setGroupVolume(
1619                 PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0, TEST_GAIN_INDEX, TEST_FLAGS);
1620 
1621         // if an exception is thrown, the test automatically fails
1622         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
1623                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
1624         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
1625     }
1626 
1627     @Test
getOutputDeviceAddressForUsage_forMusicUsage()1628     public void getOutputDeviceAddressForUsage_forMusicUsage() throws Exception {
1629         CarAudioService service = setUpAudioService();
1630 
1631         String mediaDeviceAddress =
1632                 service.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA);
1633 
1634         expectWithMessage("Media usage audio device address")
1635                 .that(mediaDeviceAddress).isEqualTo(MEDIA_TEST_DEVICE);
1636     }
1637 
1638     @Test
getOutputDeviceAddressForUsage_withNonDynamicRouting_forMediaUsage_fails()1639     public void getOutputDeviceAddressForUsage_withNonDynamicRouting_forMediaUsage_fails()
1640             throws Exception {
1641         when(mMockResources.getBoolean(audioUseCoreRouting)).thenReturn(false);
1642         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1643 
1644         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1645                 () -> nonDynamicAudioService
1646                         .getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA));
1647 
1648         expectWithMessage("Non dynamic routing media usage audio device address exception")
1649                 .that(thrown).hasMessageThat().contains("Non legacy routing is required");
1650     }
1651 
1652     @Test
getOutputDeviceAddressForUsage_forNavigationUsage()1653     public void getOutputDeviceAddressForUsage_forNavigationUsage() throws Exception {
1654         CarAudioService service = setUpAudioService();
1655 
1656         String mediaDeviceAddress =
1657                 service.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1658                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
1659 
1660         expectWithMessage("Navigation usage audio device address")
1661                 .that(mediaDeviceAddress).isEqualTo(NAVIGATION_TEST_DEVICE);
1662     }
1663 
1664     @Test
getOutputDeviceAddressForUsage_forInvalidUsage_fails()1665     public void getOutputDeviceAddressForUsage_forInvalidUsage_fails() throws Exception {
1666         CarAudioService service = setUpAudioService();
1667 
1668         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
1669                 service.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1670                         INVALID_USAGE));
1671 
1672         expectWithMessage("Invalid usage audio device address exception")
1673                 .that(thrown).hasMessageThat().contains("Invalid audio attribute " + INVALID_USAGE);
1674     }
1675 
1676     @Test
getOutputDeviceAddressForUsage_forVirtualUsage_fails()1677     public void getOutputDeviceAddressForUsage_forVirtualUsage_fails() throws Exception {
1678         CarAudioService service = setUpAudioService();
1679 
1680         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
1681                 service.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1682                         AudioManagerHelper.getUsageVirtualSource()));
1683 
1684         expectWithMessage("Invalid context audio device address exception")
1685                 .that(thrown).hasMessageThat()
1686                 .contains("invalid");
1687     }
1688 
1689     @Test
getOutputDeviceAddressForUsage_onSecondaryZone_forMusicUsage()1690     public void getOutputDeviceAddressForUsage_onSecondaryZone_forMusicUsage() throws Exception {
1691         CarAudioService service = setUpAudioService();
1692 
1693         String mediaDeviceAddress = service.getOutputDeviceAddressForUsage(
1694                 TEST_REAR_LEFT_ZONE_ID, USAGE_MEDIA);
1695 
1696         expectWithMessage("Media usage audio device address for secondary zone")
1697                 .that(mediaDeviceAddress).isEqualTo(SECONDARY_TEST_DEVICE_CONFIG_0);
1698     }
1699 
1700     @Test
getSuggestedAudioContextForZone_inPrimaryZone()1701     public void getSuggestedAudioContextForZone_inPrimaryZone() throws Exception {
1702         CarAudioService service = setUpAudioService();
1703         int defaultAudioContext = service.getCarAudioContext()
1704                 .getContextForAudioAttribute(CAR_DEFAULT_AUDIO_ATTRIBUTE);
1705 
1706         expectWithMessage("Suggested audio context for primary zone")
1707                 .that(service.getSuggestedAudioContextForZone(PRIMARY_AUDIO_ZONE))
1708                 .isEqualTo(defaultAudioContext);
1709     }
1710 
1711     @Test
getSuggestedAudioContextForZone_inSecondaryZone()1712     public void getSuggestedAudioContextForZone_inSecondaryZone() throws Exception {
1713         CarAudioService service = setUpAudioService();
1714         int defaultAudioContext = service.getCarAudioContext()
1715                 .getContextForAudioAttribute(CAR_DEFAULT_AUDIO_ATTRIBUTE);
1716 
1717         expectWithMessage("Suggested audio context for secondary zone")
1718                 .that(service.getSuggestedAudioContextForZone(TEST_REAR_LEFT_ZONE_ID))
1719                 .isEqualTo(defaultAudioContext);
1720     }
1721 
1722     @Test
getSuggestedAudioContextForZone_inInvalidZone()1723     public void getSuggestedAudioContextForZone_inInvalidZone() throws Exception {
1724         CarAudioService service = setUpAudioService();
1725 
1726         expectWithMessage("Suggested audio context for invalid zone")
1727                 .that(service.getSuggestedAudioContextForZone(INVALID_AUDIO_ZONE))
1728                 .isEqualTo(CarAudioContext.getInvalidContext());
1729     }
1730 
1731     @Test
isVolumeGroupMuted_noSetVolumeGroupMute()1732     public void isVolumeGroupMuted_noSetVolumeGroupMute() throws Exception {
1733         CarAudioService service = setUpAudioService();
1734 
1735         expectWithMessage("Volume group mute for default state")
1736                 .that(service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
1737                         TEST_PRIMARY_ZONE_GROUP_0)).isFalse();
1738     }
1739 
1740     @Test
isVolumeGroupMuted_setVolumeGroupMuted_isFalse()1741     public void isVolumeGroupMuted_setVolumeGroupMuted_isFalse() throws Exception {
1742         CarAudioService service = setUpAudioService();
1743         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1744                 /* mute= */ true, TEST_FLAGS);
1745 
1746         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1747                 /* mute= */ false, TEST_FLAGS);
1748 
1749         expectWithMessage("Volume group muted after mute and unmute")
1750                 .that(service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
1751                         TEST_PRIMARY_ZONE_GROUP_0)).isFalse();
1752     }
1753 
1754     @Test
isVolumeGroupMuted_setVolumeGroupMuted_isTrue()1755     public void isVolumeGroupMuted_setVolumeGroupMuted_isTrue() throws Exception {
1756         CarAudioService service = setUpAudioService();
1757 
1758         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1759                 /* mute= */ true, TEST_FLAGS);
1760         expectWithMessage("Volume group muted after mute")
1761                 .that(service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
1762                         TEST_PRIMARY_ZONE_GROUP_0)).isTrue();
1763     }
1764 
1765     @Test
isVolumeGroupMuted_withVolumeGroupMutingDisabled()1766     public void isVolumeGroupMuted_withVolumeGroupMutingDisabled() throws Exception {
1767         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting)).thenReturn(false);
1768         CarAudioService nonVolumeGroupMutingAudioService =
1769                 setUpAudioServiceWithDisabledResource(audioUseCarVolumeGroupMuting);
1770 
1771         expectWithMessage("Volume group for disabled volume group muting")
1772                 .that(nonVolumeGroupMutingAudioService.isVolumeGroupMuted(
1773                         PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
1774                 .isFalse();
1775     }
1776 
1777     @Test
getGroupMaxVolume_forPrimaryZone()1778     public void getGroupMaxVolume_forPrimaryZone() throws Exception {
1779         CarAudioService service = setUpAudioService();
1780 
1781         expectWithMessage("Group max volume for primary audio zone and group")
1782                 .that(service.getGroupMaxVolume(PRIMARY_AUDIO_ZONE,
1783                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo((MAX_GAIN - MIN_GAIN) / STEP_SIZE);
1784     }
1785 
1786     @Test
getGroupMinVolume_forPrimaryZone()1787     public void getGroupMinVolume_forPrimaryZone() throws Exception {
1788         CarAudioService service = setUpAudioService();
1789 
1790         expectWithMessage("Group Min Volume for primary audio zone and group")
1791                 .that(service.getGroupMinVolume(PRIMARY_AUDIO_ZONE,
1792                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(0);
1793     }
1794 
1795     @Test
getGroupCurrentVolume_forPrimaryZone()1796     public void getGroupCurrentVolume_forPrimaryZone() throws Exception {
1797         CarAudioService service = setUpAudioService();
1798 
1799         expectWithMessage("Current group volume for primary audio zone and group")
1800                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
1801                         TEST_PRIMARY_ZONE_GROUP_0))
1802                 .isEqualTo((DEFAULT_GAIN - MIN_GAIN) / STEP_SIZE);
1803     }
1804 
1805     @Test
getGroupMaxVolume_withNoDynamicRouting()1806     public void getGroupMaxVolume_withNoDynamicRouting() throws Exception {
1807         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1808 
1809         nonDynamicAudioService.getGroupMaxVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
1810 
1811         verify(mAudioManager).getStreamMaxVolume(
1812                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0]);
1813     }
1814 
1815     @Test
getGroupMinVolume_withNoDynamicRouting()1816     public void getGroupMinVolume_withNoDynamicRouting() throws Exception {
1817         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1818 
1819         nonDynamicAudioService.getGroupMinVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
1820 
1821         verify(mAudioManager).getStreamMinVolume(
1822                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0]);
1823     }
1824 
1825     @Test
getGroupCurrentVolume_withNoDynamicRouting()1826     public void getGroupCurrentVolume_withNoDynamicRouting() throws Exception {
1827         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1828 
1829         nonDynamicAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
1830 
1831         verify(mAudioManager).getStreamVolume(
1832                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0]);
1833     }
1834 
1835     @Test
setBalanceTowardRight_nonNullValue()1836     public void setBalanceTowardRight_nonNullValue() throws Exception {
1837         CarAudioService service = setUpAudioService();
1838 
1839         service.setBalanceTowardRight(TEST_VALUE);
1840 
1841         verify(mAudioControlWrapperAidl).setBalanceTowardRight(TEST_VALUE);
1842     }
1843 
1844     @Test
setBalanceTowardRight_throws()1845     public void setBalanceTowardRight_throws() throws Exception {
1846         CarAudioService service = setUpAudioService();
1847 
1848         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, ()
1849                 -> service.setBalanceTowardRight(INVALID_TEST_VALUE));
1850 
1851         expectWithMessage("Out of bounds balance")
1852                 .that(thrown).hasMessageThat()
1853                 .contains(String.format("Balance is out of range of [%f, %f]", -1f, 1f));
1854     }
1855 
1856     @Test
setFadeTowardFront_nonNullValue()1857     public void setFadeTowardFront_nonNullValue() throws Exception {
1858         CarAudioService service = setUpAudioService();
1859 
1860         service.setFadeTowardFront(TEST_VALUE);
1861 
1862         verify(mAudioControlWrapperAidl).setFadeTowardFront(TEST_VALUE);
1863     }
1864 
1865     @Test
setFadeTowardFront_throws()1866     public void setFadeTowardFront_throws() throws Exception {
1867         CarAudioService service = setUpAudioService();
1868 
1869         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, ()
1870                 -> service.setFadeTowardFront(INVALID_TEST_VALUE));
1871 
1872         expectWithMessage("Out of bounds fade")
1873                 .that(thrown).hasMessageThat()
1874                 .contains(String.format("Fade is out of range of [%f, %f]", -1f, 1f));
1875     }
1876 
1877     @Test
isAudioFeatureEnabled_forDynamicRouting()1878     public void isAudioFeatureEnabled_forDynamicRouting() throws Exception {
1879         CarAudioService service = setUpAudioService();
1880 
1881         expectWithMessage("Dynamic routing audio feature")
1882                 .that(service.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING))
1883                 .isEqualTo(mUseDynamicRouting);
1884     }
1885 
1886     @Test
isAudioFeatureEnabled_forDisabledDynamicRouting()1887     public void isAudioFeatureEnabled_forDisabledDynamicRouting() throws Exception {
1888         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
1889 
1890         expectWithMessage("Disabled dynamic routing audio feature")
1891                 .that(nonDynamicAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING))
1892                 .isFalse();
1893     }
1894 
1895     @Test
isAudioFeatureEnabled_forVolumeGroupMuting()1896     public void isAudioFeatureEnabled_forVolumeGroupMuting() throws Exception {
1897         CarAudioService service = setUpAudioService();
1898 
1899         expectWithMessage("Group muting audio feature")
1900                 .that(service.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING))
1901                 .isEqualTo(mUseCarVolumeGroupMuting);
1902     }
1903 
1904     @Test
isAudioFeatureEnabled_forDisabledVolumeGroupMuting()1905     public void isAudioFeatureEnabled_forDisabledVolumeGroupMuting() throws Exception {
1906         CarAudioService nonVolumeGroupMutingAudioService =
1907                 setUpAudioServiceWithDisabledResource(audioUseCarVolumeGroupMuting);
1908 
1909         expectWithMessage("Disabled group muting audio feature")
1910                 .that(nonVolumeGroupMutingAudioService
1911                         .isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING))
1912                 .isFalse();
1913     }
1914 
1915     @Test
isAudioFeatureEnabled_forVolumeGroupEvent()1916     public void isAudioFeatureEnabled_forVolumeGroupEvent() throws Exception {
1917         CarAudioService service = setUpAudioService();
1918 
1919         expectWithMessage("Group events audio feature")
1920                 .that(service.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS))
1921                 .isEqualTo(mUseCarVolumeGroupEvents);
1922     }
1923 
1924     @Test
isAudioFeatureEnabled_forDisabledVolumeGroupEvent()1925     public void isAudioFeatureEnabled_forDisabledVolumeGroupEvent() throws Exception {
1926         CarAudioService nonVolumeGroupEventsAudioService =
1927                 setUpAudioServiceWithDisabledResource(audioUseCarVolumeGroupEvent);
1928 
1929         expectWithMessage("Disabled group event audio feature")
1930                 .that(nonVolumeGroupEventsAudioService
1931                         .isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS))
1932                 .isFalse();
1933     }
1934 
1935     @Test
isAudioFeatureEnabled_forUnrecognizableAudioFeature_throws()1936     public void isAudioFeatureEnabled_forUnrecognizableAudioFeature_throws() throws Exception {
1937         CarAudioService service = setUpAudioService();
1938 
1939         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
1940                 () -> service.isAudioFeatureEnabled(INVALID_AUDIO_FEATURE));
1941 
1942         expectWithMessage("Unknown audio feature")
1943                 .that(thrown).hasMessageThat()
1944                 .contains("Unknown Audio Feature type: " + INVALID_AUDIO_FEATURE);
1945     }
1946 
1947     @Test
isAudioFeatureEnabled_forDisabledOemService()1948     public void isAudioFeatureEnabled_forDisabledOemService() throws Exception {
1949         CarAudioService service = setUpAudioService();
1950 
1951         boolean isEnabled =
1952                 service.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1953 
1954         expectWithMessage("Oem service enabled with disabled oem service")
1955                 .that(isEnabled).isFalse();
1956     }
1957 
1958     @Test
isAudioFeatureEnabled_withEnabledFocusService()1959     public void isAudioFeatureEnabled_withEnabledFocusService() throws Exception {
1960         CarOemAudioFocusProxyService focusProxyService = mock(CarOemAudioFocusProxyService.class);
1961         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1962         when(mMockCarOemProxyService.getCarOemAudioFocusService()).thenReturn(focusProxyService);
1963         CarAudioService service = setUpAudioService();
1964 
1965         boolean isEnabled =
1966                 service.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1967 
1968         expectWithMessage("Oem service enabled with enabled focus service")
1969                 .that(isEnabled).isTrue();
1970     }
1971 
1972     @Test
isAudioFeatureEnabled_withEnabledVolumeService()1973     public void isAudioFeatureEnabled_withEnabledVolumeService() throws Exception {
1974         CarOemAudioVolumeProxyService volumeProxyService =
1975                 mock(CarOemAudioVolumeProxyService.class);
1976         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1977         when(mMockCarOemProxyService.getCarOemAudioVolumeService()).thenReturn(volumeProxyService);
1978         CarAudioService service = setUpAudioService();
1979 
1980         boolean isEnabled =
1981                 service.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1982 
1983         expectWithMessage("Oem service enabled with enabled volume service")
1984                 .that(isEnabled).isTrue();
1985     }
1986 
1987     @Test
isAudioFeatureEnabled_withEnabledDuckingService()1988     public void isAudioFeatureEnabled_withEnabledDuckingService() throws Exception {
1989         CarOemAudioDuckingProxyService duckingProxyService =
1990                 mock(CarOemAudioDuckingProxyService.class);
1991         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1992         when(mMockCarOemProxyService.getCarOemAudioDuckingService())
1993                 .thenReturn(duckingProxyService);
1994         CarAudioService service = setUpAudioService();
1995 
1996         boolean isEnabled = service.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1997 
1998         expectWithMessage("Oem service enabled with enabled ducking service")
1999                 .that(isEnabled).isTrue();
2000     }
2001 
2002     @Test
isAudioFeatureEnabled_withEnabledAudioMirror()2003     public void isAudioFeatureEnabled_withEnabledAudioMirror() throws Exception {
2004         CarAudioService service = setUpAudioService();
2005 
2006         boolean isEnabled = service.isAudioFeatureEnabled(AUDIO_FEATURE_AUDIO_MIRRORING);
2007 
2008         expectWithMessage("Audio mirror enabled status")
2009                 .that(isEnabled).isTrue();
2010     }
2011 
2012     @Test
isAudioFeatureEnabled_withDisabledAudioMirror()2013     public void isAudioFeatureEnabled_withDisabledAudioMirror() throws Exception {
2014         CarAudioService service = setUpCarAudioServiceWithoutMirroring();
2015 
2016         boolean isEnabled = service.isAudioFeatureEnabled(AUDIO_FEATURE_AUDIO_MIRRORING);
2017 
2018         expectWithMessage("Audio mirror enabled status")
2019                 .that(isEnabled).isFalse();
2020     }
2021 
2022     @Test
isAudioFeatureEnabled_forMinMaxActivationVolume()2023     public void isAudioFeatureEnabled_forMinMaxActivationVolume() throws Exception {
2024         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
2025         CarAudioService service = setUpAudioService();
2026 
2027         expectWithMessage("Min/max activation volume feature")
2028                 .that(service.isAudioFeatureEnabled(AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME))
2029                 .isEqualTo(mUseMinMaxActivationVolume);
2030     }
2031 
2032     @Test
isAudioFeatureEnabled_forDisabledMinMaxActivationVolume()2033     public void isAudioFeatureEnabled_forDisabledMinMaxActivationVolume() throws Exception {
2034         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
2035         CarAudioService nonMinMaxActivationVolumeAudioService =
2036                 setUpAudioServiceWithDisabledResource(audioUseMinMaxActivationVolume);
2037 
2038         expectWithMessage("Disabled min/max activation volume feature")
2039                 .that(nonMinMaxActivationVolumeAudioService
2040                         .isAudioFeatureEnabled(AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME))
2041                 .isFalse();
2042     }
2043 
2044     @Test
isAudioFeatureEnabled_forMinMaxActivationVolumeWithDisabledFlag()2045     public void isAudioFeatureEnabled_forMinMaxActivationVolumeWithDisabledFlag() throws Exception {
2046         mSetFlagsRule.disableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
2047         CarAudioService service = setUpAudioService();
2048 
2049         expectWithMessage("Min/max activation volume feature with disabled feature flag")
2050                 .that(service.isAudioFeatureEnabled(AUDIO_FEATURE_MIN_MAX_ACTIVATION_VOLUME))
2051                 .isFalse();
2052     }
2053 
2054     @Test
onOccupantZoneConfigChanged_noUserAssignedToPrimaryZone()2055     public void onOccupantZoneConfigChanged_noUserAssignedToPrimaryZone() throws Exception {
2056         CarAudioService service = setUpAudioService();
2057         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(UserManagerHelper.USER_NULL);
2058         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
2059                 .thenReturn(UserManagerHelper.USER_NULL);
2060         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2061         int prevUserId = service.getUserIdForZone(PRIMARY_AUDIO_ZONE);
2062 
2063         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2064 
2065         expectWithMessage("User ID before config changed")
2066                 .that(service.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2067                 .isEqualTo(prevUserId);
2068     }
2069 
2070     @Test
onOccupantZoneConfigChanged_userAssignedToPrimaryZone()2071     public void onOccupantZoneConfigChanged_userAssignedToPrimaryZone() throws Exception {
2072         CarAudioService service = setUpAudioService();
2073         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2074         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
2075                 .thenReturn(TEST_REAR_LEFT_USER_ID);
2076         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2077 
2078         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2079 
2080         expectWithMessage("User ID after config changed")
2081                 .that(service.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2082                 .isEqualTo(TEST_REAR_LEFT_USER_ID);
2083     }
2084 
2085     @Test
onOccupantZoneConfigChanged_afterResettingUser_returnNoUser()2086     public void onOccupantZoneConfigChanged_afterResettingUser_returnNoUser() throws Exception {
2087         CarAudioService service = setUpAudioService();
2088         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2089         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
2090                 .thenReturn(TEST_REAR_LEFT_USER_ID);
2091         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2092         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2093         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
2094                 .thenReturn(UserManagerHelper.USER_NULL);
2095 
2096         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2097 
2098         expectWithMessage("User ID config changed to null")
2099                 .that(service.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2100                 .isEqualTo(UserManagerHelper.USER_NULL);
2101     }
2102 
2103     @Test
onOccupantZoneConfigChanged_noOccupantZoneMapping()2104     public void onOccupantZoneConfigChanged_noOccupantZoneMapping() throws Exception {
2105         setUpCarAudioServiceWithoutZoneMapping();
2106         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2107 
2108         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2109 
2110         verify(mMockOccupantZoneService, never()).getUserForOccupant(anyInt());
2111     }
2112 
2113     @Test
onOccupantZoneConfigChanged_noOccupantZoneMapping_alreadyAssigned()2114     public void onOccupantZoneConfigChanged_noOccupantZoneMapping_alreadyAssigned()
2115             throws Exception {
2116         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2117         CarAudioService noZoneMappingAudioService = setUpCarAudioServiceWithoutZoneMapping();
2118         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2119         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2120         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2121 
2122         verify(mMockOccupantZoneService, never()).getUserForOccupant(anyInt());
2123         expectWithMessage("Occupant Zone for primary zone")
2124                 .that(noZoneMappingAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2125                 .isEqualTo(TEST_DRIVER_USER_ID);
2126     }
2127 
2128     @Test
onOccupantZoneConfigChanged_multipleZones()2129     public void onOccupantZoneConfigChanged_multipleZones() throws Exception {
2130         CarAudioService service = setUpAudioService();
2131         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2132         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
2133                 .thenReturn(TEST_REAR_LEFT_USER_ID, TEST_REAR_RIGHT_USER_ID);
2134         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2135 
2136         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2137 
2138         expectWithMessage("User ID for primary and secondary zone after config changed")
2139                 .that(service.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2140                 .isNotEqualTo(service.getUserIdForZone(TEST_REAR_LEFT_ZONE_ID));
2141         expectWithMessage("Secondary user ID config changed")
2142                 .that(service.getUserIdForZone(TEST_REAR_LEFT_ZONE_ID))
2143                 .isEqualTo(TEST_REAR_RIGHT_USER_ID);
2144     }
2145 
2146     @Test
init_forUserAlreadySetup_callsInternalConfigChange()2147     public void init_forUserAlreadySetup_callsInternalConfigChange() throws Exception {
2148         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2149         when(mMockOccupantZoneService.getUserForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
2150                 .thenReturn(TEST_DRIVER_USER_ID);
2151         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
2152                 .thenReturn(TEST_REAR_RIGHT_USER_ID);
2153         CarAudioService service = setUpAudioServiceWithoutInit();
2154 
2155         service.init();
2156 
2157         waitForInternalCallback();
2158         expectWithMessage("User ID for primary zone for user available at init")
2159                 .that(service.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2160                 .isEqualTo(TEST_DRIVER_USER_ID);
2161         expectWithMessage("User ID secondary zone for user available at init")
2162                 .that(service.getUserIdForZone(TEST_REAR_RIGHT_ZONE_ID))
2163                 .isEqualTo(TEST_REAR_RIGHT_USER_ID);
2164     }
2165 
2166     @Test
init_withAudioModuleCallbackFeatureDisabled()2167     public void init_withAudioModuleCallbackFeatureDisabled() throws Exception {
2168         when(mAudioControlWrapperAidl.supportsFeature(
2169                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)).thenReturn(false);
2170 
2171         setUpAudioService();
2172 
2173         verify(mAudioControlWrapperAidl, never()).setModuleChangeCallback(any());
2174     }
2175 
2176     @Test
init_withAudioFocusFeatureDisabled()2177     public void init_withAudioFocusFeatureDisabled() throws Exception {
2178         when(mAudioControlWrapperAidl.supportsFeature(
2179                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS)).thenReturn(false);
2180 
2181         setUpAudioService();
2182 
2183         verify(mAudioControlWrapperAidl, never()).registerFocusListener(any());
2184     }
2185 
2186     @Test
init_withAudioGainCallbackFeatureDisabled()2187     public void init_withAudioGainCallbackFeatureDisabled() throws Exception {
2188         when(mAudioControlWrapperAidl.supportsFeature(
2189                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)).thenReturn(false);
2190 
2191         setUpAudioService();
2192 
2193         verify(mAudioControlWrapperAidl, never()).registerAudioGainCallback(any());
2194     }
2195 
2196     @Test
serviceDied_registersAudioGainCallback()2197     public void serviceDied_registersAudioGainCallback() throws Exception {
2198         setUpAudioService();
2199         ArgumentCaptor<AudioControlDeathRecipient> captor =
2200                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2201         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2202         AudioControlDeathRecipient runnable = captor.getValue();
2203         reset(mAudioControlWrapperAidl);
2204 
2205         runnable.serviceDied();
2206 
2207         verify(mAudioControlWrapperAidl).registerAudioGainCallback(any());
2208     }
2209 
2210     @Test
serviceDied_withNullAudioGainCallback()2211     public void serviceDied_withNullAudioGainCallback() throws Exception {
2212         when(mAudioControlWrapperAidl.supportsFeature(
2213                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)).thenReturn(false);
2214         setUpAudioService();
2215         ArgumentCaptor<AudioControlDeathRecipient> captor =
2216                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2217         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2218         AudioControlDeathRecipient runnable = captor.getValue();
2219         reset(mAudioControlWrapperAidl);
2220 
2221         runnable.serviceDied();
2222 
2223         verify(mAudioControlWrapperAidl, never()).registerAudioGainCallback(any());
2224     }
2225 
2226     @Test
serviceDied_registersFocusListener()2227     public void serviceDied_registersFocusListener() throws Exception {
2228         setUpAudioService();
2229         ArgumentCaptor<AudioControlDeathRecipient> captor =
2230                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2231         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2232         AudioControlDeathRecipient runnable = captor.getValue();
2233         reset(mAudioControlWrapperAidl);
2234 
2235         runnable.serviceDied();
2236 
2237         verify(mAudioControlWrapperAidl).registerFocusListener(any());
2238     }
2239 
2240     @Test
serviceDied_withAudioServerNotRunning()2241     public void serviceDied_withAudioServerNotRunning() throws Exception {
2242         setUpAudioService();
2243         ArgumentCaptor<AudioControlDeathRecipient> captor =
2244                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2245         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2246         AudioControlDeathRecipient runnable = captor.getValue();
2247         reset(mAudioControlWrapperAidl);
2248         when(mAudioManager.isAudioServerRunning()).thenReturn(false);
2249 
2250         runnable.serviceDied();
2251 
2252         verify(mAudioControlWrapperAidl, never()).registerAudioGainCallback(any());
2253         verify(mAudioControlWrapperAidl, never()).registerFocusListener(any());
2254     }
2255 
2256     @Test
serviceDied_withAudioServerDown()2257     public void serviceDied_withAudioServerDown() throws Exception {
2258         CarAudioService service = setUpAudioService();
2259         ArgumentCaptor<AudioControlDeathRecipient> captor =
2260                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2261         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2262         AudioControlDeathRecipient runnable = captor.getValue();
2263         reset(mAudioControlWrapperAidl);
2264         service.releaseAudioCallbacks(/* isAudioServerDown= */ true);
2265 
2266         runnable.serviceDied();
2267 
2268         verify(mAudioControlWrapperAidl, never()).registerAudioGainCallback(any());
2269         verify(mAudioControlWrapperAidl, never()).registerFocusListener(any());
2270         verify(mAudioControlWrapperAidl, never()).setModuleChangeCallback(any());
2271     }
2272 
2273     @Test
serviceDied_setsModuleChangeCallback()2274     public void serviceDied_setsModuleChangeCallback() throws Exception {
2275         setUpAudioService();
2276         ArgumentCaptor<AudioControlDeathRecipient> captor =
2277                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2278         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2279         AudioControlDeathRecipient runnable = captor.getValue();
2280         reset(mAudioControlWrapperAidl);
2281 
2282         runnable.serviceDied();
2283 
2284         verify(mAudioControlWrapperAidl).setModuleChangeCallback(any());
2285     }
2286 
2287     @Test
serviceDied_withNullModuleChangeCallback()2288     public void serviceDied_withNullModuleChangeCallback() throws Exception {
2289         when(mAudioControlWrapperAidl.supportsFeature(
2290                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)).thenReturn(false);
2291         setUpAudioService();
2292         ArgumentCaptor<AudioControlDeathRecipient> captor =
2293                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2294         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2295         AudioControlDeathRecipient runnable = captor.getValue();
2296         reset(mAudioControlWrapperAidl);
2297 
2298         runnable.serviceDied();
2299 
2300         verify(mAudioControlWrapperAidl, never()).setModuleChangeCallback(any());
2301     }
2302 
2303     @Test
getVolumeGroupIdForAudioContext_forPrimaryGroup()2304     public void getVolumeGroupIdForAudioContext_forPrimaryGroup() throws Exception {
2305         CarAudioService service = setUpAudioService();
2306 
2307         expectWithMessage("Volume group ID for primary audio zone")
2308                 .that(service.getVolumeGroupIdForAudioContext(PRIMARY_AUDIO_ZONE,
2309                         CarAudioContext.MUSIC))
2310                 .isEqualTo(TEST_PRIMARY_ZONE_GROUP_0);
2311     }
2312 
2313     @Test
getVolumeGroupIdForAudioAttribute()2314     public void getVolumeGroupIdForAudioAttribute() throws Exception {
2315         CarAudioService service = setUpAudioService();
2316 
2317         expectWithMessage("Volume group ID for primary audio zone")
2318                 .that(service.getVolumeGroupIdForAudioAttribute(PRIMARY_AUDIO_ZONE,
2319                         CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA)))
2320                 .isEqualTo(TEST_PRIMARY_ZONE_GROUP_0);
2321     }
2322 
2323     @Test
getVolumeGroupIdForAudioAttribute_withNullAttribute_fails()2324     public void getVolumeGroupIdForAudioAttribute_withNullAttribute_fails() throws Exception {
2325         CarAudioService service = setUpAudioService();
2326 
2327         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
2328                 service.getVolumeGroupIdForAudioAttribute(PRIMARY_AUDIO_ZONE,
2329                 /* attributes= */ null));
2330 
2331         expectWithMessage("Null audio attribute exception").that(thrown).hasMessageThat()
2332                 .contains("Audio attributes");
2333     }
2334 
2335     @Test
getVolumeGroupIdForAudioAttribute_withInvalidZoneId_fails()2336     public void getVolumeGroupIdForAudioAttribute_withInvalidZoneId_fails() throws Exception {
2337         CarAudioService service = setUpAudioService();
2338 
2339         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
2340                 service.getVolumeGroupIdForAudioAttribute(INVALID_AUDIO_ZONE,
2341                         CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA)));
2342 
2343         expectWithMessage("Invalid audio zone exception").that(thrown).hasMessageThat()
2344                 .contains("Invalid audio zone Id");
2345     }
2346 
2347     @Test
getInputDevicesForZoneId_primaryZone()2348     public void getInputDevicesForZoneId_primaryZone() throws Exception {
2349         CarAudioService service = setUpAudioService();
2350 
2351         expectWithMessage("Get input device for primary zone id")
2352                 .that(service.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE))
2353                 .containsExactly(new AudioDeviceAttributes(mMicrophoneInputDevice));
2354     }
2355 
2356     @Test
getExternalSources_forSingleDevice()2357     public void getExternalSources_forSingleDevice() throws Exception {
2358         CarAudioService service = setUpAudioService();
2359         AudioDeviceInfo[] inputDevices = generateInputDeviceInfos();
2360 
2361         expectWithMessage("External input device addresses")
2362                 .that(service.getExternalSources())
2363                 .asList().containsExactly(inputDevices[1].getAddress());
2364     }
2365 
2366     @Test
setAudioEnabled_forEnabledVolumeGroupMuting()2367     public void setAudioEnabled_forEnabledVolumeGroupMuting() throws Exception {
2368         CarAudioService service = setUpAudioService();
2369 
2370         service.setAudioEnabled(/* isAudioEnabled= */ true);
2371 
2372         verify(mAudioControlWrapperAidl).onDevicesToMuteChange(any());
2373     }
2374 
2375     @Test
setAudioEnabled_forDisabledVolumeGroupMuting()2376     public void setAudioEnabled_forDisabledVolumeGroupMuting() throws Exception {
2377         CarAudioService nonVolumeGroupMutingAudioService =
2378                 setUpAudioServiceWithDisabledResource(audioUseCarVolumeGroupMuting);
2379 
2380         nonVolumeGroupMutingAudioService.setAudioEnabled(/* isAudioEnabled= */ true);
2381 
2382         verify(mAudioControlWrapperAidl, never()).onDevicesToMuteChange(any());
2383     }
2384 
2385     @Test
onAudioServerDown_forCarAudioServiceCallback()2386     public void onAudioServerDown_forCarAudioServiceCallback() throws Exception {
2387         setUpAudioService();
2388         AudioServerStateCallback callback = getAudioServerStateCallback();
2389         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
2390         AudioPlaybackCallback playbackCallback = getCarAudioPlaybackCallback();
2391         ICarOccupantZoneCallback occupantZoneCallback = getOccupantZoneCallback();
2392         KeyEventListener keyInputListener = getAudioKeyEventListener();
2393 
2394         callback.onAudioServerDown();
2395 
2396         verify(mAudioControlWrapperAidl, never()).onDevicesToMuteChange(any());
2397         // Routing policy is not unregistered on audio server going down
2398         verify(mAudioManager, times(AUDIO_SERVICE_POLICY_REGISTRATIONS - 1))
2399                 .unregisterAudioPolicy(any());
2400         verify(mAudioManager).unregisterAudioPlaybackCallback(playbackCallback);
2401         verify(mAudioControlWrapperAidl).unregisterFocusListener();
2402         verify(mAudioManager, never()).unregisterVolumeGroupCallback(any());
2403         verify(mMockPowerService).removePowerPolicyListener(any());
2404         verify(mMockTelephonyManager).unregisterTelephonyCallback(any());
2405         verify(mAudioManager).unregisterAudioDeviceCallback(deviceCallback);
2406         verify(mAudioControlWrapperAidl).clearModuleChangeCallback();
2407         verify(mMockOccupantZoneService).unregisterCallback(occupantZoneCallback);
2408         verify(mMockCarInputService).unregisterKeyEventListener(keyInputListener);
2409         verify(mAudioControlWrapperAidl, never()).unlinkToDeath();
2410     }
2411 
2412     @Test
onAudioServerDown_forCarAudioServiceCallback_withFadeManagerEnabled()2413     public void onAudioServerDown_forCarAudioServiceCallback_withFadeManagerEnabled()
2414             throws Exception {
2415         setUpCarAudioServiceWithFadeManagerEnabled();
2416         AudioServerStateCallback callback = getAudioServerStateCallback();
2417         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
2418         AudioPlaybackCallback playbackCallback = getCarAudioPlaybackCallback();
2419         ICarOccupantZoneCallback occupantZoneCallback = getOccupantZoneCallback();
2420         KeyEventListener keyInputListener = getAudioKeyEventListener();
2421 
2422         callback.onAudioServerDown();
2423 
2424         verify(mAudioControlWrapperAidl, never()).onDevicesToMuteChange(any());
2425         // Routing policy is not unregistered on audio server going down
2426         verify(mAudioManager, times(AUDIO_SERVICE_POLICY_REGISTRATIONS_WITH_FADE_MANAGER - 1))
2427                 .unregisterAudioPolicy(any());
2428         verify(mAudioManager).unregisterAudioPlaybackCallback(playbackCallback);
2429         verify(mAudioControlWrapperAidl).unregisterFocusListener();
2430         verify(mAudioManager, never()).unregisterVolumeGroupCallback(any());
2431         verify(mMockPowerService).removePowerPolicyListener(any());
2432         verify(mMockTelephonyManager).unregisterTelephonyCallback(any());
2433         verify(mAudioManager).unregisterAudioDeviceCallback(deviceCallback);
2434         verify(mAudioControlWrapperAidl).clearModuleChangeCallback();
2435         verify(mMockOccupantZoneService).unregisterCallback(occupantZoneCallback);
2436         verify(mMockCarInputService).unregisterKeyEventListener(keyInputListener);
2437         verify(mAudioControlWrapperAidl, never()).unlinkToDeath();
2438     }
2439 
2440     @Test
onAudioServerDown_forCarAudioServiceCallback_withCoreVolumeAndRouting()2441     public void onAudioServerDown_forCarAudioServiceCallback_withCoreVolumeAndRouting()
2442             throws Exception {
2443         setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
2444         AudioServerStateCallback callback = getAudioServerStateCallback();
2445         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
2446         AudioPlaybackCallback playbackCallback = getCarAudioPlaybackCallback();
2447         ICarOccupantZoneCallback occupantZoneCallback = getOccupantZoneCallback();
2448         KeyEventListener keyInputListener = getAudioKeyEventListener();
2449 
2450         callback.onAudioServerDown();
2451 
2452         verify(mAudioControlWrapperAidl, never()).onDevicesToMuteChange(any());
2453         // Routing policy is not unregistered on audio server going down
2454         verify(mAudioManager, times(AUDIO_SERVICE_POLICY_REGISTRATIONS - 1))
2455                 .unregisterAudioPolicy(any());
2456         verify(mAudioManager).unregisterAudioPlaybackCallback(playbackCallback);
2457         verify(mAudioControlWrapperAidl).unregisterFocusListener();
2458         verify(mAudioManager).unregisterVolumeGroupCallback(any());
2459         verify(mMockPowerService).removePowerPolicyListener(any());
2460         verify(mMockTelephonyManager).unregisterTelephonyCallback(any());
2461         verify(mAudioManager).unregisterAudioDeviceCallback(deviceCallback);
2462         verify(mAudioControlWrapperAidl).clearModuleChangeCallback();
2463         verify(mMockOccupantZoneService).unregisterCallback(occupantZoneCallback);
2464         verify(mMockCarInputService).unregisterKeyEventListener(keyInputListener);
2465         verify(mAudioControlWrapperAidl, never()).unlinkToDeath();
2466     }
2467 
2468     @Test
onAudioServerUp_forCarAudioServiceCallback()2469     public void onAudioServerUp_forCarAudioServiceCallback() throws Exception {
2470         CarAudioService service = setUpAudioService();
2471         AudioServerStateCallback callback = getAudioServerStateCallback();
2472         callback.onAudioServerDown();
2473 
2474         callback.onAudioServerUp();
2475 
2476         waitForInternalCallback();
2477         expectWithMessage("Re-initialized Car Audio Service Zones")
2478                 .that(service.getAudioZoneIds()).asList()
2479                 .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
2480                         TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID);
2481         // Each callback should register twice the registration from init for each required callback
2482         verify(mAudioManager, times(2 * AUDIO_SERVICE_POLICY_REGISTRATIONS))
2483                 .registerAudioPolicy(any());
2484         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2485                 .registerAudioPlaybackCallback(any(), any());
2486         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2487                 .registerFocusListener(any());
2488         verify(mAudioManager, never()).registerVolumeGroupCallback(any(), any());
2489         verify(mMockPowerService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2490                 .addPowerPolicyListener(any(), any());
2491         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2492                 .registerAudioDeviceCallback(any(), any());
2493         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2494                 .setModuleChangeCallback(any());
2495         verify(mMockOccupantZoneService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2496                 .registerCallback(any());
2497         verify(mMockCarInputService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2498                 .registerKeyEventListener(any(), any());
2499         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2500                 .linkToDeath(any());
2501     }
2502 
2503     @Test
onAudioServerUp_forCarAudioServiceCallback_withFadeManagerEnabled()2504     public void onAudioServerUp_forCarAudioServiceCallback_withFadeManagerEnabled()
2505             throws Exception {
2506         CarAudioService service = setUpCarAudioServiceWithFadeManagerEnabled();
2507         AudioServerStateCallback callback = getAudioServerStateCallback();
2508         callback.onAudioServerDown();
2509 
2510         callback.onAudioServerUp();
2511 
2512         expectWithMessage("Re-initialized Car Audio Service Zones")
2513                 .that(service.getAudioZoneIds()).asList()
2514                 .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
2515                         TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID);
2516         // Each callback should register twice the registration from init for each required callback
2517         verify(mAudioManager, times(2 * AUDIO_SERVICE_POLICY_REGISTRATIONS_WITH_FADE_MANAGER))
2518                 .registerAudioPolicy(any());
2519         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2520                 .registerAudioPlaybackCallback(any(), any());
2521         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2522                 .registerFocusListener(any());
2523         verify(mAudioManager, never()).registerVolumeGroupCallback(any(), any());
2524         verify(mMockPowerService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2525                 .addPowerPolicyListener(any(), any());
2526         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2527                 .registerAudioDeviceCallback(any(), any());
2528         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2529                 .setModuleChangeCallback(any());
2530         verify(mMockOccupantZoneService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2531                 .registerCallback(any());
2532         verify(mMockCarInputService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2533                 .registerKeyEventListener(any(), any());
2534         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2535                 .linkToDeath(any());
2536     }
2537 
2538 
2539     @Test
onAudioServerUp_forCarAudioServiceCallback_withCoreVolumeAndRouting()2540     public void onAudioServerUp_forCarAudioServiceCallback_withCoreVolumeAndRouting()
2541             throws Exception {
2542         CarAudioService service = setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
2543         AudioServerStateCallback callback = getAudioServerStateCallback();
2544         callback.onAudioServerDown();
2545 
2546         callback.onAudioServerUp();
2547 
2548         expectWithMessage("Re-initialized Car Audio Service Zones")
2549                 .that(service.getAudioZoneIds()).asList()
2550                 .containsExactly(PRIMARY_AUDIO_ZONE);
2551         // Each callback should register twice the registration from init for each required callback
2552         verify(mAudioManager, times(2 * AUDIO_SERVICE_POLICY_REGISTRATIONS))
2553                 .registerAudioPolicy(any());
2554         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2555                 .registerAudioPlaybackCallback(any(), any());
2556         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2557                 .registerFocusListener(any());
2558         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2559                 .registerVolumeGroupCallback(any(), any());
2560         verify(mMockPowerService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2561                 .addPowerPolicyListener(any(), any());
2562         verify(mAudioManager, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2563                 .registerAudioDeviceCallback(any(), any());
2564         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2565                 .setModuleChangeCallback(any());
2566         verify(mMockOccupantZoneService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2567                 .registerCallback(any());
2568         verify(mMockCarInputService, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2569                 .registerKeyEventListener(any(), any());
2570         verify(mAudioControlWrapperAidl, times(2 * AUDIO_SERVICE_CALLBACKS_REGISTRATION))
2571                 .linkToDeath(any());
2572     }
2573 
2574     @Test
onAudioServerUp_forUserIdAssignments()2575     public void onAudioServerUp_forUserIdAssignments() throws Exception {
2576         CarAudioService service = setUpAudioService();
2577         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2578         when(mMockOccupantZoneService.getUserForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
2579                 .thenReturn(TEST_DRIVER_USER_ID);
2580         AudioServerStateCallback callback = getAudioServerStateCallback();
2581         callback.onAudioServerDown();
2582 
2583         callback.onAudioServerUp();
2584 
2585         waitForInternalCallback();
2586         expectWithMessage("Re-initialized Car Audio Service Zones")
2587                 .that(service.getAudioZoneIds()).asList()
2588                 .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
2589                         TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID);
2590         expectWithMessage("Primary user id after server recovery")
2591                 .that(service.getUserIdForZone(PRIMARY_AUDIO_ZONE)).isEqualTo(TEST_DRIVER_USER_ID);
2592         expectWithMessage("Rear left user id after server recovery")
2593                 .that(service.getUserIdForZone(TEST_REAR_LEFT_ZONE_ID))
2594                 .isEqualTo(TEST_REAR_LEFT_USER_ID);
2595         expectWithMessage("Rear right user id after server recovery")
2596                 .that(service.getUserIdForZone(TEST_REAR_RIGHT_ZONE_ID))
2597                 .isEqualTo(TEST_REAR_RIGHT_USER_ID);
2598         expectWithMessage("Rear front user id after server recovery")
2599                 .that(service.getUserIdForZone(TEST_FRONT_ZONE_ID))
2600                 .isEqualTo(TEST_FRONT_PASSENGER_USER_ID);
2601         expectWithMessage("Rear row 3 user id after server recovery")
2602                 .that(service.getUserIdForZone(TEST_REAR_ROW_3_ZONE_ID))
2603                 .isEqualTo(TEST_REAR_ROW_3_PASSENGER_USER_ID);
2604     }
2605 
2606     @Test
registerVolumeCallback_verifyCallbackHandler()2607     public void registerVolumeCallback_verifyCallbackHandler() throws Exception {
2608         int uid = Binder.getCallingUid();
2609         CarAudioService service = setUpAudioService();
2610 
2611         service.registerVolumeCallback(mVolumeCallbackBinder);
2612 
2613         verify(mCarVolumeCallbackHandler).registerCallback(mVolumeCallbackBinder, uid, true);
2614     }
2615 
2616     @Test
unregisterVolumeCallback_verifyCallbackHandler()2617     public void unregisterVolumeCallback_verifyCallbackHandler() throws Exception {
2618         int uid = Binder.getCallingUid();
2619         CarAudioService service = setUpAudioService();
2620 
2621         service.unregisterVolumeCallback(mVolumeCallbackBinder);
2622 
2623         verify(mCarVolumeCallbackHandler).unregisterCallback(mVolumeCallbackBinder, uid);
2624     }
2625 
2626     @Test
getMutedVolumeGroups_forInvalidZone()2627     public void getMutedVolumeGroups_forInvalidZone() throws Exception {
2628         CarAudioService service = setUpAudioService();
2629 
2630         expectWithMessage("Muted volume groups for invalid zone")
2631                 .that(service.getMutedVolumeGroups(INVALID_AUDIO_ZONE))
2632                 .isEmpty();
2633     }
2634 
2635     @Test
getMutedVolumeGroups_whenVolumeGroupMuteNotSupported()2636     public void getMutedVolumeGroups_whenVolumeGroupMuteNotSupported() throws Exception {
2637         CarAudioService nonVolumeGroupMutingAudioService =
2638                 setUpAudioServiceWithDisabledResource(audioUseCarVolumeGroupMuting);
2639 
2640         expectWithMessage("Muted volume groups with disable mute feature")
2641                 .that(nonVolumeGroupMutingAudioService.getMutedVolumeGroups(PRIMARY_AUDIO_ZONE))
2642                 .isEmpty();
2643     }
2644 
2645     @Test
getMutedVolumeGroups_withMutedGroups()2646     public void getMutedVolumeGroups_withMutedGroups() throws Exception {
2647         CarAudioService service = setUpAudioService();
2648         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2649                 /* mute= */ true, TEST_FLAGS);
2650         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1,
2651                 /* mute= */ true, TEST_FLAGS);
2652 
2653         expectWithMessage("Muted volume groups")
2654                 .that(service.getMutedVolumeGroups(PRIMARY_AUDIO_ZONE))
2655                 .containsExactly(mTestPrimaryZoneVolumeInfo0,
2656                         mTestPrimaryZoneVolumeInfo1);
2657     }
2658 
2659     @Test
getMutedVolumeGroups_afterUnmuting()2660     public void getMutedVolumeGroups_afterUnmuting() throws Exception {
2661         CarAudioService service = setUpAudioService();
2662         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2663                 /* mute= */ true, TEST_FLAGS);
2664         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1,
2665                 /* mute= */ true, TEST_FLAGS);
2666         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2667                 /* mute= */ false, TEST_FLAGS);
2668 
2669         expectWithMessage("Muted volume groups after unmuting one group")
2670                 .that(service.getMutedVolumeGroups(PRIMARY_AUDIO_ZONE))
2671                 .containsExactly(mTestPrimaryZoneVolumeInfo1);
2672     }
2673 
2674     @Test
getMutedVolumeGroups_withMutedGroupsForDifferentZone()2675     public void getMutedVolumeGroups_withMutedGroupsForDifferentZone() throws Exception {
2676         CarAudioService service = setUpAudioService();
2677         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2678                 /* mute= */ true, TEST_FLAGS);
2679         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1,
2680                 /* mute= */ true, TEST_FLAGS);
2681 
2682         expectWithMessage("Muted volume groups for secondary zone")
2683                 .that(service.getMutedVolumeGroups(TEST_REAR_LEFT_ZONE_ID)).isEmpty();
2684     }
2685 
2686     @Test
onReceive_forLegacy_noCallToOnVolumeGroupChanged()2687     public void onReceive_forLegacy_noCallToOnVolumeGroupChanged() throws Exception {
2688         setUpAudioServiceWithoutDynamicRouting();
2689         mVolumeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class);
2690         verify(mMockContext).registerReceiver(mVolumeReceiverCaptor.capture(), any(), anyInt());
2691         BroadcastReceiver receiver = mVolumeReceiverCaptor.getValue();
2692         Intent intent = new Intent(VOLUME_CHANGED_ACTION);
2693 
2694         receiver.onReceive(mMockContext, intent);
2695 
2696         verify(mCarVolumeCallbackHandler, never())
2697                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
2698     }
2699 
2700     @Test
onReceive_forLegacy_forStreamMusic()2701     public void onReceive_forLegacy_forStreamMusic() throws Exception {
2702         setUpAudioServiceWithoutDynamicRouting();
2703         verify(mMockContext).registerReceiver(mVolumeReceiverCaptor.capture(), any(), anyInt());
2704         BroadcastReceiver receiver = mVolumeReceiverCaptor.getValue();
2705         Intent intent = new Intent(VOLUME_CHANGED_ACTION)
2706                 .putExtra(EXTRA_VOLUME_STREAM_TYPE, STREAM_MUSIC);
2707 
2708         receiver.onReceive(mMockContext, intent);
2709 
2710         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(
2711                 eq(PRIMARY_AUDIO_ZONE), anyInt(), eq(FLAG_FROM_KEY | FLAG_SHOW_UI));
2712     }
2713 
2714     @Test
onReceive_forLegacy_onMuteChanged()2715     public void onReceive_forLegacy_onMuteChanged() throws Exception {
2716         setUpAudioServiceWithoutDynamicRouting();
2717         ArgumentCaptor<BroadcastReceiver> captor =
2718                 ArgumentCaptor.forClass(BroadcastReceiver.class);
2719         verify(mMockContext).registerReceiver(captor.capture(), any(), anyInt());
2720         BroadcastReceiver receiver = captor.getValue();
2721         Intent intent = new Intent();
2722         intent.setAction(MASTER_MUTE_CHANGED_ACTION);
2723 
2724         receiver.onReceive(mMockContext, intent);
2725 
2726         verify(mCarVolumeCallbackHandler)
2727                 .onMasterMuteChanged(eq(PRIMARY_AUDIO_ZONE), eq(FLAG_FROM_KEY | FLAG_SHOW_UI));
2728     }
2729 
2730     @Test
getVolumeGroupInfosForZone()2731     public void getVolumeGroupInfosForZone() throws Exception {
2732         CarAudioService service = setUpAudioService();
2733         int groupCount = service.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
2734 
2735         List<CarVolumeGroupInfo> infos =
2736                 service.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2737 
2738         for (int index = 0; index < groupCount; index++) {
2739             CarVolumeGroupInfo info = service
2740                     .getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, index);
2741             expectWithMessage("Car volume group infos for primary zone and info %s", info)
2742                     .that(infos).contains(info);
2743         }
2744     }
2745 
2746     @Test
getVolumeGroupInfosForZone_forDynamicRoutingDisabled()2747     public void getVolumeGroupInfosForZone_forDynamicRoutingDisabled() throws Exception {
2748         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
2749 
2750         List<CarVolumeGroupInfo> infos =
2751                 nonDynamicAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2752 
2753         expectWithMessage("Car volume group infos with dynamic routing disabled")
2754                 .that(infos).isEmpty();
2755     }
2756 
2757     @Test
getVolumeGroupInfosForZone_forOEMConfiguration()2758     public void getVolumeGroupInfosForZone_forOEMConfiguration() throws Exception {
2759         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_using_oem_defined_context);
2760         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext, mAudioManager,
2761                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
2762                 /* audioFadeConfigurationPath= */ null);
2763         nonDynamicAudioService.init();
2764 
2765         List<CarVolumeGroupInfo> infos =
2766                 nonDynamicAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2767 
2768         expectWithMessage("Car volume group infos size with OEM configuration")
2769                 .that(infos).hasSize(1);
2770         expectWithMessage("Car volume group info name with OEM configuration")
2771                 .that(infos.get(0).getName()).isEqualTo("OEM_VOLUME_GROUP");
2772     }
2773 
2774     @Test
getVolumeGroupInfosForZone_size()2775     public void getVolumeGroupInfosForZone_size() throws Exception {
2776         CarAudioService service = setUpAudioService();
2777         int groupCount = service.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
2778 
2779         List<CarVolumeGroupInfo> infos =
2780                 service.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2781 
2782         expectWithMessage("Car volume group infos size for primary zone")
2783                 .that(infos).hasSize(groupCount);
2784     }
2785 
2786     @Test
getVolumeGroupInfosForZone_forInvalidZone()2787     public void getVolumeGroupInfosForZone_forInvalidZone() throws Exception {
2788         CarAudioService service = setUpAudioService();
2789 
2790         IllegalArgumentException thrown =
2791                 assertThrows(IllegalArgumentException.class, () ->
2792                         service.getVolumeGroupInfosForZone(INVALID_AUDIO_ZONE));
2793 
2794         expectWithMessage("Exception for volume group infos size for invalid zone")
2795                 .that(thrown).hasMessageThat().contains("audio zone Id");
2796     }
2797 
2798     @Test
getVolumeGroupInfo()2799     public void getVolumeGroupInfo() throws Exception {
2800         CarVolumeGroupInfo testVolumeGroupInfo = new CarVolumeGroupInfo.Builder(
2801                 mTestPrimaryZoneVolumeInfo0).setMuted(false).build();
2802         CarAudioService service = setUpAudioService();
2803 
2804         expectWithMessage("Car volume group info for primary zone")
2805                 .that(service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
2806                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(testVolumeGroupInfo);
2807     }
2808 
2809     @Test
getVolumeGroupInfo_forInvalidZone()2810     public void getVolumeGroupInfo_forInvalidZone() throws Exception {
2811         CarAudioService service = setUpAudioService();
2812 
2813         IllegalArgumentException thrown =
2814                 assertThrows(IllegalArgumentException.class, () ->
2815                         service.getVolumeGroupInfo(INVALID_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0));
2816 
2817         expectWithMessage("Exception for volume group info size for invalid zone")
2818                 .that(thrown).hasMessageThat().contains("audio zone Id");
2819     }
2820 
2821     @Test
getVolumeGroupInfo_forInvalidGroup()2822     public void getVolumeGroupInfo_forInvalidGroup() throws Exception {
2823         CarAudioService service = setUpAudioService();
2824 
2825         IllegalArgumentException thrown =
2826                 assertThrows(IllegalArgumentException.class, () ->
2827                         service.getVolumeGroupInfo(INVALID_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0));
2828 
2829         expectWithMessage("Exception for volume groups info size for invalid group id")
2830                 .that(thrown).hasMessageThat().contains("audio zone Id");
2831     }
2832 
2833     @Test
getVolumeGroupInfo_forGroupOverRange()2834     public void getVolumeGroupInfo_forGroupOverRange() throws Exception {
2835         CarAudioService service = setUpAudioService();
2836         int groupCount = service.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
2837 
2838         IllegalArgumentException thrown =
2839                 assertThrows(IllegalArgumentException.class, () ->
2840                         service.getVolumeGroupInfo(INVALID_AUDIO_ZONE,
2841                                 groupCount));
2842 
2843         expectWithMessage("Exception for volume groups info size for out of range group")
2844                 .that(thrown).hasMessageThat().contains("audio zone Id");
2845     }
2846 
2847     @Test
getVolumeGroupInfo_withLegacyMode()2848     public void getVolumeGroupInfo_withLegacyMode() throws Exception {
2849         CarAudioService service = setUpAudioServiceWithoutDynamicRouting();
2850 
2851         expectWithMessage("Volume group info in legacy mode")
2852                 .that(service.getVolumeGroupInfo(PRIMARY_OCCUPANT_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
2853                 .isNull();
2854     }
2855 
2856     @Test
registerPrimaryZoneMediaAudioRequestCallbackListener_withNullCallback_fails()2857     public void registerPrimaryZoneMediaAudioRequestCallbackListener_withNullCallback_fails()
2858             throws Exception {
2859         CarAudioService service = setUpAudioService();
2860 
2861         NullPointerException thrown = assertThrows(NullPointerException.class, ()
2862                 -> service.registerPrimaryZoneMediaAudioRequestCallback(
2863                         /* callback= */ null));
2864 
2865         expectWithMessage("Register audio media request callback exception")
2866                 .that(thrown).hasMessageThat()
2867                 .contains("Media request callback");
2868     }
2869 
2870     @Test
unregisterPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails()2871     public void unregisterPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails()
2872             throws Exception {
2873         CarAudioService service = setUpAudioService();
2874 
2875         NullPointerException thrown = assertThrows(NullPointerException.class, ()
2876                 -> service.unregisterPrimaryZoneMediaAudioRequestCallback(
2877                         /* callback= */ null));
2878 
2879         expectWithMessage("Unregister audio media request callback exception")
2880                 .that(thrown).hasMessageThat()
2881                 .contains("Media request callback");
2882     }
2883 
2884     @Test
requestMediaAudioOnPrimaryZone_withPassengerOccupant_succeeds()2885     public void requestMediaAudioOnPrimaryZone_withPassengerOccupant_succeeds()
2886             throws Exception {
2887         CarAudioService service = setUpAudioService();
2888         TestPrimaryZoneMediaAudioRequestCallback
2889                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2890         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2891         assignOccupantToAudioZones();
2892         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2893 
2894         expectWithMessage("Audio media request id")
2895                 .that(service.requestMediaAudioOnPrimaryZone(requestCallback,
2896                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2897                 .isNotEqualTo(INVALID_REQUEST_ID);
2898     }
2899 
2900     @Test
requestMediaAudioOnPrimaryZone_withDriverOccupant_fails()2901     public void requestMediaAudioOnPrimaryZone_withDriverOccupant_fails()
2902             throws Exception {
2903         CarAudioService service = setUpAudioService();
2904         TestPrimaryZoneMediaAudioRequestCallback
2905                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2906         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2907         assignOccupantToAudioZones();
2908         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2909 
2910         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, ()
2911                 -> service.requestMediaAudioOnPrimaryZone(requestCallback,
2912                 TEST_DRIVER_OCCUPANT));
2913 
2914         expectWithMessage("Request media audio exception")
2915                 .that(thrown).hasMessageThat().contains("already owns the primary audio zone");
2916     }
2917 
2918     @Test
requestMediaAudioOnPrimaryZone_withNonAssignedOccupant_fails()2919     public void requestMediaAudioOnPrimaryZone_withNonAssignedOccupant_fails()
2920             throws Exception {
2921         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_UNASSIGNED_OCCUPANT_ZONE_ID))
2922                 .thenReturn(OUT_OF_RANGE_ZONE);
2923         CarAudioService service = setUpAudioService();
2924         TestPrimaryZoneMediaAudioRequestCallback
2925                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2926         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2927         CarOccupantZoneManager.OccupantZoneInfo info =
2928                 getOccupantInfo(TEST_UNASSIGNED_OCCUPANT_ZONE_ID,
2929                 CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
2930                 VehicleAreaSeat.SEAT_ROW_1_LEFT);
2931         assignOccupantToAudioZones();
2932         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2933 
2934         expectWithMessage("Invalid audio media request id")
2935                 .that(service.requestMediaAudioOnPrimaryZone(requestCallback, info))
2936                 .isEqualTo(INVALID_REQUEST_ID);
2937     }
2938 
2939     @Test
requestMediaAudioOnPrimaryZone_withPassengerOccupant_callsApprover()2940     public void requestMediaAudioOnPrimaryZone_withPassengerOccupant_callsApprover()
2941             throws Exception {
2942         CarAudioService service = setUpAudioService();
2943         TestPrimaryZoneMediaAudioRequestCallback
2944                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2945         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2946         assignOccupantToAudioZones();
2947         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2948 
2949         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
2950                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2951 
2952         requestToken.waitForCallback();
2953         expectWithMessage("Called audio media request id")
2954                 .that(requestToken.mRequestId).isEqualTo(requestId);
2955         expectWithMessage("Called audio media request info")
2956                 .that(requestToken.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2957     }
2958 
2959     @Test
requestMediaAudioOnPrimaryZone_withZoneMirroring_fails()2960     public void requestMediaAudioOnPrimaryZone_withZoneMirroring_fails()
2961             throws Exception {
2962         CarAudioService service = setUpAudioService();
2963         TestPrimaryZoneMediaAudioRequestCallback
2964                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2965         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2966         assignOccupantToAudioZones();
2967         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2968         TestAudioZonesMirrorStatusCallbackCallback mirrorCallback =
2969                 getAudioZonesMirrorStatusCallback(service);
2970         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
2971         mirrorCallback.waitForCallback();
2972 
2973         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
2974                         service.requestMediaAudioOnPrimaryZone(requestCallback,
2975                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT));
2976 
2977         expectWithMessage("Request audio share while mirroring exception").that(thrown)
2978                 .hasMessageThat().contains("Can not request audio share to primary zone");
2979     }
2980 
2981     @Test
binderDied_onMediaRequestApprover_resetsApprovedRequest()2982     public void binderDied_onMediaRequestApprover_resetsApprovedRequest()
2983             throws Exception {
2984         CarAudioService service = setUpAudioService();
2985         TestPrimaryZoneMediaAudioRequestCallback requestToken =
2986                 new TestPrimaryZoneMediaAudioRequestCallback();
2987         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2988         assignOccupantToAudioZones();
2989         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2990         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
2991                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2992         requestToken.waitForCallback();
2993         requestToken.reset();
2994         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
2995         requestToken.waitForCallback();
2996         requestCallback.waitForCallback();
2997         requestCallback.reset();
2998 
2999         requestToken.mDeathRecipient.binderDied();
3000 
3001         requestCallback.waitForCallback();
3002         expectWithMessage("Stopped status due to approver's death").that(requestCallback.mStatus)
3003                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
3004         expectWithMessage("Stopped id due to approver's death")
3005                 .that(requestCallback.mRequestId).isEqualTo(requestId);
3006     }
3007 
3008     @Test
allowMediaAudioOnPrimaryZone_withAllowedRequest()3009     public void allowMediaAudioOnPrimaryZone_withAllowedRequest() throws Exception {
3010         CarAudioService service = setUpAudioService();
3011         TestPrimaryZoneMediaAudioRequestCallback
3012                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3013         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3014         assignOccupantToAudioZones();
3015         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3016         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3017                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3018         requestToken.waitForCallback();
3019 
3020         boolean results = service.allowMediaAudioOnPrimaryZone(requestToken, requestId,
3021                 /* allow= */ true);
3022 
3023         expectWithMessage("Allowed audio playback").that(results).isTrue();
3024     }
3025 
3026     @Test
allowMediaAudioOnPrimaryZone_whileMirroring_fails()3027     public void allowMediaAudioOnPrimaryZone_whileMirroring_fails() throws Exception {
3028         CarAudioService service = setUpAudioService();
3029         TestPrimaryZoneMediaAudioRequestCallback
3030                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3031         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3032         assignOccupantToAudioZones();
3033         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3034         long shareId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3035                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3036         requestToken.waitForCallback();
3037         TestAudioZonesMirrorStatusCallbackCallback mirrorCallback =
3038                 getAudioZonesMirrorStatusCallback(service);
3039         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3040         mirrorCallback.waitForCallback();
3041         requestCallback.waitForCallback();
3042 
3043         IllegalStateException thrown =
3044                 assertThrows(IllegalStateException.class, () -> service
3045                         .allowMediaAudioOnPrimaryZone(requestToken, shareId, /* allow= */ true));
3046 
3047         expectWithMessage("Allow audio share while mirroring exception").that(thrown)
3048                 .hasMessageThat().contains("Can not allow audio share to primary zone");
3049         requestCallback.waitForCallback();
3050         expectWithMessage("Rejected status due to mirroring").that(requestCallback.mStatus)
3051                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
3052         expectWithMessage("Rejected id with rejected due to mirroring")
3053                 .that(requestCallback.mRequestId).isEqualTo(shareId);
3054     }
3055 
3056     @Test
allowMediaAudioOnPrimaryZone_withUnallowedRequest()3057     public void allowMediaAudioOnPrimaryZone_withUnallowedRequest() throws Exception {
3058         CarAudioService service = setUpAudioService();
3059         TestPrimaryZoneMediaAudioRequestCallback
3060                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3061         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3062         assignOccupantToAudioZones();
3063         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3064         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3065                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3066         requestToken.waitForCallback();
3067 
3068         boolean results = service.allowMediaAudioOnPrimaryZone(requestToken, requestId,
3069                 /* allow= */ false);
3070 
3071         expectWithMessage("Unallowed audio playback").that(results).isTrue();
3072     }
3073 
3074     @Test
allowMediaAudioOnPrimaryZone_withAllowedRequest_callsRequester()3075     public void allowMediaAudioOnPrimaryZone_withAllowedRequest_callsRequester() throws Exception {
3076         CarAudioService service = setUpAudioService();
3077         TestPrimaryZoneMediaAudioRequestCallback
3078                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3079         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3080         assignOccupantToAudioZones();
3081         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3082         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3083                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3084         requestToken.waitForCallback();
3085 
3086         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3087 
3088         requestCallback.waitForCallback();
3089         expectWithMessage("Media request called audio media request id")
3090                 .that(requestCallback.mRequestId).isEqualTo(requestId);
3091         expectWithMessage("Media request called audio media request info")
3092                 .that(requestCallback.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3093         expectWithMessage("Media request called audio media request status")
3094                 .that(requestCallback.mStatus)
3095                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
3096     }
3097 
3098     @Test
allowMediaAudioOnPrimaryZone_withAllowedRequest_callsApprover()3099     public void allowMediaAudioOnPrimaryZone_withAllowedRequest_callsApprover() throws Exception {
3100         CarAudioService service = setUpAudioService();
3101         TestPrimaryZoneMediaAudioRequestCallback
3102                 requestApprover = new TestPrimaryZoneMediaAudioRequestCallback();
3103         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3104         assignOccupantToAudioZones();
3105         service.registerPrimaryZoneMediaAudioRequestCallback(requestApprover);
3106         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3107                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3108         requestApprover.waitForCallback();
3109         requestApprover.reset();
3110 
3111         service.allowMediaAudioOnPrimaryZone(requestApprover, requestId, /* allow= */ true);
3112 
3113         requestApprover.waitForCallback();
3114         expectWithMessage("Media approver called audio media request id")
3115                 .that(requestApprover.mRequestId).isEqualTo(requestId);
3116         expectWithMessage("Media approver called audio media request info")
3117                 .that(requestApprover.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3118         expectWithMessage("Media approver called audio media request status")
3119                 .that(requestApprover.mStatus)
3120                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
3121     }
3122 
3123     @Test
allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsRequester()3124     public void allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsRequester()
3125             throws Exception {
3126         CarAudioService service = setUpAudioService();
3127         TestPrimaryZoneMediaAudioRequestCallback
3128                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3129         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3130         assignOccupantToAudioZones();
3131         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3132         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3133                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3134         requestToken.waitForCallback();
3135 
3136         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ false);
3137 
3138         requestCallback.waitForCallback();
3139         expectWithMessage("Unallowed media request called audio media request id")
3140                 .that(requestCallback.mRequestId).isEqualTo(requestId);
3141         expectWithMessage("Unallowed media request called audio media request info")
3142                 .that(requestCallback.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3143         expectWithMessage("Unallowed media request called audio media request status")
3144                 .that(requestCallback.mStatus)
3145                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
3146     }
3147 
3148     @Test
allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsApprover()3149     public void allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsApprover() throws Exception {
3150         CarAudioService service = setUpAudioService();
3151         TestPrimaryZoneMediaAudioRequestCallback
3152                 requestApprover = new TestPrimaryZoneMediaAudioRequestCallback();
3153         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3154         assignOccupantToAudioZones();
3155         service.registerPrimaryZoneMediaAudioRequestCallback(requestApprover);
3156         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3157                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3158         requestApprover.waitForCallback();
3159         requestApprover.reset();
3160 
3161         service.allowMediaAudioOnPrimaryZone(requestApprover, requestId, /* allow= */ false);
3162 
3163         requestApprover.waitForCallback();
3164         expectWithMessage("Unallowed media approver called audio media request id")
3165                 .that(requestApprover.mRequestId).isEqualTo(requestId);
3166         expectWithMessage("Unallowed approver token called audio media request info")
3167                 .that(requestApprover.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3168         expectWithMessage("Unallowed approver token called audio media request status")
3169                 .that(requestApprover.mStatus)
3170                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
3171     }
3172 
3173     @Test
isMediaAudioAllowedInPrimaryZone_witNullOccupant_fails()3174     public void isMediaAudioAllowedInPrimaryZone_witNullOccupant_fails() throws Exception {
3175         CarAudioService service = setUpAudioService();
3176         NullPointerException thrown = assertThrows(NullPointerException.class, ()
3177                 -> service.isMediaAudioAllowedInPrimaryZone(/* info= */ null));
3178 
3179         expectWithMessage("Media status exception").that(thrown)
3180                 .hasMessageThat().contains("Occupant zone info");
3181     }
3182 
3183     @Test
isMediaAudioAllowedInPrimaryZone_byDefault()3184     public void isMediaAudioAllowedInPrimaryZone_byDefault() throws Exception {
3185         CarAudioService service = setUpAudioService();
3186 
3187         expectWithMessage("Media default status")
3188                 .that(service.isMediaAudioAllowedInPrimaryZone(
3189                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
3190                 .isFalse();
3191     }
3192 
3193     @Test
isMediaAudioAllowedInPrimaryZone_afterAllowed()3194     public void isMediaAudioAllowedInPrimaryZone_afterAllowed() throws Exception {
3195         CarAudioService service = setUpAudioService();
3196         TestPrimaryZoneMediaAudioRequestCallback
3197                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3198         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3199         assignOccupantToAudioZones();
3200         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3201         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3202                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3203         requestToken.waitForCallback();
3204         requestToken.reset();
3205         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3206         requestToken.waitForCallback();
3207 
3208         expectWithMessage("Media allowed status")
3209                 .that(service.isMediaAudioAllowedInPrimaryZone(
3210                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
3211                 .isTrue();
3212     }
3213 
3214     @Test
isMediaAudioAllowedInPrimaryZone_afterDisallowed()3215     public void isMediaAudioAllowedInPrimaryZone_afterDisallowed() throws Exception {
3216         CarAudioService service = setUpAudioService();
3217         TestPrimaryZoneMediaAudioRequestCallback
3218                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3219         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3220         assignOccupantToAudioZones();
3221         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3222         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3223                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3224         requestToken.waitForCallback();
3225         requestToken.reset();
3226         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ false);
3227         requestToken.waitForCallback();
3228 
3229         expectWithMessage("Media after disallowed status")
3230                 .that(service.isMediaAudioAllowedInPrimaryZone(
3231                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
3232                 .isFalse();
3233     }
3234 
3235     @Test
isMediaAudioAllowedInPrimaryZone_afterUserLogout()3236     public void isMediaAudioAllowedInPrimaryZone_afterUserLogout() throws Exception {
3237         CarAudioService service = setUpAudioService();
3238         TestPrimaryZoneMediaAudioRequestCallback
3239                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3240         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3241         assignOccupantToAudioZones();
3242         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3243         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3244                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3245         requestToken.waitForCallback();
3246         requestToken.reset();
3247         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3248         requestToken.waitForCallback();
3249         requestToken.reset();
3250         simulateLogoutPassengers();
3251         requestToken.waitForCallback();
3252 
3253         expectWithMessage("Media allowed status after passenger logout")
3254                 .that(service.isMediaAudioAllowedInPrimaryZone(
3255                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT)).isFalse();
3256     }
3257 
3258     @Test
isMediaAudioAllowedInPrimaryZone_afterUserSwitch()3259     public void isMediaAudioAllowedInPrimaryZone_afterUserSwitch() throws Exception {
3260         CarAudioService service = setUpAudioService();
3261         TestPrimaryZoneMediaAudioRequestCallback
3262                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3263         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3264         assignOccupantToAudioZones();
3265         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3266         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3267                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3268         requestToken.waitForCallback();
3269         requestToken.reset();
3270         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3271         requestToken.waitForCallback();
3272         requestToken.reset();
3273         simulatePassengersSwitch();
3274         requestToken.waitForCallback();
3275 
3276         expectWithMessage("Media allowed status after passenger switch")
3277                 .that(service.isMediaAudioAllowedInPrimaryZone(
3278                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT)).isFalse();
3279     }
3280 
3281     @Test
resetMediaAudioOnPrimaryZone_afterAllowed()3282     public void resetMediaAudioOnPrimaryZone_afterAllowed() throws Exception {
3283         CarAudioService service = setUpAudioService();
3284         TestPrimaryZoneMediaAudioRequestCallback
3285                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3286         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3287         assignOccupantToAudioZones();
3288         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3289         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3290                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3291         requestToken.waitForCallback();
3292         requestToken.reset();
3293         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3294         requestToken.waitForCallback();
3295         requestToken.reset();
3296 
3297         boolean reset = service.resetMediaAudioOnPrimaryZone(
3298                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3299 
3300         requestToken.waitForCallback();
3301         expectWithMessage("Reset status").that(reset).isTrue();
3302         expectWithMessage("Media reset status")
3303                 .that(service.isMediaAudioAllowedInPrimaryZone(
3304                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
3305                 .isFalse();
3306     }
3307 
3308     @Test
cancelMediaAudioOnPrimaryZone_beforeAllowed()3309     public void cancelMediaAudioOnPrimaryZone_beforeAllowed() throws Exception {
3310         CarAudioService service = setUpAudioService();
3311         TestPrimaryZoneMediaAudioRequestCallback
3312                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3313         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3314         assignOccupantToAudioZones();
3315         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3316         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3317                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3318         requestToken.waitForCallback();
3319         requestToken.reset();
3320 
3321         boolean cancel = service.cancelMediaAudioOnPrimaryZone(requestId);
3322 
3323         requestToken.waitForCallback();
3324         expectWithMessage("Cancel status").that(cancel).isTrue();
3325         expectWithMessage("Canceled media token called audio media request id")
3326                 .that(requestToken.mRequestId).isEqualTo(requestId);
3327         expectWithMessage("Canceled media token called audio media request info")
3328                 .that(requestToken.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3329         expectWithMessage("Canceled media token called audio media request status")
3330                 .that(requestToken.mStatus)
3331                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
3332     }
3333 
3334     @Test
cancelMediaAudioOnPrimaryZone_afterAllowed()3335     public void cancelMediaAudioOnPrimaryZone_afterAllowed() throws Exception {
3336         CarAudioService service = setUpAudioService();
3337         TestPrimaryZoneMediaAudioRequestCallback
3338                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3339         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3340         assignOccupantToAudioZones();
3341         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3342         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3343                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3344         requestToken.waitForCallback();
3345         requestToken.reset();
3346         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3347         requestToken.waitForCallback();
3348         requestToken.reset();
3349 
3350         boolean cancel = service.cancelMediaAudioOnPrimaryZone(requestId);
3351 
3352         requestToken.waitForCallback();
3353         expectWithMessage("Cancel status after allowed").that(cancel).isTrue();
3354         expectWithMessage("Media allowed status after canceled")
3355                 .that(service.isMediaAudioAllowedInPrimaryZone(
3356                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
3357                 .isFalse();
3358     }
3359 
3360     @Test
getZoneIdForAudioFocusInfo_beforeAllowedSharedAudio()3361     public void getZoneIdForAudioFocusInfo_beforeAllowedSharedAudio() throws Exception {
3362         CarAudioService service = setUpAudioService();
3363         TestPrimaryZoneMediaAudioRequestCallback
3364                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3365         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3366         assignOccupantToAudioZones();
3367         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3368         service.requestMediaAudioOnPrimaryZone(requestCallback,
3369                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3370         requestToken.waitForCallback();
3371 
3372         expectWithMessage("Not yet shared media user zone")
3373                 .that(service.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
3374                 .isEqualTo(TEST_REAR_RIGHT_ZONE_ID);
3375     }
3376 
3377     @Test
getZoneIdForAudioFocusInfo_afterAllowedShareAudio()3378     public void getZoneIdForAudioFocusInfo_afterAllowedShareAudio() throws Exception {
3379         CarAudioService service = setUpAudioService();
3380         TestPrimaryZoneMediaAudioRequestCallback
3381                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3382         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3383         assignOccupantToAudioZones();
3384         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3385         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3386                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3387         requestToken.waitForCallback();
3388         requestToken.reset();
3389         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3390         requestToken.waitForCallback();
3391 
3392         expectWithMessage("Shared media user zone")
3393                 .that(service.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
3394                 .isEqualTo(PRIMARY_AUDIO_ZONE);
3395     }
3396 
3397     @Test
getZoneIdForAudioFocusInfo_afterCanceled()3398     public void getZoneIdForAudioFocusInfo_afterCanceled() throws Exception {
3399         CarAudioService service = setUpAudioService();
3400         TestPrimaryZoneMediaAudioRequestCallback
3401                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3402         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3403         assignOccupantToAudioZones();
3404         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3405         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3406                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3407         requestToken.waitForCallback();
3408         requestToken.reset();
3409         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3410         requestToken.waitForCallback();
3411         requestToken.reset();
3412         service.cancelMediaAudioOnPrimaryZone(requestId);
3413         requestToken.waitForCallback();
3414 
3415         expectWithMessage("Canceled shared media user zone")
3416                 .that(service.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
3417                 .isEqualTo(TEST_REAR_RIGHT_ZONE_ID);
3418     }
3419 
3420     @Test
getZoneIdForAudioFocusInfo_afterReset()3421     public void getZoneIdForAudioFocusInfo_afterReset() throws Exception {
3422         CarAudioService service = setUpAudioService();
3423         TestPrimaryZoneMediaAudioRequestCallback
3424                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3425         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3426         assignOccupantToAudioZones();
3427         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3428         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
3429                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3430         requestToken.waitForCallback();
3431         requestToken.reset();
3432         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
3433         requestToken.waitForCallback();
3434         requestToken.reset();
3435         service.resetMediaAudioOnPrimaryZone(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3436         requestToken.waitForCallback();
3437 
3438         expectWithMessage("Reset shared media user zone")
3439                 .that(service.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
3440                 .isEqualTo(TEST_REAR_RIGHT_ZONE_ID);
3441     }
3442 
getOccupantInfo(int occupantZoneId, int occupantType, int seat)3443     private static CarOccupantZoneManager.OccupantZoneInfo getOccupantInfo(int occupantZoneId,
3444             int occupantType, int seat) {
3445         return new CarOccupantZoneManager.OccupantZoneInfo(occupantZoneId, occupantType, seat);
3446     }
3447 
3448     @Test
getAudioAttributesForVolumeGroup()3449     public void getAudioAttributesForVolumeGroup() throws Exception {
3450         CarAudioService service = setUpAudioService();
3451         CarVolumeGroupInfo info = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3452                 TEST_PRIMARY_ZONE_GROUP_0);
3453 
3454         List<AudioAttributes> audioAttributes =
3455                 service.getAudioAttributesForVolumeGroup(info);
3456 
3457         expectWithMessage("Volume group audio attributes").that(audioAttributes)
3458                 .containsExactly(
3459                         CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA),
3460                         CarAudioContext.getAudioAttributeFromUsage(USAGE_GAME),
3461                         CarAudioContext.getAudioAttributeFromUsage(USAGE_UNKNOWN),
3462                         CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION),
3463                         CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_EVENT),
3464                         CarAudioContext.getAudioAttributeFromUsage(USAGE_ANNOUNCEMENT));
3465     }
3466 
3467     @Test
getAudioAttributesForVolumeGroup_withNullInfo_fails()3468     public void getAudioAttributesForVolumeGroup_withNullInfo_fails() throws Exception {
3469         CarAudioService service = setUpAudioService();
3470 
3471         NullPointerException thrown =
3472                 assertThrows(NullPointerException.class, () ->
3473                         service.getAudioAttributesForVolumeGroup(/* groupInfo= */ null));
3474 
3475         expectWithMessage("Volume group audio attributes with null info exception")
3476                 .that(thrown).hasMessageThat().contains("Car volume group info");
3477     }
3478 
3479     @Test
getAudioAttributesForVolumeGroup_withDynamicRoutingDisabled()3480     public void getAudioAttributesForVolumeGroup_withDynamicRoutingDisabled() throws Exception {
3481         CarAudioService nonDynamicAudioService = setUpAudioServiceWithoutDynamicRouting();
3482 
3483         List<AudioAttributes> audioAttributes = nonDynamicAudioService
3484                 .getAudioAttributesForVolumeGroup(mTestPrimaryZoneVolumeInfo0);
3485 
3486         expectWithMessage("Volume group audio attributes with dynamic routing disabled")
3487                 .that(audioAttributes).isEmpty();
3488     }
3489 
3490     @Test
onKeyEvent_forInvalidAudioZone()3491     public void onKeyEvent_forInvalidAudioZone() throws Exception {
3492         CarAudioService service = setUpAudioService();
3493         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3494                 TEST_PRIMARY_ZONE_GROUP_0);
3495         KeyEventListener listener = getAudioKeyEventListener();
3496         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3497                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3498         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3499                 .thenReturn(INVALID_AUDIO_ZONE);
3500         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_UNKNOWN);
3501 
3502         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3503 
3504         expectWithMessage("Volume group volume after invalid audio zone")
3505                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3506                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3507     }
3508 
3509     @Test
onKeyEvent_forInvalidEvent()3510     public void onKeyEvent_forInvalidEvent() throws Exception {
3511         CarAudioService service = setUpAudioService();
3512         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3513                 TEST_PRIMARY_ZONE_GROUP_0);
3514         KeyEventListener listener = getAudioKeyEventListener();
3515         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3516                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3517         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3518                 .thenReturn(PRIMARY_AUDIO_ZONE);
3519         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_UNKNOWN);
3520 
3521         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3522 
3523         expectWithMessage("Volume group volume after unknown key event")
3524                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3525                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3526     }
3527 
3528     @Test
onKeyEvent_forActionUp()3529     public void onKeyEvent_forActionUp() throws Exception {
3530         CarAudioService service = setUpAudioService();
3531         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3532                 TEST_PRIMARY_ZONE_GROUP_0);
3533         KeyEventListener listener = getAudioKeyEventListener();
3534         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3535                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3536         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3537                 .thenReturn(PRIMARY_AUDIO_ZONE);
3538         KeyEvent keyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3539 
3540         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3541 
3542         expectWithMessage("Volume group volume after volume up in primary zone in primary group "
3543                 + "for action up")
3544                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3545                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3546     }
3547 
3548     @Test
onKeyEvent_forDynamicDevKeyEventEnabledForDefaultConfigForZoneWithDynamicDevices()3549     public void onKeyEvent_forDynamicDevKeyEventEnabledForDefaultConfigForZoneWithDynamicDevices()
3550             throws Exception {
3551         enableVolumeKeyEventsToDynamicDevices(/* enableVolumeKeyEvents= */ true);
3552         CarAudioService service = setUpAudioServiceWithDynamicDevices();
3553         service.init();
3554         assignOccupantToAudioZones();
3555         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
3556         KeyEventListener listener = getAudioKeyEventListener();
3557         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3558                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3559         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3560                 .thenReturn(PRIMARY_AUDIO_ZONE);
3561         KeyEvent actionDownKeyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3562         KeyEvent actionUpKeyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3563         listener.onKeyEvent(actionDownKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3564 
3565         listener.onKeyEvent(actionUpKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3566 
3567         expectWithMessage("Volume group volume after volume up in primary zone in primary group "
3568                 + "for volume group without dynamic devices")
3569                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
3570                 .isEqualTo(volumeBefore + 1);
3571     }
3572 
3573     @Test
3574     public void
onKeyEvent_forDynamicDevKeyEventEnabledForDynamicDeviceConfigForZoneWithDynamicDevices()3575             onKeyEvent_forDynamicDevKeyEventEnabledForDynamicDeviceConfigForZoneWithDynamicDevices()
3576             throws Exception {
3577         enableVolumeKeyEventsToDynamicDevices(/* enableVolumeKeyEvents= */ true);
3578         CarAudioService service = setUpAudioServiceWithDynamicDevices();
3579         service.init();
3580         assignOccupantToAudioZones();
3581         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3582         TestAudioZoneConfigurationsChangeCallback configCallback =
3583                 getRegisteredZoneConfigCallback(service);
3584         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
3585         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
3586         configCallback.waitForCallback();
3587         configCallback.reset();
3588         List<CarAudioZoneConfigInfo> zoneConfigInfos =
3589                 service.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
3590         CarAudioZoneConfigInfo zoneConfigSwitchTo = zoneConfigInfos.stream()
3591                 .filter(c -> c.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
3592                 .findFirst().orElseThrow();
3593         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
3594         configCallback.waitForCallback();
3595         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
3596         KeyEventListener listener = getAudioKeyEventListener();
3597         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3598                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3599         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3600                 .thenReturn(PRIMARY_AUDIO_ZONE);
3601         KeyEvent actionDownKeyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3602         KeyEvent actionUpKeyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3603         listener.onKeyEvent(actionDownKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3604 
3605         listener.onKeyEvent(actionUpKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3606 
3607         expectWithMessage("Volume after volume up in primary zone in primary group "
3608                 + "for volume group with dynamic devices while dynamic device key events enabled")
3609                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
3610                 .isEqualTo(volumeBefore + 1);
3611     }
3612 
3613     @Test
3614     public void
onKeyEvent_forDynDevKeyEventDisabledForDynamicDeviceConfigForZoneWithDynamicDevices()3615             onKeyEvent_forDynDevKeyEventDisabledForDynamicDeviceConfigForZoneWithDynamicDevices()
3616             throws Exception {
3617         enableVolumeKeyEventsToDynamicDevices(/* enableVolumeKeyEvents= */ false);
3618         CarAudioService service = setUpAudioServiceWithDynamicDevices();
3619         service.init();
3620         assignOccupantToAudioZones();
3621         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3622         TestAudioZoneConfigurationsChangeCallback configCallback =
3623                 getRegisteredZoneConfigCallback(service);
3624         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
3625         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
3626         configCallback.waitForCallback();
3627         configCallback.reset();
3628         List<CarAudioZoneConfigInfo> zoneConfigInfos =
3629                 service.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
3630         CarAudioZoneConfigInfo zoneConfigSwitchTo = zoneConfigInfos.stream()
3631                 .filter(c -> c.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
3632                 .findFirst().orElseThrow();
3633         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
3634         configCallback.waitForCallback();
3635         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
3636         KeyEventListener listener = getAudioKeyEventListener();
3637         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3638                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3639         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3640                 .thenReturn(PRIMARY_AUDIO_ZONE);
3641         KeyEvent actionDownKeyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3642         KeyEvent actionUpKeyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3643         listener.onKeyEvent(actionDownKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3644 
3645         listener.onKeyEvent(actionUpKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3646 
3647         expectWithMessage("Volume after volume up in primary zone in primary group "
3648                 + "for volume group with dynamic devices while dynamic device key events disabled")
3649                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
3650                 .isEqualTo(volumeBefore);
3651     }
3652 
3653     @Test
onKeyEvent_forActionDownFollowedByActionUp()3654     public void onKeyEvent_forActionDownFollowedByActionUp() throws Exception {
3655         CarAudioService service = setUpAudioService();
3656         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3657                 TEST_PRIMARY_ZONE_GROUP_0);
3658         KeyEventListener listener = getAudioKeyEventListener();
3659         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3660                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3661         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3662                 .thenReturn(PRIMARY_AUDIO_ZONE);
3663         KeyEvent actionDownKeyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3664         KeyEvent actionUpKeyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3665         listener.onKeyEvent(actionDownKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3666 
3667         listener.onKeyEvent(actionUpKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3668 
3669         expectWithMessage("Volume group volume after volume up in primary zone in primary group "
3670                 + "for action down then action up")
3671                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3672                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore + 1);
3673     }
3674 
3675     @Test
onKeyEvent_forVolumeUpEvent_inPrimaryZone()3676     public void onKeyEvent_forVolumeUpEvent_inPrimaryZone() throws Exception {
3677         CarAudioService service = setUpAudioService();
3678         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3679                 TEST_PRIMARY_ZONE_GROUP_0);
3680         KeyEventListener listener = getAudioKeyEventListener();
3681         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3682                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3683         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3684                 .thenReturn(PRIMARY_AUDIO_ZONE);
3685         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3686 
3687         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3688 
3689         expectWithMessage("Volume group volume after volume up in primary zone in primary group")
3690                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3691                         TEST_PRIMARY_ZONE_GROUP_0)).isGreaterThan(volumeBefore);
3692     }
3693 
3694     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone()3695     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone() throws Exception {
3696         CarAudioService service = setUpAudioService();
3697         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3698                 TEST_PRIMARY_ZONE_GROUP_0);
3699         KeyEventListener listener = getAudioKeyEventListener();
3700         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3701                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3702         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3703                 .thenReturn(PRIMARY_AUDIO_ZONE);
3704         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3705 
3706         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3707 
3708         expectWithMessage("Volume group volume after volume down in primary zone in primary group")
3709                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3710                         TEST_PRIMARY_ZONE_GROUP_0)).isLessThan(volumeBefore);
3711     }
3712 
3713     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone_forSecondaryGroup()3714     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone_forSecondaryGroup() throws Exception {
3715         CarAudioService service = setUpAudioService();
3716         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3717                 TEST_PRIMARY_ZONE_GROUP_1);
3718         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
3719         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
3720                 .setUsage(USAGE_ASSISTANT)
3721                 .setDeviceAddress(VOICE_TEST_DEVICE)
3722                 .build())
3723         );
3724         KeyEventListener listener = getAudioKeyEventListener();
3725         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3726                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3727         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3728                 .thenReturn(PRIMARY_AUDIO_ZONE);
3729         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3730 
3731         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3732 
3733         expectWithMessage("Assistant volume group volume after volume down")
3734                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3735                         TEST_PRIMARY_ZONE_GROUP_1)).isLessThan(volumeBefore);
3736     }
3737 
3738     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone_withHigherPriority()3739     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone_withHigherPriority() throws Exception {
3740         CarAudioService service = setUpAudioService();
3741         int primaryGroupVolumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3742                 TEST_PRIMARY_ZONE_GROUP_0);
3743         int voiceVolumeGroupBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3744                 TEST_PRIMARY_ZONE_GROUP_2);
3745         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
3746         callback.onPlaybackConfigChanged(List.of(
3747                 new AudioPlaybackConfigurationBuilder()
3748                         .setUsage(USAGE_VOICE_COMMUNICATION)
3749                         .setDeviceAddress(CALL_TEST_DEVICE)
3750                         .build(),
3751                 new AudioPlaybackConfigurationBuilder()
3752                         .setUsage(USAGE_MEDIA)
3753                         .setDeviceAddress(MEDIA_TEST_DEVICE)
3754                         .build())
3755         );
3756         KeyEventListener listener = getAudioKeyEventListener();
3757         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3758                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3759         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3760                 .thenReturn(PRIMARY_AUDIO_ZONE);
3761         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3762 
3763         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3764 
3765         expectWithMessage("Media volume group volume after volume down")
3766                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3767                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(primaryGroupVolumeBefore);
3768         expectWithMessage("Call volume group volume after volume down")
3769                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3770                         TEST_PRIMARY_ZONE_GROUP_2)).isLessThan(voiceVolumeGroupBefore);
3771     }
3772 
3773     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone_withVersionTwoVolumeList()3774     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone_withVersionTwoVolumeList()
3775             throws Exception {
3776         CarAudioService service = setUpCarAudioServiceWithVersionTwoVolumeList();
3777         int primaryGroupVolumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3778                 TEST_PRIMARY_ZONE_GROUP_0);
3779         int voiceVolumeGroupBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3780                 TEST_PRIMARY_ZONE_GROUP_2);
3781         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
3782         callback.onPlaybackConfigChanged(List.of(
3783                 new AudioPlaybackConfigurationBuilder()
3784                         .setUsage(USAGE_VOICE_COMMUNICATION)
3785                         .setDeviceAddress(CALL_TEST_DEVICE)
3786                         .build(),
3787                 new AudioPlaybackConfigurationBuilder()
3788                         .setUsage(USAGE_MEDIA)
3789                         .setDeviceAddress(MEDIA_TEST_DEVICE)
3790                         .build())
3791         );
3792         KeyEventListener listener = getAudioKeyEventListener();
3793         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3794                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3795         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3796                 .thenReturn(PRIMARY_AUDIO_ZONE);
3797         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3798 
3799         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3800 
3801         expectWithMessage("Media volume group volume after volume down for volume list two")
3802                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3803                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(primaryGroupVolumeBefore);
3804         expectWithMessage("Call volume group volume after volume down for volume list two")
3805                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3806                         TEST_PRIMARY_ZONE_GROUP_2)).isLessThan(voiceVolumeGroupBefore);
3807     }
3808 
3809     @Test
onKeyEvent_forVolumeMuteEvent_inPrimaryZone()3810     public void onKeyEvent_forVolumeMuteEvent_inPrimaryZone() throws Exception {
3811         CarAudioService service = setUpAudioService();
3812         boolean muteBefore = service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
3813                 TEST_PRIMARY_ZONE_GROUP_0);
3814         KeyEventListener listener = getAudioKeyEventListener();
3815         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3816                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3817         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3818                 .thenReturn(PRIMARY_AUDIO_ZONE);
3819         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_MUTE);
3820 
3821         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3822 
3823         expectWithMessage("Volume group volume after volume mute")
3824                 .that(service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
3825                         TEST_PRIMARY_ZONE_GROUP_0)).isNotEqualTo(muteBefore);
3826     }
3827 
3828     @Test
onKeyEvent_forVolumeUpEvent_inSecondaryZone()3829     public void onKeyEvent_forVolumeUpEvent_inSecondaryZone() throws Exception {
3830         CarAudioService service = setUpAudioService();
3831         int volumeBefore = service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3832                 SECONDARY_ZONE_VOLUME_GROUP_ID);
3833         KeyEventListener listener = getAudioKeyEventListener();
3834         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3835                 .thenReturn(TEST_DRIVER_OCCUPANT_ZONE_ID);
3836         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
3837                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
3838         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3839 
3840         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3841 
3842         expectWithMessage("Secondary zone volume group after volume up")
3843                 .that(service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3844                         SECONDARY_ZONE_VOLUME_GROUP_ID))
3845                 .isGreaterThan(volumeBefore);
3846     }
3847 
3848     @Test
onKeyEvent_forVolumeDownEvent_inSecondaryZone()3849     public void onKeyEvent_forVolumeDownEvent_inSecondaryZone() throws Exception {
3850         CarAudioService service = setUpAudioService();
3851         int volumeBefore = service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3852                 SECONDARY_ZONE_VOLUME_GROUP_ID);
3853         KeyEventListener listener = getAudioKeyEventListener();
3854         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3855                 .thenReturn(TEST_DRIVER_OCCUPANT_ZONE_ID);
3856         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
3857                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
3858         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3859 
3860         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3861 
3862         expectWithMessage("Secondary zone volume group after volume down")
3863                 .that(service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3864                         SECONDARY_ZONE_VOLUME_GROUP_ID))
3865                 .isLessThan(volumeBefore);
3866     }
3867 
3868     @Test
onKeyEvent_forVolumeMuteEvent_inSecondaryZone()3869     public void onKeyEvent_forVolumeMuteEvent_inSecondaryZone() throws Exception {
3870         CarAudioService service = setUpAudioService();
3871         boolean muteBefore = service.isVolumeGroupMuted(TEST_REAR_LEFT_ZONE_ID,
3872                 SECONDARY_ZONE_VOLUME_GROUP_ID);
3873         KeyEventListener listener = getAudioKeyEventListener();
3874         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3875                 .thenReturn(TEST_DRIVER_OCCUPANT_ZONE_ID);
3876         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
3877                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
3878         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_MUTE);
3879 
3880         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3881 
3882         expectWithMessage("Secondary zone volume group after volume mute")
3883                 .that(service.isVolumeGroupMuted(TEST_REAR_LEFT_ZONE_ID,
3884                         SECONDARY_ZONE_VOLUME_GROUP_ID))
3885                 .isNotEqualTo(muteBefore);
3886     }
3887 
3888     @Test
onAudioDeviceGainsChanged_forPrimaryZone_changesVolume()3889     public void onAudioDeviceGainsChanged_forPrimaryZone_changesVolume() throws Exception {
3890         CarAudioService service = setUpAudioService();
3891         HalAudioGainCallback callback = getHalAudioGainCallback();
3892         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(PRIMARY_AUDIO_ZONE,
3893                 MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
3894 
3895         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION), List.of(carGain));
3896 
3897         expectWithMessage("New audio gains for primary zone")
3898                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3899                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3900     }
3901 
3902     @Test
onAudioDeviceGainsChanged_forSecondaryZone_changesVolume()3903     public void onAudioDeviceGainsChanged_forSecondaryZone_changesVolume() throws Exception {
3904         CarAudioService service = setUpAudioService();
3905         HalAudioGainCallback callback = getHalAudioGainCallback();
3906         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(TEST_REAR_LEFT_ZONE_ID,
3907                 SECONDARY_TEST_DEVICE_CONFIG_0, TEST_GAIN_INDEX);
3908 
3909         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION), List.of(carGain));
3910 
3911         expectWithMessage("New audio gains for secondary zone")
3912                 .that(service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3913                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3914     }
3915 
3916     @Test
onAudioDeviceGainsChanged_forIncorrectDeviceAddress_sameVolume()3917     public void onAudioDeviceGainsChanged_forIncorrectDeviceAddress_sameVolume() throws Exception {
3918         CarAudioService service = setUpAudioService();
3919         HalAudioGainCallback callback = getHalAudioGainCallback();
3920         int volumeBefore = service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3921                 TEST_PRIMARY_ZONE_GROUP_0);
3922         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(PRIMARY_AUDIO_ZONE,
3923                 SECONDARY_TEST_DEVICE_CONFIG_0, TEST_GAIN_INDEX);
3924 
3925         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION), List.of(carGain));
3926 
3927         expectWithMessage("Same audio gains for primary zone")
3928                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3929                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3930     }
3931 
3932     @Test
onAudioDeviceGainsChanged_forMultipleZones_changesVolume()3933     public void onAudioDeviceGainsChanged_forMultipleZones_changesVolume() throws Exception {
3934         CarAudioService service = setUpAudioService();
3935         HalAudioGainCallback callback = getHalAudioGainCallback();
3936         CarAudioGainConfigInfo primaryAudioZoneCarGain = createCarAudioGainConfigInfo(
3937                 PRIMARY_AUDIO_ZONE, MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
3938         CarAudioGainConfigInfo secondaryAudioZoneCarGain = createCarAudioGainConfigInfo(
3939                 TEST_REAR_LEFT_ZONE_ID, SECONDARY_TEST_DEVICE_CONFIG_0, TEST_GAIN_INDEX);
3940 
3941         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION),
3942                 List.of(primaryAudioZoneCarGain, secondaryAudioZoneCarGain));
3943 
3944         expectWithMessage("New audio gains for primary zone")
3945                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE,
3946                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3947         expectWithMessage("New audio gains for secondary zone")
3948                 .that(service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3949                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3950     }
3951 
3952     @Test
onAudioDeviceGainsChanged_withMute_setsSystemMute()3953     public void onAudioDeviceGainsChanged_withMute_setsSystemMute() throws Exception {
3954         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MUTE_AMBIGUITY);
3955         CarAudioService service = setUpAudioService();
3956         HalAudioGainCallback halAudioGainCallback = getHalAudioGainCallback();
3957         CarAudioGainConfigInfo primaryAudioZoneCarGain = createCarAudioGainConfigInfo(
3958                 PRIMARY_AUDIO_ZONE, MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
3959 
3960         halAudioGainCallback.onAudioDeviceGainsChanged(List.of(Reasons.TCU_MUTE),
3961                 List.of(primaryAudioZoneCarGain));
3962 
3963         expectWithMessage("Hal mute status for primary zone %s", service
3964                 .getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0)).that(service
3965                 .getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0)
3966                 .isMutedBySystem()).isTrue();
3967     }
3968 
3969     @Test
onAudioPortsChanged_forMediaBus_changesVolumeRanges()3970     public void onAudioPortsChanged_forMediaBus_changesVolumeRanges() throws Exception {
3971         CarAudioService service = setUpAudioService();
3972         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
3973         TestCarVolumeEventCallback volumeEventCallback =
3974                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
3975         service.registerCarVolumeEventCallback(volumeEventCallback);
3976         HalAudioDeviceInfo mediaBusDeviceInfo = createHalAudioDeviceInfo(
3977                 TEST_MEDIA_PORT_ID, TEST_MEDIA_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
3978                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, MEDIA_TEST_DEVICE);
3979         CarVolumeGroupInfo volumeGroupInfoBefore =
3980                 service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
3981 
3982         callback.onAudioPortsChanged(List.of(mediaBusDeviceInfo));
3983 
3984         CarVolumeGroupInfo volumeGroupInfoAfter = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3985                 TEST_PRIMARY_ZONE_GROUP_0);
3986         expectWithMessage("update audio port for media device")
3987                 .that(volumeGroupInfoAfter).isNotEqualTo(volumeGroupInfoBefore);
3988         volumeEventCallback.waitForCallback();
3989         expectWithMessage("Volume events count after switching zone configuration")
3990                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
3991         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
3992         expectWithMessage("Volume group infos after switching zone configuration")
3993                 .that(groupEvent.getCarVolumeGroupInfos())
3994                 .containsExactly(volumeGroupInfoAfter);
3995     }
3996 
3997     @Test
onAudioPortsChanged_forNavBus_changesVolumeRanges()3998     public void onAudioPortsChanged_forNavBus_changesVolumeRanges() throws Exception {
3999         CarAudioService service = setUpAudioService();
4000         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
4001         HalAudioDeviceInfo navBusDeviceInfo = createHalAudioDeviceInfo(
4002                 TEST_NAV_PORT_ID, TEST_NAV_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
4003                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, NAVIGATION_TEST_DEVICE);
4004         CarVolumeGroupInfo volumeGroupInfoBefore =
4005                 service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1);
4006 
4007         callback.onAudioPortsChanged(List.of(navBusDeviceInfo));
4008 
4009         expectWithMessage("update audio port for nav device")
4010                 .that(service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
4011                         TEST_PRIMARY_ZONE_GROUP_1)).isNotEqualTo(volumeGroupInfoBefore);
4012     }
4013 
4014     @Test
onAudioPortsChanged_forMultipleBuses_changesVolumeRanges()4015     public void onAudioPortsChanged_forMultipleBuses_changesVolumeRanges() throws Exception {
4016         CarAudioService service = setUpAudioService();
4017         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
4018         HalAudioDeviceInfo mediaBusDeviceInfo = createHalAudioDeviceInfo(
4019                 TEST_MEDIA_PORT_ID, TEST_MEDIA_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
4020                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, MEDIA_TEST_DEVICE);
4021         HalAudioDeviceInfo navBusDeviceInfo = createHalAudioDeviceInfo(
4022                 TEST_NAV_PORT_ID, TEST_NAV_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
4023                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, NAVIGATION_TEST_DEVICE);
4024         CarVolumeGroupInfo mediaVolumeGroupInfoBefore =
4025                 service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
4026         CarVolumeGroupInfo navVolumeGroupInfoBefore =
4027                 service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1);
4028 
4029         callback.onAudioPortsChanged(List.of(mediaBusDeviceInfo, navBusDeviceInfo));
4030 
4031         expectWithMessage("update audio port for media device")
4032                 .that(service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
4033                         TEST_PRIMARY_ZONE_GROUP_0)).isNotEqualTo(mediaVolumeGroupInfoBefore);
4034         expectWithMessage("update audio port for nav device")
4035                 .that(service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
4036                         TEST_PRIMARY_ZONE_GROUP_1)).isNotEqualTo(navVolumeGroupInfoBefore);
4037     }
4038 
4039     @Test
onAudioPortsChanged_withEmptyDeviceInfoList()4040     public void onAudioPortsChanged_withEmptyDeviceInfoList() throws Exception {
4041         CarAudioService service = setUpAudioService();
4042         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
4043         TestCarVolumeEventCallback volumeEventCallback =
4044                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
4045         service.registerCarVolumeEventCallback(volumeEventCallback);
4046 
4047         callback.onAudioPortsChanged(Collections.EMPTY_LIST);
4048 
4049         expectWithMessage("No volume event callback invocation with empty device info list")
4050                 .that(volumeEventCallback.waitForCallback()).isFalse();
4051     }
4052 
4053     @Test
getActiveAudioAttributesForZone()4054     public void getActiveAudioAttributesForZone() throws Exception {
4055         CarAudioService service = setUpAudioService();
4056 
4057         expectWithMessage("Default active audio attributes").that(
4058                 service.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE)).isEmpty();
4059     }
4060 
4061     @Test
getActiveAudioAttributesForZone_withActiveHalFocus()4062     public void getActiveAudioAttributesForZone_withActiveHalFocus() throws Exception {
4063         when(mAudioManager.requestAudioFocus(any())).thenReturn(
4064                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
4065         CarAudioService service = setUpAudioService();
4066         requestHalAudioFocus(USAGE_ALARM);
4067 
4068         expectWithMessage("HAL active audio attributes")
4069                 .that(service.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE))
4070                 .containsExactly(new AudioAttributes.Builder().setUsage(USAGE_ALARM).build());
4071     }
4072 
4073     @Test
getActiveAudioAttributesForZone_withActivePlayback()4074     public void getActiveAudioAttributesForZone_withActivePlayback() throws Exception {
4075         CarAudioService service = setUpAudioService();
4076         mockActivePlayback();
4077 
4078         expectWithMessage("Playback active audio attributes")
4079                 .that(service.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE))
4080                 .containsExactly(new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build());
4081     }
4082 
4083     @Test
getActiveAudioAttributesForZone_withActiveHalAndPlayback()4084     public void getActiveAudioAttributesForZone_withActiveHalAndPlayback() throws Exception {
4085         CarAudioService service = setUpAudioService();
4086         mockActivePlayback();
4087         when(mAudioManager.requestAudioFocus(any())).thenReturn(
4088                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
4089         requestHalAudioFocus(USAGE_VOICE_COMMUNICATION);
4090 
4091         expectWithMessage("Playback active audio attributes")
4092                 .that(service.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE))
4093                 .containsExactly(new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build(),
4094                         new AudioAttributes.Builder().setUsage(USAGE_VOICE_COMMUNICATION).build());
4095     }
4096 
4097     @Test
getCallStateForZone_forPrimaryZone()4098     public void getCallStateForZone_forPrimaryZone() throws Exception {
4099         when(mMockTelephonyManagerWithoutSubscriptionId.getCallState())
4100                 .thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
4101         CarAudioService service = setUpAudioService();
4102         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
4103         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
4104                 .thenReturn(TEST_DRIVER_USER_ID, TEST_REAR_RIGHT_USER_ID);
4105         assignOccupantToAudioZones();
4106 
4107         expectWithMessage("Primary zone call state").that(
4108                 service.getCallStateForZone(PRIMARY_AUDIO_ZONE))
4109                 .isEqualTo(TelephonyManager.CALL_STATE_OFFHOOK);
4110     }
4111 
4112     @Test
getCallStateForZone_forNonPrimaryZone()4113     public void getCallStateForZone_forNonPrimaryZone() throws Exception {
4114         CarAudioService service = setUpAudioService();
4115         when(mMockTelephonyManagerWithoutSubscriptionId.getCallState())
4116                 .thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
4117         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
4118         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
4119                 .thenReturn(TEST_REAR_LEFT_USER_ID, TEST_REAR_RIGHT_USER_ID);
4120         assignOccupantToAudioZones();
4121 
4122         expectWithMessage("Secondary zone call state").that(
4123                         service.getCallStateForZone(TEST_REAR_LEFT_ZONE_ID))
4124                 .isEqualTo(TelephonyManager.CALL_STATE_IDLE);
4125     }
4126 
4127     @Test
getVolumeGroupAndContextCount()4128     public void getVolumeGroupAndContextCount() throws Exception {
4129         CarAudioService useCoreAudioCarAudioService =
4130                 setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
4131 
4132         verify(mAudioManager).registerVolumeGroupCallback(any(), any());
4133         expectWithMessage("Primary zone car volume group count")
4134                 .that(useCoreAudioCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
4135                 .isEqualTo(CoreAudioRoutingUtils.getVolumeGroups().size());
4136         expectWithMessage("Number of contexts")
4137                 .that(useCoreAudioCarAudioService.getCarAudioContext().getAllContextsIds().size())
4138                 .isEqualTo(CoreAudioRoutingUtils.getProductStrategies().size());
4139         expectWithMessage("Car Audio Contexts")
4140                 .that(useCoreAudioCarAudioService.getCarAudioContext().getAllContextsIds())
4141                 .containsExactly(CoreAudioRoutingUtils.NAV_STRATEGY_ID,
4142                         CoreAudioRoutingUtils.MUSIC_STRATEGY_ID,
4143                         CoreAudioRoutingUtils.OEM_STRATEGY_ID);
4144     }
4145 
4146     @Test
registerAudioZonesMirrorStatusCallback()4147     public void registerAudioZonesMirrorStatusCallback() throws Exception {
4148         CarAudioService service = setUpAudioService();
4149         TestAudioZonesMirrorStatusCallbackCallback callback =
4150                 new TestAudioZonesMirrorStatusCallbackCallback(/* count= */ 1);
4151 
4152         boolean registered = service.registerAudioZonesMirrorStatusCallback(callback);
4153 
4154         expectWithMessage("Audio zones mirror status callback registered status")
4155                 .that(registered).isTrue();
4156     }
4157 
4158     @Test
registerAudioZonesMirrorStatusCallback_withoutMirroringEnabled()4159     public void registerAudioZonesMirrorStatusCallback_withoutMirroringEnabled() throws Exception {
4160         CarAudioService service = setUpCarAudioServiceWithoutMirroring();
4161         TestAudioZonesMirrorStatusCallbackCallback callback =
4162                 new TestAudioZonesMirrorStatusCallbackCallback(/* count= */ 1);
4163 
4164         IllegalStateException thrown =
4165                 assertThrows(IllegalStateException.class, () ->
4166                         service.registerAudioZonesMirrorStatusCallback(callback));
4167 
4168         expectWithMessage("Disabled audio zones mirror register exception").that(thrown)
4169                 .hasMessageThat().contains("Audio zones mirroring is required");
4170     }
4171 
4172     @Test
registerAudioZonesMirrorStatusCallback_withNullCallback()4173     public void registerAudioZonesMirrorStatusCallback_withNullCallback() throws Exception {
4174         CarAudioService service = setUpAudioService();
4175 
4176         NullPointerException thrown =
4177                 assertThrows(NullPointerException.class, () ->
4178                     service.registerAudioZonesMirrorStatusCallback(/* callback= */ null));
4179 
4180         expectWithMessage("Null audio zones mirror register exception").that(thrown)
4181                 .hasMessageThat().contains("Audio zones mirror status callback");
4182     }
4183 
4184     @Test
unregisterAudioZonesMirrorStatusCallback_withNullCallback()4185     public void unregisterAudioZonesMirrorStatusCallback_withNullCallback() throws Exception {
4186         CarAudioService service = setUpAudioService();
4187 
4188         NullPointerException thrown =
4189                 assertThrows(NullPointerException.class, () -> service
4190                         .unregisterAudioZonesMirrorStatusCallback(/* callback= */ null));
4191 
4192         expectWithMessage("Null audio zones mirror unregister exception").that(thrown)
4193                 .hasMessageThat().contains("Audio zones mirror status callback");
4194     }
4195 
4196     @Test
enableMirrorForAudioZones_withNullAudioZones()4197     public void enableMirrorForAudioZones_withNullAudioZones() throws Exception {
4198         CarAudioService service = setUpAudioService();
4199 
4200         NullPointerException thrown =
4201                 assertThrows(NullPointerException.class, () ->
4202                         service.enableMirrorForAudioZones(/* audioZones= */ null));
4203 
4204         expectWithMessage("Null mirror audio zones exception").that(thrown)
4205                 .hasMessageThat().contains("Mirror audio zones");
4206     }
4207 
4208     @Test
enableMirrorForAudioZones()4209     public void enableMirrorForAudioZones() throws Exception {
4210         CarAudioService service = setUpAudioService();
4211         TestAudioZonesMirrorStatusCallbackCallback callback =
4212                 getAudioZonesMirrorStatusCallback(service);
4213         assignOccupantToAudioZones();
4214 
4215         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4216 
4217         callback.waitForCallback();
4218         expectWithMessage("Audio mirror approved status").that(callback.getLastStatus())
4219                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
4220         expectWithMessage("Audio mirror approved zones").that(callback.getLastZoneIds())
4221                 .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
4222     }
4223 
4224     @Test
enableMirrorForAudioZones_sendsMirrorInfoToAudioHAL()4225     public void enableMirrorForAudioZones_sendsMirrorInfoToAudioHAL() throws Exception {
4226         CarAudioService service = setUpAudioService();
4227         TestAudioZonesMirrorStatusCallbackCallback callback =
4228                 getAudioZonesMirrorStatusCallback(service);
4229         assignOccupantToAudioZones();
4230 
4231         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4232 
4233         callback.waitForCallback();
4234         String audioMirrorInfoCommand = captureAudioMirrorInfoCommand(1);
4235         List<String> commands = Arrays.asList(audioMirrorInfoCommand.split(";"));
4236         String sourceDeviceAddress = removeUpToEquals(commands.get(0));
4237         expectWithMessage("Audio mirror source info").that(sourceDeviceAddress)
4238                 .isEqualTo(MIRROR_TEST_DEVICE);
4239         String destinationsDevices = commands.get(1);
4240         List<String> deviceAddresses = Arrays.asList(removeUpToEquals(destinationsDevices)
4241                 .split(","));
4242         expectWithMessage("Audio mirror zone one info").that(deviceAddresses.get(0))
4243                 .isEqualTo(SECONDARY_TEST_DEVICE_CONFIG_0);
4244         expectWithMessage("Audio mirror zone two info").that(deviceAddresses.get(1))
4245                 .isEqualTo(TERTIARY_TEST_DEVICE_1);
4246     }
4247 
4248     @Test
enableMirrorForAudioZones_forPrimaryZone_fails()4249     public void enableMirrorForAudioZones_forPrimaryZone_fails() throws Exception {
4250         CarAudioService service = setUpAudioService();
4251         assignOccupantToAudioZones();
4252         int[] audioZones = new int[]{TEST_REAR_LEFT_ZONE_ID, PRIMARY_AUDIO_ZONE};
4253 
4254         IllegalArgumentException thrown =
4255                 assertThrows(IllegalArgumentException.class, () ->
4256                         service.enableMirrorForAudioZones(audioZones));
4257 
4258         expectWithMessage("Mirror audio zones with primary zone exception").that(thrown)
4259                 .hasMessageThat().contains("not allowed for primary audio zone");
4260     }
4261 
4262     @Test
enableMirrorForAudioZones_forNonAssignedZone_fails()4263     public void enableMirrorForAudioZones_forNonAssignedZone_fails() throws Exception {
4264         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
4265                 .thenReturn(UserManagerHelper.USER_NULL);
4266         CarAudioService service = setUpAudioService();
4267         getAudioZonesMirrorStatusCallback(service);
4268         assignOccupantToAudioZones();
4269 
4270         IllegalStateException thrown =
4271                 assertThrows(IllegalStateException.class, () ->
4272                         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
4273 
4274         expectWithMessage("Mirror audio zones for unoccupied audio zone exception")
4275                 .that(thrown).hasMessageThat().contains("must have an active user");
4276     }
4277 
4278     @Test
enableMirrorForAudioZones_forRepeatingZones_fails()4279     public void enableMirrorForAudioZones_forRepeatingZones_fails() throws Exception {
4280         CarAudioService service = setUpAudioService();
4281         assignOccupantToAudioZones();
4282         int[] audioZones = new int[]{TEST_REAR_LEFT_ZONE_ID,
4283                 TEST_REAR_LEFT_ZONE_ID};
4284 
4285         IllegalArgumentException thrown =
4286                 assertThrows(IllegalArgumentException.class, () ->
4287                         service.enableMirrorForAudioZones(audioZones));
4288 
4289         expectWithMessage("Repeated mirror audio zones exception").that(thrown)
4290                 .hasMessageThat().contains("must be unique");
4291     }
4292 
4293     @Test
enableMirrorForAudioZones_forAlreadyMirroredZones()4294     public void enableMirrorForAudioZones_forAlreadyMirroredZones() throws Exception {
4295         CarAudioService service = setUpAudioService();
4296         TestAudioZonesMirrorStatusCallbackCallback callback =
4297                 getAudioZonesMirrorStatusCallback(service);
4298         assignOccupantToAudioZones();
4299         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4300         callback.waitForCallback();
4301         callback.reset(/* count= */ 1);
4302 
4303         IllegalStateException thrown =
4304                 assertThrows(IllegalStateException.class, () ->
4305                         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
4306 
4307         expectWithMessage("Audio mirror exception for repeating request")
4308                 .that(thrown).hasMessageThat().contains("is already mirroring");
4309 
4310     }
4311 
4312     @Test
enableMirrorForAudioZones_afterSharedInPrimaryZone()4313     public void enableMirrorForAudioZones_afterSharedInPrimaryZone() throws Exception {
4314         CarAudioService service = setUpAudioService();
4315         TestPrimaryZoneMediaAudioRequestCallback
4316                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
4317         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
4318         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
4319         assignOccupantToAudioZones();
4320         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
4321                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
4322         requestToken.waitForCallback();
4323         requestToken.reset();
4324         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
4325         requestToken.waitForCallback();
4326         requestToken.reset();
4327 
4328         IllegalStateException thrown =
4329                 assertThrows(IllegalStateException.class, () ->
4330                         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
4331 
4332         expectWithMessage("Mirror audio zones while sharing in primary zone exception")
4333                 .that(thrown).hasMessageThat().contains("currently sharing to primary zone");
4334     }
4335 
4336     @Test
enableMirrorForAudioZones_forInvertedMirrorConfiguration()4337     public void enableMirrorForAudioZones_forInvertedMirrorConfiguration() throws Exception {
4338         CarAudioService service = setUpAudioService();
4339         TestAudioZonesMirrorStatusCallbackCallback callback =
4340                 getAudioZonesMirrorStatusCallback(service);
4341         assignOccupantToAudioZones();
4342         service.enableMirrorForAudioZones(new int[] {TEST_REAR_RIGHT_ZONE_ID,
4343                 TEST_REAR_LEFT_ZONE_ID});
4344         callback.waitForCallback();
4345         callback.reset(/* count= */ 1);
4346 
4347         IllegalStateException thrown =
4348                 assertThrows(IllegalStateException.class, () ->
4349                         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
4350 
4351         expectWithMessage("Audio mirror exception for inverted zone request")
4352                 .that(thrown).hasMessageThat().contains("is already mirroring");
4353     }
4354 
4355     @Test
enableMirrorForAudioZones_withNoMoreMirrorDevices_fails()4356     public void enableMirrorForAudioZones_withNoMoreMirrorDevices_fails() throws Exception {
4357         CarAudioService service = setUpAudioService();
4358         TestAudioZonesMirrorStatusCallbackCallback callback =
4359                 getAudioZonesMirrorStatusCallback(service);
4360         assignOccupantToAudioZones();
4361         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4362         callback.waitForCallback();
4363         callback.reset(/* count= */ 1);
4364 
4365         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
4366                 service.enableMirrorForAudioZones(
4367                         new int[] {TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID}));
4368 
4369         expectWithMessage("Audio mirror for out of mirror devices exception")
4370                 .that(thrown).hasMessageThat().contains("available mirror output devices");
4371     }
4372 
4373     @Test
canEnableAudioMirror_withOutOfMirroringDevices()4374     public void canEnableAudioMirror_withOutOfMirroringDevices() throws Exception {
4375         CarAudioService service = setUpAudioService();
4376         TestAudioZonesMirrorStatusCallbackCallback callback =
4377                 getAudioZonesMirrorStatusCallback(service);
4378         assignOccupantToAudioZones();
4379         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4380         callback.waitForCallback();
4381 
4382         expectWithMessage("Can audio mirror status").that(service
4383                         .canEnableAudioMirror())
4384                 .isEqualTo(AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES);
4385     }
4386 
4387     @Test
canEnableAudioMirror_withAudioMirrorEnabledAndNoPendingRequests()4388     public void canEnableAudioMirror_withAudioMirrorEnabledAndNoPendingRequests() throws Exception {
4389         CarAudioService service = setUpAudioService();
4390 
4391         expectWithMessage("Can audio mirror status before audio mirror request")
4392                 .that(service.canEnableAudioMirror())
4393                 .isEqualTo(AUDIO_MIRROR_CAN_ENABLE);
4394     }
4395 
4396     @Test
canEnableAudioMirror_withMirroringDisabled()4397     public void canEnableAudioMirror_withMirroringDisabled() throws Exception {
4398         CarAudioService service = setUpCarAudioServiceWithoutMirroring();
4399 
4400         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
4401                         service.canEnableAudioMirror());
4402 
4403         expectWithMessage("Can enable audio mirror exception")
4404                 .that(thrown).hasMessageThat().contains("Audio zones mirroring is required");
4405     }
4406 
4407     @Test
extendAudioMirrorRequest()4408     public void extendAudioMirrorRequest() throws Exception {
4409         CarAudioService service = setUpAudioService();
4410         TestAudioZonesMirrorStatusCallbackCallback callback =
4411                 getAudioZonesMirrorStatusCallback(service);
4412         assignOccupantToAudioZones();
4413         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4414         callback.waitForCallback();
4415         callback.reset(1);
4416 
4417         service.extendAudioMirrorRequest(requestId, new int[] {TEST_FRONT_ZONE_ID});
4418 
4419         callback.waitForCallback();
4420         expectWithMessage("Audio mirror approved status").that(callback.getLastStatus())
4421                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
4422         expectWithMessage("Audio mirror approved zones").that(callback.getLastZoneIds())
4423                 .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID,
4424                         TEST_FRONT_ZONE_ID);
4425     }
4426 
4427     @Test
extendAudioMirrorRequest_withNullAudioZones()4428     public void extendAudioMirrorRequest_withNullAudioZones() throws Exception {
4429         CarAudioService service = setUpAudioService();
4430         TestAudioZonesMirrorStatusCallbackCallback callback =
4431                 getAudioZonesMirrorStatusCallback(service);
4432         assignOccupantToAudioZones();
4433         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4434         callback.waitForCallback();
4435         callback.reset(1);
4436 
4437         NullPointerException thrown =
4438                 assertThrows(NullPointerException.class, () ->
4439                         service.extendAudioMirrorRequest(requestId,
4440                                 /* audioZones = */ null));
4441 
4442         expectWithMessage("Null audio zones to extend for mirror request exception")
4443                 .that(thrown).hasMessageThat().contains("Mirror audio zones");
4444     }
4445 
4446     @Test
extendAudioMirrorRequest_withPrimaryAudioZone()4447     public void extendAudioMirrorRequest_withPrimaryAudioZone() throws Exception {
4448         CarAudioService service = setUpAudioService();
4449         TestAudioZonesMirrorStatusCallbackCallback callback =
4450                 getAudioZonesMirrorStatusCallback(service);
4451         assignOccupantToAudioZones();
4452         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4453         callback.waitForCallback();
4454         callback.reset(1);
4455 
4456         IllegalArgumentException thrown =
4457                 assertThrows(IllegalArgumentException.class, () ->
4458                         service.extendAudioMirrorRequest(requestId,
4459                                 new int[] {PRIMARY_AUDIO_ZONE}));
4460 
4461         expectWithMessage("Primary audio zone to extend for mirror request exception")
4462                 .that(thrown).hasMessageThat().contains(
4463                         "Audio mirroring not allowed for primary audio zone");
4464     }
4465 
4466     @Test
getAudioZoneConfigInfos()4467     public void getAudioZoneConfigInfos() throws Exception {
4468         CarAudioService service = setUpAudioService();
4469 
4470         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4471                 service.getAudioZoneConfigInfos(TEST_REAR_LEFT_ZONE_ID);
4472 
4473         List<String> zoneConfigNames = zoneConfigInfos.stream().map(cf -> cf.getName()).toList();
4474         expectWithMessage("Zone configurations for secondary zone").that(zoneConfigNames)
4475                 .containsExactly(SECONDARY_ZONE_CONFIG_NAME_1, SECONDARY_ZONE_CONFIG_NAME_2);
4476     }
4477 
4478     @Test
getCurrentAudioZoneConfigInfo()4479     public void getCurrentAudioZoneConfigInfo() throws Exception {
4480         CarAudioService service = setUpAudioService();
4481 
4482         CarAudioZoneConfigInfo currentZoneConfigInfo =
4483                 service.getCurrentAudioZoneConfigInfo(TEST_REAR_LEFT_ZONE_ID);
4484 
4485         expectWithMessage("Name of current zone configuration for secondary zone")
4486                 .that(currentZoneConfigInfo.getName()).isEqualTo(SECONDARY_ZONE_CONFIG_NAME_1);
4487     }
4488 
4489     @Test
switchZoneToConfig()4490     public void switchZoneToConfig() throws Exception {
4491         CarAudioService service = setUpAudioService();
4492         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4493         assignOccupantToAudioZones();
4494         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4495                 TEST_REAR_LEFT_ZONE_ID);
4496 
4497         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
4498 
4499         callback.waitForCallback();
4500         expectWithMessage("Updated zone configuration")
4501                 .that(callback.getZoneConfig())
4502                 .isEqualTo(getUpdatedCarAudioZoneConfigInfo(zoneConfigSwitchTo, service));
4503         expectWithMessage("Zone configuration switching status")
4504                 .that(callback.getSwitchStatus()).isTrue();
4505     }
4506 
4507     @Test
switchZoneToConfig_forNonAssignedZone_fails()4508     public void switchZoneToConfig_forNonAssignedZone_fails() throws Exception {
4509         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
4510                 .thenReturn(UserManagerHelper.USER_NULL);
4511         CarAudioService service = setUpAudioService();
4512         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4513         assignOccupantToAudioZones();
4514         CarAudioZoneConfigInfo  zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4515                 TEST_REAR_LEFT_ZONE_ID);
4516 
4517         IllegalStateException thrown =
4518                 assertThrows(IllegalStateException.class, () ->
4519                         service.switchZoneToConfig(zoneConfigSwitchTo, callback));
4520 
4521         expectWithMessage("Switching zone configuration for unoccupied audio zone exception")
4522                 .that(thrown).hasMessageThat().contains("must have an active user");
4523     }
4524 
4525     @Test
switchZoneToConfig_afterSharedInPrimaryZone_fails()4526     public void switchZoneToConfig_afterSharedInPrimaryZone_fails() throws Exception {
4527         CarAudioService service = setUpAudioService();
4528         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4529         TestPrimaryZoneMediaAudioRequestCallback
4530                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
4531         service.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
4532         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
4533         assignOccupantToAudioZones();
4534         long requestId = service.requestMediaAudioOnPrimaryZone(requestCallback,
4535                 TEST_REAR_LEFT_PASSENGER_OCCUPANT);
4536         requestToken.waitForCallback();
4537         requestToken.reset();
4538         service.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allow= */ true);
4539         requestToken.waitForCallback();
4540         requestToken.reset();
4541         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4542                 TEST_REAR_LEFT_ZONE_ID);
4543 
4544         IllegalStateException thrown =
4545                 assertThrows(IllegalStateException.class, () ->
4546                         service.switchZoneToConfig(zoneConfigSwitchTo, callback));
4547 
4548         expectWithMessage("Switching zone configuration while sharing in primary zone exception")
4549                 .that(thrown).hasMessageThat().contains("currently sharing to primary zone");
4550     }
4551 
4552     @Test
switchZoneToConfig_afterMirroring_fails()4553     public void switchZoneToConfig_afterMirroring_fails() throws Exception {
4554         CarAudioService service = setUpAudioService();
4555         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4556         TestAudioZonesMirrorStatusCallbackCallback mirrorCallback =
4557                 getAudioZonesMirrorStatusCallback(service);
4558         assignOccupantToAudioZones();
4559         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4560         mirrorCallback.waitForCallback();
4561         mirrorCallback.reset(/* count= */ 1);
4562         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4563                 TEST_REAR_LEFT_ZONE_ID);
4564 
4565         IllegalStateException thrown =
4566                 assertThrows(IllegalStateException.class, () ->
4567                         service.switchZoneToConfig(zoneConfigSwitchTo, callback));
4568 
4569         expectWithMessage("Switching zone configuration while audio mirroring").that(thrown)
4570                 .hasMessageThat().contains("currently in a mirroring configuration");
4571     }
4572 
4573     @Test
switchZoneToConfig_withPendingFocus_regainsFocus()4574     public void switchZoneToConfig_withPendingFocus_regainsFocus() throws Exception {
4575         CarAudioService service = setUpAudioService();
4576         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4577         assignOccupantToAudioZones();
4578         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4579         service.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4580         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4581                 TEST_REAR_RIGHT_ZONE_ID);
4582 
4583         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
4584 
4585         callback.waitForCallback();
4586         expectWithMessage("Updated zone configuration with pending focus")
4587                 .that(callback.getZoneConfig())
4588                 .isEqualTo(getUpdatedCarAudioZoneConfigInfo(zoneConfigSwitchTo, service));
4589         expectWithMessage("Zone configuration switching status with pending focus")
4590                 .that(callback.getSwitchStatus()).isTrue();
4591         List<Integer> focusChanges = getFocusChanges(audioFocusInfo);
4592         expectWithMessage("Media audio focus changes after switching zone")
4593                 .that(focusChanges).containsExactly(AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_GAIN);
4594     }
4595 
4596     @Test
switchZoneToConfig_withPendingFocus_updatesDuckingInfo()4597     public void switchZoneToConfig_withPendingFocus_updatesDuckingInfo() throws Exception {
4598         CarAudioService service = setUpAudioService();
4599         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4600         assignOccupantToAudioZones();
4601         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4602         service.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4603         ArgumentCaptor<List<CarDuckingInfo>> carDuckingInfosCaptor =
4604                 ArgumentCaptor.forClass(List.class);
4605         verify(mAudioControlWrapperAidl).onDevicesToDuckChange(carDuckingInfosCaptor.capture());
4606         verifyMediaDuckingInfoInZone(carDuckingInfosCaptor, TEST_REAR_RIGHT_ZONE_ID,
4607                 " before switching zone");
4608         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4609                 TEST_REAR_RIGHT_ZONE_ID);
4610 
4611         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
4612 
4613         callback.waitForCallback();
4614         expectWithMessage("Updated zone configuration with pending focus")
4615                 .that(callback.getZoneConfig())
4616                 .isEqualTo(getUpdatedCarAudioZoneConfigInfo(zoneConfigSwitchTo, service));
4617         expectWithMessage("Zone configuration switching status with pending focus")
4618                 .that(callback.getSwitchStatus()).isTrue();
4619         verify(mAudioControlWrapperAidl, times(2))
4620                 .onDevicesToDuckChange(carDuckingInfosCaptor.capture());
4621         verifyMediaDuckingInfoInZone(carDuckingInfosCaptor, TEST_REAR_RIGHT_ZONE_ID,
4622                 " after switching zone");
4623     }
4624 
4625     @Test
switchZoneToConfig_withCurrentZoneConfigAndPendingFocus_notLoseAndRegainFocus()4626     public void switchZoneToConfig_withCurrentZoneConfigAndPendingFocus_notLoseAndRegainFocus()
4627             throws Exception {
4628         CarAudioService service = setUpAudioService();
4629         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4630         assignOccupantToAudioZones();
4631         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4632         service.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4633         CarAudioZoneConfigInfo currentZoneConfig =
4634                 service.getCurrentAudioZoneConfigInfo(TEST_REAR_RIGHT_ZONE_ID);
4635 
4636         service.switchZoneToConfig(currentZoneConfig, callback);
4637 
4638         callback.waitForCallback();
4639         expectWithMessage("Updated zone configuration with current configuration")
4640                 .that(callback.getZoneConfig()).isEqualTo(currentZoneConfig);
4641         expectWithMessage("Zone configuration switching status with current configuration")
4642                 .that(callback.getSwitchStatus()).isTrue();
4643         verify(mAudioManager, never()).dispatchAudioFocusChange(eq(audioFocusInfo), anyInt(),
4644                 any(AudioPolicy.class));
4645     }
4646 
4647     @Test
switchZoneToConfig_withVolumeGroupEventCallbackRegistered_invokesEvent()4648     public void switchZoneToConfig_withVolumeGroupEventCallbackRegistered_invokesEvent()
4649             throws Exception {
4650         CarAudioService service = setUpAudioService();
4651         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4652         TestCarVolumeEventCallback volumeEventCallback =
4653                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
4654         assignOccupantToAudioZones();
4655         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4656                 TEST_REAR_LEFT_ZONE_ID);
4657         service.registerCarVolumeEventCallback(volumeEventCallback);
4658 
4659         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
4660 
4661         callback.waitForCallback();
4662         expectWithMessage("Updated zone configuration")
4663                 .that(callback.getZoneConfig())
4664                 .isEqualTo(getUpdatedCarAudioZoneConfigInfo(zoneConfigSwitchTo, service));
4665         expectWithMessage("Zone configuration switching status")
4666                 .that(callback.getSwitchStatus()).isTrue();
4667         volumeEventCallback.waitForCallback();
4668         expectWithMessage("Volume events count after switching zone configuration")
4669                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4670         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4671         expectWithMessage("Volume event type after switching zone configuration")
4672                 .that(groupEvent.getEventTypes())
4673                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED);
4674         expectWithMessage("Volume group infos after switching zone configuration")
4675                 .that(groupEvent.getCarVolumeGroupInfos())
4676                 .containsExactly(mTestSecondaryZoneConfig1VolumeInfo0,
4677                         mTestSecondaryZoneConfig1VolumeInfo1);
4678     }
4679 
4680     @Test
switchZoneToConfig_updatesVolumeGroupInfos()4681     public void switchZoneToConfig_updatesVolumeGroupInfos()
4682             throws Exception {
4683         CarAudioService service = setUpAudioService();
4684         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4685         assignOccupantToAudioZones();
4686         Log.e(TAG, "Current volume group " + service.getVolumeGroupInfosForZone(
4687                 TEST_REAR_LEFT_ZONE_ID));
4688         expectWithMessage("Volume group infos before switching zone configuration")
4689                 .that(service.getVolumeGroupInfosForZone(TEST_REAR_LEFT_ZONE_ID))
4690                 .containsExactly(mTestSecondaryConfig0VolumeGroup0Info);
4691         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
4692                 TEST_REAR_LEFT_ZONE_ID);
4693 
4694         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
4695 
4696         callback.waitForCallback();
4697         expectWithMessage("Volume group infos after switching zone configuration")
4698                 .that(service.getVolumeGroupInfosForZone(TEST_REAR_LEFT_ZONE_ID))
4699                 .containsExactly(mTestSecondaryZoneConfig1VolumeInfo0,
4700                         mTestSecondaryZoneConfig1VolumeInfo1);
4701     }
4702 
4703     @Test
switchZoneToConfig_withDynamicDevicesFlagEnabled()4704     public void switchZoneToConfig_withDynamicDevicesFlagEnabled() throws Exception {
4705         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
4706         CarAudioService service = setUpAudioService();
4707         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4708         assignOccupantToAudioZones();
4709         CarAudioZoneConfigInfo previousConfig = service
4710                 .getCurrentAudioZoneConfigInfo(TEST_REAR_LEFT_ZONE_ID);
4711         CarAudioZoneConfigInfo zoneConfigSwitchTo =
4712                 getZoneConfigToSwitch(service, TEST_REAR_LEFT_ZONE_ID);
4713 
4714         service.switchZoneToConfig(zoneConfigSwitchTo, callback);
4715 
4716         callback.waitForCallback();
4717         expectWithMessage("Updated zone configuration, with dynamic devices enabled")
4718                 .that(callback.getZoneConfig().hasSameConfigInfo(zoneConfigSwitchTo)).isTrue();
4719         expectWithMessage("Zone configuration switched status, with dynamic devices enabled")
4720                 .that(callback.getSwitchStatus()).isTrue();
4721         CarAudioZoneConfigInfo switchedInfo = service
4722                 .getCurrentAudioZoneConfigInfo(TEST_REAR_LEFT_ZONE_ID);
4723         expectWithMessage("Switched config active status")
4724                 .that(switchedInfo.isActive()).isTrue();
4725         expectWithMessage("Switched config selected status")
4726                 .that(switchedInfo.isSelected()).isTrue();
4727         CarAudioZoneConfigInfo previousUpdated =
4728                 getUpdatedCarAudioZoneConfigInfo(previousConfig, service);
4729         expectWithMessage("Previous config active status")
4730                 .that(previousUpdated.isActive()).isTrue();
4731         expectWithMessage("Previous config selected status")
4732                 .that(previousUpdated.isSelected()).isFalse();
4733     }
4734 
4735     @Test
switchZoneToConfig_toDynamicConfig_withDynamicDevicesInMultipleZones()4736     public void switchZoneToConfig_toDynamicConfig_withDynamicDevicesInMultipleZones()
4737             throws Exception {
4738         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_using_dynamic_devices);
4739         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
4740         CarAudioService dynamicDeviceService =
4741                 setUpAudioServiceWithDynamicDevices(mTempCarAudioConfigFile,
4742                         mTempCarAudioFadeConfigFile);
4743         dynamicDeviceService.init();
4744         assignOccupantToAudioZones();
4745         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4746         TestAudioZoneConfigurationsChangeCallback configCallback =
4747                 getRegisteredZoneConfigCallback(dynamicDeviceService);
4748         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4749         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4750         configCallback.waitForCallback();
4751         configCallback.reset();
4752         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4753                 dynamicDeviceService.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
4754         CarAudioZoneConfigInfo zoneConfigSwitchTo = zoneConfigInfos.stream()
4755                 .filter(c -> c.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4756                 .findFirst().orElseThrow();
4757 
4758         dynamicDeviceService.switchZoneToConfig(zoneConfigSwitchTo, callback);
4759 
4760         configCallback.waitForCallback();
4761         callback.waitForCallback();
4762         CarAudioZoneConfigInfo secondaryZoneBTConfig = configCallback.mInfos.stream()
4763                 .filter(c -> c.getName().equals(SECONDARY_ZONE_BT_CONFIG_NAME))
4764                 .findFirst().orElseThrow();
4765         expectWithMessage("Inactive dynamic config due to dynamic device being used")
4766                 .that(secondaryZoneBTConfig.isActive()).isFalse();
4767     }
4768 
4769     @Test
switchZoneToConfig_backFromDynamicConfig_withDynamicDevicesInMultipleZones()4770     public void switchZoneToConfig_backFromDynamicConfig_withDynamicDevicesInMultipleZones()
4771             throws Exception {
4772         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_using_dynamic_devices);
4773         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
4774         CarAudioService dynamicDeviceService =
4775                 setUpAudioServiceWithDynamicDevices(mTempCarAudioConfigFile,
4776                         mTempCarAudioFadeConfigFile);
4777         dynamicDeviceService.init();
4778         assignOccupantToAudioZones();
4779         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4780         TestAudioZoneConfigurationsChangeCallback configCallback =
4781                 getRegisteredZoneConfigCallback(dynamicDeviceService);
4782         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4783         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4784         configCallback.waitForCallback();
4785         configCallback.reset();
4786         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4787                 dynamicDeviceService.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
4788         CarAudioZoneConfigInfo dynamicConfig = zoneConfigInfos.stream()
4789                 .filter(c -> c.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4790                 .findFirst().orElseThrow();
4791         dynamicDeviceService.switchZoneToConfig(dynamicConfig, callback);
4792         callback.waitForCallback();
4793         callback.reset();
4794         configCallback.waitForCallback();
4795         configCallback.reset();
4796         CarAudioZoneConfigInfo defaultConfig = zoneConfigInfos.stream()
4797                 .filter(CarAudioZoneConfigInfo::isDefault).findFirst().orElseThrow();
4798 
4799         dynamicDeviceService.switchZoneToConfig(defaultConfig, callback);
4800 
4801         configCallback.waitForCallback();
4802         callback.waitForCallback();
4803         CarAudioZoneConfigInfo secondaryZoneBTConfig = configCallback.mInfos.stream()
4804                 .filter(c -> c.getName().equals(SECONDARY_ZONE_BT_CONFIG_NAME))
4805                 .findFirst().orElseThrow();
4806         expectWithMessage("Re-activated dynamic config due to dynamic device not used")
4807                 .that(secondaryZoneBTConfig.isActive()).isTrue();
4808     }
4809 
4810     @Test
registerAudioZoneConfigsChangeCallback()4811     public void registerAudioZoneConfigsChangeCallback() throws Exception {
4812         IAudioZoneConfigurationsChangeCallback callback =
4813                 new TestAudioZoneConfigurationsChangeCallback();
4814         CarAudioService service = setUpAudioService();
4815 
4816         boolean registered = service.registerAudioZoneConfigsChangeCallback(callback);
4817 
4818         expectWithMessage("Car audio zone configuration change register status")
4819                 .that(registered).isTrue();
4820     }
4821 
4822     @Test
registerAudioZoneConfigsChangeCallback_multipleTimes()4823     public void registerAudioZoneConfigsChangeCallback_multipleTimes() throws Exception {
4824         IAudioZoneConfigurationsChangeCallback callback =
4825                 new TestAudioZoneConfigurationsChangeCallback();
4826         CarAudioService service = setUpAudioService();
4827         service.registerAudioZoneConfigsChangeCallback(callback);
4828 
4829         boolean registered = service.registerAudioZoneConfigsChangeCallback(callback);
4830 
4831         expectWithMessage("Car audio zone configuration change re-register status")
4832                 .that(registered).isTrue();
4833     }
4834 
4835     @Test
registerAudioZoneConfigsChangeCallback_withNullCallback()4836     public void registerAudioZoneConfigsChangeCallback_withNullCallback() throws Exception {
4837         CarAudioService service = setUpAudioService();
4838 
4839         NullPointerException thrown = assertThrows(NullPointerException.class,
4840                 () -> service.registerAudioZoneConfigsChangeCallback(null));
4841 
4842         expectWithMessage("Car audio zone configuration change registration exception")
4843                 .that(thrown).hasMessageThat().contains("Car audio zone configs");
4844     }
4845 
4846     @Test
onAudioDevicesAdded_forDynamicDevicesEnabled()4847     public void onAudioDevicesAdded_forDynamicDevicesEnabled() throws Exception {
4848         CarAudioService audioServiceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4849         audioServiceWithDynamicDevices.init();
4850         TestAudioZoneConfigurationsChangeCallback
4851                 configCallback = getRegisteredZoneConfigCallback(audioServiceWithDynamicDevices);
4852         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4853 
4854         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4855 
4856         configCallback.waitForCallback();
4857         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4858                 audioServiceWithDynamicDevices.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
4859         CarAudioZoneConfigInfo btConfig = zoneConfigInfos.stream()
4860                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4861                 .findFirst().orElseThrow();
4862         expectWithMessage("Enabled bluetooth configuration").that(btConfig.isActive()).isTrue();
4863     }
4864 
4865     @Test
onAudioDevicesAdded_forDynamicDevicesEnabled_triggersCallback()4866     public void onAudioDevicesAdded_forDynamicDevicesEnabled_triggersCallback() throws Exception {
4867         CarAudioService serviceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4868         serviceWithDynamicDevices.init();
4869         TestAudioZoneConfigurationsChangeCallback
4870                 configCallback = getRegisteredZoneConfigCallback(serviceWithDynamicDevices);
4871         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4872 
4873         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4874 
4875         configCallback.waitForCallback();
4876         expectWithMessage("Enabled dynamic config callback status").that(configCallback.mStatus)
4877                 .isEqualTo(CONFIG_STATUS_CHANGED);
4878         CarAudioZoneConfigInfo btConfig = configCallback.mInfos.stream()
4879                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4880                 .findFirst().orElseThrow();
4881         expectWithMessage("Callback enabled bluetooth configuration").that(btConfig.isActive())
4882                 .isTrue();
4883     }
4884 
4885     @Test
onAudioDevicesAdded_forDynamicDevicesEnabled_withAudioServerDown()4886     public void onAudioDevicesAdded_forDynamicDevicesEnabled_withAudioServerDown()
4887             throws Exception {
4888         CarAudioService audioServiceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4889         audioServiceWithDynamicDevices.init();
4890         TestAudioZoneConfigurationsChangeCallback
4891                 configCallback = getRegisteredZoneConfigCallback(audioServiceWithDynamicDevices);
4892         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4893         audioServiceWithDynamicDevices.releaseAudioCallbacks(/* isAudioServerDown= */ true);
4894 
4895         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4896 
4897         configCallback.waitForCallback();
4898         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4899                 audioServiceWithDynamicDevices.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
4900         CarAudioZoneConfigInfo btConfig = zoneConfigInfos.stream()
4901                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4902                 .findFirst().orElseThrow();
4903         expectWithMessage("Disabled bluetooth configuration with audio server down")
4904                 .that(btConfig.isActive()).isFalse();
4905     }
4906 
4907     @Test
onAudioDevicesRemoved_forDynamicDevicesEnabled_triggersCallback()4908     public void onAudioDevicesRemoved_forDynamicDevicesEnabled_triggersCallback()
4909             throws Exception {
4910         CarAudioService serviceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4911         serviceWithDynamicDevices.init();
4912         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4913         TestAudioZoneConfigurationsChangeCallback
4914                 configCallback = getRegisteredZoneConfigCallback(serviceWithDynamicDevices);
4915         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4916         configCallback.waitForCallback();
4917         configCallback.reset();
4918 
4919         deviceCallback.onAudioDevicesRemoved(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4920 
4921         configCallback.waitForCallback();
4922         expectWithMessage("Disabled dynamic config callback status").that(configCallback.mStatus)
4923                 .isEqualTo(CONFIG_STATUS_CHANGED);
4924         CarAudioZoneConfigInfo btConfig = configCallback.mInfos.stream()
4925                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4926                 .findFirst().orElseThrow();
4927         expectWithMessage("Callback disabled bluetooth configuration").that(btConfig.isActive())
4928                 .isFalse();
4929     }
4930 
4931     @Test
onAudioDevicesRemoved_afterAdded_forDynamicDevicesEnabled()4932     public void onAudioDevicesRemoved_afterAdded_forDynamicDevicesEnabled() throws Exception {
4933         CarAudioService audioServiceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4934         audioServiceWithDynamicDevices.init();
4935         TestAudioZoneConfigurationsChangeCallback
4936                 configCallback = getRegisteredZoneConfigCallback(audioServiceWithDynamicDevices);
4937         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4938         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4939         configCallback.waitForCallback();
4940         configCallback.reset();
4941 
4942         deviceCallback.onAudioDevicesRemoved(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4943 
4944         configCallback.waitForCallback();
4945         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4946                 audioServiceWithDynamicDevices.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
4947         CarAudioZoneConfigInfo btConfig = zoneConfigInfos.stream()
4948                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4949                 .findFirst().orElseThrow();
4950         expectWithMessage("Enabled bluetooth configuration after removed device")
4951                 .that(btConfig.isActive()).isFalse();
4952     }
4953 
4954     @Test
onAudioDevicesRemoved_forSelectedDynamicDevicesEnabled_triggersCallback()4955     public void onAudioDevicesRemoved_forSelectedDynamicDevicesEnabled_triggersCallback()
4956             throws Exception {
4957         SwitchAudioZoneConfigCallbackImpl switchCallback = new SwitchAudioZoneConfigCallbackImpl();
4958         CarAudioService serviceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4959         serviceWithDynamicDevices.init();
4960         assignOccupantToAudioZones();
4961         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4962         TestAudioZoneConfigurationsChangeCallback
4963                 configCallback = getRegisteredZoneConfigCallback(serviceWithDynamicDevices);
4964         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4965         configCallback.waitForCallback();
4966         configCallback.reset();
4967         List<CarAudioZoneConfigInfo> infos =
4968                 serviceWithDynamicDevices.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
4969         CarAudioZoneConfigInfo btConfig = infos.stream().filter(
4970                 config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4971                 .findFirst().orElseThrow();
4972         serviceWithDynamicDevices.switchZoneToConfig(btConfig, switchCallback);
4973         switchCallback.waitForCallback();
4974 
4975         deviceCallback.onAudioDevicesRemoved(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4976 
4977         configCallback.waitForCallback();
4978         CarAudioZoneConfigInfo updatedBTConfig = configCallback.mInfos.stream().filter(
4979                         config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
4980                 .findFirst().orElseThrow();
4981         expectWithMessage("Disabled selected dynamic config callback status")
4982                 .that(configCallback.mStatus).isEqualTo(CONFIG_STATUS_AUTO_SWITCHED);
4983         expectWithMessage("Callback disabled selected bluetooth configuration")
4984                 .that(updatedBTConfig.isActive()).isFalse();
4985     }
4986 
4987     @Test
onAudioDevicesRemoved_forDynamicDevicesEnabled_afterAddedWithAudioServerDown()4988     public void onAudioDevicesRemoved_forDynamicDevicesEnabled_afterAddedWithAudioServerDown()
4989             throws Exception {
4990         CarAudioService audioServiceWithDynamicDevices = setUpAudioServiceWithDynamicDevices();
4991         audioServiceWithDynamicDevices.init();
4992         TestAudioZoneConfigurationsChangeCallback
4993                 configCallback = getRegisteredZoneConfigCallback(audioServiceWithDynamicDevices);
4994         AudioDeviceCallback deviceCallback = captureAudioDeviceCallback();
4995         deviceCallback.onAudioDevicesAdded(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
4996         configCallback.waitForCallback();
4997         configCallback.reset();
4998         audioServiceWithDynamicDevices.releaseAudioCallbacks(/* isAudioServerDown= */ true);
4999 
5000         deviceCallback.onAudioDevicesRemoved(new AudioDeviceInfo[]{mBTAudioDeviceInfo});
5001 
5002         configCallback.waitForCallback();
5003         List<CarAudioZoneConfigInfo> zoneConfigInfos =
5004                 audioServiceWithDynamicDevices.getAudioZoneConfigInfos(PRIMARY_AUDIO_ZONE);
5005         CarAudioZoneConfigInfo btConfig = zoneConfigInfos.stream()
5006                 .filter(config -> config.getName().equals(PRIMARY_CONFIG_NAME_DYNAMIC_DEVICES))
5007                 .findFirst().orElseThrow();
5008         expectWithMessage(
5009                 "Enabled bluetooth configuration after removed device with audio server down")
5010                 .that(btConfig.isActive()).isTrue();
5011     }
5012 
5013     @Test
unregisterAudioZoneConfigsChangeCallback()5014     public void unregisterAudioZoneConfigsChangeCallback() throws Exception {
5015         IAudioZoneConfigurationsChangeCallback callback =
5016                 new TestAudioZoneConfigurationsChangeCallback();
5017         CarAudioService service = setUpAudioService();
5018         service.registerAudioZoneConfigsChangeCallback(callback);
5019 
5020         boolean registered = service.unregisterAudioZoneConfigsChangeCallback(callback);
5021 
5022         expectWithMessage("Car audio zone configuration change un-register status")
5023                 .that(registered).isTrue();
5024     }
5025 
5026     @Test
unregisterAudioZoneConfigsChangeCallback_afterUnregister_fails()5027     public void unregisterAudioZoneConfigsChangeCallback_afterUnregister_fails() throws Exception {
5028         IAudioZoneConfigurationsChangeCallback callback =
5029                 new TestAudioZoneConfigurationsChangeCallback();
5030         CarAudioService service = setUpAudioService();
5031         service.registerAudioZoneConfigsChangeCallback(callback);
5032         service.unregisterAudioZoneConfigsChangeCallback(callback);
5033 
5034         boolean registered = service.unregisterAudioZoneConfigsChangeCallback(callback);
5035 
5036         expectWithMessage("Car audio zone configuration change un-register multiple times status")
5037                 .that(registered).isFalse();
5038     }
5039 
5040     @Test
unregisterAudioZoneConfigsChangeCallback_withNullCallback()5041     public void unregisterAudioZoneConfigsChangeCallback_withNullCallback() throws Exception {
5042         CarAudioService service = setUpAudioService();
5043 
5044         NullPointerException thrown = assertThrows(NullPointerException.class,
5045                 () -> service.unregisterAudioZoneConfigsChangeCallback(null));
5046 
5047         expectWithMessage("Car audio zone configuration change un-registration exception")
5048                 .that(thrown).hasMessageThat().contains("Car audio zone configs");
5049     }
5050 
5051     @Test
disableAudioMirrorForZone_withInvalidZone()5052     public void disableAudioMirrorForZone_withInvalidZone() throws Exception {
5053         CarAudioService service = setUpAudioService();
5054         assignOccupantToAudioZones();
5055 
5056         IllegalArgumentException thrown =
5057                 assertThrows(IllegalArgumentException.class, () ->
5058                         service.disableAudioMirrorForZone(INVALID_AUDIO_ZONE));
5059 
5060         expectWithMessage("Disable mirror for invalid audio zone exception").that(thrown)
5061                         .hasMessageThat().contains("Invalid audio zone");
5062     }
5063 
5064     @Test
disableAudioMirrorForZone_withMirroringDisabled()5065     public void disableAudioMirrorForZone_withMirroringDisabled() throws Exception {
5066         CarAudioService service = setUpCarAudioServiceWithoutMirroring();
5067 
5068         IllegalStateException thrown =
5069                 assertThrows(IllegalStateException.class, () ->
5070                         service.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID));
5071 
5072         expectWithMessage("Disable mirror for zone with audio mirroring disabled")
5073                 .that(thrown).hasMessageThat().contains("Audio zones mirroring is required");
5074     }
5075 
5076     @Test
disableAudioMirrorForZone_forNonMirroringZone()5077     public void disableAudioMirrorForZone_forNonMirroringZone() throws Exception {
5078         CarAudioService service = setUpAudioService();
5079         TestAudioZonesMirrorStatusCallbackCallback callback =
5080                 getAudioZonesMirrorStatusCallback(service);
5081         assignOccupantToAudioZones();
5082 
5083         service.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID);
5084 
5085         callback.waitForCallback();
5086         expectWithMessage("Disable audio mirror for non-mirroring zone callback count")
5087                 .that(callback.mNumberOfCalls).isEqualTo(0);
5088     }
5089 
5090     @Test
disableAudioMirrorForZone_forMirroringZones()5091     public void disableAudioMirrorForZone_forMirroringZones() throws Exception {
5092         CarAudioService service = setUpAudioService();
5093         TestAudioZonesMirrorStatusCallbackCallback callback =
5094                 getAudioZonesMirrorStatusCallback(service);
5095         assignOccupantToAudioZones();
5096         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5097         callback.waitForCallback();
5098         callback.reset(/* count= */ 1);
5099 
5100         service.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID);
5101 
5102         callback.waitForCallback();
5103         expectWithMessage("Callback count for disable audio mirror")
5104                 .that(callback.mNumberOfCalls).isEqualTo(2);
5105         expectWithMessage("Callback status disable audio mirror for mirroring zone")
5106                 .that(callback.getLastStatus())
5107                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
5108         expectWithMessage("Callback zones disable audio mirror for mirroring zone")
5109                 .that(callback.getLastZoneIds()).asList()
5110                 .containsExactly(TEST_REAR_RIGHT_ZONE_ID, TEST_REAR_LEFT_ZONE_ID);
5111     }
5112 
5113     @Test
disableAudioMirrorForZone_forMirroringZones_forFirstMirroringConfig()5114     public void disableAudioMirrorForZone_forMirroringZones_forFirstMirroringConfig()
5115             throws Exception {
5116         CarAudioService service = setUpAudioService();
5117         TestAudioZonesMirrorStatusCallbackCallback callback =
5118                 getAudioZonesMirrorStatusCallback(service);
5119         assignOccupantToAudioZones();
5120         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5121         callback.waitForCallback();
5122         callback.reset(/* count= */ 1);
5123 
5124         service.disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID);
5125 
5126         callback.waitForCallback();
5127         expectWithMessage("Callback count for disable audio mirror")
5128                 .that(callback.mNumberOfCalls).isEqualTo(2);
5129         expectWithMessage("Callback status disable audio mirror for mirroring zone")
5130                 .that(callback.getLastStatus())
5131                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
5132         expectWithMessage("Callback zones disable audio mirror for mirroring zone")
5133                 .that(callback.getLastZoneIds()).asList()
5134                 .containsExactly(TEST_REAR_RIGHT_ZONE_ID, TEST_REAR_LEFT_ZONE_ID);
5135         String audioMirrorOffCommand = captureAudioMirrorInfoCommand(2);
5136         expectWithMessage("Audio HAL off source for mirroring zone")
5137                 .that(audioMirrorOffCommand).contains(MIRROR_TEST_DEVICE);
5138         expectWithMessage("Audio HAL off signal for mirroring zone")
5139                 .that(audioMirrorOffCommand).contains(MIRROR_OFF_SIGNAL);
5140     }
5141 
5142     @Test
disableAudioMirrorForZone_withPendingFocus()5143     public void disableAudioMirrorForZone_withPendingFocus()
5144             throws Exception {
5145         CarAudioService service = setUpAudioService();
5146         TestAudioZonesMirrorStatusCallbackCallback callback =
5147                 getAudioZonesMirrorStatusCallback(service);
5148         assignOccupantToAudioZones();
5149         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5150         callback.waitForCallback();
5151         callback.reset(/* count= */ 2);
5152         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
5153         service.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
5154 
5155         service.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID);
5156 
5157         callback.waitForCallback();
5158         List<Integer> focusChanges = getFocusChanges(audioFocusInfo);
5159         expectWithMessage("Media audio focus changes after disable mirror for zone")
5160                 .that(focusChanges).containsExactly(AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_GAIN);
5161     }
5162 
5163     @Test
disableAudioMirror_withoutMirroringDisabled()5164     public void disableAudioMirror_withoutMirroringDisabled() throws Exception {
5165         CarAudioService service = setUpCarAudioServiceWithoutMirroring();
5166 
5167         IllegalStateException thrown =
5168                 assertThrows(IllegalStateException.class, () ->
5169                         service.disableAudioMirror(INVALID_REQUEST_ID));
5170 
5171         expectWithMessage("Disable mirror for audio zones with audio mirroring disabled")
5172                 .that(thrown).hasMessageThat().contains("Audio zones mirroring is required");
5173     }
5174 
5175     @Test
disableAudioMirror_withInvalidRequestId()5176     public void disableAudioMirror_withInvalidRequestId() throws Exception {
5177         CarAudioService service = setUpAudioService();
5178 
5179         IllegalArgumentException thrown =
5180                 assertThrows(IllegalArgumentException.class, () ->
5181                         service.disableAudioMirror(INVALID_REQUEST_ID));
5182 
5183         expectWithMessage("Disable mirror for audio zones with audio invalid request id")
5184                 .that(thrown).hasMessageThat().contains("INVALID_REQUEST_ID");
5185     }
5186 
5187     @Test
disableAudioMirror_forNonMirroringZone()5188     public void disableAudioMirror_forNonMirroringZone() throws Exception {
5189         CarAudioService service = setUpAudioService();
5190         TestAudioZonesMirrorStatusCallbackCallback callback =
5191                 getAudioZonesMirrorStatusCallback(service);
5192         assignOccupantToAudioZones();
5193         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5194         callback.waitForCallback();
5195         callback.reset(1);
5196         service.disableAudioMirror(requestId);
5197         callback.waitForCallback();
5198         callback.reset(1);
5199 
5200         service.disableAudioMirror(requestId);
5201 
5202         expectWithMessage("Disable audio mirror for non-mirroring zone callback count")
5203                 .that(callback.mNumberOfCalls).isEqualTo(2);
5204     }
5205 
5206     @Test
disableAudioMirror_forMirroringZones()5207     public void disableAudioMirror_forMirroringZones() throws Exception {
5208         CarAudioService service = setUpAudioService();
5209         TestAudioZonesMirrorStatusCallbackCallback callback =
5210                 getAudioZonesMirrorStatusCallback(service);
5211         assignOccupantToAudioZones();
5212         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5213         callback.waitForCallback();
5214         callback.reset(/* count= */ 1);
5215 
5216         service.disableAudioMirror(requestId);
5217 
5218         callback.waitForCallback();
5219         expectWithMessage("Callback count for disable mirror in audio zones")
5220                 .that(callback.mNumberOfCalls).isEqualTo(2);
5221         expectWithMessage("Callback status disable audio mirror for mirroring zones")
5222                 .that(callback.getLastStatus())
5223                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
5224         expectWithMessage("Callback zones disable audio mirror for mirroring zones")
5225                 .that(callback.getLastZoneIds()).asList()
5226                 .containsExactly(TEST_REAR_RIGHT_ZONE_ID, TEST_REAR_LEFT_ZONE_ID);
5227         String audioMirrorOffCommand = captureAudioMirrorInfoCommand(2);
5228         expectWithMessage("Audio HAL off source for mirroring zones")
5229                 .that(audioMirrorOffCommand).contains(MIRROR_TEST_DEVICE);
5230         expectWithMessage("Audio HAL off signal for mirroring zones")
5231                 .that(audioMirrorOffCommand).contains(MIRROR_OFF_SIGNAL);
5232     }
5233 
5234     @Test
disableAudioMirror_withPendingFocus()5235     public void disableAudioMirror_withPendingFocus() throws Exception {
5236         CarAudioService service = setUpAudioService();
5237         TestAudioZonesMirrorStatusCallbackCallback callback =
5238                 getAudioZonesMirrorStatusCallback(service);
5239         assignOccupantToAudioZones();
5240         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5241         callback.waitForCallback();
5242         callback.reset(/* count= */ 2);
5243         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
5244         service.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
5245 
5246         service.disableAudioMirror(requestId);
5247 
5248         callback.waitForCallback();
5249         List<Integer> focusChanges = getFocusChanges(audioFocusInfo);
5250         expectWithMessage("Media audio focus changes after disable audio"
5251                 + "mirror for zones config").that(focusChanges)
5252                 .containsExactly(AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_GAIN);
5253     }
5254 
5255     @Test
getMirrorAudioZonesForAudioZone_withoutMirroringEnabled()5256     public void getMirrorAudioZonesForAudioZone_withoutMirroringEnabled()
5257             throws Exception {
5258         CarAudioService service = setUpAudioService();
5259         assignOccupantToAudioZones();
5260 
5261         int[] zones = service.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
5262 
5263         expectWithMessage("Mirroring zones for non mirror zone %s", TEST_REAR_RIGHT_ZONE_ID)
5264                 .that(zones).asList().isEmpty();
5265     }
5266 
5267     @Test
getMirrorAudioZonesForAudioZone_withMirroringEnabled()5268     public void getMirrorAudioZonesForAudioZone_withMirroringEnabled() throws Exception {
5269         CarAudioService service = setUpAudioService();
5270         TestAudioZonesMirrorStatusCallbackCallback callback =
5271                 getAudioZonesMirrorStatusCallback(service);
5272         assignOccupantToAudioZones();
5273         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5274         callback.waitForCallback();
5275 
5276         int[] zones = service.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
5277 
5278         expectWithMessage("Mirroring zones for mirror zone %s", TEST_REAR_RIGHT_ZONE_ID).that(zones)
5279                 .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
5280     }
5281 
5282     @Test
getMirrorAudioZonesForAudioZone_afterDisableMirror()5283     public void getMirrorAudioZonesForAudioZone_afterDisableMirror() throws Exception {
5284         CarAudioService service = setUpAudioService();
5285         TestAudioZonesMirrorStatusCallbackCallback callback =
5286                 getAudioZonesMirrorStatusCallback(service);
5287         assignOccupantToAudioZones();
5288         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5289         callback.waitForCallback();
5290         callback.reset(1);
5291         service.disableAudioMirror(requestId);
5292         callback.waitForCallback();
5293 
5294         int[] zones = service.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
5295 
5296         expectWithMessage("Mirroring zones for mirror zone %s after disabling mirroring",
5297                 TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
5298     }
5299 
5300     @Test
getMirrorAudioZonesForAudioZone_afterPassengerLogout()5301     public void getMirrorAudioZonesForAudioZone_afterPassengerLogout() throws Exception {
5302         CarAudioService service = setUpAudioService();
5303         TestAudioZonesMirrorStatusCallbackCallback callback =
5304                 getAudioZonesMirrorStatusCallback(service);
5305         assignOccupantToAudioZones();
5306         service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5307         callback.waitForCallback();
5308         callback.reset(1);
5309         simulateLogoutRightPassengers();
5310         callback.waitForCallback();
5311 
5312         int[] zones = service.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
5313 
5314         expectWithMessage("Mirroring zones for mirror zone %s after logout",
5315                 TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
5316     }
5317 
5318     @Test
getMirrorAudioZonesForMirrorRequest_withMirroringEnabled()5319     public void getMirrorAudioZonesForMirrorRequest_withMirroringEnabled() throws Exception {
5320         CarAudioService service = setUpAudioService();
5321         TestAudioZonesMirrorStatusCallbackCallback callback =
5322                 getAudioZonesMirrorStatusCallback(service);
5323         assignOccupantToAudioZones();
5324         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5325         callback.waitForCallback();
5326 
5327         int[] zones = service.getMirrorAudioZonesForMirrorRequest(requestId);
5328 
5329         expectWithMessage("Mirroring zones for mirror request %s", requestId).that(zones).asList()
5330                 .containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
5331     }
5332 
5333     @Test
getMirrorAudioZonesForMirrorRequest_afterDisableMirror()5334     public void getMirrorAudioZonesForMirrorRequest_afterDisableMirror() throws Exception {
5335         CarAudioService service = setUpAudioService();
5336         TestAudioZonesMirrorStatusCallbackCallback callback =
5337                 getAudioZonesMirrorStatusCallback(service);
5338         assignOccupantToAudioZones();
5339         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5340         callback.waitForCallback();
5341         callback.reset(1);
5342         service.disableAudioMirror(requestId);
5343         callback.waitForCallback();
5344 
5345         int[] zones = service.getMirrorAudioZonesForMirrorRequest(TEST_REAR_RIGHT_ZONE_ID);
5346 
5347         expectWithMessage("Mirroring zones for mirror request %s after disabling mirroring",
5348                 requestId).that(zones).asList().isEmpty();
5349     }
5350 
5351     @Test
getMirrorAudioZonesForMirrorRequest_afterPassengerLogout()5352     public void getMirrorAudioZonesForMirrorRequest_afterPassengerLogout() throws Exception {
5353         CarAudioService service = setUpAudioService();
5354         TestAudioZonesMirrorStatusCallbackCallback callback =
5355                 getAudioZonesMirrorStatusCallback(service);
5356         assignOccupantToAudioZones();
5357         long requestId = service.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
5358         callback.waitForCallback();
5359         callback.reset(1);
5360         simulateLogoutRightPassengers();
5361         callback.waitForCallback();
5362 
5363         int[] zones = service.getMirrorAudioZonesForMirrorRequest(requestId);
5364 
5365         expectWithMessage("Mirroring zones for mirror request %s after logout",
5366                 TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
5367     }
5368 
5369     @Test
onAudioVolumeGroupChanged_dispatchCallbackEvent()5370     public void onAudioVolumeGroupChanged_dispatchCallbackEvent() throws Exception {
5371         CarAudioService useCoreAudioCarAudioService =
5372                 setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
5373         int musicIndex = useCoreAudioCarAudioService.getGroupVolume(
5374                 PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID);
5375         // Report a volume change
5376         when(mAudioManager.getVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
5377                 .thenReturn(musicIndex + 1);
5378         when(mAudioManager.getLastAudibleVolumeForVolumeGroup(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
5379                 .thenReturn(musicIndex + 1);
5380         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
5381                 .thenReturn(false);
5382 
5383         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
5384                 CoreAudioRoutingUtils.MUSIC_GROUP_NAME, /* flags= */ 0);
5385 
5386         verify(mCarVolumeCallbackHandler)
5387                 .onVolumeGroupChange(PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID,
5388                         FLAG_SHOW_UI | FLAG_PLAY_SOUND);
5389     }
5390 
5391     @Test
onAudioVolumeGroupChanged_noDispatchCallbackEvent_whenAlreadySynced()5392     public void onAudioVolumeGroupChanged_noDispatchCallbackEvent_whenAlreadySynced()
5393             throws Exception {
5394         CarAudioService useCoreAudioCarAudioService =
5395                 setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
5396         useCoreAudioCarAudioService.setGroupVolume(PRIMARY_AUDIO_ZONE,
5397                 CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID, CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX,
5398                 /* flags= */ 0);
5399         reset(mCarVolumeCallbackHandler);
5400 
5401         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
5402                 CoreAudioRoutingUtils.MUSIC_GROUP_NAME, /* flags= */ 0);
5403 
5404         verify(mCarVolumeCallbackHandler, never())
5405                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
5406     }
5407 
5408     @Test
onAudioVolumeGroupChanged_dispatchCallbackEvent_whenMuted()5409     public void onAudioVolumeGroupChanged_dispatchCallbackEvent_whenMuted() throws Exception {
5410         CarAudioService useCoreAudioCarAudioService =
5411                 setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
5412         // Report a mute change
5413         when(mAudioManager.getVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
5414                 .thenReturn(CoreAudioRoutingUtils.MUSIC_MIN_INDEX);
5415         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
5416                 .thenReturn(true);
5417 
5418         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
5419                 CoreAudioRoutingUtils.MUSIC_GROUP_NAME, /* flags= */ 0);
5420 
5421         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
5422                 CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID, FLAG_SHOW_UI);
5423     }
5424 
5425     @Test
onAudioVolumeGroupChanged_withInvalidVolumeGroupName()5426     public void onAudioVolumeGroupChanged_withInvalidVolumeGroupName() throws Exception {
5427         CarAudioService useCoreAudioCarAudioService =
5428                 setUpCarAudioServiceUsingCoreAudioRoutingAndVolume();
5429 
5430         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
5431                 CoreAudioRoutingUtils.INVALID_GROUP_NAME, /* flags= */ 0);
5432 
5433         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5434                 anyInt(), anyInt());
5435     }
5436 
5437     @Test
callbackVolumeGroupEvent_withEmptyEventList()5438     public void callbackVolumeGroupEvent_withEmptyEventList() throws Exception {
5439         CarAudioService service = setUpAudioService();
5440         TestCarVolumeEventCallback volumeEventCallback =
5441                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5442         service.registerCarVolumeEventCallback(volumeEventCallback);
5443 
5444         service.callbackVolumeGroupEvent(Collections.EMPTY_LIST);
5445 
5446         expectWithMessage("Volume group event callback reception status for empty event list")
5447                 .that(volumeEventCallback.waitForCallback()).isFalse();
5448     }
5449 
5450     @Test
onVolumeGroupEvent_withVolumeEvent_triggersCallback()5451     public void onVolumeGroupEvent_withVolumeEvent_triggersCallback() throws Exception {
5452         CarAudioService service = setUpAudioService();
5453         TestCarVolumeEventCallback volumeEventCallback =
5454                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5455         service.registerCarVolumeEventCallback(volumeEventCallback);
5456 
5457         service.onVolumeGroupEvent(List.of(mTestCarVolumeGroupEvent));
5458 
5459         expectWithMessage("Volume event callback reception status")
5460                 .that(volumeEventCallback.waitForCallback()).isTrue();
5461         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
5462         verify(mCarVolumeCallbackHandler)
5463                 .onVolumeGroupChange(PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID,
5464                         /* flags= */ 0);
5465         expectWithMessage("Volume events count after volume event")
5466                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5467         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5468         expectWithMessage("Volume event type after volume event")
5469                 .that(groupEvent.getEventTypes())
5470                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
5471         expectWithMessage("Volume group infos after unmute")
5472                 .that(groupEvent.getCarVolumeGroupInfos())
5473                 .containsExactly(mTestPrimaryZoneUmMutedVolueInfo0);
5474     }
5475 
5476     @Test
onVolumeGroupEvent_withMuteEvent_triggersCallback()5477     public void onVolumeGroupEvent_withMuteEvent_triggersCallback() throws Exception {
5478         CarAudioService service = setUpAudioService();
5479         TestCarVolumeEventCallback volumeEventCallback =
5480                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5481         service.registerCarVolumeEventCallback(volumeEventCallback);
5482 
5483         service.onVolumeGroupEvent(List.of(mTestCarMuteGroupEvent));
5484 
5485         expectWithMessage("Volume event callback reception status")
5486                 .that(volumeEventCallback.waitForCallback()).isTrue();
5487         verify(mCarVolumeCallbackHandler, never())
5488                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
5489         verify(mCarVolumeCallbackHandler)
5490                 .onGroupMuteChange(PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID,
5491                         /* flags= */ 0);
5492         expectWithMessage("Volume events count after mute event")
5493                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5494         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5495         expectWithMessage("Volume event type after mute event")
5496                 .that(groupEvent.getEventTypes())
5497                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
5498         expectWithMessage("Volume group infos after mute event")
5499                 .that(groupEvent.getCarVolumeGroupInfos())
5500                 .containsExactly(mTestPrimaryZoneUmMutedVolueInfo0);
5501     }
5502 
5503     @Test
onVolumeGroupEvent_withoutMuteOrVolumeEvent_triggersCallback()5504     public void onVolumeGroupEvent_withoutMuteOrVolumeEvent_triggersCallback()
5505             throws Exception {
5506         CarAudioService service = setUpAudioService();
5507         TestCarVolumeEventCallback volumeEventCallback =
5508                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5509         service.registerCarVolumeEventCallback(volumeEventCallback);
5510 
5511         service.onVolumeGroupEvent(List.of(mTestCarZoneReconfigurationEvent));
5512 
5513         expectWithMessage("Volume event callback reception status")
5514                 .that(volumeEventCallback.waitForCallback()).isTrue();
5515         verify(mCarVolumeCallbackHandler, never())
5516                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
5517         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
5518         expectWithMessage("Volume events count after reconfiguration event")
5519                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5520         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5521         expectWithMessage("Volume event type after reconfiguration event")
5522                 .that(groupEvent.getEventTypes())
5523                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED);
5524         expectWithMessage("Volume group infos after reconfiguration event")
5525                 .that(groupEvent.getCarVolumeGroupInfos())
5526                 .containsExactly(mTestPrimaryZoneUmMutedVolueInfo0);
5527     }
5528 
5529     @Test
setMuted_whenUnmuted_onActivation_triggersCallback()5530     public void setMuted_whenUnmuted_onActivation_triggersCallback() throws Exception {
5531         CarAudioService service = setUpAudioService();
5532         TestCarVolumeEventCallback volumeEventCallback =
5533                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5534         service.registerCarVolumeEventCallback(volumeEventCallback);
5535 
5536         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5537                 /* mute= */ true, TEST_FLAGS);
5538 
5539         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
5540                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
5541         expectWithMessage("Volume event callback reception status after mute")
5542                 .that(volumeEventCallback.waitForCallback()).isTrue();
5543         expectWithMessage("Volume events count after mute")
5544                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5545         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5546         expectWithMessage("Volume event type after mute")
5547                 .that(groupEvent.getEventTypes())
5548                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
5549         expectWithMessage("Volume group infos after mute")
5550                 .that(groupEvent.getCarVolumeGroupInfos())
5551                 .containsExactly(mTestPrimaryZoneVolumeInfo0);
5552     }
5553 
5554     @Test
setMuted_whenUnmuted_onDeactivation_doesNotTriggerCallback()5555     public void setMuted_whenUnmuted_onDeactivation_doesNotTriggerCallback() throws Exception {
5556         CarAudioService service = setUpAudioService();
5557         TestCarVolumeEventCallback volumeEventCallback =
5558                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5559         service.registerCarVolumeEventCallback(volumeEventCallback);
5560 
5561         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5562                 /* mute= */ false, TEST_FLAGS);
5563 
5564         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
5565         expectWithMessage("Volume event callback reception status")
5566                 .that(volumeEventCallback.waitForCallback()).isFalse();
5567     }
5568 
5569     @Test
setMuted_whenMuted_onDeactivation_triggersCallback()5570     public void setMuted_whenMuted_onDeactivation_triggersCallback() throws Exception {
5571         CarAudioService service = setUpAudioService();
5572         TestCarVolumeEventCallback volumeEventCallback =
5573                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5574         service.registerCarVolumeEventCallback(volumeEventCallback);
5575         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5576                 /* mute= */ true, TEST_FLAGS);
5577         resetVolumeCallbacks(volumeEventCallback);
5578 
5579         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5580                 /* mute= */ false, TEST_FLAGS);
5581 
5582         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
5583                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
5584         expectWithMessage("Volume event callback reception status after unmute")
5585                 .that(volumeEventCallback.waitForCallback()).isTrue();
5586         expectWithMessage("Volume events count after mute")
5587                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5588         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5589         expectWithMessage("Volume event type after unmute")
5590                 .that(groupEvent.getEventTypes())
5591                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
5592         expectWithMessage("Volume group infos after unmute")
5593                 .that(groupEvent.getCarVolumeGroupInfos())
5594                 .containsExactly(mTestPrimaryZoneUmMutedVolueInfo0);
5595     }
5596 
5597     @Test
setUnmuted_whenMutedBySystem_triggersCallback()5598     public void setUnmuted_whenMutedBySystem_triggersCallback() throws Exception {
5599         CarAudioService service = setUpAudioService();
5600         TestCarVolumeEventCallback volumeEventCallback =
5601                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5602         service.registerCarVolumeEventCallback(volumeEventCallback);
5603         CarAudioGainConfigInfo primaryAudioZoneCarGain = createCarAudioGainConfigInfo(
5604                 PRIMARY_AUDIO_ZONE, MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
5605         HalAudioGainCallback halAudioGainCallback = getHalAudioGainCallback();
5606         halAudioGainCallback.onAudioDeviceGainsChanged(List.of(Reasons.TCU_MUTE),
5607                 List.of(primaryAudioZoneCarGain));
5608         resetVolumeCallbacks(volumeEventCallback);
5609 
5610         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5611                 /* mute= */ false, TEST_FLAGS);
5612 
5613         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
5614                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
5615         expectWithMessage("Volume event callback reception status after unmute when muted by "
5616                 + "system").that(volumeEventCallback.waitForCallback()).isTrue();
5617         expectWithMessage("Volume events count after mute when muted by system")
5618                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5619         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5620         expectWithMessage("Volume event type after unmute when muted by system")
5621                 .that(groupEvent.getEventTypes())
5622                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
5623     }
5624 
5625     @Test
setMuted_whenMutedByApiAndSystem_doesNotTriggerCallback()5626     public void setMuted_whenMutedByApiAndSystem_doesNotTriggerCallback() throws Exception {
5627         CarAudioService service = setUpAudioService();
5628         TestCarVolumeEventCallback volumeEventCallback =
5629                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5630         service.registerCarVolumeEventCallback(volumeEventCallback);
5631         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0, /* mute= */ true,
5632                 TEST_FLAGS);
5633         resetVolumeCallbacks(volumeEventCallback);
5634         CarAudioGainConfigInfo primaryAudioZoneCarGain = createCarAudioGainConfigInfo(
5635                 PRIMARY_AUDIO_ZONE, MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
5636         HalAudioGainCallback halAudioGainCallback = getHalAudioGainCallback();
5637         halAudioGainCallback.onAudioDeviceGainsChanged(List.of(Reasons.TCU_MUTE),
5638                 List.of(primaryAudioZoneCarGain));
5639         resetVolumeCallbacks(volumeEventCallback);
5640 
5641         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0, /* mute= */ true,
5642                 TEST_FLAGS);
5643 
5644         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
5645         expectWithMessage("Volume event callback reception status after mute when muted by "
5646                 + "both API and system").that(volumeEventCallback.waitForCallback()).isFalse();
5647     }
5648 
5649     @Test
setMuted_whenMuted_onActivation_doesNotTriggerCallback()5650     public void setMuted_whenMuted_onActivation_doesNotTriggerCallback() throws Exception {
5651         CarAudioService service = setUpAudioService();
5652         TestCarVolumeEventCallback volumeEventCallback =
5653                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5654         service.registerCarVolumeEventCallback(volumeEventCallback);
5655         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5656                 /* mute= */ true, TEST_FLAGS);
5657         resetVolumeCallbacks(volumeEventCallback);
5658 
5659         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5660                 /* mute= */ true, TEST_FLAGS);
5661 
5662         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
5663         expectWithMessage("Volume event callback reception status")
5664                 .that(volumeEventCallback.waitForCallback()).isFalse();
5665     }
5666 
5667     @Test
handleActivationVolumeWithAudioAttributes_withMultipleAudioAttributes()5668     public void handleActivationVolumeWithAudioAttributes_withMultipleAudioAttributes()
5669             throws Exception {
5670         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5671         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5672         TestCarVolumeEventCallback volumeEventCallback =
5673                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5674         service.registerCarVolumeEventCallback(volumeEventCallback);
5675         int currentConfigId = service.getCurrentAudioZoneConfigInfo(PRIMARY_AUDIO_ZONE)
5676                 .getConfigId();
5677         int mediaMaxActivationGainIndex = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5678                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex();
5679         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5680                 TEST_PRIMARY_ZONE_GROUP_0, mediaMaxActivationGainIndex + 1);
5681         int navMinActivationGainIndex = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5682                 TEST_PRIMARY_ZONE_GROUP_1).getMinActivationVolumeGainIndex();
5683         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5684                 TEST_PRIMARY_ZONE_GROUP_1, navMinActivationGainIndex - 1);
5685 
5686         service.handleActivationVolumeWithActivationInfos(List.of(
5687                 new CarAudioPlaybackMonitor.ActivationInfo(TEST_PRIMARY_ZONE_GROUP_0,
5688                         CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_BOOT),
5689                 new CarAudioPlaybackMonitor.ActivationInfo(TEST_PRIMARY_ZONE_GROUP_1,
5690                         CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_BOOT)),
5691                 PRIMARY_AUDIO_ZONE, currentConfigId);
5692 
5693         expectWithMessage("Media volume for above-activation gain index")
5694                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
5695                 .isEqualTo(mediaMaxActivationGainIndex);
5696         expectWithMessage("Navigation volume for below-activation gain index")
5697                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1))
5698                 .isEqualTo(navMinActivationGainIndex);
5699         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5700                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
5701         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5702                 eq(TEST_PRIMARY_ZONE_GROUP_1), anyInt());
5703         expectWithMessage("Volume event callback for volume out of activation gain index range")
5704                 .that(volumeEventCallback.waitForCallback()).isTrue();
5705         expectWithMessage("Volume events count after activation gain index adjustment")
5706                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5707         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5708         expectWithMessage("Volume event type after activation gain index adjustment")
5709                 .that(groupEvent.getEventTypes())
5710                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
5711         expectWithMessage("Volume group info after activation gain index adjustment"
5712                 + " adjustment").that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
5713                         service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0),
5714                 service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1));
5715     }
5716 
5717     @Test
handleActivationVolumeWithAudioAttributes_withNonCurrentZoneConfig()5718     public void handleActivationVolumeWithAudioAttributes_withNonCurrentZoneConfig()
5719             throws Exception {
5720         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5721         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5722         TestCarVolumeEventCallback volumeEventCallback =
5723                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5724         service.registerCarVolumeEventCallback(volumeEventCallback);
5725         int nonCurrentConfigId = getZoneConfigToSwitch(service, TEST_REAR_LEFT_ZONE_ID)
5726                 .getConfigId();
5727         int mediaGainIndexAboveMaxActivation = service.getVolumeGroupInfo(TEST_REAR_LEFT_ZONE_ID,
5728                 SECONDARY_ZONE_VOLUME_GROUP_ID).getMaxActivationVolumeGainIndex() + 1;
5729         setVolumeForGroup(service, volumeEventCallback, TEST_REAR_LEFT_ZONE_ID,
5730                 SECONDARY_ZONE_VOLUME_GROUP_ID, mediaGainIndexAboveMaxActivation);
5731 
5732         service.handleActivationVolumeWithActivationInfos(List.of(
5733                         new CarAudioPlaybackMonitor.ActivationInfo(TEST_REAR_LEFT_ZONE_ID,
5734                                 CarActivationVolumeConfig.ACTIVATION_VOLUME_ON_BOOT)),
5735                 TEST_REAR_LEFT_ZONE_ID, nonCurrentConfigId);
5736 
5737         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(TEST_REAR_LEFT_ZONE_ID),
5738                 eq(SECONDARY_ZONE_VOLUME_GROUP_ID), anyInt());
5739         expectWithMessage("Volume event callback for non-current zone config activation volume")
5740                 .that(volumeEventCallback.waitForCallback()).isFalse();
5741     }
5742 
5743     @Test
onPlaybackConfigChanged_withActivationVolumeFlagDisabled()5744     public void onPlaybackConfigChanged_withActivationVolumeFlagDisabled() throws Exception {
5745         mSetFlagsRule.disableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5746         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5747         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5748         TestCarVolumeEventCallback volumeEventCallback =
5749                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5750         service.registerCarVolumeEventCallback(volumeEventCallback);
5751         int gainIndex = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5752                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex() + 1;
5753         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5754                 TEST_PRIMARY_ZONE_GROUP_0, gainIndex);
5755 
5756         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5757                 .setUsage(USAGE_MEDIA).setDeviceAddress(MEDIA_TEST_DEVICE)
5758                 .setClientUid(TEST_PLAYBACK_UID).build()));
5759 
5760         expectWithMessage("Playback group volume with activation volume flag disabled")
5761                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
5762                 .isEqualTo(gainIndex);
5763         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5764                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
5765         expectWithMessage("No volume event callback for activation volume flag disabled")
5766                 .that(volumeEventCallback.waitForCallback()).isFalse();
5767     }
5768 
5769     @Test
onPlaybackConfigChanged_withActivationVolumeFeatureDisabled()5770     public void onPlaybackConfigChanged_withActivationVolumeFeatureDisabled() throws Exception {
5771         mSetFlagsRule.disableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5772         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ false);
5773         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5774         TestCarVolumeEventCallback volumeEventCallback =
5775                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5776         service.registerCarVolumeEventCallback(volumeEventCallback);
5777         int gainIndex = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5778                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex() + 1;
5779         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5780                 TEST_PRIMARY_ZONE_GROUP_0, gainIndex);
5781 
5782         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5783                 .setUsage(USAGE_MEDIA).setDeviceAddress(MEDIA_TEST_DEVICE)
5784                 .setClientUid(TEST_PLAYBACK_UID).build()));
5785 
5786         expectWithMessage("Playback group volume with activation volume feature disabled")
5787                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
5788                 .isEqualTo(gainIndex);
5789         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5790                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
5791         expectWithMessage("No volume event callback for activation volume feature disabled")
5792                 .that(volumeEventCallback.waitForCallback()).isFalse();
5793     }
5794 
5795     @Test
onPlaybackConfigChanged_withVolumeAboveMaxActivationVolume()5796     public void onPlaybackConfigChanged_withVolumeAboveMaxActivationVolume() throws Exception {
5797         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5798         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5799         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5800         TestCarVolumeEventCallback volumeEventCallback =
5801                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5802         service.registerCarVolumeEventCallback(volumeEventCallback);
5803         int maxActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5804                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex();
5805         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5806                 TEST_PRIMARY_ZONE_GROUP_0, maxActivationVolume + 1);
5807 
5808         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5809                 .setUsage(USAGE_MEDIA).setDeviceAddress(MEDIA_TEST_DEVICE)
5810                 .setClientUid(TEST_PLAYBACK_UID).build()));
5811 
5812         expectWithMessage("Playback group volume for above-activation gain index")
5813                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
5814                 .isEqualTo(maxActivationVolume);
5815         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5816                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
5817         expectWithMessage("Volume event callback for above-activation gain index")
5818                 .that(volumeEventCallback.waitForCallback()).isTrue();
5819         expectWithMessage("Volume events count after above-activation gain index adjustment")
5820                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5821         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5822         expectWithMessage("Volume event type after above-activation gain index adjustment")
5823                 .that(groupEvent.getEventTypes())
5824                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
5825         expectWithMessage("Volume group info after above-activation gain index adjustment")
5826                 .that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
5827                         service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0));
5828     }
5829 
5830     @Test
onPlaybackConfigChanged_withVolumeBelowMinActivationVolume()5831     public void onPlaybackConfigChanged_withVolumeBelowMinActivationVolume() throws Exception {
5832         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5833         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5834         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5835         TestCarVolumeEventCallback volumeEventCallback =
5836                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5837         service.registerCarVolumeEventCallback(volumeEventCallback);
5838         int minActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5839                 TEST_PRIMARY_ZONE_GROUP_1).getMinActivationVolumeGainIndex();
5840         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5841                 TEST_PRIMARY_ZONE_GROUP_1, minActivationVolume - 1);
5842 
5843         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5844                 .setUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)
5845                 .setDeviceAddress(NAVIGATION_TEST_DEVICE).setClientUid(TEST_PLAYBACK_UID)
5846                 .build()));
5847 
5848         expectWithMessage("Playback group volume for below-activation gain index")
5849                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1))
5850                 .isEqualTo(minActivationVolume);
5851         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5852                 eq(TEST_PRIMARY_ZONE_GROUP_1), anyInt());
5853         expectWithMessage("Volume event callback for below-activation gain index")
5854                 .that(volumeEventCallback.waitForCallback()).isTrue();
5855         expectWithMessage("Volume events count after below-activation gain index adjustment")
5856                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5857         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5858         expectWithMessage("Volume event type after below-activation gain index adjustment")
5859                 .that(groupEvent.getEventTypes())
5860                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
5861         expectWithMessage("Volume group info after below-activation gain index adjustment")
5862                 .that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
5863                         service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1));
5864     }
5865 
5866     @Test
onPlaybackConfigChanged_withVolumeInActivationVolumeRange()5867     public void onPlaybackConfigChanged_withVolumeInActivationVolumeRange() throws Exception {
5868         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5869         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5870         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5871         TestCarVolumeEventCallback volumeEventCallback =
5872                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5873         service.registerCarVolumeEventCallback(volumeEventCallback);
5874         int gainIndexInActivationVolumeRange = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5875                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex() - 1;
5876         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5877                 TEST_PRIMARY_ZONE_GROUP_0, gainIndexInActivationVolumeRange);
5878 
5879         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5880                 .setUsage(USAGE_MEDIA).setDeviceAddress(MEDIA_TEST_DEVICE)
5881                 .setClientUid(TEST_PLAYBACK_UID).build()));
5882 
5883         expectWithMessage("Playback group volume in activation volume range")
5884                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
5885                 .isEqualTo(gainIndexInActivationVolumeRange);
5886         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5887                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
5888         expectWithMessage("No volume event callback for no activation volume adjustment")
5889                 .that(volumeEventCallback.waitForCallback()).isFalse();
5890     }
5891 
5892     @Test
onPlaybackConfigChanged_withVolumeGroupMute()5893     public void onPlaybackConfigChanged_withVolumeGroupMute() throws Exception {
5894         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5895         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5896         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5897         TestCarVolumeEventCallback volumeEventCallback =
5898                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5899         service.registerCarVolumeEventCallback(volumeEventCallback);
5900         int gainIndexAboveActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
5901                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex() + 1;
5902         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
5903                 TEST_PRIMARY_ZONE_GROUP_0, gainIndexAboveActivationVolume);
5904         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
5905                 /* mute= */ true, TEST_FLAGS);
5906         resetVolumeCallbacks(volumeEventCallback);
5907 
5908         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5909                 .setUsage(USAGE_MEDIA).setDeviceAddress(MEDIA_TEST_DEVICE)
5910                 .setClientUid(TEST_PLAYBACK_UID).build()));
5911 
5912         expectWithMessage("Mute state with playback volume higher than max activation volume")
5913                 .that(service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
5914                 .isTrue();
5915         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
5916                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
5917         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
5918                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
5919         expectWithMessage("No volume event callback for activation volume when mute")
5920                 .that(volumeEventCallback.waitForCallback()).isFalse();
5921     }
5922 
5923     @Test
onPlaybackConfigChanged_afterZoneConfigSwitched()5924     public void onPlaybackConfigChanged_afterZoneConfigSwitched() throws Exception {
5925         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5926         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5927         SwitchAudioZoneConfigCallbackImpl zoneConfigSwitchCallback =
5928                 new SwitchAudioZoneConfigCallbackImpl();
5929         assignOccupantToAudioZones();
5930         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5931         TestCarVolumeEventCallback volumeEventCallback =
5932                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5933         service.registerCarVolumeEventCallback(volumeEventCallback);
5934         int maxActivationVolume = service.getVolumeGroupInfo(TEST_REAR_LEFT_ZONE_ID,
5935                 TEST_SECONDARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex();
5936         setVolumeForGroup(service, volumeEventCallback, TEST_REAR_LEFT_ZONE_ID,
5937                 TEST_SECONDARY_ZONE_GROUP_0, maxActivationVolume + 1);
5938         createActivePlayback(callback, volumeEventCallback, USAGE_MEDIA,
5939                 SECONDARY_TEST_DEVICE_CONFIG_0, TEST_PLAYBACK_UID);
5940         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(service,
5941                 TEST_REAR_LEFT_ZONE_ID);
5942         service.switchZoneToConfig(zoneConfigSwitchTo, zoneConfigSwitchCallback);
5943         zoneConfigSwitchCallback.waitForCallback();
5944         resetVolumeCallbacks(volumeEventCallback);
5945         maxActivationVolume = service.getVolumeGroupInfo(TEST_REAR_LEFT_ZONE_ID,
5946                 TEST_SECONDARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex();
5947         setVolumeForGroup(service, volumeEventCallback, TEST_REAR_LEFT_ZONE_ID,
5948                 TEST_SECONDARY_ZONE_GROUP_0, maxActivationVolume + 1);
5949 
5950         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5951                 .setUsage(USAGE_MEDIA).setDeviceAddress(SECONDARY_TEST_DEVICE_CONFIG_1_0)
5952                 .setClientUid(TEST_PLAYBACK_UID).build()));
5953 
5954         expectWithMessage("Playback group volume after zone config switch")
5955                 .that(service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0))
5956                 .isEqualTo(maxActivationVolume);
5957         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(TEST_REAR_LEFT_ZONE_ID),
5958                 eq(TEST_SECONDARY_ZONE_GROUP_0), anyInt());
5959         expectWithMessage("Volume event callback after zone config switch")
5960                 .that(volumeEventCallback.waitForCallback()).isTrue();
5961         expectWithMessage("Volume events count after zone config switch")
5962                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
5963         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
5964         expectWithMessage("Volume event type after zone config switch")
5965                 .that(groupEvent.getEventTypes())
5966                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
5967         expectWithMessage("Volume group info after zone config switch")
5968                 .that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
5969                         service.getVolumeGroupInfo(TEST_REAR_LEFT_ZONE_ID,
5970                                 TEST_SECONDARY_ZONE_GROUP_0));
5971     }
5972 
5973     @Test
onPlaybackConfigChanged_afterOccupantZoneConfigChanged()5974     public void onPlaybackConfigChanged_afterOccupantZoneConfigChanged() throws Exception {
5975         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
5976         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
5977         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
5978         TestCarVolumeEventCallback volumeEventCallback =
5979                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
5980         service.registerCarVolumeEventCallback(volumeEventCallback);
5981         int maxActivationVolume = service.getVolumeGroupInfo(TEST_REAR_LEFT_ZONE_ID,
5982                 TEST_SECONDARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex();
5983         setVolumeForGroup(service, volumeEventCallback, TEST_REAR_LEFT_ZONE_ID,
5984                 TEST_SECONDARY_ZONE_GROUP_0, maxActivationVolume + 1);
5985         createActivePlayback(callback, volumeEventCallback, USAGE_MEDIA,
5986                 SECONDARY_TEST_DEVICE_CONFIG_0, TEST_PLAYBACK_UID);
5987         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
5988                 .setUsage(USAGE_MEDIA).setDeviceAddress(SECONDARY_TEST_DEVICE_CONFIG_0)
5989                 .setClientUid(TEST_PLAYBACK_UID).setInactive().build()));
5990         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
5991         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
5992                 .thenReturn(TEST_REAR_LEFT_USER_ID, TEST_REAR_RIGHT_USER_ID);
5993         ICarOccupantZoneCallback occupantZoneCallback = getOccupantZoneCallback();
5994         occupantZoneCallback.onOccupantZoneConfigChanged(
5995                 CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
5996         setVolumeForGroup(service, volumeEventCallback, TEST_REAR_LEFT_ZONE_ID,
5997                 TEST_SECONDARY_ZONE_GROUP_0, maxActivationVolume + 1);
5998 
5999         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
6000                 .setUsage(USAGE_MEDIA).setDeviceAddress(SECONDARY_TEST_DEVICE_CONFIG_0)
6001                 .setClientUid(TEST_PLAYBACK_UID).build()));
6002 
6003         expectWithMessage("Playback group volume after zone user switch")
6004                 .that(service.getGroupVolume(TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0))
6005                 .isEqualTo(maxActivationVolume);
6006         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(TEST_REAR_LEFT_ZONE_ID),
6007                 eq(TEST_SECONDARY_ZONE_GROUP_0), anyInt());
6008         expectWithMessage("Volume event callback after zone user switch")
6009                 .that(volumeEventCallback.waitForCallback()).isTrue();
6010         expectWithMessage("Volume events count after zone user switch")
6011                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
6012         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
6013         expectWithMessage("Volume event type after zone user switch")
6014                 .that(groupEvent.getEventTypes())
6015                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
6016         expectWithMessage("Volume group info after zone user switch")
6017                 .that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
6018                         service.getVolumeGroupInfo(TEST_REAR_LEFT_ZONE_ID,
6019                                 TEST_SECONDARY_ZONE_GROUP_0));
6020     }
6021 
6022     @Test
setVolumeGroupMute_withUnMuteAfterPlaybackConfigChangedWhenMute()6023     public void setVolumeGroupMute_withUnMuteAfterPlaybackConfigChangedWhenMute() throws Exception {
6024         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
6025         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6026         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
6027         TestCarVolumeEventCallback volumeEventCallback =
6028                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6029         service.registerCarVolumeEventCallback(volumeEventCallback);
6030         int maxActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
6031                 TEST_PRIMARY_ZONE_GROUP_0).getMaxActivationVolumeGainIndex();
6032         int gainIndexAboveActivationVolume = maxActivationVolume + 1;
6033         service.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
6034                 gainIndexAboveActivationVolume, TEST_FLAGS);
6035         resetVolumeCallbacks(volumeEventCallback);
6036         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
6037                 /* mute= */ true, TEST_FLAGS);
6038         resetVolumeCallbacks(volumeEventCallback);
6039         createActivePlayback(callback, volumeEventCallback, USAGE_MEDIA, MEDIA_TEST_DEVICE,
6040                 TEST_PLAYBACK_UID);
6041 
6042         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
6043                 /* mute= */ false, TEST_FLAGS);
6044 
6045         expectWithMessage("Mute state after playback changed and unmute")
6046                 .that(service.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
6047                 .isFalse();
6048         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
6049                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
6050         expectWithMessage("Volume gain index after playback changed and unmute")
6051                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
6052                 .isEqualTo(maxActivationVolume);
6053         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
6054                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
6055         expectWithMessage("Volume event callback for activation volume adjustment and unmute")
6056                 .that(volumeEventCallback.waitForCallback()).isTrue();
6057         expectWithMessage("Volume events count for activation volume adjustment and unmute")
6058                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
6059         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
6060         expectWithMessage("Volume event type after activation volume adjustment and unmute")
6061                 .that(groupEvent.getEventTypes())
6062                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
6063         expectWithMessage("Volume group info after activation volume adjustment and unmute")
6064                 .that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
6065                         service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0));
6066     }
6067 
6068     @Test
requestHalAudioFocus_withVolumeAboveActivationVolume_adjustsToActivationVolume()6069     public void requestHalAudioFocus_withVolumeAboveActivationVolume_adjustsToActivationVolume()
6070             throws Exception {
6071         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
6072         when(mAudioManager.requestAudioFocus(any())).thenReturn(
6073                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
6074         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6075         TestCarVolumeEventCallback volumeEventCallback =
6076                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6077         service.registerCarVolumeEventCallback(volumeEventCallback);
6078         int maxActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
6079                 TEST_PRIMARY_ZONE_GROUP_1).getMaxActivationVolumeGainIndex();
6080         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
6081                 TEST_PRIMARY_ZONE_GROUP_1, maxActivationVolume + 1);
6082 
6083         requestHalAudioFocus(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
6084 
6085         expectWithMessage("Playback group volume for HAL focus and above-activation gain index")
6086                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1))
6087                 .isEqualTo(maxActivationVolume);
6088         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
6089                 eq(TEST_PRIMARY_ZONE_GROUP_1), anyInt());
6090         expectWithMessage("Volume event callback for HAL focus and above-activation gain index")
6091                 .that(volumeEventCallback.waitForCallback()).isTrue();
6092         expectWithMessage("Volume events count after HAL focus and above-activation gain"
6093                 + " index adjustment").that(volumeEventCallback.getVolumeGroupEvents())
6094                 .hasSize(1);
6095         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
6096         expectWithMessage("Volume event type after HAL focus and above-activation gain"
6097                 + " index adjustment").that(groupEvent.getEventTypes())
6098                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
6099         expectWithMessage("Volume group info after HAL focus and above-activation gain"
6100                 + " index adjustment").that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
6101                         service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1));
6102     }
6103 
6104     @Test
requestHalAudioFocus_withVolumeInActivationVolumeRange()6105     public void requestHalAudioFocus_withVolumeInActivationVolumeRange()
6106             throws Exception {
6107         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
6108         when(mAudioManager.requestAudioFocus(any())).thenReturn(
6109                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
6110         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6111         TestCarVolumeEventCallback volumeEventCallback =
6112                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6113         service.registerCarVolumeEventCallback(volumeEventCallback);
6114         int gainIndexInActivationVolumeRange = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
6115                 TEST_PRIMARY_ZONE_GROUP_1).getMaxActivationVolumeGainIndex() - 1;
6116         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE,
6117                 TEST_PRIMARY_ZONE_GROUP_1, gainIndexInActivationVolumeRange);
6118 
6119         requestHalAudioFocus(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
6120 
6121         expectWithMessage("Playback group volume for HAL focus in activation volume index range")
6122                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1))
6123                 .isEqualTo(gainIndexInActivationVolumeRange);
6124         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
6125                 eq(TEST_PRIMARY_ZONE_GROUP_1), anyInt());
6126         expectWithMessage("No volume event callback for HAL focus in activation volume"
6127                 + " index range").that(volumeEventCallback.waitForCallback()).isFalse();
6128     }
6129 
6130     @Test
onCallStateChanged_withOffHookStateAndVolumeBelowMinActivationVolume()6131     public void onCallStateChanged_withOffHookStateAndVolumeBelowMinActivationVolume()
6132             throws Exception {
6133         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
6134         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6135         TelephonyCallback.CallStateListener callStateListener = getCallStateListener();
6136         TestCarVolumeEventCallback volumeEventCallback =
6137                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6138         service.registerCarVolumeEventCallback(volumeEventCallback);
6139         int voiceGroupId = service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
6140                 USAGE_VOICE_COMMUNICATION);
6141         int minActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
6142                 TEST_PRIMARY_ZONE_GROUP_1).getMinActivationVolumeGainIndex();
6143         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE, voiceGroupId,
6144                 minActivationVolume - 1);
6145 
6146         callStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_OFFHOOK);
6147 
6148         expectWithMessage("Playback group volume for off-hook and below-activation gain index")
6149                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, voiceGroupId))
6150                 .isEqualTo(minActivationVolume);
6151         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
6152                 eq(voiceGroupId), anyInt());
6153         expectWithMessage("Volume event callback for off-hook and below-activation gain index")
6154                 .that(volumeEventCallback.waitForCallback()).isTrue();
6155         expectWithMessage("Volume events count for off-hook after below-activation gain index "
6156                 + "adjustment").that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
6157         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
6158         expectWithMessage("Volume event type for off-hook after below-activation gain index "
6159                 + "adjustment").that(groupEvent.getEventTypes())
6160                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
6161         expectWithMessage("Volume group info for off-hook after below-activation gain index "
6162                 + "adjustment").that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
6163                         service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, voiceGroupId));
6164     }
6165 
6166     @Test
onCallStateChanged_withRingingStateAndVolumeBelowMinActivationVolume()6167     public void onCallStateChanged_withRingingStateAndVolumeBelowMinActivationVolume()
6168             throws Exception {
6169         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
6170         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6171         TelephonyCallback.CallStateListener callStateListener = getCallStateListener();
6172         TestCarVolumeEventCallback volumeEventCallback =
6173                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6174         service.registerCarVolumeEventCallback(volumeEventCallback);
6175         int ringGroupId = service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
6176                 USAGE_NOTIFICATION_RINGTONE);
6177         int minActivationVolume = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
6178                 TEST_PRIMARY_ZONE_GROUP_1).getMinActivationVolumeGainIndex();
6179         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE, ringGroupId,
6180                 minActivationVolume - 1);
6181 
6182         callStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING);
6183 
6184         expectWithMessage("Playback group volume for ringing and below-activation gain index")
6185                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, ringGroupId))
6186                 .isEqualTo(minActivationVolume);
6187         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
6188                 eq(ringGroupId), anyInt());
6189         expectWithMessage("Volume event callback for ringing and below-activation gain index")
6190                 .that(volumeEventCallback.waitForCallback()).isTrue();
6191         expectWithMessage("Volume events count for ringing after below-activation gain index "
6192                 + "adjustment").that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
6193         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
6194         expectWithMessage("Volume event type for ringing after below-activation gain index "
6195                 + "adjustment").that(groupEvent.getEventTypes())
6196                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
6197         expectWithMessage("Volume group info for ringing after below-activation gain index "
6198                 + "adjustment").that(groupEvent.getCarVolumeGroupInfos()).containsExactly(
6199                 service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, ringGroupId));
6200     }
6201 
6202     @Test
onCallStateChanged_withRingingStateAndWithinActivationVolumeRange()6203     public void onCallStateChanged_withRingingStateAndWithinActivationVolumeRange()
6204             throws Exception {
6205         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_MIN_MAX_ACTIVATION_VOLUME);
6206         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6207         TelephonyCallback.CallStateListener callStateListener = getCallStateListener();
6208         TestCarVolumeEventCallback volumeEventCallback =
6209                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6210         service.registerCarVolumeEventCallback(volumeEventCallback);
6211         int ringGroupId = service.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
6212                 USAGE_NOTIFICATION_RINGTONE);
6213         int gainIndexInActivationVolumeRange = service.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
6214                 TEST_PRIMARY_ZONE_GROUP_0).getMinActivationVolumeGainIndex() + 1;
6215         setVolumeForGroup(service, volumeEventCallback, PRIMARY_AUDIO_ZONE, ringGroupId,
6216                 gainIndexInActivationVolumeRange);
6217 
6218         callStateListener.onCallStateChanged(TelephonyManager.CALL_STATE_RINGING);
6219 
6220         expectWithMessage("Playback group volume for ring state in activation volume index range")
6221                 .that(service.getGroupVolume(PRIMARY_AUDIO_ZONE, ringGroupId))
6222                 .isEqualTo(gainIndexInActivationVolumeRange);
6223         verify(mCarVolumeCallbackHandler, never()).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
6224                 eq(ringGroupId), anyInt());
6225         expectWithMessage("No volume event callback for ring state in activation volume"
6226                 + " index range").that(volumeEventCallback.waitForCallback()).isFalse();
6227     }
6228 
6229     @Test
unregisterCarVolumeEventCallback_forCarVolumeEventHandler()6230     public void unregisterCarVolumeEventCallback_forCarVolumeEventHandler() throws Exception {
6231         CarAudioService service = setUpAudioServiceWithMinMaxActivationVolume(/* enabled= */ true);
6232         TestCarVolumeEventCallback volumeEventCallback =
6233                 new TestCarVolumeEventCallback(TEST_CALLBACK_TIMEOUT_MS);
6234         service.registerCarVolumeEventCallback(volumeEventCallback);
6235 
6236         service.unregisterCarVolumeEventCallback(volumeEventCallback);
6237 
6238         service.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
6239                 /* mute= */ true, TEST_FLAGS);
6240         expectWithMessage("Volume event callback reception status with callback unregistered")
6241                 .that(volumeEventCallback.waitForCallback()).isFalse();
6242     }
waitForInternalCallback()6243     private void waitForInternalCallback() throws Exception {
6244         CountDownLatch latch = new CountDownLatch(1);
6245         mHandler.post(latch::countDown);
6246         latch.await(TEST_ZONE_CONFIG_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6247     }
6248 
setUpCarAudioServiceWithoutZoneMapping()6249     private CarAudioService setUpCarAudioServiceWithoutZoneMapping() throws Exception {
6250         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_without_zone_mapping);
6251         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6252         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
6253                 .thenReturn(SUCCESS);
6254         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext, mAudioManager,
6255                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6256                 /* audioFadeConfigurationPath= */ null);
6257         noZoneMappingAudioService.init();
6258         return noZoneMappingAudioService;
6259     }
6260 
setUpAudioService()6261     private CarAudioService setUpAudioService() throws Exception {
6262         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration);
6263         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6264         CarAudioService service = new CarAudioService(mMockContext, mAudioManager,
6265                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6266                 mTempCarAudioFadeConfigFile.getFile().getAbsolutePath());
6267         service.init();
6268         return service;
6269     }
6270 
setUpAudioServiceWithoutInit()6271     private CarAudioService setUpAudioServiceWithoutInit() throws Exception {
6272         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration);
6273         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6274         CarAudioService service = new CarAudioService(mMockContext, mAudioManager,
6275                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6276                 mTempCarAudioFadeConfigFile.getFile().getAbsolutePath());
6277         return service;
6278     }
6279 
setUpAudioServiceWithoutDynamicRouting()6280     private CarAudioService setUpAudioServiceWithoutDynamicRouting() throws Exception {
6281         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration);
6282         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6283         when(mMockResources.getBoolean(audioUseDynamicRouting)).thenReturn(false);
6284         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext, mAudioManager,
6285                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6286                 /* audioFadeConfigurationPath= */ null);
6287         nonDynamicAudioService.init();
6288         return nonDynamicAudioService;
6289     }
6290 
setUpAudioServiceWithDisabledResource(int resource)6291     private CarAudioService setUpAudioServiceWithDisabledResource(int resource) throws Exception {
6292         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration);
6293         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6294         when(mMockResources.getBoolean(resource)).thenReturn(false);
6295         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext, mAudioManager,
6296                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6297                 mTempCarAudioFadeConfigFile.getFile().getAbsolutePath());
6298         nonDynamicAudioService.init();
6299         return nonDynamicAudioService;
6300     }
6301 
getRegisteredZoneConfigCallback( CarAudioService audioServiceWithDynamicDevices)6302     private static TestAudioZoneConfigurationsChangeCallback getRegisteredZoneConfigCallback(
6303             CarAudioService audioServiceWithDynamicDevices) {
6304         TestAudioZoneConfigurationsChangeCallback configCallback =
6305                 new TestAudioZoneConfigurationsChangeCallback();
6306         audioServiceWithDynamicDevices.registerAudioZoneConfigsChangeCallback(configCallback);
6307         return configCallback;
6308     }
6309 
captureAudioDeviceCallback()6310     private AudioDeviceCallback captureAudioDeviceCallback() {
6311         ArgumentCaptor<AudioDeviceCallback> captor =
6312                 ArgumentCaptor.forClass(AudioDeviceCallback.class);
6313         verify(mAudioManager).registerAudioDeviceCallback(captor.capture(), any());
6314         return captor.getValue();
6315     }
6316 
setUpAudioServiceWithDynamicDevices()6317     private CarAudioService setUpAudioServiceWithDynamicDevices() throws Exception {
6318         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_using_dynamic_routing);
6319         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6320         return setUpAudioServiceWithDynamicDevices(mTempCarAudioConfigFile,
6321                 mTempCarAudioFadeConfigFile);
6322     }
6323 
setUpAudioServiceWithDynamicDevices(TemporaryFile fileAudio, TemporaryFile fileFade)6324     private CarAudioService setUpAudioServiceWithDynamicDevices(TemporaryFile fileAudio,
6325             TemporaryFile fileFade) {
6326         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_DYNAMIC_DEVICES);
6327         when(mMockResources.getBoolean(audioUseCoreVolume)).thenReturn(true);
6328         when(mMockResources.getBoolean(audioUseCoreRouting)).thenReturn(false);
6329         CarAudioService audioServiceWithDynamicDevices = new CarAudioService(mMockContext,
6330                 mAudioManager, fileAudio.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6331                 fileFade.getFile().getAbsolutePath());
6332         return audioServiceWithDynamicDevices;
6333     }
6334 
setUpAudioServiceWithMinMaxActivationVolume(boolean enabled)6335     private CarAudioService setUpAudioServiceWithMinMaxActivationVolume(boolean enabled)
6336             throws Exception {
6337         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_using_activation_volumes);
6338         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6339         when(mMockResources.getBoolean(audioUseMinMaxActivationVolume)).thenReturn(enabled);
6340         CarAudioService service = new CarAudioService(mMockContext, mAudioManager,
6341                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6342                 mTempCarAudioFadeConfigFile.getFile().getAbsolutePath());
6343         service.init();
6344         return service;
6345     }
6346 
getUpdatedCarAudioZoneConfigInfo( CarAudioZoneConfigInfo previousConfig, CarAudioService service)6347     private CarAudioZoneConfigInfo getUpdatedCarAudioZoneConfigInfo(
6348             CarAudioZoneConfigInfo previousConfig, CarAudioService service) {
6349         List<CarAudioZoneConfigInfo> infos =
6350                 service.getAudioZoneConfigInfos(previousConfig.getZoneId());
6351         CarAudioZoneConfigInfo previousUpdated = infos.stream()
6352                 .filter(i-> i.hasSameConfigInfo(previousConfig)).findFirst().orElseThrow(
6353                         () -> new NoSuchPropertyException("Missing previously selected config"));
6354         return previousUpdated;
6355     }
6356 
getOccupantZoneCallback()6357     private ICarOccupantZoneCallback getOccupantZoneCallback() {
6358         ArgumentCaptor<ICarOccupantZoneCallback> captor =
6359                 ArgumentCaptor.forClass(ICarOccupantZoneCallback.class);
6360         verify(mMockOccupantZoneService).registerCallback(captor.capture());
6361         return captor.getValue();
6362     }
6363 
getAudioServerStateCallback()6364     private AudioServerStateCallback getAudioServerStateCallback() {
6365         ArgumentCaptor<AudioServerStateCallback> captor = ArgumentCaptor.forClass(
6366                 AudioServerStateCallback.class);
6367         verify(mAudioManager).setAudioServerStateCallback(any(), captor.capture());
6368         return captor.getValue();
6369     }
6370 
removeUpToEquals(String command)6371     private String removeUpToEquals(String command) {
6372         return command.replaceAll("^[^=]*=", "");
6373     }
6374 
captureAudioMirrorInfoCommand(int count)6375     private String captureAudioMirrorInfoCommand(int count) {
6376         ArgumentCaptor<String> capture = ArgumentCaptor.forClass(String.class);
6377         verify(mAudioManager, times(count)).setParameters(capture.capture());
6378         return capture.getValue();
6379     }
6380 
getAudioZonesMirrorStatusCallback( CarAudioService service)6381     private TestAudioZonesMirrorStatusCallbackCallback getAudioZonesMirrorStatusCallback(
6382             CarAudioService service) {
6383         TestAudioZonesMirrorStatusCallbackCallback callback =
6384                 new TestAudioZonesMirrorStatusCallbackCallback(/* count= */ 1);
6385         service.registerAudioZonesMirrorStatusCallback(callback);
6386         return callback;
6387     }
6388 
assignOccupantToAudioZones()6389     private void assignOccupantToAudioZones() throws RemoteException {
6390         ICarOccupantZoneCallback occupantZoneCallback = getOccupantZoneCallback();
6391         occupantZoneCallback.onOccupantZoneConfigChanged(
6392                 CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
6393     }
6394 
simulateLogoutPassengers()6395     private void simulateLogoutPassengers() throws Exception {
6396         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
6397                 .thenReturn(UserManagerHelper.USER_NULL);
6398         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
6399                 .thenReturn(UserManagerHelper.USER_NULL);
6400 
6401         assignOccupantToAudioZones();
6402     }
6403 
simulateLogoutRightPassengers()6404     private void simulateLogoutRightPassengers() throws Exception {
6405         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
6406                 .thenReturn(UserManagerHelper.USER_NULL);
6407 
6408         assignOccupantToAudioZones();
6409     }
6410 
simulatePassengersSwitch()6411     private void simulatePassengersSwitch() throws Exception {
6412         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
6413                 .thenReturn(TEST_REAR_RIGHT_USER_ID);
6414         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
6415                 .thenReturn(TEST_REAR_LEFT_USER_ID);
6416 
6417         assignOccupantToAudioZones();
6418     }
6419 
createCarAudioGainConfigInfo(int zoneId, String devicePortAddress, int volumeIndex)6420     private CarAudioGainConfigInfo createCarAudioGainConfigInfo(int zoneId,
6421             String devicePortAddress, int volumeIndex) {
6422         AudioGainConfigInfo configInfo = new AudioGainConfigInfo();
6423         configInfo.zoneId = zoneId;
6424         configInfo.devicePortAddress = devicePortAddress;
6425         configInfo.volumeIndex = volumeIndex;
6426         return new CarAudioGainConfigInfo(configInfo);
6427     }
6428 
getHalAudioGainCallback()6429     private HalAudioGainCallback getHalAudioGainCallback() {
6430         ArgumentCaptor<HalAudioGainCallback> captor = ArgumentCaptor.forClass(
6431                 HalAudioGainCallback.class);
6432         verify(mAudioControlWrapperAidl).registerAudioGainCallback(captor.capture());
6433         return captor.getValue();
6434     }
6435 
createHalAudioDeviceInfo(int id, String name, int minVal, int maxVal, int defaultVal, int stepVal, int type, String address)6436     private HalAudioDeviceInfo createHalAudioDeviceInfo(int id, String name, int minVal,
6437             int maxVal, int defaultVal, int stepVal, int type, String address) {
6438         AudioPortDeviceExt deviceExt = new AudioPortDeviceExt();
6439         deviceExt.device = new AudioDevice();
6440         deviceExt.device.type = new AudioDeviceDescription();
6441         deviceExt.device.type.type = type;
6442         deviceExt.device.type.connection = CONNECTION_BUS;
6443         deviceExt.device.address = AudioDeviceAddress.id(address);
6444         AudioPort audioPort = new AudioPort();
6445         audioPort.id = id;
6446         audioPort.name = name;
6447         audioPort.gains = new android.media.audio.common.AudioGain[] {
6448                 new android.media.audio.common.AudioGain() {{
6449                     mode = JOINT;
6450                     minValue = minVal;
6451                     maxValue = maxVal;
6452                     defaultValue = defaultVal;
6453                     stepValue = stepVal;
6454                 }}
6455         };
6456         audioPort.ext = AudioPortExt.device(deviceExt);
6457         return new HalAudioDeviceInfo(audioPort);
6458     }
6459 
getHalModuleChangeCallback()6460     private HalAudioModuleChangeCallback getHalModuleChangeCallback() {
6461         ArgumentCaptor<HalAudioModuleChangeCallback> captor = ArgumentCaptor.forClass(
6462                 HalAudioModuleChangeCallback.class);
6463         verify(mAudioControlWrapperAidl).setModuleChangeCallback(captor.capture());
6464         return captor.getValue();
6465     }
6466 
getCarAudioPlaybackCallback()6467     private AudioPlaybackCallback getCarAudioPlaybackCallback() {
6468         ArgumentCaptor<AudioPlaybackCallback> captor = ArgumentCaptor.forClass(
6469                 AudioPlaybackCallback.class);
6470         verify(mAudioManager).registerAudioPlaybackCallback(captor.capture(), any());
6471         return captor.getValue();
6472     }
6473 
getAudioKeyEventListener()6474     private KeyEventListener getAudioKeyEventListener() {
6475         ArgumentCaptor<KeyEventListener> captor = ArgumentCaptor.forClass(KeyEventListener.class);
6476         verify(mMockCarInputService).registerKeyEventListener(captor.capture(), any());
6477         return captor.getValue();
6478     }
6479 
getCallStateListener()6480     private TelephonyCallback.CallStateListener getCallStateListener() {
6481         ArgumentCaptor<TelephonyCallback> captor =
6482                 ArgumentCaptor.forClass(TelephonyCallback.class);
6483         verify(mMockTelephonyManager).registerTelephonyCallback(any(), captor.capture());
6484         return (TelephonyCallback.CallStateListener) captor.getValue();
6485     }
6486 
requestHalAudioFocus(int usage)6487     private void requestHalAudioFocus(int usage) {
6488         ArgumentCaptor<HalFocusListener> captor =
6489                 ArgumentCaptor.forClass(HalFocusListener.class);
6490         verify(mAudioControlWrapperAidl).registerFocusListener(captor.capture());
6491         HalFocusListener halFocusListener = captor.getValue();
6492         halFocusListener.requestAudioFocus(usageToMetadata(usage), PRIMARY_AUDIO_ZONE,
6493                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
6494     }
6495 
mockActivePlayback()6496     private void mockActivePlayback() {
6497         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
6498         callback.onPlaybackConfigChanged(List.of(getPlaybackConfig()));
6499     }
6500 
getPlaybackConfig()6501     private AudioPlaybackConfiguration getPlaybackConfig() {
6502         AudioAttributes audioAttributes = new AudioAttributes.Builder()
6503                 .setUsage(USAGE_MEDIA).build();
6504         AudioPlaybackConfiguration config = mock(AudioPlaybackConfiguration.class);
6505         when(config.getAudioAttributes()).thenReturn(audioAttributes);
6506         when(config.getAudioDeviceInfo()).thenReturn(mMediaOutputDevice);
6507         when(config.isActive()).thenReturn(true);
6508 
6509         return config;
6510     }
6511 
setUpCarAudioServiceWithoutMirroring()6512     private CarAudioService setUpCarAudioServiceWithoutMirroring() throws Exception {
6513         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration_without_mirroring);
6514         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6515         AudioDeviceInfo[] outputDevices = generateOutputDeviceInfos();
6516         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(outputDevices);
6517         CarAudioService service = new CarAudioService(mMockContext, mAudioManager,
6518                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6519                 mTempCarAudioFadeConfigFile.getFile().getAbsolutePath());
6520         service.init();
6521         return service;
6522     }
6523 
setUpCarAudioServiceWithVersionTwoVolumeList()6524     private CarAudioService setUpCarAudioServiceWithVersionTwoVolumeList() throws Exception {
6525         setUpTempFileForAudioConfiguration(R.raw.car_audio_configuration);
6526         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6527         when(mMockResources.getInteger(audioVolumeAdjustmentContextsVersion))
6528                 .thenReturn(AUDIO_CONTEXT_PRIORITY_LIST_VERSION_TWO);
6529         CarAudioService service = new CarAudioService(mMockContext, mAudioManager,
6530                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6531                 mTempCarAudioFadeConfigFile.getFile().getAbsolutePath());
6532         service.init();
6533         return service;
6534     }
6535 
setUpTempFileForAudioConfiguration(int resource)6536     private void setUpTempFileForAudioConfiguration(int resource) throws Exception {
6537         try (InputStream configurationStream = mContext.getResources().openRawResource(resource)) {
6538             mTempCarAudioConfigFile = new TemporaryFile("xml");
6539             mTempCarAudioConfigFile.write(new String(configurationStream.readAllBytes()));
6540         }
6541     }
6542 
setUpTempFileForAudioFadeConfiguration(int resource)6543     private void setUpTempFileForAudioFadeConfiguration(int resource) throws Exception {
6544         try (InputStream configurationStream = mContext.getResources().openRawResource(resource)) {
6545             mTempCarAudioFadeConfigFile = new TemporaryFile("xml");
6546             mTempCarAudioFadeConfigFile.write(new String(configurationStream.readAllBytes()));
6547         }
6548     }
6549 
setUpCarAudioServiceWithFadeManagerEnabled()6550     private CarAudioService setUpCarAudioServiceWithFadeManagerEnabled() throws Exception {
6551         mSetFlagsRule.enableFlags(FLAG_ENABLE_FADE_MANAGER_CONFIGURATION);
6552         mSetFlagsRule.enableFlags(Flags.FLAG_CAR_AUDIO_FADE_MANAGER_CONFIGURATION);
6553         when(mMockResources.getBoolean(audioUseFadeManagerConfiguration)).thenReturn(true);
6554         return setUpAudioService();
6555     }
6556 
setUpCarAudioServiceUsingCoreAudioRoutingAndVolume()6557     private CarAudioService setUpCarAudioServiceUsingCoreAudioRoutingAndVolume() throws Exception {
6558         when(mMockResources.getBoolean(audioUseCoreVolume)).thenReturn(true);
6559         when(mMockResources.getBoolean(audioUseCoreRouting)).thenReturn(true);
6560         setUpTempFileForAudioConfiguration(
6561                 R.raw.car_audio_configuration_using_core_audio_routing_and_volume);
6562         setUpTempFileForAudioFadeConfiguration(R.raw.car_audio_fade_configuration);
6563 
6564         CarAudioService useCoreAudioCarAudioService = new CarAudioService(mMockContext,
6565                 mAudioManager,
6566                 mTempCarAudioConfigFile.getFile().getAbsolutePath(), mCarVolumeCallbackHandler,
6567                 /* audioFadeConfigurationPath= */ null);
6568         useCoreAudioCarAudioService.init();
6569         return useCoreAudioCarAudioService;
6570     }
6571 
mockGrantCarControlAudioSettingsPermission()6572     private void mockGrantCarControlAudioSettingsPermission() {
6573         mockContextCheckCallingOrSelfPermission(mMockContext,
6574                 PERMISSION_CAR_CONTROL_AUDIO_SETTINGS, PERMISSION_GRANTED);
6575     }
6576 
mockDenyCarControlAudioSettingsPermission()6577     private void mockDenyCarControlAudioSettingsPermission() {
6578         mockContextCheckCallingOrSelfPermission(mMockContext,
6579                 PERMISSION_CAR_CONTROL_AUDIO_SETTINGS, PERMISSION_DENIED);
6580     }
6581 
mockDenyCarControlAudioVolumePermission()6582     private void mockDenyCarControlAudioVolumePermission() {
6583         mockContextCheckCallingOrSelfPermission(mMockContext,
6584                 PERMISSION_CAR_CONTROL_AUDIO_VOLUME, PERMISSION_DENIED);
6585     }
6586 
generateInputDeviceInfos()6587     private AudioDeviceInfo[] generateInputDeviceInfos() {
6588         mMicrophoneInputDevice = new AudioDeviceInfoBuilder()
6589                 .setAddressName(PRIMARY_ZONE_MICROPHONE_ADDRESS)
6590                 .setType(TYPE_BUILTIN_MIC)
6591                 .setIsSource(true)
6592                 .build();
6593         mFmTunerInputDevice = new AudioDeviceInfoBuilder()
6594                 .setAddressName(PRIMARY_ZONE_FM_TUNER_ADDRESS)
6595                 .setType(TYPE_FM_TUNER)
6596                 .setIsSource(true)
6597                 .build();
6598         return new AudioDeviceInfo[]{mMicrophoneInputDevice, mFmTunerInputDevice};
6599     }
6600 
generateOutputDeviceInfos()6601     private AudioDeviceInfo[] generateOutputDeviceInfos() {
6602         mMediaOutputDevice = new AudioDeviceInfoBuilder()
6603                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6604                 .setAddressName(MEDIA_TEST_DEVICE)
6605                 .build();
6606         mNotificationOutpuBus = new AudioDeviceInfoBuilder()
6607                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6608                 .setAddressName(NOTIFICATION_TEST_DEVICE)
6609                 .build();
6610         mNavOutputDevice = new AudioDeviceInfoBuilder()
6611                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6612                 .setAddressName(NAVIGATION_TEST_DEVICE)
6613                 .build();
6614         mVoiceOutpuBus = new AudioDeviceInfoBuilder()
6615                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6616                 .setAddressName(VOICE_TEST_DEVICE)
6617                 .build();
6618         mSecondaryConfig0Group0Device = new AudioDeviceInfoBuilder()
6619                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6620                 .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_0)
6621                 .build();
6622         mSecondaryConfig1Group0Device = new AudioDeviceInfoBuilder()
6623                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6624                 .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_1_0)
6625                 .build();
6626         mSecondaryConfig1Group1Device = new AudioDeviceInfoBuilder()
6627                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6628                 .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_1_1)
6629                 .build();
6630         mBTAudioDeviceInfo = new AudioDeviceInfoBuilder()
6631                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6632                 .setAddressName(TEST_BT_DEVICE)
6633                 .setType(TYPE_BLUETOOTH_A2DP)
6634                 .build();
6635         return new AudioDeviceInfo[] {
6636                 mBTAudioDeviceInfo,
6637                 mMediaOutputDevice,
6638                 mNavOutputDevice,
6639                 new AudioDeviceInfoBuilder()
6640                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6641                         .setAddressName(CALL_TEST_DEVICE)
6642                         .build(),
6643                 new AudioDeviceInfoBuilder()
6644                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6645                         .setAddressName(SYSTEM_BUS_DEVICE)
6646                         .build(),
6647                 mNotificationOutpuBus,
6648                 mVoiceOutpuBus,
6649                 new AudioDeviceInfoBuilder()
6650                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6651                         .setAddressName(RING_TEST_DEVICE)
6652                         .build(),
6653                 new AudioDeviceInfoBuilder()
6654                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6655                         .setAddressName(ALARM_TEST_DEVICE)
6656                         .build(),
6657                 new AudioDeviceInfoBuilder()
6658                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6659                         .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_0)
6660                         .build(),
6661                 mSecondaryConfig1Group0Device,
6662                 mSecondaryConfig1Group1Device,
6663                 new AudioDeviceInfoBuilder()
6664                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6665                         .setAddressName(TERTIARY_TEST_DEVICE_1)
6666                         .build(),
6667                 new AudioDeviceInfoBuilder()
6668                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6669                         .setAddressName(TERTIARY_TEST_DEVICE_2)
6670                         .build(),
6671                 new AudioDeviceInfoBuilder()
6672                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6673                         .setAddressName(QUATERNARY_TEST_DEVICE_1)
6674                         .build(),
6675                 new AudioDeviceInfoBuilder()
6676                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6677                         .setAddressName(OEM_TEST_DEVICE)
6678                         .build(),
6679                 new AudioDeviceInfoBuilder()
6680                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6681                         .setAddressName(MIRROR_TEST_DEVICE).build(),
6682                 new AudioDeviceInfoBuilder()
6683                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
6684                         .setAddressName(TEST_REAR_ROW_3_DEVICE).build(),
6685         };
6686     }
6687 
mockCoreAudioRoutingAndVolume()6688     private void mockCoreAudioRoutingAndVolume() {
6689         doReturn(CoreAudioRoutingUtils.getProductStrategies())
6690                 .when(AudioManagerWrapper::getAudioProductStrategies);
6691         doReturn(CoreAudioRoutingUtils.getVolumeGroups())
6692                 .when(AudioManagerWrapper::getAudioVolumeGroups);
6693 
6694         when(mAudioManager.getMinVolumeIndexForAttributes(
6695                 eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
6696                 .thenReturn(CoreAudioRoutingUtils.MUSIC_MIN_INDEX);
6697         when(mAudioManager.getMaxVolumeIndexForAttributes(
6698                 eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
6699                 .thenReturn(CoreAudioRoutingUtils.MUSIC_MAX_INDEX);
6700         when(mAudioManager.getVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
6701                 .thenReturn(CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX);
6702         when(mAudioManager.getLastAudibleVolumeForVolumeGroup(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
6703                 .thenReturn(CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX);
6704         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
6705                 .thenReturn(false);
6706 
6707         when(mAudioManager.getMinVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.NAV_ATTRIBUTES)))
6708                 .thenReturn(CoreAudioRoutingUtils.NAV_MIN_INDEX);
6709         when(mAudioManager.getMaxVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.NAV_ATTRIBUTES)))
6710                 .thenReturn(CoreAudioRoutingUtils.NAV_MAX_INDEX);
6711         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.NAV_GROUP_ID))
6712                 .thenReturn(false);
6713 
6714         when(mAudioManager.getMinVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.OEM_ATTRIBUTES)))
6715                 .thenReturn(CoreAudioRoutingUtils.OEM_MIN_INDEX);
6716         when(mAudioManager.getMaxVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.OEM_ATTRIBUTES)))
6717                 .thenReturn(CoreAudioRoutingUtils.OEM_MAX_INDEX);
6718         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.OEM_GROUP_ID))
6719                 .thenReturn(false);
6720 
6721         doReturn(CoreAudioRoutingUtils.MUSIC_GROUP_ID)
6722                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(
6723                         CoreAudioRoutingUtils.MUSIC_ATTRIBUTES));
6724         doReturn(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)
6725                 .when(() -> CoreAudioHelper.selectAttributesForVolumeGroupName(
6726                         CoreAudioRoutingUtils.MUSIC_GROUP_NAME));
6727 
6728         doReturn(CoreAudioRoutingUtils.NAV_GROUP_ID)
6729                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(
6730                         CoreAudioRoutingUtils.NAV_ATTRIBUTES));
6731         doReturn(CoreAudioRoutingUtils.NAV_ATTRIBUTES)
6732                 .when(() -> CoreAudioHelper.selectAttributesForVolumeGroupName(
6733                         CoreAudioRoutingUtils.NAV_GROUP_NAME));
6734 
6735         doReturn(CoreAudioRoutingUtils.OEM_GROUP_ID)
6736                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(
6737                         CoreAudioRoutingUtils.OEM_ATTRIBUTES));
6738         doReturn(CoreAudioRoutingUtils.OEM_ATTRIBUTES)
6739                 .when(() -> CoreAudioHelper.selectAttributesForVolumeGroupName(
6740                         CoreAudioRoutingUtils.OEM_GROUP_NAME));
6741     }
6742 
createAudioFocusInfoForMedia()6743     private static AudioFocusInfo createAudioFocusInfoForMedia() {
6744         return createAudioFocusInfoForMedia(MEDIA_APP_UID);
6745     }
6746 
createAudioFocusInfoForMedia(int uid)6747     private static AudioFocusInfo createAudioFocusInfoForMedia(int uid) {
6748         AudioAttributes.Builder builder = new AudioAttributes.Builder();
6749         builder.setUsage(USAGE_MEDIA);
6750 
6751         return new AudioFocusInfo(builder.build(), uid, MEDIA_CLIENT_ID,
6752                 MEDIA_PACKAGE_NAME, AUDIOFOCUS_GAIN, AUDIOFOCUS_LOSS, MEDIA_EMPTY_FLAG, SDK_INT);
6753     }
6754 
getFocusChanges(AudioFocusInfo info)6755     private List<Integer> getFocusChanges(AudioFocusInfo info) {
6756         ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
6757         verify(mAudioManager, atLeastOnce()).dispatchAudioFocusChange(eq(info), captor.capture(),
6758                 any());
6759         return captor.getAllValues();
6760     }
6761 
verifyMediaDuckingInfoInZone(ArgumentCaptor<List<CarDuckingInfo>> carDuckingInfosCaptor, int zoneId, String message)6762     private void verifyMediaDuckingInfoInZone(ArgumentCaptor<List<CarDuckingInfo>>
6763             carDuckingInfosCaptor, int zoneId, String message) {
6764         expectWithMessage("Zone size of notified ducking info " + message)
6765                 .that(carDuckingInfosCaptor.getValue().size()).isEqualTo(1);
6766         CarDuckingInfo duckingInfo = carDuckingInfosCaptor.getValue().get(0);
6767         expectWithMessage("Ducking info zone id " + message)
6768                 .that(duckingInfo.mZoneId).isEqualTo(zoneId);
6769         expectWithMessage("Audio attributes holding focus " + message)
6770                 .that(CarHalAudioUtils.metadataToAudioAttributes(duckingInfo
6771                         .mPlaybackMetaDataHoldingFocus))
6772                 .containsExactly(CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA));
6773     }
6774 
getZoneConfigToSwitch(CarAudioService service, int zoneId)6775     private CarAudioZoneConfigInfo getZoneConfigToSwitch(CarAudioService service, int zoneId) {
6776         CarAudioZoneConfigInfo currentZoneConfigInfo =
6777                 service.getCurrentAudioZoneConfigInfo(zoneId);
6778         List<CarAudioZoneConfigInfo> zoneConfigInfos = service.getAudioZoneConfigInfos(zoneId);
6779 
6780         for (int index = 0; index < zoneConfigInfos.size(); index++) {
6781             if (currentZoneConfigInfo.equals(zoneConfigInfos.get(index))) {
6782                 continue;
6783             }
6784             return zoneConfigInfos.get(index);
6785         }
6786         return null;
6787     }
6788 
setVolumeForGroup(CarAudioService service, TestCarVolumeEventCallback volumeEventCallback, int zoneId, int groupId, int volumeIndex)6789     private void setVolumeForGroup(CarAudioService service,
6790                                    TestCarVolumeEventCallback volumeEventCallback,
6791                                    int zoneId, int groupId, int volumeIndex) throws Exception {
6792         service.setGroupVolume(zoneId, groupId, volumeIndex, TEST_FLAGS);
6793         resetVolumeCallbacks(volumeEventCallback);
6794     }
6795 
createActivePlayback(AudioPlaybackCallback callback, TestCarVolumeEventCallback volumeEventCallback, int playbackUsage, String deviceAddress, int playbackUid)6796     private void createActivePlayback(AudioPlaybackCallback callback,
6797                                       TestCarVolumeEventCallback volumeEventCallback,
6798                                       int playbackUsage, String deviceAddress, int playbackUid)
6799             throws Exception {
6800         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
6801                 .setUsage(playbackUsage).setDeviceAddress(deviceAddress)
6802                 .setClientUid(playbackUid).build()));
6803         resetVolumeCallbacks(volumeEventCallback);
6804     }
6805 
resetVolumeCallbacks(TestCarVolumeEventCallback volumeEventCallback)6806     private void resetVolumeCallbacks(TestCarVolumeEventCallback volumeEventCallback)
6807             throws Exception {
6808         volumeEventCallback.waitForCallback();
6809         volumeEventCallback.reset();
6810         reset(mCarVolumeCallbackHandler);
6811     }
6812 
6813     private static final class TestAudioZoneConfigurationsChangeCallback
6814             extends IAudioZoneConfigurationsChangeCallback.Stub {
6815 
6816         private List<CarAudioZoneConfigInfo> mInfos;
6817         private int mStatus = INVALID_STATUS;
6818 
6819         private CountDownLatch mStatusLatch = new CountDownLatch(1);
6820         @Override
onAudioZoneConfigurationsChanged(List<CarAudioZoneConfigInfo> configs, int status)6821         public void onAudioZoneConfigurationsChanged(List<CarAudioZoneConfigInfo> configs,
6822                 int status) {
6823             mInfos = configs;
6824             mStatus = status;
6825             mStatusLatch.countDown();
6826         }
6827 
waitForCallback()6828         private void waitForCallback() throws Exception {
6829             mStatusLatch.await(TEST_ZONE_CONFIG_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6830         }
6831 
reset()6832         public void reset() {
6833             mInfos = null;
6834             mStatus = INVALID_STATUS;
6835             mStatusLatch = new CountDownLatch(1);
6836         }
6837     }
6838 
6839     private static final class TestPrimaryZoneMediaAudioRequestCallback extends
6840             IPrimaryZoneMediaAudioRequestCallback.Stub {
6841         private long mRequestId = INVALID_REQUEST_ID;
6842         private CarOccupantZoneManager.OccupantZoneInfo mInfo;
6843         private CountDownLatch mStatusLatch = new CountDownLatch(1);
6844         private int mStatus;
6845         private DeathRecipient mDeathRecipient;
6846 
6847         @Override
linkToDeath(@onNull DeathRecipient recipient, int flags)6848         public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
6849             mDeathRecipient = recipient;
6850             super.linkToDeath(recipient, flags);
6851         }
6852 
6853         @Override
onRequestMediaOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info, long requestId)6854         public void onRequestMediaOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info,
6855                 long requestId) {
6856             mInfo = info;
6857             mRequestId = requestId;
6858             mStatusLatch.countDown();
6859         }
6860 
6861         @Override
onMediaAudioRequestStatusChanged( @onNull CarOccupantZoneManager.OccupantZoneInfo info, long requestId, int status)6862         public void onMediaAudioRequestStatusChanged(
6863                 @NonNull CarOccupantZoneManager.OccupantZoneInfo info,
6864                 long requestId, int status) {
6865             mInfo = info;
6866             mRequestId = requestId;
6867             mStatus = status;
6868             mStatusLatch.countDown();
6869         }
6870 
waitForCallback()6871         private void waitForCallback() throws Exception {
6872             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6873         }
6874 
reset()6875         public void reset() {
6876             mInfo = null;
6877             mRequestId = INVALID_REQUEST_ID;
6878             mStatus = INVALID_STATUS;
6879             mStatusLatch = new CountDownLatch(1);
6880         }
6881     }
6882 
6883     private static final class TestMediaRequestStatusCallback extends
6884             IMediaAudioRequestStatusCallback.Stub {
6885         private long mRequestId = INVALID_REQUEST_ID;
6886         private CarOccupantZoneManager.OccupantZoneInfo mInfo;
6887         private int mStatus;
6888         private CountDownLatch mStatusLatch = new CountDownLatch(1);
6889 
6890         @Override
onMediaAudioRequestStatusChanged( @onNull CarOccupantZoneManager.OccupantZoneInfo info, long requestId, @CarAudioManager.MediaAudioRequestStatus int status)6891         public void onMediaAudioRequestStatusChanged(
6892                 @NonNull CarOccupantZoneManager.OccupantZoneInfo info,
6893                 long requestId, @CarAudioManager.MediaAudioRequestStatus int status)
6894                 throws RemoteException {
6895             mInfo = info;
6896             mRequestId = requestId;
6897             mStatus = status;
6898             mStatusLatch.countDown();
6899         }
6900 
waitForCallback()6901         private void waitForCallback() throws Exception {
6902             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6903         }
6904 
reset()6905         private void reset() {
6906             mInfo = null;
6907             mRequestId = INVALID_REQUEST_ID;
6908             mStatus = INVALID_STATUS;
6909             mStatusLatch = new CountDownLatch(1);
6910         }
6911     }
6912 
6913     private static final class TestAudioZonesMirrorStatusCallbackCallback extends
6914             IAudioZonesMirrorStatusCallback.Stub {
6915 
6916         private static final long TEST_CALLBACK_TIMEOUT_MS = 300;
6917 
6918         private List<int[]> mZoneIds = new ArrayList<>();
6919         private List<Integer> mStatus = new ArrayList<>();
6920         private int mNumberOfCalls = 0;
6921         private CountDownLatch mStatusLatch;
6922 
TestAudioZonesMirrorStatusCallbackCallback(int count)6923         private TestAudioZonesMirrorStatusCallbackCallback(int count) {
6924             mStatusLatch = new CountDownLatch(count);
6925         }
6926 
6927         @Override
onAudioZonesMirrorStatusChanged(int[] zoneIds, int status)6928         public void onAudioZonesMirrorStatusChanged(int[] zoneIds, int status) {
6929             mZoneIds.add(zoneIds);
6930             mStatus.add(status);
6931             mNumberOfCalls++;
6932             mStatusLatch.countDown();
6933         }
6934 
waitForCallback()6935         private void waitForCallback() throws Exception {
6936             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6937         }
6938 
reset(int count)6939         public void reset(int count) {
6940             mStatusLatch = new CountDownLatch(count);
6941         }
6942 
getLastZoneIds()6943         private int[] getLastZoneIds() {
6944             return mZoneIds.get(mZoneIds.size() - 1);
6945         }
6946 
getLastStatus()6947         public int getLastStatus() {
6948             return mStatus.get(mStatus.size() - 1);
6949         }
6950     }
6951 
6952     private static final class SwitchAudioZoneConfigCallbackImpl extends
6953             ISwitchAudioZoneConfigCallback.Stub {
6954         private CountDownLatch mStatusLatch = new CountDownLatch(1);
6955         private CarAudioZoneConfigInfo mZoneConfig;
6956         private boolean mIsSuccessful;
6957 
6958         @Override
onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfig, boolean isSuccessful)6959         public void onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfig,
6960                 boolean isSuccessful) {
6961             mZoneConfig = zoneConfig;
6962             mIsSuccessful = isSuccessful;
6963             mStatusLatch.countDown();
6964         }
6965 
waitForCallback()6966         private void waitForCallback() throws Exception {
6967             mStatusLatch.await(TEST_ZONE_CONFIG_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
6968         }
6969 
getZoneConfig()6970         CarAudioZoneConfigInfo getZoneConfig() {
6971             return mZoneConfig;
6972         }
6973 
getSwitchStatus()6974         boolean getSwitchStatus() {
6975             return mIsSuccessful;
6976         }
6977 
reset()6978         public void reset() {
6979             mZoneConfig = null;
6980             mIsSuccessful = false;
6981             mStatusLatch = new CountDownLatch(1);
6982         }
6983     }
6984 }
6985