• 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.vc;
19 
20 import static org.mockito.Mockito.*;
21 
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothProfile;
25 import android.bluetooth.BluetoothUuid;
26 import android.bluetooth.BluetoothVolumeControl;
27 import android.bluetooth.IBluetoothVolumeControlCallback;
28 import android.content.AttributionSource;
29 import android.content.BroadcastReceiver;
30 import android.content.Context;
31 import android.content.Intent;
32 import android.content.IntentFilter;
33 import android.media.AudioManager;
34 import android.os.Binder;
35 import android.os.Looper;
36 import android.os.ParcelUuid;
37 
38 import androidx.test.InstrumentationRegistry;
39 import androidx.test.filters.MediumTest;
40 import androidx.test.rule.ServiceTestRule;
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import com.android.bluetooth.btservice.AdapterService;
44 import com.android.bluetooth.btservice.ServiceFactory;
45 import com.android.bluetooth.btservice.storage.DatabaseManager;
46 import com.android.bluetooth.csip.CsipSetCoordinatorService;
47 import com.android.bluetooth.R;
48 import com.android.bluetooth.TestUtils;
49 import com.android.bluetooth.x.com.android.modules.utils.SynchronousResultReceiver;
50 
51 import org.junit.After;
52 import org.junit.Assert;
53 import org.junit.Assume;
54 import org.junit.Before;
55 import org.junit.Rule;
56 import org.junit.Test;
57 import org.junit.runner.RunWith;
58 import org.mockito.Mock;
59 import org.mockito.Mockito;
60 import org.mockito.MockitoAnnotations;
61 
62 import java.time.Duration;
63 import java.util.HashMap;
64 import java.util.List;
65 import java.util.concurrent.LinkedBlockingQueue;
66 import java.util.concurrent.TimeoutException;
67 import java.util.stream.IntStream;
68 
69 @MediumTest
70 @RunWith(AndroidJUnit4.class)
71 public class VolumeControlServiceTest {
72     private BluetoothAdapter mAdapter;
73     private AttributionSource mAttributionSource;
74     private Context mTargetContext;
75     private VolumeControlService mService;
76     private VolumeControlService.BluetoothVolumeControlBinder mServiceBinder;
77     private BluetoothDevice mDevice;
78     private BluetoothDevice mDeviceTwo;
79     private HashMap<BluetoothDevice, LinkedBlockingQueue<Intent>> mDeviceQueueMap;
80     private static final int TIMEOUT_MS = 1000;
81     private static final int BT_LE_AUDIO_MAX_VOL = 255;
82     private static final int MEDIA_MIN_VOL = 0;
83     private static final int MEDIA_MAX_VOL = 25;
84     private static final int CALL_MIN_VOL = 1;
85     private static final int CALL_MAX_VOL = 8;
86 
87     private BroadcastReceiver mVolumeControlIntentReceiver;
88 
89     @Mock private AdapterService mAdapterService;
90     @Mock private DatabaseManager mDatabaseManager;
91     @Mock private VolumeControlNativeInterface mNativeInterface;
92     @Mock private AudioManager mAudioManager;
93     @Mock private ServiceFactory mServiceFactory;
94     @Mock private CsipSetCoordinatorService mCsipService;
95 
96     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
97 
98     @Before
setUp()99     public void setUp() throws Exception {
100         mTargetContext = InstrumentationRegistry.getTargetContext();
101         // Set up mocks and test assets
102         MockitoAnnotations.initMocks(this);
103 
104         if (Looper.myLooper() == null) {
105             Looper.prepare();
106         }
107 
108         TestUtils.setAdapterService(mAdapterService);
109         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
110         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
111 
112         mAdapter = BluetoothAdapter.getDefaultAdapter();
113         mAttributionSource = mAdapter.getAttributionSource();
114 
115         doReturn(MEDIA_MIN_VOL).when(mAudioManager)
116                 .getStreamMinVolume(eq(AudioManager.STREAM_MUSIC));
117         doReturn(MEDIA_MAX_VOL).when(mAudioManager)
118                 .getStreamMaxVolume(eq(AudioManager.STREAM_MUSIC));
119         doReturn(CALL_MIN_VOL).when(mAudioManager)
120                 .getStreamMinVolume(eq(AudioManager.STREAM_VOICE_CALL));
121         doReturn(CALL_MAX_VOL).when(mAudioManager)
122                 .getStreamMaxVolume(eq(AudioManager.STREAM_VOICE_CALL));
123 
124         startService();
125         mService.mVolumeControlNativeInterface = mNativeInterface;
126         mService.mAudioManager = mAudioManager;
127         mService.mFactory = mServiceFactory;
128         mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder();
129         mServiceBinder.mIsTesting = true;
130 
131         doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();
132 
133         // Override the timeout value to speed up the test
134         VolumeControlStateMachine.sConnectTimeoutMs = TIMEOUT_MS;    // 1s
135 
136         // Set up the Connection State Changed receiver
137         IntentFilter filter = new IntentFilter();
138         filter.addAction(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED);
139 
140         mVolumeControlIntentReceiver = new VolumeControlIntentReceiver();
141         mTargetContext.registerReceiver(mVolumeControlIntentReceiver, filter);
142 
143         // Get a device for testing
144         mDevice = TestUtils.getTestDevice(mAdapter, 0);
145         mDeviceTwo = TestUtils.getTestDevice(mAdapter, 1);
146         mDeviceQueueMap = new HashMap<>();
147         mDeviceQueueMap.put(mDevice, new LinkedBlockingQueue<>());
148         mDeviceQueueMap.put(mDeviceTwo, new LinkedBlockingQueue<>());
149         doReturn(BluetoothDevice.BOND_BONDED).when(mAdapterService)
150                 .getBondState(any(BluetoothDevice.class));
151         doReturn(new ParcelUuid[]{BluetoothUuid.VOLUME_CONTROL}).when(mAdapterService)
152                 .getRemoteUuids(any(BluetoothDevice.class));
153     }
154 
155     @After
tearDown()156     public void tearDown() throws Exception {
157         if (mService == null) {
158             return;
159         }
160 
161         stopService();
162         mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver);
163         mDeviceQueueMap.clear();
164         TestUtils.clearAdapterService(mAdapterService);
165         reset(mAudioManager);
166     }
167 
startService()168     private void startService() throws TimeoutException {
169         TestUtils.startService(mServiceRule, VolumeControlService.class);
170         mService = VolumeControlService.getVolumeControlService();
171         Assert.assertNotNull(mService);
172     }
173 
stopService()174     private void stopService() throws TimeoutException {
175         TestUtils.stopService(mServiceRule, VolumeControlService.class);
176         mService = VolumeControlService.getVolumeControlService();
177         Assert.assertNull(mService);
178     }
179 
180     private class VolumeControlIntentReceiver extends BroadcastReceiver {
181         @Override
onReceive(Context context, Intent intent)182         public void onReceive(Context context, Intent intent) {
183             try {
184                 BluetoothDevice device = intent.getParcelableExtra(
185                         BluetoothDevice.EXTRA_DEVICE);
186                 Assert.assertNotNull(device);
187                 LinkedBlockingQueue<Intent> queue = mDeviceQueueMap.get(device);
188                 Assert.assertNotNull(queue);
189                 queue.put(intent);
190             } catch (InterruptedException e) {
191                 Assert.fail("Cannot add Intent to the Connection State queue: "
192                         + e.getMessage());
193             }
194         }
195     }
196 
verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device, int newState, int prevState)197     private void verifyConnectionStateIntent(int timeoutMs, BluetoothDevice device,
198             int newState, int prevState) {
199         Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device));
200         Assert.assertNotNull(intent);
201         Assert.assertEquals(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED,
202                 intent.getAction());
203         Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
204         Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
205         Assert.assertEquals(prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE,
206                 -1));
207     }
208 
verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)209     private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) {
210         Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device));
211         Assert.assertNull(intent);
212     }
213 
214     /**
215      * Test getting VolumeControl Service: getVolumeControlService()
216      */
217     @Test
testGetVolumeControlService()218     public void testGetVolumeControlService() {
219         Assert.assertEquals(mService, VolumeControlService.getVolumeControlService());
220     }
221 
222     /**
223      * Test stop VolumeControl Service
224      */
225     @Test
testStopVolumeControlService()226     public void testStopVolumeControlService() throws Exception {
227         // Prepare: connect
228         connectDevice(mDevice);
229         // VolumeControl Service is already running: test stop().
230         // Note: must be done on the main thread
231         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
232             public void run() {
233                 Assert.assertTrue(mService.stop());
234             }
235         });
236         // Try to restart the service. Note: must be done on the main thread
237         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
238             public void run() {
239                 Assert.assertTrue(mService.start());
240             }
241         });
242     }
243 
244     /**
245      * Test get/set policy for BluetoothDevice
246      */
247     @Test
testGetSetPolicy()248     public void testGetSetPolicy() {
249         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
250         when(mDatabaseManager
251                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
252                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
253         Assert.assertEquals("Initial device policy",
254                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
255                 mService.getConnectionPolicy(mDevice));
256 
257         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
258         when(mDatabaseManager
259                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
260                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
261         Assert.assertEquals("Setting device policy to POLICY_FORBIDDEN",
262                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
263                 mService.getConnectionPolicy(mDevice));
264 
265         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
266         when(mDatabaseManager
267                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
268                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
269         Assert.assertEquals("Setting device policy to POLICY_ALLOWED",
270                 BluetoothProfile.CONNECTION_POLICY_ALLOWED,
271                 mService.getConnectionPolicy(mDevice));
272     }
273 
274     /**
275      * Test if getProfileConnectionPolicy works after the service is stopped.
276      */
277     @Test
testGetPolicyAfterStopped()278     public void testGetPolicyAfterStopped() throws Exception {
279         mService.stop();
280         when(mDatabaseManager
281                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
282                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
283         final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
284         int defaultRecvValue = -1000;
285         mServiceBinder.getConnectionPolicy(mDevice, mAttributionSource, recv);
286         int policy = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
287                 .getValue(defaultRecvValue);
288         Assert.assertEquals("Initial device policy",
289                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN, policy);
290     }
291 
292     /**
293      *  Test okToConnect method using various test cases
294      */
295     @Test
testOkToConnect()296     public void testOkToConnect() {
297         int badPolicyValue = 1024;
298         int badBondState = 42;
299         testOkToConnectCase(mDevice,
300                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
301         testOkToConnectCase(mDevice,
302                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
303         testOkToConnectCase(mDevice,
304                 BluetoothDevice.BOND_NONE, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
305         testOkToConnectCase(mDevice,
306                 BluetoothDevice.BOND_NONE, badPolicyValue, false);
307         testOkToConnectCase(mDevice,
308                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
309         testOkToConnectCase(mDevice,
310                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
311         testOkToConnectCase(mDevice,
312                 BluetoothDevice.BOND_BONDING, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
313         testOkToConnectCase(mDevice,
314                 BluetoothDevice.BOND_BONDING, badPolicyValue, false);
315         testOkToConnectCase(mDevice,
316                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, true);
317         testOkToConnectCase(mDevice,
318                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
319         testOkToConnectCase(mDevice,
320                 BluetoothDevice.BOND_BONDED, BluetoothProfile.CONNECTION_POLICY_ALLOWED, true);
321         testOkToConnectCase(mDevice,
322                 BluetoothDevice.BOND_BONDED, badPolicyValue, false);
323         testOkToConnectCase(mDevice,
324                 badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
325         testOkToConnectCase(mDevice,
326                 badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
327         testOkToConnectCase(mDevice,
328                 badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
329         testOkToConnectCase(mDevice,
330                 badBondState, badPolicyValue, false);
331     }
332 
333     /**
334      * Test that an outgoing connection to device that does not have Volume Control UUID is rejected
335      */
336     @Test
testOutgoingConnectMissingVolumeControlUuid()337     public void testOutgoingConnectMissingVolumeControlUuid() {
338         // Update the device policy so okToConnect() returns true
339         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
340         when(mDatabaseManager
341                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
342                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
343         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
344         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
345 
346         // Return No UUID
347         doReturn(new ParcelUuid[]{}).when(mAdapterService)
348                 .getRemoteUuids(any(BluetoothDevice.class));
349 
350         // Send a connect request
351         Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
352     }
353 
354     /**
355      * Test that an outgoing connection to device that have Volume Control UUID is successful
356      */
357     @Test
testOutgoingConnectDisconnectExistingVolumeControlUuid()358     public void testOutgoingConnectDisconnectExistingVolumeControlUuid() throws Exception {
359         // Update the device policy so okToConnect() returns true
360         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
361         when(mDatabaseManager
362                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
363                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
364         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
365         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
366 
367         // Return Volume Control UUID
368         doReturn(new ParcelUuid[]{BluetoothUuid.VOLUME_CONTROL}).when(mAdapterService)
369                 .getRemoteUuids(any(BluetoothDevice.class));
370 
371         // Send a connect request via binder
372         SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
373         mServiceBinder.connect(mDevice, mAttributionSource, recv);
374         Assert.assertTrue("Connect expected to succeed",
375                 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(false));
376 
377         // Verify the connection state broadcast, and that we are in Connecting state
378         verifyConnectionStateIntent(TIMEOUT_MS, mDevice, BluetoothProfile.STATE_CONNECTING,
379                 BluetoothProfile.STATE_DISCONNECTED);
380 
381         // Send a disconnect request via binder
382         recv = SynchronousResultReceiver.get();
383         mServiceBinder.disconnect(mDevice, mAttributionSource, recv);
384         Assert.assertTrue("Disconnect expected to succeed",
385                 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(false));
386 
387         // Verify the connection state broadcast, and that we are in Connecting state
388         verifyConnectionStateIntent(TIMEOUT_MS, mDevice, BluetoothProfile.STATE_DISCONNECTED,
389                 BluetoothProfile.STATE_CONNECTING);
390     }
391 
392     /**
393      * Test that an outgoing connection to device with POLICY_FORBIDDEN is rejected
394      */
395     @Test
testOutgoingConnectPolicyForbidden()396     public void testOutgoingConnectPolicyForbidden() {
397         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
398         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
399 
400         // Set the device policy to POLICY_FORBIDDEN so connect() should fail
401         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
402         when(mDatabaseManager
403                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
404                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
405 
406         // Send a connect request
407         Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
408     }
409 
410     /**
411      * Test that an outgoing connection times out
412      */
413     @Test
testOutgoingConnectTimeout()414     public void testOutgoingConnectTimeout() throws Exception {
415         // Update the device policy so okToConnect() returns true
416         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
417         when(mDatabaseManager
418                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
419                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
420         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
421         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
422 
423         // Send a connect request
424         Assert.assertTrue("Connect failed", mService.connect(mDevice));
425 
426         // Verify the connection state broadcast, and that we are in Connecting state
427         verifyConnectionStateIntent(TIMEOUT_MS, mDevice, BluetoothProfile.STATE_CONNECTING,
428                 BluetoothProfile.STATE_DISCONNECTED);
429         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
430                 mService.getConnectionState(mDevice));
431 
432         // Verify the connection state broadcast, and that we are in Disconnected state
433         verifyConnectionStateIntent(VolumeControlStateMachine.sConnectTimeoutMs * 2,
434                 mDevice, BluetoothProfile.STATE_DISCONNECTED,
435                 BluetoothProfile.STATE_CONNECTING);
436 
437         final SynchronousResultReceiver<Integer> recv = SynchronousResultReceiver.get();
438         int defaultRecvValue = -1000;
439         mServiceBinder.getConnectionState(mDevice, mAttributionSource, recv);
440         int state = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
441                 .getValue(defaultRecvValue);
442         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, state);
443     }
444 
445     /**
446      * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Volume Control stack
447      * events will create a state machine.
448      */
449     @Test
testCreateStateMachineStackEvents()450     public void testCreateStateMachineStackEvents() {
451         // Update the device policy so okToConnect() returns true
452         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
453         when(mDatabaseManager
454                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
455                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
456         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
457         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
458 
459         // stack event: CONNECTION_STATE_CONNECTING - state machine should be created
460         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTING,
461                 BluetoothProfile.STATE_DISCONNECTED);
462         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
463                 mService.getConnectionState(mDevice));
464         Assert.assertTrue(mService.getDevices().contains(mDevice));
465 
466         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
467         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED,
468                 BluetoothProfile.STATE_CONNECTING);
469         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
470                 mService.getConnectionState(mDevice));
471         Assert.assertTrue(mService.getDevices().contains(mDevice));
472         mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE);
473         Assert.assertFalse(mService.getDevices().contains(mDevice));
474 
475         // stack event: CONNECTION_STATE_CONNECTED - state machine should be created
476         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED,
477                 BluetoothProfile.STATE_DISCONNECTED);
478         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
479                 mService.getConnectionState(mDevice));
480         Assert.assertTrue(mService.getDevices().contains(mDevice));
481 
482         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
483         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED,
484                 BluetoothProfile.STATE_CONNECTED);
485         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
486                 mService.getConnectionState(mDevice));
487         Assert.assertTrue(mService.getDevices().contains(mDevice));
488         mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE);
489         Assert.assertFalse(mService.getDevices().contains(mDevice));
490 
491         // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created
492         generateUnexpectedConnectionMessageFromNative(mDevice,
493                 BluetoothProfile.STATE_DISCONNECTING,
494                 BluetoothProfile.STATE_DISCONNECTED);
495         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
496                 mService.getConnectionState(mDevice));
497         Assert.assertFalse(mService.getDevices().contains(mDevice));
498 
499         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created
500         generateUnexpectedConnectionMessageFromNative(mDevice,
501                 BluetoothProfile.STATE_DISCONNECTED,
502                 BluetoothProfile.STATE_DISCONNECTED);
503         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
504                 mService.getConnectionState(mDevice));
505         Assert.assertFalse(mService.getDevices().contains(mDevice));
506     }
507 
508     /**
509      * Test that a CONNECTION_STATE_DISCONNECTED Volume Control stack event will remove the state
510      * machine only if the device is unbond.
511      */
512     @Test
testDeleteStateMachineDisconnectEvents()513     public void testDeleteStateMachineDisconnectEvents() {
514         // Update the device policy so okToConnect() returns true
515         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
516         when(mDatabaseManager
517                 .getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
518                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
519         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
520         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
521 
522         // stack event: CONNECTION_STATE_CONNECTING - state machine should be created
523         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTING,
524                 BluetoothProfile.STATE_DISCONNECTED);
525         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
526                 mService.getConnectionState(mDevice));
527         Assert.assertTrue(mService.getDevices().contains(mDevice));
528 
529         // stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed
530         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED,
531                 BluetoothProfile.STATE_CONNECTING);
532         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
533                 mService.getConnectionState(mDevice));
534         Assert.assertTrue(mService.getDevices().contains(mDevice));
535 
536         // stack event: CONNECTION_STATE_CONNECTING - state machine remains
537         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTING,
538                 BluetoothProfile.STATE_DISCONNECTED);
539         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
540                 mService.getConnectionState(mDevice));
541         Assert.assertTrue(mService.getDevices().contains(mDevice));
542 
543         // device bond state marked as unbond - state machine is not removed
544         doReturn(BluetoothDevice.BOND_NONE).when(mAdapterService)
545                 .getBondState(any(BluetoothDevice.class));
546         Assert.assertTrue(mService.getDevices().contains(mDevice));
547 
548         // stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed
549         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED,
550                 BluetoothProfile.STATE_CONNECTING);
551         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
552                 mService.getConnectionState(mDevice));
553         Assert.assertFalse(mService.getDevices().contains(mDevice));
554     }
555 
556     /**
557      * Test that various Volume Control stack events will broadcast related states.
558      */
559     @Test
testVolumeControlStackEvents()560     public void testVolumeControlStackEvents() {
561         int group_id = -1;
562         int volume = 6;
563         boolean mute = false;
564 
565         // Send a message to trigger volume state changed broadcast
566         VolumeControlStackEvent stackEvent = new VolumeControlStackEvent(
567                 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
568         stackEvent.device = mDevice;
569         stackEvent.valueInt1 = group_id;
570         stackEvent.valueInt2 = volume;
571         stackEvent.valueBool1 = mute;
572         mService.messageFromNative(stackEvent);
573     }
574 
getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType)575     int getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType) {
576         // Note: This has to be the same as mBtHelper.setLeAudioVolume()
577         return (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex);
578     }
579 
testVolumeCalculations(int streamType, int minIdx, int maxIdx)580     void testVolumeCalculations(int streamType, int minIdx, int maxIdx) {
581         // Send a message to trigger volume state changed broadcast
582         final VolumeControlStackEvent stackEvent = new VolumeControlStackEvent(
583                 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
584         stackEvent.device = null;
585         stackEvent.valueInt1 = 1;       // groupId
586         stackEvent.valueBool1 = false;  // isMuted
587         stackEvent.valueBool2 = true;   // isAutonomous
588 
589         IntStream.range(minIdx, maxIdx).forEach(idx -> {
590             // Given the reference volume index, set the LeAudio Volume
591             stackEvent.valueInt2 = getLeAudioVolume(idx,
592                             mAudioManager.getStreamMinVolume(streamType),
593                             mAudioManager.getStreamMaxVolume(streamType), streamType);
594             mService.messageFromNative(stackEvent);
595 
596             // Verify that setting LeAudio Volume, sets the original volume index to Audio FW
597             verify(mAudioManager, times(1)).setStreamVolume(eq(streamType), eq(idx), anyInt());
598         });
599     }
600 
601     @Test
testAutonomousVolumeStateChange()602     public void testAutonomousVolumeStateChange() {
603         doReturn(AudioManager.MODE_IN_CALL).when(mAudioManager).getMode();
604         testVolumeCalculations(AudioManager.STREAM_VOICE_CALL, CALL_MIN_VOL, CALL_MAX_VOL);
605 
606         doReturn(AudioManager.MODE_NORMAL).when(mAudioManager).getMode();
607         testVolumeCalculations(AudioManager.STREAM_MUSIC, MEDIA_MIN_VOL, MEDIA_MAX_VOL);
608     }
609 
610     /**
611      * Test Volume Control cache.
612      */
613     @Test
testVolumeCache()614     public void testVolumeCache() throws Exception {
615         int groupId = 1;
616         int volume = 6;
617 
618         Assert.assertEquals(-1, mService.getGroupVolume(groupId));
619         final SynchronousResultReceiver<Void> voidRecv = SynchronousResultReceiver.get();
620         mServiceBinder.setGroupVolume(groupId, volume, mAttributionSource, voidRecv);
621         voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS));
622 
623         final SynchronousResultReceiver<Integer> intRecv = SynchronousResultReceiver.get();
624         int defaultRecvValue = -100;
625         mServiceBinder.getGroupVolume(groupId, mAttributionSource, intRecv);
626         int groupVolume = intRecv.awaitResultNoInterrupt(
627                 Duration.ofMillis(TIMEOUT_MS)).getValue(defaultRecvValue);
628         Assert.assertEquals(volume, groupVolume);
629 
630         volume = 10;
631         // Send autonomous volume change.
632         VolumeControlStackEvent stackEvent = new VolumeControlStackEvent(
633                 VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
634         stackEvent.device = null;
635         stackEvent.valueInt1 = groupId;
636         stackEvent.valueInt2 = volume;
637         stackEvent.valueBool1 = false;
638         stackEvent.valueBool2 = true; /* autonomous */
639         mService.messageFromNative(stackEvent);
640 
641         Assert.assertEquals(volume, mService.getGroupVolume(groupId));
642     }
643 
644     /**
645      * Test setting volume for a group member who connects after the volume level
646      * for a group was already changed and cached.
647      */
648     @Test
testLateConnectingDevice()649     public void testLateConnectingDevice() throws Exception {
650         int groupId = 1;
651         int groupVolume = 56;
652 
653         // Both devices are in the same group
654         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
655         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
656 
657         // Update the device policy so okToConnect() returns true
658         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
659         when(mDatabaseManager
660                 .getProfileConnectionPolicy(any(BluetoothDevice.class),
661                         eq(BluetoothProfile.VOLUME_CONTROL)))
662                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
663         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
664         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
665 
666         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED,
667                 BluetoothProfile.STATE_DISCONNECTED);
668         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
669                 mService.getConnectionState(mDevice));
670         Assert.assertTrue(mService.getDevices().contains(mDevice));
671 
672         mService.setGroupVolume(groupId, groupVolume);
673         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume));
674         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));
675 
676         // Verify that second device gets the proper group volume level when connected
677         generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED,
678                 BluetoothProfile.STATE_DISCONNECTED);
679         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
680                 mService.getConnectionState(mDeviceTwo));
681         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
682         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
683     }
684 
685     /**
686      * Test setting volume for a new group member who is discovered after the volume level
687      * for a group was already changed and cached.
688      */
689     @Test
testLateDiscoveredGroupMember()690     public void testLateDiscoveredGroupMember() throws Exception {
691         int groupId = 1;
692         int groupVolume = 56;
693 
694         // For now only one device is in the group
695         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
696         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1);
697 
698         // Update the device policy so okToConnect() returns true
699         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
700         when(mDatabaseManager
701                 .getProfileConnectionPolicy(any(BluetoothDevice.class),
702                         eq(BluetoothProfile.VOLUME_CONTROL)))
703                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
704         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
705         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
706 
707         generateConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_CONNECTED,
708                 BluetoothProfile.STATE_DISCONNECTED);
709         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
710                 mService.getConnectionState(mDevice));
711         Assert.assertTrue(mService.getDevices().contains(mDevice));
712 
713         // Set the group volume
714         mService.setGroupVolume(groupId, groupVolume);
715 
716         // Verify that second device will not get the group volume level if it is not a group member
717         generateConnectionMessageFromNative(mDeviceTwo, BluetoothProfile.STATE_CONNECTED,
718                 BluetoothProfile.STATE_DISCONNECTED);
719         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
720                 mService.getConnectionState(mDeviceTwo));
721         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
722         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));
723 
724         // But gets the volume when it becomes the group member
725         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
726         mService.handleGroupNodeAdded(groupId, mDeviceTwo);
727         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
728     }
729 
730     @Test
testServiceBinderGetDevicesMatchingConnectionStates()731     public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception {
732         final SynchronousResultReceiver<List<BluetoothDevice>> recv =
733                 SynchronousResultReceiver.get();
734         mServiceBinder.getDevicesMatchingConnectionStates(null, mAttributionSource, recv);
735         List<BluetoothDevice> devices = recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
736                 .getValue(null);
737         Assert.assertEquals(0, devices.size());
738     }
739 
740     @Test
testServiceBinderSetConnectionPolicy()741     public void testServiceBinderSetConnectionPolicy() throws Exception {
742         final SynchronousResultReceiver<Boolean> recv = SynchronousResultReceiver.get();
743         boolean defaultRecvValue = false;
744         mServiceBinder.setConnectionPolicy(
745                 mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, mAttributionSource, recv);
746         Assert.assertTrue(recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
747                 .getValue(defaultRecvValue));
748         verify(mDatabaseManager).setProfileConnectionPolicy(
749                 mDevice, BluetoothProfile.VOLUME_CONTROL, BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
750     }
751 
752     @Test
testServiceBinderVolumeOffsetMethods()753     public void testServiceBinderVolumeOffsetMethods() throws Exception {
754         // Send a message to trigger connection completed
755         VolumeControlStackEvent event = new VolumeControlStackEvent(
756                 VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
757         event.device = mDevice;
758         event.valueInt1 = 2; // number of external outputs
759         mService.messageFromNative(event);
760 
761         final SynchronousResultReceiver<Boolean> boolRecv = SynchronousResultReceiver.get();
762         boolean defaultRecvValue = false;
763         mServiceBinder.isVolumeOffsetAvailable(mDevice, mAttributionSource, boolRecv);
764         Assert.assertTrue(boolRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS))
765                 .getValue(defaultRecvValue));
766 
767         int volumeOffset = 100;
768         final SynchronousResultReceiver<Void> voidRecv = SynchronousResultReceiver.get();
769         mServiceBinder.setVolumeOffset(mDevice, volumeOffset, mAttributionSource, voidRecv);
770         voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS));
771         verify(mNativeInterface).setExtAudioOutVolumeOffset(mDevice, 1, volumeOffset);
772     }
773 
774     @Test
testServiceBinderRegisterUnregisterCallback()775     public void testServiceBinderRegisterUnregisterCallback() throws Exception {
776         IBluetoothVolumeControlCallback callback =
777                 Mockito.mock(IBluetoothVolumeControlCallback.class);
778         Binder binder = Mockito.mock(Binder.class);
779         when(callback.asBinder()).thenReturn(binder);
780 
781         int size = mService.mCallbacks.getRegisteredCallbackCount();
782         SynchronousResultReceiver<Void> recv = SynchronousResultReceiver.get();
783         mServiceBinder.registerCallback(callback, mAttributionSource, recv);
784         recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null);
785         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
786 
787         recv = SynchronousResultReceiver.get();
788         mServiceBinder.unregisterCallback(callback, mAttributionSource, recv);
789         recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null);
790         Assert.assertEquals(size, mService.mCallbacks.getRegisteredCallbackCount());
791     }
792 
793     @Test
testServiceBinderMuteMethods()794     public void testServiceBinderMuteMethods() throws Exception {
795         SynchronousResultReceiver<Void> voidRecv = SynchronousResultReceiver.get();
796         mServiceBinder.mute(mDevice, mAttributionSource, voidRecv);
797         voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS));
798         verify(mNativeInterface).mute(mDevice);
799 
800         voidRecv = SynchronousResultReceiver.get();
801         mServiceBinder.unmute(mDevice, mAttributionSource, voidRecv);
802         voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS));
803         verify(mNativeInterface).unmute(mDevice);
804 
805         int groupId = 1;
806         voidRecv = SynchronousResultReceiver.get();
807         mServiceBinder.muteGroup(groupId, mAttributionSource, voidRecv);
808         voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS));
809         verify(mNativeInterface).muteGroup(groupId);
810 
811         voidRecv = SynchronousResultReceiver.get();
812         mServiceBinder.unmuteGroup(groupId, mAttributionSource, voidRecv);
813         voidRecv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS));
814         verify(mNativeInterface).unmuteGroup(groupId);
815     }
816 
817     @Test
testVolumeControlOffsetDescriptor()818     public void testVolumeControlOffsetDescriptor() {
819         VolumeControlService.VolumeControlOffsetDescriptor descriptor =
820                 new VolumeControlService.VolumeControlOffsetDescriptor();
821         int invalidId = -1;
822         int validId = 10;
823         int testValue = 100;
824         String testDesc = "testDescription";
825         int testLocation = 10000;
826 
827         Assert.assertEquals(0, descriptor.size());
828         descriptor.add(validId);
829         Assert.assertEquals(1, descriptor.size());
830 
831         Assert.assertFalse(descriptor.setValue(invalidId, testValue));
832         Assert.assertTrue(descriptor.setValue(validId, testValue));
833         Assert.assertEquals(0, descriptor.getValue(invalidId));
834         Assert.assertEquals(testValue, descriptor.getValue(validId));
835 
836         Assert.assertFalse(descriptor.setDescription(invalidId, testDesc));
837         Assert.assertTrue(descriptor.setDescription(validId, testDesc));
838         Assert.assertEquals(null, descriptor.getDescription(invalidId));
839         Assert.assertEquals(testDesc, descriptor.getDescription(validId));
840 
841         Assert.assertFalse(descriptor.setLocation(invalidId, testLocation));
842         Assert.assertTrue(descriptor.setLocation(validId, testLocation));
843         Assert.assertEquals(0, descriptor.getLocation(invalidId));
844         Assert.assertEquals(testLocation, descriptor.getLocation(validId));
845 
846         StringBuilder sb = new StringBuilder();
847         descriptor.dump(sb);
848         Assert.assertTrue(sb.toString().contains(testDesc));
849 
850         descriptor.add(validId + 1);
851         Assert.assertEquals(2, descriptor.size());
852         descriptor.remove(validId);
853         Assert.assertEquals(1, descriptor.size());
854         descriptor.clear();
855         Assert.assertEquals(0, descriptor.size());
856     }
857 
858     @Test
testDump_doesNotCrash()859     public void testDump_doesNotCrash() throws Exception {
860         connectDevice(mDevice);
861 
862         StringBuilder sb = new StringBuilder();
863         mService.dump(sb);
864     }
865 
connectDevice(BluetoothDevice device)866     private void connectDevice(BluetoothDevice device) throws Exception {
867         VolumeControlStackEvent connCompletedEvent;
868 
869         List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices();
870 
871         // Update the device policy so okToConnect() returns true
872         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
873         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL))
874                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
875         doReturn(true).when(mNativeInterface).connectVolumeControl(device);
876         doReturn(true).when(mNativeInterface).disconnectVolumeControl(device);
877 
878         // Send a connect request
879         Assert.assertTrue("Connect failed", mService.connect(device));
880 
881         // Verify the connection state broadcast, and that we are in Connecting state
882         verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTING,
883                 BluetoothProfile.STATE_DISCONNECTED);
884         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING,
885                 mService.getConnectionState(device));
886 
887         // Send a message to trigger connection completed
888         connCompletedEvent = new VolumeControlStackEvent(
889                 VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
890         connCompletedEvent.device = device;
891         connCompletedEvent.valueInt1 = VolumeControlStackEvent.CONNECTION_STATE_CONNECTED;
892         mService.messageFromNative(connCompletedEvent);
893 
894         // Verify the connection state broadcast, and that we are in Connected state
895         verifyConnectionStateIntent(TIMEOUT_MS, device, BluetoothProfile.STATE_CONNECTED,
896                 BluetoothProfile.STATE_CONNECTING);
897         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED,
898                 mService.getConnectionState(device));
899 
900         // Verify that the device is in the list of connected devices
901         final SynchronousResultReceiver<List<BluetoothDevice>> recv =
902                 SynchronousResultReceiver.get();
903         mServiceBinder.getConnectedDevices(mAttributionSource, recv);
904         List<BluetoothDevice> connectedDevices =
905                 recv.awaitResultNoInterrupt(Duration.ofMillis(TIMEOUT_MS)).getValue(null);
906         Assert.assertTrue(connectedDevices.contains(device));
907         // Verify the list of previously connected devices
908         for (BluetoothDevice prevDevice : prevConnectedDevices) {
909             Assert.assertTrue(connectedDevices.contains(prevDevice));
910         }
911     }
912 
generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)913     private void generateConnectionMessageFromNative(BluetoothDevice device, int newConnectionState,
914             int oldConnectionState) {
915         VolumeControlStackEvent stackEvent =
916                 new VolumeControlStackEvent(
917                         VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
918         stackEvent.device = device;
919         stackEvent.valueInt1 = newConnectionState;
920         mService.messageFromNative(stackEvent);
921         // Verify the connection state broadcast
922         verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState);
923     }
924 
generateUnexpectedConnectionMessageFromNative(BluetoothDevice device, int newConnectionState, int oldConnectionState)925     private void generateUnexpectedConnectionMessageFromNative(BluetoothDevice device,
926             int newConnectionState, int oldConnectionState) {
927         VolumeControlStackEvent stackEvent =
928                 new VolumeControlStackEvent(
929                         VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
930         stackEvent.device = device;
931         stackEvent.valueInt1 = newConnectionState;
932         mService.messageFromNative(stackEvent);
933         // Verify the connection state broadcast
934         verifyNoConnectionStateIntent(TIMEOUT_MS, device);
935     }
936 
937     /**
938      *  Helper function to test okToConnect() method
939      *
940      *  @param device test device
941      *  @param bondState bond state value, could be invalid
942      *  @param policy value, could be invalid
943      *  @param expected expected result from okToConnect()
944      */
testOkToConnectCase(BluetoothDevice device, int bondState, int policy, boolean expected)945     private void testOkToConnectCase(BluetoothDevice device, int bondState, int policy,
946             boolean expected) {
947         doReturn(bondState).when(mAdapterService).getBondState(device);
948         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
949         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL))
950                 .thenReturn(policy);
951         Assert.assertEquals(expected, mService.okToConnect(device));
952     }
953 }
954