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