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