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.hearingaid;
18 
19 import static org.mockito.Mockito.*;
20 
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothDevice;
23 import android.bluetooth.BluetoothHearingAid;
24 import android.bluetooth.BluetoothProfile;
25 import android.bluetooth.BluetoothUuid;
26 import android.content.BroadcastReceiver;
27 import android.content.Context;
28 import android.content.Intent;
29 import android.content.IntentFilter;
30 import android.media.AudioManager;
31 import android.os.Looper;
32 import android.os.ParcelUuid;
33 
34 import androidx.test.InstrumentationRegistry;
35 import androidx.test.filters.MediumTest;
36 import androidx.test.rule.ServiceTestRule;
37 import androidx.test.runner.AndroidJUnit4;
38 
39 import com.android.bluetooth.TestUtils;
40 import com.android.bluetooth.btservice.AdapterService;
41 import com.android.bluetooth.btservice.storage.DatabaseManager;
42 import com.android.internal.R;
43 
44 import org.junit.After;
45 import org.junit.Assert;
46 import org.junit.Assume;
47 import org.junit.Before;
48 import org.junit.Rule;
49 import org.junit.Test;
50 import org.junit.runner.RunWith;
51 import org.mockito.Mock;
52 import org.mockito.MockitoAnnotations;
53 
54 import java.util.HashMap;
55 import java.util.List;
56 import java.util.concurrent.LinkedBlockingQueue;
57 import java.util.concurrent.TimeoutException;
58 
59 @MediumTest
60 @RunWith(AndroidJUnit4.class)
61 public class HearingAidServiceTest {
62     private BluetoothAdapter mAdapter;
63     private Context mTargetContext;
64     private HearingAidService mService;
65     private BluetoothDevice mLeftDevice;
66     private BluetoothDevice mRightDevice;
67     private BluetoothDevice mSingleDevice;
68     private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mDeviceQueueMap;
69     private static final int TIMEOUT_MS = 1000;
70 
71     private BroadcastReceiver mHearingAidIntentReceiver;
72 
73     @Mock private AdapterService mAdapterService;
74     @Mock private DatabaseManager mDatabaseManager;
75     @Mock private HearingAidNativeInterface mNativeInterface;
76     @Mock private AudioManager mAudioManager;
77 
78     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
79 
80     @Before
setUp()81     public void setUp() throws Exception {
82         mTargetContext = InstrumentationRegistry.getTargetContext();
83         Assume.assumeTrue("Ignore test when HearingAidService is not enabled",
84                 mTargetContext.getResources().getBoolean(
85                     R.bool.config_hearing_aid_profile_supported));
86         // Set up mocks and test assets
87         MockitoAnnotations.initMocks(this);
88 
89         if (Looper.myLooper() == null) {
90             Looper.prepare();
91         }
92 
93         TestUtils.setAdapterService(mAdapterService);
94 
95         mAdapter = BluetoothAdapter.getDefaultAdapter();
96 
97         startService();
98         mService.mHearingAidNativeInterface = mNativeInterface;
99         mService.mAudioManager = mAudioManager;
100 
101         // Override the timeout value to speed up the test
102         HearingAidStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s
103 
104         // Set up the Connection State Changed receiver
105         IntentFilter filter = new IntentFilter();
106         filter.addAction(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED);
107         mHearingAidIntentReceiver = new HearingAidIntentReceiver();
108         mTargetContext.registerReceiver(mHearingAidIntentReceiver, filter);
109 
110         // Get a device for testing
111         mLeftDevice = TestUtils.getTestDevice(mAdapter, 0);
112         mRightDevice = TestUtils.getTestDevice(mAdapter, 1);
113         mSingleDevice = TestUtils.getTestDevice(mAdapter, 2);
114         mDeviceQueueMap = new HashMap<>();
115         mDeviceQueueMap.put(mLeftDevice, new LinkedBlockingQueue<>());
116         mDeviceQueueMap.put(mRightDevice, new LinkedBlockingQueue<>());
117         mDeviceQueueMap.put(mSingleDevice, new LinkedBlockingQueue<>());
118         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
119                 .getBondState(any(BluetoothDevice.class));
120         doReturn(new ParcelUuid[]{BluetoothUuid.HEARING_AID}).when(mAdapterService)
121                 .getRemoteUuids(any(BluetoothDevice.class));
122     }
123 
124     @After
tearDown()125     public void tearDown() throws Exception {
126         if (!mTargetContext.getResources().getBoolean(
127                             R.bool.config_hearing_aid_profile_supported)) {
128             return;
129         }
130         stopService();
131         mTargetContext.unregisterReceiver(mHearingAidIntentReceiver);
132         mDeviceQueueMap.clear();
133         TestUtils.clearAdapterService(mAdapterService);
134         reset(mAudioManager);
135     }
136 
startService()137     private void startService() throws TimeoutException {
138         TestUtils.startService(mServiceRule, HearingAidService.class);
139         mService = HearingAidService.getHearingAidService();
140         Assert.assertNotNull(mService);
141     }
142 
stopService()143     private void stopService() throws TimeoutException {
144         TestUtils.stopService(mServiceRule, HearingAidService.class);
145         mService = HearingAidService.getHearingAidService();
146         Assert.assertNull(mService);
147     }
148 
149     private class HearingAidIntentReceiver extends BroadcastReceiver {
150         @Override
onReceive(Context context, Intent intent)151         public void onReceive(Context context, Intent intent) {
152             if (BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED.equals(intent.getAction())) {
153                 try {
154                     BluetoothDevice device = intent.getParcelableExtra(
155                             BluetoothDevice.EXTRA_DEVICE);
156                     Assert.assertNotNull(device);
157                     LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device);
158                     Assert.assertNotNull(queue);
159                     queue.put(intent);
160                 } catch (InterruptedException e) {
161                     Assert.fail("Cannot add Intent to the Connection State queue: "
162                             + e.getMessage());
163                 }
164             }
165         }
166     }
167 
verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)168     private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device,
169             int newState, int prevState) {
170         Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device));
171         Assert.assertNotNull(intent);
172         Assert.assertEquals(BluetoothHearingAid.ACTION_CONNECTION_STATE_CHANGED,
173                 intent.getAction());
174         Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
175         Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
176         Assert.assertEquals(prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE,
177                 -1));
178     }
179 
verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)180     private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) {
181         Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device));
182         Assert.assertNull(intent);
183     }
184 
185     /**
186      * Test getting HearingAid Service: getHearingAidService()
187      */
188     @Test
testGetHearingAidService()189     public void testGetHearingAidService() {
190         Assert.assertEquals(mService, HearingAidService.getHearingAidService());
191     }
192 
193     /**
194      * Test stop HearingAid Service
195      */
196     @Test
testStopHearingAidService()197     public void testStopHearingAidService() {
198         // Prepare: connect
199         connectDevice(mLeftDevice);
200         // HearingAid Service is already running: test stop(). Note: must be done on the main thread
201         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
202             public void run() {
203                 Assert.assertTrue(mService.stop());
204             }
205         });
206         // Try to restart the service. Note: must be done on the main thread
207         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
208             public void run() {
209                 Assert.assertTrue(mService.start());
210             }
211         });
212     }
213 
214     /**
215      * Test get/set priority for BluetoothDevice
216      */
217     @Test
testGetSetPriority()218     public void testGetSetPriority() {
219         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
220         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
221                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
222         Assert.assertEquals("Initial device priority",
223                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
224                 mService.getConnectionPolicy(mLeftDevice));
225 
226         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
227         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
228                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
229         Assert.assertEquals("Setting device priority to PRIORITY_OFF",
230                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
231                 mService.getConnectionPolicy(mLeftDevice));
232 
233         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
234         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
235                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
236         Assert.assertEquals("Setting device priority to PRIORITY_ON",
237                 BluetoothProfile.CONNECTION_POLICY_ALLOWED,
238                 mService.getConnectionPolicy(mLeftDevice));
239     }
240 
241     /**
242      *  Test okToConnect method using various test cases
243      */
244     @Test
testOkToConnect()245     public void testOkToConnect() {
246         int badPriorityValue = 1024;
247         int badBondState = 42;
248         testOkToConnectCase(mSingleDevice,
249                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
250         testOkToConnectCase(mSingleDevice,
251                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
252         testOkToConnectCase(mSingleDevice,
253                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
254         testOkToConnectCase(mSingleDevice,
255                 BluetoothDevice.BOND_NONE, badPriorityValue, false);
256         testOkToConnectCase(mSingleDevice,
257                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
258         testOkToConnectCase(mSingleDevice,
259                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
260         testOkToConnectCase(mSingleDevice,
261                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
262         testOkToConnectCase(mSingleDevice,
263                 BluetoothDevice.BOND_BONDING, badPriorityValue, false);
264         testOkToConnectCase(mSingleDevice,
265                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true);
266         testOkToConnectCase(mSingleDevice,
267                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
268         testOkToConnectCase(mSingleDevice,
269                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_ALLOWED, true);
270         testOkToConnectCase(mSingleDevice,
271                 BluetoothDevice.BOND_BONDED, badPriorityValue, false);
272         testOkToConnectCase(mSingleDevice,
273                 badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
274         testOkToConnectCase(mSingleDevice,
275                 badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
276         testOkToConnectCase(mSingleDevice,
277                 badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
278         testOkToConnectCase(mSingleDevice,
279                 badBondState, badPriorityValue, false);
280     }
281 
282     /**
283      * Test that an outgoing connection to device that does not have Hearing Aid UUID is rejected
284      */
285     @Test
testOutgoingConnectMissingHearingAidUuid()286     public void testOutgoingConnectMissingHearingAidUuid() {
287         // Update the device priority so okToConnect() returns true
288         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
289         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
290                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
291         when(mDatabaseManager
292                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
293                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
294         when(mDatabaseManager
295                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
296                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
297         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
298         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
299 
300         // Return No UUID
301         doReturn(new ParcelUuid[]{}).when(mAdapterService)
302                 .getRemoteUuids(any(BluetoothDevice.class));
303 
304         // Send a connect request
305         Assert.assertFalse("Connect expected to fail", mService.connect(mLeftDevice));
306     }
307 
308     /**
309      * Test that an outgoing connection to device with PRIORITY_OFF is rejected
310      */
311     @Test
testOutgoingConnectPriorityOff()312     public void testOutgoingConnectPriorityOff() {
313         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
314         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
315 
316         // Set the device priority to PRIORITY_OFF so connect() should fail
317         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
318         when(mDatabaseManager
319                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
320                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
321 
322         // Send a connect request
323         Assert.assertFalse("Connect expected to fail", mService.connect(mLeftDevice));
324     }
325 
326     /**
327      * Test that an outgoing connection times out
328      */
329     @Test
testOutgoingConnectTimeout()330     public void testOutgoingConnectTimeout() {
331         // Update the device priority so okToConnect() returns true
332         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
333         when(mDatabaseManager
334                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
335                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
336         when(mDatabaseManager
337                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
338                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
339         when(mDatabaseManager
340                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
341                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
342         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
343         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
344 
345         // Send a connect request
346         Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));
347 
348         // Verify the connection state broadcast, and that we are in Connecting state
349         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
350                 BluetoothProfile.STATE_DISCONNECTED);
351         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
352                 mService.getConnectionState(mLeftDevice));
353 
354         // Verify the connection state broadcast, and that we are in Disconnected state
355         verifyConnectionStateIntent(HearingAidStateMachine.sConnectTimeoutMs * 2,
356                 mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
357                 BluetoothProfile.STATE_CONNECTING);
358         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
359                 mService.getConnectionState(mLeftDevice));
360     }
361 
362     /**
363      * Test that the Hearing Aid Service connects to left and right device at the same time.
364      */
365     @Test
testConnectAPair_connectBothDevices()366     public void testConnectAPair_connectBothDevices() {
367         // Update hiSyncId map
368         getHiSyncIdFromNative();
369         // Update the device priority so okToConnect() returns true
370         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
371         when(mDatabaseManager
372                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
373                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
374         when(mDatabaseManager
375                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
376                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
377         when(mDatabaseManager
378                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
379                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
380         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
381         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
382 
383         // Send a connect request
384         Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));
385 
386         // Verify the connection state broadcast, and that we are in Connecting state
387         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
388                 BluetoothProfile.STATE_DISCONNECTED);
389         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
390                 mService.getConnectionState(mLeftDevice));
391         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
392                 BluetoothProfile.STATE_DISCONNECTED);
393         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
394                 mService.getConnectionState(mRightDevice));
395     }
396 
397     /**
398      * Test that the service disconnects the current pair before connecting to another pair.
399      */
400     @Test
testConnectAnotherPair_disconnectCurrentPair()401     public void testConnectAnotherPair_disconnectCurrentPair() {
402         // Update hiSyncId map
403         getHiSyncIdFromNative();
404         // Update the device priority so okToConnect() returns true
405         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
406         when(mDatabaseManager
407                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
408                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
409         when(mDatabaseManager
410                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
411                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
412         when(mDatabaseManager
413                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
414                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
415         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
416         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
417 
418         // Send a connect request
419         Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));
420 
421         // Verify the connection state broadcast, and that we are in Connecting state
422         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
423                 BluetoothProfile.STATE_DISCONNECTED);
424         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
425                 BluetoothProfile.STATE_DISCONNECTED);
426 
427         HearingAidStackEvent connCompletedEvent;
428         // Send a message to trigger connection completed
429         connCompletedEvent = new HearingAidStackEvent(
430                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
431         connCompletedEvent.device = mLeftDevice;
432         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
433         mService.messageFromNative(connCompletedEvent);
434         connCompletedEvent = new HearingAidStackEvent(
435                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
436         connCompletedEvent.device = mRightDevice;
437         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
438         mService.messageFromNative(connCompletedEvent);
439 
440         // Verify the connection state broadcast, and that we are in Connected state for right side
441         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED,
442                 BluetoothProfile.STATE_CONNECTING);
443         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED,
444                 BluetoothProfile.STATE_CONNECTING);
445 
446         // Send a connect request for another pair
447         Assert.assertTrue("Connect failed", mService.connect(mSingleDevice));
448 
449         // Verify the connection state broadcast, and that the first pair is in Disconnecting state
450         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING,
451                 BluetoothProfile.STATE_CONNECTED);
452         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTING,
453                 BluetoothProfile.STATE_CONNECTED);
454         Assert.assertFalse(mService.getConnectedDevices().contains(mLeftDevice));
455         Assert.assertFalse(mService.getConnectedDevices().contains(mRightDevice));
456 
457         // Verify the connection state broadcast, and that the second device is in Connecting state
458         verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_CONNECTING,
459                 BluetoothProfile.STATE_DISCONNECTED);
460         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
461                 mService.getConnectionState(mSingleDevice));
462     }
463 
464     /**
465      * Test that the outgoing connect/disconnect and audio switch is successful.
466      */
467     @Test
testAudioManagerConnectDisconnect()468     public void testAudioManagerConnectDisconnect() {
469         // Update hiSyncId map
470         getHiSyncIdFromNative();
471         // Update the device priority so okToConnect() returns true
472         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
473         when(mDatabaseManager
474                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
475                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
476         when(mDatabaseManager
477                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
478                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
479         when(mDatabaseManager
480                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
481                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
482         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
483         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
484 
485         // Send a connect request
486         Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));
487         Assert.assertTrue("Connect failed", mService.connect(mRightDevice));
488 
489         // Verify the connection state broadcast, and that we are in Connecting state
490         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
491                 BluetoothProfile.STATE_DISCONNECTED);
492         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
493                 mService.getConnectionState(mLeftDevice));
494         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
495                 BluetoothProfile.STATE_DISCONNECTED);
496         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
497                 mService.getConnectionState(mRightDevice));
498 
499         HearingAidStackEvent connCompletedEvent;
500         // Send a message to trigger connection completed
501         connCompletedEvent = new HearingAidStackEvent(
502                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
503         connCompletedEvent.device = mLeftDevice;
504         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
505         mService.messageFromNative(connCompletedEvent);
506 
507         // Verify the connection state broadcast, and that we are in Connected state
508         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED,
509                 BluetoothProfile.STATE_CONNECTING);
510         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
511                 mService.getConnectionState(mLeftDevice));
512 
513         // Send a message to trigger connection completed for right side
514         connCompletedEvent = new HearingAidStackEvent(
515                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
516         connCompletedEvent.device = mRightDevice;
517         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
518         mService.messageFromNative(connCompletedEvent);
519 
520         // Verify the connection state broadcast, and that we are in Connected state for right side
521         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED,
522                 BluetoothProfile.STATE_CONNECTING);
523         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
524                 mService.getConnectionState(mRightDevice));
525 
526         // Verify the list of connected devices
527         Assert.assertTrue(mService.getConnectedDevices().contains(mLeftDevice));
528         Assert.assertTrue(mService.getConnectedDevices().contains(mRightDevice));
529 
530         // Verify the audio is routed to Hearing Aid Profile
531         verify(mAudioManager).setBluetoothHearingAidDeviceConnectionState(
532                 any(BluetoothDevice.class), eq(BluetoothProfile.STATE_CONNECTED),
533                 eq(true), eq(0));
534 
535         // Send a disconnect request
536         Assert.assertTrue("Disconnect failed", mService.disconnect(mLeftDevice));
537         Assert.assertTrue("Disconnect failed", mService.disconnect(mRightDevice));
538 
539         // Verify the connection state broadcast, and that we are in Disconnecting state
540         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING,
541                 BluetoothProfile.STATE_CONNECTED);
542         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING,
543                 mService.getConnectionState(mLeftDevice));
544         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTING,
545                 BluetoothProfile.STATE_CONNECTED);
546         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING,
547                 mService.getConnectionState(mRightDevice));
548 
549         // Send a message to trigger disconnection completed
550         connCompletedEvent = new HearingAidStackEvent(
551                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
552         connCompletedEvent.device = mLeftDevice;
553         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_DISCONNECTED;
554         mService.messageFromNative(connCompletedEvent);
555 
556         // Verify the connection state broadcast, and that we are in Disconnected state
557         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
558                 BluetoothProfile.STATE_DISCONNECTING);
559         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
560                 mService.getConnectionState(mLeftDevice));
561 
562         // Send a message to trigger disconnection completed to the right device
563         connCompletedEvent = new HearingAidStackEvent(
564                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
565         connCompletedEvent.device = mRightDevice;
566         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_DISCONNECTED;
567         mService.messageFromNative(connCompletedEvent);
568 
569         // Verify the connection state broadcast, and that we are in Disconnected state
570         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTED,
571                 BluetoothProfile.STATE_DISCONNECTING);
572         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
573                 mService.getConnectionState(mRightDevice));
574 
575         // Verify the list of connected devices
576         Assert.assertFalse(mService.getConnectedDevices().contains(mLeftDevice));
577         Assert.assertFalse(mService.getConnectedDevices().contains(mRightDevice));
578 
579         // Verify the audio is not routed to Hearing Aid Profile
580         verify(mAudioManager).setBluetoothHearingAidDeviceConnectionState(
581                 any(BluetoothDevice.class), eq(BluetoothProfile.STATE_DISCONNECTED),
582                 eq(false), eq(0));
583     }
584 
585     /**
586      * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Hearing Aid stack
587      * events will create a state machine.
588      */
589     @Test
testCreateStateMachineStackEvents()590     public void testCreateStateMachineStackEvents() {
591         // Update the device priority so okToConnect() returns true
592         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
593         when(mDatabaseManager
594                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
595                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
596         when(mDatabaseManager
597                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
598                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
599         when(mDatabaseManager
600                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
601                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
602         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
603         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
604 
605         // Hearing Aid stack event: CONNECTION_STATE_CONNECTING - state machine should be created
606         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
607                 BluetoothProfile.STATE_DISCONNECTED);
608         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
609                 mService.getConnectionState(mLeftDevice));
610         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
611 
612         // HearingAid stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
613         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
614                 BluetoothProfile.STATE_CONNECTING);
615         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
616                 mService.getConnectionState(mLeftDevice));
617         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
618         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
619         Assert.assertFalse(mService.getDevices().contains(mLeftDevice));
620 
621         // stack event: CONNECTION_STATE_CONNECTED - state machine should be created
622         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED,
623                 BluetoothProfile.STATE_DISCONNECTED);
624         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
625                 mService.getConnectionState(mLeftDevice));
626         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
627 
628         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
629         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
630                 BluetoothProfile.STATE_CONNECTED);
631         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
632                 mService.getConnectionState(mLeftDevice));
633         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
634         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
635         Assert.assertFalse(mService.getDevices().contains(mLeftDevice));
636 
637         // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created
638         generateUnexpectedConnectionMessageFromNative(mLeftDevice,
639                 BluetoothProfile.STATE_DISCONNECTING,
640                 BluetoothProfile.STATE_DISCONNECTED);
641         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
642                 mService.getConnectionState(mLeftDevice));
643         Assert.assertFalse(mService.getDevices().contains(mLeftDevice));
644 
645         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created
646         generateUnexpectedConnectionMessageFromNative(mLeftDevice,
647                 BluetoothProfile.STATE_DISCONNECTED,
648                 BluetoothProfile.STATE_DISCONNECTED);
649         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
650                 mService.getConnectionState(mLeftDevice));
651         Assert.assertFalse(mService.getDevices().contains(mLeftDevice));
652     }
653 
654     /**
655      * Test that a state machine in DISCONNECTED state is removed only after the device is unbond.
656      */
657     @Test
testDeleteStateMachineUnbondEvents()658     public void testDeleteStateMachineUnbondEvents() {
659         // Update the device priority so okToConnect() returns true
660         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
661         when(mDatabaseManager
662                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
663                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
664         when(mDatabaseManager
665                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
666                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
667         when(mDatabaseManager
668                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
669                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
670         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
671         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
672 
673         // HearingAid stack event: CONNECTION_STATE_CONNECTING - state machine should be created
674         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
675                 BluetoothProfile.STATE_DISCONNECTED);
676         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
677                 mService.getConnectionState(mLeftDevice));
678         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
679         // Device unbond - state machine is not removed
680         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
681         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
682 
683         // HearingAid stack event: CONNECTION_STATE_CONNECTED - state machine is not removed
684         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED);
685         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED,
686                 BluetoothProfile.STATE_CONNECTING);
687         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
688                 mService.getConnectionState(mLeftDevice));
689         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
690         // Device unbond - state machine is not removed
691         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
692         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
693 
694         // HearingAid stack event: CONNECTION_STATE_DISCONNECTING - state machine is not removed
695         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED);
696         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTING,
697                 BluetoothProfile.STATE_CONNECTED);
698         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTING,
699                 mService.getConnectionState(mLeftDevice));
700         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
701         // Device unbond - state machine is not removed
702         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
703         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
704 
705         // HearingAid stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed
706         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED);
707         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
708                 BluetoothProfile.STATE_DISCONNECTING);
709         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
710                 mService.getConnectionState(mLeftDevice));
711         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
712         // Device unbond - state machine is removed
713         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
714         Assert.assertFalse(mService.getDevices().contains(mLeftDevice));
715     }
716 
717     /**
718      * Test that a CONNECTION_STATE_DISCONNECTED Hearing Aid stack event will remove the state
719      * machine only if the device is unbond.
720      */
721     @Test
testDeleteStateMachineDisconnectEvents()722     public void testDeleteStateMachineDisconnectEvents() {
723         // Update the device priority so okToConnect() returns true
724         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
725         when(mDatabaseManager
726                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
727                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
728         when(mDatabaseManager
729                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
730                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
731         when(mDatabaseManager
732                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
733                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
734         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
735         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
736 
737         // HearingAid stack event: CONNECTION_STATE_CONNECTING - state machine should be created
738         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
739                 BluetoothProfile.STATE_DISCONNECTED);
740         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
741                 mService.getConnectionState(mLeftDevice));
742         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
743 
744         // HearingAid stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed
745         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
746                 BluetoothProfile.STATE_CONNECTING);
747         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
748                 mService.getConnectionState(mLeftDevice));
749         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
750 
751         // HearingAid stack event: CONNECTION_STATE_CONNECTING - state machine remains
752         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
753                 BluetoothProfile.STATE_DISCONNECTED);
754         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
755                 mService.getConnectionState(mLeftDevice));
756         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
757 
758         // Device bond state marked as unbond - state machine is not removed
759         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService)
760                 .getBondState(any(BluetoothDevice.class));
761         Assert.assertTrue(mService.getDevices().contains(mLeftDevice));
762 
763         // HearingAid stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed
764         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
765                 BluetoothProfile.STATE_CONNECTING);
766         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
767                 mService.getConnectionState(mLeftDevice));
768         Assert.assertFalse(mService.getDevices().contains(mLeftDevice));
769     }
770 
771     @Test
testConnectionStateChangedActiveDevice()772     public void testConnectionStateChangedActiveDevice() {
773         // Update hiSyncId map
774         getHiSyncIdFromNative();
775         // Update the device priority so okToConnect() returns true
776         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
777         when(mDatabaseManager
778                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
779                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
780         when(mDatabaseManager
781                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
782                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
783         when(mDatabaseManager
784                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
785                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
786 
787         generateConnectionMessageFromNative(mRightDevice, BluetoothProfile.STATE_CONNECTED,
788                 BluetoothProfile.STATE_DISCONNECTED);
789         Assert.assertTrue(mService.getActiveDevices().contains(mRightDevice));
790         Assert.assertFalse(mService.getActiveDevices().contains(mLeftDevice));
791 
792         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED,
793                 BluetoothProfile.STATE_DISCONNECTED);
794         Assert.assertTrue(mService.getActiveDevices().contains(mRightDevice));
795         Assert.assertTrue(mService.getActiveDevices().contains(mLeftDevice));
796 
797         generateConnectionMessageFromNative(mRightDevice, BluetoothProfile.STATE_DISCONNECTED,
798                 BluetoothProfile.STATE_CONNECTED);
799         Assert.assertFalse(mService.getActiveDevices().contains(mRightDevice));
800         Assert.assertTrue(mService.getActiveDevices().contains(mLeftDevice));
801 
802         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
803                 BluetoothProfile.STATE_CONNECTED);
804         Assert.assertFalse(mService.getActiveDevices().contains(mRightDevice));
805         Assert.assertFalse(mService.getActiveDevices().contains(mLeftDevice));
806     }
807 
808     @Test
testConnectionStateChangedAnotherActiveDevice()809     public void testConnectionStateChangedAnotherActiveDevice() {
810         // Update hiSyncId map
811         getHiSyncIdFromNative();
812         // Update the device priority so okToConnect() returns true
813         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
814         when(mDatabaseManager
815                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
816                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
817         when(mDatabaseManager
818                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
819                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
820         when(mDatabaseManager
821                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
822                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
823 
824         generateConnectionMessageFromNative(mRightDevice, BluetoothProfile.STATE_CONNECTED,
825                 BluetoothProfile.STATE_DISCONNECTED);
826         Assert.assertTrue(mService.getActiveDevices().contains(mRightDevice));
827         Assert.assertFalse(mService.getActiveDevices().contains(mLeftDevice));
828 
829         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED,
830                 BluetoothProfile.STATE_DISCONNECTED);
831         Assert.assertTrue(mService.getActiveDevices().contains(mRightDevice));
832         Assert.assertTrue(mService.getActiveDevices().contains(mLeftDevice));
833 
834         generateConnectionMessageFromNative(mSingleDevice, BluetoothProfile.STATE_CONNECTED,
835                 BluetoothProfile.STATE_DISCONNECTED);
836         Assert.assertFalse(mService.getActiveDevices().contains(mRightDevice));
837         Assert.assertFalse(mService.getActiveDevices().contains(mLeftDevice));
838         Assert.assertTrue(mService.getActiveDevices().contains(mSingleDevice));
839     }
840 
841     /**
842      * Verify the correctness during first time connection.
843      * Connect to left device -> Get left device hiSyncId -> Connect to right device ->
844      * Get right device hiSyncId -> Both devices should be always connected
845      */
846     @Test
firstTimeConnection_shouldConnectToBothDevices()847     public void firstTimeConnection_shouldConnectToBothDevices() {
848         // Update the device priority so okToConnect() returns true
849         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
850         when(mDatabaseManager
851                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
852                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
853         when(mDatabaseManager
854                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
855                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
856         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
857         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
858         // Send a connect request for left device
859         Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));
860         // Verify the connection state broadcast, and that we are in Connecting state
861         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
862                 BluetoothProfile.STATE_DISCONNECTED);
863         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
864                 mService.getConnectionState(mLeftDevice));
865 
866         HearingAidStackEvent connCompletedEvent;
867         // Send a message to trigger connection completed
868         connCompletedEvent = new HearingAidStackEvent(
869                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
870         connCompletedEvent.device = mLeftDevice;
871         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
872         mService.messageFromNative(connCompletedEvent);
873 
874         // Verify the connection state broadcast, and that we are in Connected state
875         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED,
876                 BluetoothProfile.STATE_CONNECTING);
877         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
878                 mService.getConnectionState(mLeftDevice));
879 
880         // Get hiSyncId for left device
881         HearingAidStackEvent hiSyncIdEvent = new HearingAidStackEvent(
882                 HearingAidStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
883         hiSyncIdEvent.device = mLeftDevice;
884         hiSyncIdEvent.valueInt1 = 0x02;
885         hiSyncIdEvent.valueLong2 = 0x0101;
886         mService.messageFromNative(hiSyncIdEvent);
887 
888         // Send a connect request for right device
889         Assert.assertTrue("Connect failed", mService.connect(mRightDevice));
890         // Verify the connection state broadcast, and that we are in Connecting state
891         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
892                 BluetoothProfile.STATE_DISCONNECTED);
893         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
894                 mService.getConnectionState(mRightDevice));
895         // Verify the left device is still connected
896         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
897                 mService.getConnectionState(mLeftDevice));
898 
899         // Send a message to trigger connection completed
900         connCompletedEvent = new HearingAidStackEvent(
901                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
902         connCompletedEvent.device = mRightDevice;
903         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
904         mService.messageFromNative(connCompletedEvent);
905 
906         // Verify the connection state broadcast, and that we are in Connected state
907         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED,
908                 BluetoothProfile.STATE_CONNECTING);
909         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
910                 mService.getConnectionState(mRightDevice));
911         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
912                 mService.getConnectionState(mLeftDevice));
913 
914         // Get hiSyncId for right device
915         hiSyncIdEvent = new HearingAidStackEvent(
916                 HearingAidStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
917         hiSyncIdEvent.device = mRightDevice;
918         hiSyncIdEvent.valueInt1 = 0x02;
919         hiSyncIdEvent.valueLong2 = 0x0101;
920         mService.messageFromNative(hiSyncIdEvent);
921 
922         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
923                 mService.getConnectionState(mRightDevice));
924         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
925                 mService.getConnectionState(mLeftDevice));
926     }
927 
928     /**
929      * Get the HiSyncId from native stack after connecting to left device, then connect right
930      */
931     @Test
getHiSyncId_afterFirstDeviceConnected()932     public void getHiSyncId_afterFirstDeviceConnected() {
933         // Update the device priority so okToConnect() returns true
934         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
935         when(mDatabaseManager
936                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.HEARING_AID))
937                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
938         when(mDatabaseManager
939                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.HEARING_AID))
940                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
941         when(mDatabaseManager
942                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.HEARING_AID))
943                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
944         doReturn(true).when(mNativeInterface).connectHearingAid(any(BluetoothDevice.class));
945         doReturn(true).when(mNativeInterface).disconnectHearingAid(any(BluetoothDevice.class));
946         // Send a connect request
947         Assert.assertTrue("Connect failed", mService.connect(mLeftDevice));
948         // Verify the connection state broadcast, and that we are in Connecting state
949         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
950                 BluetoothProfile.STATE_DISCONNECTED);
951         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
952                 mService.getConnectionState(mLeftDevice));
953         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
954                 mService.getConnectionState(mRightDevice));
955 
956         HearingAidStackEvent connCompletedEvent;
957         // Send a message to trigger connection completed
958         connCompletedEvent = new HearingAidStackEvent(
959                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
960         connCompletedEvent.device = mLeftDevice;
961         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
962         mService.messageFromNative(connCompletedEvent);
963         // Verify the connection state broadcast, and that we are in Connected state
964         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED,
965                 BluetoothProfile.STATE_CONNECTING);
966         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
967                 mService.getConnectionState(mLeftDevice));
968 
969         // Get hiSyncId update from native stack
970         getHiSyncIdFromNative();
971         // Send a connect request for right
972         Assert.assertTrue("Connect failed", mService.connect(mRightDevice));
973         // Verify the connection state broadcast, and that we are in Connecting state
974         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
975                 BluetoothProfile.STATE_DISCONNECTED);
976         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
977                 mService.getConnectionState(mRightDevice));
978         // Verify the left device is still connected
979         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
980                 mService.getConnectionState(mLeftDevice));
981 
982         // Send a message to trigger connection completed
983         connCompletedEvent = new HearingAidStackEvent(
984                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
985         connCompletedEvent.device = mRightDevice;
986         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
987         mService.messageFromNative(connCompletedEvent);
988 
989         // Verify the connection state broadcast, and that we are in Connected state
990         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED,
991                 BluetoothProfile.STATE_CONNECTING);
992         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
993                 mService.getConnectionState(mRightDevice));
994         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
995                 mService.getConnectionState(mLeftDevice));
996     }
997 
998     /**
999      * Test that the service can update HiSyncId from native message
1000      */
1001     @Test
getHiSyncIdFromNative_addToMap()1002     public void getHiSyncIdFromNative_addToMap() {
1003         getHiSyncIdFromNative();
1004         Assert.assertTrue("hiSyncIdMap should contain mLeftDevice",
1005                 mService.getHiSyncIdMap().containsKey(mLeftDevice));
1006         Assert.assertTrue("hiSyncIdMap should contain mRightDevice",
1007                 mService.getHiSyncIdMap().containsKey(mRightDevice));
1008         Assert.assertTrue("hiSyncIdMap should contain mSingleDevice",
1009                 mService.getHiSyncIdMap().containsKey(mSingleDevice));
1010     }
1011 
1012     /**
1013      * Test that the service removes the device from HiSyncIdMap when it's unbonded
1014      */
1015     @Test
deviceUnbonded_removeHiSyncId()1016     public void deviceUnbonded_removeHiSyncId() {
1017         getHiSyncIdFromNative();
1018         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
1019         Assert.assertFalse("hiSyncIdMap shouldn't contain mLeftDevice",
1020                 mService.getHiSyncIdMap().containsKey(mLeftDevice));
1021     }
1022 
connectDevice(BluetoothDevice device)1023     private void connectDevice(BluetoothDevice device) {
1024         HearingAidStackEvent connCompletedEvent;
1025 
1026         List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices();
1027 
1028         // Update the device priority so okToConnect() returns true
1029         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1030         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEARING_AID))
1031                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1032         doReturn(true).when(mNativeInterface).connectHearingAid(device);
1033         doReturn(true).when(mNativeInterface).disconnectHearingAid(device);
1034 
1035         // Send a connect request
1036         Assert.assertTrue("Connect failed", mService.connect(device));
1037 
1038         // Verify the connection state broadcast, and that we are in Connecting state
1039         verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTING,
1040                 BluetoothProfile.STATE_DISCONNECTED);
1041         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
1042                 mService.getConnectionState(device));
1043 
1044         // Send a message to trigger connection completed
1045         connCompletedEvent = new HearingAidStackEvent(
1046                 HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1047         connCompletedEvent.device = device;
1048         connCompletedEvent.valueInt1 = HearingAidStackEvent.CONNECTION_STATE_CONNECTED;
1049         mService.messageFromNative(connCompletedEvent);
1050 
1051         // Verify the connection state broadcast, and that we are in Connected state
1052         verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTED,
1053                 BluetoothProfile.STATE_CONNECTING);
1054         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
1055                 mService.getConnectionState(device));
1056 
1057         // Verify that the device is in the list of connected devices
1058         Assert.assertTrue(mService.getConnectedDevices().contains(device));
1059         // Verify the list of previously connected devices
1060         for (BluetoothDevice prevDevice : prevConnectedDevices) {
1061             Assert.assertTrue(mService.getConnectedDevices().contains(prevDevice));
1062         }
1063     }
1064 
generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)1065     private void generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState,
1066             int oldConnectionState) {
1067         HearingAidStackEvent stackEvent =
1068                 new HearingAidStackEvent(HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1069         stackEvent.device = device;
1070         stackEvent.valueInt1 = newConnectionState;
1071         mService.messageFromNative(stackEvent);
1072         // Verify the connection state broadcast
1073         verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState);
1074     }
1075 
generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)1076     private void generateUnexpectedConnectionMessageFromNative(BluetoothDevice device,
1077             int newConnectionState, int oldConnectionState) {
1078         HearingAidStackEvent stackEvent =
1079                 new HearingAidStackEvent(HearingAidStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1080         stackEvent.device = device;
1081         stackEvent.valueInt1 = newConnectionState;
1082         mService.messageFromNative(stackEvent);
1083         // Verify the connection state broadcast
1084         verifyNoConnectionStateIntent(TIMEOUT_MS, device);
1085     }
1086 
1087     /**
1088      *  Helper function to test okToConnect() method
1089      *
1090      *  @param device test device
1091      *  @param bondState bond state value, could be invalid
1092      *  @param priority value, could be invalid, coudl be invalid
1093      *  @param expected expected result from okToConnect()
1094      */
testOkToConnectCase(BluetoothDevice device, int bondState, int priority, boolean expected)1095     private void testOkToConnectCase(BluetoothDevice device, int bondState, int priority,
1096             boolean expected) {
1097         doReturn(bondState).when(mAdapterService).getBondState(device);
1098         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1099         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.HEARING_AID))
1100                 .thenReturn(priority);
1101         Assert.assertEquals(expected, mService.okToConnect(device));
1102     }
1103 
getHiSyncIdFromNative()1104     private void getHiSyncIdFromNative() {
1105         HearingAidStackEvent event = new HearingAidStackEvent(
1106                 HearingAidStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
1107         event.device = mLeftDevice;
1108         event.valueInt1 = 0x02;
1109         event.valueLong2 = 0x0101;
1110         mService.messageFromNative(event);
1111         event.device = mRightDevice;
1112         event.valueInt1 = 0x03;
1113         mService.messageFromNative(event);
1114         event.device = mSingleDevice;
1115         event.valueInt1 = 0x00;
1116         event.valueLong2 = 0x0102;
1117         mService.messageFromNative(event);
1118     }
1119 }
1120