blob: 3cec1ea023765711dd146f8afbf41ab9e3731834 [file] [log] [blame]
/*
* Copyright (C) 2017 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.bluetooth.a2dpsink;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.*;
import android.content.Context;
import android.content.Intent;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.media.AudioManager;
import android.os.HandlerThread;
import android.os.Looper;
import androidx.test.InstrumentationRegistry;
import androidx.test.filters.MediumTest;
import androidx.test.rule.ServiceTestRule;
import androidx.test.runner.AndroidJUnit4;
import com.android.bluetooth.R;
import com.android.bluetooth.TestUtils;
import com.android.bluetooth.avrcpcontroller.AvrcpControllerService;
import com.android.bluetooth.avrcpcontroller.BluetoothMediaBrowserService;
import com.android.bluetooth.btservice.AdapterService;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@MediumTest
@RunWith(AndroidJUnit4.class)
public class A2dpSinkStreamHandlerTest {
private static final int DUCK_PERCENT = 75;
private HandlerThread mHandlerThread;
private A2dpSinkStreamHandler mStreamHandler;
private Context mTargetContext;
@Mock private A2dpSinkService mMockA2dpSink;
@Mock private A2dpSinkNativeInterface mMockNativeInterface;
@Mock private AudioManager mMockAudioManager;
@Mock private Resources mMockResources;
@Mock private PackageManager mMockPackageManager;
@Rule
public final ServiceTestRule mServiceRule = new ServiceTestRule();
@Rule
public final ServiceTestRule mBluetoothBrowserMediaServiceTestRule = new ServiceTestRule();
@Mock
private AdapterService mAdapterService;
@Before
public void setUp() throws Exception {
mTargetContext = InstrumentationRegistry.getTargetContext();
MockitoAnnotations.initMocks(this);
// Mock the looper
if (Looper.myLooper() == null) {
Looper.prepare();
}
TestUtils.setAdapterService(mAdapterService);
doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
TestUtils.startService(mServiceRule, AvrcpControllerService.class);
final Intent bluetoothBrowserMediaServiceStartIntent =
TestUtils.prepareIntentToStartBluetoothBrowserMediaService();
mBluetoothBrowserMediaServiceTestRule.startService(bluetoothBrowserMediaServiceStartIntent);
mHandlerThread = new HandlerThread("A2dpSinkStreamHandlerTest");
mHandlerThread.start();
when(mMockA2dpSink.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mMockAudioManager);
when(mMockA2dpSink.getSystemServiceName(AudioManager.class))
.thenReturn(Context.AUDIO_SERVICE);
when(mMockA2dpSink.getResources()).thenReturn(mMockResources);
when(mMockResources.getInteger(anyInt())).thenReturn(DUCK_PERCENT);
when(mMockAudioManager.requestAudioFocus(any())).thenReturn(
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
when(mMockAudioManager.abandonAudioFocus(any())).thenReturn(
AudioManager.AUDIOFOCUS_REQUEST_GRANTED);
when(mMockAudioManager.generateAudioSessionId()).thenReturn(0);
when(mMockA2dpSink.getMainLooper()).thenReturn(mHandlerThread.getLooper());
when(mMockA2dpSink.getPackageManager()).thenReturn(mMockPackageManager);
when(mMockPackageManager.hasSystemFeature(any())).thenReturn(false);
mStreamHandler = spy(new A2dpSinkStreamHandler(mMockA2dpSink, mMockNativeInterface));
}
@After
public void tearDown() throws Exception {
TestUtils.stopService(mServiceRule, AvrcpControllerService.class);
TestUtils.clearAdapterService(mAdapterService);
}
@Test
public void testSrcStart() {
// Stream started without local play, expect no change in streaming.
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_STR_START));
verify(mMockAudioManager, times(0)).requestAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(1);
verify(mMockNativeInterface, times(0)).informAudioTrackGain(1.0f);
assertThat(mStreamHandler.isPlaying()).isFalse();
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testSrcStop() {
// Stream stopped without local play, expect no change in streaming.
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_STR_STOP));
verify(mMockAudioManager, times(0)).requestAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(1);
verify(mMockNativeInterface, times(0)).informAudioTrackGain(1.0f);
assertThat(mStreamHandler.isPlaying()).isFalse();
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testSnkPlay() {
// Play was pressed locally, expect streaming to start soon.
mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SNK_PLAY));
verify(mMockAudioManager, times(1)).requestAudioFocus(any());
assertThat(mStreamHandler.isPlaying()).isFalse();
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testSnkPause() {
// Pause was pressed locally, expect streaming to stop.
mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SNK_PAUSE));
verify(mMockAudioManager, times(0)).requestAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(1);
verify(mMockNativeInterface, times(0)).informAudioTrackGain(1.0f);
assertThat(mStreamHandler.isPlaying()).isFalse();
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testDisconnect() {
// Remote device was disconnected, expect streaming to stop.
testSnkPlay();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.DISCONNECT));
verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(0);
assertThat(mStreamHandler.isPlaying()).isFalse();
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testSrcPlay() {
// Play was pressed remotely, expect no streaming due to lack of audio focus.
mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY));
verify(mMockAudioManager, times(0)).requestAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(1);
verify(mMockNativeInterface, times(0)).informAudioTrackGain(1.0f);
assertThat(mStreamHandler.isPlaying()).isFalse();
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testSrcPlayIot() {
// Play was pressed remotely for an iot device, expect streaming to start.
when(mMockPackageManager.hasSystemFeature(any())).thenReturn(true);
mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY));
verify(mMockAudioManager, times(1)).requestAudioFocus(any());
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
assertThat(mStreamHandler.isPlaying()).isTrue();
}
@Test
public void testSrcPause() {
// Play was pressed locally, expect streaming to start.
mStreamHandler.handleMessage(mStreamHandler.obtainMessage(A2dpSinkStreamHandler.SRC_PLAY));
verify(mMockAudioManager, times(0)).requestAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(1);
verify(mMockNativeInterface, times(0)).informAudioTrackGain(1.0f);
assertThat(mStreamHandler.isPlaying()).isFalse();
}
@Test
public void testFocusGain() {
// Focus was gained, expect streaming to resume.
testSnkPlay();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
AudioManager.AUDIOFOCUS_GAIN));
verify(mMockAudioManager, times(1)).requestAudioFocus(any());
verify(mMockNativeInterface, times(1)).informAudioFocusState(1);
verify(mMockNativeInterface, times(1)).informAudioTrackGain(1.0f);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
assertThat(mStreamHandler.getFocusState()).isEqualTo(AudioManager.AUDIOFOCUS_GAIN);
assertThat(BluetoothMediaBrowserService.isActive()).isTrue();
}
@Test
public void testFocusTransientMayDuck() {
// TransientMayDuck focus was gained, expect audio stream to duck.
testSnkPlay();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK));
verify(mMockNativeInterface, times(1)).informAudioTrackGain(DUCK_PERCENT / 100.0f);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
assertThat(mStreamHandler.getFocusState()).isEqualTo(
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT_CAN_DUCK);
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testFocusLostTransient() {
// Focus was lost transiently, expect streaming to stop.
testSnkPlay();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT));
verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(0);
verify(mMockNativeInterface, times(1)).informAudioTrackGain(0);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
assertThat(mStreamHandler.getFocusState()).isEqualTo(
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
}
@Test
public void testFocusRerequest() {
// Focus was lost transiently, expect streaming to stop.
testSnkPlay();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
AudioManager.AUDIOFOCUS_LOSS_TRANSIENT));
verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
verify(mMockNativeInterface, times(0)).informAudioFocusState(0);
verify(mMockNativeInterface, times(1)).informAudioTrackGain(0);
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.REQUEST_FOCUS, true));
verify(mMockAudioManager, times(2)).requestAudioFocus(any());
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
}
@Test
public void testFocusGainFromTransientLoss() {
// Focus was lost transiently and then regained.
testFocusLostTransient();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
AudioManager.AUDIOFOCUS_GAIN));
verify(mMockAudioManager, times(0)).abandonAudioFocus(any());
verify(mMockNativeInterface, times(1)).informAudioTrackGain(1.0f);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
assertThat(BluetoothMediaBrowserService.isActive()).isTrue();
assertThat(mStreamHandler.getFocusState()).isEqualTo(AudioManager.AUDIOFOCUS_GAIN);
}
@Test
public void testFocusLost() {
// Focus was lost permanently, expect streaming to stop.
testSnkPlay();
mStreamHandler.handleMessage(
mStreamHandler.obtainMessage(A2dpSinkStreamHandler.AUDIO_FOCUS_CHANGE,
AudioManager.AUDIOFOCUS_LOSS));
verify(mMockAudioManager, times(1)).abandonAudioFocus(any());
verify(mMockNativeInterface, times(1)).informAudioFocusState(0);
TestUtils.waitForLooperToFinishScheduledTask(mHandlerThread.getLooper());
assertThat(BluetoothMediaBrowserService.isActive()).isFalse();
assertThat(mStreamHandler.getFocusState()).isEqualTo(AudioManager.AUDIOFOCUS_NONE);
assertThat(mStreamHandler.isPlaying()).isFalse();
}
}