1 /* 2 * Copyright 2018 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.bluetooth.btservice; 18 19 import static org.mockito.Mockito.*; 20 21 import android.bluetooth.BluetoothA2dp; 22 import android.bluetooth.BluetoothAdapter; 23 import android.bluetooth.BluetoothDevice; 24 import android.bluetooth.BluetoothHeadset; 25 import android.bluetooth.BluetoothHearingAid; 26 import android.bluetooth.BluetoothProfile; 27 import android.content.Context; 28 import android.content.Intent; 29 import android.media.AudioManager; 30 import android.support.test.InstrumentationRegistry; 31 import android.support.test.filters.MediumTest; 32 import android.support.test.runner.AndroidJUnit4; 33 34 import com.android.bluetooth.R; 35 import com.android.bluetooth.TestUtils; 36 import com.android.bluetooth.a2dp.A2dpService; 37 import com.android.bluetooth.hearingaid.HearingAidService; 38 import com.android.bluetooth.hfp.HeadsetService; 39 40 import org.junit.After; 41 import org.junit.Assert; 42 import org.junit.Assume; 43 import org.junit.Before; 44 import org.junit.Test; 45 import org.junit.runner.RunWith; 46 import org.mockito.Mock; 47 import org.mockito.MockitoAnnotations; 48 49 @MediumTest 50 @RunWith(AndroidJUnit4.class) 51 public class ActiveDeviceManagerTest { 52 private BluetoothAdapter mAdapter; 53 private Context mContext; 54 private BluetoothDevice mA2dpDevice; 55 private BluetoothDevice mHeadsetDevice; 56 private BluetoothDevice mA2dpHeadsetDevice; 57 private BluetoothDevice mHearingAidDevice; 58 private ActiveDeviceManager mActiveDeviceManager; 59 private static final int TIMEOUT_MS = 1000; 60 61 @Mock private AdapterService mAdapterService; 62 @Mock private ServiceFactory mServiceFactory; 63 @Mock private A2dpService mA2dpService; 64 @Mock private HeadsetService mHeadsetService; 65 @Mock private HearingAidService mHearingAidService; 66 @Mock private AudioManager mAudioManager; 67 68 @Before setUp()69 public void setUp() throws Exception { 70 mContext = InstrumentationRegistry.getTargetContext(); 71 Assume.assumeTrue("Ignore test when A2dpService is not enabled", 72 mContext.getResources().getBoolean(R.bool.profile_supported_a2dp)); 73 Assume.assumeTrue("Ignore test when HeadsetService is not enabled", 74 mContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp)); 75 76 // Set up mocks and test assets 77 MockitoAnnotations.initMocks(this); 78 TestUtils.setAdapterService(mAdapterService); 79 when(mAdapterService.getSystemService(Context.AUDIO_SERVICE)).thenReturn(mAudioManager); 80 when(mServiceFactory.getA2dpService()).thenReturn(mA2dpService); 81 when(mServiceFactory.getHeadsetService()).thenReturn(mHeadsetService); 82 when(mServiceFactory.getHearingAidService()).thenReturn(mHearingAidService); 83 when(mA2dpService.setActiveDevice(any())).thenReturn(true); 84 when(mHeadsetService.setActiveDevice(any())).thenReturn(true); 85 when(mHearingAidService.setActiveDevice(any())).thenReturn(true); 86 87 mActiveDeviceManager = new ActiveDeviceManager(mAdapterService, mServiceFactory); 88 mActiveDeviceManager.start(); 89 mAdapter = BluetoothAdapter.getDefaultAdapter(); 90 91 // Get devices for testing 92 mA2dpDevice = TestUtils.getTestDevice(mAdapter, 0); 93 mHeadsetDevice = TestUtils.getTestDevice(mAdapter, 1); 94 mA2dpHeadsetDevice = TestUtils.getTestDevice(mAdapter, 2); 95 mHearingAidDevice = TestUtils.getTestDevice(mAdapter, 3); 96 } 97 98 @After tearDown()99 public void tearDown() throws Exception { 100 if (!mContext.getResources().getBoolean(R.bool.profile_supported_hs_hfp) 101 || !mContext.getResources().getBoolean(R.bool.profile_supported_a2dp)) { 102 return; 103 } 104 mActiveDeviceManager.cleanup(); 105 TestUtils.clearAdapterService(mAdapterService); 106 } 107 108 @Test testSetUpAndTearDown()109 public void testSetUpAndTearDown() {} 110 111 /** 112 * One A2DP is connected. 113 */ 114 @Test onlyA2dpConnected_setA2dpActive()115 public void onlyA2dpConnected_setA2dpActive() { 116 a2dpConnected(mA2dpDevice); 117 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); 118 } 119 120 /** 121 * Two A2DP are connected. Should set the second one active. 122 */ 123 @Test secondA2dpConnected_setSecondA2dpActive()124 public void secondA2dpConnected_setSecondA2dpActive() { 125 a2dpConnected(mA2dpDevice); 126 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); 127 128 a2dpConnected(mA2dpHeadsetDevice); 129 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); 130 } 131 132 /** 133 * One A2DP is connected and disconnected later. Should then set active device to null. 134 */ 135 @Test lastA2dpDisconnected_clearA2dpActive()136 public void lastA2dpDisconnected_clearA2dpActive() { 137 a2dpConnected(mA2dpDevice); 138 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); 139 140 a2dpDisconnected(mA2dpDevice); 141 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 142 } 143 144 /** 145 * Two A2DP are connected and active device is explicitly set. 146 */ 147 @Test a2dpActiveDeviceSelected_setActive()148 public void a2dpActiveDeviceSelected_setActive() { 149 a2dpConnected(mA2dpDevice); 150 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); 151 152 a2dpConnected(mA2dpHeadsetDevice); 153 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); 154 155 a2dpActiveDeviceChanged(mA2dpDevice); 156 // Don't call mA2dpService.setActiveDevice() 157 TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); 158 verify(mA2dpService, times(1)).setActiveDevice(mA2dpDevice); 159 Assert.assertEquals(mA2dpDevice, mActiveDeviceManager.getA2dpActiveDevice()); 160 } 161 162 /** 163 * One Headset is connected. 164 */ 165 @Test onlyHeadsetConnected_setHeadsetActive()166 public void onlyHeadsetConnected_setHeadsetActive() { 167 headsetConnected(mHeadsetDevice); 168 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); 169 } 170 171 /** 172 * Two Headset are connected. Should set the second one active. 173 */ 174 @Test secondHeadsetConnected_setSecondHeadsetActive()175 public void secondHeadsetConnected_setSecondHeadsetActive() { 176 headsetConnected(mHeadsetDevice); 177 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); 178 179 headsetConnected(mA2dpHeadsetDevice); 180 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); 181 } 182 183 /** 184 * One Headset is connected and disconnected later. Should then set active device to null. 185 */ 186 @Test lastHeadsetDisconnected_clearHeadsetActive()187 public void lastHeadsetDisconnected_clearHeadsetActive() { 188 headsetConnected(mHeadsetDevice); 189 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); 190 191 headsetDisconnected(mHeadsetDevice); 192 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 193 } 194 195 /** 196 * Two Headset are connected and active device is explicitly set. 197 */ 198 @Test headsetActiveDeviceSelected_setActive()199 public void headsetActiveDeviceSelected_setActive() { 200 headsetConnected(mHeadsetDevice); 201 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); 202 203 headsetConnected(mA2dpHeadsetDevice); 204 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); 205 206 headsetActiveDeviceChanged(mHeadsetDevice); 207 // Don't call mHeadsetService.setActiveDevice() 208 TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); 209 verify(mHeadsetService, times(1)).setActiveDevice(mHeadsetDevice); 210 Assert.assertEquals(mHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); 211 } 212 213 /** 214 * A combo (A2DP + Headset) device is connected. Then a Hearing Aid is connected. 215 */ 216 @Test hearingAidActive_clearA2dpAndHeadsetActive()217 public void hearingAidActive_clearA2dpAndHeadsetActive() { 218 Assume.assumeTrue("Ignore test when HearingAidService is not enabled", 219 mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); 220 221 a2dpConnected(mA2dpHeadsetDevice); 222 headsetConnected(mA2dpHeadsetDevice); 223 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); 224 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpHeadsetDevice); 225 226 hearingAidActiveDeviceChanged(mHearingAidDevice); 227 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 228 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 229 } 230 231 /** 232 * A Hearing Aid is connected. Then a combo (A2DP + Headset) device is connected. 233 */ 234 @Test hearingAidActive_dontSetA2dpAndHeadsetActive()235 public void hearingAidActive_dontSetA2dpAndHeadsetActive() { 236 Assume.assumeTrue("Ignore test when HearingAidService is not enabled", 237 mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); 238 239 hearingAidActiveDeviceChanged(mHearingAidDevice); 240 a2dpConnected(mA2dpHeadsetDevice); 241 headsetConnected(mA2dpHeadsetDevice); 242 243 TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); 244 verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice); 245 verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice); 246 } 247 248 /** 249 * A Hearing Aid is connected. Then an A2DP active device is explicitly set. 250 */ 251 @Test hearingAidActive_setA2dpActiveExplicitly()252 public void hearingAidActive_setA2dpActiveExplicitly() { 253 Assume.assumeTrue("Ignore test when HearingAidService is not enabled", 254 mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); 255 256 hearingAidActiveDeviceChanged(mHearingAidDevice); 257 a2dpConnected(mA2dpHeadsetDevice); 258 a2dpActiveDeviceChanged(mA2dpHeadsetDevice); 259 260 TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); 261 verify(mHearingAidService).setActiveDevice(isNull()); 262 // Don't call mA2dpService.setActiveDevice() 263 verify(mA2dpService, never()).setActiveDevice(mA2dpHeadsetDevice); 264 Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getA2dpActiveDevice()); 265 Assert.assertEquals(null, mActiveDeviceManager.getHearingAidActiveDevice()); 266 } 267 268 /** 269 * A Hearing Aid is connected. Then a Headset active device is explicitly set. 270 */ 271 @Test hearingAidActive_setHeadsetActiveExplicitly()272 public void hearingAidActive_setHeadsetActiveExplicitly() { 273 Assume.assumeTrue("Ignore test when HearingAidService is not enabled", 274 mContext.getResources().getBoolean(R.bool.profile_supported_hearing_aid)); 275 276 hearingAidActiveDeviceChanged(mHearingAidDevice); 277 headsetConnected(mA2dpHeadsetDevice); 278 headsetActiveDeviceChanged(mA2dpHeadsetDevice); 279 280 TestUtils.waitForLooperToFinishScheduledTask(mActiveDeviceManager.getHandlerLooper()); 281 verify(mHearingAidService).setActiveDevice(isNull()); 282 // Don't call mHeadsetService.setActiveDevice() 283 verify(mHeadsetService, never()).setActiveDevice(mA2dpHeadsetDevice); 284 Assert.assertEquals(mA2dpHeadsetDevice, mActiveDeviceManager.getHfpActiveDevice()); 285 Assert.assertEquals(null, mActiveDeviceManager.getHearingAidActiveDevice()); 286 } 287 288 /** 289 * A wired audio device is connected. Then all active devices are set to null. 290 */ 291 @Test wiredAudioDeviceConnected_setAllActiveDevicesNull()292 public void wiredAudioDeviceConnected_setAllActiveDevicesNull() { 293 a2dpConnected(mA2dpDevice); 294 headsetConnected(mHeadsetDevice); 295 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(mA2dpDevice); 296 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(mHeadsetDevice); 297 298 mActiveDeviceManager.wiredAudioDeviceConnected(); 299 verify(mA2dpService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 300 verify(mHeadsetService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 301 verify(mHearingAidService, timeout(TIMEOUT_MS)).setActiveDevice(isNull()); 302 } 303 304 /** 305 * Helper to indicate A2dp connected for a device. 306 */ a2dpConnected(BluetoothDevice device)307 private void a2dpConnected(BluetoothDevice device) { 308 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 309 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 310 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 311 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 312 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 313 } 314 315 /** 316 * Helper to indicate A2dp disconnected for a device. 317 */ a2dpDisconnected(BluetoothDevice device)318 private void a2dpDisconnected(BluetoothDevice device) { 319 Intent intent = new Intent(BluetoothA2dp.ACTION_CONNECTION_STATE_CHANGED); 320 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 321 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); 322 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); 323 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 324 } 325 326 /** 327 * Helper to indicate A2dp active device changed for a device. 328 */ a2dpActiveDeviceChanged(BluetoothDevice device)329 private void a2dpActiveDeviceChanged(BluetoothDevice device) { 330 Intent intent = new Intent(BluetoothA2dp.ACTION_ACTIVE_DEVICE_CHANGED); 331 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 332 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 333 } 334 335 /** 336 * Helper to indicate Headset connected for a device. 337 */ headsetConnected(BluetoothDevice device)338 private void headsetConnected(BluetoothDevice device) { 339 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 340 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 341 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_DISCONNECTED); 342 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_CONNECTED); 343 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 344 } 345 346 /** 347 * Helper to indicate Headset disconnected for a device. 348 */ headsetDisconnected(BluetoothDevice device)349 private void headsetDisconnected(BluetoothDevice device) { 350 Intent intent = new Intent(BluetoothHeadset.ACTION_CONNECTION_STATE_CHANGED); 351 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 352 intent.putExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, BluetoothProfile.STATE_CONNECTED); 353 intent.putExtra(BluetoothProfile.EXTRA_STATE, BluetoothProfile.STATE_DISCONNECTED); 354 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 355 } 356 357 /** 358 * Helper to indicate Headset active device changed for a device. 359 */ headsetActiveDeviceChanged(BluetoothDevice device)360 private void headsetActiveDeviceChanged(BluetoothDevice device) { 361 Intent intent = new Intent(BluetoothHeadset.ACTION_ACTIVE_DEVICE_CHANGED); 362 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 363 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 364 } 365 366 /** 367 * Helper to indicate Hearing Aid active device changed for a device. 368 */ hearingAidActiveDeviceChanged(BluetoothDevice device)369 private void hearingAidActiveDeviceChanged(BluetoothDevice device) { 370 Intent intent = new Intent(BluetoothHearingAid.ACTION_ACTIVE_DEVICE_CHANGED); 371 intent.putExtra(BluetoothDevice.EXTRA_DEVICE, device); 372 mActiveDeviceManager.getBroadcastReceiver().onReceive(mContext, intent); 373 } 374 } 375