• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.TIRAMISU_1;
22 import static android.car.PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0;
23 import static android.car.media.CarAudioManager.AUDIO_FEATURE_AUDIO_MIRRORING;
24 import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING;
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.INVALID_AUDIO_ZONE;
31 import static android.car.media.CarAudioManager.INVALID_REQUEST_ID;
32 import static android.car.media.CarAudioManager.INVALID_VOLUME_GROUP_ID;
33 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
34 import static android.car.test.mocks.AndroidMockitoHelper.mockCarGetPlatformVersion;
35 import static android.car.test.mocks.AndroidMockitoHelper.mockContextCheckCallingOrSelfPermission;
36 import static android.content.pm.PackageManager.PERMISSION_DENIED;
37 import static android.content.pm.PackageManager.PERMISSION_GRANTED;
38 import static android.media.AudioAttributes.USAGE_ALARM;
39 import static android.media.AudioAttributes.USAGE_ANNOUNCEMENT;
40 import static android.media.AudioAttributes.USAGE_ASSISTANCE_ACCESSIBILITY;
41 import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE;
42 import static android.media.AudioAttributes.USAGE_ASSISTANCE_SONIFICATION;
43 import static android.media.AudioAttributes.USAGE_ASSISTANT;
44 import static android.media.AudioAttributes.USAGE_CALL_ASSISTANT;
45 import static android.media.AudioAttributes.USAGE_EMERGENCY;
46 import static android.media.AudioAttributes.USAGE_GAME;
47 import static android.media.AudioAttributes.USAGE_MEDIA;
48 import static android.media.AudioAttributes.USAGE_NOTIFICATION;
49 import static android.media.AudioAttributes.USAGE_NOTIFICATION_EVENT;
50 import static android.media.AudioAttributes.USAGE_NOTIFICATION_RINGTONE;
51 import static android.media.AudioAttributes.USAGE_SAFETY;
52 import static android.media.AudioAttributes.USAGE_UNKNOWN;
53 import static android.media.AudioAttributes.USAGE_VEHICLE_STATUS;
54 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION;
55 import static android.media.AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING;
56 import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC;
57 import static android.media.AudioDeviceInfo.TYPE_FM_TUNER;
58 import static android.media.AudioManager.AUDIOFOCUS_GAIN;
59 import static android.media.AudioManager.AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK;
60 import static android.media.AudioManager.AUDIOFOCUS_LOSS;
61 import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
62 import static android.media.AudioManager.AUDIOFOCUS_NONE;
63 import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
64 import static android.media.AudioManager.EXTRA_VOLUME_STREAM_TYPE;
65 import static android.media.AudioManager.FLAG_FROM_KEY;
66 import static android.media.AudioManager.FLAG_PLAY_SOUND;
67 import static android.media.AudioManager.FLAG_SHOW_UI;
68 import static android.media.AudioManager.MASTER_MUTE_CHANGED_ACTION;
69 import static android.media.AudioManager.STREAM_MUSIC;
70 import static android.media.AudioManager.SUCCESS;
71 import static android.media.AudioManager.VOLUME_CHANGED_ACTION;
72 import static android.media.audio.common.AudioDeviceDescription.CONNECTION_BUS;
73 import static android.media.audio.common.AudioDeviceType.OUT_DEVICE;
74 import static android.media.audio.common.AudioGainMode.JOINT;
75 import static android.os.Build.VERSION.SDK_INT;
76 import static android.view.KeyEvent.ACTION_DOWN;
77 import static android.view.KeyEvent.ACTION_UP;
78 import static android.view.KeyEvent.KEYCODE_UNKNOWN;
79 import static android.view.KeyEvent.KEYCODE_VOLUME_DOWN;
80 import static android.view.KeyEvent.KEYCODE_VOLUME_MUTE;
81 import static android.view.KeyEvent.KEYCODE_VOLUME_UP;
82 
83 import static com.android.car.R.bool.audioPersistMasterMuteState;
84 import static com.android.car.R.bool.audioUseCarVolumeGroupEvent;
85 import static com.android.car.R.bool.audioUseCarVolumeGroupMuting;
86 import static com.android.car.R.bool.audioUseCoreRouting;
87 import static com.android.car.R.bool.audioUseCoreVolume;
88 import static com.android.car.R.bool.audioUseDynamicRouting;
89 import static com.android.car.R.bool.audioUseHalDuckingSignals;
90 import static com.android.car.R.integer.audioVolumeAdjustmentContextsVersion;
91 import static com.android.car.R.integer.audioVolumeKeyEventTimeoutMs;
92 import static com.android.car.audio.CarAudioService.CAR_DEFAULT_AUDIO_ATTRIBUTE;
93 import static com.android.car.audio.GainBuilder.DEFAULT_GAIN;
94 import static com.android.car.audio.GainBuilder.MAX_GAIN;
95 import static com.android.car.audio.GainBuilder.MIN_GAIN;
96 import static com.android.car.audio.GainBuilder.STEP_SIZE;
97 import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn;
98 import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
99 
100 import static org.junit.Assert.assertThrows;
101 import static org.mockito.ArgumentMatchers.any;
102 import static org.mockito.ArgumentMatchers.anyBoolean;
103 import static org.mockito.ArgumentMatchers.anyInt;
104 import static org.mockito.ArgumentMatchers.anyString;
105 import static org.mockito.ArgumentMatchers.eq;
106 import static org.mockito.Mockito.atLeastOnce;
107 import static org.mockito.Mockito.mock;
108 import static org.mockito.Mockito.never;
109 import static org.mockito.Mockito.reset;
110 import static org.mockito.Mockito.times;
111 import static org.mockito.Mockito.when;
112 
113 import android.annotation.NonNull;
114 import android.car.Car;
115 import android.car.CarOccupantZoneManager;
116 import android.car.ICarOccupantZoneCallback;
117 import android.car.VehicleAreaSeat;
118 import android.car.builtin.media.AudioManagerHelper;
119 import android.car.builtin.media.AudioManagerHelper.AudioPatchInfo;
120 import android.car.builtin.os.UserManagerHelper;
121 import android.car.media.CarAudioManager;
122 import android.car.media.CarAudioPatchHandle;
123 import android.car.media.CarAudioZoneConfigInfo;
124 import android.car.media.CarVolumeGroupEvent;
125 import android.car.media.CarVolumeGroupInfo;
126 import android.car.media.IAudioZonesMirrorStatusCallback;
127 import android.car.media.ICarVolumeEventCallback;
128 import android.car.media.IMediaAudioRequestStatusCallback;
129 import android.car.media.IPrimaryZoneMediaAudioRequestCallback;
130 import android.car.media.ISwitchAudioZoneConfigCallback;
131 import android.car.settings.CarSettings;
132 import android.car.test.mocks.AbstractExtendedMockitoTestCase;
133 import android.car.test.mocks.MockSettings;
134 import android.content.BroadcastReceiver;
135 import android.content.ContentResolver;
136 import android.content.Context;
137 import android.content.Intent;
138 import android.content.pm.PackageManager;
139 import android.content.res.Resources;
140 import android.hardware.automotive.audiocontrol.AudioGainConfigInfo;
141 import android.hardware.automotive.audiocontrol.IAudioControl;
142 import android.hardware.automotive.audiocontrol.Reasons;
143 import android.media.AudioAttributes;
144 import android.media.AudioDeviceAttributes;
145 import android.media.AudioDeviceInfo;
146 import android.media.AudioFocusInfo;
147 import android.media.AudioGain;
148 import android.media.AudioManager;
149 import android.media.AudioManager.AudioPlaybackCallback;
150 import android.media.AudioPlaybackConfiguration;
151 import android.media.IAudioService;
152 import android.media.audio.common.AudioDevice;
153 import android.media.audio.common.AudioDeviceAddress;
154 import android.media.audio.common.AudioDeviceDescription;
155 import android.media.audio.common.AudioPort;
156 import android.media.audio.common.AudioPortDeviceExt;
157 import android.media.audio.common.AudioPortExt;
158 import android.media.audiopolicy.AudioPolicy;
159 import android.net.Uri;
160 import android.os.Binder;
161 import android.os.Build;
162 import android.os.IBinder;
163 import android.os.RemoteException;
164 import android.os.ServiceManager;
165 import android.os.SystemProperties;
166 import android.os.UserHandle;
167 import android.provider.Settings;
168 import android.telephony.TelephonyManager;
169 import android.util.Log;
170 import android.view.KeyEvent;
171 
172 import androidx.test.core.app.ApplicationProvider;
173 
174 import com.android.car.CarInputService;
175 import com.android.car.CarLocalServices;
176 import com.android.car.CarOccupantZoneService;
177 import com.android.car.R;
178 import com.android.car.audio.hal.AudioControlFactory;
179 import com.android.car.audio.hal.AudioControlWrapper;
180 import com.android.car.audio.hal.AudioControlWrapper.AudioControlDeathRecipient;
181 import com.android.car.audio.hal.AudioControlWrapperAidl;
182 import com.android.car.audio.hal.HalAudioDeviceInfo;
183 import com.android.car.audio.hal.HalAudioGainCallback;
184 import com.android.car.audio.hal.HalAudioModuleChangeCallback;
185 import com.android.car.audio.hal.HalFocusListener;
186 import com.android.car.oem.CarOemAudioDuckingProxyService;
187 import com.android.car.oem.CarOemAudioFocusProxyService;
188 import com.android.car.oem.CarOemAudioVolumeProxyService;
189 import com.android.car.oem.CarOemProxyService;
190 import com.android.car.test.utils.TemporaryFile;
191 
192 import org.junit.After;
193 import org.junit.Before;
194 import org.junit.Test;
195 import org.mockito.ArgumentCaptor;
196 import org.mockito.Captor;
197 import org.mockito.Mock;
198 
199 import java.io.InputStream;
200 import java.util.ArrayList;
201 import java.util.Arrays;
202 import java.util.List;
203 import java.util.concurrent.CountDownLatch;
204 import java.util.concurrent.TimeUnit;
205 
206 public final class CarAudioServiceUnitTest extends AbstractExtendedMockitoTestCase {
207     private static final String TAG = CarAudioServiceUnitTest.class.getSimpleName();
208     private static final long TEST_CALLBACK_TIMEOUT_MS = 100;
209     private static final int VOLUME_KEY_EVENT_TIMEOUT_MS = 3000;
210     private static final int AUDIO_CONTEXT_PRIORITY_LIST_VERSION_ONE = 1;
211     private static final int AUDIO_CONTEXT_PRIORITY_LIST_VERSION_TWO = 2;
212     private static final String MEDIA_TEST_DEVICE = "media_bus_device";
213     private static final String OEM_TEST_DEVICE = "oem_bus_device";
214     private static final String MIRROR_TEST_DEVICE = "mirror_bus_device";
215     private static final String NAVIGATION_TEST_DEVICE = "navigation_bus_device";
216     private static final String CALL_TEST_DEVICE = "call_bus_device";
217     private static final String NOTIFICATION_TEST_DEVICE = "notification_bus_device";
218     private static final String VOICE_TEST_DEVICE = "voice_bus_device";
219     private static final String RING_TEST_DEVICE = "ring_bus_device";
220     private static final String ALARM_TEST_DEVICE = "alarm_bus_device";
221     private static final String SYSTEM_BUS_DEVICE = "system_bus_device";
222     private static final String SECONDARY_TEST_DEVICE_CONFIG_0 = "secondary_zone_bus_100";
223     private static final String SECONDARY_TEST_DEVICE_CONFIG_1_0 = "secondary_zone_bus_200";
224     private static final String SECONDARY_TEST_DEVICE_CONFIG_1_1 = "secondary_zone_bus_201";
225     private static final String TERTIARY_TEST_DEVICE_1 = "tertiary_zone_bus_100";
226     private static final String TERTIARY_TEST_DEVICE_2 = "tertiary_zone_bus_200";
227     private static final String QUATERNARY_TEST_DEVICE_1 = "quaternary_zone_bus_1";
228     private static final String TEST_REAR_ROW_3_DEVICE = "rear_row_three_zone_bus_1";
229     private static final String PRIMARY_ZONE_MICROPHONE_ADDRESS = "Built-In Mic";
230     private static final String PRIMARY_ZONE_FM_TUNER_ADDRESS = "FM Tuner";
231     private static final String SECONDARY_ZONE_CONFIG_NAME_1 = "secondary zone config 1";
232     private static final String SECONDARY_ZONE_CONFIG_NAME_2 = "secondary zone config 2";
233     private static final String MIRROR_OFF_SIGNAL = "mirroring=off";
234     // From the car audio configuration file in /res/raw/car_audio_configuration.xml
235     private static final int TEST_REAR_LEFT_ZONE_ID = 1;
236     private static final int TEST_REAR_RIGHT_ZONE_ID = 2;
237     private static final int TEST_FRONT_ZONE_ID = 3;
238     private static final int TEST_REAR_ROW_3_ZONE_ID = 4;
239     public static final int[] TEST_MIRROR_AUDIO_ZONES = new int[]{TEST_REAR_LEFT_ZONE_ID,
240             TEST_REAR_RIGHT_ZONE_ID};
241     private static final int OUT_OF_RANGE_ZONE = TEST_REAR_ROW_3_ZONE_ID + 1;
242     private static final int PRIMARY_ZONE_VOLUME_GROUP_COUNT = 4;
243     private static final int SECONDARY_ZONE_VOLUME_GROUP_COUNT = 1;
244     private static final int SECONDARY_ZONE_VOLUME_GROUP_ID = SECONDARY_ZONE_VOLUME_GROUP_COUNT - 1;
245     private static final int TEST_PRIMARY_ZONE_GROUP_0 = 0;
246     private static final int TEST_PRIMARY_ZONE_GROUP_1 = 1;
247     private static final int TEST_PRIMARY_ZONE_GROUP_2 = 2;
248     private static final int TEST_SECONDARY_ZONE_GROUP_0 = 0;
249     private static final int TEST_SECONDARY_ZONE_GROUP_1 = 1;
250     private static final int TEST_FLAGS = 0;
251     private static final float TEST_VALUE = -.75f;
252     private static final float INVALID_TEST_VALUE = -1.5f;
253     private static final int TEST_DISPLAY_TYPE = 2;
254     private static final int TEST_SEAT = 2;
255     private static final int PRIMARY_OCCUPANT_ZONE = 0;
256     private static final int INVALID_STATUS = 0;
257 
258     private static final int TEST_DRIVER_OCCUPANT_ZONE_ID = 1;
259     private static final int TEST_REAR_LEFT_OCCUPANT_ZONE_ID = 2;
260     private static final int TEST_REAR_RIGHT_OCCUPANT_ZONE_ID = 3;
261     private static final int TEST_FRONT_OCCUPANT_ZONE_ID = 4;
262     private static final int TEST_REAR_ROW_3_OCCUPANT_ZONE_ID = 5;
263     private static final int TEST_UNASSIGNED_OCCUPANT_ZONE_ID = 6;
264 
265     private static final int TEST_MEDIA_PORT_ID = 0;
266     private static final int TEST_NAV_PORT_ID = 1;
267     private static final String TEST_MEDIA_PORT_NAME = "Media bus";
268     private static final String TEST_NAV_PORT_NAME = "Nav bus";
269     private static final int TEST_GAIN_MIN_VALUE = -3000;
270     private static final int TEST_GAIN_MAX_VALUE = -1000;
271     private static final int TEST_GAIN_DEFAULT_VALUE = -2000;
272     private static final int TEST_GAIN_STEP_VALUE = 2;
273 
274     private static final CarOccupantZoneManager.OccupantZoneInfo TEST_DRIVER_OCCUPANT =
275             getOccupantInfo(TEST_DRIVER_OCCUPANT_ZONE_ID,
276                     CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
277                     VehicleAreaSeat.SEAT_ROW_1_LEFT);
278     private static final CarOccupantZoneManager.OccupantZoneInfo
279             TEST_REAR_RIGHT_PASSENGER_OCCUPANT =
280             getOccupantInfo(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID,
281                     CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
282                     VehicleAreaSeat.SEAT_ROW_2_RIGHT);
283     private static final CarOccupantZoneManager.OccupantZoneInfo
284             TEST_FRONT_PASSENGER_OCCUPANT =
285             getOccupantInfo(TEST_FRONT_OCCUPANT_ZONE_ID,
286                     CarOccupantZoneManager.OCCUPANT_TYPE_FRONT_PASSENGER,
287                     VehicleAreaSeat.SEAT_ROW_1_RIGHT);
288     private static final CarOccupantZoneManager.OccupantZoneInfo
289             TEST_REAR_LEFT_PASSENGER_OCCUPANT =
290             getOccupantInfo(TEST_REAR_LEFT_OCCUPANT_ZONE_ID,
291                     CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
292                     VehicleAreaSeat.SEAT_ROW_2_LEFT);
293 
294     private static final CarOccupantZoneManager.OccupantZoneInfo
295             TEST_REAR_ROW_3_PASSENGER_OCCUPANT =
296             getOccupantInfo(TEST_REAR_ROW_3_OCCUPANT_ZONE_ID,
297                     CarOccupantZoneManager.OCCUPANT_TYPE_REAR_PASSENGER,
298                     VehicleAreaSeat.SEAT_ROW_3_LEFT);
299 
300     private static final String PROPERTY_RO_ENABLE_AUDIO_PATCH =
301             "ro.android.car.audio.enableaudiopatch";
302 
303     private static final int MEDIA_APP_UID = 1086753;
304     private static final int TEST_REAR_RIGHT_UID = 1286753;
305     private static final String MEDIA_CLIENT_ID = "media-client-id";
306     private static final String MEDIA_PACKAGE_NAME = "com.android.car.audio";
307     private static final int MEDIA_EMPTY_FLAG = 0;
308     private static final String REGISTRATION_ID = "meh";
309     private static final int MEDIA_VOLUME_GROUP_ID = 0;
310     private static final int NAVIGATION_VOLUME_GROUP_ID = 1;
311     private static final int INVALID_USAGE = -1;
312     private static final int INVALID_AUDIO_FEATURE = -1;
313     private static final int TEST_DRIVER_USER_ID = 10;
314     private static final int TEST_REAR_LEFT_USER_ID = 11;
315     private static final int TEST_REAR_RIGHT_USER_ID = 12;
316     private static final int TEST_FRONT_PASSENGER_USER_ID = 13;
317     private static final int TEST_REAR_ROW_3_PASSENGER_USER_ID = 14;
318     private static final int TEST_GAIN_INDEX = 4;
319 
320     // TODO(b/273800524): create a utility test class for audio attributes.
321     private static final AudioAttributes ATTRIBUTES_UNKNOWN =
322             CarAudioContext.getAudioAttributeFromUsage(USAGE_UNKNOWN);
323     private static final AudioAttributes ATTRIBUTES_GAME =
324             CarAudioContext.getAudioAttributeFromUsage(USAGE_GAME);
325     private static final AudioAttributes ATTRIBUTES_MEDIA =
326             CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA);
327     private static final AudioAttributes ATTRIBUTES_NOTIFICATION =
328             CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION);
329     private static final AudioAttributes ATTRIBUTES_NOTIFICATION_EVENT =
330             CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_EVENT);
331     private static final AudioAttributes ATTRIBUTES_ANNOUNCEMENT =
332             CarAudioContext.getAudioAttributeFromUsage(USAGE_ANNOUNCEMENT);
333     private static final AudioAttributes ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE =
334             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
335     private static final AudioAttributes ATTRIBUTES_ASSISTANCE_ACCESSIBILITY =
336             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANCE_ACCESSIBILITY);
337     private static final AudioAttributes ATTRIBUTES_ASSISTANT =
338             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANT);
339     private static final AudioAttributes ATTRIBUTES_NOTIFICATION_RINGTONE =
340             CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_RINGTONE);
341     private static final AudioAttributes ATTRIBUTES_VOICE_COMMUNICATION =
342             CarAudioContext.getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION);
343     private static final AudioAttributes ATTRIBUTES_CALL_ASSISTANT =
344             CarAudioContext.getAudioAttributeFromUsage(USAGE_CALL_ASSISTANT);
345     private static final AudioAttributes ATTRIBUTES_VOICE_COMMUNICATION_SIGNALLING =
346             CarAudioContext.getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION_SIGNALLING);
347     private static final AudioAttributes ATTRIBUTES_ALARM =
348             CarAudioContext.getAudioAttributeFromUsage(USAGE_ALARM);
349     private static final AudioAttributes ATTRIBUTES_ASSISTANCE_SONIFICATION =
350             CarAudioContext.getAudioAttributeFromUsage(USAGE_ASSISTANCE_SONIFICATION);
351     private static final AudioAttributes ATTRIBUTES_EMERGENCY =
352             CarAudioContext.getAudioAttributeFromUsage(USAGE_EMERGENCY);
353     private static final AudioAttributes ATTRIBUTES_SAFETY =
354             CarAudioContext.getAudioAttributeFromUsage(USAGE_SAFETY);
355     private static final AudioAttributes ATTRIBUTES_VEHICLE_STATUS =
356             CarAudioContext.getAudioAttributeFromUsage(USAGE_VEHICLE_STATUS);
357 
358     private static final List<AudioAttributes> TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0 = List.of(
359             ATTRIBUTES_UNKNOWN, ATTRIBUTES_GAME, ATTRIBUTES_MEDIA, ATTRIBUTES_NOTIFICATION,
360             ATTRIBUTES_NOTIFICATION_EVENT, ATTRIBUTES_ANNOUNCEMENT);
361 
362     private static final List<AudioAttributes> TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_1 = List.of(
363             ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE, ATTRIBUTES_ASSISTANCE_ACCESSIBILITY,
364             ATTRIBUTES_ASSISTANT);
365 
366     private static final List<AudioAttributes> TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_DEFAULT =
367             List.of(ATTRIBUTES_UNKNOWN, ATTRIBUTES_GAME, ATTRIBUTES_MEDIA,
368                     ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE, ATTRIBUTES_ASSISTANCE_ACCESSIBILITY,
369                     ATTRIBUTES_ASSISTANT, ATTRIBUTES_NOTIFICATION_RINGTONE,
370                     ATTRIBUTES_VOICE_COMMUNICATION, ATTRIBUTES_CALL_ASSISTANT,
371                     ATTRIBUTES_VOICE_COMMUNICATION_SIGNALLING, ATTRIBUTES_ALARM,
372                     ATTRIBUTES_NOTIFICATION, ATTRIBUTES_NOTIFICATION_EVENT,
373                     ATTRIBUTES_ASSISTANCE_SONIFICATION, ATTRIBUTES_EMERGENCY, ATTRIBUTES_SAFETY,
374                     ATTRIBUTES_VEHICLE_STATUS, ATTRIBUTES_ANNOUNCEMENT);
375 
376     private static final List<AudioAttributes> TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_0 = List.of(
377             ATTRIBUTES_UNKNOWN, ATTRIBUTES_GAME, ATTRIBUTES_MEDIA,
378             ATTRIBUTES_ASSISTANCE_NAVIGATION_GUIDANCE, ATTRIBUTES_ASSISTANCE_ACCESSIBILITY,
379             ATTRIBUTES_ASSISTANT, ATTRIBUTES_NOTIFICATION, ATTRIBUTES_NOTIFICATION_EVENT,
380             ATTRIBUTES_ANNOUNCEMENT);
381 
382     private static final List<AudioAttributes> TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_1 = List.of(
383             ATTRIBUTES_NOTIFICATION_RINGTONE, ATTRIBUTES_VOICE_COMMUNICATION,
384             ATTRIBUTES_CALL_ASSISTANT, ATTRIBUTES_VOICE_COMMUNICATION_SIGNALLING, ATTRIBUTES_ALARM,
385             ATTRIBUTES_ASSISTANCE_SONIFICATION, ATTRIBUTES_EMERGENCY, ATTRIBUTES_SAFETY,
386             ATTRIBUTES_VEHICLE_STATUS);
387 
388 
389     private static final CarVolumeGroupInfo TEST_PRIMARY_ZONE_VOLUME_INFO_0 =
390             new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_0,
391                     PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0).setMuted(true)
392                     .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
393                     .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
394                     .setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0).build();
395 
396     private static final CarVolumeGroupInfo TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0 =
397             new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_0,
398                     PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0).setMuted(false)
399                     .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
400                     .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE)
401                     .setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_0).build();
402 
403     private static final CarVolumeGroupInfo TEST_PRIMARY_ZONE_VOLUME_INFO_1 =
404             new CarVolumeGroupInfo.Builder("config 0 group " + TEST_PRIMARY_ZONE_GROUP_1,
405                     PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1).setMuted(true)
406                     .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
407                     .setAudioAttributes(TEST_PRIMARY_ZONE_AUDIO_ATTRIBUTES_1)
408                     .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
409 
410     private static final CarVolumeGroupInfo TEST_SECONDARY_ZONE_CONFIG_0_VOLUME_INFO =
411             new CarVolumeGroupInfo.Builder("config 0 group " + TEST_SECONDARY_ZONE_GROUP_0,
412                     TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0)
413                     .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
414                     .setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_DEFAULT)
415                     .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
416 
417     private static final CarVolumeGroupInfo TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_0 =
418             new CarVolumeGroupInfo.Builder("config 1 group " + TEST_SECONDARY_ZONE_GROUP_0,
419                     TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_0)
420                     .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
421                     .setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_0)
422                     .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
423 
424     private static final CarVolumeGroupInfo TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_1 =
425             new CarVolumeGroupInfo.Builder("config 1 group " + TEST_SECONDARY_ZONE_GROUP_1,
426                     TEST_REAR_LEFT_ZONE_ID, TEST_SECONDARY_ZONE_GROUP_1)
427                     .setMinVolumeGainIndex(0).setMaxVolumeGainIndex(MAX_GAIN / STEP_SIZE)
428                     .setAudioAttributes(TEST_SECONDARY_ZONE_AUDIO_ATTRIBUTES_1)
429                     .setVolumeGainIndex(DEFAULT_GAIN / STEP_SIZE).build();
430 
431     private static final AudioDeviceInfo MICROPHONE_TEST_DEVICE =
432             new AudioDeviceInfoBuilder().setAddressName(PRIMARY_ZONE_MICROPHONE_ADDRESS)
433             .setType(TYPE_BUILTIN_MIC)
434             .setIsSource(true)
435             .build();
436     private static final AudioDeviceInfo FM_TUNER_TEST_DEVICE =
437             new AudioDeviceInfoBuilder().setAddressName(PRIMARY_ZONE_FM_TUNER_ADDRESS)
438             .setType(TYPE_FM_TUNER)
439             .setIsSource(true)
440             .build();
441 
442     private static final AudioFocusInfo TEST_AUDIO_FOCUS_INFO =
443             new AudioFocusInfo(CarAudioContext
444                     .getAudioAttributeFromUsage(USAGE_VOICE_COMMUNICATION), MEDIA_APP_UID,
445             MEDIA_CLIENT_ID, "com.android.car.audio",
446             AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, AUDIOFOCUS_NONE, /* loss= */ 0,
447             Build.VERSION.SDK_INT);
448 
449     private static final AudioFocusInfo TEST_REAR_RIGHT_AUDIO_FOCUS_INFO =
450             new AudioFocusInfo(CarAudioContext
451             .getAudioAttributeFromUsage(USAGE_MEDIA), TEST_REAR_RIGHT_UID,
452             MEDIA_CLIENT_ID, "com.android.car.audio",
453             AUDIOFOCUS_GAIN_TRANSIENT_MAY_DUCK, AUDIOFOCUS_NONE, /* loss= */ 0,
454             Build.VERSION.SDK_INT);
455 
456     private static final CarVolumeGroupEvent TEST_CAR_VOLUME_GROUP_EVENT =
457             new CarVolumeGroupEvent.Builder(List.of(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0),
458                     CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED,
459                     List.of(CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_UI)).build();
460 
461     private static final CarVolumeGroupEvent TEST_CAR_MUTE_GROUP_EVENT =
462             new CarVolumeGroupEvent.Builder(List.of(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0),
463                     CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED,
464                     List.of(CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_UI)).build();
465 
466     private static final CarVolumeGroupEvent TEST_CAR_ZONE_RECONFIGURATION_EVENT =
467             new CarVolumeGroupEvent.Builder(List.of(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0),
468                     CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED,
469                     List.of(CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_UI)).build();
470 
471     private CarAudioService mCarAudioService;
472     @Mock
473     private Context mMockContext;
474     @Mock
475     private TelephonyManager mMockTelephonyManager;
476     @Mock
477     private AudioManager mAudioManager;
478     @Mock
479     private Resources mMockResources;
480     @Mock
481     private ContentResolver mMockContentResolver;
482     @Mock
483     IBinder mBinder;
484     @Mock
485     IBinder mVolumeCallbackBinder;
486     @Mock
487     IAudioControl mAudioControl;
488     @Mock
489     private PackageManager mMockPackageManager;
490     @Mock
491     private CarOccupantZoneService mMockOccupantZoneService;
492     @Mock
493     private CarOemProxyService mMockCarOemProxyService;
494     @Mock
495     private IAudioService mMockAudioService;
496     @Mock
497     private Uri mNavSettingUri;
498     @Mock
499     private AudioControlWrapperAidl mAudioControlWrapperAidl;
500     @Mock
501     private CarVolumeCallbackHandler mCarVolumeCallbackHandler;
502     @Mock
503     private CarInputService mMockCarInputService;
504 
505     // Not used directly, but sets proper mockStatic() expectations on Settings
506     @SuppressWarnings("UnusedVariable")
507     private MockSettings mMockSettings;
508 
509     private boolean mPersistMasterMute = true;
510     private boolean mUseDynamicRouting = true;
511     private boolean mUseHalAudioDucking = true;
512     private boolean mUseCarVolumeGroupMuting = true;
513     private boolean mUseCarVolumeGroupEvents = true;
514 
515     private TemporaryFile mTemporaryAudioConfigurationUsingCoreAudioFile;
516     private TemporaryFile mTemporaryAudioConfigurationFile;
517     private TemporaryFile mTemporaryAudioConfigurationWithoutZoneMappingFile;
518     private TemporaryFile mTemporaryAudioConfigurationWithoutMirroringFile;
519     private TemporaryFile mTemporaryAudioConfigurationWithOEMContexts;
520     private Context mContext;
521     private AudioDeviceInfo mMicrophoneInputDevice;
522     private AudioDeviceInfo mFmTunerInputDevice;
523     private AudioDeviceInfo mMediaOutputDevice;
524 
525     @Captor
526     private ArgumentCaptor<BroadcastReceiver> mVolumeReceiverCaptor;
527 
CarAudioServiceUnitTest()528     public CarAudioServiceUnitTest() {
529         super(CarAudioService.TAG);
530     }
531 
532     @Override
onSessionBuilder(CustomMockitoSessionBuilder session)533     protected void onSessionBuilder(CustomMockitoSessionBuilder session) {
534         mMockSettings = new MockSettings(session);
535         session
536                 .spyStatic(AudioManager.class)
537                 .spyStatic(AudioManagerHelper.class)
538                 .spyStatic(AudioControlWrapperAidl.class)
539                 .spyStatic(CoreAudioHelper.class)
540                 .spyStatic(AudioControlFactory.class)
541                 .spyStatic(SystemProperties.class)
542                 .spyStatic(ServiceManager.class)
543                 .spyStatic(Car.class);
544     }
545 
546     @Before
setUp()547     public void setUp() throws Exception {
548         mContext = ApplicationProvider.getApplicationContext();
549 
550         try (InputStream configurationStream = mContext.getResources().openRawResource(
551                 R.raw.car_audio_configuration)) {
552             mTemporaryAudioConfigurationFile = new TemporaryFile("xml");
553             mTemporaryAudioConfigurationFile.write(new String(configurationStream.readAllBytes()));
554             Log.i(TAG, "Temporary Car Audio Configuration File Location: "
555                     + mTemporaryAudioConfigurationFile.getPath());
556         }
557 
558         try (InputStream configurationStream = mContext.getResources().openRawResource(
559                 R.raw.car_audio_configuration_using_oem_defined_context)) {
560             mTemporaryAudioConfigurationWithOEMContexts = new TemporaryFile("xml");
561             mTemporaryAudioConfigurationWithOEMContexts.write(
562                     new String(configurationStream.readAllBytes()));
563             Log.i(TAG, "Temporary Car Audio Configuration with OEM Context File Location: "
564                     + mTemporaryAudioConfigurationWithOEMContexts.getPath());
565         }
566 
567 
568 
569         try (InputStream configurationStream = mContext.getResources().openRawResource(
570                 R.raw.car_audio_configuration_without_zone_mapping)) {
571             mTemporaryAudioConfigurationWithoutZoneMappingFile = new TemporaryFile("xml");
572             mTemporaryAudioConfigurationWithoutZoneMappingFile
573                     .write(new String(configurationStream.readAllBytes()));
574             Log.i(TAG, "Temporary Car Audio Configuration without Zone mapping File Location: "
575                     + mTemporaryAudioConfigurationWithoutZoneMappingFile.getPath());
576         }
577 
578         try (InputStream configurationStream = mContext.getResources().openRawResource(
579                 R.raw.car_audio_configuration_using_core_audio_routing_and_volume)) {
580             mTemporaryAudioConfigurationUsingCoreAudioFile = new TemporaryFile("xml");
581             mTemporaryAudioConfigurationUsingCoreAudioFile
582                     .write(new String(configurationStream.readAllBytes()));
583             Log.i(TAG, "Temporary Car Audio Configuration using Core Audio File Location: "
584                     + mTemporaryAudioConfigurationUsingCoreAudioFile.getPath());
585         }
586 
587         try (InputStream configurationStream = mContext.getResources().openRawResource(
588                 R.raw.car_audio_configuration_without_mirroring)) {
589             mTemporaryAudioConfigurationWithoutMirroringFile = new TemporaryFile("xml");
590             mTemporaryAudioConfigurationWithoutMirroringFile
591                     .write(new String(configurationStream.readAllBytes()));
592             Log.i(TAG, "Temporary Car Audio Configuration Without Mirroring File Location: "
593                     + mTemporaryAudioConfigurationWithoutMirroringFile.getPath());
594         }
595 
596         mockCarGetPlatformVersion(UPSIDE_DOWN_CAKE_0);
597 
598         mockCoreAudioRoutingAndVolume();
599         mockGrantCarControlAudioSettingsPermission();
600 
601         setupAudioControlHAL();
602         setupService();
603 
604         when(Settings.Secure.getUriFor(
605                 CarSettings.Secure.KEY_AUDIO_FOCUS_NAVIGATION_REJECTED_DURING_CALL))
606                 .thenReturn(mNavSettingUri);
607     }
608 
609     @After
tearDown()610     public void tearDown() throws Exception {
611         mTemporaryAudioConfigurationFile.close();
612         mTemporaryAudioConfigurationWithoutZoneMappingFile.close();
613         CarLocalServices.removeServiceForTest(CarOemProxyService.class);
614         CarLocalServices.removeServiceForTest(CarOccupantZoneService.class);
615     }
616 
setupAudioControlHAL()617     private void setupAudioControlHAL() {
618         when(mBinder.queryLocalInterface(anyString())).thenReturn(mAudioControl);
619         doReturn(mBinder).when(AudioControlWrapperAidl::getService);
620         when(mAudioControlWrapperAidl.supportsFeature(
621                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_DUCKING)).thenReturn(true);
622         when(mAudioControlWrapperAidl.supportsFeature(
623                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_FOCUS)).thenReturn(true);
624         when(mAudioControlWrapperAidl.supportsFeature(
625                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GAIN_CALLBACK)).thenReturn(true);
626         when(mAudioControlWrapperAidl.supportsFeature(
627                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_GROUP_MUTING)).thenReturn(true);
628         when(mAudioControlWrapperAidl.supportsFeature(
629                 AudioControlWrapper.AUDIOCONTROL_FEATURE_AUDIO_MODULE_CALLBACK)).thenReturn(true);
630         doReturn(mAudioControlWrapperAidl)
631                 .when(() -> AudioControlFactory.newAudioControl());
632     }
633 
setupService()634     private void setupService() throws Exception {
635         when(mMockContext.getSystemService(Context.TELEPHONY_SERVICE))
636                 .thenReturn(mMockTelephonyManager);
637         when(mMockContext.getSystemService(Context.AUDIO_SERVICE))
638                 .thenReturn(mAudioManager);
639         when(mMockContext.getPackageManager()).thenReturn(mMockPackageManager);
640         doReturn(true)
641                 .when(() -> AudioManagerHelper
642                         .setAudioDeviceGain(any(), any(), anyInt(), anyBoolean()));
643         doReturn(true)
644                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, false));
645 
646         when(mMockOccupantZoneService.getUserForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
647                 .thenReturn(TEST_DRIVER_USER_ID);
648         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
649         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
650                 .thenReturn(PRIMARY_AUDIO_ZONE);
651         when(mMockOccupantZoneService.getOccupantZoneForUser(UserHandle.of(TEST_DRIVER_USER_ID)))
652                 .thenReturn(TEST_DRIVER_OCCUPANT);
653 
654         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
655                 .thenReturn(TEST_REAR_LEFT_USER_ID);
656         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
657                 .thenReturn(TEST_REAR_RIGHT_USER_ID);
658         when(mMockOccupantZoneService.getUserForOccupant(TEST_FRONT_OCCUPANT_ZONE_ID))
659                 .thenReturn(TEST_FRONT_PASSENGER_USER_ID);
660         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_ROW_3_OCCUPANT_ZONE_ID))
661                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_USER_ID);
662         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
663                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
664         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
665                 .thenReturn(TEST_REAR_RIGHT_ZONE_ID);
666         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_FRONT_OCCUPANT_ZONE_ID))
667                 .thenReturn(TEST_FRONT_PASSENGER_USER_ID);
668         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_REAR_ROW_3_ZONE_ID))
669                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_USER_ID);
670         when(mMockOccupantZoneService.getOccupantZoneForUser(
671                 UserHandle.of(TEST_REAR_RIGHT_USER_ID))).thenReturn(
672                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
673         when(mMockOccupantZoneService.getOccupantZoneForUser(
674                 UserHandle.of(TEST_FRONT_PASSENGER_USER_ID))).thenReturn(
675                 TEST_FRONT_PASSENGER_OCCUPANT);
676         when(mMockOccupantZoneService.getOccupantZoneForUser(
677                 UserHandle.of(TEST_REAR_LEFT_USER_ID))).thenReturn(
678                 TEST_REAR_LEFT_PASSENGER_OCCUPANT);
679         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_ROW_3_ZONE_ID))
680                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_OCCUPANT);
681         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_RIGHT_ZONE_ID))
682                 .thenReturn(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
683         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_FRONT_ZONE_ID))
684                 .thenReturn(TEST_FRONT_PASSENGER_OCCUPANT);
685         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_LEFT_ZONE_ID))
686                 .thenReturn(TEST_REAR_LEFT_PASSENGER_OCCUPANT);
687         when(mMockOccupantZoneService.getOccupantForAudioZoneId(TEST_REAR_ROW_3_ZONE_ID))
688                 .thenReturn(TEST_REAR_ROW_3_PASSENGER_OCCUPANT);
689 
690         CarLocalServices.removeServiceForTest(CarOccupantZoneService.class);
691         CarLocalServices.addService(CarOccupantZoneService.class, mMockOccupantZoneService);
692         CarLocalServices.removeServiceForTest(CarInputService.class);
693         CarLocalServices.addService(CarInputService.class, mMockCarInputService);
694 
695         CarLocalServices.removeServiceForTest(CarOemProxyService.class);
696         CarLocalServices.addService(CarOemProxyService.class, mMockCarOemProxyService);
697 
698         setupAudioManager();
699 
700         setupResources();
701 
702         mCarAudioService =
703                 new CarAudioService(mMockContext,
704                         mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
705                         mCarVolumeCallbackHandler);
706     }
707 
setupAudioManager()708     private void setupAudioManager() throws Exception {
709         AudioDeviceInfo[] outputDevices = generateOutputDeviceInfos();
710         AudioDeviceInfo[] inputDevices = generateInputDeviceInfos();
711         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS))
712                 .thenReturn(outputDevices);
713         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS))
714                .thenReturn(inputDevices);
715         when(mMockContext.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager);
716 
717         when(mAudioManager.registerAudioPolicy(any())).thenAnswer(invocation -> {
718             AudioPolicy policy = (AudioPolicy) invocation.getArguments()[0];
719             policy.setRegistration(REGISTRATION_ID);
720             return SUCCESS;
721         });
722 
723         IBinder mockBinder = mock(IBinder.class);
724         when(mockBinder.queryLocalInterface(any())).thenReturn(mMockAudioService);
725         doReturn(mockBinder).when(() -> ServiceManager.getService(Context.AUDIO_SERVICE));
726     }
727 
setupResources()728     private void setupResources() {
729         when(mMockContext.getContentResolver()).thenReturn(mMockContentResolver);
730         when(mMockContext.createContextAsUser(any(), anyInt())).thenReturn(mMockContext);
731         when(mMockContext.getResources()).thenReturn(mMockResources);
732         when(mMockResources.getBoolean(audioUseDynamicRouting)).thenReturn(mUseDynamicRouting);
733         when(mMockResources.getInteger(audioVolumeKeyEventTimeoutMs))
734                 .thenReturn(VOLUME_KEY_EVENT_TIMEOUT_MS);
735         when(mMockResources.getBoolean(audioUseHalDuckingSignals)).thenReturn(mUseHalAudioDucking);
736         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting))
737                 .thenReturn(mUseCarVolumeGroupMuting);
738         when(mMockResources.getBoolean(audioUseCarVolumeGroupEvent))
739                 .thenReturn(mUseCarVolumeGroupEvents);
740         when(mMockResources.getInteger(audioVolumeAdjustmentContextsVersion))
741                 .thenReturn(AUDIO_CONTEXT_PRIORITY_LIST_VERSION_TWO);
742         when(mMockResources.getBoolean(audioPersistMasterMuteState)).thenReturn(mPersistMasterMute);
743     }
744 
745     @Test
constructor_withNullContext_fails()746     public void constructor_withNullContext_fails() {
747         NullPointerException thrown =
748                 assertThrows(NullPointerException.class, () -> new CarAudioService(null));
749 
750         expectWithMessage("Car Audio Service Construction")
751                 .that(thrown).hasMessageThat().contains("Context");
752     }
753 
754     @Test
constructor_withNullContextAndNullPath_fails()755     public void constructor_withNullContextAndNullPath_fails() {
756         NullPointerException thrown =
757                 assertThrows(NullPointerException.class,
758                         () -> new CarAudioService(/* context= */null,
759                                 /* audioConfigurationPath= */ null,
760                                 /* carVolumeCallbackHandler= */ null));
761 
762         expectWithMessage("Car Audio Service Construction")
763                 .that(thrown).hasMessageThat().contains("Context");
764     }
765 
766     @Test
constructor_withInvalidVolumeConfiguration_fails()767     public void constructor_withInvalidVolumeConfiguration_fails() {
768         when(mMockResources.getInteger(audioVolumeAdjustmentContextsVersion))
769                 .thenReturn(AUDIO_CONTEXT_PRIORITY_LIST_VERSION_ONE);
770 
771         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
772                 () -> new CarAudioService(mMockContext));
773 
774         expectWithMessage("Car Audio Service Construction")
775                 .that(thrown).hasMessageThat()
776                 .contains("requires audioVolumeAdjustmentContextsVersion 2");
777     }
778 
779     @Test
getAudioZoneIds_withBaseConfiguration_returnAllTheZones()780     public void getAudioZoneIds_withBaseConfiguration_returnAllTheZones() {
781         mCarAudioService.init();
782 
783         expectWithMessage("Car Audio Service Zones")
784                 .that(mCarAudioService.getAudioZoneIds()).asList()
785                 .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID,
786                         TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID);
787     }
788 
789     @Test
getVolumeGroupCount_onPrimaryZone_returnsAllGroups()790     public void getVolumeGroupCount_onPrimaryZone_returnsAllGroups() {
791         mCarAudioService.init();
792 
793         expectWithMessage("Primary zone car volume group count")
794                 .that(mCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
795                 .isEqualTo(PRIMARY_ZONE_VOLUME_GROUP_COUNT);
796     }
797 
798     @Test
getVolumeGroupCount_onPrimaryZone_withNonDynamicRouting_returnsAllGroups()799     public void getVolumeGroupCount_onPrimaryZone_withNonDynamicRouting_returnsAllGroups() {
800         when(mMockResources.getBoolean(audioUseDynamicRouting))
801                 .thenReturn(/* useDynamicRouting= */ false);
802         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
803                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
804                 mCarVolumeCallbackHandler);
805         nonDynamicAudioService.init();
806 
807         expectWithMessage("Non dynamic routing primary zone car volume group count")
808                 .that(nonDynamicAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
809                 .isEqualTo(CarAudioDynamicRouting.STREAM_TYPES.length);
810     }
811 
812     @Test
getVolumeGroupIdForUsage_forMusicUsage()813     public void getVolumeGroupIdForUsage_forMusicUsage() {
814         mCarAudioService.init();
815 
816         expectWithMessage("Primary zone's media car volume group id")
817                 .that(mCarAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA))
818                 .isEqualTo(MEDIA_VOLUME_GROUP_ID);
819     }
820 
821     @Test
getVolumeGroupIdForUsage_withNonDynamicRouting_forMusicUsage()822     public void getVolumeGroupIdForUsage_withNonDynamicRouting_forMusicUsage() {
823         when(mMockResources.getBoolean(audioUseDynamicRouting))
824                 .thenReturn(/* useDynamicRouting= */ false);
825         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
826                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
827                 mCarVolumeCallbackHandler);
828         nonDynamicAudioService.init();
829 
830         expectWithMessage("Non dynamic routing primary zone's media car volume group id")
831                 .that(nonDynamicAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
832                         USAGE_MEDIA)).isEqualTo(MEDIA_VOLUME_GROUP_ID);
833     }
834 
835     @Test
getVolumeGroupIdForUsage_forNavigationUsage()836     public void getVolumeGroupIdForUsage_forNavigationUsage() {
837         mCarAudioService.init();
838 
839         expectWithMessage("Primary zone's navigation car volume group id")
840                 .that(mCarAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
841                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE))
842                 .isEqualTo(NAVIGATION_VOLUME_GROUP_ID);
843     }
844 
845     @Test
getVolumeGroupIdForUsage_withNonDynamicRouting_forNavigationUsage()846     public void getVolumeGroupIdForUsage_withNonDynamicRouting_forNavigationUsage() {
847         when(mMockResources.getBoolean(audioUseDynamicRouting))
848                 .thenReturn(/* useDynamicRouting= */ false);
849         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
850                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
851                 mCarVolumeCallbackHandler);
852         nonDynamicAudioService.init();
853 
854         expectWithMessage("Non dynamic routing primary zone's navigation car volume group id")
855                 .that(nonDynamicAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
856                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE))
857                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
858     }
859 
860     @Test
getVolumeGroupIdForUsage_forInvalidUsage_returnsInvalidGroupId()861     public void getVolumeGroupIdForUsage_forInvalidUsage_returnsInvalidGroupId() {
862         mCarAudioService.init();
863 
864         expectWithMessage("Primary zone's invalid car volume group id")
865                 .that(mCarAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, INVALID_USAGE))
866                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
867     }
868 
869     @Test
870     public void
getVolumeGroupIdForUsage_forInvalidUsage_withNonDynamicRouting_returnsInvalidGroupId()871             getVolumeGroupIdForUsage_forInvalidUsage_withNonDynamicRouting_returnsInvalidGroupId() {
872         when(mMockResources.getBoolean(audioUseDynamicRouting))
873                 .thenReturn(/* useDynamicRouting= */ false);
874         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
875                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
876                 mCarVolumeCallbackHandler);
877         nonDynamicAudioService.init();
878 
879         expectWithMessage("Non dynamic routing primary zone's invalid car volume group id")
880                 .that(nonDynamicAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
881                         INVALID_USAGE)).isEqualTo(INVALID_VOLUME_GROUP_ID);
882     }
883 
884     @Test
getVolumeGroupIdForUsage_forUnknownUsage_returnsMediaGroupId()885     public void getVolumeGroupIdForUsage_forUnknownUsage_returnsMediaGroupId() {
886         mCarAudioService.init();
887 
888         expectWithMessage("Primary zone's unknown car volume group id")
889                 .that(mCarAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, USAGE_UNKNOWN))
890                 .isEqualTo(MEDIA_VOLUME_GROUP_ID);
891     }
892 
893     @Test
getVolumeGroupIdForUsage_forVirtualUsage_returnsInvalidGroupId()894     public void getVolumeGroupIdForUsage_forVirtualUsage_returnsInvalidGroupId() {
895         mCarAudioService.init();
896 
897         expectWithMessage("Primary zone's virtual car volume group id")
898                 .that(mCarAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE,
899                         AudioManagerHelper.getUsageVirtualSource()))
900                 .isEqualTo(INVALID_VOLUME_GROUP_ID);
901     }
902 
903     @Test
getVolumeGroupCount_onSecondaryZone_returnsAllGroups()904     public void getVolumeGroupCount_onSecondaryZone_returnsAllGroups() {
905         mCarAudioService.init();
906 
907         expectWithMessage("Secondary Zone car volume group count")
908                 .that(mCarAudioService.getVolumeGroupCount(TEST_REAR_LEFT_ZONE_ID))
909                 .isEqualTo(SECONDARY_ZONE_VOLUME_GROUP_COUNT);
910     }
911 
912     @Test
getUsagesForVolumeGroupId_forMusicContext()913     public void getUsagesForVolumeGroupId_forMusicContext() {
914         mCarAudioService.init();
915 
916 
917         expectWithMessage("Primary zone's music car volume group id usages")
918                 .that(mCarAudioService.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE,
919                         MEDIA_VOLUME_GROUP_ID)).asList()
920                 .containsExactly(USAGE_UNKNOWN, USAGE_GAME, USAGE_MEDIA, USAGE_ANNOUNCEMENT,
921                         USAGE_NOTIFICATION, USAGE_NOTIFICATION_EVENT);
922     }
923 
924     @Test
getUsagesForVolumeGroupId_forSystemContext()925     public void getUsagesForVolumeGroupId_forSystemContext() {
926         mCarAudioService.init();
927         int systemVolumeGroup =
928                 mCarAudioService.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, USAGE_EMERGENCY);
929 
930         expectWithMessage("Primary zone's system car volume group id usages")
931                 .that(mCarAudioService.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE,
932                         systemVolumeGroup)).asList().containsExactly(USAGE_ALARM, USAGE_EMERGENCY,
933                         USAGE_SAFETY, USAGE_VEHICLE_STATUS, USAGE_ASSISTANCE_SONIFICATION);
934     }
935 
936     @Test
getUsagesForVolumeGroupId_onSecondaryZone_forSingleVolumeGroupId_returnAllUsages()937     public void getUsagesForVolumeGroupId_onSecondaryZone_forSingleVolumeGroupId_returnAllUsages() {
938         mCarAudioService.init();
939 
940         expectWithMessage("Secondary Zone's car volume group id usages")
941                 .that(mCarAudioService.getUsagesForVolumeGroupId(TEST_REAR_LEFT_ZONE_ID,
942                         SECONDARY_ZONE_VOLUME_GROUP_ID))
943                 .asList().containsExactly(USAGE_UNKNOWN, USAGE_MEDIA,
944                         USAGE_VOICE_COMMUNICATION, USAGE_VOICE_COMMUNICATION_SIGNALLING,
945                         USAGE_ALARM, USAGE_NOTIFICATION, USAGE_NOTIFICATION_RINGTONE,
946                         USAGE_NOTIFICATION_EVENT, USAGE_ASSISTANCE_ACCESSIBILITY,
947                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE, USAGE_ASSISTANCE_SONIFICATION,
948                         USAGE_GAME, USAGE_ASSISTANT, USAGE_CALL_ASSISTANT, USAGE_EMERGENCY,
949                         USAGE_ANNOUNCEMENT, USAGE_SAFETY, USAGE_VEHICLE_STATUS);
950     }
951 
952     @Test
getUsagesForVolumeGroupId_withoutDynamicRouting()953     public void getUsagesForVolumeGroupId_withoutDynamicRouting() {
954         when(mMockResources.getBoolean(audioUseDynamicRouting))
955                 .thenReturn(/* useDynamicRouting= */ false);
956         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
957                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
958                 mCarVolumeCallbackHandler);
959         nonDynamicAudioService.init();
960 
961         expectWithMessage("Media car volume group id without dynamic routing").that(
962                 nonDynamicAudioService.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE,
963                 MEDIA_VOLUME_GROUP_ID)).asList()
964                 .containsExactly(CarAudioDynamicRouting.STREAM_TYPE_USAGES[MEDIA_VOLUME_GROUP_ID]);
965     }
966 
967     @Test
createAudioPatch_onMediaOutputDevice_failsForConfigurationMissing()968     public void createAudioPatch_onMediaOutputDevice_failsForConfigurationMissing() {
969         mCarAudioService.init();
970 
971         doReturn(false)
972                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, false));
973 
974         IllegalStateException thrown = assertThrows(IllegalStateException.class,
975                 () -> mCarAudioService
976                         .createAudioPatch(PRIMARY_ZONE_FM_TUNER_ADDRESS,
977                                 USAGE_MEDIA, DEFAULT_GAIN));
978 
979         expectWithMessage("FM and Media Audio Patch Exception")
980                 .that(thrown).hasMessageThat().contains("Audio Patch APIs not enabled");
981     }
982 
983     @Test
createAudioPatch_onMediaOutputDevice_failsForMissingPermission()984     public void createAudioPatch_onMediaOutputDevice_failsForMissingPermission() {
985         mCarAudioService.init();
986 
987         mockDenyCarControlAudioSettingsPermission();
988 
989         SecurityException thrown = assertThrows(SecurityException.class,
990                 () -> mCarAudioService
991                         .createAudioPatch(PRIMARY_ZONE_FM_TUNER_ADDRESS,
992                                 USAGE_MEDIA, DEFAULT_GAIN));
993 
994         expectWithMessage("FM and Media Audio Patch Permission Exception")
995                 .that(thrown).hasMessageThat().contains(PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
996     }
997 
998     @Test
createAudioPatch_onMediaOutputDevice_succeeds()999     public void createAudioPatch_onMediaOutputDevice_succeeds() {
1000         mCarAudioService.init();
1001 
1002         mockGrantCarControlAudioSettingsPermission();
1003         doReturn(false)
1004                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, true));
1005         doReturn(new AudioPatchInfo(PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE, 0))
1006                 .when(() -> AudioManagerHelper
1007                         .createAudioPatch(mFmTunerInputDevice, mMediaOutputDevice, DEFAULT_GAIN));
1008 
1009         CarAudioPatchHandle audioPatch = mCarAudioService
1010                 .createAudioPatch(PRIMARY_ZONE_FM_TUNER_ADDRESS, USAGE_MEDIA, DEFAULT_GAIN);
1011 
1012         expectWithMessage("Audio Patch Sink Address")
1013                 .that(audioPatch.getSinkAddress()).isEqualTo(MEDIA_TEST_DEVICE);
1014         expectWithMessage("Audio Patch Source Address")
1015                 .that(audioPatch.getSourceAddress()).isEqualTo(PRIMARY_ZONE_FM_TUNER_ADDRESS);
1016         expectWithMessage("Audio Patch Handle")
1017                 .that(audioPatch.getHandleId()).isEqualTo(0);
1018     }
1019 
1020     @Test
releaseAudioPatch_failsForConfigurationMissing()1021     public void releaseAudioPatch_failsForConfigurationMissing() {
1022         mCarAudioService.init();
1023 
1024         doReturn(false)
1025                 .when(() -> SystemProperties.getBoolean(PROPERTY_RO_ENABLE_AUDIO_PATCH, false));
1026         CarAudioPatchHandle carAudioPatchHandle =
1027                 new CarAudioPatchHandle(0, PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE);
1028 
1029         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1030                 () -> mCarAudioService.releaseAudioPatch(carAudioPatchHandle));
1031 
1032         expectWithMessage("Release FM and Media Audio Patch Exception")
1033                 .that(thrown).hasMessageThat().contains("Audio Patch APIs not enabled");
1034     }
1035 
1036     @Test
releaseAudioPatch_failsForMissingPermission()1037     public void releaseAudioPatch_failsForMissingPermission() {
1038         mCarAudioService.init();
1039 
1040         mockDenyCarControlAudioSettingsPermission();
1041         CarAudioPatchHandle carAudioPatchHandle =
1042                 new CarAudioPatchHandle(0, PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE);
1043 
1044         SecurityException thrown = assertThrows(SecurityException.class,
1045                 () -> mCarAudioService.releaseAudioPatch(carAudioPatchHandle));
1046 
1047         expectWithMessage("FM and Media Audio Patch Permission Exception")
1048                 .that(thrown).hasMessageThat().contains(PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1049     }
1050 
1051     @Test
releaseAudioPatch_forNullSourceAddress_throwsNullPointerException()1052     public void releaseAudioPatch_forNullSourceAddress_throwsNullPointerException() {
1053         mCarAudioService.init();
1054         mockGrantCarControlAudioSettingsPermission();
1055         doReturn(new AudioPatchInfo(PRIMARY_ZONE_FM_TUNER_ADDRESS, MEDIA_TEST_DEVICE, 0))
1056                 .when(() -> AudioManagerHelper
1057                         .createAudioPatch(mFmTunerInputDevice, mMediaOutputDevice, DEFAULT_GAIN));
1058 
1059         CarAudioPatchHandle audioPatch = mock(CarAudioPatchHandle.class);
1060         when(audioPatch.getSourceAddress()).thenReturn(null);
1061 
1062         NullPointerException thrown = assertThrows(NullPointerException.class,
1063                 () -> mCarAudioService.releaseAudioPatch(audioPatch));
1064 
1065         expectWithMessage("Release audio patch for null source address "
1066                 + "and sink address Null Exception")
1067                 .that(thrown).hasMessageThat()
1068                 .contains("Source Address can not be null for patch id 0");
1069     }
1070 
1071     @Test
releaseAudioPatch_failsForNullPatch()1072     public void releaseAudioPatch_failsForNullPatch() {
1073         mCarAudioService.init();
1074 
1075         assertThrows(NullPointerException.class,
1076                 () -> mCarAudioService.releaseAudioPatch(null));
1077     }
1078 
1079     @Test
setZoneIdForUid_withoutRoutingPermission_fails()1080     public void setZoneIdForUid_withoutRoutingPermission_fails() {
1081         mCarAudioService.init();
1082 
1083         mockDenyCarControlAudioSettingsPermission();
1084 
1085         SecurityException thrown = assertThrows(SecurityException.class,
1086                 () -> mCarAudioService.setZoneIdForUid(OUT_OF_RANGE_ZONE, MEDIA_APP_UID));
1087 
1088         expectWithMessage("Set Zone for UID Permission Exception")
1089                 .that(thrown).hasMessageThat()
1090                 .contains(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1091     }
1092 
1093     @Test
setZoneIdForUid_withoutDynamicRouting_fails()1094     public void setZoneIdForUid_withoutDynamicRouting_fails() {
1095         when(mMockResources.getBoolean(audioUseDynamicRouting))
1096                 .thenReturn(/* useDynamicRouting= */ false);
1097         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1098                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1099                 mCarVolumeCallbackHandler);
1100         nonDynamicAudioService.init();
1101 
1102         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1103                 () -> nonDynamicAudioService.setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID));
1104 
1105         expectWithMessage("Set Zone for UID Dynamic Configuration Exception")
1106                 .that(thrown).hasMessageThat()
1107                 .contains("Dynamic routing is required");
1108     }
1109 
1110     @Test
setZoneIdForUid_withInvalidZone_fails()1111     public void setZoneIdForUid_withInvalidZone_fails() {
1112         mCarAudioService.init();
1113 
1114         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
1115                 () -> mCarAudioService.setZoneIdForUid(INVALID_AUDIO_ZONE, MEDIA_APP_UID));
1116 
1117         expectWithMessage("Set Zone for UID Invalid Zone Exception")
1118                 .that(thrown).hasMessageThat()
1119                 .contains("Invalid audio zone Id " + INVALID_AUDIO_ZONE);
1120     }
1121 
1122     @Test
setZoneIdForUid_withOutOfRangeZone_fails()1123     public void setZoneIdForUid_withOutOfRangeZone_fails() {
1124         mCarAudioService.init();
1125 
1126         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
1127                 () -> mCarAudioService.setZoneIdForUid(OUT_OF_RANGE_ZONE, MEDIA_APP_UID));
1128 
1129         expectWithMessage("Set Zone for UID Zone Out of Range Exception")
1130                 .that(thrown).hasMessageThat()
1131                 .contains("Invalid audio zone Id " + OUT_OF_RANGE_ZONE);
1132     }
1133 
1134     @Test
setZoneIdForUid_withZoneAudioMapping_fails()1135     public void setZoneIdForUid_withZoneAudioMapping_fails() {
1136         mCarAudioService.init();
1137 
1138         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1139                 () -> mCarAudioService.setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID));
1140 
1141         expectWithMessage("Set Zone for UID With Audio Zone Mapping Exception")
1142                 .that(thrown).hasMessageThat()
1143                 .contains("UID based routing is not supported while using occupant zone mapping");
1144     }
1145 
1146     @Test
setZoneIdForUid_withValidZone_succeeds()1147     public void setZoneIdForUid_withValidZone_succeeds() throws Exception {
1148         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1149                 .thenReturn(SUCCESS);
1150         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1151                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1152                 mCarVolumeCallbackHandler);
1153         noZoneMappingAudioService.init();
1154 
1155         boolean results = noZoneMappingAudioService
1156                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1157 
1158         expectWithMessage("Set Zone for UID Status").that(results).isTrue();
1159     }
1160 
1161     @Test
setZoneIdForUid_onDifferentZones_succeeds()1162     public void setZoneIdForUid_onDifferentZones_succeeds() throws Exception {
1163         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1164                 .thenReturn(SUCCESS);
1165         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1166                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1167                 mCarVolumeCallbackHandler);
1168         noZoneMappingAudioService.init();
1169 
1170         noZoneMappingAudioService
1171                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1172 
1173         boolean results = noZoneMappingAudioService
1174                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1175 
1176         expectWithMessage("Set Zone for UID For Different Zone")
1177                 .that(results).isTrue();
1178     }
1179 
1180     @Test
setZoneIdForUid_onDifferentZones_withAudioFocus_succeeds()1181     public void setZoneIdForUid_onDifferentZones_withAudioFocus_succeeds() throws Exception {
1182         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1183                 .thenReturn(SUCCESS);
1184         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1185                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1186                 mCarVolumeCallbackHandler);
1187         noZoneMappingAudioService.init();
1188         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia();
1189 
1190         noZoneMappingAudioService
1191                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1192 
1193         noZoneMappingAudioService
1194                 .requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
1195 
1196         boolean results = noZoneMappingAudioService
1197                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1198 
1199         expectWithMessage("Set Zone for UID For Different Zone with Audio Focus")
1200                 .that(results).isTrue();
1201     }
1202 
1203     @Test
getZoneIdForUid_withoutMappedUid_succeeds()1204     public void getZoneIdForUid_withoutMappedUid_succeeds() throws Exception {
1205         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1206                 .thenReturn(SUCCESS);
1207         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1208                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1209                 mCarVolumeCallbackHandler);
1210         noZoneMappingAudioService.init();
1211 
1212         int zoneId = noZoneMappingAudioService
1213                 .getZoneIdForUid(MEDIA_APP_UID);
1214 
1215         expectWithMessage("Get Zone for Non Mapped UID")
1216                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1217     }
1218 
1219     @Test
getZoneIdForUid_succeeds()1220     public void getZoneIdForUid_succeeds() throws Exception {
1221         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1222                 .thenReturn(SUCCESS);
1223         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1224                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1225                 mCarVolumeCallbackHandler);
1226         noZoneMappingAudioService.init();
1227 
1228         noZoneMappingAudioService
1229                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1230 
1231         int zoneId = noZoneMappingAudioService
1232                 .getZoneIdForUid(MEDIA_APP_UID);
1233 
1234         expectWithMessage("Get Zone for UID Zone Id")
1235                 .that(zoneId).isEqualTo(TEST_REAR_LEFT_ZONE_ID);
1236     }
1237 
1238     @Test
getZoneIdForUid_afterSwitchingZones_succeeds()1239     public void getZoneIdForUid_afterSwitchingZones_succeeds() throws Exception {
1240         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1241                 .thenReturn(SUCCESS);
1242         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1243                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1244                 mCarVolumeCallbackHandler);
1245         noZoneMappingAudioService.init();
1246 
1247         noZoneMappingAudioService
1248                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1249 
1250         noZoneMappingAudioService
1251                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1252 
1253         int zoneId = noZoneMappingAudioService
1254                 .getZoneIdForUid(MEDIA_APP_UID);
1255 
1256         expectWithMessage("Get Zone for UID Zone Id")
1257                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1258     }
1259 
1260     @Test
clearZoneIdForUid_withoutRoutingPermission_fails()1261     public void clearZoneIdForUid_withoutRoutingPermission_fails() {
1262         mCarAudioService.init();
1263 
1264         mockDenyCarControlAudioSettingsPermission();
1265 
1266         SecurityException thrown = assertThrows(SecurityException.class,
1267                 () -> mCarAudioService.clearZoneIdForUid(MEDIA_APP_UID));
1268 
1269         expectWithMessage("Clear Zone for UID Permission Exception")
1270                 .that(thrown).hasMessageThat()
1271                 .contains(Car.PERMISSION_CAR_CONTROL_AUDIO_SETTINGS);
1272     }
1273 
1274     @Test
clearZoneIdForUid_withoutDynamicRouting_fails()1275     public void clearZoneIdForUid_withoutDynamicRouting_fails() {
1276         when(mMockResources.getBoolean(audioUseDynamicRouting))
1277                 .thenReturn(/* useDynamicRouting= */ false);
1278         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1279                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1280                 mCarVolumeCallbackHandler);
1281         nonDynamicAudioService.init();
1282 
1283         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1284                 () -> nonDynamicAudioService.clearZoneIdForUid(MEDIA_APP_UID));
1285 
1286         expectWithMessage("Clear Zone for UID Dynamic Configuration Exception")
1287                 .that(thrown).hasMessageThat()
1288                 .contains("Dynamic routing is required");
1289     }
1290 
1291     @Test
clearZoneIdForUid_withZoneAudioMapping_fails()1292     public void clearZoneIdForUid_withZoneAudioMapping_fails() {
1293         mCarAudioService.init();
1294 
1295         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1296                 () -> mCarAudioService.clearZoneIdForUid(MEDIA_APP_UID));
1297 
1298         expectWithMessage("Clear Zone for UID Audio Zone Mapping Exception")
1299                 .that(thrown).hasMessageThat()
1300                 .contains("UID based routing is not supported while using occupant zone mapping");
1301     }
1302 
1303     @Test
clearZoneIdForUid_forNonMappedUid_succeeds()1304     public void clearZoneIdForUid_forNonMappedUid_succeeds() throws Exception {
1305         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1306                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1307                 mCarVolumeCallbackHandler);
1308         noZoneMappingAudioService.init();
1309 
1310         boolean status = noZoneMappingAudioService
1311                 .clearZoneIdForUid(MEDIA_APP_UID);
1312 
1313         expectWithMessage("Clear Zone for UID Audio Zone without Mapping")
1314                 .that(status).isTrue();
1315     }
1316 
1317     @Test
clearZoneIdForUid_forMappedUid_succeeds()1318     public void clearZoneIdForUid_forMappedUid_succeeds() throws Exception {
1319         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1320                 .thenReturn(SUCCESS);
1321         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1322                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1323                 mCarVolumeCallbackHandler);
1324         noZoneMappingAudioService.init();
1325 
1326         noZoneMappingAudioService
1327                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1328 
1329         boolean status = noZoneMappingAudioService.clearZoneIdForUid(MEDIA_APP_UID);
1330 
1331         expectWithMessage("Clear Zone for UID Audio Zone with Mapping")
1332                 .that(status).isTrue();
1333     }
1334 
1335     @Test
getZoneIdForUid_afterClearedUidMapping_returnsDefaultZone()1336     public void getZoneIdForUid_afterClearedUidMapping_returnsDefaultZone() throws Exception {
1337         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1338                 .thenReturn(SUCCESS);
1339         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1340                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1341                 mCarVolumeCallbackHandler);
1342         noZoneMappingAudioService.init();
1343 
1344         noZoneMappingAudioService
1345                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1346 
1347         noZoneMappingAudioService.clearZoneIdForUid(MEDIA_APP_UID);
1348 
1349         int zoneId = noZoneMappingAudioService.getZoneIdForUid(MEDIA_APP_UID);
1350 
1351         expectWithMessage("Get Zone for UID Audio Zone with Cleared Mapping")
1352                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1353     }
1354 
1355     @Test
getZoneIdForAudioFocusInfo_withoutMappedUid_succeeds()1356     public void getZoneIdForAudioFocusInfo_withoutMappedUid_succeeds() throws Exception {
1357         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1358                 .thenReturn(SUCCESS);
1359         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1360                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1361                 mCarVolumeCallbackHandler);
1362         noZoneMappingAudioService.init();
1363 
1364         int zoneId = noZoneMappingAudioService
1365                 .getZoneIdForAudioFocusInfo(TEST_AUDIO_FOCUS_INFO);
1366 
1367         expectWithMessage("Mapped audio focus info's zone")
1368                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1369     }
1370 
1371     @Test
getZoneIdForAudioFocusInfo_succeeds()1372     public void getZoneIdForAudioFocusInfo_succeeds() throws Exception {
1373         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1374                 .thenReturn(SUCCESS);
1375         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1376                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1377                 mCarVolumeCallbackHandler);
1378         noZoneMappingAudioService.init();
1379 
1380         noZoneMappingAudioService
1381                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1382 
1383         int zoneId = noZoneMappingAudioService
1384                 .getZoneIdForAudioFocusInfo(TEST_AUDIO_FOCUS_INFO);
1385 
1386         expectWithMessage("Mapped audio focus info's zone")
1387                 .that(zoneId).isEqualTo(TEST_REAR_LEFT_ZONE_ID);
1388     }
1389 
1390     @Test
getZoneIdForAudioFocusInfo_afterSwitchingZones_succeeds()1391     public void getZoneIdForAudioFocusInfo_afterSwitchingZones_succeeds() throws Exception {
1392         when(mMockAudioService.setUidDeviceAffinity(any(), anyInt(), any(), any()))
1393                 .thenReturn(SUCCESS);
1394         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1395                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1396                 mCarVolumeCallbackHandler);
1397         noZoneMappingAudioService.init();
1398         noZoneMappingAudioService
1399                 .setZoneIdForUid(TEST_REAR_LEFT_ZONE_ID, MEDIA_APP_UID);
1400         noZoneMappingAudioService
1401                 .setZoneIdForUid(PRIMARY_AUDIO_ZONE, MEDIA_APP_UID);
1402 
1403         int zoneId = noZoneMappingAudioService
1404                 .getZoneIdForAudioFocusInfo(TEST_AUDIO_FOCUS_INFO);
1405 
1406         expectWithMessage("Remapped audio focus info's zone")
1407                 .that(zoneId).isEqualTo(PRIMARY_AUDIO_ZONE);
1408     }
1409 
1410     @Test
setGroupVolume_withoutPermission_fails()1411     public void setGroupVolume_withoutPermission_fails() {
1412         mCarAudioService.init();
1413 
1414         mockDenyCarControlAudioVolumePermission();
1415 
1416         SecurityException thrown = assertThrows(SecurityException.class,
1417                 () -> mCarAudioService.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1418                         TEST_GAIN_INDEX, TEST_FLAGS));
1419 
1420         expectWithMessage("Set Volume Group Permission Exception")
1421                 .that(thrown).hasMessageThat()
1422                 .contains(Car.PERMISSION_CAR_CONTROL_AUDIO_VOLUME);
1423     }
1424 
1425     @Test
setGroupVolume_withDynamicRoutingDisabled()1426     public void setGroupVolume_withDynamicRoutingDisabled() {
1427         when(mMockResources.getBoolean(audioUseDynamicRouting))
1428                 .thenReturn(/* useDynamicRouting= */ false);
1429         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1430                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1431                 mCarVolumeCallbackHandler);
1432         nonDynamicAudioService.init();
1433 
1434         nonDynamicAudioService.setGroupVolume(
1435                 PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0, TEST_GAIN_INDEX, TEST_FLAGS);
1436 
1437         verify(mAudioManager).setStreamVolume(
1438                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0],
1439                 TEST_GAIN_INDEX,
1440                 TEST_FLAGS);
1441     }
1442 
1443     @Test
setGroupVolume_verifyNoCallbacks()1444     public void setGroupVolume_verifyNoCallbacks() {
1445         mCarAudioService.init();
1446         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1447                 /* mute= */ false, TEST_FLAGS);
1448         reset(mCarVolumeCallbackHandler);
1449 
1450         mCarAudioService.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1451                 TEST_GAIN_INDEX, TEST_FLAGS);
1452 
1453         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
1454     }
1455 
1456     @Test
setGroupVolume_afterSetVolumeGroupMute()1457     public void setGroupVolume_afterSetVolumeGroupMute() {
1458         mCarAudioService.init();
1459         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1460                 /* mute= */ true, TEST_FLAGS);
1461         reset(mCarVolumeCallbackHandler);
1462 
1463         mCarAudioService.setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1464                 TEST_GAIN_INDEX, TEST_FLAGS);
1465 
1466         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
1467                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
1468     }
1469 
1470     @Test
setGroupVolume_withVolumeGroupMutingDisabled_doesnotThrowException()1471     public void setGroupVolume_withVolumeGroupMutingDisabled_doesnotThrowException() {
1472         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting)).thenReturn(false);
1473         CarAudioService nonVolumeGroupMutingAudioService = new CarAudioService(mMockContext,
1474                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1475                 mCarVolumeCallbackHandler);
1476         nonVolumeGroupMutingAudioService.init();
1477         HalAudioGainCallback callback = getHalAudioGainCallback();
1478         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(PRIMARY_AUDIO_ZONE,
1479                 MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
1480         callback.onAudioDeviceGainsChanged(List.of(Reasons.TCU_MUTE), List.of(carGain));
1481         reset(mCarVolumeCallbackHandler);
1482 
1483         nonVolumeGroupMutingAudioService.setGroupVolume(
1484                 PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0, TEST_GAIN_INDEX, TEST_FLAGS);
1485 
1486         // if an exception is thrown, the test automatically fails
1487         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(eq(PRIMARY_AUDIO_ZONE),
1488                 eq(TEST_PRIMARY_ZONE_GROUP_0), anyInt());
1489         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
1490     }
1491 
1492     @Test
getOutputDeviceAddressForUsage_forMusicUsage()1493     public void getOutputDeviceAddressForUsage_forMusicUsage() {
1494         mCarAudioService.init();
1495 
1496         String mediaDeviceAddress =
1497                 mCarAudioService.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA);
1498 
1499         expectWithMessage("Media usage audio device address")
1500                 .that(mediaDeviceAddress).isEqualTo(MEDIA_TEST_DEVICE);
1501     }
1502 
1503     @Test
getOutputDeviceAddressForUsage_withNonDynamicRouting_forMediaUsage_fails()1504     public void getOutputDeviceAddressForUsage_withNonDynamicRouting_forMediaUsage_fails() {
1505         when(mMockResources.getBoolean(audioUseDynamicRouting))
1506                 .thenReturn(/* useDynamicRouting= */ false);
1507         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1508                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1509                 mCarVolumeCallbackHandler);
1510         nonDynamicAudioService.init();
1511 
1512         IllegalStateException thrown = assertThrows(IllegalStateException.class,
1513                 () -> nonDynamicAudioService
1514                         .getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE, USAGE_MEDIA));
1515 
1516         expectWithMessage("Non dynamic routing media usage audio device address exception")
1517                 .that(thrown).hasMessageThat().contains("Dynamic routing is required");
1518     }
1519 
1520     @Test
getOutputDeviceAddressForUsage_forNavigationUsage()1521     public void getOutputDeviceAddressForUsage_forNavigationUsage() {
1522         mCarAudioService.init();
1523 
1524         String mediaDeviceAddress =
1525                 mCarAudioService.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1526                         USAGE_ASSISTANCE_NAVIGATION_GUIDANCE);
1527 
1528         expectWithMessage("Navigation usage audio device address")
1529                 .that(mediaDeviceAddress).isEqualTo(NAVIGATION_TEST_DEVICE);
1530     }
1531 
1532     @Test
getOutputDeviceAddressForUsage_forInvalidUsage_fails()1533     public void getOutputDeviceAddressForUsage_forInvalidUsage_fails() {
1534         mCarAudioService.init();
1535 
1536         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
1537                 mCarAudioService.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1538                         INVALID_USAGE));
1539 
1540         expectWithMessage("Invalid usage audio device address exception")
1541                 .that(thrown).hasMessageThat().contains("Invalid audio attribute " + INVALID_USAGE);
1542     }
1543 
1544     @Test
getOutputDeviceAddressForUsage_forVirtualUsage_fails()1545     public void getOutputDeviceAddressForUsage_forVirtualUsage_fails() {
1546         mCarAudioService.init();
1547 
1548         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
1549                 mCarAudioService.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE,
1550                         AudioManagerHelper.getUsageVirtualSource()));
1551 
1552         expectWithMessage("Invalid context audio device address exception")
1553                 .that(thrown).hasMessageThat()
1554                 .contains("invalid");
1555     }
1556 
1557     @Test
getOutputDeviceAddressForUsage_onSecondaryZone_forMusicUsage()1558     public void getOutputDeviceAddressForUsage_onSecondaryZone_forMusicUsage() {
1559         mCarAudioService.init();
1560 
1561         String mediaDeviceAddress = mCarAudioService.getOutputDeviceAddressForUsage(
1562                 TEST_REAR_LEFT_ZONE_ID, USAGE_MEDIA);
1563 
1564         expectWithMessage("Media usage audio device address for secondary zone")
1565                 .that(mediaDeviceAddress).isEqualTo(SECONDARY_TEST_DEVICE_CONFIG_0);
1566     }
1567 
1568     @Test
getSuggestedAudioContextForZone_inPrimaryZone()1569     public void getSuggestedAudioContextForZone_inPrimaryZone() {
1570         mCarAudioService.init();
1571         int defaultAudioContext = mCarAudioService.getCarAudioContext()
1572                 .getContextForAudioAttribute(CAR_DEFAULT_AUDIO_ATTRIBUTE);
1573 
1574         expectWithMessage("Suggested audio context for primary zone")
1575                 .that(mCarAudioService.getSuggestedAudioContextForZone(PRIMARY_AUDIO_ZONE))
1576                 .isEqualTo(defaultAudioContext);
1577     }
1578 
1579     @Test
getSuggestedAudioContextForZone_inSecondaryZone()1580     public void getSuggestedAudioContextForZone_inSecondaryZone() {
1581         mCarAudioService.init();
1582         int defaultAudioContext = mCarAudioService.getCarAudioContext()
1583                 .getContextForAudioAttribute(CAR_DEFAULT_AUDIO_ATTRIBUTE);
1584 
1585         expectWithMessage("Suggested audio context for secondary zone")
1586                 .that(mCarAudioService.getSuggestedAudioContextForZone(TEST_REAR_LEFT_ZONE_ID))
1587                 .isEqualTo(defaultAudioContext);
1588     }
1589 
1590     @Test
getSuggestedAudioContextForZone_inInvalidZone()1591     public void getSuggestedAudioContextForZone_inInvalidZone() {
1592         mCarAudioService.init();
1593 
1594         expectWithMessage("Suggested audio context for invalid zone")
1595                 .that(mCarAudioService.getSuggestedAudioContextForZone(INVALID_AUDIO_ZONE))
1596                 .isEqualTo(CarAudioContext.getInvalidContext());
1597     }
1598 
1599     @Test
isVolumeGroupMuted_noSetVolumeGroupMute()1600     public void isVolumeGroupMuted_noSetVolumeGroupMute() {
1601         mCarAudioService.init();
1602 
1603         expectWithMessage("Volume group mute for default state")
1604                 .that(mCarAudioService.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
1605                         TEST_PRIMARY_ZONE_GROUP_0)).isFalse();
1606     }
1607 
1608     @Test
isVolumeGroupMuted_setVolumeGroupMuted_isFalse()1609     public void isVolumeGroupMuted_setVolumeGroupMuted_isFalse() {
1610         mCarAudioService.init();
1611         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1612                 /* mute= */ true, TEST_FLAGS);
1613 
1614         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1615                 /* mute= */ false, TEST_FLAGS);
1616 
1617         expectWithMessage("Volume group muted after mute and unmute")
1618                 .that(mCarAudioService.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
1619                         TEST_PRIMARY_ZONE_GROUP_0)).isFalse();
1620     }
1621 
1622     @Test
isVolumeGroupMuted_setVolumeGroupMuted_isTrue()1623     public void isVolumeGroupMuted_setVolumeGroupMuted_isTrue() {
1624         mCarAudioService.init();
1625 
1626         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
1627                 /* mute= */ true, TEST_FLAGS);
1628         expectWithMessage("Volume group muted after mute")
1629                 .that(mCarAudioService.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
1630                         TEST_PRIMARY_ZONE_GROUP_0)).isTrue();
1631     }
1632 
1633     @Test
isVolumeGroupMuted_withVolumeGroupMutingDisabled()1634     public void isVolumeGroupMuted_withVolumeGroupMutingDisabled() {
1635         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting))
1636                 .thenReturn(false);
1637         CarAudioService nonVolumeGroupMutingAudioService = new CarAudioService(mMockContext,
1638                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1639                 mCarVolumeCallbackHandler);
1640         nonVolumeGroupMutingAudioService.init();
1641 
1642         expectWithMessage("Volume group for disabled volume group muting")
1643                 .that(nonVolumeGroupMutingAudioService.isVolumeGroupMuted(
1644                         PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0))
1645                 .isFalse();
1646     }
1647 
1648     @Test
getGroupMaxVolume_forPrimaryZone()1649     public void getGroupMaxVolume_forPrimaryZone() {
1650         mCarAudioService.init();
1651 
1652         expectWithMessage("Group max volume for primary audio zone and group")
1653                 .that(mCarAudioService.getGroupMaxVolume(PRIMARY_AUDIO_ZONE,
1654                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo((MAX_GAIN - MIN_GAIN) / STEP_SIZE);
1655     }
1656 
1657     @Test
getGroupMinVolume_forPrimaryZone()1658     public void getGroupMinVolume_forPrimaryZone() {
1659         mCarAudioService.init();
1660 
1661         expectWithMessage("Group Min Volume for primary audio zone and group")
1662                 .that(mCarAudioService.getGroupMinVolume(PRIMARY_AUDIO_ZONE,
1663                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(0);
1664     }
1665 
1666     @Test
getGroupCurrentVolume_forPrimaryZone()1667     public void getGroupCurrentVolume_forPrimaryZone() {
1668         mCarAudioService.init();
1669 
1670         expectWithMessage("Current group volume for primary audio zone and group")
1671                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
1672                         TEST_PRIMARY_ZONE_GROUP_0))
1673                 .isEqualTo((DEFAULT_GAIN - MIN_GAIN) / STEP_SIZE);
1674     }
1675 
1676     @Test
getGroupMaxVolume_withNoDynamicRouting()1677     public void getGroupMaxVolume_withNoDynamicRouting() {
1678         when(mMockResources.getBoolean(audioUseDynamicRouting))
1679                 .thenReturn(/* useDynamicRouting= */ false);
1680         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1681                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1682                 mCarVolumeCallbackHandler);
1683         nonDynamicAudioService.init();
1684 
1685         nonDynamicAudioService.getGroupMaxVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
1686 
1687         verify(mAudioManager).getStreamMaxVolume(
1688                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0]);
1689     }
1690 
1691     @Test
getGroupMinVolume_withNoDynamicRouting()1692     public void getGroupMinVolume_withNoDynamicRouting() {
1693         when(mMockResources.getBoolean(audioUseDynamicRouting))
1694                 .thenReturn(/* useDynamicRouting= */ false);
1695         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1696                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1697                 mCarVolumeCallbackHandler);
1698         nonDynamicAudioService.init();
1699 
1700         nonDynamicAudioService.getGroupMinVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
1701 
1702         verify(mAudioManager).getStreamMinVolume(
1703                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0]);
1704     }
1705 
1706     @Test
getGroupCurrentVolume_withNoDynamicRouting()1707     public void getGroupCurrentVolume_withNoDynamicRouting() {
1708         when(mMockResources.getBoolean(audioUseDynamicRouting))
1709                 .thenReturn(/* useDynamicRouting= */ false);
1710         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1711                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1712                 mCarVolumeCallbackHandler);
1713         nonDynamicAudioService.init();
1714 
1715         nonDynamicAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
1716 
1717         verify(mAudioManager).getStreamVolume(
1718                 CarAudioDynamicRouting.STREAM_TYPES[TEST_PRIMARY_ZONE_GROUP_0]);
1719     }
1720 
1721     @Test
setBalanceTowardRight_nonNullValue()1722     public void setBalanceTowardRight_nonNullValue() {
1723         mCarAudioService.init();
1724 
1725         mCarAudioService.setBalanceTowardRight(TEST_VALUE);
1726 
1727         verify(mAudioControlWrapperAidl).setBalanceTowardRight(TEST_VALUE);
1728     }
1729 
1730     @Test
setBalanceTowardRight_throws()1731     public void setBalanceTowardRight_throws() {
1732         mCarAudioService.init();
1733 
1734         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, ()
1735                 -> mCarAudioService.setBalanceTowardRight(INVALID_TEST_VALUE));
1736 
1737         expectWithMessage("Out of bounds balance")
1738                 .that(thrown).hasMessageThat()
1739                 .contains(String.format("Balance is out of range of [%f, %f]", -1f, 1f));
1740     }
1741 
1742     @Test
setFadeTowardFront_nonNullValue()1743     public void setFadeTowardFront_nonNullValue() {
1744         mCarAudioService.init();
1745 
1746         mCarAudioService.setFadeTowardFront(TEST_VALUE);
1747 
1748         verify(mAudioControlWrapperAidl).setFadeTowardFront(TEST_VALUE);
1749     }
1750 
1751     @Test
setFadeTowardFront_throws()1752     public void setFadeTowardFront_throws() {
1753         mCarAudioService.init();
1754 
1755         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, ()
1756                 -> mCarAudioService.setFadeTowardFront(INVALID_TEST_VALUE));
1757 
1758         expectWithMessage("Out of bounds fade")
1759                 .that(thrown).hasMessageThat()
1760                 .contains(String.format("Fade is out of range of [%f, %f]", -1f, 1f));
1761     }
1762 
1763     @Test
isAudioFeatureEnabled_forDynamicRouting()1764     public void isAudioFeatureEnabled_forDynamicRouting() {
1765         mCarAudioService.init();
1766 
1767         expectWithMessage("Dynamic routing audio feature")
1768                 .that(mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING))
1769                 .isEqualTo(mUseDynamicRouting);
1770     }
1771 
1772     @Test
isAudioFeatureEnabled_forDisabledDynamicRouting()1773     public void isAudioFeatureEnabled_forDisabledDynamicRouting() {
1774         when(mMockResources.getBoolean(audioUseDynamicRouting))
1775                 .thenReturn(/* useDynamicRouting= */ false);
1776         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
1777                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1778                 mCarVolumeCallbackHandler);
1779         nonDynamicAudioService.init();
1780 
1781         expectWithMessage("Disabled dynamic routing audio feature")
1782                 .that(nonDynamicAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING))
1783                 .isFalse();
1784     }
1785 
1786     @Test
isAudioFeatureEnabled_forVolumeGroupMuting()1787     public void isAudioFeatureEnabled_forVolumeGroupMuting() {
1788         mCarAudioService.init();
1789 
1790         expectWithMessage("Group muting audio feature")
1791                 .that(mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING))
1792                 .isEqualTo(mUseCarVolumeGroupMuting);
1793     }
1794 
1795     @Test
isAudioFeatureEnabled_forDisabledVolumeGroupMuting()1796     public void isAudioFeatureEnabled_forDisabledVolumeGroupMuting() {
1797         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting)).thenReturn(false);
1798         CarAudioService nonVolumeGroupMutingAudioService = new CarAudioService(mMockContext,
1799                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1800                 mCarVolumeCallbackHandler);
1801         nonVolumeGroupMutingAudioService.init();
1802 
1803         expectWithMessage("Disabled group muting audio feature")
1804                 .that(nonVolumeGroupMutingAudioService
1805                         .isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING))
1806                 .isFalse();
1807     }
1808 
1809     @Test
isAudioFeatureEnabled_forVolumeGroupEvent()1810     public void isAudioFeatureEnabled_forVolumeGroupEvent() {
1811         mCarAudioService.init();
1812 
1813         expectWithMessage("Group events audio feature")
1814                 .that(mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS))
1815                 .isEqualTo(mUseCarVolumeGroupEvents);
1816     }
1817 
1818     @Test
isAudioFeatureEnabled_forDisabledVolumeGroupEvent()1819     public void isAudioFeatureEnabled_forDisabledVolumeGroupEvent() {
1820         when(mMockResources.getBoolean(audioUseCarVolumeGroupEvent)).thenReturn(false);
1821         CarAudioService nonVolumeGroupEventsAudioService = new CarAudioService(mMockContext,
1822                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
1823                 mCarVolumeCallbackHandler);
1824         nonVolumeGroupEventsAudioService.init();
1825 
1826         expectWithMessage("Disabled group event audio feature")
1827                 .that(nonVolumeGroupEventsAudioService
1828                         .isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS))
1829                 .isFalse();
1830     }
1831 
1832     @Test
isAudioFeatureEnabled_forUnrecognizableAudioFeature_throws()1833     public void isAudioFeatureEnabled_forUnrecognizableAudioFeature_throws() {
1834         mCarAudioService.init();
1835 
1836         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class,
1837                 () -> mCarAudioService.isAudioFeatureEnabled(INVALID_AUDIO_FEATURE));
1838 
1839         expectWithMessage("Unknown audio feature")
1840                 .that(thrown).hasMessageThat()
1841                 .contains("Unknown Audio Feature type: " + INVALID_AUDIO_FEATURE);
1842     }
1843 
1844     @Test
isAudioFeatureEnabled_forDisabledOemService()1845     public void isAudioFeatureEnabled_forDisabledOemService() {
1846         mCarAudioService.init();
1847 
1848         boolean isEnabled =
1849                 mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1850 
1851         expectWithMessage("Oem service enabled with disabled oem service")
1852                 .that(isEnabled).isFalse();
1853     }
1854 
1855     @Test
isAudioFeatureEnabled_withEnabledFocusService()1856     public void isAudioFeatureEnabled_withEnabledFocusService() {
1857         CarOemAudioFocusProxyService service = mock(CarOemAudioFocusProxyService.class);
1858         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1859         when(mMockCarOemProxyService.getCarOemAudioFocusService()).thenReturn(service);
1860         mCarAudioService.init();
1861 
1862         boolean isEnabled =
1863                 mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1864 
1865         expectWithMessage("Oem service enabled with enabled focus service")
1866                 .that(isEnabled).isTrue();
1867     }
1868 
1869     @Test
isAudioFeatureEnabled_withEnabledFocusServiceAndReleaseLessThanU()1870     public void isAudioFeatureEnabled_withEnabledFocusServiceAndReleaseLessThanU() {
1871         mockCarGetPlatformVersion(TIRAMISU_1);
1872         CarOemAudioFocusProxyService service = mock(CarOemAudioFocusProxyService.class);
1873         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1874         when(mMockCarOemProxyService.getCarOemAudioFocusService()).thenReturn(service);
1875         mCarAudioService.init();
1876 
1877         boolean isEnabled =
1878                 mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1879 
1880         expectWithMessage("Oem service enabled with release less than U")
1881                 .that(isEnabled).isFalse();
1882     }
1883 
1884     @Test
isAudioFeatureEnabled_withEnabledVolumeService()1885     public void isAudioFeatureEnabled_withEnabledVolumeService() {
1886         CarOemAudioVolumeProxyService service = mock(CarOemAudioVolumeProxyService.class);
1887         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1888         when(mMockCarOemProxyService.getCarOemAudioVolumeService()).thenReturn(service);
1889         mCarAudioService.init();
1890 
1891         boolean isEnabled =
1892                 mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1893 
1894         expectWithMessage("Oem service enabled with enabled volume service")
1895                 .that(isEnabled).isTrue();
1896     }
1897 
1898     @Test
isAudioFeatureEnabled_withEnabledDuckingService()1899     public void isAudioFeatureEnabled_withEnabledDuckingService() {
1900         CarOemAudioDuckingProxyService service = mock(CarOemAudioDuckingProxyService.class);
1901         when(mMockCarOemProxyService.isOemServiceEnabled()).thenReturn(true);
1902         when(mMockCarOemProxyService.getCarOemAudioDuckingService()).thenReturn(service);
1903         mCarAudioService.init();
1904 
1905         boolean isEnabled =
1906                 mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_OEM_AUDIO_SERVICE);
1907 
1908         expectWithMessage("Oem service enabled with enabled ducking service")
1909                 .that(isEnabled).isTrue();
1910     }
1911 
1912     @Test
isAudioFeatureEnabled_withEnabledAudioMirror()1913     public void isAudioFeatureEnabled_withEnabledAudioMirror() {
1914         mCarAudioService.init();
1915 
1916         boolean isEnabled = mCarAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_AUDIO_MIRRORING);
1917 
1918         expectWithMessage("Audio mirror enabled status")
1919                 .that(isEnabled).isTrue();
1920     }
1921 
1922     @Test
isAudioFeatureEnabled_withDisabledAudioMirror()1923     public void isAudioFeatureEnabled_withDisabledAudioMirror() {
1924         CarAudioService carAudioService = getCarAudioServiceWithoutMirroring();
1925         carAudioService.init();
1926 
1927         boolean isEnabled = carAudioService.isAudioFeatureEnabled(AUDIO_FEATURE_AUDIO_MIRRORING);
1928 
1929         expectWithMessage("Audio mirror enabled status")
1930                 .that(isEnabled).isFalse();
1931     }
1932 
1933     @Test
onOccupantZoneConfigChanged_noUserAssignedToPrimaryZone()1934     public void onOccupantZoneConfigChanged_noUserAssignedToPrimaryZone() throws Exception {
1935         mCarAudioService.init();
1936         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(UserManagerHelper.USER_NULL);
1937         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
1938                 .thenReturn(UserManagerHelper.USER_NULL);
1939         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
1940         int prevUserId = mCarAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE);
1941 
1942         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
1943 
1944         expectWithMessage("User ID before config changed")
1945                 .that(mCarAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE))
1946                 .isEqualTo(prevUserId);
1947     }
1948 
1949     @Test
onOccupantZoneConfigChanged_userAssignedToPrimaryZone()1950     public void onOccupantZoneConfigChanged_userAssignedToPrimaryZone() throws Exception {
1951         mCarAudioService.init();
1952         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
1953         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
1954                 .thenReturn(TEST_REAR_LEFT_USER_ID);
1955         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
1956 
1957         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
1958 
1959         expectWithMessage("User ID after config changed")
1960                 .that(mCarAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE))
1961                 .isEqualTo(TEST_REAR_LEFT_USER_ID);
1962     }
1963 
1964     @Test
onOccupantZoneConfigChanged_afterResettingUser_returnNoUser()1965     public void onOccupantZoneConfigChanged_afterResettingUser_returnNoUser() throws Exception {
1966         mCarAudioService.init();
1967         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
1968         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
1969                 .thenReturn(TEST_REAR_LEFT_USER_ID);
1970         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
1971         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
1972         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
1973                 .thenReturn(UserManagerHelper.USER_NULL);
1974 
1975         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
1976 
1977         expectWithMessage("User ID config changed to null")
1978                 .that(mCarAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE))
1979                 .isEqualTo(UserManagerHelper.USER_NULL);
1980     }
1981 
1982     @Test
onOccupantZoneConfigChanged_noOccupantZoneMapping()1983     public void onOccupantZoneConfigChanged_noOccupantZoneMapping() throws Exception {
1984         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1985                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
1986                 mCarVolumeCallbackHandler);
1987         noZoneMappingAudioService.init();
1988         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
1989 
1990         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
1991 
1992         verify(mMockOccupantZoneService, never()).getUserForOccupant(anyInt());
1993     }
1994 
1995     @Test
onOccupantZoneConfigChanged_noOccupantZoneMapping_alreadyAssigned()1996     public void onOccupantZoneConfigChanged_noOccupantZoneMapping_alreadyAssigned()
1997             throws Exception {
1998         CarAudioService noZoneMappingAudioService = new CarAudioService(mMockContext,
1999                 mTemporaryAudioConfigurationWithoutZoneMappingFile.getFile().getAbsolutePath(),
2000                 mCarVolumeCallbackHandler);
2001         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2002         noZoneMappingAudioService.init();
2003         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2004         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2005         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2006 
2007         verify(mMockOccupantZoneService, never()).getUserForOccupant(anyInt());
2008         expectWithMessage("Occupant Zone for primary zone")
2009                 .that(noZoneMappingAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2010                 .isEqualTo(TEST_DRIVER_USER_ID);
2011     }
2012 
2013     @Test
onOccupantZoneConfigChanged_multipleZones()2014     public void onOccupantZoneConfigChanged_multipleZones() throws Exception {
2015         mCarAudioService.init();
2016         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
2017         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
2018                 .thenReturn(TEST_REAR_LEFT_USER_ID, TEST_REAR_RIGHT_USER_ID);
2019         ICarOccupantZoneCallback callback = getOccupantZoneCallback();
2020 
2021         callback.onOccupantZoneConfigChanged(CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
2022 
2023         expectWithMessage("User ID for primary and secondary zone after config changed")
2024                 .that(mCarAudioService.getUserIdForZone(PRIMARY_AUDIO_ZONE))
2025                 .isNotEqualTo(mCarAudioService.getUserIdForZone(TEST_REAR_LEFT_ZONE_ID));
2026         expectWithMessage("Secondary user ID config changed")
2027                 .that(mCarAudioService.getUserIdForZone(TEST_REAR_LEFT_ZONE_ID))
2028                 .isEqualTo(TEST_REAR_RIGHT_USER_ID);
2029     }
2030 
2031     @Test
serviceDied_registersAudioGainCallback()2032     public void serviceDied_registersAudioGainCallback() {
2033         mCarAudioService.init();
2034         ArgumentCaptor<AudioControlDeathRecipient> captor =
2035                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2036         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2037         AudioControlDeathRecipient runnable = captor.getValue();
2038         reset(mAudioControlWrapperAidl);
2039 
2040         runnable.serviceDied();
2041 
2042         verify(mAudioControlWrapperAidl).registerAudioGainCallback(any());
2043     }
2044 
2045     @Test
serviceDied_registersFocusListener()2046     public void serviceDied_registersFocusListener() {
2047         mCarAudioService.init();
2048         ArgumentCaptor<AudioControlDeathRecipient> captor =
2049                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2050         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2051         AudioControlDeathRecipient runnable = captor.getValue();
2052         reset(mAudioControlWrapperAidl);
2053 
2054         runnable.serviceDied();
2055 
2056         verify(mAudioControlWrapperAidl).registerFocusListener(any());
2057     }
2058 
2059     @Test
serviceDied_setsModuleChangeCallback()2060     public void serviceDied_setsModuleChangeCallback() {
2061         mCarAudioService.init();
2062         ArgumentCaptor<AudioControlDeathRecipient> captor =
2063                 ArgumentCaptor.forClass(AudioControlDeathRecipient.class);
2064         verify(mAudioControlWrapperAidl).linkToDeath(captor.capture());
2065         AudioControlDeathRecipient runnable = captor.getValue();
2066         reset(mAudioControlWrapperAidl);
2067 
2068         runnable.serviceDied();
2069 
2070         verify(mAudioControlWrapperAidl).setModuleChangeCallback(any());
2071     }
2072 
getOccupantZoneCallback()2073     private ICarOccupantZoneCallback getOccupantZoneCallback() {
2074         ArgumentCaptor<ICarOccupantZoneCallback> captor =
2075                 ArgumentCaptor.forClass(ICarOccupantZoneCallback.class);
2076         verify(mMockOccupantZoneService).registerCallback(captor.capture());
2077         return captor.getValue();
2078     }
2079 
2080     @Test
getVolumeGroupIdForAudioContext_forPrimaryGroup()2081     public void getVolumeGroupIdForAudioContext_forPrimaryGroup() {
2082         mCarAudioService.init();
2083 
2084         expectWithMessage("Volume group ID for primary audio zone")
2085                 .that(mCarAudioService.getVolumeGroupIdForAudioContext(PRIMARY_AUDIO_ZONE,
2086                         CarAudioContext.MUSIC))
2087                 .isEqualTo(TEST_PRIMARY_ZONE_GROUP_0);
2088     }
2089 
2090     @Test
getVolumeGroupIdForAudioAttribute()2091     public void getVolumeGroupIdForAudioAttribute() {
2092         mCarAudioService.init();
2093 
2094         expectWithMessage("Volume group ID for primary audio zone")
2095                 .that(mCarAudioService.getVolumeGroupIdForAudioAttribute(PRIMARY_AUDIO_ZONE,
2096                         CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA)))
2097                 .isEqualTo(TEST_PRIMARY_ZONE_GROUP_0);
2098     }
2099 
2100     @Test
getVolumeGroupIdForAudioAttribute_withNullAttribute_fails()2101     public void getVolumeGroupIdForAudioAttribute_withNullAttribute_fails() {
2102         mCarAudioService.init();
2103 
2104         NullPointerException thrown = assertThrows(NullPointerException.class, () ->
2105                 mCarAudioService.getVolumeGroupIdForAudioAttribute(PRIMARY_AUDIO_ZONE,
2106                 /* attribute= */ null));
2107 
2108         expectWithMessage("Null audio attribute exception").that(thrown).hasMessageThat()
2109                 .contains("Audio attributes");
2110     }
2111 
2112     @Test
getVolumeGroupIdForAudioAttribute_withInvalidZoneId_fails()2113     public void getVolumeGroupIdForAudioAttribute_withInvalidZoneId_fails() {
2114         mCarAudioService.init();
2115 
2116         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, () ->
2117                 mCarAudioService.getVolumeGroupIdForAudioAttribute(INVALID_AUDIO_ZONE,
2118                         CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA)));
2119 
2120         expectWithMessage("Invalid audio zone exception").that(thrown).hasMessageThat()
2121                 .contains("Invalid audio zone Id");
2122     }
2123 
2124     @Test
getInputDevicesForZoneId_primaryZone()2125     public void getInputDevicesForZoneId_primaryZone() {
2126         mCarAudioService.init();
2127 
2128         expectWithMessage("Get input device for primary zone id")
2129                 .that(mCarAudioService.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE))
2130                 .containsExactly(new AudioDeviceAttributes(mMicrophoneInputDevice));
2131     }
2132 
2133     @Test
getExternalSources_forSingleDevice()2134     public void getExternalSources_forSingleDevice() {
2135         mCarAudioService.init();
2136         AudioDeviceInfo[] inputDevices = generateInputDeviceInfos();
2137 
2138         expectWithMessage("External input device addresses")
2139                 .that(mCarAudioService.getExternalSources())
2140                 .asList().containsExactly(inputDevices[1].getAddress());
2141     }
2142 
2143     @Test
setAudioEnabled_forEnabledVolumeGroupMuting()2144     public void setAudioEnabled_forEnabledVolumeGroupMuting() {
2145         mCarAudioService.init();
2146 
2147         mCarAudioService.setAudioEnabled(/* enabled= */ true);
2148 
2149         verify(mAudioControlWrapperAidl).onDevicesToMuteChange(any());
2150     }
2151 
2152     @Test
setAudioEnabled_forDisabledVolumeGroupMuting()2153     public void setAudioEnabled_forDisabledVolumeGroupMuting() {
2154         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting)).thenReturn(false);
2155         CarAudioService nonVolumeGroupMutingAudioService = new CarAudioService(mMockContext,
2156                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
2157                 mCarVolumeCallbackHandler);
2158         nonVolumeGroupMutingAudioService.init();
2159 
2160         nonVolumeGroupMutingAudioService.setAudioEnabled(/* enabled= */ true);
2161 
2162         verify(mAudioControlWrapperAidl, never()).onDevicesToMuteChange(any());
2163     }
2164 
2165     @Test
registerVolumeCallback_verifyCallbackHandler()2166     public void registerVolumeCallback_verifyCallbackHandler() {
2167         int uid = Binder.getCallingUid();
2168         mCarAudioService.init();
2169 
2170         mCarAudioService.registerVolumeCallback(mVolumeCallbackBinder);
2171 
2172         verify(mCarVolumeCallbackHandler).registerCallback(mVolumeCallbackBinder, uid, true);
2173     }
2174 
2175     @Test
unregisterVolumeCallback_verifyCallbackHandler()2176     public void unregisterVolumeCallback_verifyCallbackHandler() {
2177         int uid = Binder.getCallingUid();
2178         mCarAudioService.init();
2179 
2180         mCarAudioService.unregisterVolumeCallback(mVolumeCallbackBinder);
2181 
2182         verify(mCarVolumeCallbackHandler).unregisterCallback(mVolumeCallbackBinder, uid);
2183     }
2184 
2185     @Test
getMutedVolumeGroups_forInvalidZone()2186     public void getMutedVolumeGroups_forInvalidZone() {
2187         mCarAudioService.init();
2188 
2189         expectWithMessage("Muted volume groups for invalid zone")
2190                 .that(mCarAudioService.getMutedVolumeGroups(INVALID_AUDIO_ZONE))
2191                 .isEmpty();
2192     }
2193 
2194     @Test
getMutedVolumeGroups_whenVolumeGroupMuteNotSupported()2195     public void getMutedVolumeGroups_whenVolumeGroupMuteNotSupported() {
2196         when(mMockResources.getBoolean(audioUseCarVolumeGroupMuting)).thenReturn(false);
2197         CarAudioService nonVolumeGroupMutingAudioService = new CarAudioService(mMockContext,
2198                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
2199                 mCarVolumeCallbackHandler);
2200         nonVolumeGroupMutingAudioService.init();
2201 
2202         expectWithMessage("Muted volume groups with disable mute feature")
2203                 .that(nonVolumeGroupMutingAudioService.getMutedVolumeGroups(PRIMARY_AUDIO_ZONE))
2204                 .isEmpty();
2205     }
2206 
2207     @Test
getMutedVolumeGroups_withMutedGroups()2208     public void getMutedVolumeGroups_withMutedGroups() {
2209         mCarAudioService.init();
2210         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2211                 /* muted= */ true, TEST_FLAGS);
2212         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1,
2213                 /* muted= */ true, TEST_FLAGS);
2214 
2215         expectWithMessage("Muted volume groups")
2216                 .that(mCarAudioService.getMutedVolumeGroups(PRIMARY_AUDIO_ZONE))
2217                 .containsExactly(TEST_PRIMARY_ZONE_VOLUME_INFO_0, TEST_PRIMARY_ZONE_VOLUME_INFO_1);
2218     }
2219 
2220     @Test
getMutedVolumeGroups_afterUnmuting()2221     public void getMutedVolumeGroups_afterUnmuting() {
2222         mCarAudioService.init();
2223         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2224                 /* muted= */ true, TEST_FLAGS);
2225         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1,
2226                 /* muted= */ true, TEST_FLAGS);
2227         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2228                 /* muted= */ false, TEST_FLAGS);
2229 
2230         expectWithMessage("Muted volume groups after unmuting one group")
2231                 .that(mCarAudioService.getMutedVolumeGroups(PRIMARY_AUDIO_ZONE))
2232                 .containsExactly(TEST_PRIMARY_ZONE_VOLUME_INFO_1);
2233     }
2234 
2235     @Test
getMutedVolumeGroups_withMutedGroupsForDifferentZone()2236     public void getMutedVolumeGroups_withMutedGroupsForDifferentZone() {
2237         mCarAudioService.init();
2238         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
2239                 /* muted= */ true, TEST_FLAGS);
2240         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1,
2241                 /* muted= */ true, TEST_FLAGS);
2242 
2243         expectWithMessage("Muted volume groups for secondary zone")
2244                 .that(mCarAudioService.getMutedVolumeGroups(TEST_REAR_LEFT_ZONE_ID)).isEmpty();
2245     }
2246 
2247     @Test
onReceive_forLegacy_noCallToOnVolumeGroupChanged()2248     public void onReceive_forLegacy_noCallToOnVolumeGroupChanged() {
2249         when(mMockResources.getBoolean(audioUseDynamicRouting))
2250                 .thenReturn(false);
2251         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
2252                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
2253                 mCarVolumeCallbackHandler);
2254         nonDynamicAudioService.init();
2255         mVolumeReceiverCaptor = ArgumentCaptor.forClass(BroadcastReceiver.class);
2256         verify(mMockContext).registerReceiver(mVolumeReceiverCaptor.capture(), any(), anyInt());
2257         BroadcastReceiver receiver = mVolumeReceiverCaptor.getValue();
2258         Intent intent = new Intent(VOLUME_CHANGED_ACTION);
2259 
2260         receiver.onReceive(mMockContext, intent);
2261 
2262         verify(mCarVolumeCallbackHandler, never())
2263                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
2264     }
2265 
2266     @Test
onReceive_forLegacy_forStreamMusic()2267     public void onReceive_forLegacy_forStreamMusic() {
2268         when(mMockResources.getBoolean(audioUseDynamicRouting))
2269                 .thenReturn(false);
2270         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
2271                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
2272                 mCarVolumeCallbackHandler);
2273         nonDynamicAudioService.init();
2274         verify(mMockContext).registerReceiver(mVolumeReceiverCaptor.capture(), any(), anyInt());
2275         BroadcastReceiver receiver = mVolumeReceiverCaptor.getValue();
2276         Intent intent = new Intent(VOLUME_CHANGED_ACTION)
2277                 .putExtra(EXTRA_VOLUME_STREAM_TYPE, STREAM_MUSIC);
2278 
2279         receiver.onReceive(mMockContext, intent);
2280 
2281         verify(mCarVolumeCallbackHandler).onVolumeGroupChange(
2282                 eq(PRIMARY_AUDIO_ZONE), anyInt(), eq(FLAG_FROM_KEY | FLAG_SHOW_UI));
2283     }
2284 
2285     @Test
onReceive_forLegacy_onMuteChanged()2286     public void onReceive_forLegacy_onMuteChanged() {
2287         when(mMockResources.getBoolean(audioUseDynamicRouting))
2288                 .thenReturn(false);
2289         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
2290                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
2291                 mCarVolumeCallbackHandler);
2292         nonDynamicAudioService.init();
2293         ArgumentCaptor<BroadcastReceiver> captor =
2294                 ArgumentCaptor.forClass(BroadcastReceiver.class);
2295         verify(mMockContext).registerReceiver(captor.capture(), any(), anyInt());
2296         BroadcastReceiver receiver = captor.getValue();
2297         Intent intent = new Intent();
2298         intent.setAction(MASTER_MUTE_CHANGED_ACTION);
2299 
2300         receiver.onReceive(mMockContext, intent);
2301 
2302         verify(mCarVolumeCallbackHandler)
2303                 .onMasterMuteChanged(eq(PRIMARY_AUDIO_ZONE), eq(FLAG_FROM_KEY | FLAG_SHOW_UI));
2304     }
2305 
2306     @Test
getVolumeGroupInfosForZone()2307     public void getVolumeGroupInfosForZone() {
2308         mCarAudioService.init();
2309         int groupCount = mCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
2310 
2311         List<CarVolumeGroupInfo> infos =
2312                 mCarAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2313 
2314         for (int index = 0; index < groupCount; index++) {
2315             CarVolumeGroupInfo info = mCarAudioService
2316                     .getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, index);
2317             expectWithMessage("Car volume group infos for primary zone and info %s", info)
2318                     .that(infos).contains(info);
2319         }
2320     }
2321 
2322     @Test
getVolumeGroupInfosForZone_forDynamicRoutingDisabled()2323     public void getVolumeGroupInfosForZone_forDynamicRoutingDisabled() {
2324         when(mMockResources.getBoolean(audioUseDynamicRouting))
2325                 .thenReturn(/* useDynamicRouting= */ false);
2326         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
2327                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
2328                 mCarVolumeCallbackHandler);
2329         nonDynamicAudioService.init();
2330 
2331         List<CarVolumeGroupInfo> infos =
2332                 nonDynamicAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2333 
2334         expectWithMessage("Car volume group infos with dynamic routing disabled")
2335                 .that(infos).isEmpty();
2336     }
2337 
2338     @Test
getVolumeGroupInfosForZone_forOEMConfiguration()2339     public void getVolumeGroupInfosForZone_forOEMConfiguration() {
2340         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
2341                 mTemporaryAudioConfigurationWithOEMContexts.getFile().getAbsolutePath(),
2342                 mCarVolumeCallbackHandler);
2343         nonDynamicAudioService.init();
2344 
2345         List<CarVolumeGroupInfo> infos =
2346                 nonDynamicAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2347 
2348         expectWithMessage("Car volume group infos size with OEM configuration")
2349                 .that(infos).hasSize(1);
2350         expectWithMessage("Car volume group info name with OEM configuration")
2351                 .that(infos.get(0).getName()).isEqualTo("OEM_VOLUME_GROUP");
2352     }
2353 
2354     @Test
getVolumeGroupInfosForZone_size()2355     public void getVolumeGroupInfosForZone_size() {
2356         mCarAudioService.init();
2357         int groupCount = mCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
2358 
2359         List<CarVolumeGroupInfo> infos =
2360                 mCarAudioService.getVolumeGroupInfosForZone(PRIMARY_AUDIO_ZONE);
2361 
2362         expectWithMessage("Car volume group infos size for primary zone")
2363                 .that(infos).hasSize(groupCount);
2364     }
2365 
2366     @Test
getVolumeGroupInfosForZone_forInvalidZone()2367     public void getVolumeGroupInfosForZone_forInvalidZone() {
2368         mCarAudioService.init();
2369 
2370         IllegalArgumentException thrown =
2371                 assertThrows(IllegalArgumentException.class, () ->
2372                         mCarAudioService.getVolumeGroupInfosForZone(INVALID_AUDIO_ZONE));
2373 
2374         expectWithMessage("Exception for volume group infos size for invalid zone")
2375                 .that(thrown).hasMessageThat().contains("audio zone Id");
2376     }
2377 
2378     @Test
getVolumeGroupInfo()2379     public void getVolumeGroupInfo() {
2380         CarVolumeGroupInfo testVolumeGroupInfo = new CarVolumeGroupInfo.Builder(
2381                 TEST_PRIMARY_ZONE_VOLUME_INFO_0).setMuted(false).build();
2382         mCarAudioService.init();
2383 
2384         expectWithMessage("Car volume group info for primary zone")
2385                 .that(mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
2386                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(testVolumeGroupInfo);
2387     }
2388 
2389     @Test
getVolumeGroupInfo_forInvalidZone()2390     public void getVolumeGroupInfo_forInvalidZone() {
2391         mCarAudioService.init();
2392 
2393         IllegalArgumentException thrown =
2394                 assertThrows(IllegalArgumentException.class, () ->
2395                         mCarAudioService.getVolumeGroupInfo(INVALID_AUDIO_ZONE,
2396                                 TEST_PRIMARY_ZONE_GROUP_0));
2397 
2398         expectWithMessage("Exception for volume group info size for invalid zone")
2399                 .that(thrown).hasMessageThat().contains("audio zone Id");
2400     }
2401 
2402     @Test
getVolumeGroupInfo_forInvalidGroup()2403     public void getVolumeGroupInfo_forInvalidGroup() {
2404         mCarAudioService.init();
2405 
2406         IllegalArgumentException thrown =
2407                 assertThrows(IllegalArgumentException.class, () ->
2408                         mCarAudioService.getVolumeGroupInfo(INVALID_AUDIO_ZONE,
2409                                 TEST_PRIMARY_ZONE_GROUP_0));
2410 
2411         expectWithMessage("Exception for volume groups info size for invalid group id")
2412                 .that(thrown).hasMessageThat().contains("audio zone Id");
2413     }
2414 
2415     @Test
getVolumeGroupInfo_forGroupOverRange()2416     public void getVolumeGroupInfo_forGroupOverRange() {
2417         mCarAudioService.init();
2418         int groupCount = mCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE);
2419 
2420         IllegalArgumentException thrown =
2421                 assertThrows(IllegalArgumentException.class, () ->
2422                         mCarAudioService.getVolumeGroupInfo(INVALID_AUDIO_ZONE,
2423                                 groupCount));
2424 
2425         expectWithMessage("Exception for volume groups info size for out of range group")
2426                 .that(thrown).hasMessageThat().contains("audio zone Id");
2427     }
2428 
2429     @Test
registerPrimaryZoneMediaAudioRequestCallbackListener_withNullCallback_fails()2430     public void registerPrimaryZoneMediaAudioRequestCallbackListener_withNullCallback_fails() {
2431         mCarAudioService.init();
2432 
2433         NullPointerException thrown = assertThrows(NullPointerException.class, ()
2434                 -> mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(
2435                         /* callback= */ null));
2436 
2437         expectWithMessage("Register audio media request callback exception")
2438                 .that(thrown).hasMessageThat()
2439                 .contains("Media request callback");
2440     }
2441 
2442     @Test
unregisterPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails()2443     public void unregisterPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails() {
2444         mCarAudioService.init();
2445 
2446         NullPointerException thrown = assertThrows(NullPointerException.class, ()
2447                 -> mCarAudioService.unregisterPrimaryZoneMediaAudioRequestCallback(
2448                         /* callback= */ null));
2449 
2450         expectWithMessage("Unregister audio media request callback exception")
2451                 .that(thrown).hasMessageThat()
2452                 .contains("Media request callback");
2453     }
2454 
2455     @Test
requestMediaAudioOnPrimaryZone_withPassengerOccupant_succeeds()2456     public void requestMediaAudioOnPrimaryZone_withPassengerOccupant_succeeds()
2457             throws Exception {
2458         mCarAudioService.init();
2459         TestPrimaryZoneMediaAudioRequestCallback
2460                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2461         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2462         assignOccupantToAudioZones();
2463         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2464 
2465         expectWithMessage("Audio media request id")
2466                 .that(mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2467                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2468                 .isNotEqualTo(INVALID_REQUEST_ID);
2469     }
2470 
2471     @Test
requestMediaAudioOnPrimaryZone_withDriverOccupant_fails()2472     public void requestMediaAudioOnPrimaryZone_withDriverOccupant_fails()
2473             throws Exception {
2474         mCarAudioService.init();
2475         TestPrimaryZoneMediaAudioRequestCallback
2476                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2477         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2478         assignOccupantToAudioZones();
2479         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2480 
2481         IllegalArgumentException thrown = assertThrows(IllegalArgumentException.class, ()
2482                 -> mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2483                 TEST_DRIVER_OCCUPANT));
2484 
2485         expectWithMessage("Request media audio exception")
2486                 .that(thrown).hasMessageThat().contains("already owns the primary audio zone");
2487     }
2488 
2489     @Test
requestMediaAudioOnPrimaryZone_withNonAssignedOccupant_fails()2490     public void requestMediaAudioOnPrimaryZone_withNonAssignedOccupant_fails()
2491             throws Exception {
2492         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_UNASSIGNED_OCCUPANT_ZONE_ID))
2493                 .thenReturn(OUT_OF_RANGE_ZONE);
2494         mCarAudioService.init();
2495         TestPrimaryZoneMediaAudioRequestCallback
2496                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2497         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2498         CarOccupantZoneManager.OccupantZoneInfo info =
2499                 getOccupantInfo(TEST_UNASSIGNED_OCCUPANT_ZONE_ID,
2500                 CarOccupantZoneManager.OCCUPANT_TYPE_DRIVER,
2501                 VehicleAreaSeat.SEAT_ROW_1_LEFT);
2502         assignOccupantToAudioZones();
2503         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2504 
2505         expectWithMessage("Invalid audio media request id")
2506                 .that(mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback, info))
2507                 .isEqualTo(INVALID_REQUEST_ID);
2508     }
2509 
2510     @Test
requestMediaAudioOnPrimaryZone_withPassengerOccupant_callsApprover()2511     public void requestMediaAudioOnPrimaryZone_withPassengerOccupant_callsApprover()
2512             throws Exception {
2513         mCarAudioService.init();
2514         TestPrimaryZoneMediaAudioRequestCallback
2515                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2516         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2517         assignOccupantToAudioZones();
2518         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2519 
2520         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2521                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2522 
2523         requestToken.waitForCallback();
2524         expectWithMessage("Called audio media request id")
2525                 .that(requestToken.mRequestId).isEqualTo(requestId);
2526         expectWithMessage("Called audio media request info")
2527                 .that(requestToken.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2528     }
2529 
2530     @Test
requestMediaAudioOnPrimaryZone_withZoneMirroring_fails()2531     public void requestMediaAudioOnPrimaryZone_withZoneMirroring_fails()
2532             throws Exception {
2533         mCarAudioService.init();
2534         TestPrimaryZoneMediaAudioRequestCallback
2535                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2536         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2537         assignOccupantToAudioZones();
2538         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2539         TestAudioZonesMirrorStatusCallbackCallback mirrorCallback =
2540                 getAudioZonesMirrorStatusCallback();
2541         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
2542         mirrorCallback.waitForCallback();
2543 
2544         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
2545                         mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2546                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT));
2547 
2548         expectWithMessage("Request audio share while mirroring exception").that(thrown)
2549                 .hasMessageThat().contains("Can not request audio share to primary zone");
2550     }
2551 
2552     @Test
binderDied_onMediaRequestApprover_resetsApprovedRequest()2553     public void binderDied_onMediaRequestApprover_resetsApprovedRequest()
2554             throws Exception {
2555         mCarAudioService.init();
2556         TestPrimaryZoneMediaAudioRequestCallback requestToken =
2557                 new TestPrimaryZoneMediaAudioRequestCallback();
2558         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2559         assignOccupantToAudioZones();
2560         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2561         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2562                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2563         requestToken.waitForCallback();
2564         requestToken.reset();
2565         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
2566         requestToken.waitForCallback();
2567         requestCallback.waitForCallback();
2568         requestCallback.reset();
2569 
2570         requestToken.mDeathRecipient.binderDied();
2571 
2572         requestCallback.waitForCallback();
2573         expectWithMessage("Stopped status due to approver's death").that(requestCallback.mStatus)
2574                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
2575         expectWithMessage("Stopped id due to approver's death")
2576                 .that(requestCallback.mRequestId).isEqualTo(requestId);
2577     }
2578 
2579     @Test
allowMediaAudioOnPrimaryZone_withAllowedRequest()2580     public void allowMediaAudioOnPrimaryZone_withAllowedRequest() throws Exception {
2581         mCarAudioService.init();
2582         TestPrimaryZoneMediaAudioRequestCallback
2583                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2584         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2585         assignOccupantToAudioZones();
2586         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2587         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2588                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2589         requestToken.waitForCallback();
2590 
2591         boolean results = mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2592                 /* allowed= */ true);
2593 
2594         expectWithMessage("Allowed audio playback").that(results).isTrue();
2595     }
2596 
2597     @Test
allowMediaAudioOnPrimaryZone_whileMirroring_fails()2598     public void allowMediaAudioOnPrimaryZone_whileMirroring_fails() throws Exception {
2599         mCarAudioService.init();
2600         TestPrimaryZoneMediaAudioRequestCallback
2601                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2602         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2603         assignOccupantToAudioZones();
2604         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2605         long shareId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2606                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2607         requestToken.waitForCallback();
2608         TestAudioZonesMirrorStatusCallbackCallback mirrorCallback =
2609                 getAudioZonesMirrorStatusCallback();
2610         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
2611         mirrorCallback.waitForCallback();
2612         requestCallback.waitForCallback();
2613 
2614         IllegalStateException thrown =
2615                 assertThrows(IllegalStateException.class, () -> mCarAudioService
2616                         .allowMediaAudioOnPrimaryZone(requestToken, shareId, /* allowed= */ true));
2617 
2618         expectWithMessage("Allow audio share while mirroring exception").that(thrown)
2619                 .hasMessageThat().contains("Can not allow audio share to primary zone");
2620         requestCallback.waitForCallback();
2621         expectWithMessage("Rejected status due to mirroring").that(requestCallback.mStatus)
2622                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
2623         expectWithMessage("Rejected id with rejected due to mirroring")
2624                 .that(requestCallback.mRequestId).isEqualTo(shareId);
2625     }
2626 
2627     @Test
allowMediaAudioOnPrimaryZone_withUnallowedRequest()2628     public void allowMediaAudioOnPrimaryZone_withUnallowedRequest() throws Exception {
2629         mCarAudioService.init();
2630         TestPrimaryZoneMediaAudioRequestCallback
2631                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2632         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2633         assignOccupantToAudioZones();
2634         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2635         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2636                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2637         requestToken.waitForCallback();
2638 
2639         boolean results = mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2640                 /* allowed= */ false);
2641 
2642         expectWithMessage("Unallowed audio playback").that(results).isTrue();
2643     }
2644 
2645     @Test
allowMediaAudioOnPrimaryZone_withAllowedRequest_callsRequester()2646     public void allowMediaAudioOnPrimaryZone_withAllowedRequest_callsRequester() throws Exception {
2647         mCarAudioService.init();
2648         TestPrimaryZoneMediaAudioRequestCallback
2649                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2650         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2651         assignOccupantToAudioZones();
2652         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2653         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2654                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2655         requestToken.waitForCallback();
2656 
2657         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2658                 /* allowed= */ true);
2659 
2660         requestCallback.waitForCallback();
2661         expectWithMessage("Media request called audio media request id")
2662                 .that(requestCallback.mRequestId).isEqualTo(requestId);
2663         expectWithMessage("Media request called audio media request info")
2664                 .that(requestCallback.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2665         expectWithMessage("Media request called audio media request status")
2666                 .that(requestCallback.mStatus)
2667                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
2668     }
2669 
2670     @Test
allowMediaAudioOnPrimaryZone_withAllowedRequest_callsApprover()2671     public void allowMediaAudioOnPrimaryZone_withAllowedRequest_callsApprover() throws Exception {
2672         mCarAudioService.init();
2673         TestPrimaryZoneMediaAudioRequestCallback
2674                 requestApprover = new TestPrimaryZoneMediaAudioRequestCallback();
2675         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2676         assignOccupantToAudioZones();
2677         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestApprover);
2678         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2679                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2680         requestApprover.waitForCallback();
2681         requestApprover.reset();
2682 
2683         mCarAudioService.allowMediaAudioOnPrimaryZone(requestApprover, requestId,
2684                 /* allowed= */ true);
2685 
2686         requestApprover.waitForCallback();
2687         expectWithMessage("Media approver called audio media request id")
2688                 .that(requestApprover.mRequestId).isEqualTo(requestId);
2689         expectWithMessage("Media approver called audio media request info")
2690                 .that(requestApprover.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2691         expectWithMessage("Media approver called audio media request status")
2692                 .that(requestApprover.mStatus)
2693                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
2694     }
2695 
2696     @Test
allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsRequester()2697     public void allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsRequester()
2698             throws Exception {
2699         mCarAudioService.init();
2700         TestPrimaryZoneMediaAudioRequestCallback
2701                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2702         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2703         assignOccupantToAudioZones();
2704         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2705         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2706                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2707         requestToken.waitForCallback();
2708 
2709         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2710                 /* allowed= */ false);
2711 
2712         requestCallback.waitForCallback();
2713         expectWithMessage("Unallowed media request called audio media request id")
2714                 .that(requestCallback.mRequestId).isEqualTo(requestId);
2715         expectWithMessage("Unallowed media request called audio media request info")
2716                 .that(requestCallback.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2717         expectWithMessage("Unallowed media request called audio media request status")
2718                 .that(requestCallback.mStatus)
2719                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
2720     }
2721 
2722     @Test
allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsApprover()2723     public void allowMediaAudioOnPrimaryZone_withUnallowedRequest_callsApprover() throws Exception {
2724         mCarAudioService.init();
2725         TestPrimaryZoneMediaAudioRequestCallback
2726                 requestApprover = new TestPrimaryZoneMediaAudioRequestCallback();
2727         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2728         assignOccupantToAudioZones();
2729         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestApprover);
2730         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2731                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2732         requestApprover.waitForCallback();
2733         requestApprover.reset();
2734 
2735         mCarAudioService.allowMediaAudioOnPrimaryZone(requestApprover, requestId,
2736                 /* allowed= */ false);
2737 
2738         requestApprover.waitForCallback();
2739         expectWithMessage("Unallowed media approver called audio media request id")
2740                 .that(requestApprover.mRequestId).isEqualTo(requestId);
2741         expectWithMessage("Unallowed approver token called audio media request info")
2742                 .that(requestApprover.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2743         expectWithMessage("Unallowed approver token called audio media request status")
2744                 .that(requestApprover.mStatus)
2745                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED);
2746     }
2747 
2748     @Test
isMediaAudioAllowedInPrimaryZone_witNullOccupant_fails()2749     public void isMediaAudioAllowedInPrimaryZone_witNullOccupant_fails() throws Exception {
2750         mCarAudioService.init();
2751         NullPointerException thrown = assertThrows(NullPointerException.class, ()
2752                 -> mCarAudioService.isMediaAudioAllowedInPrimaryZone(/* occupantZoneInfo= */ null));
2753 
2754         expectWithMessage("Media status exception").that(thrown)
2755                 .hasMessageThat().contains("Occupant zone info");
2756     }
2757 
2758     @Test
isMediaAudioAllowedInPrimaryZone_byDefault()2759     public void isMediaAudioAllowedInPrimaryZone_byDefault() throws Exception {
2760         mCarAudioService.init();
2761 
2762         expectWithMessage("Media default status")
2763                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2764                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2765                 .isFalse();
2766     }
2767 
2768     @Test
isMediaAudioAllowedInPrimaryZone_afterAllowed()2769     public void isMediaAudioAllowedInPrimaryZone_afterAllowed() throws Exception {
2770         mCarAudioService.init();
2771         TestPrimaryZoneMediaAudioRequestCallback
2772                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2773         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2774         assignOccupantToAudioZones();
2775         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2776         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2777                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2778         requestToken.waitForCallback();
2779         requestToken.reset();
2780         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2781                 /* allowed= */ true);
2782         requestToken.waitForCallback();
2783 
2784         expectWithMessage("Media allowed status")
2785                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2786                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2787                 .isTrue();
2788     }
2789 
2790     @Test
isMediaAudioAllowedInPrimaryZone_afterDisallowed()2791     public void isMediaAudioAllowedInPrimaryZone_afterDisallowed() throws Exception {
2792         mCarAudioService.init();
2793         TestPrimaryZoneMediaAudioRequestCallback
2794                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2795         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2796         assignOccupantToAudioZones();
2797         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2798         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2799                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2800         requestToken.waitForCallback();
2801         requestToken.reset();
2802         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2803                 /* allowed= */ false);
2804         requestToken.waitForCallback();
2805 
2806         expectWithMessage("Media after disallowed status")
2807                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2808                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2809                 .isFalse();
2810     }
2811 
2812     @Test
isMediaAudioAllowedInPrimaryZone_afterUserLogout()2813     public void isMediaAudioAllowedInPrimaryZone_afterUserLogout() throws Exception {
2814         mCarAudioService.init();
2815         TestPrimaryZoneMediaAudioRequestCallback
2816                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2817         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2818         assignOccupantToAudioZones();
2819         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2820         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2821                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2822         requestToken.waitForCallback();
2823         requestToken.reset();
2824         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
2825         requestToken.waitForCallback();
2826         requestToken.reset();
2827         simulateLogoutPassengers();
2828         requestToken.waitForCallback();
2829 
2830         expectWithMessage("Media allowed status after passenger logout")
2831                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2832                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT)).isFalse();
2833     }
2834 
2835     @Test
isMediaAudioAllowedInPrimaryZone_afterUserSwitch()2836     public void isMediaAudioAllowedInPrimaryZone_afterUserSwitch() throws Exception {
2837         mCarAudioService.init();
2838         TestPrimaryZoneMediaAudioRequestCallback
2839                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2840         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2841         assignOccupantToAudioZones();
2842         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2843         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2844                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2845         requestToken.waitForCallback();
2846         requestToken.reset();
2847         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2848                 /* allowed= */ true);
2849         requestToken.waitForCallback();
2850         requestToken.reset();
2851         simulatePassengersSwitch();
2852         requestToken.waitForCallback();
2853 
2854         expectWithMessage("Media allowed status after passenger switch")
2855                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2856                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT)).isFalse();
2857     }
2858 
2859     @Test
resetMediaAudioOnPrimaryZone_afterAllowed()2860     public void resetMediaAudioOnPrimaryZone_afterAllowed() throws Exception {
2861         mCarAudioService.init();
2862         TestPrimaryZoneMediaAudioRequestCallback
2863                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2864         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2865         assignOccupantToAudioZones();
2866         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2867         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2868                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2869         requestToken.waitForCallback();
2870         requestToken.reset();
2871         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2872                 /* allowed= */ true);
2873         requestToken.waitForCallback();
2874         requestToken.reset();
2875 
2876         boolean reset = mCarAudioService.resetMediaAudioOnPrimaryZone(
2877                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2878 
2879         requestToken.waitForCallback();
2880         expectWithMessage("Reset status").that(reset).isTrue();
2881         expectWithMessage("Media reset status")
2882                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2883                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2884                 .isFalse();
2885     }
2886 
2887     @Test
cancelMediaAudioOnPrimaryZone_beforeAllowed()2888     public void cancelMediaAudioOnPrimaryZone_beforeAllowed() throws Exception {
2889         mCarAudioService.init();
2890         TestPrimaryZoneMediaAudioRequestCallback
2891                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2892         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2893         assignOccupantToAudioZones();
2894         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2895         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2896                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2897         requestToken.waitForCallback();
2898         requestToken.reset();
2899 
2900         boolean cancel = mCarAudioService.cancelMediaAudioOnPrimaryZone(requestId);
2901 
2902         requestToken.waitForCallback();
2903         expectWithMessage("Cancel status").that(cancel).isTrue();
2904         expectWithMessage("Canceled media token called audio media request id")
2905                 .that(requestToken.mRequestId).isEqualTo(requestId);
2906         expectWithMessage("Canceled media token called audio media request info")
2907                 .that(requestToken.mInfo).isEqualTo(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2908         expectWithMessage("Canceled media token called audio media request status")
2909                 .that(requestToken.mStatus)
2910                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED);
2911     }
2912 
2913     @Test
cancelMediaAudioOnPrimaryZone_afterAllowed()2914     public void cancelMediaAudioOnPrimaryZone_afterAllowed() throws Exception {
2915         mCarAudioService.init();
2916         TestPrimaryZoneMediaAudioRequestCallback
2917                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2918         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2919         assignOccupantToAudioZones();
2920         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2921         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2922                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2923         requestToken.waitForCallback();
2924         requestToken.reset();
2925         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2926                 /* allowed= */ true);
2927         requestToken.waitForCallback();
2928         requestToken.reset();
2929 
2930         boolean cancel = mCarAudioService.cancelMediaAudioOnPrimaryZone(requestId);
2931 
2932         requestToken.waitForCallback();
2933         expectWithMessage("Cancel status after allowed").that(cancel).isTrue();
2934         expectWithMessage("Media allowed status after canceled")
2935                 .that(mCarAudioService.isMediaAudioAllowedInPrimaryZone(
2936                         TEST_REAR_RIGHT_PASSENGER_OCCUPANT))
2937                 .isFalse();
2938     }
2939 
2940     @Test
getZoneIdForAudioFocusInfo_beforeAllowedSharedAudio()2941     public void getZoneIdForAudioFocusInfo_beforeAllowedSharedAudio() throws Exception {
2942         mCarAudioService.init();
2943         TestPrimaryZoneMediaAudioRequestCallback
2944                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2945         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2946         assignOccupantToAudioZones();
2947         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2948         mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2949                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2950         requestToken.waitForCallback();
2951 
2952         expectWithMessage("Not yet shared media user zone")
2953                 .that(mCarAudioService.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
2954                 .isEqualTo(TEST_REAR_RIGHT_ZONE_ID);
2955     }
2956 
2957     @Test
getZoneIdForAudioFocusInfo_afterAllowedShareAudio()2958     public void getZoneIdForAudioFocusInfo_afterAllowedShareAudio() throws Exception {
2959         mCarAudioService.init();
2960         TestPrimaryZoneMediaAudioRequestCallback
2961                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2962         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2963         assignOccupantToAudioZones();
2964         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2965         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2966                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2967         requestToken.waitForCallback();
2968         requestToken.reset();
2969         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
2970         requestToken.waitForCallback();
2971 
2972         expectWithMessage("Shared media user zone")
2973                 .that(mCarAudioService.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
2974                 .isEqualTo(PRIMARY_AUDIO_ZONE);
2975     }
2976 
2977     @Test
getZoneIdForAudioFocusInfo_afterCanceled()2978     public void getZoneIdForAudioFocusInfo_afterCanceled() throws Exception {
2979         mCarAudioService.init();
2980         TestPrimaryZoneMediaAudioRequestCallback
2981                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
2982         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
2983         assignOccupantToAudioZones();
2984         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
2985         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
2986                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
2987         requestToken.waitForCallback();
2988         requestToken.reset();
2989         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
2990                 /* allowed= */ true);
2991         requestToken.waitForCallback();
2992         requestToken.reset();
2993         mCarAudioService.cancelMediaAudioOnPrimaryZone(requestId);
2994         requestToken.waitForCallback();
2995 
2996         expectWithMessage("Canceled shared media user zone")
2997                 .that(mCarAudioService.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
2998                 .isEqualTo(TEST_REAR_RIGHT_ZONE_ID);
2999     }
3000 
3001     @Test
getZoneIdForAudioFocusInfo_afterReset()3002     public void getZoneIdForAudioFocusInfo_afterReset() throws Exception {
3003         mCarAudioService.init();
3004         TestPrimaryZoneMediaAudioRequestCallback
3005                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3006         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3007         assignOccupantToAudioZones();
3008         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3009         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
3010                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3011         requestToken.waitForCallback();
3012         requestToken.reset();
3013         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId,
3014                 /* allowed= */ true);
3015         requestToken.waitForCallback();
3016         requestToken.reset();
3017         mCarAudioService.resetMediaAudioOnPrimaryZone(TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3018         requestToken.waitForCallback();
3019 
3020         expectWithMessage("Reset shared media user zone")
3021                 .that(mCarAudioService.getZoneIdForAudioFocusInfo(TEST_REAR_RIGHT_AUDIO_FOCUS_INFO))
3022                 .isEqualTo(TEST_REAR_RIGHT_ZONE_ID);
3023     }
3024 
getOccupantInfo(int occupantZoneId, int occupantType, int seat)3025     private static CarOccupantZoneManager.OccupantZoneInfo getOccupantInfo(int occupantZoneId,
3026             int occupantType, int seat) {
3027         return new CarOccupantZoneManager.OccupantZoneInfo(occupantZoneId, occupantType, seat);
3028     }
3029 
3030     @Test
getAudioAttributesForVolumeGroup()3031     public void getAudioAttributesForVolumeGroup() {
3032         mCarAudioService.init();
3033         CarVolumeGroupInfo info = mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3034                 TEST_PRIMARY_ZONE_GROUP_0);
3035 
3036         List<AudioAttributes> audioAttributes =
3037                 mCarAudioService.getAudioAttributesForVolumeGroup(info);
3038 
3039         expectWithMessage("Volume group audio attributes").that(audioAttributes)
3040                 .containsExactly(
3041                         CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA),
3042                         CarAudioContext.getAudioAttributeFromUsage(USAGE_GAME),
3043                         CarAudioContext.getAudioAttributeFromUsage(USAGE_UNKNOWN),
3044                         CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION),
3045                         CarAudioContext.getAudioAttributeFromUsage(USAGE_NOTIFICATION_EVENT),
3046                         CarAudioContext.getAudioAttributeFromUsage(USAGE_ANNOUNCEMENT));
3047     }
3048 
3049     @Test
getAudioAttributesForVolumeGroup_withNullInfo_fails()3050     public void getAudioAttributesForVolumeGroup_withNullInfo_fails() {
3051         mCarAudioService.init();
3052 
3053         NullPointerException thrown =
3054                 assertThrows(NullPointerException.class, () ->
3055                         mCarAudioService.getAudioAttributesForVolumeGroup(/* groupInfo= */ null));
3056 
3057         expectWithMessage("Volume group audio attributes with null info exception")
3058                 .that(thrown).hasMessageThat().contains("Car volume group info");
3059     }
3060 
3061     @Test
getAudioAttributesForVolumeGroup_withDynamicRoutingDisabled()3062     public void getAudioAttributesForVolumeGroup_withDynamicRoutingDisabled() {
3063         when(mMockResources.getBoolean(audioUseDynamicRouting))
3064                 .thenReturn(/* useDynamicRouting= */ false);
3065         CarAudioService nonDynamicAudioService = new CarAudioService(mMockContext,
3066                 mTemporaryAudioConfigurationFile.getFile().getAbsolutePath(),
3067                 mCarVolumeCallbackHandler);
3068         nonDynamicAudioService.init();
3069 
3070         List<AudioAttributes> audioAttributes = nonDynamicAudioService
3071                 .getAudioAttributesForVolumeGroup(TEST_PRIMARY_ZONE_VOLUME_INFO_0);
3072 
3073         expectWithMessage("Volume group audio attributes with dynamic routing disabled")
3074                 .that(audioAttributes).isEmpty();
3075     }
3076 
3077     @Test
onKeyEvent_forInvalidAudioZone()3078     public void onKeyEvent_forInvalidAudioZone() {
3079         mCarAudioService.init();
3080         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3081                 TEST_PRIMARY_ZONE_GROUP_0);
3082         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3083         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3084                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3085         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3086                 .thenReturn(INVALID_AUDIO_ZONE);
3087         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_UNKNOWN);
3088 
3089         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3090 
3091         expectWithMessage("Volume group volume after invalid audio zone")
3092                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3093                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3094     }
3095 
3096     @Test
onKeyEvent_forInvalidEvent()3097     public void onKeyEvent_forInvalidEvent() {
3098         mCarAudioService.init();
3099         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3100                 TEST_PRIMARY_ZONE_GROUP_0);
3101         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3102         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3103                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3104         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3105                 .thenReturn(PRIMARY_AUDIO_ZONE);
3106         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_UNKNOWN);
3107 
3108         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3109 
3110         expectWithMessage("Volume group volume after unknown key event")
3111                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3112                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3113     }
3114 
3115     @Test
onKeyEvent_forActionUp()3116     public void onKeyEvent_forActionUp() {
3117         mCarAudioService.init();
3118         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3119                 TEST_PRIMARY_ZONE_GROUP_0);
3120         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3121         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3122                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3123         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3124                 .thenReturn(PRIMARY_AUDIO_ZONE);
3125         KeyEvent keyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3126 
3127         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3128 
3129         expectWithMessage("Volume group volume after volume up in primary zone in primary group "
3130                 + "for action up")
3131                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3132                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3133     }
3134 
3135     @Test
onKeyEvent_forActionDownFollowedByActionUp()3136     public void onKeyEvent_forActionDownFollowedByActionUp() {
3137         mCarAudioService.init();
3138         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3139                 TEST_PRIMARY_ZONE_GROUP_0);
3140         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3141         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3142                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3143         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3144                 .thenReturn(PRIMARY_AUDIO_ZONE);
3145         KeyEvent actionDownKeyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3146         KeyEvent actionUpKeyEvent = new KeyEvent(ACTION_UP, KEYCODE_VOLUME_UP);
3147         listener.onKeyEvent(actionDownKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3148 
3149         listener.onKeyEvent(actionUpKeyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3150 
3151         expectWithMessage("Volume group volume after volume up in primary zone in primary group "
3152                 + "for action down then action up")
3153                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3154                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore + 1);
3155     }
3156 
3157     @Test
onKeyEvent_forVolumeUpEvent_inPrimaryZone()3158     public void onKeyEvent_forVolumeUpEvent_inPrimaryZone() {
3159         mCarAudioService.init();
3160         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3161                 TEST_PRIMARY_ZONE_GROUP_0);
3162         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3163         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3164                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3165         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3166                 .thenReturn(PRIMARY_AUDIO_ZONE);
3167         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3168 
3169         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3170 
3171         expectWithMessage("Volume group volume after volume up in primary zone in primary group")
3172                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3173                         TEST_PRIMARY_ZONE_GROUP_0)).isGreaterThan(volumeBefore);
3174     }
3175 
3176     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone()3177     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone() {
3178         mCarAudioService.init();
3179         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3180                 TEST_PRIMARY_ZONE_GROUP_0);
3181         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3182         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3183                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3184         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3185                 .thenReturn(PRIMARY_AUDIO_ZONE);
3186         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3187 
3188         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3189 
3190         expectWithMessage("Volume group volume after volume down in primary zone in primary group")
3191                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3192                         TEST_PRIMARY_ZONE_GROUP_0)).isLessThan(volumeBefore);
3193     }
3194 
3195     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone_forSecondaryGroup()3196     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone_forSecondaryGroup() {
3197         mCarAudioService.init();
3198         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3199                 TEST_PRIMARY_ZONE_GROUP_1);
3200         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
3201         callback.onPlaybackConfigChanged(List.of(new AudioPlaybackConfigurationBuilder()
3202                 .setUsage(USAGE_ASSISTANT)
3203                 .setDeviceAddress(VOICE_TEST_DEVICE)
3204                 .build())
3205         );
3206         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3207         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3208                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3209         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3210                 .thenReturn(PRIMARY_AUDIO_ZONE);
3211         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3212 
3213         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3214 
3215         expectWithMessage("Assistant volume group volume after volume down")
3216                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3217                         TEST_PRIMARY_ZONE_GROUP_1)).isLessThan(volumeBefore);
3218     }
3219 
3220     @Test
onKeyEvent_forVolumeDownEvent_inPrimaryZone_withHigherPriority()3221     public void onKeyEvent_forVolumeDownEvent_inPrimaryZone_withHigherPriority() {
3222         mCarAudioService.init();
3223         int primaryGroupVolumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3224                 TEST_PRIMARY_ZONE_GROUP_0);
3225         int voiceVolumeGroupBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3226                 TEST_PRIMARY_ZONE_GROUP_2);
3227         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
3228         callback.onPlaybackConfigChanged(List.of(
3229                 new AudioPlaybackConfigurationBuilder()
3230                         .setUsage(USAGE_VOICE_COMMUNICATION)
3231                         .setDeviceAddress(CALL_TEST_DEVICE)
3232                         .build(),
3233                 new AudioPlaybackConfigurationBuilder()
3234                         .setUsage(USAGE_MEDIA)
3235                         .setDeviceAddress(MEDIA_TEST_DEVICE)
3236                         .build())
3237         );
3238         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3239         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3240                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3241         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3242                 .thenReturn(PRIMARY_AUDIO_ZONE);
3243         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3244 
3245         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3246 
3247         expectWithMessage("Media volume group volume after volume down")
3248                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3249                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(primaryGroupVolumeBefore);
3250         expectWithMessage("Call volume group volume after volume do")
3251                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3252                         TEST_PRIMARY_ZONE_GROUP_2)).isLessThan(voiceVolumeGroupBefore);
3253     }
3254 
3255     @Test
onKeyEvent_forVolumeMuteEvent_inPrimaryZone()3256     public void onKeyEvent_forVolumeMuteEvent_inPrimaryZone() {
3257         mCarAudioService.init();
3258         boolean muteBefore = mCarAudioService.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
3259                 TEST_PRIMARY_ZONE_GROUP_0);
3260         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3261         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3262                 .thenReturn(PRIMARY_OCCUPANT_ZONE);
3263         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(PRIMARY_OCCUPANT_ZONE))
3264                 .thenReturn(PRIMARY_AUDIO_ZONE);
3265         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_MUTE);
3266 
3267         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3268 
3269         expectWithMessage("Volume group volume after volume mute")
3270                 .that(mCarAudioService.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE,
3271                         TEST_PRIMARY_ZONE_GROUP_0)).isNotEqualTo(muteBefore);
3272     }
3273 
3274     @Test
onKeyEvent_forVolumeUpEvent_inSecondaryZone()3275     public void onKeyEvent_forVolumeUpEvent_inSecondaryZone() {
3276         mCarAudioService.init();
3277         int volumeBefore = mCarAudioService.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3278                 SECONDARY_ZONE_VOLUME_GROUP_ID);
3279         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3280         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3281                 .thenReturn(TEST_DRIVER_OCCUPANT_ZONE_ID);
3282         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
3283                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
3284         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_UP);
3285 
3286         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3287 
3288         expectWithMessage("Secondary zone volume group after volume up")
3289                 .that(mCarAudioService.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3290                         SECONDARY_ZONE_VOLUME_GROUP_ID))
3291                 .isGreaterThan(volumeBefore);
3292     }
3293 
3294     @Test
onKeyEvent_forVolumeDownEvent_inSecondaryZone()3295     public void onKeyEvent_forVolumeDownEvent_inSecondaryZone() {
3296         mCarAudioService.init();
3297         int volumeBefore = mCarAudioService.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3298                 SECONDARY_ZONE_VOLUME_GROUP_ID);
3299         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3300         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3301                 .thenReturn(TEST_DRIVER_OCCUPANT_ZONE_ID);
3302         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
3303                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
3304         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_DOWN);
3305 
3306         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3307 
3308         expectWithMessage("Secondary zone volume group after volume down")
3309                 .that(mCarAudioService.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3310                         SECONDARY_ZONE_VOLUME_GROUP_ID))
3311                 .isLessThan(volumeBefore);
3312     }
3313 
3314     @Test
onKeyEvent_forVolumeMuteEvent_inSecondaryZone()3315     public void onKeyEvent_forVolumeMuteEvent_inSecondaryZone() {
3316         mCarAudioService.init();
3317         boolean muteBefore = mCarAudioService.isVolumeGroupMuted(TEST_REAR_LEFT_ZONE_ID,
3318                 SECONDARY_ZONE_VOLUME_GROUP_ID);
3319         CarInputService.KeyEventListener listener = getAudioKeyEventListener();
3320         when(mMockOccupantZoneService.getOccupantZoneIdForSeat(TEST_SEAT))
3321                 .thenReturn(TEST_DRIVER_OCCUPANT_ZONE_ID);
3322         when(mMockOccupantZoneService.getAudioZoneIdForOccupant(TEST_DRIVER_OCCUPANT_ZONE_ID))
3323                 .thenReturn(TEST_REAR_LEFT_ZONE_ID);
3324         KeyEvent keyEvent = new KeyEvent(ACTION_DOWN, KEYCODE_VOLUME_MUTE);
3325 
3326         listener.onKeyEvent(keyEvent, TEST_DISPLAY_TYPE, TEST_SEAT);
3327 
3328         expectWithMessage("Secondary zone volume group after volume mute")
3329                 .that(mCarAudioService.isVolumeGroupMuted(TEST_REAR_LEFT_ZONE_ID,
3330                         SECONDARY_ZONE_VOLUME_GROUP_ID))
3331                 .isNotEqualTo(muteBefore);
3332     }
3333 
3334     @Test
onAudioDeviceGainsChanged_forPrimaryZone_changesVolume()3335     public void onAudioDeviceGainsChanged_forPrimaryZone_changesVolume() {
3336         mCarAudioService.init();
3337         HalAudioGainCallback callback = getHalAudioGainCallback();
3338         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(PRIMARY_AUDIO_ZONE,
3339                 MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
3340 
3341         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION), List.of(carGain));
3342 
3343         expectWithMessage("New audio gains for primary zone")
3344                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3345                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3346     }
3347 
3348     @Test
onAudioDeviceGainsChanged_forSecondaryZone_changesVolume()3349     public void onAudioDeviceGainsChanged_forSecondaryZone_changesVolume() {
3350         mCarAudioService.init();
3351         HalAudioGainCallback callback = getHalAudioGainCallback();
3352         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(TEST_REAR_LEFT_ZONE_ID,
3353                 SECONDARY_TEST_DEVICE_CONFIG_0, TEST_GAIN_INDEX);
3354 
3355         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION), List.of(carGain));
3356 
3357         expectWithMessage("New audio gains for secondary zone")
3358                 .that(mCarAudioService.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3359                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3360     }
3361 
3362     @Test
onAudioDeviceGainsChanged_forIncorrectDeviceAddress_sameVolume()3363     public void onAudioDeviceGainsChanged_forIncorrectDeviceAddress_sameVolume() {
3364         mCarAudioService.init();
3365         HalAudioGainCallback callback = getHalAudioGainCallback();
3366         int volumeBefore = mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3367                 TEST_PRIMARY_ZONE_GROUP_0);
3368         CarAudioGainConfigInfo carGain = createCarAudioGainConfigInfo(PRIMARY_AUDIO_ZONE,
3369                 SECONDARY_TEST_DEVICE_CONFIG_0, TEST_GAIN_INDEX);
3370 
3371         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION), List.of(carGain));
3372 
3373         expectWithMessage("Same audio gains for primary zone")
3374                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3375                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(volumeBefore);
3376     }
3377 
3378     @Test
onAudioDeviceGainsChanged_forMultipleZones_changesVolume()3379     public void onAudioDeviceGainsChanged_forMultipleZones_changesVolume() {
3380         mCarAudioService.init();
3381         HalAudioGainCallback callback = getHalAudioGainCallback();
3382         CarAudioGainConfigInfo primaryAudioZoneCarGain = createCarAudioGainConfigInfo(
3383                 PRIMARY_AUDIO_ZONE, MEDIA_TEST_DEVICE, TEST_GAIN_INDEX);
3384         CarAudioGainConfigInfo secondaryAudioZoneCarGain = createCarAudioGainConfigInfo(
3385                 TEST_REAR_LEFT_ZONE_ID, SECONDARY_TEST_DEVICE_CONFIG_0, TEST_GAIN_INDEX);
3386 
3387         callback.onAudioDeviceGainsChanged(List.of(Reasons.THERMAL_LIMITATION),
3388                 List.of(primaryAudioZoneCarGain, secondaryAudioZoneCarGain));
3389 
3390         expectWithMessage("New audio gains for primary zone")
3391                 .that(mCarAudioService.getGroupVolume(PRIMARY_AUDIO_ZONE,
3392                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3393         expectWithMessage("New audio gains for secondary zone")
3394                 .that(mCarAudioService.getGroupVolume(TEST_REAR_LEFT_ZONE_ID,
3395                         TEST_PRIMARY_ZONE_GROUP_0)).isEqualTo(TEST_GAIN_INDEX);
3396     }
3397 
3398     @Test
onAudioPortsChanged_forMediaBus_changesVolumeRanges()3399     public void onAudioPortsChanged_forMediaBus_changesVolumeRanges() {
3400         mCarAudioService.init();
3401         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
3402         HalAudioDeviceInfo mediaBusDeviceInfo = createHalAudioDeviceInfo(
3403                 TEST_MEDIA_PORT_ID, TEST_MEDIA_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
3404                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, MEDIA_TEST_DEVICE);
3405         CarVolumeGroupInfo volumeGroupInfoBefore =
3406                 mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
3407 
3408         callback.onAudioPortsChanged(List.of(mediaBusDeviceInfo));
3409 
3410         expectWithMessage("update audio port for media device")
3411                 .that(mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3412                         TEST_PRIMARY_ZONE_GROUP_0)).isNotEqualTo(volumeGroupInfoBefore);
3413     }
3414 
3415     @Test
onAudioPortsChanged_forNavBus_changesVolumeRanges()3416     public void onAudioPortsChanged_forNavBus_changesVolumeRanges() {
3417         mCarAudioService.init();
3418         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
3419         HalAudioDeviceInfo navBusDeviceInfo = createHalAudioDeviceInfo(
3420                 TEST_NAV_PORT_ID, TEST_NAV_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
3421                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, NAVIGATION_TEST_DEVICE);
3422         CarVolumeGroupInfo volumeGroupInfoBefore =
3423                 mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1);
3424 
3425         callback.onAudioPortsChanged(List.of(navBusDeviceInfo));
3426 
3427         expectWithMessage("update audio port for nav device")
3428                 .that(mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3429                         TEST_PRIMARY_ZONE_GROUP_1)).isNotEqualTo(volumeGroupInfoBefore);
3430     }
3431 
3432     @Test
onAudioPortsChanged_forMultipleBuses_changesVolumeRanges()3433     public void onAudioPortsChanged_forMultipleBuses_changesVolumeRanges() {
3434         mCarAudioService.init();
3435         HalAudioModuleChangeCallback callback = getHalModuleChangeCallback();
3436         HalAudioDeviceInfo mediaBusDeviceInfo = createHalAudioDeviceInfo(
3437                 TEST_MEDIA_PORT_ID, TEST_MEDIA_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
3438                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, MEDIA_TEST_DEVICE);
3439         HalAudioDeviceInfo navBusDeviceInfo = createHalAudioDeviceInfo(
3440                 TEST_NAV_PORT_ID, TEST_NAV_PORT_NAME, TEST_GAIN_MIN_VALUE, TEST_GAIN_MAX_VALUE,
3441                 TEST_GAIN_DEFAULT_VALUE, TEST_GAIN_STEP_VALUE, OUT_DEVICE, NAVIGATION_TEST_DEVICE);
3442         CarVolumeGroupInfo mediaVolumeGroupInfoBefore =
3443                 mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0);
3444         CarVolumeGroupInfo navVolumeGroupInfoBefore =
3445                 mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_1);
3446 
3447         callback.onAudioPortsChanged(List.of(mediaBusDeviceInfo, navBusDeviceInfo));
3448 
3449         expectWithMessage("update audio port for media device")
3450                 .that(mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3451                         TEST_PRIMARY_ZONE_GROUP_0)).isNotEqualTo(mediaVolumeGroupInfoBefore);
3452         expectWithMessage("update audio port for nav device")
3453                 .that(mCarAudioService.getVolumeGroupInfo(PRIMARY_AUDIO_ZONE,
3454                         TEST_PRIMARY_ZONE_GROUP_1)).isNotEqualTo(navVolumeGroupInfoBefore);
3455     }
3456 
3457     @Test
getActiveAudioAttributesForZone()3458     public void getActiveAudioAttributesForZone() {
3459         mCarAudioService.init();
3460 
3461         expectWithMessage("Default active audio attributes").that(
3462                 mCarAudioService.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE)).isEmpty();
3463     }
3464 
3465     @Test
getActiveAudioAttributesForZone_withActiveHalFocus()3466     public void getActiveAudioAttributesForZone_withActiveHalFocus() {
3467         when(mAudioManager.requestAudioFocus(any())).thenReturn(
3468                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
3469         mCarAudioService.init();
3470         requestHalAudioFocus(USAGE_ALARM);
3471 
3472         expectWithMessage("HAL active audio attributes")
3473                 .that(mCarAudioService.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE))
3474                 .containsExactly(new AudioAttributes.Builder().setUsage(USAGE_ALARM).build());
3475     }
3476 
3477     @Test
getActiveAudioAttributesForZone_withActivePlayback()3478     public void getActiveAudioAttributesForZone_withActivePlayback() {
3479         mCarAudioService.init();
3480         mockActivePlayback();
3481 
3482         expectWithMessage("Playback active audio attributes")
3483                 .that(mCarAudioService.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE))
3484                 .containsExactly(new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build());
3485     }
3486 
3487     @Test
getActiveAudioAttributesForZone_withActiveHalAndPlayback()3488     public void getActiveAudioAttributesForZone_withActiveHalAndPlayback() {
3489         mCarAudioService.init();
3490         mockActivePlayback();
3491         when(mAudioManager.requestAudioFocus(any())).thenReturn(
3492                 AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
3493         requestHalAudioFocus(USAGE_VOICE_COMMUNICATION);
3494 
3495         expectWithMessage("Playback active audio attributes")
3496                 .that(mCarAudioService.getActiveAudioAttributesForZone(PRIMARY_AUDIO_ZONE))
3497                 .containsExactly(new AudioAttributes.Builder().setUsage(USAGE_MEDIA).build(),
3498                         new AudioAttributes.Builder().setUsage(USAGE_VOICE_COMMUNICATION).build());
3499     }
3500 
3501     @Test
getCallStateForZone_forPrimaryZone()3502     public void getCallStateForZone_forPrimaryZone() throws Exception {
3503         when(mMockTelephonyManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
3504         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
3505         mCarAudioService.init();
3506         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
3507                 .thenReturn(TEST_DRIVER_USER_ID, TEST_REAR_RIGHT_USER_ID);
3508         assignOccupantToAudioZones();
3509 
3510         expectWithMessage("Primary zone call state").that(
3511                 mCarAudioService.getCallStateForZone(PRIMARY_AUDIO_ZONE))
3512                 .isEqualTo(TelephonyManager.CALL_STATE_OFFHOOK);
3513     }
3514 
3515     @Test
getCallStateForZone_forNonPrimaryZone()3516     public void getCallStateForZone_forNonPrimaryZone() throws Exception {
3517         when(mMockTelephonyManager.getCallState()).thenReturn(TelephonyManager.CALL_STATE_OFFHOOK);
3518         when(mMockOccupantZoneService.getDriverUserId()).thenReturn(TEST_DRIVER_USER_ID);
3519         mCarAudioService.init();
3520         when(mMockOccupantZoneService.getUserForOccupant(anyInt()))
3521                 .thenReturn(TEST_REAR_LEFT_USER_ID, TEST_REAR_RIGHT_USER_ID);
3522         assignOccupantToAudioZones();
3523 
3524         expectWithMessage("Secondary zone call state").that(
3525                         mCarAudioService.getCallStateForZone(TEST_REAR_LEFT_ZONE_ID))
3526                 .isEqualTo(TelephonyManager.CALL_STATE_IDLE);
3527     }
3528 
3529     @Test
getVolumeGroupAndContextCount()3530     public void getVolumeGroupAndContextCount() {
3531         CarAudioService useCoreAudioCarAudioService =
3532                 getCarAudioServiceUsingCoreAudioRoutingAndVolume();
3533 
3534         verify(mAudioManager).registerVolumeGroupCallback(any(), any());
3535         expectWithMessage("Primary zone car volume group count")
3536                 .that(useCoreAudioCarAudioService.getVolumeGroupCount(PRIMARY_AUDIO_ZONE))
3537                 .isEqualTo(CoreAudioRoutingUtils.getVolumeGroups().size());
3538         expectWithMessage("Number of contexts")
3539                 .that(useCoreAudioCarAudioService.getCarAudioContext().getAllContextsIds().size())
3540                 .isEqualTo(CoreAudioRoutingUtils.getProductStrategies().size());
3541         expectWithMessage("Car Audio Contexts")
3542                 .that(useCoreAudioCarAudioService.getCarAudioContext().getAllContextsIds())
3543                 .containsExactly(CoreAudioRoutingUtils.NAV_STRATEGY_ID,
3544                         CoreAudioRoutingUtils.MUSIC_STRATEGY_ID,
3545                         CoreAudioRoutingUtils.OEM_STRATEGY_ID);
3546     }
3547 
3548     @Test
registerAudioZonesMirrorStatusCallback()3549     public void registerAudioZonesMirrorStatusCallback() {
3550         mCarAudioService.init();
3551         TestAudioZonesMirrorStatusCallbackCallback callback =
3552                 new TestAudioZonesMirrorStatusCallbackCallback(/* count= */ 1);
3553 
3554         boolean registered = mCarAudioService.registerAudioZonesMirrorStatusCallback(callback);
3555 
3556         expectWithMessage("Audio zones mirror status callback registered status")
3557                 .that(registered).isTrue();
3558     }
3559 
3560     @Test
registerAudioZonesMirrorStatusCallback_withoutMirroringEnabled()3561     public void registerAudioZonesMirrorStatusCallback_withoutMirroringEnabled() {
3562         CarAudioService carAudioService = getCarAudioServiceWithoutMirroring();
3563         carAudioService.init();
3564         TestAudioZonesMirrorStatusCallbackCallback callback =
3565                 new TestAudioZonesMirrorStatusCallbackCallback(/* count= */ 1);
3566 
3567         IllegalStateException thrown =
3568                 assertThrows(IllegalStateException.class, () ->
3569                         carAudioService.registerAudioZonesMirrorStatusCallback(callback));
3570 
3571         expectWithMessage("Disabled audio zones mirror register exception").that(thrown)
3572                 .hasMessageThat().contains("Audio zones mirroring is required");
3573     }
3574 
3575     @Test
registerAudioZonesMirrorStatusCallback_withNullCallback()3576     public void registerAudioZonesMirrorStatusCallback_withNullCallback() {
3577         mCarAudioService.init();
3578 
3579         NullPointerException thrown =
3580                 assertThrows(NullPointerException.class, () ->
3581                     mCarAudioService.registerAudioZonesMirrorStatusCallback(/* callback= */ null));
3582 
3583         expectWithMessage("Null audio zones mirror register exception").that(thrown)
3584                 .hasMessageThat().contains("Audio zones mirror status callback");
3585     }
3586 
3587     @Test
unregisterAudioZonesMirrorStatusCallback_withNullCallback()3588     public void unregisterAudioZonesMirrorStatusCallback_withNullCallback() {
3589         mCarAudioService.init();
3590 
3591         NullPointerException thrown =
3592                 assertThrows(NullPointerException.class, () -> mCarAudioService
3593                         .unregisterAudioZonesMirrorStatusCallback(/* callback= */ null));
3594 
3595         expectWithMessage("Null audio zones mirror unregister exception").that(thrown)
3596                 .hasMessageThat().contains("Audio zones mirror status callback");
3597     }
3598 
3599     @Test
enableMirrorForAudioZones_withNullAudioZones()3600     public void enableMirrorForAudioZones_withNullAudioZones() {
3601         mCarAudioService.init();
3602 
3603         NullPointerException thrown =
3604                 assertThrows(NullPointerException.class, () ->
3605                         mCarAudioService.enableMirrorForAudioZones(/* audioZones= */ null));
3606 
3607         expectWithMessage("Null mirror audio zones exception").that(thrown)
3608                 .hasMessageThat().contains("Mirror audio zones");
3609     }
3610 
3611     @Test
enableMirrorForAudioZones()3612     public void enableMirrorForAudioZones() throws Exception {
3613         mCarAudioService.init();
3614         TestAudioZonesMirrorStatusCallbackCallback callback =
3615                 getAudioZonesMirrorStatusCallback();
3616         assignOccupantToAudioZones();
3617 
3618         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3619 
3620         callback.waitForCallback();
3621         expectWithMessage("Audio mirror approved status").that(callback.getLastStatus())
3622                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
3623         expectWithMessage("Audio mirror approved zones").that(callback.getLastZoneIds())
3624                 .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
3625     }
3626 
3627     @Test
enableMirrorForAudioZones_sendsMirrorInfoToAudioHAL()3628     public void enableMirrorForAudioZones_sendsMirrorInfoToAudioHAL() throws Exception {
3629         mCarAudioService.init();
3630         TestAudioZonesMirrorStatusCallbackCallback callback =
3631                 getAudioZonesMirrorStatusCallback();
3632         assignOccupantToAudioZones();
3633 
3634         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3635 
3636         callback.waitForCallback();
3637         String audioMirrorInfoCommand = captureAudioMirrorInfoCommand(1);
3638         List<String> commands = Arrays.asList(audioMirrorInfoCommand.split(";"));
3639         String sourceDeviceAddress = removeUpToEquals(commands.get(0));
3640         expectWithMessage("Audio mirror source info").that(sourceDeviceAddress)
3641                 .isEqualTo(MIRROR_TEST_DEVICE);
3642         String destinationsDevices = commands.get(1);
3643         List<String> deviceAddresses = Arrays.asList(removeUpToEquals(destinationsDevices)
3644                 .split(","));
3645         expectWithMessage("Audio mirror zone one info").that(deviceAddresses.get(0))
3646                 .isEqualTo(SECONDARY_TEST_DEVICE_CONFIG_0);
3647         expectWithMessage("Audio mirror zone two info").that(deviceAddresses.get(1))
3648                 .isEqualTo(TERTIARY_TEST_DEVICE_1);
3649     }
3650 
3651     @Test
enableMirrorForAudioZones_forPrimaryZone_fails()3652     public void enableMirrorForAudioZones_forPrimaryZone_fails() throws Exception {
3653         mCarAudioService.init();
3654         assignOccupantToAudioZones();
3655         int[] audioZones = new int[]{TEST_REAR_LEFT_ZONE_ID, PRIMARY_AUDIO_ZONE};
3656 
3657         IllegalArgumentException thrown =
3658                 assertThrows(IllegalArgumentException.class, () ->
3659                         mCarAudioService.enableMirrorForAudioZones(audioZones));
3660 
3661         expectWithMessage("Mirror audio zones with primary zone exception").that(thrown)
3662                 .hasMessageThat().contains("not allowed for primary audio zone");
3663     }
3664 
3665     @Test
enableMirrorForAudioZones_forNonAssignedZone_fails()3666     public void enableMirrorForAudioZones_forNonAssignedZone_fails() throws Exception {
3667         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
3668                 .thenReturn(UserManagerHelper.USER_NULL);
3669         mCarAudioService.init();
3670         getAudioZonesMirrorStatusCallback();
3671         assignOccupantToAudioZones();
3672 
3673         IllegalStateException thrown =
3674                 assertThrows(IllegalStateException.class, () ->
3675                         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
3676 
3677         expectWithMessage("Mirror audio zones for unoccupied audio zone exception")
3678                 .that(thrown).hasMessageThat().contains("must have an active user");
3679     }
3680 
3681     @Test
enableMirrorForAudioZones_forRepeatingZones_fails()3682     public void enableMirrorForAudioZones_forRepeatingZones_fails() throws Exception {
3683         mCarAudioService.init();
3684         assignOccupantToAudioZones();
3685         int[] audioZones = new int[]{TEST_REAR_LEFT_ZONE_ID,
3686                 TEST_REAR_LEFT_ZONE_ID};
3687 
3688         IllegalArgumentException thrown =
3689                 assertThrows(IllegalArgumentException.class, () ->
3690                         mCarAudioService.enableMirrorForAudioZones(audioZones));
3691 
3692         expectWithMessage("Repeated mirror audio zones exception").that(thrown)
3693                 .hasMessageThat().contains("must be unique");
3694     }
3695 
3696     @Test
enableMirrorForAudioZones_forAlreadyMirroredZones()3697     public void enableMirrorForAudioZones_forAlreadyMirroredZones() throws Exception {
3698         mCarAudioService.init();
3699         TestAudioZonesMirrorStatusCallbackCallback callback =
3700                 getAudioZonesMirrorStatusCallback();
3701         assignOccupantToAudioZones();
3702         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3703         callback.waitForCallback();
3704         callback.reset(/* count= */ 1);
3705 
3706         IllegalStateException thrown =
3707                 assertThrows(IllegalStateException.class, () ->
3708                         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
3709 
3710         expectWithMessage("Audio mirror exception for repeating request")
3711                 .that(thrown).hasMessageThat().contains("is already mirroring");
3712 
3713     }
3714 
3715     @Test
enableMirrorForAudioZones_afterSharedInPrimaryZone()3716     public void enableMirrorForAudioZones_afterSharedInPrimaryZone() throws Exception {
3717         mCarAudioService.init();
3718         TestPrimaryZoneMediaAudioRequestCallback
3719                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3720         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3721         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3722         assignOccupantToAudioZones();
3723         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
3724                 TEST_REAR_RIGHT_PASSENGER_OCCUPANT);
3725         requestToken.waitForCallback();
3726         requestToken.reset();
3727         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
3728         requestToken.waitForCallback();
3729         requestToken.reset();
3730 
3731         IllegalStateException thrown =
3732                 assertThrows(IllegalStateException.class, () ->
3733                         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
3734 
3735         expectWithMessage("Mirror audio zones while sharing in primary zone exception")
3736                 .that(thrown).hasMessageThat().contains("currently sharing to primary zone");
3737     }
3738 
3739     @Test
enableMirrorForAudioZones_forInvertedMirrorConfiguration()3740     public void enableMirrorForAudioZones_forInvertedMirrorConfiguration() throws Exception {
3741         mCarAudioService.init();
3742         TestAudioZonesMirrorStatusCallbackCallback callback =
3743                 getAudioZonesMirrorStatusCallback();
3744         assignOccupantToAudioZones();
3745         mCarAudioService.enableMirrorForAudioZones(new int[] {TEST_REAR_RIGHT_ZONE_ID,
3746                 TEST_REAR_LEFT_ZONE_ID});
3747         callback.waitForCallback();
3748         callback.reset(/* count= */ 1);
3749 
3750         IllegalStateException thrown =
3751                 assertThrows(IllegalStateException.class, () ->
3752                         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES));
3753 
3754         expectWithMessage("Audio mirror exception for inverted zone request")
3755                 .that(thrown).hasMessageThat().contains("is already mirroring");
3756     }
3757 
3758     @Test
enableMirrorForAudioZones_withNoMoreMirrorDevices_fails()3759     public void enableMirrorForAudioZones_withNoMoreMirrorDevices_fails() throws Exception {
3760         mCarAudioService.init();
3761         TestAudioZonesMirrorStatusCallbackCallback callback =
3762                 getAudioZonesMirrorStatusCallback();
3763         assignOccupantToAudioZones();
3764         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3765         callback.waitForCallback();
3766         callback.reset(/* count= */ 1);
3767 
3768         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
3769                 mCarAudioService.enableMirrorForAudioZones(
3770                         new int[] {TEST_FRONT_ZONE_ID, TEST_REAR_ROW_3_ZONE_ID}));
3771 
3772         expectWithMessage("Audio mirror for out of mirror devices exception")
3773                 .that(thrown).hasMessageThat().contains("available mirror output devices");
3774     }
3775 
3776     @Test
canEnableAudioMirror_withOutOfMirroringDevices()3777     public void canEnableAudioMirror_withOutOfMirroringDevices() throws Exception {
3778         mCarAudioService.init();
3779         TestAudioZonesMirrorStatusCallbackCallback callback =
3780                 getAudioZonesMirrorStatusCallback();
3781         assignOccupantToAudioZones();
3782         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3783         callback.waitForCallback();
3784 
3785         expectWithMessage("Can audio mirror status").that(mCarAudioService
3786                         .canEnableAudioMirror())
3787                 .isEqualTo(AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES);
3788     }
3789 
3790     @Test
canEnableAudioMirror_withAudioMirrorEnabledAndNoPendingRequests()3791     public void canEnableAudioMirror_withAudioMirrorEnabledAndNoPendingRequests() {
3792         mCarAudioService.init();
3793 
3794         expectWithMessage("Can audio mirror status before audio mirror request")
3795                 .that(mCarAudioService.canEnableAudioMirror())
3796                 .isEqualTo(AUDIO_MIRROR_CAN_ENABLE);
3797     }
3798 
3799     @Test
canEnableAudioMirror_withMirroringDisabled()3800     public void canEnableAudioMirror_withMirroringDisabled() {
3801         CarAudioService carAudioService = getCarAudioServiceWithoutMirroring();
3802         carAudioService.init();
3803 
3804         IllegalStateException thrown = assertThrows(IllegalStateException.class, () ->
3805                         mCarAudioService.canEnableAudioMirror());
3806 
3807         expectWithMessage("Can enable audio mirror exception")
3808                 .that(thrown).hasMessageThat().contains("Audio zones mirroring is required");
3809     }
3810 
3811     @Test
extendAudioMirrorRequest()3812     public void extendAudioMirrorRequest() throws Exception {
3813         mCarAudioService.init();
3814         TestAudioZonesMirrorStatusCallbackCallback callback =
3815                 getAudioZonesMirrorStatusCallback();
3816         assignOccupantToAudioZones();
3817         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3818         callback.waitForCallback();
3819         callback.reset(1);
3820 
3821         mCarAudioService.extendAudioMirrorRequest(requestId, new int[] {TEST_FRONT_ZONE_ID});
3822 
3823         callback.waitForCallback();
3824         expectWithMessage("Audio mirror approved status").that(callback.getLastStatus())
3825                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED);
3826         expectWithMessage("Audio mirror approved zones").that(callback.getLastZoneIds())
3827                 .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID,
3828                         TEST_FRONT_ZONE_ID);
3829     }
3830 
3831     @Test
extendAudioMirrorRequest_withNullAudioZones()3832     public void extendAudioMirrorRequest_withNullAudioZones() throws Exception {
3833         mCarAudioService.init();
3834         TestAudioZonesMirrorStatusCallbackCallback callback =
3835                 getAudioZonesMirrorStatusCallback();
3836         assignOccupantToAudioZones();
3837         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3838         callback.waitForCallback();
3839         callback.reset(1);
3840 
3841         NullPointerException thrown =
3842                 assertThrows(NullPointerException.class, () ->
3843                         mCarAudioService.extendAudioMirrorRequest(requestId,
3844                                 /* audioZones = */ null));
3845 
3846         expectWithMessage("Null audio zones to extend for mirror request exception")
3847                 .that(thrown).hasMessageThat().contains("Mirror audio zones");
3848     }
3849 
3850     @Test
extendAudioMirrorRequest_withPrimaryAudioZone()3851     public void extendAudioMirrorRequest_withPrimaryAudioZone() throws Exception {
3852         mCarAudioService.init();
3853         TestAudioZonesMirrorStatusCallbackCallback callback =
3854                 getAudioZonesMirrorStatusCallback();
3855         assignOccupantToAudioZones();
3856         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3857         callback.waitForCallback();
3858         callback.reset(1);
3859 
3860         IllegalArgumentException thrown =
3861                 assertThrows(IllegalArgumentException.class, () ->
3862                         mCarAudioService.extendAudioMirrorRequest(requestId,
3863                                 new int[] {PRIMARY_AUDIO_ZONE}));
3864 
3865         expectWithMessage("Primary audio zone to extend for mirror request exception")
3866                 .that(thrown).hasMessageThat().contains(
3867                         "Audio mirroring not allowed for primary audio zone");
3868     }
3869 
3870     @Test
getAudioZoneConfigInfos()3871     public void getAudioZoneConfigInfos() {
3872         mCarAudioService.init();
3873 
3874         List<CarAudioZoneConfigInfo> zoneConfigInfos =
3875                 mCarAudioService.getAudioZoneConfigInfos(TEST_REAR_LEFT_ZONE_ID);
3876 
3877         List<String> zoneConfigNames = new ArrayList(zoneConfigInfos.size());
3878         for (int index = 0; index < zoneConfigInfos.size(); index++) {
3879             zoneConfigNames.add(zoneConfigInfos.get(index).getName());
3880         }
3881         expectWithMessage("Zone configurations for secondary zone").that(zoneConfigNames)
3882                 .containsExactly(SECONDARY_ZONE_CONFIG_NAME_1, SECONDARY_ZONE_CONFIG_NAME_2);
3883     }
3884 
3885     @Test
getCurrentAudioZoneConfigInfo()3886     public void getCurrentAudioZoneConfigInfo() {
3887         mCarAudioService.init();
3888 
3889         CarAudioZoneConfigInfo currentZoneConfigInfo =
3890                 mCarAudioService.getCurrentAudioZoneConfigInfo(TEST_REAR_LEFT_ZONE_ID);
3891 
3892         expectWithMessage("Name of current zone configuration for secondary zone")
3893                 .that(currentZoneConfigInfo.getName()).isEqualTo(SECONDARY_ZONE_CONFIG_NAME_1);
3894     }
3895 
3896     @Test
switchZoneToConfig()3897     public void switchZoneToConfig() throws Exception {
3898         mCarAudioService.init();
3899         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3900         assignOccupantToAudioZones();
3901         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_LEFT_ZONE_ID);
3902 
3903         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback);
3904 
3905         callback.waitForCallback();
3906         expectWithMessage("Updated zone configuration")
3907                 .that(callback.getZoneConfig()).isEqualTo(zoneConfigSwitchTo);
3908         expectWithMessage("Zone configuration switching status")
3909                 .that(callback.getSwitchStatus()).isTrue();
3910     }
3911 
3912     @Test
switchZoneToConfig_forNonAssignedZone_fails()3913     public void switchZoneToConfig_forNonAssignedZone_fails() throws Exception {
3914         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
3915                 .thenReturn(UserManagerHelper.USER_NULL);
3916         mCarAudioService.init();
3917         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3918         assignOccupantToAudioZones();
3919         CarAudioZoneConfigInfo  zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_LEFT_ZONE_ID);
3920 
3921         IllegalStateException thrown =
3922                 assertThrows(IllegalStateException.class, () ->
3923                         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback));
3924 
3925         expectWithMessage("Switching zone configuration for unoccupied audio zone exception")
3926                 .that(thrown).hasMessageThat().contains("must have an active user");
3927     }
3928 
3929     @Test
switchZoneToConfig_afterSharedInPrimaryZone_fails()3930     public void switchZoneToConfig_afterSharedInPrimaryZone_fails() throws Exception {
3931         mCarAudioService.init();
3932         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3933         TestPrimaryZoneMediaAudioRequestCallback
3934                 requestToken = new TestPrimaryZoneMediaAudioRequestCallback();
3935         mCarAudioService.registerPrimaryZoneMediaAudioRequestCallback(requestToken);
3936         TestMediaRequestStatusCallback requestCallback = new TestMediaRequestStatusCallback();
3937         assignOccupantToAudioZones();
3938         long requestId = mCarAudioService.requestMediaAudioOnPrimaryZone(requestCallback,
3939                 TEST_REAR_LEFT_PASSENGER_OCCUPANT);
3940         requestToken.waitForCallback();
3941         requestToken.reset();
3942         mCarAudioService.allowMediaAudioOnPrimaryZone(requestToken, requestId, /* allowed= */ true);
3943         requestToken.waitForCallback();
3944         requestToken.reset();
3945         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_LEFT_ZONE_ID);
3946 
3947         IllegalStateException thrown =
3948                 assertThrows(IllegalStateException.class, () ->
3949                         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback));
3950 
3951         expectWithMessage("Switching zone configuration while sharing in primary zone exception")
3952                 .that(thrown).hasMessageThat().contains("currently sharing to primary zone");
3953     }
3954 
3955     @Test
switchZoneToConfig_afterMirroring_fails()3956     public void switchZoneToConfig_afterMirroring_fails() throws Exception {
3957         mCarAudioService.init();
3958         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3959         TestAudioZonesMirrorStatusCallbackCallback mirrorCallback =
3960                 getAudioZonesMirrorStatusCallback();
3961         assignOccupantToAudioZones();
3962         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
3963         mirrorCallback.waitForCallback();
3964         mirrorCallback.reset(/* count= */ 1);
3965         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_LEFT_ZONE_ID);
3966 
3967         IllegalStateException thrown =
3968                 assertThrows(IllegalStateException.class, () ->
3969                         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback));
3970 
3971         expectWithMessage("Switching zone configuration while audio mirroring").that(thrown)
3972                 .hasMessageThat().contains("currently in a mirroring configuration");
3973     }
3974 
3975     @Test
switchZoneToConfig_withPendingFocus_regainsFocus()3976     public void switchZoneToConfig_withPendingFocus_regainsFocus() throws Exception {
3977         mCarAudioService.init();
3978         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
3979         assignOccupantToAudioZones();
3980         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
3981         mCarAudioService.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
3982         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_RIGHT_ZONE_ID);
3983 
3984         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback);
3985 
3986         callback.waitForCallback();
3987         expectWithMessage("Updated zone configuration with pending focus")
3988                 .that(callback.getZoneConfig()).isEqualTo(zoneConfigSwitchTo);
3989         expectWithMessage("Zone configuration switching status with pending focus")
3990                 .that(callback.getSwitchStatus()).isTrue();
3991         List<Integer> focusChanges = getFocusChanges(audioFocusInfo);
3992         expectWithMessage("Media audio focus changes after switching zone")
3993                 .that(focusChanges).containsExactly(AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_GAIN);
3994     }
3995 
3996     @Test
switchZoneToConfig_withPendingFocus_updatesDuckingInfo()3997     public void switchZoneToConfig_withPendingFocus_updatesDuckingInfo() throws Exception {
3998         mCarAudioService.init();
3999         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4000         assignOccupantToAudioZones();
4001         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4002         mCarAudioService.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4003         ArgumentCaptor<List<CarDuckingInfo>> carDuckingInfosCaptor =
4004                 ArgumentCaptor.forClass(List.class);
4005         verify(mAudioControlWrapperAidl).onDevicesToDuckChange(carDuckingInfosCaptor.capture());
4006         verifyMediaDuckingInfoInZone(carDuckingInfosCaptor, TEST_REAR_RIGHT_ZONE_ID,
4007                 " before switching zone");
4008         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_RIGHT_ZONE_ID);
4009 
4010         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback);
4011 
4012         callback.waitForCallback();
4013         expectWithMessage("Updated zone configuration with pending focus")
4014                 .that(callback.getZoneConfig()).isEqualTo(zoneConfigSwitchTo);
4015         expectWithMessage("Zone configuration switching status with pending focus")
4016                 .that(callback.getSwitchStatus()).isTrue();
4017         verify(mAudioControlWrapperAidl, times(2))
4018                 .onDevicesToDuckChange(carDuckingInfosCaptor.capture());
4019         verifyMediaDuckingInfoInZone(carDuckingInfosCaptor, TEST_REAR_RIGHT_ZONE_ID,
4020                 " after switching zone");
4021     }
4022 
4023     @Test
switchZoneToConfig_withCurrentZoneConfigAndPendingFocus_notLoseAndRegainFocus()4024     public void switchZoneToConfig_withCurrentZoneConfigAndPendingFocus_notLoseAndRegainFocus()
4025             throws Exception {
4026         mCarAudioService.init();
4027         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4028         assignOccupantToAudioZones();
4029         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4030         mCarAudioService.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4031         CarAudioZoneConfigInfo currentZoneConfig =
4032                 mCarAudioService.getCurrentAudioZoneConfigInfo(TEST_REAR_RIGHT_ZONE_ID);
4033 
4034         mCarAudioService.switchZoneToConfig(currentZoneConfig, callback);
4035 
4036         callback.waitForCallback();
4037         expectWithMessage("Updated zone configuration with current configuration")
4038                 .that(callback.getZoneConfig()).isEqualTo(currentZoneConfig);
4039         expectWithMessage("Zone configuration switching status with current configuration")
4040                 .that(callback.getSwitchStatus()).isTrue();
4041         verify(mAudioManager, never()).dispatchAudioFocusChange(eq(audioFocusInfo), anyInt(),
4042                 any(AudioPolicy.class));
4043     }
4044 
4045     @Test
switchZoneToConfig_withVolumeGroupEventCallbackRegistered_invokesEvent()4046     public void switchZoneToConfig_withVolumeGroupEventCallbackRegistered_invokesEvent()
4047             throws Exception {
4048         mCarAudioService.init();
4049         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4050         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4051         assignOccupantToAudioZones();
4052         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_LEFT_ZONE_ID);
4053         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4054 
4055         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback);
4056 
4057         callback.waitForCallback();
4058         expectWithMessage("Updated zone configuration")
4059                 .that(callback.getZoneConfig()).isEqualTo(zoneConfigSwitchTo);
4060         expectWithMessage("Zone configuration switching status")
4061                 .that(callback.getSwitchStatus()).isTrue();
4062         volumeEventCallback.waitForCallback();
4063         expectWithMessage("Volume events count after switching zone configuration")
4064                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4065         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4066         expectWithMessage("Volume event type after switching zone configuration")
4067                 .that(groupEvent.getEventTypes())
4068                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED);
4069         expectWithMessage("Volume group infos after switching zone configuration")
4070                 .that(groupEvent.getCarVolumeGroupInfos())
4071                 .containsExactly(TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_0,
4072                         TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_1);
4073     }
4074 
4075     @Test
switchZoneToConfig_updatesVolumeGroupInfos()4076     public void switchZoneToConfig_updatesVolumeGroupInfos()
4077             throws Exception {
4078         mCarAudioService.init();
4079         SwitchAudioZoneConfigCallbackImpl callback = new SwitchAudioZoneConfigCallbackImpl();
4080         assignOccupantToAudioZones();
4081         Log.e(TAG, "Current volume group " + mCarAudioService.getVolumeGroupInfosForZone(
4082                 TEST_REAR_LEFT_ZONE_ID));
4083         expectWithMessage("Volume group infos before switching zone configuration")
4084                 .that(mCarAudioService.getVolumeGroupInfosForZone(TEST_REAR_LEFT_ZONE_ID))
4085                 .containsExactly(TEST_SECONDARY_ZONE_CONFIG_0_VOLUME_INFO);
4086         CarAudioZoneConfigInfo zoneConfigSwitchTo = getZoneConfigToSwitch(TEST_REAR_LEFT_ZONE_ID);
4087 
4088         mCarAudioService.switchZoneToConfig(zoneConfigSwitchTo, callback);
4089 
4090         callback.waitForCallback();
4091         expectWithMessage("Volume group infos after switching zone configuration")
4092                 .that(mCarAudioService.getVolumeGroupInfosForZone(TEST_REAR_LEFT_ZONE_ID))
4093                 .containsExactly(TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_0,
4094                         TEST_SECONDARY_ZONE_CONFIG_1_VOLUME_INFO_1);
4095     }
4096 
4097     @Test
disableAudioMirrorForZone_withInvalidZone()4098     public void disableAudioMirrorForZone_withInvalidZone() throws Exception {
4099         mCarAudioService.init();
4100         assignOccupantToAudioZones();
4101 
4102         IllegalArgumentException thrown =
4103                 assertThrows(IllegalArgumentException.class, () ->
4104                         mCarAudioService.disableAudioMirrorForZone(INVALID_AUDIO_ZONE));
4105 
4106         expectWithMessage("Disable mirror for invalid audio zone exception").that(thrown)
4107                         .hasMessageThat().contains("Invalid audio zone");
4108     }
4109 
4110     @Test
disableAudioMirrorForZone_withMirroringDisabled()4111     public void disableAudioMirrorForZone_withMirroringDisabled() {
4112         CarAudioService carAudioService = getCarAudioServiceWithoutMirroring();
4113         carAudioService.init();
4114 
4115         IllegalStateException thrown =
4116                 assertThrows(IllegalStateException.class, () ->
4117                         mCarAudioService.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID));
4118 
4119         expectWithMessage("Disable mirror for zone with audio mirroring disabled")
4120                 .that(thrown).hasMessageThat().contains("Audio zones mirroring is required");
4121     }
4122 
4123     @Test
disableAudioMirrorForZone_forNonMirroringZone()4124     public void disableAudioMirrorForZone_forNonMirroringZone() throws Exception {
4125         mCarAudioService.init();
4126         TestAudioZonesMirrorStatusCallbackCallback callback =
4127                 getAudioZonesMirrorStatusCallback();
4128         assignOccupantToAudioZones();
4129 
4130         mCarAudioService.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID);
4131 
4132         callback.waitForCallback();
4133         expectWithMessage("Disable audio mirror for non-mirroring zone callback count")
4134                 .that(callback.mNumberOfCalls).isEqualTo(0);
4135     }
4136 
4137     @Test
disableAudioMirrorForZone_forMirroringZones()4138     public void disableAudioMirrorForZone_forMirroringZones() throws Exception {
4139         mCarAudioService.init();
4140         TestAudioZonesMirrorStatusCallbackCallback callback =
4141                 getAudioZonesMirrorStatusCallback();
4142         assignOccupantToAudioZones();
4143         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4144         callback.waitForCallback();
4145         callback.reset(/* count= */ 1);
4146 
4147         mCarAudioService.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID);
4148 
4149         callback.waitForCallback();
4150         expectWithMessage("Callback count for disable audio mirror")
4151                 .that(callback.mNumberOfCalls).isEqualTo(2);
4152         expectWithMessage("Callback status disable audio mirror for mirroring zone")
4153                 .that(callback.getLastStatus())
4154                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
4155         expectWithMessage("Callback zones disable audio mirror for mirroring zone")
4156                 .that(callback.getLastZoneIds()).asList()
4157                 .containsExactly(TEST_REAR_RIGHT_ZONE_ID, TEST_REAR_LEFT_ZONE_ID);
4158     }
4159 
4160     @Test
disableAudioMirrorForZone_forMirroringZones_forFirstMirroringConfig()4161     public void disableAudioMirrorForZone_forMirroringZones_forFirstMirroringConfig()
4162             throws Exception {
4163         mCarAudioService.init();
4164         TestAudioZonesMirrorStatusCallbackCallback callback =
4165                 getAudioZonesMirrorStatusCallback();
4166         assignOccupantToAudioZones();
4167         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4168         callback.waitForCallback();
4169         callback.reset(/* count= */ 1);
4170 
4171         mCarAudioService.disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID);
4172 
4173         callback.waitForCallback();
4174         expectWithMessage("Callback count for disable audio mirror")
4175                 .that(callback.mNumberOfCalls).isEqualTo(2);
4176         expectWithMessage("Callback status disable audio mirror for mirroring zone")
4177                 .that(callback.getLastStatus())
4178                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
4179         expectWithMessage("Callback zones disable audio mirror for mirroring zone")
4180                 .that(callback.getLastZoneIds()).asList()
4181                 .containsExactly(TEST_REAR_RIGHT_ZONE_ID, TEST_REAR_LEFT_ZONE_ID);
4182         String audioMirrorOffCommand = captureAudioMirrorInfoCommand(2);
4183         expectWithMessage("Audio HAL off source for mirroring zone")
4184                 .that(audioMirrorOffCommand).contains(MIRROR_TEST_DEVICE);
4185         expectWithMessage("Audio HAL off signal for mirroring zone")
4186                 .that(audioMirrorOffCommand).contains(MIRROR_OFF_SIGNAL);
4187     }
4188 
4189     @Test
disableAudioMirrorForZone_withPendingFocus()4190     public void disableAudioMirrorForZone_withPendingFocus()
4191             throws Exception {
4192         mCarAudioService.init();
4193         TestAudioZonesMirrorStatusCallbackCallback callback =
4194                 getAudioZonesMirrorStatusCallback();
4195         assignOccupantToAudioZones();
4196         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4197         callback.waitForCallback();
4198         callback.reset(/* count= */ 2);
4199         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4200         mCarAudioService.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4201 
4202         mCarAudioService.disableAudioMirrorForZone(TEST_REAR_LEFT_ZONE_ID);
4203 
4204         callback.waitForCallback();
4205         List<Integer> focusChanges = getFocusChanges(audioFocusInfo);
4206         expectWithMessage("Media audio focus changes after disable mirror for zone")
4207                 .that(focusChanges).containsExactly(AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_GAIN);
4208     }
4209 
4210     @Test
disableAudioMirror_withoutMirroringDisabled()4211     public void disableAudioMirror_withoutMirroringDisabled() {
4212         CarAudioService carAudioService = getCarAudioServiceWithoutMirroring();
4213         carAudioService.init();
4214 
4215         IllegalStateException thrown =
4216                 assertThrows(IllegalStateException.class, () ->
4217                         carAudioService.disableAudioMirror(INVALID_REQUEST_ID));
4218 
4219         expectWithMessage("Disable mirror for audio zones with audio mirroring disabled")
4220                 .that(thrown).hasMessageThat().contains("Audio zones mirroring is required");
4221     }
4222 
4223     @Test
disableAudioMirror_withInvalidRequestId()4224     public void disableAudioMirror_withInvalidRequestId() {
4225         mCarAudioService.init();
4226 
4227         IllegalArgumentException thrown =
4228                 assertThrows(IllegalArgumentException.class, () ->
4229                         mCarAudioService.disableAudioMirror(INVALID_REQUEST_ID));
4230 
4231         expectWithMessage("Disable mirror for audio zones with audio invalid request id")
4232                 .that(thrown).hasMessageThat().contains("INVALID_REQUEST_ID");
4233     }
4234 
4235     @Test
disableAudioMirror_forNonMirroringZone()4236     public void disableAudioMirror_forNonMirroringZone() throws Exception {
4237         mCarAudioService.init();
4238         TestAudioZonesMirrorStatusCallbackCallback callback =
4239                 getAudioZonesMirrorStatusCallback();
4240         assignOccupantToAudioZones();
4241         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4242         callback.waitForCallback();
4243         callback.reset(1);
4244         mCarAudioService.disableAudioMirror(requestId);
4245         callback.waitForCallback();
4246         callback.reset(1);
4247 
4248         mCarAudioService.disableAudioMirror(requestId);
4249 
4250         expectWithMessage("Disable audio mirror for non-mirroring zone callback count")
4251                 .that(callback.mNumberOfCalls).isEqualTo(2);
4252     }
4253 
4254     @Test
disableAudioMirror_forMirroringZones()4255     public void disableAudioMirror_forMirroringZones() throws Exception {
4256         mCarAudioService.init();
4257         TestAudioZonesMirrorStatusCallbackCallback callback =
4258                 getAudioZonesMirrorStatusCallback();
4259         assignOccupantToAudioZones();
4260         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4261         callback.waitForCallback();
4262         callback.reset(/* count= */ 1);
4263 
4264         mCarAudioService.disableAudioMirror(requestId);
4265 
4266         callback.waitForCallback();
4267         expectWithMessage("Callback count for disable mirror in audio zones")
4268                 .that(callback.mNumberOfCalls).isEqualTo(2);
4269         expectWithMessage("Callback status disable audio mirror for mirroring zones")
4270                 .that(callback.getLastStatus())
4271                 .isEqualTo(CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED);
4272         expectWithMessage("Callback zones disable audio mirror for mirroring zones")
4273                 .that(callback.getLastZoneIds()).asList()
4274                 .containsExactly(TEST_REAR_RIGHT_ZONE_ID, TEST_REAR_LEFT_ZONE_ID);
4275         String audioMirrorOffCommand = captureAudioMirrorInfoCommand(2);
4276         expectWithMessage("Audio HAL off source for mirroring zones")
4277                 .that(audioMirrorOffCommand).contains(MIRROR_TEST_DEVICE);
4278         expectWithMessage("Audio HAL off signal for mirroring zones")
4279                 .that(audioMirrorOffCommand).contains(MIRROR_OFF_SIGNAL);
4280     }
4281 
4282     @Test
disableAudioMirror_withPendingFocus()4283     public void disableAudioMirror_withPendingFocus() throws Exception {
4284         mCarAudioService.init();
4285         TestAudioZonesMirrorStatusCallbackCallback callback =
4286                 getAudioZonesMirrorStatusCallback();
4287         assignOccupantToAudioZones();
4288         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4289         callback.waitForCallback();
4290         callback.reset(/* count= */ 2);
4291         AudioFocusInfo audioFocusInfo = createAudioFocusInfoForMedia(TEST_REAR_RIGHT_UID);
4292         mCarAudioService.requestAudioFocusForTest(audioFocusInfo, AUDIOFOCUS_REQUEST_GRANTED);
4293 
4294         mCarAudioService.disableAudioMirror(requestId);
4295 
4296         callback.waitForCallback();
4297         List<Integer> focusChanges = getFocusChanges(audioFocusInfo);
4298         expectWithMessage("Media audio focus changes after disable audio"
4299                 + "mirror for zones config").that(focusChanges)
4300                 .containsExactly(AUDIOFOCUS_LOSS_TRANSIENT, AUDIOFOCUS_GAIN);
4301     }
4302 
4303     @Test
getMirrorAudioZonesForAudioZone_withoutMirroringEnabled()4304     public void getMirrorAudioZonesForAudioZone_withoutMirroringEnabled()
4305             throws Exception {
4306         mCarAudioService.init();
4307         assignOccupantToAudioZones();
4308 
4309         int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
4310 
4311         expectWithMessage("Mirroring zones for non mirror zone %s", TEST_REAR_RIGHT_ZONE_ID)
4312                 .that(zones).asList().isEmpty();
4313     }
4314 
4315     @Test
getMirrorAudioZonesForAudioZone_withMirroringEnabled()4316     public void getMirrorAudioZonesForAudioZone_withMirroringEnabled() throws Exception {
4317         mCarAudioService.init();
4318         TestAudioZonesMirrorStatusCallbackCallback callback =
4319                 getAudioZonesMirrorStatusCallback();
4320         assignOccupantToAudioZones();
4321         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4322         callback.waitForCallback();
4323 
4324         int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
4325 
4326         expectWithMessage("Mirroring zones for mirror zone %s", TEST_REAR_RIGHT_ZONE_ID).that(zones)
4327                 .asList().containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
4328     }
4329 
4330     @Test
getMirrorAudioZonesForAudioZone_afterDisableMirror()4331     public void getMirrorAudioZonesForAudioZone_afterDisableMirror() throws Exception {
4332         mCarAudioService.init();
4333         TestAudioZonesMirrorStatusCallbackCallback callback =
4334                 getAudioZonesMirrorStatusCallback();
4335         assignOccupantToAudioZones();
4336         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4337         callback.waitForCallback();
4338         callback.reset(1);
4339         mCarAudioService.disableAudioMirror(requestId);
4340         callback.waitForCallback();
4341 
4342         int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
4343 
4344         expectWithMessage("Mirroring zones for mirror zone %s after disabling mirroring",
4345                 TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
4346     }
4347 
4348     @Test
getMirrorAudioZonesForAudioZone_afterPassengerLogout()4349     public void getMirrorAudioZonesForAudioZone_afterPassengerLogout() throws Exception {
4350         mCarAudioService.init();
4351         TestAudioZonesMirrorStatusCallbackCallback callback =
4352                 getAudioZonesMirrorStatusCallback();
4353         assignOccupantToAudioZones();
4354         mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4355         callback.waitForCallback();
4356         callback.reset(1);
4357         simulateLogoutRightPassengers();
4358         callback.waitForCallback();
4359 
4360         int[] zones = mCarAudioService.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID);
4361 
4362         expectWithMessage("Mirroring zones for mirror zone %s after logout",
4363                 TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
4364     }
4365 
4366     @Test
getMirrorAudioZonesForMirrorRequest_withMirroringEnabled()4367     public void getMirrorAudioZonesForMirrorRequest_withMirroringEnabled() throws Exception {
4368         mCarAudioService.init();
4369         TestAudioZonesMirrorStatusCallbackCallback callback =
4370                 getAudioZonesMirrorStatusCallback();
4371         assignOccupantToAudioZones();
4372         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4373         callback.waitForCallback();
4374 
4375         int[] zones = mCarAudioService.getMirrorAudioZonesForMirrorRequest(requestId);
4376 
4377         expectWithMessage("Mirroring zones for mirror request %s", requestId).that(zones).asList()
4378                 .containsExactly(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID);
4379     }
4380 
4381     @Test
getMirrorAudioZonesForMirrorRequest_afterDisableMirror()4382     public void getMirrorAudioZonesForMirrorRequest_afterDisableMirror() throws Exception {
4383         mCarAudioService.init();
4384         TestAudioZonesMirrorStatusCallbackCallback callback =
4385                 getAudioZonesMirrorStatusCallback();
4386         assignOccupantToAudioZones();
4387         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4388         callback.waitForCallback();
4389         callback.reset(1);
4390         mCarAudioService.disableAudioMirror(requestId);
4391         callback.waitForCallback();
4392 
4393         int[] zones = mCarAudioService.getMirrorAudioZonesForMirrorRequest(TEST_REAR_RIGHT_ZONE_ID);
4394 
4395         expectWithMessage("Mirroring zones for mirror request %s after disabling mirroring",
4396                 requestId).that(zones).asList().isEmpty();
4397     }
4398 
4399     @Test
getMirrorAudioZonesForMirrorRequest_afterPassengerLogout()4400     public void getMirrorAudioZonesForMirrorRequest_afterPassengerLogout() throws Exception {
4401         mCarAudioService.init();
4402         TestAudioZonesMirrorStatusCallbackCallback callback =
4403                 getAudioZonesMirrorStatusCallback();
4404         assignOccupantToAudioZones();
4405         long requestId = mCarAudioService.enableMirrorForAudioZones(TEST_MIRROR_AUDIO_ZONES);
4406         callback.waitForCallback();
4407         callback.reset(1);
4408         simulateLogoutRightPassengers();
4409         callback.waitForCallback();
4410 
4411         int[] zones = mCarAudioService.getMirrorAudioZonesForMirrorRequest(requestId);
4412 
4413         expectWithMessage("Mirroring zones for mirror request %s after logout",
4414                 TEST_REAR_RIGHT_ZONE_ID).that(zones).asList().isEmpty();
4415     }
4416 
4417     @Test
onAudioVolumeGroupChanged_dispatchCallbackEvent()4418     public void onAudioVolumeGroupChanged_dispatchCallbackEvent() throws RemoteException {
4419         CarAudioService useCoreAudioCarAudioService =
4420                 getCarAudioServiceUsingCoreAudioRoutingAndVolume();
4421         int musicIndex = useCoreAudioCarAudioService.getGroupVolume(
4422                 PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID);
4423         // Report a volume change
4424         when(mAudioManager.getVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
4425                 .thenReturn(musicIndex + 1);
4426         when(mAudioManager.getLastAudibleVolumeForVolumeGroup(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
4427                 .thenReturn(musicIndex + 1);
4428         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
4429                 .thenReturn(false);
4430 
4431         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
4432                 CoreAudioRoutingUtils.MUSIC_GROUP_NAME, /* flags= */ 0);
4433 
4434         verify(mCarVolumeCallbackHandler)
4435                 .onVolumeGroupChange(PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID,
4436                         FLAG_SHOW_UI | FLAG_PLAY_SOUND);
4437     }
4438 
4439     @Test
onAudioVolumeGroupChanged_noDispatchCallbackEvent_whenAlreadySynced()4440     public void onAudioVolumeGroupChanged_noDispatchCallbackEvent_whenAlreadySynced()
4441             throws RemoteException {
4442         CarAudioService useCoreAudioCarAudioService =
4443                 getCarAudioServiceUsingCoreAudioRoutingAndVolume();
4444         useCoreAudioCarAudioService.setGroupVolume(PRIMARY_AUDIO_ZONE,
4445                 CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID, CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX,
4446                 /* flags= */ 0);
4447         reset(mCarVolumeCallbackHandler);
4448 
4449         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
4450                 CoreAudioRoutingUtils.MUSIC_GROUP_NAME, /* flags= */ 0);
4451 
4452         verify(mCarVolumeCallbackHandler, never())
4453                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
4454     }
4455 
4456     @Test
onAudioVolumeGroupChanged_dispatchCallbackEvent_whenMuted()4457     public void onAudioVolumeGroupChanged_dispatchCallbackEvent_whenMuted() throws RemoteException {
4458         CarAudioService useCoreAudioCarAudioService =
4459                 getCarAudioServiceUsingCoreAudioRoutingAndVolume();
4460         // Report a mute change
4461         when(mAudioManager.getVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
4462                 .thenReturn(CoreAudioRoutingUtils.MUSIC_MIN_INDEX);
4463         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
4464                 .thenReturn(true);
4465 
4466         useCoreAudioCarAudioService.onAudioVolumeGroupChanged(PRIMARY_AUDIO_ZONE,
4467                 CoreAudioRoutingUtils.MUSIC_GROUP_NAME, /* flags= */ 0);
4468 
4469         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
4470                 CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID, FLAG_SHOW_UI);
4471     }
4472 
4473     @Test
onVolumeGroupEvent_withVolumeEvent_triggersCallback()4474     public void onVolumeGroupEvent_withVolumeEvent_triggersCallback() throws Exception {
4475         mCarAudioService.init();
4476         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4477         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4478 
4479         mCarAudioService.onVolumeGroupEvent(List.of(TEST_CAR_VOLUME_GROUP_EVENT));
4480 
4481         expectWithMessage("Volume event callback reception status")
4482                 .that(volumeEventCallback.waitForCallback()).isTrue();
4483         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
4484         verify(mCarVolumeCallbackHandler)
4485                 .onVolumeGroupChange(PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID,
4486                         /* flags= */ 0);
4487         expectWithMessage("Volume events count after volume event")
4488                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4489         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4490         expectWithMessage("Volume event type after volume event")
4491                 .that(groupEvent.getEventTypes())
4492                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_VOLUME_GAIN_INDEX_CHANGED);
4493         expectWithMessage("Volume group infos after unmute")
4494                 .that(groupEvent.getCarVolumeGroupInfos())
4495                 .containsExactly(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0);
4496     }
4497 
4498     @Test
onVolumeGroupEvent_withMuteEvent_triggersCallback()4499     public void onVolumeGroupEvent_withMuteEvent_triggersCallback() throws Exception {
4500         mCarAudioService.init();
4501         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4502         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4503 
4504         mCarAudioService.onVolumeGroupEvent(List.of(TEST_CAR_MUTE_GROUP_EVENT));
4505 
4506         expectWithMessage("Volume event callback reception status")
4507                 .that(volumeEventCallback.waitForCallback()).isTrue();
4508         verify(mCarVolumeCallbackHandler, never())
4509                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
4510         verify(mCarVolumeCallbackHandler)
4511                 .onGroupMuteChange(PRIMARY_AUDIO_ZONE, CoreAudioRoutingUtils.MUSIC_CAR_GROUP_ID,
4512                         /* flags= */ 0);
4513         expectWithMessage("Volume events count after mute event")
4514                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4515         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4516         expectWithMessage("Volume event type after mute event")
4517                 .that(groupEvent.getEventTypes())
4518                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
4519         expectWithMessage("Volume group infos after mute event")
4520                 .that(groupEvent.getCarVolumeGroupInfos())
4521                 .containsExactly(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0);
4522     }
4523 
4524     @Test
onVolumeGroupEvent_withoutMuteOrVolumeEvent_doesNotTriggerCallback()4525     public void onVolumeGroupEvent_withoutMuteOrVolumeEvent_doesNotTriggerCallback()
4526             throws Exception {
4527         mCarAudioService.init();
4528         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4529         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4530 
4531         mCarAudioService.onVolumeGroupEvent(List.of(TEST_CAR_ZONE_RECONFIGURATION_EVENT));
4532 
4533         expectWithMessage("Volume event callback reception status")
4534                 .that(volumeEventCallback.waitForCallback()).isTrue();
4535         verify(mCarVolumeCallbackHandler, never())
4536                 .onVolumeGroupChange(anyInt(), anyInt(), anyInt());
4537         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
4538         expectWithMessage("Volume events count after reconfiguration event")
4539                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4540         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4541         expectWithMessage("Volume event type after reconfiguration event")
4542                 .that(groupEvent.getEventTypes())
4543                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_ZONE_CONFIGURATION_CHANGED);
4544         expectWithMessage("Volume group infos after reconfiguration event")
4545                 .that(groupEvent.getCarVolumeGroupInfos())
4546                 .containsExactly(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0);
4547     }
4548 
4549     @Test
setMuted_whenUnmuted_onActivation_triggersCallback()4550     public void setMuted_whenUnmuted_onActivation_triggersCallback() throws Exception {
4551         mCarAudioService.init();
4552         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4553         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4554 
4555         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
4556                 /* mute= */ true, TEST_FLAGS);
4557 
4558         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
4559                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
4560         expectWithMessage("Volume event callback reception status after mute")
4561                 .that(volumeEventCallback.waitForCallback()).isTrue();
4562         expectWithMessage("Volume events count after mute")
4563                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4564         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4565         expectWithMessage("Volume event type after mute")
4566                 .that(groupEvent.getEventTypes())
4567                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
4568         expectWithMessage("Volume group infos after mute")
4569                 .that(groupEvent.getCarVolumeGroupInfos())
4570                 .containsExactly(TEST_PRIMARY_ZONE_VOLUME_INFO_0);
4571     }
4572 
4573     @Test
setMuted_whenUnmuted_onDeactivation_doesNotTriggerCallback()4574     public void setMuted_whenUnmuted_onDeactivation_doesNotTriggerCallback() throws Exception {
4575         mCarAudioService.init();
4576         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4577         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4578 
4579         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
4580                 /* mute= */ false, TEST_FLAGS);
4581 
4582         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
4583         expectWithMessage("Volume event callback reception status")
4584                 .that(volumeEventCallback.waitForCallback()).isFalse();
4585     }
4586 
4587     @Test
setMuted_whenMuted_onDeactivation_triggersCallback()4588     public void setMuted_whenMuted_onDeactivation_triggersCallback() throws Exception {
4589         mCarAudioService.init();
4590         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4591         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4592         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
4593                 /* mute= */ true, TEST_FLAGS);
4594         volumeEventCallback.waitForCallback();
4595         volumeEventCallback.reset();
4596         reset(mCarVolumeCallbackHandler);
4597 
4598         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
4599                 /* mute= */ false, TEST_FLAGS);
4600 
4601         verify(mCarVolumeCallbackHandler).onGroupMuteChange(PRIMARY_AUDIO_ZONE,
4602                 TEST_PRIMARY_ZONE_GROUP_0, TEST_FLAGS);
4603         expectWithMessage("Volume event callback reception status after unmute")
4604                 .that(volumeEventCallback.waitForCallback()).isTrue();
4605         expectWithMessage("Volume events count after mute")
4606                 .that(volumeEventCallback.getVolumeGroupEvents()).hasSize(1);
4607         CarVolumeGroupEvent groupEvent = volumeEventCallback.getVolumeGroupEvents().get(0);
4608         expectWithMessage("Volume event type after unmute")
4609                 .that(groupEvent.getEventTypes())
4610                 .isEqualTo(CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED);
4611         expectWithMessage("Volume group infos after unmute")
4612                 .that(groupEvent.getCarVolumeGroupInfos())
4613                 .containsExactly(TEST_PRIMARY_ZONE_UNMUTED_VOLUME_INFO_0);
4614     }
4615 
4616     @Test
setMuted_whenMuted_onActivation_doesNotTriggerCallback()4617     public void setMuted_whenMuted_onActivation_doesNotTriggerCallback() throws Exception {
4618         mCarAudioService.init();
4619         CarVolumeEventCallbackImpl volumeEventCallback = new CarVolumeEventCallbackImpl();
4620         mCarAudioService.registerCarVolumeEventCallback(volumeEventCallback);
4621         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
4622                 /* mute= */ true, TEST_FLAGS);
4623         volumeEventCallback.waitForCallback();
4624         volumeEventCallback.reset();
4625         reset(mCarVolumeCallbackHandler);
4626 
4627         mCarAudioService.setVolumeGroupMute(PRIMARY_AUDIO_ZONE, TEST_PRIMARY_ZONE_GROUP_0,
4628                 /* mute= */ true, TEST_FLAGS);
4629 
4630         verify(mCarVolumeCallbackHandler, never()).onGroupMuteChange(anyInt(), anyInt(), anyInt());
4631         expectWithMessage("Volume event callback reception status")
4632                 .that(volumeEventCallback.waitForCallback()).isFalse();
4633     }
4634 
removeUpToEquals(String command)4635     private String removeUpToEquals(String command) {
4636         return command.replaceAll("^[^=]*=", "");
4637     }
4638 
captureAudioMirrorInfoCommand(int count)4639     private String captureAudioMirrorInfoCommand(int count) {
4640         ArgumentCaptor<String> capture = ArgumentCaptor.forClass(String.class);
4641         verify(mAudioManager, times(count)).setParameters(capture.capture());
4642         return capture.getValue();
4643     }
4644 
getAudioZonesMirrorStatusCallback()4645     private TestAudioZonesMirrorStatusCallbackCallback getAudioZonesMirrorStatusCallback() {
4646         TestAudioZonesMirrorStatusCallbackCallback callback =
4647                 new TestAudioZonesMirrorStatusCallbackCallback(/* count= */ 1);
4648         mCarAudioService.registerAudioZonesMirrorStatusCallback(callback);
4649         return callback;
4650     }
4651 
assignOccupantToAudioZones()4652     private void assignOccupantToAudioZones() throws RemoteException {
4653         ICarOccupantZoneCallback occupantZoneCallback = getOccupantZoneCallback();
4654         occupantZoneCallback.onOccupantZoneConfigChanged(
4655                 CarOccupantZoneManager.ZONE_CONFIG_CHANGE_FLAG_USER);
4656     }
4657 
simulateLogoutPassengers()4658     private void simulateLogoutPassengers() throws Exception {
4659         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
4660                 .thenReturn(UserManagerHelper.USER_NULL);
4661         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
4662                 .thenReturn(UserManagerHelper.USER_NULL);
4663 
4664         assignOccupantToAudioZones();
4665     }
4666 
simulateLogoutRightPassengers()4667     private void simulateLogoutRightPassengers() throws Exception {
4668         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
4669                 .thenReturn(UserManagerHelper.USER_NULL);
4670 
4671         assignOccupantToAudioZones();
4672     }
4673 
simulatePassengersSwitch()4674     private void simulatePassengersSwitch() throws Exception {
4675         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_LEFT_OCCUPANT_ZONE_ID))
4676                 .thenReturn(TEST_REAR_RIGHT_USER_ID);
4677         when(mMockOccupantZoneService.getUserForOccupant(TEST_REAR_RIGHT_OCCUPANT_ZONE_ID))
4678                 .thenReturn(TEST_REAR_LEFT_USER_ID);
4679 
4680         assignOccupantToAudioZones();
4681     }
4682 
createCarAudioGainConfigInfo(int zoneId, String devicePortAddress, int volumeIndex)4683     private CarAudioGainConfigInfo createCarAudioGainConfigInfo(int zoneId,
4684             String devicePortAddress, int volumeIndex) {
4685         AudioGainConfigInfo configInfo = new AudioGainConfigInfo();
4686         configInfo.zoneId = zoneId;
4687         configInfo.devicePortAddress = devicePortAddress;
4688         configInfo.volumeIndex = volumeIndex;
4689         return new CarAudioGainConfigInfo(configInfo);
4690     }
4691 
getHalAudioGainCallback()4692     private HalAudioGainCallback getHalAudioGainCallback() {
4693         ArgumentCaptor<HalAudioGainCallback> captor = ArgumentCaptor.forClass(
4694                 HalAudioGainCallback.class);
4695         verify(mAudioControlWrapperAidl).registerAudioGainCallback(captor.capture());
4696         return captor.getValue();
4697     }
4698 
createHalAudioDeviceInfo(int id, String name, int minVal, int maxVal, int defaultVal, int stepVal, int type, String address)4699     private HalAudioDeviceInfo createHalAudioDeviceInfo(int id, String name, int minVal,
4700             int maxVal, int defaultVal, int stepVal, int type, String address) {
4701         AudioPortDeviceExt deviceExt = new AudioPortDeviceExt();
4702         deviceExt.device = new AudioDevice();
4703         deviceExt.device.type = new AudioDeviceDescription();
4704         deviceExt.device.type.type = type;
4705         deviceExt.device.type.connection = CONNECTION_BUS;
4706         deviceExt.device.address = AudioDeviceAddress.id(address);
4707         AudioPort audioPort = new AudioPort();
4708         audioPort.id = id;
4709         audioPort.name = name;
4710         audioPort.gains = new android.media.audio.common.AudioGain[] {
4711                 new android.media.audio.common.AudioGain() {{
4712                     mode = JOINT;
4713                     minValue = minVal;
4714                     maxValue = maxVal;
4715                     defaultValue = defaultVal;
4716                     stepValue = stepVal;
4717                 }}
4718         };
4719         audioPort.ext = AudioPortExt.device(deviceExt);
4720         return new HalAudioDeviceInfo(audioPort);
4721     }
4722 
getHalModuleChangeCallback()4723     private HalAudioModuleChangeCallback getHalModuleChangeCallback() {
4724         ArgumentCaptor<HalAudioModuleChangeCallback> captor = ArgumentCaptor.forClass(
4725                 HalAudioModuleChangeCallback.class);
4726         verify(mAudioControlWrapperAidl).setModuleChangeCallback(captor.capture());
4727         return captor.getValue();
4728     }
4729 
getCarAudioPlaybackCallback()4730     private AudioPlaybackCallback getCarAudioPlaybackCallback() {
4731         ArgumentCaptor<AudioPlaybackCallback> captor = ArgumentCaptor.forClass(
4732                 AudioPlaybackCallback.class);
4733         verify(mAudioManager).registerAudioPlaybackCallback(captor.capture(), any());
4734         return captor.getValue();
4735     }
4736 
getAudioKeyEventListener()4737     private CarInputService.KeyEventListener getAudioKeyEventListener() {
4738         ArgumentCaptor<CarInputService.KeyEventListener> captor =
4739                 ArgumentCaptor.forClass(CarInputService.KeyEventListener.class);
4740         verify(mMockCarInputService).registerKeyEventListener(captor.capture(), any());
4741         return captor.getValue();
4742     }
4743 
requestHalAudioFocus(int usage)4744     private void requestHalAudioFocus(int usage) {
4745         ArgumentCaptor<HalFocusListener> captor =
4746                 ArgumentCaptor.forClass(HalFocusListener.class);
4747         verify(mAudioControlWrapperAidl).registerFocusListener(captor.capture());
4748         HalFocusListener halFocusListener = captor.getValue();
4749         halFocusListener.requestAudioFocus(usage, PRIMARY_AUDIO_ZONE,
4750                 AudioManager.AUDIOFOCUS_GAIN_TRANSIENT);
4751     }
4752 
mockActivePlayback()4753     private void mockActivePlayback() {
4754         AudioPlaybackCallback callback = getCarAudioPlaybackCallback();
4755         callback.onPlaybackConfigChanged(List.of(getPlaybackConfig()));
4756     }
4757 
getPlaybackConfig()4758     private AudioPlaybackConfiguration getPlaybackConfig() {
4759         AudioAttributes audioAttributes = new AudioAttributes.Builder()
4760                 .setUsage(USAGE_MEDIA).build();
4761         AudioPlaybackConfiguration config = mock(AudioPlaybackConfiguration.class);
4762         when(config.getAudioAttributes()).thenReturn(audioAttributes);
4763         when(config.getAudioDeviceInfo()).thenReturn(mMediaOutputDevice);
4764         when(config.isActive()).thenReturn(true);
4765 
4766         return config;
4767     }
4768 
getCarAudioServiceWithoutMirroring()4769     private CarAudioService getCarAudioServiceWithoutMirroring() {
4770         AudioDeviceInfo[] outputDevices = generateOutputDeviceInfos();
4771         when(mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn(outputDevices);
4772         CarAudioService carAudioService =
4773                 new CarAudioService(mMockContext, mTemporaryAudioConfigurationWithoutMirroringFile
4774                         .getFile().getAbsolutePath(), mCarVolumeCallbackHandler);
4775         carAudioService.init();
4776         return carAudioService;
4777     }
4778 
getCarAudioServiceUsingCoreAudioRoutingAndVolume()4779     private CarAudioService getCarAudioServiceUsingCoreAudioRoutingAndVolume() {
4780         when(mMockResources.getBoolean(audioUseCoreVolume))
4781                 .thenReturn(/* audioUseCoreVolume= */ true);
4782         when(mMockResources.getBoolean(audioUseCoreRouting))
4783                 .thenReturn(/* audioUseCoreRouting= */ true);
4784         CarAudioService useCoreAudioCarAudioService =
4785                 new CarAudioService(mMockContext,
4786                         mTemporaryAudioConfigurationUsingCoreAudioFile.getFile().getAbsolutePath(),
4787                         mCarVolumeCallbackHandler);
4788         useCoreAudioCarAudioService.init();
4789         return useCoreAudioCarAudioService;
4790     }
4791 
mockGrantCarControlAudioSettingsPermission()4792     private void mockGrantCarControlAudioSettingsPermission() {
4793         mockContextCheckCallingOrSelfPermission(mMockContext,
4794                 PERMISSION_CAR_CONTROL_AUDIO_SETTINGS, PERMISSION_GRANTED);
4795     }
4796 
mockDenyCarControlAudioSettingsPermission()4797     private void mockDenyCarControlAudioSettingsPermission() {
4798         mockContextCheckCallingOrSelfPermission(mMockContext,
4799                 PERMISSION_CAR_CONTROL_AUDIO_SETTINGS, PERMISSION_DENIED);
4800     }
4801 
mockDenyCarControlAudioVolumePermission()4802     private void mockDenyCarControlAudioVolumePermission() {
4803         mockContextCheckCallingOrSelfPermission(mMockContext,
4804                 PERMISSION_CAR_CONTROL_AUDIO_VOLUME, PERMISSION_DENIED);
4805     }
4806 
generateInputDeviceInfos()4807     private AudioDeviceInfo[] generateInputDeviceInfos() {
4808         mMicrophoneInputDevice = new AudioDeviceInfoBuilder()
4809                 .setAddressName(PRIMARY_ZONE_MICROPHONE_ADDRESS)
4810                 .setType(TYPE_BUILTIN_MIC)
4811                 .setIsSource(true)
4812                 .build();
4813         mFmTunerInputDevice = new AudioDeviceInfoBuilder()
4814                 .setAddressName(PRIMARY_ZONE_FM_TUNER_ADDRESS)
4815                 .setType(TYPE_FM_TUNER)
4816                 .setIsSource(true)
4817                 .build();
4818         return new AudioDeviceInfo[]{mMicrophoneInputDevice, mFmTunerInputDevice};
4819     }
4820 
generateOutputDeviceInfos()4821     private AudioDeviceInfo[] generateOutputDeviceInfos() {
4822         mMediaOutputDevice = new AudioDeviceInfoBuilder()
4823                 .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4824                 .setAddressName(MEDIA_TEST_DEVICE)
4825                 .build();
4826         return new AudioDeviceInfo[] {
4827                 mMediaOutputDevice,
4828                 new AudioDeviceInfoBuilder()
4829                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4830                         .setAddressName(NAVIGATION_TEST_DEVICE)
4831                         .build(),
4832                 new AudioDeviceInfoBuilder()
4833                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4834                         .setAddressName(CALL_TEST_DEVICE)
4835                         .build(),
4836                 new AudioDeviceInfoBuilder()
4837                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4838                         .setAddressName(SYSTEM_BUS_DEVICE)
4839                         .build(),
4840                 new AudioDeviceInfoBuilder()
4841                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4842                         .setAddressName(NOTIFICATION_TEST_DEVICE)
4843                         .build(),
4844                 new AudioDeviceInfoBuilder()
4845                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4846                         .setAddressName(VOICE_TEST_DEVICE)
4847                         .build(),
4848                 new AudioDeviceInfoBuilder()
4849                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4850                         .setAddressName(RING_TEST_DEVICE)
4851                         .build(),
4852                 new AudioDeviceInfoBuilder()
4853                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4854                         .setAddressName(ALARM_TEST_DEVICE)
4855                         .build(),
4856                 new AudioDeviceInfoBuilder()
4857                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4858                         .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_0)
4859                         .build(),
4860                 new AudioDeviceInfoBuilder()
4861                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4862                         .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_1_0)
4863                         .build(),
4864                 new AudioDeviceInfoBuilder()
4865                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4866                         .setAddressName(SECONDARY_TEST_DEVICE_CONFIG_1_1)
4867                         .build(),
4868                 new AudioDeviceInfoBuilder()
4869                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4870                         .setAddressName(TERTIARY_TEST_DEVICE_1)
4871                         .build(),
4872                 new AudioDeviceInfoBuilder()
4873                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4874                         .setAddressName(TERTIARY_TEST_DEVICE_2)
4875                         .build(),
4876                 new AudioDeviceInfoBuilder()
4877                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4878                         .setAddressName(QUATERNARY_TEST_DEVICE_1)
4879                         .build(),
4880                 new AudioDeviceInfoBuilder()
4881                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4882                         .setAddressName(OEM_TEST_DEVICE)
4883                         .build(),
4884                 new AudioDeviceInfoBuilder()
4885                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4886                         .setAddressName(MIRROR_TEST_DEVICE).build(),
4887                 new AudioDeviceInfoBuilder()
4888                         .setAudioGains(new AudioGain[] {new GainBuilder().build()})
4889                         .setAddressName(TEST_REAR_ROW_3_DEVICE).build(),
4890         };
4891     }
4892 
mockCoreAudioRoutingAndVolume()4893     private void mockCoreAudioRoutingAndVolume() {
4894         doReturn(CoreAudioRoutingUtils.getProductStrategies())
4895                 .when(() -> AudioManager.getAudioProductStrategies());
4896         doReturn(CoreAudioRoutingUtils.getVolumeGroups())
4897                 .when(() -> AudioManager.getAudioVolumeGroups());
4898 
4899         when(mAudioManager.getVolumeGroupIdForAttributes(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES))
4900                 .thenReturn(CoreAudioRoutingUtils.MUSIC_GROUP_ID);
4901         when(mAudioManager.getMinVolumeIndexForAttributes(
4902                 eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
4903                 .thenReturn(CoreAudioRoutingUtils.MUSIC_MIN_INDEX);
4904         when(mAudioManager.getMaxVolumeIndexForAttributes(
4905                 eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
4906                 .thenReturn(CoreAudioRoutingUtils.MUSIC_MAX_INDEX);
4907         when(mAudioManager.getVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)))
4908                 .thenReturn(CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX);
4909         when(mAudioManager.getLastAudibleVolumeForVolumeGroup(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
4910                 .thenReturn(CoreAudioRoutingUtils.MUSIC_AM_INIT_INDEX);
4911         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.MUSIC_GROUP_ID))
4912                 .thenReturn(false);
4913 
4914         when(mAudioManager.getVolumeGroupIdForAttributes(CoreAudioRoutingUtils.NAV_ATTRIBUTES))
4915                 .thenReturn(CoreAudioRoutingUtils.NAV_GROUP_ID);
4916         when(mAudioManager.getMinVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.NAV_ATTRIBUTES)))
4917                 .thenReturn(CoreAudioRoutingUtils.NAV_MIN_INDEX);
4918         when(mAudioManager.getMaxVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.NAV_ATTRIBUTES)))
4919                 .thenReturn(CoreAudioRoutingUtils.NAV_MAX_INDEX);
4920         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.NAV_GROUP_ID))
4921                 .thenReturn(false);
4922 
4923         when(mAudioManager.getVolumeGroupIdForAttributes(CoreAudioRoutingUtils.OEM_ATTRIBUTES))
4924                 .thenReturn(CoreAudioRoutingUtils.OEM_GROUP_ID);
4925         when(mAudioManager.getMinVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.OEM_ATTRIBUTES)))
4926                 .thenReturn(CoreAudioRoutingUtils.OEM_MIN_INDEX);
4927         when(mAudioManager.getMaxVolumeIndexForAttributes(eq(CoreAudioRoutingUtils.OEM_ATTRIBUTES)))
4928                 .thenReturn(CoreAudioRoutingUtils.OEM_MAX_INDEX);
4929         when(mAudioManager.isVolumeGroupMuted(CoreAudioRoutingUtils.OEM_GROUP_ID))
4930                 .thenReturn(false);
4931 
4932         doReturn(CoreAudioRoutingUtils.MUSIC_GROUP_ID)
4933                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(
4934                         CoreAudioRoutingUtils.MUSIC_ATTRIBUTES));
4935         doReturn(CoreAudioRoutingUtils.MUSIC_ATTRIBUTES)
4936                 .when(() -> CoreAudioHelper.selectAttributesForVolumeGroupName(
4937                         CoreAudioRoutingUtils.MUSIC_GROUP_NAME));
4938 
4939         doReturn(CoreAudioRoutingUtils.NAV_GROUP_ID)
4940                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(
4941                         CoreAudioRoutingUtils.NAV_ATTRIBUTES));
4942         doReturn(CoreAudioRoutingUtils.NAV_ATTRIBUTES)
4943                 .when(() -> CoreAudioHelper.selectAttributesForVolumeGroupName(
4944                         CoreAudioRoutingUtils.NAV_GROUP_NAME));
4945 
4946         doReturn(CoreAudioRoutingUtils.OEM_GROUP_ID)
4947                 .when(() -> CoreAudioHelper.getVolumeGroupIdForAudioAttributes(
4948                         CoreAudioRoutingUtils.OEM_ATTRIBUTES));
4949         doReturn(CoreAudioRoutingUtils.OEM_ATTRIBUTES)
4950                 .when(() -> CoreAudioHelper.selectAttributesForVolumeGroupName(
4951                         CoreAudioRoutingUtils.OEM_GROUP_NAME));
4952     }
4953 
createAudioFocusInfoForMedia()4954     private static AudioFocusInfo createAudioFocusInfoForMedia() {
4955         return createAudioFocusInfoForMedia(MEDIA_APP_UID);
4956     }
4957 
createAudioFocusInfoForMedia(int uid)4958     private static AudioFocusInfo createAudioFocusInfoForMedia(int uid) {
4959         AudioAttributes.Builder builder = new AudioAttributes.Builder();
4960         builder.setUsage(USAGE_MEDIA);
4961 
4962         return new AudioFocusInfo(builder.build(), uid, MEDIA_CLIENT_ID,
4963                 MEDIA_PACKAGE_NAME, AUDIOFOCUS_GAIN, AUDIOFOCUS_LOSS, MEDIA_EMPTY_FLAG, SDK_INT);
4964     }
4965 
getFocusChanges(AudioFocusInfo info)4966     private List<Integer> getFocusChanges(AudioFocusInfo info) {
4967         ArgumentCaptor<Integer> captor = ArgumentCaptor.forClass(Integer.class);
4968         verify(mAudioManager, atLeastOnce()).dispatchAudioFocusChange(eq(info), captor.capture(),
4969                 any());
4970         return captor.getAllValues();
4971     }
4972 
verifyMediaDuckingInfoInZone(ArgumentCaptor<List<CarDuckingInfo>> carDuckingInfosCaptor, int zoneId, String message)4973     private void verifyMediaDuckingInfoInZone(ArgumentCaptor<List<CarDuckingInfo>>
4974             carDuckingInfosCaptor, int zoneId, String message) {
4975         expectWithMessage("Zone size of notified ducking info " + message)
4976                 .that(carDuckingInfosCaptor.getValue().size()).isEqualTo(1);
4977         CarDuckingInfo duckingInfo = carDuckingInfosCaptor.getValue().get(0);
4978         expectWithMessage("Ducking info zone id " + message)
4979                 .that(duckingInfo.mZoneId).isEqualTo(zoneId);
4980         expectWithMessage("Audio attributes holding focus " + message)
4981                 .that(CarHalAudioUtils.metadataToAudioAttributes(duckingInfo
4982                         .mPlaybackMetaDataHoldingFocus))
4983                 .containsExactly(CarAudioContext.getAudioAttributeFromUsage(USAGE_MEDIA));
4984     }
4985 
getZoneConfigToSwitch(int zoneId)4986     private CarAudioZoneConfigInfo getZoneConfigToSwitch(int zoneId) {
4987         CarAudioZoneConfigInfo currentZoneConfigInfo =
4988                 mCarAudioService.getCurrentAudioZoneConfigInfo(zoneId);
4989         List<CarAudioZoneConfigInfo> zoneConfigInfos =
4990                 mCarAudioService.getAudioZoneConfigInfos(zoneId);
4991 
4992         for (int index = 0; index < zoneConfigInfos.size(); index++) {
4993             if (!currentZoneConfigInfo.equals(zoneConfigInfos.get(index))) {
4994                 return zoneConfigInfos.get(index);
4995             }
4996         }
4997         return null;
4998     }
4999 
5000     private static final class TestPrimaryZoneMediaAudioRequestCallback extends
5001             IPrimaryZoneMediaAudioRequestCallback.Stub {
5002         private long mRequestId = INVALID_REQUEST_ID;
5003         private CarOccupantZoneManager.OccupantZoneInfo mInfo;
5004         private CountDownLatch mStatusLatch = new CountDownLatch(1);
5005         private int mStatus;
5006         private DeathRecipient mDeathRecipient;
5007 
5008         @Override
linkToDeath(@onNull DeathRecipient recipient, int flags)5009         public void linkToDeath(@NonNull DeathRecipient recipient, int flags) {
5010             mDeathRecipient = recipient;
5011             super.linkToDeath(recipient, flags);
5012         }
5013 
5014         @Override
onRequestMediaOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info, long requestId)5015         public void onRequestMediaOnPrimaryZone(CarOccupantZoneManager.OccupantZoneInfo info,
5016                 long requestId) {
5017             mInfo = info;
5018             mRequestId = requestId;
5019             mStatusLatch.countDown();
5020         }
5021 
5022         @Override
onMediaAudioRequestStatusChanged( @onNull CarOccupantZoneManager.OccupantZoneInfo info, long requestId, int status)5023         public void onMediaAudioRequestStatusChanged(
5024                 @NonNull CarOccupantZoneManager.OccupantZoneInfo info,
5025                 long requestId, int status) {
5026             mInfo = info;
5027             mRequestId = requestId;
5028             mStatus = status;
5029             mStatusLatch.countDown();
5030         }
5031 
waitForCallback()5032         private void waitForCallback() throws Exception {
5033             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
5034         }
5035 
reset()5036         public void reset() {
5037             mInfo = null;
5038             mRequestId = INVALID_REQUEST_ID;
5039             mStatus = INVALID_STATUS;
5040             mStatusLatch = new CountDownLatch(1);
5041         }
5042     }
5043 
5044     private static final class TestMediaRequestStatusCallback extends
5045             IMediaAudioRequestStatusCallback.Stub {
5046         private long mRequestId = INVALID_REQUEST_ID;
5047         private CarOccupantZoneManager.OccupantZoneInfo mInfo;
5048         private int mStatus;
5049         private CountDownLatch mStatusLatch = new CountDownLatch(1);
5050 
5051         @Override
onMediaAudioRequestStatusChanged( @onNull CarOccupantZoneManager.OccupantZoneInfo info, long requestId, @CarAudioManager.MediaAudioRequestStatus int status)5052         public void onMediaAudioRequestStatusChanged(
5053                 @NonNull CarOccupantZoneManager.OccupantZoneInfo info,
5054                 long requestId, @CarAudioManager.MediaAudioRequestStatus int status)
5055                 throws RemoteException {
5056             mInfo = info;
5057             mRequestId = requestId;
5058             mStatus = status;
5059             mStatusLatch.countDown();
5060         }
5061 
waitForCallback()5062         private void waitForCallback() throws Exception {
5063             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
5064         }
5065 
reset()5066         private void reset() {
5067             mInfo = null;
5068             mRequestId = INVALID_REQUEST_ID;
5069             mStatus = INVALID_STATUS;
5070             mStatusLatch = new CountDownLatch(1);
5071         }
5072     }
5073 
5074     private static final class TestAudioZonesMirrorStatusCallbackCallback extends
5075             IAudioZonesMirrorStatusCallback.Stub {
5076 
5077         private static final long TEST_CALLBACK_TIMEOUT_MS = 300;
5078 
5079         private List<int[]> mZoneIds = new ArrayList<>();
5080         private List<Integer> mStatus = new ArrayList<>();
5081         private int mNumberOfCalls = 0;
5082         private CountDownLatch mStatusLatch;
5083 
TestAudioZonesMirrorStatusCallbackCallback(int count)5084         private TestAudioZonesMirrorStatusCallbackCallback(int count) {
5085             mStatusLatch = new CountDownLatch(count);
5086         }
5087 
5088         @Override
onAudioZonesMirrorStatusChanged(int[] zoneIds, int status)5089         public void onAudioZonesMirrorStatusChanged(int[] zoneIds, int status) {
5090             mZoneIds.add(zoneIds);
5091             mStatus.add(status);
5092             mNumberOfCalls++;
5093             mStatusLatch.countDown();
5094         }
5095 
waitForCallback()5096         private void waitForCallback() throws Exception {
5097             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
5098         }
5099 
reset(int count)5100         public void reset(int count) {
5101             mStatusLatch = new CountDownLatch(count);
5102         }
5103 
getLastZoneIds()5104         private int[] getLastZoneIds() {
5105             return mZoneIds.get(mZoneIds.size() - 1);
5106         }
5107 
getZoneIds(int index)5108         private int[] getZoneIds(int index) {
5109             return mZoneIds.get(index);
5110         }
5111 
getLastStatus()5112         public int getLastStatus() {
5113             return mStatus.get(mStatus.size() - 1);
5114         }
5115 
getStatus(int index)5116         public int getStatus(int index) {
5117             return mStatus.get(index);
5118         }
5119     }
5120 
5121     private static final class SwitchAudioZoneConfigCallbackImpl extends
5122             ISwitchAudioZoneConfigCallback.Stub {
5123         private CountDownLatch mStatusLatch = new CountDownLatch(1);
5124         private CarAudioZoneConfigInfo mZoneConfig;
5125         private boolean mIsSuccessful;
5126 
5127         @Override
onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfig, boolean isSuccessful)5128         public void onAudioZoneConfigSwitched(CarAudioZoneConfigInfo zoneConfig,
5129                 boolean isSuccessful) {
5130             mZoneConfig = zoneConfig;
5131             mIsSuccessful = isSuccessful;
5132             mStatusLatch.countDown();
5133         }
5134 
waitForCallback()5135         private void waitForCallback() throws Exception {
5136             mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
5137         }
5138 
getZoneConfig()5139         CarAudioZoneConfigInfo getZoneConfig() {
5140             return mZoneConfig;
5141         }
5142 
getSwitchStatus()5143         boolean getSwitchStatus() {
5144             return mIsSuccessful;
5145         }
5146     }
5147 
5148     private static final class CarVolumeEventCallbackImpl extends ICarVolumeEventCallback.Stub {
5149         private CountDownLatch mStatusLatch = new CountDownLatch(1);
5150         private List<CarVolumeGroupEvent> mVolumeGroupEvents;
5151 
5152         @Override
onVolumeGroupEvent(List<CarVolumeGroupEvent> volumeGroupEvents)5153         public void onVolumeGroupEvent(List<CarVolumeGroupEvent> volumeGroupEvents) {
5154             mVolumeGroupEvents = volumeGroupEvents;
5155             mStatusLatch.countDown();
5156         }
5157 
5158         @Override
onMasterMuteChanged(int zoneId, int flags)5159         public void onMasterMuteChanged(int zoneId, int flags) {
5160             mStatusLatch.countDown();
5161         }
5162 
waitForCallback()5163         private boolean waitForCallback() throws Exception {
5164             return mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS);
5165         }
5166 
getVolumeGroupEvents()5167         List<CarVolumeGroupEvent> getVolumeGroupEvents() {
5168             return mVolumeGroupEvents;
5169         }
5170 
reset()5171         public void reset() {
5172             mStatusLatch = new CountDownLatch(1);
5173         }
5174     }
5175 }
5176