/* * Copyright 2018 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.btservice; import static org.mockito.Mockito.*; import android.bluetooth.BluetoothA2dp; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; import android.bluetooth.BluetoothHearingAid; import android.bluetooth.BluetoothProfile; import android.content.Context; import android.content.Intent; import android.media.AudioManager; import android.support.test.InstrumentationRegistry; import android.support.test.filters.MediumTest; import android.support.test.runner.AndroidJUnit4; import com.android.bluetooth.R; import com.android.bluetooth.TestUtils; import com.android.bluetooth.a2dp.A2dpService; import com.android.bluetooth.hearingaid.HearingAidService; import com.android.bluetooth.hfp.HeadsetService; import org.junit.After; import org.junit.Assert; import org.junit.Assume; import org.junit.Before; import org.junit.Test; import org.junit.runner.RunWith; import org.mockito.Mock; import org.mockito.MockitoAnnotations; @MediumTest @RunWith(AndroidJUnit4.class) public class ActiveDeviceManagerTest { private BluetoothAdapter mAdapter; private Context mContext; private BluetoothDevice mA2dpDevice; private BluetoothDevice mHeadsetDevice; private BluetoothDevice mA2dpHeadsetDevice; private BluetoothDevice mHearingAidDevice; private ActiveDeviceManager mActiveDeviceManager; private static final int TIMEOUT_MS = 1000; @Mock private AdapterService mAdapterService; @Mock private ServiceFactory mServiceFactory; @Mock private A2dpService mA2dpService; @Mock private HeadsetService mHeadsetService; @Mock private HearingAidService mHearingAidService; @Mock private AudioManager mAudioManager; @Before public void setUp() throws Exception { mContext = InstrumentationRegistry.getTargetContext(); Assume.assumeTrue("Ignore test when A2dpService is not enabled", mContext.getResources().getBoolean(R.bool.profile_supported_a2dp)); Assume.assumeTrue("Ignore test when HeadsetService is not enabled", mContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp)); // Set up mocks and test assets MockitoAnnotations.initMocks(this); TestUtils.setAdapterService(mAdapterService); when(mAdapterService.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager); when(mServiceFactory.getA2dpService()).thenReturn(mA2dpService); when(mServiceFactory.getHeadsetService()).thenReturn(mHeadsetService); when(mServiceFactory.getHearingAidService()).thenReturn(mHearingAidService); when(mA2dpService.setActiveDevice(any())).thenReturn(true); when(mHeadsetService.setActiveDevice(any())).thenReturn(true); when(mHearingAidService.setActiveDevice(any())).thenReturn(true); mActiveDeviceManager = new ActiveDeviceManager(mAdapterService, mServiceFactory); mActiveDeviceManager.start(); mAdapter = BluetoothAdapter.getDefaultAdapter(); // Get devices for testing mA2dpDevice = TestUtils.getTestDevice(mAdapter, 0); mHeadsetDevice = TestUtils.getTestDevice(mAdapter, 1); mA2dpHeadsetDevice = TestUtils.getTestDevice(mAdapter, 2); mHearingAidDevice = TestUtils.getTestDevice(mAdapter, 3); } @After public void tearDown() throws Exception { if (!mContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp) || !mContext.getResources().getBoolean(R.bool.profile_supported_a2dp)) { return; } mActiveDeviceManager.cleanup(); TestUtils.clearAdapterService(mAdapterService); } @Test public void testSetUpAndTearDown() {} /** * One A2DP is connected. */ @Test public void onlyA2dpConnected_setA2dpActive() { a2dpConnected(mA2dpDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); } /** * Two A2DP are connected. Should set the second one active. */ @Test public void secondA2dpConnected_setSecondA2dpActive() { a2dpConnected(mA2dpDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpConnected(mA2dpHeadsetDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); } /** * One A2DP is connected and disconnected later. Should then set active device to null. */ @Test public void lastA2dpDisconnected_clearA2dpActive() { a2dpConnected(mA2dpDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpDisconnected(mA2dpDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** * Two A2DP are connected and active device is explicitly set. */ @Test public void a2dpActiveDeviceSelected_setActive() { a2dpConnected(mA2dpDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); a2dpConnected(mA2dpHeadsetDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); a2dpActiveDeviceChanged(mA2dpDevice); // Don't call mA2dpService.setActiveDevice() TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mA2dpService, times(1)).setActiveDevice(mA2dpDevice); Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice()); } /** * One Headset is connected. */ @Test public void onlyHeadsetConnected_setHeadsetActive() { headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); } /** * Two Headset are connected. Should set the second one active. */ @Test public void secondHeadsetConnected_setSecondHeadsetActive() { headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); headsetConnected(mA2dpHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); } /** * One Headset is connected and disconnected later. Should then set active device to null. */ @Test public void lastHeadsetDisconnected_clearHeadsetActive() { headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); headsetDisconnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** * Two Headset are connected and active device is explicitly set. */ @Test public void headsetActiveDeviceSelected_setActive() { headsetConnected(mHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); headsetConnected(mA2dpHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); headsetActiveDeviceChanged(mHeadsetDevice); // Don't call mHeadsetService.setActiveDevice() TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mHeadsetService, times(1)).setActiveDevice(mHeadsetDevice); Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); } /** * A combo (A2DP + Headset) device is connected. Then a Hearing Aid is connected. */ @Test public void hearingAidActive_clearA2dpAndHeadsetActive() { Assume.assumeTrue("Ignore test when HearingAidService is not enabled", mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); a2dpConnected(mA2dpHeadsetDevice); headsetConnected(mA2dpHeadsetDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); hearingAidActiveDeviceChanged(mHearingAidDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** * A Hearing Aid is connected. Then a combo (A2DP + Headset) device is connected. */ @Test public void hearingAidActive_dontSetA2dpAndHeadsetActive() { Assume.assumeTrue("Ignore test when HearingAidService is not enabled", mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); hearingAidActiveDeviceChanged(mHearingAidDevice); a2dpConnected(mA2dpHeadsetDevice); headsetConnected(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice); verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice); } /** * A Hearing Aid is connected. Then an A2DP active device is explicitly set. */ @Test public void hearingAidActive_setA2dpActiveExplicitly() { Assume.assumeTrue("Ignore test when HearingAidService is not enabled", mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); hearingAidActiveDeviceChanged(mHearingAidDevice); a2dpConnected(mA2dpHeadsetDevice); a2dpActiveDeviceChanged(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mHearingAidService).setActiveDevice(isNull()); // Don't call mA2dpService.setActiveDevice() verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice); Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getA2dpActiveDevice()); Assert.assertEquals(null, mActiveDeviceManager.getHearingAidActiveDevice()); } /** * A Hearing Aid is connected. Then a Headset active device is explicitly set. */ @Test public void hearingAidActive_setHeadsetActiveExplicitly() { Assume.assumeTrue("Ignore test when HearingAidService is not enabled", mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); hearingAidActiveDeviceChanged(mHearingAidDevice); headsetConnected(mA2dpHeadsetDevice); headsetActiveDeviceChanged(mA2dpHeadsetDevice); TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); verify(mHearingAidService).setActiveDevice(isNull()); // Don't call mHeadsetService.setActiveDevice() verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice); Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); Assert.assertEquals(null, mActiveDeviceManager.getHearingAidActiveDevice()); } /** * A wired audio device is connected. Then all active devices are set to null. */ @Test public void wiredAudioDeviceConnected_setAllActiveDevicesNull() { a2dpConnected(mA2dpDevice); headsetConnected(mHeadsetDevice); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); mActiveDeviceManager.wiredAudioDeviceConnected(); verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); } /** * Helper to indicate A2dp connected for a device. */ private void a2dpConnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate A2dp disconnected for a device. */ private void a2dpDisconnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate A2dp active device changed for a device. */ private void a2dpActiveDeviceChanged(BluetoothDevice device) { Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate Headset connected for a device. */ private void headsetConnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate Headset disconnected for a device. */ private void headsetDisconnected(BluetoothDevice device) { Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate Headset active device changed for a device. */ private void headsetActiveDeviceChanged(BluetoothDevice device) { Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } /** * Helper to indicate Hearing Aid active device changed for a device. */ private void hearingAidActiveDeviceChanged(BluetoothDevice device) { Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); } }