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