| /* |
| * 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( |
| com.android.internal.R.bool.config_hearing_aid_profile_supported)); |
| |
| 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( |
| com.android.internal.R.bool.config_hearing_aid_profile_supported)); |
| |
| 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( |
| com.android.internal.R.bool.config_hearing_aid_profile_supported)); |
| |
| 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( |
| com.android.internal.R.bool.config_hearing_aid_profile_supported)); |
| |
| 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); |
| } |
| } |