/* * Copyright (C) 2023 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.car.media; import static android.car.media.CarAudioManager.AUDIO_FEATURE_DYNAMIC_ROUTING; import static android.car.media.CarAudioManager.CONFIG_STATUS_CHANGED; import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE; import static android.media.AudioAttributes.USAGE_ASSISTANCE_NAVIGATION_GUIDANCE; import static android.media.AudioAttributes.USAGE_GAME; import static android.media.AudioAttributes.USAGE_MEDIA; import static android.media.AudioDeviceInfo.TYPE_BUILTIN_MIC; import static android.media.AudioDeviceInfo.TYPE_FM_TUNER; import static org.junit.Assert.assertThrows; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.clearInvocations; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.timeout; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import android.car.CarOccupantZoneManager.OccupantZoneInfo; import android.car.test.AbstractExpectableTestCase; import android.content.Context; import android.media.AudioAttributes; import android.media.AudioDeviceAttributes; import android.media.AudioDeviceInfo; import android.media.AudioManager; import android.os.Handler; import android.os.HandlerThread; import android.os.IBinder; import android.os.RemoteException; import android.platform.test.ravenwood.RavenwoodRule; import com.android.car.audio.AudioDeviceInfoBuilder; import com.android.car.internal.ICarBase; import org.junit.After; import org.junit.Before; import org.junit.Rule; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.ArgumentCaptor; import org.mockito.Mock; import org.mockito.junit.MockitoJUnitRunner; import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.Executor; import java.util.concurrent.TimeUnit; @RunWith(MockitoJUnitRunner.class) public final class CarAudioManagerUnitTest extends AbstractExpectableTestCase { // Required to set the process ID and set the "main" thread for this test, otherwise // getMainLooper will return null. @Rule public final RavenwoodRule mRavenwood = new RavenwoodRule.Builder().setProcessApp() .setProvideMainThread(true).build(); private static final String Car_AUDIO_MANAGER_TEST_THREAD_NAME = "CarAudioManagerUnitTest"; private static final String MICROPHONE_ADDRESS = "Built-In Mic"; private static final String FM_TUNER_ADDRESS = "FM Tuner"; private static final String MEDIA_TEST_DEVICE = "media_bus_device"; private static final String NAVIGATION_TEST_DEVICE = "navigation_bus_device"; private static final int TEST_REAR_LEFT_ZONE_ID = 1; private static final int TEST_REAR_RIGHT_ZONE_ID = 2; private static final int TEST_FRONT_ZONE_ID = 3; private static final int TEST_VOLUME_GROUP_ID = 1; private static final int TEST_VOLUME_GROUP_INDEX = 10; private static final int TEST_FLAGS = 0; private static final int TEST_GAIN_MIN_VALUE = -3000; private static final int TEST_GAIN_MAX_VALUE = -1000; private static final int TEST_GAIN_DEFAULT_VALUE = -2000; private static final int TEST_VOLUME_GROUP_COUNT = 2; private static final int TEST_USAGE = USAGE_MEDIA; private static final int TEST_UID = 15; private static final long TEST_TIME_OUT_MS = 500; private static final long TEST_REQUEST_ID = 1; private static final AudioDeviceInfo TEST_MICROPHONE_INPUT_DEVICE = new AudioDeviceInfoBuilder().setAddressName(MICROPHONE_ADDRESS) .setType(TYPE_BUILTIN_MIC).setIsSource(true).build(); private static final AudioDeviceInfo TEST_TUNER_INPUT_DEVICE = new AudioDeviceInfoBuilder() .setAddressName(FM_TUNER_ADDRESS).setType(TYPE_FM_TUNER).setIsSource(true).build(); private static final AudioDeviceInfo TEST_MEDIA_OUTPUT_DEVICE = new AudioDeviceInfoBuilder() .setAddressName(MEDIA_TEST_DEVICE).build(); private static final AudioDeviceInfo TEST_NAV_OUTPUT_DEVICE = new AudioDeviceInfoBuilder() .setAddressName(NAVIGATION_TEST_DEVICE).build(); private static final Executor DIRECT_EXECUTOR = Runnable::run; private HandlerThread mHandlerThread; private Handler mHandler; private final RemoteException mRemoteException = new RemoteException(); @Mock private ICarBase mCar; @Mock private IBinder mBinderMock; @Mock private ICarAudio mServiceMock; @Mock private AudioManager mAudioManagerMock; @Mock private Context mContextMock; @Mock private CarVolumeGroupInfo mCarVolumeGroupInfoMock1; @Mock private CarVolumeGroupInfo mCarVolumeGroupInfoMock2; @Mock private AudioAttributes mAudioAttributesMock1; @Mock private AudioAttributes mAudioAttributesMock2; @Mock private CarAudioManager.CarVolumeCallback mVolumeCallbackMock1; @Mock private CarAudioManager.CarVolumeCallback mVolumeCallbackMock2; @Mock private CarAudioZoneConfigInfo mZoneConfigInfoMock1; @Mock private CarAudioZoneConfigInfo mZoneConfigInfoMock2; @Mock private OccupantZoneInfo mOccupantZoneInfoMock; @Mock private SwitchAudioZoneConfigCallback mSwitchCallbackMock; @Mock private CarVolumeGroupEventCallback mVolumeGroupEventCallbackMock1; @Mock private CarVolumeGroupEventCallback mVolumeGroupEventCallbackMock2; @Mock private PrimaryZoneMediaAudioRequestCallback mPrimaryZoneMediaAudioRequestCallbackMock; @Mock private MediaAudioRequestStatusCallback mMediaAudioRequestStatusCallbackMock; @Mock private AudioZonesMirrorStatusCallback mAudioZonesMirrorStatusCallback; private CarAudioManager mCarAudioManager; private TestAudioZoneConfigurationsChangeCallback mTestConfigCallback; @Before public void setUp() throws Exception { mHandlerThread = new HandlerThread(Car_AUDIO_MANAGER_TEST_THREAD_NAME); mHandlerThread.start(); mHandler = new Handler(mHandlerThread.getLooper()); when(mBinderMock.queryLocalInterface(anyString())).thenReturn(mServiceMock); when(mCar.getContext()).thenReturn(mContextMock); when(mCar.getEventHandler()).thenReturn(mHandler); when(mContextMock.getSystemService(AudioManager.class)).thenReturn(mAudioManagerMock); mCarAudioManager = new CarAudioManager(mCar, mBinderMock); doAnswer(invocation -> invocation.getArgument(1)).when(mCar) .handleRemoteExceptionFromCarService(any(RemoteException.class), any()); mTestConfigCallback = new TestAudioZoneConfigurationsChangeCallback(); when(mServiceMock.registerAudioZoneConfigsChangeCallback(any())).thenReturn(true); } @After public void tearDown() throws Exception { mHandlerThread.quitSafely(); mHandlerThread.join(); } @Test public void isAudioFeatureEnabled() throws Exception { when(mServiceMock.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)).thenReturn(true); expectWithMessage("Dynamic audio routing enabled") .that(mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) .isTrue(); } @Test public void isAudioFeatureEnabled_withServiceRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).isAudioFeatureEnabled( AUDIO_FEATURE_DYNAMIC_ROUTING); expectWithMessage("Dynamic audio routing disabled when service throws remote exception") .that(mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_DYNAMIC_ROUTING)) .isFalse(); } @Test public void setGroupVolume_withoutZoneId() throws Exception { mCarAudioManager.setGroupVolume(TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS); verify(mServiceMock).setGroupVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS); } @Test public void setGroupVolume_withZoneId() throws Exception { mCarAudioManager.setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS); verify(mServiceMock).setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS); } @Test public void setGroupVolume_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS); mCarAudioManager.setGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_VOLUME_GROUP_INDEX, TEST_FLAGS); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void getGroupMaxVolume_withoutZoneId() throws Exception { when(mServiceMock.getGroupMaxVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID)) .thenReturn(TEST_GAIN_MAX_VALUE); expectWithMessage("Primary zone max volume") .that(mCarAudioManager.getGroupMaxVolume(TEST_VOLUME_GROUP_ID)) .isEqualTo(TEST_GAIN_MAX_VALUE); } @Test public void getGroupMaxVolume_withZoneId() throws Exception { when(mServiceMock.getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .thenReturn(TEST_GAIN_MAX_VALUE); expectWithMessage("Rear right zone max volume").that(mCarAudioManager .getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .isEqualTo(TEST_GAIN_MAX_VALUE); } @Test public void getGroupMaxVolume_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Rear right zone max volume when service throws remote exception") .that(mCarAudioManager.getGroupMaxVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(0); } @Test public void getGroupMinVolume_withoutZoneId() throws Exception { when(mServiceMock.getGroupMinVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID)) .thenReturn(TEST_GAIN_MIN_VALUE); expectWithMessage("Primary zone min volume") .that(mCarAudioManager.getGroupMinVolume(TEST_VOLUME_GROUP_ID)) .isEqualTo(TEST_GAIN_MIN_VALUE); } @Test public void getGroupMinVolume_withZoneId() throws Exception { when(mServiceMock.getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .thenReturn(TEST_GAIN_MIN_VALUE); expectWithMessage("Rear right zone min volume").that(mCarAudioManager .getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .isEqualTo(TEST_GAIN_MIN_VALUE); } @Test public void getGroupMinVolume_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Rear right zone min volume when service throws remote exception") .that(mCarAudioManager.getGroupMinVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(0); } @Test public void getGroupVolume_withoutZoneId() throws Exception { when(mServiceMock.getGroupVolume(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID)) .thenReturn(TEST_GAIN_DEFAULT_VALUE); expectWithMessage("Primary zone volume") .that(mCarAudioManager.getGroupVolume(TEST_VOLUME_GROUP_ID)) .isEqualTo(TEST_GAIN_DEFAULT_VALUE); } @Test public void getGroupVolume_withZoneId() throws Exception { when(mServiceMock.getGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .thenReturn(TEST_GAIN_DEFAULT_VALUE); expectWithMessage("Rear right zone volume").that(mCarAudioManager.getGroupVolume( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(TEST_GAIN_DEFAULT_VALUE); } @Test public void getGroupVolume_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Rear right zone volume when service throws remote exception") .that(mCarAudioManager.getGroupVolume(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(0); } @Test public void setFadeTowardFront() throws Exception { float value = 0.3f; mCarAudioManager.setFadeTowardFront(value); verify(mServiceMock).setFadeTowardFront(value); } @Test public void setFadeTowardFront_withServiceRemoteException() throws Exception { float value = 0.3f; doThrow(mRemoteException).when(mServiceMock).setFadeTowardFront(value); mCarAudioManager.setFadeTowardFront(value); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void setBalanceTowardRight() throws Exception { float value = 0.4f; mCarAudioManager.setBalanceTowardRight(value); verify(mServiceMock).setBalanceTowardRight(value); } @Test public void setBalanceTowardRight_withServiceRemoteException() throws Exception { float value = 0.4f; doThrow(mRemoteException).when(mServiceMock).setBalanceTowardRight(value); mCarAudioManager.setBalanceTowardRight(value); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void getVolumeGroupCount_withoutZoneId() throws Exception { when(mServiceMock.getVolumeGroupCount(PRIMARY_AUDIO_ZONE)) .thenReturn(TEST_VOLUME_GROUP_COUNT); expectWithMessage("Primary zone volume group count") .that(mCarAudioManager.getVolumeGroupCount()).isEqualTo(TEST_VOLUME_GROUP_COUNT); } @Test public void getVolumeGroupCount_withZoneId() throws Exception { when(mServiceMock.getVolumeGroupCount(TEST_REAR_RIGHT_ZONE_ID)) .thenReturn(TEST_VOLUME_GROUP_COUNT); expectWithMessage("Rear right zone volume group count") .that(mCarAudioManager.getVolumeGroupCount(TEST_REAR_RIGHT_ZONE_ID)) .isEqualTo(TEST_VOLUME_GROUP_COUNT); } @Test public void getVolumeGroupCount_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getVolumeGroupCount( TEST_REAR_RIGHT_ZONE_ID); expectWithMessage("Rear right zone volume group count when service throws remote exception") .that(mCarAudioManager.getVolumeGroupCount(TEST_REAR_RIGHT_ZONE_ID)).isEqualTo(0); } @Test public void getVolumeGroupIdForUsage_withoutZoneId() throws Exception { when(mServiceMock.getVolumeGroupIdForUsage(PRIMARY_AUDIO_ZONE, TEST_USAGE)) .thenReturn(TEST_VOLUME_GROUP_ID); expectWithMessage("Volume group id for media usage in primary zone") .that(mCarAudioManager.getVolumeGroupIdForUsage(TEST_USAGE)) .isEqualTo(TEST_VOLUME_GROUP_ID); } @Test public void getVolumeGroupIdForUsage_withZoneId() throws Exception { when(mServiceMock.getVolumeGroupIdForUsage(TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE)) .thenReturn(TEST_VOLUME_GROUP_ID); expectWithMessage("Volume group id for media usage in rear right zone") .that(mCarAudioManager.getVolumeGroupIdForUsage(TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE)).isEqualTo(TEST_VOLUME_GROUP_ID); } @Test public void getVolumeGroupIdForUsage_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getVolumeGroupIdForUsage( TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE); expectWithMessage("Volume group id for media usage in rear right zone when service " + "throws remote exception").that(mCarAudioManager.getVolumeGroupIdForUsage( TEST_REAR_RIGHT_ZONE_ID, TEST_USAGE)).isEqualTo(0); } @Test public void getUsagesForVolumeGroupId_withoutZoneId() throws Exception { int[] volumeGroupUsages = new int[]{USAGE_MEDIA, USAGE_GAME}; when(mServiceMock.getUsagesForVolumeGroupId(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID)) .thenReturn(volumeGroupUsages); expectWithMessage("Group %s usages in primary zone", TEST_VOLUME_GROUP_ID) .that(mCarAudioManager.getUsagesForVolumeGroupId(TEST_VOLUME_GROUP_ID)) .isEqualTo(volumeGroupUsages); } @Test public void getUsagesForVolumeGroupId_withZoneId() throws Exception { int[] volumeGroupUsages = new int[]{USAGE_MEDIA, USAGE_GAME}; when(mServiceMock.getUsagesForVolumeGroupId(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .thenReturn(volumeGroupUsages); expectWithMessage("Group %s usages in rear right zone", TEST_VOLUME_GROUP_ID) .that(mCarAudioManager.getUsagesForVolumeGroupId(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(volumeGroupUsages); } @Test public void getUsagesForVolumeGroupId_withServiceRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getUsagesForVolumeGroupId( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Group %s usages in rear right zone when service throws remote exception", TEST_VOLUME_GROUP_ID).that(mCarAudioManager.getUsagesForVolumeGroupId( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).asList().isEmpty(); } @Test public void getVolumeGroupInfo() throws Exception { when(mServiceMock.getVolumeGroupInfo(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .thenReturn(mCarVolumeGroupInfoMock1); expectWithMessage("Volume group info with id %s in rear right zone") .that(mCarAudioManager.getVolumeGroupInfo(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isEqualTo(mCarVolumeGroupInfoMock1); } @Test public void getVolumeGroupInfo_withServiceRemoteException_returnsNull() throws Exception { doThrow(mRemoteException).when(mServiceMock).getVolumeGroupInfo( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Volume group info with id %s in rear right zone when service throws " + "remote exception").that(mCarAudioManager.getVolumeGroupInfo( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isNull(); } @Test public void getVolumeGroupInfosForZone() throws Exception { when(mServiceMock.getVolumeGroupInfosForZone(TEST_REAR_RIGHT_ZONE_ID)) .thenReturn(List.of(mCarVolumeGroupInfoMock1, mCarVolumeGroupInfoMock2)); expectWithMessage("Volume group infos in rear right zone") .that(mCarAudioManager.getVolumeGroupInfosForZone(TEST_REAR_RIGHT_ZONE_ID)) .containsExactly(mCarVolumeGroupInfoMock1, mCarVolumeGroupInfoMock2); } @Test public void getVolumeGroupInfosForZone_withServiceRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getVolumeGroupInfosForZone( TEST_REAR_RIGHT_ZONE_ID); expectWithMessage("Volume group infos in rear right zone when service throws remote " + "exception").that(mCarAudioManager.getVolumeGroupInfosForZone( TEST_REAR_RIGHT_ZONE_ID)).isEmpty(); } @Test public void getAudioAttributesForVolumeGroup() throws Exception { List attributesList = List.of(mAudioAttributesMock1, mAudioAttributesMock2); when(mServiceMock.getAudioAttributesForVolumeGroup(mCarVolumeGroupInfoMock1)) .thenReturn(attributesList); expectWithMessage("Audio attributes for volume group").that(mCarAudioManager .getAudioAttributesForVolumeGroup(mCarVolumeGroupInfoMock1)) .isEqualTo(attributesList); } @Test public void getAudioAttributesForVolumeGroup_withServiceRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getAudioAttributesForVolumeGroup( mCarVolumeGroupInfoMock1); expectWithMessage("Audio attributes for volume group when service throws remote exception") .that(mCarAudioManager.getAudioAttributesForVolumeGroup(mCarVolumeGroupInfoMock1)) .isEmpty(); } @Test public void isPlaybackOnVolumeGroupActive() throws Exception { when(mServiceMock.isPlaybackOnVolumeGroupActive(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).thenReturn(true); expectWithMessage("Enabled playback on volume group %s in rear right zone", TEST_VOLUME_GROUP_ID).that(mCarAudioManager.isPlaybackOnVolumeGroupActive( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isTrue(); } @Test public void isPlaybackOnVolumeGroupActive_withServiceRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).isPlaybackOnVolumeGroupActive( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Enabled playback on volume group %s in rear right zone when service " + "throws remote exception", TEST_VOLUME_GROUP_ID).that(mCarAudioManager .isPlaybackOnVolumeGroupActive(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .isFalse(); } @Test public void getAudioZoneIds() throws Exception { int[] expectedZoneIds = new int[]{PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID}; when(mServiceMock.getAudioZoneIds()).thenReturn(expectedZoneIds); expectWithMessage("Zone ids").that(mCarAudioManager.getAudioZoneIds()) .containsExactly(PRIMARY_AUDIO_ZONE, TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID, TEST_FRONT_ZONE_ID).inOrder(); } @Test public void getAudioZoneIds_withServiceRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getAudioZoneIds(); expectWithMessage("Zone ids when service throws remote exception") .that(mCarAudioManager.getAudioZoneIds()).isEmpty(); } @Test public void getZoneIdForUid() throws Exception { when(mServiceMock.getZoneIdForUid(TEST_UID)).thenReturn(TEST_REAR_LEFT_ZONE_ID); expectWithMessage("Zone id for uid %s", TEST_UID).that(mCarAudioManager .getZoneIdForUid(TEST_UID)).isEqualTo(TEST_REAR_LEFT_ZONE_ID); } @Test public void getZoneIdForUid_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getZoneIdForUid(TEST_UID); expectWithMessage("Zone id for uid %s when service throws remote exception", TEST_UID) .that(mCarAudioManager.getZoneIdForUid(TEST_UID)).isEqualTo(0); } @Test public void setZoneIdForUid() throws Exception { when(mServiceMock.setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID, TEST_UID)).thenReturn(true); expectWithMessage("Status for setting uid %s to rear right zone", TEST_UID) .that(mCarAudioManager.setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID, TEST_UID)).isTrue(); } @Test public void setZoneIdForUid_withServiceRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID, TEST_UID); expectWithMessage("Status for setting uid %s to rear right zone when service throws " + "remote exception").that(mCarAudioManager.setZoneIdForUid(TEST_REAR_RIGHT_ZONE_ID, TEST_UID)).isFalse(); } @Test public void clearZoneIdForUid() throws Exception { when(mServiceMock.clearZoneIdForUid(TEST_UID)).thenReturn(true); expectWithMessage("Status for clearing zone id for uid %s", TEST_UID) .that(mCarAudioManager.clearZoneIdForUid(TEST_UID)).isTrue(); } @Test public void clearZoneIdForUid_withServiceRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).clearZoneIdForUid(TEST_UID); expectWithMessage("Status for clearing zone id for uid when service throws remote " + "exception").that(mCarAudioManager.clearZoneIdForUid(TEST_UID)).isFalse(); } @Test public void registerCarVolumeGroupEventCallback() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); expectWithMessage("Status for registering car volume group event callback") .that(mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1)).isTrue(); } @Test public void registerCarVolumeGroupEventCallback_withMultipleCallbacks() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1); verify(mServiceMock).registerCarVolumeEventCallback(any()); clearInvocations(mServiceMock); expectWithMessage("Status for registering second car volume group event callback") .that(mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock2)).isTrue(); verify(mServiceMock, never()).registerCarVolumeEventCallback(any()); } @Test public void registerCarVolumeGroupEventCallback_withServiceReturnsFalse_returnsFalse() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(false); expectWithMessage("Failure for registering car volume group event callback") .that(mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1)).isFalse(); } @Test public void registerCarVolumeGroupEventCallback_withSameCallbackforMultipleTimes_returnsFalse() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1); expectWithMessage("Failure for registering car volume group event callback " + "for multiple times").that(mCarAudioManager.registerCarVolumeGroupEventCallback( DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1)).isFalse(); } @Test public void registerCarVolumeGroupEventCallback_withNullExecutor_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.registerCarVolumeGroupEventCallback(/* executor= */ null, mVolumeGroupEventCallbackMock1)); expectWithMessage("Exception for registering car volume group event callback with " + "null executor").that(thrown).hasMessageThat() .contains("Executor can not be null"); } @Test public void registerCarVolumeGroupEventCallback_withNullCallback_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, /* callback= */ null)); expectWithMessage("Exception for registering null car volume group event callback") .that(thrown).hasMessageThat() .contains("Car volume event callback can not be null"); } @Test public void registerCarVolumeGroupEventCallback_withServiceRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).registerCarVolumeEventCallback(any()); expectWithMessage("Failure for registering car volume group event callback when service " + "throws remote exception").that(mCarAudioManager .registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock1)).isFalse(); } @Test public void unregisterCarVolumeGroupEventCallback_withPartOfCallbacks() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl( mVolumeGroupEventCallbackMock1); mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock2); mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock1); verify(mServiceMock, never()).unregisterCarVolumeEventCallback(serviceCallback); } @Test public void unregisterCarVolumeGroupEventCallback_withAllCallbacks() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl( mVolumeGroupEventCallbackMock1); mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock2); mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock1); verify(mServiceMock, never()).unregisterCarVolumeEventCallback(serviceCallback); mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock2); verify(mServiceMock).unregisterCarVolumeEventCallback(serviceCallback); } @Test public void unregisterCarVolumeGroupEventCallback_withNullCallback_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.unregisterCarVolumeGroupEventCallback(/* callback= */ null)); expectWithMessage("Exception for unregistering null car volume group event callback") .that(thrown).hasMessageThat() .contains("Car volume event callback can not be null"); } @Test public void unregisterCarVolumeGroupEventCallback_withServiceRemoteException() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl( mVolumeGroupEventCallbackMock1); doThrow(mRemoteException).when(mServiceMock).unregisterCarVolumeEventCallback( serviceCallback); mCarAudioManager.unregisterCarVolumeGroupEventCallback(mVolumeGroupEventCallbackMock1); } @Test public void onVolumeGroupEvent_forICarVolumeEventCallback() throws Exception { List groupEvents = List.of(new CarVolumeGroupEvent.Builder( List.of(mCarVolumeGroupInfoMock1), CarVolumeGroupEvent.EVENT_TYPE_MUTE_CHANGED) .build(), new CarVolumeGroupEvent.Builder( List.of(mCarVolumeGroupInfoMock2), CarVolumeGroupEvent.EVENT_TYPE_VOLUME_BLOCKED_CHANGED).build()); when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl( mVolumeGroupEventCallbackMock1); mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, mVolumeGroupEventCallbackMock2); serviceCallback.onVolumeGroupEvent(groupEvents); verify(mVolumeGroupEventCallbackMock1, timeout(TEST_TIME_OUT_MS)).onVolumeGroupEvent( groupEvents); verify(mVolumeGroupEventCallbackMock2, timeout(TEST_TIME_OUT_MS)).onVolumeGroupEvent( groupEvents); } @Test public void onMasterMuteChanged_forICarVolumeEventCallback() throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1); mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2); ICarVolumeEventCallback serviceCallback = getCarVolumeEventCallbackImpl( mVolumeGroupEventCallbackMock1); serviceCallback.onMasterMuteChanged(TEST_REAR_RIGHT_ZONE_ID, TEST_FLAGS); verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onMasterMuteChanged( TEST_REAR_RIGHT_ZONE_ID, TEST_FLAGS); verify(mVolumeCallbackMock2, timeout(TEST_TIME_OUT_MS)).onMasterMuteChanged( TEST_REAR_RIGHT_ZONE_ID, TEST_FLAGS); } @Test public void isVolumeGroupMuted() throws Exception { when(mServiceMock.isVolumeGroupMuted(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)) .thenReturn(true); expectWithMessage("Muted volume group").that(mCarAudioManager.isVolumeGroupMuted( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isTrue(); } @Test public void isVolumeGroupMuted_withServiceRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).isVolumeGroupMuted( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID); expectWithMessage("Muted volume group when service throws remote exception") .that(mCarAudioManager.isVolumeGroupMuted(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID)).isFalse(); } @Test public void setVolumeGroupMute() throws Exception { mCarAudioManager.setVolumeGroupMute(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, /* mute= */ true, TEST_FLAGS); verify(mServiceMock).setVolumeGroupMute(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, /* mute= */ true, TEST_FLAGS); } @Test public void setVolumeGroupMute_withServiceRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).setVolumeGroupMute( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, /* mute= */ true, TEST_FLAGS); mCarAudioManager.setVolumeGroupMute(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, /* mute= */ true, TEST_FLAGS); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void registerCarVolumeCallback() throws Exception { mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1); verify(mServiceMock).registerVolumeCallback(any(IBinder.class)); } @Test public void registerCarVolumeCallback_withMultipleCallbacks() throws Exception { mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1); verify(mServiceMock).registerVolumeCallback(any(IBinder.class)); clearInvocations(mServiceMock); mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2); verify(mServiceMock, never()).registerVolumeCallback(any(IBinder.class)); } @Test public void unregisterCarVolumeCallback_withPartOfCallbacks() throws Exception { mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1); mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2); mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock1); verify(mServiceMock, never()).unregisterVolumeCallback(any(IBinder.class)); } @Test public void unregisterCarVolumeCallback_withAllCallbacks() throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(IBinder.class); mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1); verify(mServiceMock).registerVolumeCallback(captor.capture()); mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock2); mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock1); mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock2); verify(mServiceMock).unregisterVolumeCallback(captor.getValue()); } @Test public void unregisterCarVolumeCallback_withServiceRemoteException() throws Exception { mCarAudioManager.registerCarVolumeCallback(mVolumeCallbackMock1); verify(mServiceMock).registerVolumeCallback(any()); doThrow(mRemoteException).when(mServiceMock).unregisterVolumeCallback(any()); mCarAudioManager.unregisterCarVolumeCallback(mVolumeCallbackMock1); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void onCarDisconnected() throws Exception { ICarVolumeCallback serviceVolCallback = getCarVolumeCallbackImpl(mVolumeCallbackMock1); ICarVolumeEventCallback serviceVolEventCallback = getCarVolumeEventCallbackImpl( mVolumeGroupEventCallbackMock1); mCarAudioManager.onCarDisconnected(); verify(mServiceMock).unregisterVolumeCallback(serviceVolCallback.asBinder()); verify(mServiceMock).unregisterCarVolumeEventCallback(serviceVolEventCallback); } @Test public void onGroupMuteChanged_forCarVolumeCallback() throws Exception { ICarVolumeCallback callbackImpl = getCarVolumeCallbackImpl(mVolumeCallbackMock1); callbackImpl.onGroupMuteChanged(TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_FLAGS); verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onGroupMuteChanged( TEST_REAR_RIGHT_ZONE_ID, TEST_VOLUME_GROUP_ID, TEST_FLAGS); } @Test public void onGroupVolumeChanged_forCarVolumeCallback() throws Exception { ICarVolumeCallback callbackImpl = getCarVolumeCallbackImpl(mVolumeCallbackMock1); callbackImpl.onGroupVolumeChanged(PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID, TEST_FLAGS); verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onGroupVolumeChanged( PRIMARY_AUDIO_ZONE, TEST_VOLUME_GROUP_ID, TEST_FLAGS); } @Test public void onMasterMuteChanged_forCarVolumeCallback() throws Exception { ICarVolumeCallback callbackImpl = getCarVolumeCallbackImpl(mVolumeCallbackMock1); callbackImpl.onMasterMuteChanged(TEST_REAR_LEFT_ZONE_ID, TEST_FLAGS); verify(mVolumeCallbackMock1, timeout(TEST_TIME_OUT_MS)).onMasterMuteChanged( TEST_REAR_LEFT_ZONE_ID, TEST_FLAGS); } @Test public void getAudioZoneConfigInfos() throws Exception { List zoneConfigInfos = List.of(mZoneConfigInfoMock1, mZoneConfigInfoMock2); when(mServiceMock.getAudioZoneConfigInfos(TEST_REAR_RIGHT_ZONE_ID)) .thenReturn(zoneConfigInfos); expectWithMessage("All configuration infos for rear right zone") .that(mCarAudioManager.getAudioZoneConfigInfos(TEST_REAR_RIGHT_ZONE_ID)) .isEqualTo(zoneConfigInfos); } @Test public void getAudioZoneConfigInfos_withServiceRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getAudioZoneConfigInfos( TEST_REAR_RIGHT_ZONE_ID); expectWithMessage("All configuration infos for rear right zone when service throws" + " remote exception").that(mCarAudioManager.getAudioZoneConfigInfos( TEST_REAR_RIGHT_ZONE_ID)).isEmpty(); } @Test public void getCurrentAudioZoneConfigInfo() throws Exception { when(mServiceMock.getCurrentAudioZoneConfigInfo(TEST_REAR_RIGHT_ZONE_ID)) .thenReturn(mZoneConfigInfoMock1); expectWithMessage("Current configuration info for rear right zone") .that(mCarAudioManager.getCurrentAudioZoneConfigInfo(TEST_REAR_RIGHT_ZONE_ID)) .isEqualTo(mZoneConfigInfoMock1); } @Test public void getCurrentAudioZoneConfigInfo_whenServiceThrowsRemoteException_returnsNull() throws Exception { doThrow(mRemoteException).when(mServiceMock).getCurrentAudioZoneConfigInfo( TEST_REAR_RIGHT_ZONE_ID); expectWithMessage("Current configuration info for rear right zone when service throws " + "remote exception").that(mCarAudioManager.getCurrentAudioZoneConfigInfo( TEST_REAR_RIGHT_ZONE_ID)).isNull(); } @Test public void switchAudioZoneToConfig() throws Exception { doAnswer(invocation -> { CarAudioZoneConfigInfo configInfoToSwitch = invocation.getArgument(0); ISwitchAudioZoneConfigCallback serviceCallback = invocation.getArgument(1); serviceCallback.onAudioZoneConfigSwitched(configInfoToSwitch, /* isSuccessful= */ true); return null; }).when(mServiceMock).switchZoneToConfig(any(CarAudioZoneConfigInfo.class), any(ISwitchAudioZoneConfigCallback.class)); mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, DIRECT_EXECUTOR, mSwitchCallbackMock); verify(mSwitchCallbackMock).onAudioZoneConfigSwitched(mZoneConfigInfoMock1, /* isSuccessful= */ true); } @Test public void switchAudioZoneToConfig_withNullConfig_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.switchAudioZoneToConfig(/* zoneConfig= */ null, DIRECT_EXECUTOR, mSwitchCallbackMock)); expectWithMessage("Exception for switching zone configuration with null executor") .that(thrown).hasMessageThat() .contains("Audio zone configuration can not be null"); } @Test public void switchAudioZoneToConfig_withNullExecutor_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, /* executor= */ null, mSwitchCallbackMock)); expectWithMessage("Exception for switching zone configuration with null executor") .that(thrown).hasMessageThat() .contains("Executor can not be null"); } @Test public void switchAudioZoneToConfig_withNullCallback_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, DIRECT_EXECUTOR, /* callback= */ null)); expectWithMessage("Exception for switching zone configuration with null callback") .that(thrown).hasMessageThat() .contains("Switching audio zone configuration result callback can not be null"); } @Test public void switchAudioZoneToConfig_whenServiceThrowsRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).switchZoneToConfig(any( CarAudioZoneConfigInfo.class), any(ISwitchAudioZoneConfigCallback.class)); mCarAudioManager.switchAudioZoneToConfig(mZoneConfigInfoMock1, DIRECT_EXECUTOR, mSwitchCallbackMock); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void setAudioZoneConfigsChangeCallback_withNullExecutor_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.setAudioZoneConfigsChangeCallback(/* executor= */ null, mTestConfigCallback)); expectWithMessage("Null audio zone config callback executor exception").that(thrown) .hasMessageThat().contains("Executor"); } @Test public void setAudioZoneConfigsChangeCallback_withNullCallback_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, /* callback= */ null)); expectWithMessage("Null audio zone config callback exception").that(thrown) .hasMessageThat().contains("Audio zone configs change callback"); } @Test public void setAudioZoneConfigsChangeCallback() throws Exception { boolean registered = mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback); expectWithMessage("Audio zone configs callback registered status").that(registered) .isTrue(); verify(mServiceMock).registerAudioZoneConfigsChangeCallback(any()); } @Test public void setAudioZoneConfigsChangeCallback_multipleTimes_fails() { mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback); IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback)); expectWithMessage("Audio zone configs callback re-registered exception").that(thrown) .hasMessageThat().contains("already set"); } @Test public void onAudioZoneConfigurationsChanged_onRegisteredCallback() throws Exception { mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback); ArgumentCaptor captor = ArgumentCaptor.forClass(IAudioZoneConfigurationsChangeCallback.class); verify(mServiceMock).registerAudioZoneConfigsChangeCallback(captor.capture()); IAudioZoneConfigurationsChangeCallback remoteCallback = captor.getValue(); remoteCallback.onAudioZoneConfigurationsChanged( List.of(mZoneConfigInfoMock1, mZoneConfigInfoMock2), CONFIG_STATUS_CHANGED); mTestConfigCallback.waitForCallback(); expectWithMessage("Triggered callback configs").that(mTestConfigCallback.mInfos) .containsExactly(mZoneConfigInfoMock1, mZoneConfigInfoMock2); expectWithMessage("Triggered callback status").that(mTestConfigCallback.mStatus) .isEqualTo(CONFIG_STATUS_CHANGED); } @Test public void clearAudioZoneConfigsCallback() throws Exception { mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback); mCarAudioManager.clearAudioZoneConfigsCallback(); verify(mServiceMock).unregisterAudioZoneConfigsChangeCallback(any()); } @Test public void clearAudioZoneConfigsCallback_withNoRegisteredCallback() throws Exception { mCarAudioManager.clearAudioZoneConfigsCallback(); verify(mServiceMock, never()).unregisterAudioZoneConfigsChangeCallback(any()); } @Test public void setAudioZoneConfigsChangeCallback_withServiceException_fails() throws Exception { RemoteException remoteException = new RemoteException("Register failed!"); when(mServiceMock.registerAudioZoneConfigsChangeCallback(any())).thenThrow(remoteException); boolean registered = mCarAudioManager.setAudioZoneConfigsChangeCallback(DIRECT_EXECUTOR, mTestConfigCallback); expectWithMessage("Audio zone configs callback registered status after service exception") .that(registered).isFalse(); } @Test public void setPrimaryZoneMediaAudioRequestCallback() throws Exception { when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true); expectWithMessage("Status for setting primary zone media audio request callback") .that(mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mPrimaryZoneMediaAudioRequestCallbackMock)).isTrue(); } @Test public void setPrimaryZoneMediaAudioRequestCallback_fails() throws Exception { when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(false); mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mPrimaryZoneMediaAudioRequestCallbackMock); expectWithMessage("Failure for setting primary zone media audio request callback") .that(mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mPrimaryZoneMediaAudioRequestCallbackMock)).isFalse(); } @Test public void setPrimaryZoneMediaAudioRequestCallback_withNullExecutor_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(/* executor= */ null, mPrimaryZoneMediaAudioRequestCallbackMock)); expectWithMessage("Exception for setting primary zone media audio request callback with" + "null executor").that(thrown).hasMessageThat() .contains("Executor can not be null"); } @Test public void setPrimaryZoneMediaAudioRequestCallback_withNullCallback_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, /* callback= */ null)); expectWithMessage("Exception for setting null primary zone media audio request callback") .that(thrown).hasMessageThat() .contains("Audio media request callback can not be null"); } @Test public void setPrimaryZoneMediaAudioRequestCallback_forMultipleTimes_fails() throws Exception { when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true); mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mPrimaryZoneMediaAudioRequestCallbackMock); IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mock(PrimaryZoneMediaAudioRequestCallback.class))); expectWithMessage("Exception for setting primary zone media audio request callback" + " for multiple times").that(thrown).hasMessageThat() .contains("Primary zone media audio request is already set"); } @Test public void setPrimaryZoneMediaAudioRequestCallback_afterClearingPreviousCallbacks() throws Exception { when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true); mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mPrimaryZoneMediaAudioRequestCallbackMock); mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback(); expectWithMessage("Status for setting primary zone media audio request callback" + " again after clearing previous callbacks").that(mCarAudioManager .setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mock(PrimaryZoneMediaAudioRequestCallback.class))).isTrue(); } @Test public void setPrimaryZoneMediaAudioRequestCallback_whenServiceThrowsRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock) .registerPrimaryZoneMediaAudioRequestCallback(any()); expectWithMessage("Status for setting primary zone media audio request callback " + "when service throws remote exception").that(mCarAudioManager .setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, mPrimaryZoneMediaAudioRequestCallbackMock)).isFalse(); } @Test public void clearPrimaryZoneMediaAudioRequestCallback() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback(); verify(mServiceMock).unregisterPrimaryZoneMediaAudioRequestCallback(serviceCallback); } @Test public void clearPrimaryZoneMediaAudioRequestCallback_beforeRegisteringCallback() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback(); verify(mServiceMock).unregisterPrimaryZoneMediaAudioRequestCallback(serviceCallback); clearInvocations(mServiceMock); mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback(); verify(mServiceMock, never()).unregisterPrimaryZoneMediaAudioRequestCallback( serviceCallback); } @Test public void clearPrimaryZoneMediaAudioRequestCallback_whenServiceThrowsRemoteException() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); doThrow(mRemoteException).when(mServiceMock) .unregisterPrimaryZoneMediaAudioRequestCallback(serviceCallback); mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback(); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void onRequestMediaOnPrimaryZone_forIPrimaryZoneMediaAudioRequestCallback() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); serviceCallback.onRequestMediaOnPrimaryZone(mOccupantZoneInfoMock, TEST_REQUEST_ID); verify(mPrimaryZoneMediaAudioRequestCallbackMock).onRequestMediaOnPrimaryZone( mOccupantZoneInfoMock, TEST_REQUEST_ID); } @Test public void onRequestMediaOnPrimaryZone_afterClearPrimaryZoneMediaAudioRequestCallback() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); mCarAudioManager.clearPrimaryZoneMediaAudioRequestCallback(); serviceCallback.onRequestMediaOnPrimaryZone(mOccupantZoneInfoMock, TEST_REQUEST_ID); verify(mPrimaryZoneMediaAudioRequestCallbackMock, never()).onRequestMediaOnPrimaryZone( mOccupantZoneInfoMock, TEST_REQUEST_ID); } @Test public void onMediaAudioRequestStatusChanged_forIPrimaryZoneMediaAudioRequestCallback() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); serviceCallback.onMediaAudioRequestStatusChanged(mOccupantZoneInfoMock, TEST_REQUEST_ID, CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED); verify(mPrimaryZoneMediaAudioRequestCallbackMock).onMediaAudioRequestStatusChanged( mOccupantZoneInfoMock, TEST_REQUEST_ID, CarAudioManager.AUDIO_REQUEST_STATUS_STOPPED); } @Test public void requestMediaAudioOnPrimaryZone() throws Exception { int expectedRequestStatus = CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED; doAnswer(invocation -> { IMediaAudioRequestStatusCallback callback = invocation.getArgument(0); OccupantZoneInfo info = invocation.getArgument(1); callback.onMediaAudioRequestStatusChanged(info, TEST_REQUEST_ID, expectedRequestStatus); return TEST_REQUEST_ID; }).when(mServiceMock).requestMediaAudioOnPrimaryZone(any( IMediaAudioRequestStatusCallback.class), any(OccupantZoneInfo.class)); long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock, DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock); expectWithMessage("Request id for media audio on primary zone").that(requestId) .isEqualTo(TEST_REQUEST_ID); verify(mMediaAudioRequestStatusCallbackMock).onMediaAudioRequestStatusChanged( mOccupantZoneInfoMock, TEST_REQUEST_ID, expectedRequestStatus); } @Test public void requestMediaAudioOnPrimaryZone_withNullOccupantZoneInfo() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.requestMediaAudioOnPrimaryZone(/* info= */ null, DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock)); expectWithMessage("Exception for requesting media audio on primary zone with null info") .that(thrown).hasMessageThat().contains("Occupant zone info can not be null"); } @Test public void requestMediaAudioOnPrimaryZone_withNullExecutor() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock, /* executor= */ null, mMediaAudioRequestStatusCallbackMock)); expectWithMessage("Exception for requesting media audio on primary zone with null executor") .that(thrown).hasMessageThat().contains("Executor can not be null"); } @Test public void requestMediaAudioOnPrimaryZone_withNullCallback() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock, DIRECT_EXECUTOR, /* callback= */ null)); expectWithMessage("Exception for requesting media audio on primary zone with " + "null callback").that(thrown).hasMessageThat() .contains("Media audio request status callback can not be null"); } @Test public void requestMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsInvalidId() throws Exception { doThrow(mRemoteException).when(mServiceMock).requestMediaAudioOnPrimaryZone(any( IMediaAudioRequestStatusCallback.class), any(OccupantZoneInfo.class)); expectWithMessage("Request id for media audio on primary zone when service throws " + "remote exception").that(mCarAudioManager.requestMediaAudioOnPrimaryZone( mOccupantZoneInfoMock, DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock)).isEqualTo( CarAudioManager.INVALID_REQUEST_ID); } @Test public void cancelMediaAudioOnPrimaryZone() throws Exception { when(mServiceMock.requestMediaAudioOnPrimaryZone(any(), any())).thenReturn(TEST_REQUEST_ID); long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock, DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock); when(mServiceMock.cancelMediaAudioOnPrimaryZone(requestId)).thenReturn(true); expectWithMessage("Status for canceling pending media audio on primary zone") .that(mCarAudioManager.cancelMediaAudioOnPrimaryZone(requestId)).isTrue(); } @Test public void cancelMediaAudioOnPrimaryZone_withInvalidId() throws Exception { when(mServiceMock.requestMediaAudioOnPrimaryZone(any(), any())) .thenReturn(CarAudioManager.INVALID_REQUEST_ID); long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock, DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock); expectWithMessage("Status for canceling media audio request with invalid id") .that(mCarAudioManager.cancelMediaAudioOnPrimaryZone(requestId)).isTrue(); verify(mServiceMock, never()).cancelMediaAudioOnPrimaryZone(requestId); } @Test public void cancelMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse() throws Exception { when(mServiceMock.requestMediaAudioOnPrimaryZone(any(), any())).thenReturn(TEST_REQUEST_ID); long requestId = mCarAudioManager.requestMediaAudioOnPrimaryZone(mOccupantZoneInfoMock, DIRECT_EXECUTOR, mMediaAudioRequestStatusCallbackMock); doThrow(mRemoteException).when(mServiceMock).cancelMediaAudioOnPrimaryZone(requestId); expectWithMessage("Status for canceling pending media audio on primary zone " + "throws remote exception").that(mCarAudioManager.cancelMediaAudioOnPrimaryZone( requestId)).isFalse(); } @Test public void allowMediaAudioOnPrimaryZone() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); when(mServiceMock.allowMediaAudioOnPrimaryZone(serviceCallback.asBinder(), TEST_REQUEST_ID, /* allow= */ true)).thenReturn(true); expectWithMessage("Status for allowing media audio on primary zone") .that(mCarAudioManager.allowMediaAudioOnPrimaryZone(TEST_REQUEST_ID, /* allow= */ true)).isTrue(); } @Test public void allowMediaAudioOnPrimaryZone_beforeSettingCallback() { IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> mCarAudioManager.allowMediaAudioOnPrimaryZone(TEST_REQUEST_ID, /* allow= */ true)); expectWithMessage("Exception for allowing media audio on primary zone before " + "setting primary zone media audio request callback").that(thrown) .hasMessageThat().contains("Primary zone media audio request callback"); } @Test public void allowMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse() throws Exception { IPrimaryZoneMediaAudioRequestCallback serviceCallback = getPrimaryZoneMediaAudioRequestCallbackImpl( mPrimaryZoneMediaAudioRequestCallbackMock); doThrow(mRemoteException).when(mServiceMock).allowMediaAudioOnPrimaryZone( serviceCallback.asBinder(), TEST_REQUEST_ID, /* allow= */ true); expectWithMessage("Status for allowing media audio on primary zone when service throws" + "remote exception").that(mCarAudioManager.allowMediaAudioOnPrimaryZone( TEST_REQUEST_ID, /* allow= */ true)).isFalse(); } @Test public void resetMediaAudioOnPrimaryZone() throws Exception { when(mServiceMock.resetMediaAudioOnPrimaryZone(mOccupantZoneInfoMock)).thenReturn(true); expectWithMessage("Status for resetting media audio on primary zone") .that(mCarAudioManager.resetMediaAudioOnPrimaryZone(mOccupantZoneInfoMock)) .isTrue(); } @Test public void resetMediaAudioOnPrimaryZone_whenServiceThrowsRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).resetMediaAudioOnPrimaryZone( mOccupantZoneInfoMock); expectWithMessage("Status for resetting media audio on primary zone when service " + "throws remote exception").that(mCarAudioManager.resetMediaAudioOnPrimaryZone( mOccupantZoneInfoMock)).isFalse(); } @Test public void isMediaAudioAllowedInPrimaryZone() throws Exception { when(mServiceMock.isMediaAudioAllowedInPrimaryZone(mOccupantZoneInfoMock)).thenReturn(true); expectWithMessage("Media audio allowed in primary zone").that(mCarAudioManager .isMediaAudioAllowedInPrimaryZone(mOccupantZoneInfoMock)).isTrue(); } @Test public void isMediaAudioAllowedInPrimaryZone_whenServiceThrowsRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).isMediaAudioAllowedInPrimaryZone( mOccupantZoneInfoMock); expectWithMessage("Media audio allowed in primary zone when service " + "throws remote exception").that(mCarAudioManager.isMediaAudioAllowedInPrimaryZone( mOccupantZoneInfoMock)).isFalse(); } @Test public void setAudioZoneMirrorStatusCallback_succeeds() throws Exception { when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true); expectWithMessage("Status for setting audio zone mirror status callback") .that(mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback)).isTrue(); } @Test public void setAudioZoneMirrorStatusCallback_withServiceReturnsFalse_fails() throws Exception { when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(false); expectWithMessage("Status for setting audio zone mirror status callback") .that(mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback)).isFalse(); } @Test public void setAudioZoneMirrorStatusCallback_forMultipleTimes_fails() throws Exception { when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true); mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback); IllegalStateException thrown = assertThrows(IllegalStateException.class, () -> mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mock(AudioZonesMirrorStatusCallback.class))); expectWithMessage("Exception for setting audio zone mirror status callback multiple times") .that(thrown).hasMessageThat().contains("callback is already set"); } @Test public void setAudioZoneMirrorStatusCallback_withNullExecutor() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.setAudioZoneMirrorStatusCallback(/* executor= */ null, mAudioZonesMirrorStatusCallback)); expectWithMessage("Exception for setting audio zone mirror status callback with" + " null executor").that(thrown).hasMessageThat() .contains("Executor can not be null"); } @Test public void setAudioZoneMirrorStatusCallback_withNullCallback() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, /* callback= */ null)); expectWithMessage("Exception for setting audio zone mirror status callback with " + "null callback").that(thrown).hasMessageThat() .contains("Audio zones mirror status callback can not be null"); } @Test public void setAudioZoneMirrorStatusCallback_whenServiceThrowsRemoteException_returnsFalse() throws Exception { doThrow(mRemoteException).when(mServiceMock).registerAudioZonesMirrorStatusCallback( any()); expectWithMessage("Status for setting audio zone mirror status callback when service " + "throws remote exception").that(mCarAudioManager.setAudioZoneMirrorStatusCallback( DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback)).isFalse(); } @Test public void clearAudioZonesMirrorStatusCallback() throws Exception { when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true); ArgumentCaptor captor = ArgumentCaptor.forClass( IAudioZonesMirrorStatusCallback.class); mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback); verify(mServiceMock).registerAudioZonesMirrorStatusCallback(captor.capture()); mCarAudioManager.clearAudioZonesMirrorStatusCallback(); verify(mServiceMock).unregisterAudioZonesMirrorStatusCallback(captor.getValue()); } @Test public void clearAudioZonesMirrorStatusCallback_withoutCallbackSet() throws Exception { mCarAudioManager.clearAudioZonesMirrorStatusCallback(); verify(mServiceMock, never()).unregisterAudioZonesMirrorStatusCallback(any()); } @Test public void clearAudioZonesMirrorStatusCallback_whenServiceThrowsRemoteException() throws Exception { when(mServiceMock.registerAudioZonesMirrorStatusCallback(any())).thenReturn(true); ArgumentCaptor captor = ArgumentCaptor.forClass( IAudioZonesMirrorStatusCallback.class); mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback); verify(mServiceMock).registerAudioZonesMirrorStatusCallback(captor.capture()); doThrow(mRemoteException).when(mServiceMock).unregisterAudioZonesMirrorStatusCallback( captor.getValue()); mCarAudioManager.clearAudioZonesMirrorStatusCallback(); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void canEnableAudioMirror() throws Exception { when(mServiceMock.canEnableAudioMirror()).thenReturn( CarAudioManager.AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES); expectWithMessage("Audio mirror status").that(mCarAudioManager.canEnableAudioMirror()) .isEqualTo(CarAudioManager.AUDIO_MIRROR_OUT_OF_OUTPUT_DEVICES); } @Test public void canEnableAudioMirror_whenServiceThrowsRemoteException_fails() throws Exception { doThrow(mRemoteException).when(mServiceMock).canEnableAudioMirror(); expectWithMessage("Audio mirror status when service throws remote exception") .that(mCarAudioManager.canEnableAudioMirror()).isEqualTo( CarAudioManager.AUDIO_MIRROR_INTERNAL_ERROR); } @Test public void enableMirrorForAudioZones() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID); IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper(); doAnswer(invocation -> { int[] zones = invocation.getArgument(0); callback.onAudioZonesMirrorStatusChanged(zones, CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED); return TEST_REQUEST_ID; }).when(mServiceMock).enableMirrorForAudioZones(any(int[].class)); long requestId = mCarAudioManager.enableMirrorForAudioZones(zonesToMirrorList); expectWithMessage("Request id for enabling mirror for audio zones").that(requestId) .isEqualTo(TEST_REQUEST_ID); verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList, CarAudioManager.AUDIO_REQUEST_STATUS_APPROVED); } @Test public void enableMirrorForAudioZones_withNullZoneList_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.enableMirrorForAudioZones(/* audioZonesToMirror= */ null)); expectWithMessage("Exception for enabling mirror on null zone list").that(thrown) .hasMessageThat().contains("Audio zones to mirror should not be null"); } @Test public void enableMirrorForAudioZones_whenServiceThrowsRemoteException_returnsInvalidId() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID); getAudioZonesMirrorStatusCallbackWrapper(); doThrow(mRemoteException).when(mServiceMock).enableMirrorForAudioZones(any( int[].class)); expectWithMessage("Request id for enabling mirror for audio zones when service throws " + "remote exception").that(mCarAudioManager.enableMirrorForAudioZones( zonesToMirrorList)).isEqualTo(CarAudioManager.INVALID_REQUEST_ID); } @Test public void extendAudioMirrorRequest() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID); IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper(); doAnswer(invocation -> { int[] zones = invocation.getArgument(1); callback.onAudioZonesMirrorStatusChanged(zones, CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED); return null; }).when(mServiceMock).extendAudioMirrorRequest(eq(TEST_REQUEST_ID), any(int[].class)); mCarAudioManager.extendAudioMirrorRequest(TEST_REQUEST_ID, zonesToMirrorList); verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList, CarAudioManager.AUDIO_REQUEST_STATUS_REJECTED); } @Test public void extendAudioMirrorRequest_withNullZoneList_fails() { NullPointerException thrown = assertThrows(NullPointerException.class, () -> mCarAudioManager.extendAudioMirrorRequest(TEST_REQUEST_ID, /* audioZonesToMirror= */ null)); expectWithMessage("Exception for extending mirror on null zone list").that(thrown) .hasMessageThat().contains("Audio zones to mirror should not be null"); } @Test public void extendAudioMirrorRequest_whenServiceThrowsRemoteException() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID); getAudioZonesMirrorStatusCallbackWrapper(); doThrow(mRemoteException).when(mServiceMock).extendAudioMirrorRequest( eq(TEST_REQUEST_ID), any(int[].class)); mCarAudioManager.extendAudioMirrorRequest(TEST_REQUEST_ID, zonesToMirrorList); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void disableAudioMirror() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID); IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper(); long requestId = mCarAudioManager.enableMirrorForAudioZones(zonesToMirrorList); doAnswer(invocation -> { callback.onAudioZonesMirrorStatusChanged(new int[]{TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID}, CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED); return null; }).when(mServiceMock).disableAudioMirror(requestId); mCarAudioManager.disableAudioMirror(requestId); verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList, CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED); } @Test public void disableAudioMirror_whenServiceThrowsRemoteException() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID); getAudioZonesMirrorStatusCallbackWrapper(); long requestId = mCarAudioManager.enableMirrorForAudioZones(zonesToMirrorList); doThrow(mRemoteException).when(mServiceMock).disableAudioMirror(requestId); mCarAudioManager.disableAudioMirror(requestId); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void disableAudioMirrorForZone() throws Exception { List zonesToMirrorList = List.of(TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID); IAudioZonesMirrorStatusCallback callback = getAudioZonesMirrorStatusCallbackWrapper(); doAnswer(invocation -> { callback.onAudioZonesMirrorStatusChanged(new int[]{TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID}, CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED); return null; }).when(mServiceMock).disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID); mCarAudioManager.disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID); verify(mAudioZonesMirrorStatusCallback).onAudioZonesMirrorStatusChanged(zonesToMirrorList, CarAudioManager.AUDIO_REQUEST_STATUS_CANCELLED); } @Test public void disableAudioMirrorForZone_whenServiceThrowsRemoteException() throws Exception { getAudioZonesMirrorStatusCallbackWrapper(); doThrow(mRemoteException).when(mServiceMock).disableAudioMirrorForZone( TEST_REAR_RIGHT_ZONE_ID); mCarAudioManager.disableAudioMirrorForZone(TEST_REAR_RIGHT_ZONE_ID); verify(mCar).handleRemoteExceptionFromCarService(mRemoteException); } @Test public void getMirrorAudioZonesForAudioZone() throws Exception { when(mServiceMock.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID)).thenReturn( new int[]{TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID}); expectWithMessage("Mirror zones for rear right zone").that(mCarAudioManager .getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID)).containsExactly( TEST_REAR_LEFT_ZONE_ID, TEST_FRONT_ZONE_ID).inOrder(); } @Test public void getMirrorAudioZonesForAudioZone_whenServiceThrowsRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getMirrorAudioZonesForAudioZone( TEST_REAR_RIGHT_ZONE_ID); expectWithMessage("Mirror zones for rear right zone when service throws remote exception") .that(mCarAudioManager.getMirrorAudioZonesForAudioZone(TEST_REAR_RIGHT_ZONE_ID)) .isEmpty(); } @Test public void getMirrorAudioZonesForMirrorRequest() throws Exception { when(mServiceMock.getMirrorAudioZonesForMirrorRequest(TEST_REQUEST_ID)).thenReturn( new int[]{TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID}); expectWithMessage("Mirror zones for request id").that(mCarAudioManager .getMirrorAudioZonesForMirrorRequest(TEST_REQUEST_ID)).containsExactly( TEST_REAR_LEFT_ZONE_ID, TEST_REAR_RIGHT_ZONE_ID).inOrder(); } @Test public void getMirrorAudioZonesForMirrorRequest_whenServiceThrowsRemoteException() throws Exception { doThrow(mRemoteException).when(mServiceMock).getMirrorAudioZonesForMirrorRequest( TEST_REQUEST_ID); expectWithMessage("Mirror zones for request id when service throws remote exception") .that(mCarAudioManager.getMirrorAudioZonesForMirrorRequest(TEST_REQUEST_ID)) .isEmpty(); } @Test public void getOutputDeviceForUsage() throws Exception { when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn( new AudioDeviceInfo[]{TEST_MEDIA_OUTPUT_DEVICE, TEST_NAV_OUTPUT_DEVICE}); when(mServiceMock.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)).thenReturn(NAVIGATION_TEST_DEVICE); AudioDeviceInfo outputDevice = mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE); expectWithMessage("Navigation output device").that(outputDevice) .isEqualTo(TEST_NAV_OUTPUT_DEVICE); } @Test public void getOutputDeviceForUsage_withDeviceAddressNotFound_returnsNull() throws Exception { String invalidDeviceAddress = "invalid_bus"; when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_OUTPUTS)).thenReturn( new AudioDeviceInfo[]{TEST_MEDIA_OUTPUT_DEVICE, TEST_NAV_OUTPUT_DEVICE}); when(mServiceMock.getOutputDeviceAddressForUsage(PRIMARY_AUDIO_ZONE, USAGE_GAME)).thenReturn(invalidDeviceAddress); expectWithMessage("Null output device").that(mCarAudioManager.getOutputDeviceForUsage( PRIMARY_AUDIO_ZONE, USAGE_GAME)).isNull(); } @Test public void getOutputDeviceForUsage_whenServiceThrowsRemoteException_returnsNull() throws Exception { doThrow(mRemoteException).when(mServiceMock).getOutputDeviceAddressForUsage( PRIMARY_AUDIO_ZONE, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE); expectWithMessage("Navigation output device when service throws remote exception") .that(mCarAudioManager.getOutputDeviceForUsage(PRIMARY_AUDIO_ZONE, USAGE_ASSISTANCE_NAVIGATION_GUIDANCE)).isNull(); } @Test public void getInputDevicesForZoneId() throws Exception { AudioDeviceAttributes microphoneAttr = mock(AudioDeviceAttributes.class); when(microphoneAttr.getAddress()).thenReturn(MICROPHONE_ADDRESS); AudioDeviceAttributes tunerAttr = mock(AudioDeviceAttributes.class); when(tunerAttr.getAddress()).thenReturn(FM_TUNER_ADDRESS); List inputDevicesFromService = List.of(microphoneAttr, tunerAttr); when(mServiceMock.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE)).thenReturn( inputDevicesFromService); when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn( new AudioDeviceInfo[]{TEST_MICROPHONE_INPUT_DEVICE, TEST_TUNER_INPUT_DEVICE}); List inputDevices = mCarAudioManager.getInputDevicesForZoneId( PRIMARY_AUDIO_ZONE); expectWithMessage("Input devices for primary zone").that(inputDevices) .containsExactly(TEST_MICROPHONE_INPUT_DEVICE, TEST_TUNER_INPUT_DEVICE); } @Test public void getInputDevicesForZoneId_withInvalidDevicesInAudioManager() throws Exception { AudioDeviceAttributes microphoneAttr = mock(AudioDeviceAttributes.class); when(microphoneAttr.getAddress()).thenReturn(MICROPHONE_ADDRESS); when(mServiceMock.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE)).thenReturn( List.of(microphoneAttr)); when(mAudioManagerMock.getDevices(AudioManager.GET_DEVICES_INPUTS)).thenReturn( new AudioDeviceInfo[]{TEST_MEDIA_OUTPUT_DEVICE, TEST_TUNER_INPUT_DEVICE}); List inputDevices = mCarAudioManager.getInputDevicesForZoneId( PRIMARY_AUDIO_ZONE); expectWithMessage("No input devices found for primary zone").that(inputDevices).isEmpty(); } @Test public void getInputDevicesForZoneId_whenServiceThrowsRemoteException_returnsEmptyList() throws Exception { doThrow(mRemoteException).when(mServiceMock).getInputDevicesForZoneId( PRIMARY_AUDIO_ZONE); expectWithMessage("Input devices for primary zone when service throws remote exception") .that(mCarAudioManager.getInputDevicesForZoneId(PRIMARY_AUDIO_ZONE)).isEmpty(); } private ICarVolumeCallback getCarVolumeCallbackImpl(CarAudioManager.CarVolumeCallback callbackMock) throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass(IBinder.class); mCarAudioManager.registerCarVolumeCallback(callbackMock); verify(mServiceMock).registerVolumeCallback(captor.capture()); return ICarVolumeCallback.Stub.asInterface(captor.getValue()); } private ICarVolumeEventCallback getCarVolumeEventCallbackImpl(CarVolumeGroupEventCallback callbackMock) throws Exception { when(mServiceMock.registerCarVolumeEventCallback(any())).thenReturn(true); mCarAudioManager.registerCarVolumeGroupEventCallback(DIRECT_EXECUTOR, callbackMock); ArgumentCaptor captor = ArgumentCaptor.forClass( ICarVolumeEventCallback.class); verify(mServiceMock).registerCarVolumeEventCallback(captor.capture()); return captor.getValue(); } private IPrimaryZoneMediaAudioRequestCallback getPrimaryZoneMediaAudioRequestCallbackImpl( PrimaryZoneMediaAudioRequestCallback callbackMock) throws Exception { when(mServiceMock.registerPrimaryZoneMediaAudioRequestCallback(any())).thenReturn(true); ArgumentCaptor captor = ArgumentCaptor.forClass( IPrimaryZoneMediaAudioRequestCallback.class); mCarAudioManager.setPrimaryZoneMediaAudioRequestCallback(DIRECT_EXECUTOR, callbackMock); verify(mServiceMock).registerPrimaryZoneMediaAudioRequestCallback(captor.capture()); return captor.getValue(); } private IAudioZonesMirrorStatusCallback getAudioZonesMirrorStatusCallbackWrapper() throws Exception { ArgumentCaptor captor = ArgumentCaptor.forClass( IAudioZonesMirrorStatusCallback.class); when(mServiceMock.registerAudioZonesMirrorStatusCallback( any(IAudioZonesMirrorStatusCallback.class))).thenReturn(true); mCarAudioManager.setAudioZoneMirrorStatusCallback(DIRECT_EXECUTOR, mAudioZonesMirrorStatusCallback); verify(mServiceMock).registerAudioZonesMirrorStatusCallback(captor.capture()); return captor.getValue(); } private static final class TestAudioZoneConfigurationsChangeCallback implements AudioZoneConfigurationsChangeCallback { private static final long TEST_CALLBACK_TIMEOUT_MS = 500; private List mInfos; private int mStatus; private CountDownLatch mStatusLatch = new CountDownLatch(1); @Override public void onAudioZoneConfigurationsChanged(List configs, int status) { mInfos = configs; mStatus = status; mStatusLatch.countDown(); } private void waitForCallback() throws Exception { mStatusLatch.await(TEST_CALLBACK_TIMEOUT_MS, TimeUnit.MILLISECONDS); } } }