• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2020 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bluetooth.le_audio;
19 
20 import static com.google.common.truth.Truth.assertThat;
21 import static com.google.common.truth.Truth.assertWithMessage;
22 
23 import static org.mockito.Mockito.any;
24 import static org.mockito.Mockito.anyInt;
25 import static org.mockito.Mockito.anyString;
26 import static org.mockito.Mockito.doAnswer;
27 import static org.mockito.Mockito.doNothing;
28 import static org.mockito.Mockito.doReturn;
29 import static org.mockito.Mockito.eq;
30 import static org.mockito.Mockito.never;
31 import static org.mockito.Mockito.timeout;
32 import static org.mockito.Mockito.times;
33 import static org.mockito.Mockito.verify;
34 import static org.mockito.Mockito.when;
35 
36 import android.bluetooth.BluetoothAdapter;
37 import android.bluetooth.BluetoothDevice;
38 import android.bluetooth.BluetoothLeAudio;
39 import android.bluetooth.BluetoothLeAudioCodecConfig;
40 import android.bluetooth.BluetoothLeAudioCodecStatus;
41 import android.bluetooth.BluetoothManager;
42 import android.bluetooth.BluetoothProfile;
43 import android.bluetooth.BluetoothUuid;
44 import android.bluetooth.IBluetoothLeAudioCallback;
45 import android.content.BroadcastReceiver;
46 import android.content.Context;
47 import android.content.Intent;
48 import android.content.IntentFilter;
49 import android.media.AudioManager;
50 import android.media.BluetoothProfileConnectionInfo;
51 import android.os.ParcelUuid;
52 
53 import androidx.test.InstrumentationRegistry;
54 import androidx.test.filters.MediumTest;
55 import androidx.test.rule.ServiceTestRule;
56 import androidx.test.runner.AndroidJUnit4;
57 
58 import com.android.bluetooth.TestUtils;
59 import com.android.bluetooth.btservice.AdapterService;
60 import com.android.bluetooth.btservice.ServiceFactory;
61 import com.android.bluetooth.btservice.storage.DatabaseManager;
62 import com.android.bluetooth.csip.CsipSetCoordinatorService;
63 import com.android.bluetooth.hfp.HeadsetService;
64 import com.android.bluetooth.mcp.McpService;
65 import com.android.bluetooth.tbs.TbsService;
66 import com.android.bluetooth.vc.VolumeControlService;
67 
68 import org.junit.After;
69 import org.junit.Before;
70 import org.junit.Rule;
71 import org.junit.Test;
72 import org.junit.runner.RunWith;
73 import org.mockito.ArgumentCaptor;
74 import org.mockito.Mock;
75 import org.mockito.Mockito;
76 import org.mockito.MockitoAnnotations;
77 import org.mockito.Spy;
78 
79 import java.util.HashMap;
80 import java.util.HashSet;
81 import java.util.List;
82 import java.util.Objects;
83 import java.util.concurrent.LinkedBlockingQueue;
84 import java.util.concurrent.TimeoutException;
85 
86 @MediumTest
87 @RunWith(AndroidJUnit4.class)
88 public class LeAudioServiceTest {
89     private static final int ASYNC_CALL_TIMEOUT_MILLIS = 250;
90     private static final int TIMEOUT_MS = 1000;
91     private static final int AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS = 3000;
92     private static final int MAX_LE_AUDIO_CONNECTIONS = 5;
93     private static final int LE_AUDIO_GROUP_ID_INVALID = -1;
94 
95     private BluetoothAdapter mAdapter;
96     private Context mTargetContext;
97     private LeAudioService mService;
98     private BluetoothDevice mLeftDevice;
99     private BluetoothDevice mRightDevice;
100     private BluetoothDevice mSingleDevice;
101     private HashSet<BluetoothDevice> mBondedDevices = new HashSet<>();
102     private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mDeviceQueueMap;
103     private LinkedBlockingQueue<Intent> mGroupIntentQueue = new LinkedBlockingQueue<>();
104     private int testGroupId = 1;
105     private boolean onGroupStatusCallbackCalled = false;
106     private boolean onGroupCodecConfChangedCallbackCalled = false;
107     private BluetoothLeAudioCodecStatus testCodecStatus = null;
108 
109     private BroadcastReceiver mLeAudioIntentReceiver;
110 
111     @Mock private AdapterService mAdapterService;
112     @Mock private AudioManager mAudioManager;
113     @Mock private DatabaseManager mDatabaseManager;
114     @Mock private LeAudioNativeInterface mNativeInterface;
115     @Mock private LeAudioTmapGattServer mTmapGattServer;
116     @Mock private McpService mMcpService;
117     @Mock private TbsService mTbsService;
118     @Mock private VolumeControlService mVolumeControlService;
119     @Mock private CsipSetCoordinatorService mCsipSetCoordinatorService;
120     @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
121     @Spy private ServiceFactory mServiceFactory = new ServiceFactory();
122 
123     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
124 
125     private static final BluetoothLeAudioCodecConfig LC3_16KHZ_CONFIG =
126             new BluetoothLeAudioCodecConfig.Builder()
127                 .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
128                 .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
129                 .build();
130     private static final BluetoothLeAudioCodecConfig LC3_48KHZ_CONFIG =
131             new BluetoothLeAudioCodecConfig.Builder()
132                 .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
133                 .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000)
134                 .build();
135 
136     private static final BluetoothLeAudioCodecConfig LC3_48KHZ_16KHZ_CONFIG =
137              new BluetoothLeAudioCodecConfig.Builder()
138                .setCodecType(BluetoothLeAudioCodecConfig.SOURCE_CODEC_TYPE_LC3)
139                .setSampleRate(BluetoothLeAudioCodecConfig.SAMPLE_RATE_48000
140                                 | BluetoothLeAudioCodecConfig.SAMPLE_RATE_16000)
141                .build();
142 
143     private static final List<BluetoothLeAudioCodecConfig> INPUT_CAPABILITIES_CONFIG = List.of(
144             LC3_48KHZ_16KHZ_CONFIG);
145 
146     private static final List<BluetoothLeAudioCodecConfig> OUTPUT_CAPABILITIES_CONFIG = List.of(
147             LC3_48KHZ_16KHZ_CONFIG);
148 
149     private static final List<BluetoothLeAudioCodecConfig> INPUT_SELECTABLE_CONFIG = List.of(
150             LC3_16KHZ_CONFIG);
151 
152     private static final List<BluetoothLeAudioCodecConfig> OUTPUT_SELECTABLE_CONFIG = List.of(
153             LC3_48KHZ_16KHZ_CONFIG);
154 
155     @Before
setUp()156     public void setUp() throws Exception {
157         mTargetContext = InstrumentationRegistry.getTargetContext();
158 
159         // Set up mocks and test assets
160         MockitoAnnotations.initMocks(this);
161 
162         // Use spied objects factory
163         doNothing().when(mTmapGattServer).start(anyInt());
164         doNothing().when(mTmapGattServer).stop();
165         LeAudioObjectsFactory.setInstanceForTesting(mObjectsFactory);
166         doReturn(mTmapGattServer).when(mObjectsFactory).getTmapGattServer(any());
167 
168         TestUtils.setAdapterService(mAdapterService);
169         doReturn(MAX_LE_AUDIO_CONNECTIONS).when(mAdapterService).getMaxConnectedAudioDevices();
170         doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService)
171                 .getRemoteUuids(any(BluetoothDevice.class));
172         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
173         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
174 
175         BluetoothManager manager = mTargetContext.getSystemService(BluetoothManager.class);
176         assertThat(manager).isNotNull();
177         mAdapter = manager.getAdapter();
178         // Mock methods in AdapterService
179         doAnswer(invocation -> mBondedDevices.toArray(new BluetoothDevice[]{})).when(
180                 mAdapterService).getBondedDevices();
181 
182         LeAudioNativeInterface.setInstance(mNativeInterface);
183         startService();
184         mService.mAudioManager = mAudioManager;
185         mService.mMcpService = mMcpService;
186         mService.mTbsService = mTbsService;
187         mService.mServiceFactory = mServiceFactory;
188         when(mServiceFactory.getVolumeControlService()).thenReturn(mVolumeControlService);
189         when(mServiceFactory.getCsipSetCoordinatorService()).thenReturn(mCsipSetCoordinatorService);
190 
191         LeAudioStackEvent stackEvent =
192         new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_NATIVE_INITIALIZED);
193         mService.messageFromNative(stackEvent);
194         assertThat(mService.mLeAudioNativeIsInitialized).isTrue();
195 
196         // Override the timeout value to speed up the test
197         LeAudioStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s
198 
199         mGroupIntentQueue = new LinkedBlockingQueue<>();
200 
201         // Set up the Connection State Changed receiver
202         IntentFilter filter = new IntentFilter();
203         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
204         filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
205         filter.addAction(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
206         mLeAudioIntentReceiver = new LeAudioIntentReceiver();
207         mTargetContext.registerReceiver(mLeAudioIntentReceiver, filter);
208 
209         doAnswer(invocation -> mBondedDevices.toArray(new BluetoothDevice[]{})).when(
210                 mAdapterService).getBondedDevices();
211 
212         // Get a device for testing
213         mLeftDevice = TestUtils.getTestDevice(mAdapter, 0);
214         mRightDevice = TestUtils.getTestDevice(mAdapter, 1);
215         mSingleDevice = TestUtils.getTestDevice(mAdapter, 2);
216         mDeviceQueueMap = new HashMap<>();
217         mDeviceQueueMap.put(mLeftDevice, new LinkedBlockingQueue<>());
218         mDeviceQueueMap.put(mRightDevice, new LinkedBlockingQueue<>());
219         mDeviceQueueMap.put(mSingleDevice, new LinkedBlockingQueue<>());
220         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
221                 .getBondState(any(BluetoothDevice.class));
222         doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService)
223                 .getRemoteUuids(any(BluetoothDevice.class));
224 
225         verify(mNativeInterface, timeout(3000).times(1)).init(any());
226     }
227 
228     @After
tearDown()229     public void tearDown() throws Exception {
230         if ((mService == null) || (mAdapter == null)) {
231             return;
232         }
233 
234         mBondedDevices.clear();
235         mGroupIntentQueue.clear();
236         stopService();
237         mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
238         mDeviceQueueMap.clear();
239         TestUtils.clearAdapterService(mAdapterService);
240         LeAudioNativeInterface.setInstance(null);
241     }
242 
startService()243     private void startService() throws TimeoutException {
244         TestUtils.startService(mServiceRule, LeAudioService.class);
245         mService = LeAudioService.getLeAudioService();
246         assertThat(mService).isNotNull();
247     }
248 
stopService()249     private void stopService() throws TimeoutException {
250         TestUtils.stopService(mServiceRule, LeAudioService.class);
251         mService = LeAudioService.getLeAudioService();
252         assertThat(mService).isNull();
253     }
254 
255     private class LeAudioIntentReceiver extends BroadcastReceiver {
256         @Override
onReceive(Context context, Intent intent)257         public void onReceive(Context context, Intent intent) {
258             if (BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED
259                     .equals(intent.getAction())) {
260                 try {
261                     BluetoothDevice device = intent.getParcelableExtra(
262                             BluetoothDevice.EXTRA_DEVICE);
263                     assertThat(device).isNotNull();
264                     LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device);
265                     assertThat(queue).isNotNull();
266                     queue.put(intent);
267                 } catch (InterruptedException e) {
268                     assertWithMessage("Cannot add Intent to the Connection State queue: "
269                             + e.getMessage()).fail();
270                 }
271             }
272             if (BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED.equals(intent.getAction())) {
273                 try {
274                     BluetoothDevice device = intent.getParcelableExtra(
275                             BluetoothDevice.EXTRA_DEVICE);
276                     if (device != null) {
277                         LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device);
278                         assertThat(queue).isNotNull();
279                         queue.put(intent);
280                     }
281                 } catch (InterruptedException e) {
282                     assertWithMessage("Cannot add Le Audio Intent to the Connection State queue: "
283                             + e.getMessage()).fail();
284                 }
285             }
286         }
287     }
288 
verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)289     private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device,
290             int newState, int prevState) {
291         Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device));
292         assertThat(intent).isNotNull();
293         assertThat(intent.getAction())
294                 .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_CONNECTION_STATE_CHANGED);
295         assertThat((BluetoothDevice)intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE)).isEqualTo(device);
296         assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1)).isEqualTo(newState);
297         assertThat(intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1)).isEqualTo(prevState);
298 
299         if (newState == BluetoothProfile.STATE_CONNECTED) {
300             // ActiveDeviceManager calls deviceConnected when connected.
301             mService.deviceConnected(device);
302         } else if (newState == BluetoothProfile.STATE_DISCONNECTED) {
303             // ActiveDeviceManager calls deviceDisconnected when connected.
304             mService.deviceDisconnected(device, false);
305         }
306     }
307 
308     /**
309      * Test getting LeAudio Service: getLeAudioService()
310      */
311     @Test
testGetLeAudioService()312     public void testGetLeAudioService() {
313         assertThat(mService).isEqualTo(LeAudioService.getLeAudioService());
314     }
315 
316     /**
317      * Test stop LeAudio Service
318      */
319     @Test
testStopLeAudioService()320     public void testStopLeAudioService() {
321         // Prepare: connect
322         connectDevice(mLeftDevice);
323         // LeAudio Service is already running: test stop(). Note: must be done on the main thread
324         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
325             public void run() {
326                 assertThat(mService.stop()).isTrue();
327             }
328         });
329     }
330 
331     /**
332      * Test if stop during init is ok.
333      */
334     @Test
testStopStartStopService()335     public void testStopStartStopService() throws Exception {
336         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
337             public void run() {
338                 assertThat(mService.stop()).isTrue();
339                 assertThat(mService.start()).isTrue();
340                 assertThat(mService.stop()).isTrue();
341                 assertThat(mService.start()).isTrue();
342             }
343         });
344     }
345 
346     /**
347      * Test get/set priority for BluetoothDevice
348      */
349     @Test
testGetSetPriority()350     public void testGetSetPriority() {
351         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
352                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
353         assertWithMessage("Initial device priority")
354                 .that(BluetoothProfile.CONNECTION_POLICY_UNKNOWN)
355                 .isEqualTo(mService.getConnectionPolicy(mLeftDevice));
356 
357         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
358                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
359         assertWithMessage("Setting device priority to PRIORITY_OFF")
360                 .that(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)
361                 .isEqualTo(mService.getConnectionPolicy(mLeftDevice));
362 
363         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
364                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
365         assertWithMessage("Setting device priority to PRIORITY_ON")
366                 .that(BluetoothProfile.CONNECTION_POLICY_ALLOWED)
367                 .isEqualTo(mService.getConnectionPolicy(mLeftDevice));
368     }
369 
370     /**
371      *  Helper function to test okToConnect() method
372      *
373      *  @param device test device
374      *  @param bondState bond state value, could be invalid
375      *  @param priority value, could be invalid, could be invalid
376      *  @param expected expected result from okToConnect()
377      */
testOkToConnectCase(BluetoothDevice device, int bondState, int priority, boolean expected)378     private void testOkToConnectCase(BluetoothDevice device, int bondState, int priority,
379             boolean expected) {
380         doReturn(bondState).when(mAdapterService).getBondState(device);
381         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO))
382                 .thenReturn(priority);
383         assertThat(expected).isEqualTo(mService.okToConnect(device));
384     }
385 
386     /**
387      *  Test okToConnect method using various test cases
388      */
389     @Test
testOkToConnect()390     public void testOkToConnect() {
391         int badPriorityValue = 1024;
392         int badBondState = 42;
393         testOkToConnectCase(mSingleDevice,
394                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
395         testOkToConnectCase(mSingleDevice,
396                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
397         testOkToConnectCase(mSingleDevice,
398                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
399         testOkToConnectCase(mSingleDevice,
400                 BluetoothDevice.BOND_NONE, badPriorityValue, false);
401         testOkToConnectCase(mSingleDevice,
402                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
403         testOkToConnectCase(mSingleDevice,
404                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
405         testOkToConnectCase(mSingleDevice,
406                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
407         testOkToConnectCase(mSingleDevice,
408                 BluetoothDevice.BOND_BONDING, badPriorityValue, false);
409         testOkToConnectCase(mSingleDevice,
410                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true);
411         testOkToConnectCase(mSingleDevice,
412                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
413         testOkToConnectCase(mSingleDevice,
414                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_ALLOWED, true);
415         testOkToConnectCase(mSingleDevice,
416                 BluetoothDevice.BOND_BONDED, badPriorityValue, false);
417         testOkToConnectCase(mSingleDevice,
418                 badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
419         testOkToConnectCase(mSingleDevice,
420                 badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
421         testOkToConnectCase(mSingleDevice,
422                 badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
423         testOkToConnectCase(mSingleDevice,
424                 badBondState, badPriorityValue, false);
425     }
426 
427     /**
428      * Test that an outgoing connection to device that does not have Le Audio UUID is rejected
429      */
430     @Test
testOutgoingConnectMissingLeAudioUuid()431     public void testOutgoingConnectMissingLeAudioUuid() {
432         // Update the device priority so okToConnect() returns true
433         when(mDatabaseManager.getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
434                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
435         when(mDatabaseManager
436                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO))
437                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
438         when(mDatabaseManager
439                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
440                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
441         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
442         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
443 
444         // Return No UUID
445         doReturn(new ParcelUuid[]{}).when(mAdapterService)
446                 .getRemoteUuids(any(BluetoothDevice.class));
447 
448         // Send a connect request
449         assertWithMessage("Connect expected to fail").that(mService.connect(mLeftDevice)).isFalse();
450     }
451 
452     /**
453      * Test that an outgoing connection to device with PRIORITY_OFF is rejected
454      */
455     @Test
testOutgoingConnectPriorityOff()456     public void testOutgoingConnectPriorityOff() {
457         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
458         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
459 
460         // Set the device priority to PRIORITY_OFF so connect() should fail
461         when(mDatabaseManager
462                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
463                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
464 
465         // Send a connect request
466         assertWithMessage("Connect expected to fail").that(mService.connect(mLeftDevice)).isFalse();
467     }
468 
469     /**
470      * Test that an outgoing connection times out
471      */
472     @Test
testOutgoingConnectTimeout()473     public void testOutgoingConnectTimeout() {
474         // Update the device priority so okToConnect() returns true
475         when(mDatabaseManager
476                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
477                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
478         when(mDatabaseManager
479                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO))
480                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
481         when(mDatabaseManager
482                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
483                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
484         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
485         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
486 
487         // Send a connect request
488         assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
489 
490         // Verify the connection state broadcast, and that we are in Connecting state
491         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
492                 BluetoothProfile.STATE_DISCONNECTED);
493         assertThat(mService.getConnectionState(mLeftDevice))
494                 .isEqualTo(BluetoothProfile.STATE_CONNECTING);
495 
496         // Verify the connection state broadcast, and that we are in Disconnected state
497         verifyConnectionStateIntent(LeAudioStateMachine.sConnectTimeoutMs * 2,
498                 mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
499                 BluetoothProfile.STATE_CONNECTING);
500         assertThat(mService.getConnectionState(mLeftDevice))
501                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
502     }
503 
injectNoVerifyDeviceConnected(BluetoothDevice device)504     private void injectNoVerifyDeviceConnected(BluetoothDevice device) {
505         generateUnexpectedConnectionMessageFromNative(device,
506                 LeAudioStackEvent.CONNECTION_STATE_CONNECTED,
507                 LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED);
508     }
509 
injectAndVerifyDeviceDisconnected(BluetoothDevice device)510     private void injectAndVerifyDeviceDisconnected(BluetoothDevice device) {
511         generateConnectionMessageFromNative(device,
512                 LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED,
513                 LeAudioStackEvent.CONNECTION_STATE_CONNECTED);
514     }
515 
injectNoVerifyDeviceDisconnected(BluetoothDevice device)516     private void injectNoVerifyDeviceDisconnected(BluetoothDevice device) {
517         generateUnexpectedConnectionMessageFromNative(device,
518                 LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED,
519                 LeAudioStackEvent.CONNECTION_STATE_CONNECTED);
520     }
521     /**
522      * Test that the outgoing connect/disconnect and audio switch is successful.
523      */
524     @Test
testAudioManagerConnectDisconnect()525     public void testAudioManagerConnectDisconnect() {
526         // Update the device priority so okToConnect() returns true
527         when(mDatabaseManager
528                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
529                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
530         when(mDatabaseManager
531                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO))
532                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
533         when(mDatabaseManager
534                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
535                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
536         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
537         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
538 
539         // Send a connect request
540         assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
541         assertWithMessage("Connect failed").that(mService.connect(mRightDevice)).isTrue();
542 
543         // Verify the connection state broadcast, and that we are in Connecting state
544         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTING,
545                 BluetoothProfile.STATE_DISCONNECTED);
546         assertThat(mService.getConnectionState(mLeftDevice))
547                 .isEqualTo(BluetoothProfile.STATE_CONNECTING);
548         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTING,
549                 BluetoothProfile.STATE_DISCONNECTED);
550         assertThat(mService.getConnectionState(mRightDevice))
551                 .isEqualTo(BluetoothProfile.STATE_CONNECTING);
552 
553         LeAudioStackEvent connCompletedEvent;
554         // Send a message to trigger connection completed
555         connCompletedEvent = new LeAudioStackEvent(
556                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
557         connCompletedEvent.device = mLeftDevice;
558         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED;
559         mService.messageFromNative(connCompletedEvent);
560 
561         // Verify the connection state broadcast, and that we are in Connected state
562         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_CONNECTED,
563                 BluetoothProfile.STATE_CONNECTING);
564         assertThat(mService.getConnectionState(mLeftDevice))
565                 .isEqualTo(BluetoothProfile.STATE_CONNECTED);
566 
567         // Send a message to trigger connection completed for right side
568         connCompletedEvent = new LeAudioStackEvent(
569                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
570         connCompletedEvent.device = mRightDevice;
571         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED;
572         mService.messageFromNative(connCompletedEvent);
573 
574         // Verify the connection state broadcast, and that we are in Connected state for right side
575         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_CONNECTED,
576                 BluetoothProfile.STATE_CONNECTING);
577         assertThat(mService.getConnectionState(mRightDevice))
578                 .isEqualTo(BluetoothProfile.STATE_CONNECTED);
579 
580         // Verify the list of connected devices
581         assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isTrue();
582         assertThat(mService.getConnectedDevices().contains(mRightDevice)).isTrue();
583 
584         // Send a disconnect request
585         assertWithMessage("Disconnect failed").that(mService.disconnect(mLeftDevice)).isTrue();
586         assertWithMessage("Disconnect failed").that(mService.disconnect(mRightDevice)).isTrue();
587 
588         // Verify the connection state broadcast, and that we are in Disconnecting state
589         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING,
590                 BluetoothProfile.STATE_CONNECTED);
591         assertThat(BluetoothProfile.STATE_DISCONNECTING)
592                 .isEqualTo(mService.getConnectionState(mLeftDevice));
593         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTING,
594                 BluetoothProfile.STATE_CONNECTED);
595         assertThat(BluetoothProfile.STATE_DISCONNECTING)
596                 .isEqualTo(mService.getConnectionState(mRightDevice));
597 
598         // Send a message to trigger disconnection completed
599         connCompletedEvent = new LeAudioStackEvent(
600                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
601         connCompletedEvent.device = mLeftDevice;
602         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED;
603         mService.messageFromNative(connCompletedEvent);
604 
605         // Verify the connection state broadcast, and that we are in Disconnected state
606         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
607                 BluetoothProfile.STATE_DISCONNECTING);
608         assertThat(BluetoothProfile.STATE_DISCONNECTED)
609                 .isEqualTo(mService.getConnectionState(mLeftDevice));
610 
611         // Send a message to trigger disconnection completed to the right device
612         connCompletedEvent = new LeAudioStackEvent(
613                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
614         connCompletedEvent.device = mRightDevice;
615         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED;
616         mService.messageFromNative(connCompletedEvent);
617 
618         // Verify the connection state broadcast, and that we are in Disconnected state
619         verifyConnectionStateIntent(TIMEOUT_MS, mRightDevice, BluetoothProfile.STATE_DISCONNECTED,
620                 BluetoothProfile.STATE_DISCONNECTING);
621         assertThat(BluetoothProfile.STATE_DISCONNECTED)
622                 .isEqualTo(mService.getConnectionState(mRightDevice));
623 
624         // Verify the list of connected devices
625         assertThat(mService.getConnectedDevices().contains(mLeftDevice)).isFalse();
626         assertThat(mService.getConnectedDevices().contains(mRightDevice)).isFalse();
627     }
628 
629     /**
630      * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Le Audio stack
631      * events will create a state machine.
632      */
633     @Test
testCreateStateMachineStackEvents()634     public void testCreateStateMachineStackEvents() {
635         // Update the device priority so okToConnect() returns true
636         when(mDatabaseManager
637                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
638                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
639         when(mDatabaseManager
640                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO))
641                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
642         when(mDatabaseManager
643                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
644                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
645         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
646         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
647 
648         // Create device descriptor with connect request
649         assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
650 
651         // Le Audio stack event: CONNECTION_STATE_CONNECTING - state machine should be created
652         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
653                 BluetoothProfile.STATE_DISCONNECTED);
654         assertThat(BluetoothProfile.STATE_CONNECTING)
655                 .isEqualTo(mService.getConnectionState(mLeftDevice));
656         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
657 
658         // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
659         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
660                 BluetoothProfile.STATE_CONNECTING);
661         assertThat(BluetoothProfile.STATE_DISCONNECTED)
662                 .isEqualTo(mService.getConnectionState(mLeftDevice));
663         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
664         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
665         assertThat(mService.getDevices().contains(mLeftDevice)).isFalse();
666 
667         // Remove bond will remove also device descriptor. Device has to be connected again
668         assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
669         verifyConnectionStateIntent(LeAudioStateMachine.sConnectTimeoutMs * 2,
670                 mLeftDevice, BluetoothProfile.STATE_CONNECTING,
671                 BluetoothProfile.STATE_DISCONNECTED);
672 
673         // stack event: CONNECTION_STATE_CONNECTED - state machine should be created
674         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED,
675                 BluetoothProfile.STATE_CONNECTING);
676         assertThat(BluetoothProfile.STATE_CONNECTED)
677                 .isEqualTo(mService.getConnectionState(mLeftDevice));
678         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
679 
680         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
681         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
682                 BluetoothProfile.STATE_CONNECTED);
683         assertThat(BluetoothProfile.STATE_DISCONNECTED)
684                 .isEqualTo(mService.getConnectionState(mLeftDevice));
685         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
686         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
687         assertThat(mService.getDevices().contains(mLeftDevice)).isFalse();
688 
689         // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created
690         generateUnexpectedConnectionMessageFromNative(mLeftDevice,
691                 BluetoothProfile.STATE_DISCONNECTING,
692                 BluetoothProfile.STATE_DISCONNECTED);
693         assertThat(BluetoothProfile.STATE_DISCONNECTED)
694                 .isEqualTo(mService.getConnectionState(mLeftDevice));
695         assertThat(mService.getDevices().contains(mLeftDevice)).isFalse();
696 
697         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created
698         generateUnexpectedConnectionMessageFromNative(mLeftDevice,
699                 BluetoothProfile.STATE_DISCONNECTED,
700                 BluetoothProfile.STATE_DISCONNECTED);
701         assertThat(BluetoothProfile.STATE_DISCONNECTED)
702                 .isEqualTo(mService.getConnectionState(mLeftDevice));
703         assertThat(mService.getDevices().contains(mLeftDevice)).isFalse();
704     }
705 
706     /**
707      * Test that a state machine in DISCONNECTED state is removed only after the device is unbond.
708      */
709     @Test
testDeleteStateMachineUnbondEvents()710     public void testDeleteStateMachineUnbondEvents() {
711         // Update the device priority so okToConnect() returns true
712         when(mDatabaseManager
713                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
714                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
715         when(mDatabaseManager
716                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO))
717                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
718         when(mDatabaseManager
719                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
720                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
721         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
722         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
723 
724         // Create device descriptor with connect request
725         assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
726 
727         // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine should be created
728         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
729                 BluetoothProfile.STATE_DISCONNECTED);
730         assertThat(mService.getConnectionState(mLeftDevice))
731                 .isEqualTo(BluetoothProfile.STATE_CONNECTING);
732         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
733         // Device unbond - state machine is not removed
734         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
735         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
736         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
737                 BluetoothProfile.STATE_CONNECTING);
738 
739         // LeAudio stack event: CONNECTION_STATE_CONNECTED - state machine is not removed
740         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED);
741         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTED,
742                 BluetoothProfile.STATE_DISCONNECTED);
743         assertThat(mService.getConnectionState(mLeftDevice))
744                 .isEqualTo(BluetoothProfile.STATE_CONNECTED);
745         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
746         // Device unbond - state machine is not removed
747         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
748         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
749         verifyConnectionStateIntent(TIMEOUT_MS, mLeftDevice, BluetoothProfile.STATE_DISCONNECTING,
750                 BluetoothProfile.STATE_CONNECTED);
751         assertThat(mService.getConnectionState(mLeftDevice))
752                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
753         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
754 
755         // LeAudio stack event: CONNECTION_STATE_DISCONNECTING - state machine is not removed
756         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED);
757         assertThat(mService.getConnectionState(mLeftDevice))
758                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTING);
759         // Device unbond - state machine is not removed
760         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
761         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
762 
763         // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed
764         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_BONDED);
765         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
766                 BluetoothProfile.STATE_DISCONNECTING);
767         assertThat(mService.getConnectionState(mLeftDevice))
768                 .isEqualTo(BluetoothProfile.STATE_DISCONNECTED);
769         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
770         // Device unbond - state machine is removed
771         mService.bondStateChanged(mLeftDevice, BluetoothDevice.BOND_NONE);
772         assertThat(mService.getDevices().contains(mLeftDevice)).isFalse();
773     }
774 
775     /**
776      * Test that a CONNECTION_STATE_DISCONNECTED Le Audio stack event will remove the state
777      * machine only if the device is unbond.
778      */
779     @Test
testDeleteStateMachineDisconnectEvents()780     public void testDeleteStateMachineDisconnectEvents() {
781         // Update the device priority so okToConnect() returns true
782         when(mDatabaseManager
783                 .getProfileConnectionPolicy(mLeftDevice, BluetoothProfile.LE_AUDIO))
784                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
785         when(mDatabaseManager
786                 .getProfileConnectionPolicy(mRightDevice, BluetoothProfile.LE_AUDIO))
787                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
788         when(mDatabaseManager
789                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
790                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
791         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
792         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
793 
794         // Create device descriptor with connect request
795         assertWithMessage("Connect failed").that(mService.connect(mLeftDevice)).isTrue();
796 
797         // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine should be created
798         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
799                 BluetoothProfile.STATE_DISCONNECTED);
800         assertThat(BluetoothProfile.STATE_CONNECTING)
801                 .isEqualTo(mService.getConnectionState(mLeftDevice));
802         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
803 
804         // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed
805         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
806                 BluetoothProfile.STATE_CONNECTING);
807         assertThat(BluetoothProfile.STATE_DISCONNECTED)
808                 .isEqualTo(mService.getConnectionState(mLeftDevice));
809         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
810 
811         // LeAudio stack event: CONNECTION_STATE_CONNECTING - state machine remains
812         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_CONNECTING,
813                 BluetoothProfile.STATE_DISCONNECTED);
814         assertThat(BluetoothProfile.STATE_CONNECTING)
815                 .isEqualTo(mService.getConnectionState(mLeftDevice));
816         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
817 
818         // Device bond state marked as unbond - state machine is not removed
819         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService)
820                 .getBondState(any(BluetoothDevice.class));
821         assertThat(mService.getDevices().contains(mLeftDevice)).isTrue();
822 
823         // LeAudio stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed
824         generateConnectionMessageFromNative(mLeftDevice, BluetoothProfile.STATE_DISCONNECTED,
825                 BluetoothProfile.STATE_CONNECTING);
826         assertThat(BluetoothProfile.STATE_DISCONNECTED)
827                 .isEqualTo(mService.getConnectionState(mLeftDevice));
828         assertThat(mService.getDevices().contains(mLeftDevice)).isFalse();
829     }
830 
connectDevice(BluetoothDevice device)831     private void connectDevice(BluetoothDevice device) {
832         LeAudioStackEvent connCompletedEvent;
833 
834         List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices();
835 
836         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO))
837                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
838         doReturn(true).when(mNativeInterface).connectLeAudio(device);
839         doReturn(true).when(mNativeInterface).disconnectLeAudio(device);
840 
841         // Send a connect request
842         assertWithMessage("Connect failed").that(mService.connect(device)).isTrue();
843 
844         // Verify the connection state broadcast, and that we are in Connecting state
845         verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTING,
846                 BluetoothProfile.STATE_DISCONNECTED);
847         assertThat(BluetoothProfile.STATE_CONNECTING)
848                 .isEqualTo(mService.getConnectionState(device));
849 
850         // Send a message to trigger connection completed
851         connCompletedEvent = new LeAudioStackEvent(
852                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
853         connCompletedEvent.device = device;
854         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED;
855         mService.messageFromNative(connCompletedEvent);
856 
857         // Verify the connection state broadcast, and that we are in Connected state
858         verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTED,
859                 BluetoothProfile.STATE_CONNECTING);
860         assertThat(BluetoothProfile.STATE_CONNECTED)
861                 .isEqualTo(mService.getConnectionState(device));
862 
863         // Verify that the device is in the list of connected devices
864         assertThat(mService.getConnectedDevices().contains(device)).isTrue();
865         // Verify the list of previously connected devices
866         for (BluetoothDevice prevDevice : prevConnectedDevices) {
867             assertThat(mService.getConnectedDevices().contains(prevDevice)).isTrue();
868         }
869     }
870 
generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)871     private void generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState,
872             int oldConnectionState) {
873         LeAudioStackEvent stackEvent =
874                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
875         stackEvent.device = device;
876         stackEvent.valueInt1 = newConnectionState;
877         mService.messageFromNative(stackEvent);
878         // Verify the connection state broadcast
879         verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState);
880     }
881 
generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)882     private void generateUnexpectedConnectionMessageFromNative(BluetoothDevice device,
883             int newConnectionState, int oldConnectionState) {
884         LeAudioStackEvent stackEvent =
885                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
886         stackEvent.device = device;
887         stackEvent.valueInt1 = newConnectionState;
888         mService.messageFromNative(stackEvent);
889         // Verify the connection state broadcast
890         verifyNoConnectionStateIntent(TIMEOUT_MS, device);
891     }
892 
generateGroupNodeAdded(BluetoothDevice device, int groupId)893     private void generateGroupNodeAdded(BluetoothDevice device, int groupId) {
894         LeAudioStackEvent nodeGroupAdded =
895         new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
896         nodeGroupAdded.device = device;
897         nodeGroupAdded.valueInt1 = groupId;
898         nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED;
899         mService.messageFromNative(nodeGroupAdded);
900     }
901 
generateGroupNodeRemoved(BluetoothDevice device, int groupId)902     private void generateGroupNodeRemoved(BluetoothDevice device, int groupId) {
903         LeAudioStackEvent nodeGroupRemoved =
904         new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
905         nodeGroupRemoved.device = device;
906         nodeGroupRemoved.valueInt1 = groupId;
907         nodeGroupRemoved.valueInt2 = LeAudioStackEvent.GROUP_NODE_REMOVED;
908         mService.messageFromNative(nodeGroupRemoved);
909     }
910 
verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)911     private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) {
912         Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device));
913         assertThat(intent).isNull();
914     }
915 
916     /**
917      * Test setting connection policy
918      */
919     @Test
testSetConnectionPolicy()920     public void testSetConnectionPolicy() {
921         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
922         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
923         doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(BluetoothDevice.class),
924                 anyInt(), anyInt());
925         when(mVolumeControlService.setConnectionPolicy(any(), anyInt())).thenReturn(true);
926         when(mCsipSetCoordinatorService.setConnectionPolicy(any(), anyInt())).thenReturn(true);
927         when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
928                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
929 
930         assertThat(mService.setConnectionPolicy(mSingleDevice,
931                 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue();
932 
933         // Verify connection policy for CSIP and VCP are also set
934         verify(mVolumeControlService, times(1)).setConnectionPolicy(
935                 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
936         verify(mCsipSetCoordinatorService, times(1)).setConnectionPolicy(
937                 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_ALLOWED);
938 
939         // Verify the connection state broadcast, and that we are in Connecting state
940         verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_CONNECTING,
941                 BluetoothProfile.STATE_DISCONNECTED);
942         assertThat(BluetoothProfile.STATE_CONNECTING)
943                 .isEqualTo(mService.getConnectionState(mSingleDevice));
944 
945         LeAudioStackEvent connCompletedEvent;
946         // Send a message to trigger connection completed
947         connCompletedEvent = new LeAudioStackEvent(
948                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
949         connCompletedEvent.device = mSingleDevice;
950         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED;
951         mService.messageFromNative(connCompletedEvent);
952 
953         // Verify the connection state broadcast, and that we are in Connected state
954         verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_CONNECTED,
955                 BluetoothProfile.STATE_CONNECTING);
956         assertThat(BluetoothProfile.STATE_CONNECTED)
957                 .isEqualTo(mService.getConnectionState(mSingleDevice));
958 
959         // Set connection policy to forbidden
960         assertThat(mService.setConnectionPolicy(mSingleDevice,
961                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue();
962 
963         // Verify connection policy for CSIP and VCP are also set
964         verify(mVolumeControlService, times(1)).setConnectionPolicy(
965                 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
966         verify(mCsipSetCoordinatorService, times(1)).setConnectionPolicy(
967                 mSingleDevice, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
968 
969         // Verify the connection state broadcast, and that we are in Connecting state
970         verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_DISCONNECTING,
971                 BluetoothProfile.STATE_CONNECTED);
972         assertThat(BluetoothProfile.STATE_DISCONNECTING)
973                 .isEqualTo(mService.getConnectionState(mSingleDevice));
974 
975         // Send a message to trigger disconnection completed
976         connCompletedEvent = new LeAudioStackEvent(
977                 LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
978         connCompletedEvent.device = mSingleDevice;
979         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_DISCONNECTED;
980         mService.messageFromNative(connCompletedEvent);
981 
982         // Verify the connection state broadcast, and that we are in Disconnected state
983         verifyConnectionStateIntent(TIMEOUT_MS, mSingleDevice, BluetoothProfile.STATE_DISCONNECTED,
984                 BluetoothProfile.STATE_DISCONNECTING);
985         assertThat(BluetoothProfile.STATE_DISCONNECTED)
986                 .isEqualTo(mService.getConnectionState(mSingleDevice));
987     }
988 
989     /**
990      *  Helper function to connect Test device
991      *
992      *  @param device test device
993      */
connectTestDevice(BluetoothDevice device, int GroupId)994     private void connectTestDevice(BluetoothDevice device, int GroupId) {
995         List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices();
996 
997         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.LE_AUDIO))
998                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
999         // Send a connect request
1000         assertWithMessage("Connect failed").that(mService.connect(device)).isTrue();
1001 
1002         // Make device bonded
1003         mBondedDevices.add(device);
1004 
1005         // Wait ASYNC_CALL_TIMEOUT_MILLIS for state to settle, timing is also tested here and
1006         // 250ms for processing two messages should be way more than enough. Anything that breaks
1007         // this indicate some breakage in other part of Android OS
1008 
1009         verifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1010                 BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
1011         assertThat(BluetoothProfile.STATE_CONNECTING)
1012                 .isEqualTo(mService.getConnectionState(device));
1013 
1014         // Use connected event to indicate that device is connected
1015         LeAudioStackEvent connCompletedEvent =
1016                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1017         connCompletedEvent.device = device;
1018         connCompletedEvent.valueInt1 = LeAudioStackEvent.CONNECTION_STATE_CONNECTED;
1019         mService.messageFromNative(connCompletedEvent);
1020 
1021         verifyConnectionStateIntent(ASYNC_CALL_TIMEOUT_MILLIS, device,
1022                 BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_CONNECTING);
1023 
1024         assertThat(BluetoothProfile.STATE_CONNECTED)
1025                 .isEqualTo(mService.getConnectionState(device));
1026 
1027         LeAudioStackEvent nodeGroupAdded =
1028                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
1029         nodeGroupAdded.device = device;
1030         nodeGroupAdded.valueInt1 = GroupId;
1031         nodeGroupAdded.valueInt2 = LeAudioStackEvent.GROUP_NODE_ADDED;
1032         mService.messageFromNative(nodeGroupAdded);
1033 
1034         // Verify that the device is in the list of connected devices
1035         assertThat(mService.getConnectedDevices().contains(device)).isTrue();
1036         // Verify the list of previously connected devices
1037         for (BluetoothDevice prevDevice : prevConnectedDevices) {
1038                 assertThat(mService.getConnectedDevices().contains(prevDevice)).isTrue();
1039         }
1040     }
1041 
1042     /**
1043      * Test adding node
1044      */
1045     @Test
testGroupAddRemoveNode()1046     public void testGroupAddRemoveNode() {
1047         int groupId = 1;
1048 
1049         doReturn(true).when(mNativeInterface).groupAddNode(groupId, mSingleDevice);
1050         doReturn(true).when(mNativeInterface).groupRemoveNode(groupId, mSingleDevice);
1051 
1052         assertThat(mService.groupAddNode(groupId, mSingleDevice)).isTrue();
1053         assertThat(mService.groupRemoveNode(groupId, mSingleDevice)).isTrue();
1054     }
1055 
1056     /**
1057      * Test setting active device group with Ringtone context
1058      */
1059     @Test
testSetActiveDeviceGroup()1060     public void testSetActiveDeviceGroup() {
1061         int groupId = 1;
1062         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
1063         int direction = 1;
1064         int snkAudioLocation = 3;
1065         int srcAudioLocation = 4;
1066         int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;
1067 
1068         // Not connected device
1069         assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
1070 
1071         // Connected device
1072         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1073         connectTestDevice(mSingleDevice, testGroupId);
1074 
1075              // Add location support
1076         LeAudioStackEvent audioConfChangedEvent =
1077              new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
1078         audioConfChangedEvent.device = mSingleDevice;
1079         audioConfChangedEvent.valueInt1 = direction;
1080         audioConfChangedEvent.valueInt2 = groupId;
1081         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1082         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1083         audioConfChangedEvent.valueInt5 = availableContexts;
1084         mService.messageFromNative(audioConfChangedEvent);
1085 
1086         assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
1087         verify(mNativeInterface).groupSetActive(groupId);
1088 
1089         //Set group and device as active
1090         LeAudioStackEvent groupStatusChangedEvent =
1091                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
1092         groupStatusChangedEvent.valueInt1 = groupId;
1093         groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
1094         mService.messageFromNative(groupStatusChangedEvent);
1095 
1096         verify(mTbsService).setInbandRingtoneSupport(mSingleDevice);
1097 
1098         // no active device
1099         assertThat(mService.removeActiveDevice(false)).isTrue();
1100         verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID);
1101 
1102         //Set group and device as inactive active
1103         groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
1104         mService.messageFromNative(groupStatusChangedEvent);
1105 
1106         verify(mTbsService).clearInbandRingtoneSupport(mSingleDevice);
1107     }
1108 
1109     /**
1110      * Test setting active device group without Ringtone context
1111      */
1112     @Test
testSetActiveDeviceGroupWithoutRingtoneContext()1113     public void testSetActiveDeviceGroupWithoutRingtoneContext() {
1114         int groupId = 1;
1115         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
1116         int direction = 1;
1117         int snkAudioLocation = 3;
1118         int srcAudioLocation = 4;
1119         int availableContexts = 5;
1120 
1121         // Not connected device
1122         assertThat(mService.setActiveDevice(mSingleDevice)).isFalse();
1123 
1124         // Connected device
1125         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1126         connectTestDevice(mSingleDevice, testGroupId);
1127 
1128              // Add location support
1129         LeAudioStackEvent audioConfChangedEvent =
1130              new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
1131         audioConfChangedEvent.device = mSingleDevice;
1132         audioConfChangedEvent.valueInt1 = direction;
1133         audioConfChangedEvent.valueInt2 = groupId;
1134         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1135         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1136         audioConfChangedEvent.valueInt5 = availableContexts;
1137         mService.messageFromNative(audioConfChangedEvent);
1138 
1139         assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
1140         verify(mNativeInterface).groupSetActive(groupId);
1141 
1142 
1143         //Set group and device as active
1144         LeAudioStackEvent groupStatusChangedEvent =
1145                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
1146         groupStatusChangedEvent.valueInt1 = groupId;
1147         groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
1148         mService.messageFromNative(groupStatusChangedEvent);
1149 
1150         // no active device
1151         assertThat(mService.removeActiveDevice(false)).isTrue();
1152         verify(mNativeInterface).groupSetActive(BluetoothLeAudio.GROUP_ID_INVALID);
1153 
1154         //Set group and device as inactive active
1155         groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_STATUS_INACTIVE;
1156         mService.messageFromNative(groupStatusChangedEvent);
1157 
1158         verify(mTbsService, times(0)).clearInbandRingtoneSupport(mSingleDevice);
1159     }
1160 
1161     /**
1162      * Test getting active device
1163      */
1164     @Test
testGetActiveDevices()1165     public void testGetActiveDevices() {
1166         int groupId = 1;
1167         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
1168         int direction = 1;
1169         int snkAudioLocation = 3;
1170         int srcAudioLocation = 4;
1171         int availableContexts = 5;
1172         int nodeStatus = LeAudioStackEvent.GROUP_NODE_ADDED;
1173         int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
1174 
1175         // Single active device
1176         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1177         connectTestDevice(mSingleDevice, testGroupId);
1178 
1179         // Add device to group
1180         LeAudioStackEvent nodeStatusChangedEvent =
1181                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
1182         nodeStatusChangedEvent.device = mSingleDevice;
1183         nodeStatusChangedEvent.valueInt1 = groupId;
1184         nodeStatusChangedEvent.valueInt2 = nodeStatus;
1185         mService.messageFromNative(nodeStatusChangedEvent);
1186 
1187         assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
1188 
1189         // Add location support
1190         LeAudioStackEvent audioConfChangedEvent =
1191                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
1192         audioConfChangedEvent.device = mSingleDevice;
1193         audioConfChangedEvent.valueInt1 = direction;
1194         audioConfChangedEvent.valueInt2 = groupId;
1195         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1196         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1197         audioConfChangedEvent.valueInt5 = availableContexts;
1198         mService.messageFromNative(audioConfChangedEvent);
1199 
1200         // Set group and device as active
1201         LeAudioStackEvent groupStatusChangedEvent =
1202                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
1203         groupStatusChangedEvent.device = mSingleDevice;
1204         groupStatusChangedEvent.valueInt1 = groupId;
1205         groupStatusChangedEvent.valueInt2 = groupStatus;
1206         mService.messageFromNative(groupStatusChangedEvent);
1207 
1208         assertThat(mService.getActiveDevices().contains(mSingleDevice)).isTrue();
1209 
1210         // Remove device from group
1211         groupStatusChangedEvent =
1212                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
1213         groupStatusChangedEvent.device = mSingleDevice;
1214         groupStatusChangedEvent.valueInt1 = groupId;
1215         groupStatusChangedEvent.valueInt2 = LeAudioStackEvent.GROUP_NODE_REMOVED;
1216         mService.messageFromNative(groupStatusChangedEvent);
1217 
1218         assertThat(mService.getActiveDevices().contains(mSingleDevice)).isFalse();
1219     }
1220 
injectGroupStatusChange(int groupId, int groupStatus)1221     private void injectGroupStatusChange(int groupId, int groupStatus) {
1222         int eventType = LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED;
1223         LeAudioStackEvent groupStatusChangedEvent = new LeAudioStackEvent(eventType);
1224         groupStatusChangedEvent.valueInt1 = groupId;
1225         groupStatusChangedEvent.valueInt2 = groupStatus;
1226         mService.messageFromNative(groupStatusChangedEvent);
1227     }
1228 
injectAudioConfChanged(int groupId, Integer availableContexts, int direction)1229     private void injectAudioConfChanged(int groupId, Integer availableContexts, int direction) {
1230         int snkAudioLocation = 3;
1231         int srcAudioLocation = 4;
1232         int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED;
1233 
1234         // Add device to group
1235         LeAudioStackEvent audioConfChangedEvent = new LeAudioStackEvent(eventType);
1236         audioConfChangedEvent.device = mSingleDevice;
1237         audioConfChangedEvent.valueInt1 = direction;
1238         audioConfChangedEvent.valueInt2 = groupId;
1239         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1240         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1241         audioConfChangedEvent.valueInt5 = availableContexts;
1242         mService.messageFromNative(audioConfChangedEvent);
1243     }
1244     /**
1245      * Test native interface audio configuration changed message handling
1246      */
1247     @Test
testMessageFromNativeAudioConfChangedActiveGroup()1248     public void testMessageFromNativeAudioConfChangedActiveGroup() {
1249         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1250         connectTestDevice(mSingleDevice, testGroupId);
1251         injectAudioConfChanged(testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA |
1252                          BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3);
1253         injectGroupStatusChange(testGroupId, BluetoothLeAudio.GROUP_STATUS_ACTIVE);
1254 
1255         /* Expect 2 calles to Audio Manager - one for output and second for input as this is
1256          * Conversational use case */
1257         verify(mAudioManager, times(2)).handleBluetoothActiveDeviceChanged(any(), any(),
1258                         any(BluetoothProfileConnectionInfo.class));
1259         /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback
1260         * mAudioManager.onAudioDeviceAdded
1261         */
1262         mService.notifyActiveDeviceChanged(mSingleDevice);
1263 
1264         String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED;
1265         Intent intent = TestUtils.waitForIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice));
1266         assertThat(intent).isNotNull();
1267         assertThat(action).isEqualTo(intent.getAction());
1268         assertThat(mSingleDevice)
1269                 .isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
1270     }
1271     /**
1272      * Test native interface audio configuration changed message handling
1273      */
1274     @Test
testMessageFromNativeAudioConfChangedInactiveGroup()1275     public void testMessageFromNativeAudioConfChangedInactiveGroup() {
1276         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1277         connectTestDevice(mSingleDevice, testGroupId);
1278 
1279         String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED;
1280         Integer contexts = BluetoothLeAudio.CONTEXT_TYPE_MEDIA |
1281         BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL;
1282         injectAudioConfChanged(testGroupId, contexts, 3);
1283 
1284         Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice));
1285         assertThat(intent).isNull();
1286     }
1287     /**
1288      * Test native interface audio configuration changed message handling
1289      */
1290     @Test
testMessageFromNativeAudioConfChangedNoGroupChanged()1291     public void testMessageFromNativeAudioConfChangedNoGroupChanged() {
1292         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1293         connectTestDevice(mSingleDevice, testGroupId);
1294 
1295         String action = BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED;
1296 
1297         injectAudioConfChanged(testGroupId, 0, 3);
1298         Intent intent = TestUtils.waitForNoIntent(TIMEOUT_MS, mDeviceQueueMap.get(mSingleDevice));
1299         assertThat(intent).isNull();
1300     }
1301 
1302 
sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus)1303     private void sendEventAndVerifyIntentForGroupStatusChanged(int groupId, int groupStatus) {
1304 
1305         onGroupStatusCallbackCalled = false;
1306 
1307         IBluetoothLeAudioCallback leAudioCallbacks =
1308         new IBluetoothLeAudioCallback.Stub() {
1309             @Override
1310             public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) {}
1311             @Override
1312             public void onGroupStatusChanged(int gid, int gStatus) {
1313                 onGroupStatusCallbackCalled = true;
1314                 assertThat(gid == groupId).isTrue();
1315                 assertThat(gStatus == groupStatus).isTrue();
1316             }
1317             @Override
1318             public void onGroupNodeAdded(BluetoothDevice device, int gid) {}
1319             @Override
1320             public void onGroupNodeRemoved(BluetoothDevice device, int gid) {}
1321         };
1322 
1323         mService.mLeAudioCallbacks.register(leAudioCallbacks);
1324 
1325         injectGroupStatusChange(groupId, groupStatus);
1326 
1327         TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
1328         assertThat(onGroupStatusCallbackCalled).isTrue();
1329 
1330         onGroupStatusCallbackCalled = false;
1331         mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
1332     }
1333 
1334     /**
1335      * Test native interface group status message handling
1336      */
1337     @Test
testMessageFromNativeGroupStatusChanged()1338     public void testMessageFromNativeGroupStatusChanged() {
1339         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1340         connectTestDevice(mSingleDevice, testGroupId);
1341 
1342         injectAudioConfChanged(testGroupId, BluetoothLeAudio.CONTEXT_TYPE_MEDIA |
1343                                  BluetoothLeAudio.CONTEXT_TYPE_CONVERSATIONAL, 3);
1344 
1345         sendEventAndVerifyIntentForGroupStatusChanged(testGroupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
1346         sendEventAndVerifyIntentForGroupStatusChanged(testGroupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
1347     }
1348 
injectLocalCodecConfigCapaChanged(List<BluetoothLeAudioCodecConfig> inputCodecCapa, List<BluetoothLeAudioCodecConfig> outputCodecCapa)1349     private void injectLocalCodecConfigCapaChanged(List<BluetoothLeAudioCodecConfig> inputCodecCapa,
1350                                                  List<BluetoothLeAudioCodecConfig> outputCodecCapa) {
1351         int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_LOCAL_CODEC_CONFIG_CAPA_CHANGED;
1352 
1353         // Add device to group
1354         LeAudioStackEvent localCodecCapaEvent = new LeAudioStackEvent(eventType);
1355         localCodecCapaEvent.valueCodecList1 = inputCodecCapa;
1356         localCodecCapaEvent.valueCodecList2 =  outputCodecCapa;
1357         mService.messageFromNative(localCodecCapaEvent);
1358     }
1359 
injectGroupCodecConfigChanged(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig, BluetoothLeAudioCodecConfig outputCodecConfig, List<BluetoothLeAudioCodecConfig> inputSelectableCodecConfig, List<BluetoothLeAudioCodecConfig> outputSelectableCodecConfig)1360     private void injectGroupCodecConfigChanged(int groupId, BluetoothLeAudioCodecConfig inputCodecConfig,
1361                                 BluetoothLeAudioCodecConfig outputCodecConfig,
1362                                 List<BluetoothLeAudioCodecConfig> inputSelectableCodecConfig,
1363                                 List<BluetoothLeAudioCodecConfig> outputSelectableCodecConfig) {
1364         int eventType = LeAudioStackEvent.EVENT_TYPE_AUDIO_GROUP_CODEC_CONFIG_CHANGED;
1365 
1366         // Add device to group
1367         LeAudioStackEvent groupCodecConfigChangedEvent = new LeAudioStackEvent(eventType);
1368         groupCodecConfigChangedEvent.valueInt1 = groupId;
1369         groupCodecConfigChangedEvent.valueCodec1 = inputCodecConfig;
1370         groupCodecConfigChangedEvent.valueCodec2 = outputCodecConfig;
1371         groupCodecConfigChangedEvent.valueCodecList1 = inputSelectableCodecConfig;
1372         groupCodecConfigChangedEvent.valueCodecList2 =  outputSelectableCodecConfig;
1373         mService.messageFromNative(groupCodecConfigChangedEvent);
1374     }
1375 
1376     /**
1377      * Test native interface group status message handling
1378      */
1379     @Test
testMessageFromNativeGroupCodecConfigChanged()1380     public void testMessageFromNativeGroupCodecConfigChanged() {
1381         onGroupCodecConfChangedCallbackCalled = false;
1382 
1383         injectLocalCodecConfigCapaChanged(INPUT_CAPABILITIES_CONFIG, OUTPUT_CAPABILITIES_CONFIG);
1384 
1385         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1386         connectTestDevice(mSingleDevice, testGroupId);
1387 
1388         testCodecStatus = new BluetoothLeAudioCodecStatus(LC3_16KHZ_CONFIG,
1389                                 LC3_48KHZ_CONFIG, INPUT_CAPABILITIES_CONFIG,
1390                                 OUTPUT_CAPABILITIES_CONFIG, INPUT_SELECTABLE_CONFIG,
1391                                 OUTPUT_SELECTABLE_CONFIG);
1392 
1393         IBluetoothLeAudioCallback leAudioCallbacks =
1394         new IBluetoothLeAudioCallback.Stub() {
1395             @Override
1396             public void onCodecConfigChanged(int gid, BluetoothLeAudioCodecStatus status) {
1397                 onGroupCodecConfChangedCallbackCalled = true;
1398                 assertThat(status.equals(testCodecStatus)).isTrue();
1399             }
1400             @Override
1401             public void onGroupStatusChanged(int gid, int gStatus) {}
1402             @Override
1403             public void onGroupNodeAdded(BluetoothDevice device, int gid) {}
1404             @Override
1405             public void onGroupNodeRemoved(BluetoothDevice device, int gid) {}
1406         };
1407 
1408         mService.mLeAudioCallbacks.register(leAudioCallbacks);
1409 
1410         injectGroupCodecConfigChanged(testGroupId, LC3_16KHZ_CONFIG, LC3_48KHZ_CONFIG,
1411                                         INPUT_SELECTABLE_CONFIG,
1412                                         OUTPUT_SELECTABLE_CONFIG);
1413 
1414         TestUtils.waitForLooperToFinishScheduledTask(mService.getMainLooper());
1415         assertThat(onGroupCodecConfChangedCallbackCalled).isTrue();
1416 
1417         onGroupCodecConfChangedCallbackCalled = false;
1418         mService.mLeAudioCallbacks.unregister(leAudioCallbacks);
1419     }
1420 
verifyActiveDeviceStateIntent(int timeoutMs, BluetoothDevice device)1421     private void verifyActiveDeviceStateIntent(int timeoutMs, BluetoothDevice device) {
1422         Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device));
1423         assertThat(intent).isNotNull();
1424         assertThat(intent.getAction())
1425                 .isEqualTo(BluetoothLeAudio.ACTION_LE_AUDIO_ACTIVE_DEVICE_CHANGED);
1426         assertThat(device).isEqualTo(intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
1427     }
1428 
1429     /**
1430      * Test native interface group status message handling
1431      */
1432     @Test
testLeadGroupDeviceDisconnects()1433     public void testLeadGroupDeviceDisconnects() {
1434         int groupId = 1;
1435         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
1436         int direction = 1;
1437         int snkAudioLocation = 3;
1438         int srcAudioLocation = 4;
1439         int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;;
1440         int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
1441         BluetoothDevice leadDevice;
1442         BluetoothDevice memberDevice = mLeftDevice;
1443 
1444         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1445         connectTestDevice(mLeftDevice, groupId);
1446         connectTestDevice(mRightDevice, groupId);
1447 
1448         leadDevice = mService.getConnectedGroupLeadDevice(groupId);
1449         if (Objects.equals(leadDevice, mLeftDevice)) {
1450                 memberDevice = mRightDevice;
1451         }
1452 
1453         assertThat(mService.setActiveDevice(leadDevice)).isTrue();
1454 
1455         //Add location support
1456         LeAudioStackEvent audioConfChangedEvent =
1457                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
1458         audioConfChangedEvent.valueInt1 = direction;
1459         audioConfChangedEvent.valueInt2 = groupId;
1460         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1461         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1462         audioConfChangedEvent.valueInt5 = availableContexts;
1463         mService.messageFromNative(audioConfChangedEvent);
1464 
1465         //Set group and device as active
1466         LeAudioStackEvent groupStatusChangedEvent =
1467                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
1468         groupStatusChangedEvent.valueInt1 = groupId;
1469         groupStatusChangedEvent.valueInt2 = groupStatus;
1470         mService.messageFromNative(groupStatusChangedEvent);
1471 
1472         assertThat(mService.getActiveDevices().contains(leadDevice)).isTrue();
1473         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(),
1474                         any(BluetoothProfileConnectionInfo.class));
1475         /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback
1476          * mAudioManager.onAudioDeviceAdded
1477          */
1478         mService.notifyActiveDeviceChanged(leadDevice);
1479         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService).getBondState(leadDevice);
1480         verifyActiveDeviceStateIntent(AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS, leadDevice);
1481         injectNoVerifyDeviceDisconnected(leadDevice);
1482 
1483         // We should not change the audio device
1484         assertThat(mService.getConnectionState(leadDevice))
1485                 .isEqualTo(BluetoothProfile.STATE_CONNECTED);
1486 
1487         injectAndVerifyDeviceDisconnected(memberDevice);
1488 
1489         // Verify the connection state broadcast, and that we are in Connecting state
1490         verifyConnectionStateIntent(TIMEOUT_MS, leadDevice, BluetoothProfile.STATE_DISCONNECTED,
1491                 BluetoothProfile.STATE_CONNECTED);
1492 
1493         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(leadDevice),
1494                 any(BluetoothProfileConnectionInfo.class));
1495 
1496         verify(mTbsService).setInbandRingtoneSupport(mLeftDevice);
1497         verify(mTbsService).setInbandRingtoneSupport(mRightDevice);
1498     }
1499 
1500     /**
1501      * Test native interface group status message handling
1502      */
1503     @Test
testLeadGroupDeviceReconnects()1504     public void testLeadGroupDeviceReconnects() {
1505         int groupId = 1;
1506         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
1507         int direction = 1;
1508         int snkAudioLocation = 3;
1509         int srcAudioLocation = 4;
1510         int availableContexts = 5 + BluetoothLeAudio.CONTEXT_TYPE_RINGTONE;;
1511         int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
1512         BluetoothDevice leadDevice;
1513         BluetoothDevice memberDevice = mLeftDevice;
1514 
1515         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1516         connectTestDevice(mLeftDevice, groupId);
1517         connectTestDevice(mRightDevice, groupId);
1518 
1519         leadDevice = mService.getConnectedGroupLeadDevice(groupId);
1520         if (Objects.equals(leadDevice, mLeftDevice)) {
1521                 memberDevice = mRightDevice;
1522         }
1523 
1524         assertThat(mService.setActiveDevice(leadDevice)).isTrue();
1525 
1526         //Add location support
1527         LeAudioStackEvent audioConfChangedEvent =
1528                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
1529         audioConfChangedEvent.valueInt1 = direction;
1530         audioConfChangedEvent.valueInt2 = groupId;
1531         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1532         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1533         audioConfChangedEvent.valueInt5 = availableContexts;
1534         mService.messageFromNative(audioConfChangedEvent);
1535 
1536         //Set group and device as active
1537         LeAudioStackEvent groupStatusChangedEvent =
1538                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
1539         groupStatusChangedEvent.valueInt1 = groupId;
1540         groupStatusChangedEvent.valueInt2 = groupStatus;
1541         mService.messageFromNative(groupStatusChangedEvent);
1542 
1543         assertThat(mService.getActiveDevices().contains(leadDevice)).isTrue();
1544         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(leadDevice), any(),
1545                         any(BluetoothProfileConnectionInfo.class));
1546         /* Since LeAudioService called AudioManager - assume Audio manager calles properly callback
1547          * mAudioManager.onAudioDeviceAdded
1548          */
1549         mService.notifyActiveDeviceChanged(leadDevice);
1550 
1551         verifyActiveDeviceStateIntent(AUDIO_MANAGER_DEVICE_ADD_TIMEOUT_MS, leadDevice);
1552         /* We don't want to distribute DISCONNECTION event, instead will try to reconnect
1553          * (in native)
1554          */
1555         injectNoVerifyDeviceDisconnected(leadDevice);
1556         assertThat(mService.getConnectionState(leadDevice))
1557                 .isEqualTo(BluetoothProfile.STATE_CONNECTED);
1558 
1559         /* Reconnect device, there should be no intent about that, as device was pretending
1560          * connected
1561          */
1562         injectNoVerifyDeviceConnected(leadDevice);
1563 
1564         injectAndVerifyDeviceDisconnected(memberDevice);
1565         injectAndVerifyDeviceDisconnected(leadDevice);
1566 
1567         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), eq(leadDevice),
1568                 any(BluetoothProfileConnectionInfo.class));
1569 
1570         verify(mTbsService).setInbandRingtoneSupport(mLeftDevice);
1571         verify(mTbsService).setInbandRingtoneSupport(mRightDevice);
1572     }
1573 
1574     /**
1575      * Test volume caching for the group
1576      */
1577     @Test
testVolumeCache()1578     public void testVolumeCache() {
1579         int groupId = 1;
1580         int volume = 100;
1581         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 */
1582         int direction = 1;
1583         int availableContexts = 4;
1584 
1585         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1586         connectTestDevice(mLeftDevice, groupId);
1587         connectTestDevice(mRightDevice, groupId);
1588 
1589         assertThat(mService.setActiveDevice(mLeftDevice)).isTrue();
1590 
1591         ArgumentCaptor<BluetoothProfileConnectionInfo> profileInfo =
1592                         ArgumentCaptor.forClass(BluetoothProfileConnectionInfo.class);
1593 
1594         //Add location support.
1595         injectAudioConfChanged(groupId, availableContexts, direction);
1596 
1597         doReturn(-1).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId);
1598         //Set group and device as active.
1599         injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
1600 
1601         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(any(), eq(null),
1602                         profileInfo.capture());
1603         assertThat(profileInfo.getValue().getVolume()).isEqualTo(-1);
1604 
1605         mService.setVolume(volume);
1606         verify(mVolumeControlService, times(1)).setGroupVolume(groupId, volume);
1607 
1608         // Set group to inactive.
1609         injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_INACTIVE);
1610 
1611         verify(mAudioManager, times(1)).handleBluetoothActiveDeviceChanged(eq(null), any(),
1612                         any(BluetoothProfileConnectionInfo.class));
1613 
1614         doReturn(100).when(mVolumeControlService).getAudioDeviceGroupVolume(groupId);
1615 
1616         //Set back to active and check if last volume is restored.
1617         injectGroupStatusChange(groupId, LeAudioStackEvent.GROUP_STATUS_ACTIVE);
1618 
1619         verify(mAudioManager, times(2)).handleBluetoothActiveDeviceChanged(any(), eq(null),
1620                         profileInfo.capture());
1621 
1622         assertThat(profileInfo.getValue().getVolume()).isEqualTo(volume);
1623     }
1624 
1625     @Test
testGetAudioDeviceGroupVolume_whenVolumeControlServiceIsNull()1626     public void testGetAudioDeviceGroupVolume_whenVolumeControlServiceIsNull() {
1627         mService.mVolumeControlService = null;
1628         doReturn(null).when(mServiceFactory).getVolumeControlService();
1629 
1630         int groupId = 1;
1631         assertThat(mService.getAudioDeviceGroupVolume(groupId)).isEqualTo(-1);
1632     }
1633 
1634     @Test
testGetAudioLocation()1635     public void testGetAudioLocation() {
1636         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1637         connectTestDevice(mSingleDevice, testGroupId);
1638 
1639         assertThat(mService.getAudioLocation(null))
1640                 .isEqualTo(BluetoothLeAudio.AUDIO_LOCATION_INVALID);
1641 
1642         int sinkAudioLocation = 10;
1643         LeAudioStackEvent stackEvent =
1644                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_SINK_AUDIO_LOCATION_AVAILABLE);
1645         stackEvent.device = mSingleDevice;
1646         stackEvent.valueInt1 = sinkAudioLocation;
1647         mService.messageFromNative(stackEvent);
1648 
1649         assertThat(mService.getAudioLocation(mSingleDevice)).isEqualTo(sinkAudioLocation);
1650     }
1651 
1652     @Test
testGetConnectedPeerDevices()1653     public void testGetConnectedPeerDevices() {
1654         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1655         connectTestDevice(mLeftDevice, testGroupId);
1656         connectTestDevice(mRightDevice, testGroupId);
1657 
1658         List<BluetoothDevice> peerDevices = mService.getConnectedPeerDevices(testGroupId);
1659         assertThat(peerDevices.contains(mLeftDevice)).isTrue();
1660         assertThat(peerDevices.contains(mRightDevice)).isTrue();
1661     }
1662 
1663     @Test
testGetDevicesMatchingConnectionStates()1664     public void testGetDevicesMatchingConnectionStates() {
1665         assertThat(mService.getDevicesMatchingConnectionStates(null)).isEmpty();
1666 
1667         int[] states = new int[] { BluetoothProfile.STATE_CONNECTED };
1668         doReturn(null).when(mAdapterService).getBondedDevices();
1669         assertThat(mService.getDevicesMatchingConnectionStates(states)).isEmpty();
1670 
1671         doReturn(new BluetoothDevice[] { mSingleDevice }).when(mAdapterService).getBondedDevices();
1672         assertThat(mService.getDevicesMatchingConnectionStates(states)).isEmpty();
1673     }
1674 
1675     @Test
testDefaultValuesOfSeveralGetters()1676     public void testDefaultValuesOfSeveralGetters() {
1677         assertThat(mService.getMaximumNumberOfBroadcasts()).isEqualTo(1);
1678         assertThat(mService.getMaximumStreamsPerBroadcast()).isEqualTo(1);
1679         assertThat(mService.getMaximumSubgroupsPerBroadcast()).isEqualTo(1);
1680         assertThat(mService.isPlaying(100)).isFalse();
1681         assertThat(mService.isValidDeviceGroup(LE_AUDIO_GROUP_ID_INVALID)).isFalse();
1682     }
1683 
1684     @Test
testHandleGroupIdleDuringCall()1685     public void testHandleGroupIdleDuringCall() {
1686         BluetoothDevice headsetDevice = TestUtils.getTestDevice(mAdapter, 5);
1687         HeadsetService headsetService = Mockito.mock(HeadsetService.class);
1688         when(mServiceFactory.getHeadsetService()).thenReturn(headsetService);
1689 
1690         mService.mHfpHandoverDevice = null;
1691         mService.handleGroupIdleDuringCall();
1692         verify(headsetService, never()).getActiveDevice();
1693 
1694         mService.mHfpHandoverDevice = headsetDevice;
1695         when(headsetService.getActiveDevice()).thenReturn(headsetDevice);
1696         mService.handleGroupIdleDuringCall();
1697         verify(headsetService).connectAudio();
1698         assertThat(mService.mHfpHandoverDevice).isNull();
1699 
1700         mService.mHfpHandoverDevice = headsetDevice;
1701         when(headsetService.getActiveDevice()).thenReturn(null);
1702         mService.handleGroupIdleDuringCall();
1703         verify(headsetService).setActiveDevice(headsetDevice);
1704         assertThat(mService.mHfpHandoverDevice).isNull();
1705     }
1706 
1707     @Test
testDump_doesNotCrash()1708     public void testDump_doesNotCrash() {
1709         doReturn(new ParcelUuid[]{BluetoothUuid.LE_AUDIO}).when(mAdapterService)
1710                 .getRemoteUuids(any(BluetoothDevice.class));
1711         doReturn(new BluetoothDevice[]{mSingleDevice}).when(mAdapterService).getBondedDevices();
1712         when(mDatabaseManager
1713                 .getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
1714                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1715         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1716         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
1717 
1718         connectTestDevice(mSingleDevice, testGroupId);
1719 
1720         StringBuilder sb = new StringBuilder();
1721         mService.dump(sb);
1722     }
1723 
1724     /**
1725      * Test setting authorization for LeAudio device in the McpService
1726      */
1727     @Test
testAuthorizeMcpServiceWhenDeviceConnecting()1728     public void testAuthorizeMcpServiceWhenDeviceConnecting() {
1729         int groupId = 1;
1730 
1731         mService.handleBluetoothEnabled();
1732         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1733         connectTestDevice(mLeftDevice, groupId);
1734         connectTestDevice(mRightDevice, groupId);
1735         verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, true);
1736         verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, true);
1737     }
1738 
1739     /**
1740      * Test setting authorization for LeAudio device in the McpService
1741      */
1742     @Test
testAuthorizeMcpServiceOnBluetoothEnableAndNodeRemoval()1743     public void testAuthorizeMcpServiceOnBluetoothEnableAndNodeRemoval() {
1744         int groupId = 1;
1745 
1746         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1747         connectTestDevice(mLeftDevice, groupId);
1748         connectTestDevice(mRightDevice, groupId);
1749 
1750         generateGroupNodeAdded(mLeftDevice, groupId);
1751         generateGroupNodeAdded(mRightDevice, groupId);
1752 
1753         verify(mMcpService, times(0)).setDeviceAuthorized(mLeftDevice, true);
1754         verify(mMcpService, times(0)).setDeviceAuthorized(mRightDevice, true);
1755 
1756         mService.handleBluetoothEnabled();
1757 
1758         verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, true);
1759         verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, true);
1760 
1761         generateGroupNodeRemoved(mLeftDevice, groupId);
1762         verify(mMcpService, times(1)).setDeviceAuthorized(mLeftDevice, false);
1763 
1764         generateGroupNodeRemoved(mRightDevice, groupId);
1765         verify(mMcpService, times(1)).setDeviceAuthorized(mRightDevice, false);
1766     }
1767 
1768     /**
1769      * Test verifying that when the LE Audio connection policy of a device is set to
1770      * {@link BluetoothProfile#CONNECTION_POLICY_FORBIDDEN}, we unauthorize McpService and
1771      * TbsService. When the LE Audio connection policy is set to
1772      * {@link BluetoothProfile#CONNECTION_POLICY_ALLOWED}, we will authorize these services.
1773      */
1774     @Test
testMcsAndTbsAuthorizationWithConnectionPolicy()1775     public void testMcsAndTbsAuthorizationWithConnectionPolicy() {
1776         int groupId = 1;
1777 
1778         mService.handleBluetoothEnabled();
1779         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1780         doReturn(true).when(mNativeInterface).disconnectLeAudio(any(BluetoothDevice.class));
1781         doReturn(true).when(mDatabaseManager).setProfileConnectionPolicy(any(BluetoothDevice.class),
1782                 anyInt(), anyInt());
1783         when(mDatabaseManager.getProfileConnectionPolicy(mSingleDevice, BluetoothProfile.LE_AUDIO))
1784                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
1785 
1786         // Ensures GATT server services are not authorized when the device does not have a group
1787         assertThat(mService.setConnectionPolicy(mSingleDevice,
1788                 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue();
1789         verify(mMcpService, never()).setDeviceAuthorized(mSingleDevice, false);
1790         verify(mTbsService, never()).setDeviceAuthorized(mSingleDevice, false);
1791 
1792         // Connects the test device and verifies GATT server services are authorized
1793         connectTestDevice(mSingleDevice, groupId);
1794         verify(mMcpService, times(1)).setDeviceAuthorized(mSingleDevice, true);
1795         verify(mTbsService, times(1)).setDeviceAuthorized(mSingleDevice, true);
1796 
1797         // Ensure that disconnecting unauthorizes GATT server services
1798         assertThat(mService.setConnectionPolicy(mSingleDevice,
1799                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN)).isTrue();
1800         verify(mMcpService, times(1)).setDeviceAuthorized(mSingleDevice, false);
1801         verify(mTbsService, times(1)).setDeviceAuthorized(mSingleDevice, false);
1802 
1803         // Connecting a device that has a group re-authorizes the GATT server services
1804         assertThat(mService.setConnectionPolicy(mSingleDevice,
1805                 BluetoothProfile.CONNECTION_POLICY_ALLOWED)).isTrue();
1806         verify(mMcpService, times(2)).setDeviceAuthorized(mSingleDevice, true);
1807         verify(mTbsService, times(2)).setDeviceAuthorized(mSingleDevice, true);
1808     }
1809 
1810     @Test
testGetGroupDevices()1811     public void testGetGroupDevices() {
1812         int firstGroupId = 1;
1813         int secondGroupId = 2;
1814 
1815         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1816         connectTestDevice(mLeftDevice, firstGroupId);
1817         connectTestDevice(mRightDevice, firstGroupId);
1818         connectTestDevice(mSingleDevice, secondGroupId);
1819 
1820         // Checks group device lists for groupId 1
1821         List<BluetoothDevice> firstGroupDevicesById = mService.getGroupDevices(firstGroupId);
1822         List<BluetoothDevice> firstGroupDevicesByLeftDevice = mService.getGroupDevices(mLeftDevice);
1823         List<BluetoothDevice> firstGroupDevicesByRightDevice = mService.getGroupDevices(
1824                 mRightDevice);
1825 
1826         assertThat(firstGroupDevicesById.size()).isEqualTo(2);
1827         assertThat(firstGroupDevicesById.contains(mLeftDevice)).isTrue();
1828         assertThat(firstGroupDevicesById.contains(mRightDevice)).isTrue();
1829         assertThat(firstGroupDevicesById.contains(mSingleDevice)).isFalse();
1830         assertThat(firstGroupDevicesById.equals(firstGroupDevicesByLeftDevice)).isTrue();
1831         assertThat(firstGroupDevicesById.equals(firstGroupDevicesByRightDevice)).isTrue();
1832 
1833         // Checks group device lists for groupId 2
1834         List<BluetoothDevice> secondGroupDevicesById = mService.getGroupDevices(secondGroupId);
1835         List<BluetoothDevice> secondGroupDevicesByDevice = mService.getGroupDevices(mSingleDevice);
1836 
1837         assertThat(secondGroupDevicesById.size()).isEqualTo(1);
1838         assertThat(secondGroupDevicesById.contains(mSingleDevice)).isTrue();
1839         assertThat(secondGroupDevicesById.contains(mLeftDevice)).isFalse();
1840         assertThat(secondGroupDevicesById.contains(mRightDevice)).isFalse();
1841         assertThat(secondGroupDevicesById.equals(secondGroupDevicesByDevice)).isTrue();
1842     }
1843 
1844     /**
1845      * Tests that {@link LeAudioService#sendPreferredAudioProfileChangeToAudioFramework()} sends
1846      * requests to the audio framework for each active LEA device.
1847      */
1848     @Test
testSendPreferredAudioProfileChangeToAudioFramework()1849     public void testSendPreferredAudioProfileChangeToAudioFramework() {
1850         when(mAdapterService.isAllSupportedClassicAudioProfilesActive(any())).thenReturn(true);
1851 
1852         // TEST 1: Verify no requests are sent to the audio framework if there is no active device
1853         assertThat(mService.removeActiveDevice(false)).isTrue();
1854         List<BluetoothDevice> activeDevices = mService.getActiveDevices();
1855         assertThat(activeDevices.get(0)).isNull();
1856         assertThat(activeDevices.get(1)).isNull();
1857         assertThat(mService.sendPreferredAudioProfileChangeToAudioFramework()).isEqualTo(0);
1858 
1859         // TEST 2: Verify we send one request for each active direction
1860         int groupId = 1;
1861         /* AUDIO_DIRECTION_OUTPUT_BIT = 0x01 | AUDIO_DIRECTION_INPUT_BIT = 0x02; */
1862         int direction = 3;
1863         int snkAudioLocation = 3;
1864         int srcAudioLocation = 4;
1865         int availableContexts = 5;
1866         int nodeStatus = LeAudioStackEvent.GROUP_NODE_ADDED;
1867         int groupStatus = LeAudioStackEvent.GROUP_STATUS_ACTIVE;
1868 
1869         // Single active device
1870         doReturn(true).when(mNativeInterface).connectLeAudio(any(BluetoothDevice.class));
1871         connectTestDevice(mSingleDevice, testGroupId);
1872 
1873         // Add device to group
1874         LeAudioStackEvent nodeStatusChangedEvent =
1875                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_NODE_STATUS_CHANGED);
1876         nodeStatusChangedEvent.device = mSingleDevice;
1877         nodeStatusChangedEvent.valueInt1 = groupId;
1878         nodeStatusChangedEvent.valueInt2 = nodeStatus;
1879         mService.messageFromNative(nodeStatusChangedEvent);
1880 
1881         assertThat(mService.setActiveDevice(mSingleDevice)).isTrue();
1882 
1883         // Add location support
1884         LeAudioStackEvent audioConfChangedEvent =
1885                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_AUDIO_CONF_CHANGED);
1886         audioConfChangedEvent.device = mSingleDevice;
1887         audioConfChangedEvent.valueInt1 = direction;
1888         audioConfChangedEvent.valueInt2 = groupId;
1889         audioConfChangedEvent.valueInt3 = snkAudioLocation;
1890         audioConfChangedEvent.valueInt4 = srcAudioLocation;
1891         audioConfChangedEvent.valueInt5 = availableContexts;
1892         mService.messageFromNative(audioConfChangedEvent);
1893 
1894         // Set group and device as active
1895         LeAudioStackEvent groupStatusChangedEvent =
1896                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_GROUP_STATUS_CHANGED);
1897         groupStatusChangedEvent.device = mSingleDevice;
1898         groupStatusChangedEvent.valueInt1 = groupId;
1899         groupStatusChangedEvent.valueInt2 = groupStatus;
1900         mService.messageFromNative(groupStatusChangedEvent);
1901 
1902         assertThat(mService.getActiveDevices().contains(mSingleDevice)).isTrue();
1903         assertThat(mService.sendPreferredAudioProfileChangeToAudioFramework()).isEqualTo(2);
1904     }
1905 }
1906