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