blob: 310c9844df06266aebbd4189ec3f3cc682b3420a [file] [log] [blame]
/*
* Copyright (C) 2020 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 com.android.car.audio.hal;
import static android.media.AudioAttributes.USAGE_ALARM;
import static android.media.AudioAttributes.USAGE_MEDIA;
import static android.media.AudioManager.AUDIOFOCUS_GAIN;
import static android.media.AudioManager.AUDIOFOCUS_LOSS;
import static android.media.AudioManager.AUDIOFOCUS_LOSS_TRANSIENT;
import static android.media.AudioManager.AUDIOFOCUS_REQUEST_FAILED;
import static android.media.AudioManager.AUDIOFOCUS_REQUEST_GRANTED;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoMoreInteractions;
import static org.mockito.Mockito.when;
import static org.testng.Assert.assertThrows;
import android.car.media.CarAudioManager;
import android.media.AudioFocusRequest;
import android.media.AudioManager;
import android.media.AudioManager.OnAudioFocusChangeListener;
import android.os.Bundle;
import androidx.test.ext.junit.runners.AndroidJUnit4;
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.MockitoJUnit;
import org.mockito.junit.MockitoRule;
@RunWith(AndroidJUnit4.class)
public class HalAudioFocusTest {
private static final int[] AUDIO_ZONE_IDS = {0, 1, 2, 3};
private static final int ZONE_ID = 0;
private static final int SECOND_ZONE_ID = 1;
private static final int INVALID_ZONE_ID = 5;
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@Mock
AudioManager mMockAudioManager;
@Mock
AudioControlWrapper mAudioControlWrapper;
private HalAudioFocus mHalAudioFocus;
@Before
public void setUp() {
mHalAudioFocus = new HalAudioFocus(mMockAudioManager, mAudioControlWrapper, AUDIO_ZONE_IDS);
}
@Test
public void registerFocusListener_succeeds() {
mHalAudioFocus.registerFocusListener();
verify(mAudioControlWrapper).registerFocusListener(mHalAudioFocus);
}
@Test
public void requestAudioFocus_notifiesHalOfFocusChange() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mAudioControlWrapper).onAudioFocusChange(USAGE_MEDIA, ZONE_ID,
AUDIOFOCUS_REQUEST_GRANTED);
}
@Test
public void requestAudioFocus_specifiesUsage() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
assertThat(actualRequest.getAudioAttributes().getUsage()).isEqualTo(USAGE_MEDIA);
}
@Test
public void requestAudioFocus_specifiesFocusGain() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
assertThat(actualRequest.getFocusGain()).isEqualTo(AUDIOFOCUS_GAIN);
}
@Test
public void requestAudioFocus_specifiesZoneId() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
Bundle bundle = actualRequest.getAudioAttributes().getBundle();
assertThat(bundle.getInt(CarAudioManager.AUDIOFOCUS_EXTRA_REQUEST_ZONE_ID)).isEqualTo(
ZONE_ID);
}
@Test
public void requestAudioFocus_providesFocusChangeListener() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
assertThat(actualRequest.getOnAudioFocusChangeListener()).isNotNull();
}
@Test
public void requestAudioFocus_withInvalidZone_throws() {
whenAnyFocusRequestGranted();
assertThrows(IllegalArgumentException.class,
() -> mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, INVALID_ZONE_ID,
AUDIOFOCUS_GAIN));
}
@Test
public void requestAudioFocus_withSameZoneAndUsage_keepsExistingRequest() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest firstRequest = getLastRequest();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(firstRequest);
}
@Test
public void requestAudioFocus_withSameZoneAndUsage_notifiesHalOfExistingRequestStatus() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest firstRequest = getLastRequest();
OnAudioFocusChangeListener listener = firstRequest.getOnAudioFocusChangeListener();
listener.onAudioFocusChange(AUDIOFOCUS_LOSS_TRANSIENT);
verify(mAudioControlWrapper).onAudioFocusChange(USAGE_MEDIA, ZONE_ID,
AUDIOFOCUS_LOSS_TRANSIENT);
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mAudioControlWrapper, times(2)).onAudioFocusChange(USAGE_MEDIA, ZONE_ID,
AUDIOFOCUS_LOSS_TRANSIENT);
}
@Test
public void requestAudioFocus_withDifferentZoneAndSameUsage_keepsExistingRequest() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest firstRequest = getLastRequest();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, SECOND_ZONE_ID, AUDIOFOCUS_GAIN);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(firstRequest);
}
@Test
public void requestAudioFocus_withSameZoneAndDifferentUsage_keepsExistingRequest() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest firstRequest = getLastRequest();
mHalAudioFocus.requestAudioFocus(USAGE_ALARM, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(firstRequest);
}
@Test
public void requestAudioFocus_withPreviouslyFailedRequest_doesNothingForOldRequest() {
when(mMockAudioManager.requestAudioFocus(any())).thenReturn(AUDIOFOCUS_REQUEST_FAILED,
AUDIOFOCUS_REQUEST_GRANTED);
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest firstRequest = getLastRequest();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(firstRequest);
}
@Test
public void onAudioFocusChange_notifiesHalOfChange() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mAudioControlWrapper, never()).onAudioFocusChange(USAGE_MEDIA, ZONE_ID,
AUDIOFOCUS_LOSS_TRANSIENT);
AudioFocusRequest actualRequest = getLastRequest();
OnAudioFocusChangeListener listener = actualRequest.getOnAudioFocusChangeListener();
listener.onAudioFocusChange(AUDIOFOCUS_LOSS_TRANSIENT);
verify(mAudioControlWrapper).onAudioFocusChange(USAGE_MEDIA, ZONE_ID,
AUDIOFOCUS_LOSS_TRANSIENT);
}
@Test
public void abandonAudioFocus_withNoCurrentRequest_doesNothing() throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, ZONE_ID);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(any());
}
@Test
public void abandonAudioFocus_withInvalidZone_throws() {
whenAnyFocusRequestGranted();
assertThrows(IllegalArgumentException.class,
() -> mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, INVALID_ZONE_ID));
}
@Test
public void abandonAudioFocus_withCurrentRequest_abandonsExistingFocus() throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, ZONE_ID);
verify(mMockAudioManager).abandonAudioFocusRequest(actualRequest);
}
@Test
public void abandonAudioFocus_withCurrentRequest_notifiesHalOfFocusChange() throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
when(mMockAudioManager.abandonAudioFocusRequest(actualRequest)).thenReturn(
AUDIOFOCUS_REQUEST_GRANTED);
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, ZONE_ID);
verify(mAudioControlWrapper).onAudioFocusChange(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_LOSS);
}
@Test
public void abandonAudioFocus_withFocusAlreadyLost_doesNothing() throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
OnAudioFocusChangeListener listener = actualRequest.getOnAudioFocusChangeListener();
listener.onAudioFocusChange(AUDIOFOCUS_LOSS);
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, ZONE_ID);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(actualRequest);
}
@Test
public void abandonAudioFocus_withFocusTransientlyLost_abandonsExistingFocus()
throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest actualRequest = getLastRequest();
OnAudioFocusChangeListener listener = actualRequest.getOnAudioFocusChangeListener();
listener.onAudioFocusChange(AUDIOFOCUS_LOSS_TRANSIENT);
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, ZONE_ID);
verify(mMockAudioManager).abandonAudioFocusRequest(actualRequest);
}
@Test
public void abandonAudioFocus_withExistingRequestOfDifferentUsage_doesNothing()
throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
mHalAudioFocus.abandonAudioFocus(USAGE_ALARM, ZONE_ID);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(any());
}
@Test
public void abandonAudioFocus_withExistingRequestOfDifferentZoneId_doesNothing()
throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, SECOND_ZONE_ID);
verify(mMockAudioManager, never()).abandonAudioFocusRequest(any());
}
@Test
public void abandonAudioFocus_withFailedRequest_doesNotNotifyHal() throws Exception {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest request = getLastRequest();
when(mMockAudioManager.abandonAudioFocusRequest(request))
.thenReturn(AUDIOFOCUS_REQUEST_FAILED);
mHalAudioFocus.abandonAudioFocus(USAGE_MEDIA, ZONE_ID);
verify(mAudioControlWrapper, never())
.onAudioFocusChange(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_LOSS);
}
@Test
public void reset_abandonsExistingRequests() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest mediaRequest = getLastRequest();
mHalAudioFocus.requestAudioFocus(USAGE_ALARM, ZONE_ID, AUDIOFOCUS_GAIN);
AudioFocusRequest alarmRequest = getLastRequest();
verify(mMockAudioManager, never()).abandonAudioFocusRequest(any());
mHalAudioFocus.reset();
verify(mMockAudioManager).abandonAudioFocusRequest(mediaRequest);
verify(mMockAudioManager).abandonAudioFocusRequest(alarmRequest);
verifyNoMoreInteractions(mMockAudioManager);
}
@Test
public void reset_notifiesHal() {
whenAnyFocusRequestGranted();
mHalAudioFocus.requestAudioFocus(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_GAIN);
mHalAudioFocus.requestAudioFocus(USAGE_ALARM, ZONE_ID, AUDIOFOCUS_GAIN);
verify(mAudioControlWrapper, never()).onAudioFocusChange(anyInt(), eq(ZONE_ID),
eq(AUDIOFOCUS_LOSS));
when(mMockAudioManager.abandonAudioFocusRequest(any())).thenReturn(
AUDIOFOCUS_REQUEST_GRANTED);
mHalAudioFocus.reset();
verify(mAudioControlWrapper).onAudioFocusChange(USAGE_MEDIA, ZONE_ID, AUDIOFOCUS_LOSS);
verify(mAudioControlWrapper).onAudioFocusChange(USAGE_ALARM, ZONE_ID, AUDIOFOCUS_LOSS);
}
private void whenAnyFocusRequestGranted() {
when(mMockAudioManager.requestAudioFocus(any())).thenReturn(AUDIOFOCUS_REQUEST_GRANTED);
}
private AudioFocusRequest getLastRequest() {
ArgumentCaptor<AudioFocusRequest> captor = ArgumentCaptor.forClass(AudioFocusRequest.class);
verify(mMockAudioManager, atLeastOnce()).requestAudioFocus(captor.capture());
return captor.getValue();
}
}