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