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 import android.platform.test.flag.junit.SetFlagsRule;
38 
39 import androidx.test.filters.MediumTest;
40 import androidx.test.platform.app.InstrumentationRegistry;
41 import androidx.test.rule.ServiceTestRule;
42 import androidx.test.runner.AndroidJUnit4;
43 
44 import com.android.bluetooth.TestUtils;
45 import com.android.bluetooth.btservice.AdapterService;
46 import com.android.bluetooth.btservice.ServiceFactory;
47 import com.android.bluetooth.btservice.storage.DatabaseManager;
48 import com.android.bluetooth.csip.CsipSetCoordinatorService;
49 import com.android.bluetooth.flags.Flags;
50 import com.android.bluetooth.le_audio.LeAudioService;
51 
52 import org.junit.After;
53 import org.junit.Assert;
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.junit.MockitoJUnit;
61 import org.mockito.junit.MockitoRule;
62 
63 import java.util.Arrays;
64 import java.util.HashMap;
65 import java.util.List;
66 import java.util.concurrent.LinkedBlockingQueue;
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     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
90 
91     @Mock private AdapterService mAdapterService;
92     @Mock private LeAudioService mLeAudioService;
93     @Mock private DatabaseManager mDatabaseManager;
94     @Mock private VolumeControlNativeInterface mNativeInterface;
95     @Mock private AudioManager mAudioManager;
96     @Mock private ServiceFactory mServiceFactory;
97     @Mock private CsipSetCoordinatorService mCsipService;
98 
99     @Rule public final ServiceTestRule mServiceRule = new ServiceTestRule();
100     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
101 
102     @Before
setUp()103     public void setUp() throws Exception {
104         mTargetContext = InstrumentationRegistry.getInstrumentation().getTargetContext();
105 
106         if (Looper.myLooper() == null) {
107             Looper.prepare();
108         }
109 
110         TestUtils.setAdapterService(mAdapterService);
111         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
112 
113         mAdapter = BluetoothAdapter.getDefaultAdapter();
114         mAttributionSource = mAdapter.getAttributionSource();
115 
116         doReturn(MEDIA_MIN_VOL)
117                 .when(mAudioManager)
118                 .getStreamMinVolume(eq(AudioManager.STREAM_MUSIC));
119         doReturn(MEDIA_MAX_VOL)
120                 .when(mAudioManager)
121                 .getStreamMaxVolume(eq(AudioManager.STREAM_MUSIC));
122         doReturn(CALL_MIN_VOL)
123                 .when(mAudioManager)
124                 .getStreamMinVolume(eq(AudioManager.STREAM_VOICE_CALL));
125         doReturn(CALL_MAX_VOL)
126                 .when(mAudioManager)
127                 .getStreamMaxVolume(eq(AudioManager.STREAM_VOICE_CALL));
128 
129         VolumeControlNativeInterface.setInstance(mNativeInterface);
130         mService = new VolumeControlService(mTargetContext);
131         mService.start();
132         mService.setAvailable(true);
133 
134         mService.mAudioManager = mAudioManager;
135         mService.mFactory = mServiceFactory;
136         mServiceBinder = (VolumeControlService.BluetoothVolumeControlBinder) mService.initBinder();
137         mServiceBinder.mIsTesting = true;
138 
139         doReturn(mCsipService).when(mServiceFactory).getCsipSetCoordinatorService();
140         doReturn(mLeAudioService).when(mServiceFactory).getLeAudioService();
141 
142         // Override the timeout value to speed up the test
143         VolumeControlStateMachine.sConnectTimeoutMs = TIMEOUT_MS; // 1s
144 
145         // Set up the Connection State Changed receiver
146         IntentFilter filter = new IntentFilter();
147         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
148         filter.addAction(BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED);
149 
150         mVolumeControlIntentReceiver = new VolumeControlIntentReceiver();
151         mTargetContext.registerReceiver(mVolumeControlIntentReceiver, filter);
152 
153         // Get a device for testing
154         mDevice = TestUtils.getTestDevice(mAdapter, 0);
155         mDeviceTwo = TestUtils.getTestDevice(mAdapter, 1);
156         mDeviceQueueMap = new HashMap<>();
157         mDeviceQueueMap.put(mDevice, new LinkedBlockingQueue<>());
158         mDeviceQueueMap.put(mDeviceTwo, new LinkedBlockingQueue<>());
159         doReturn(BluetoothDevice.BOND_BONDED)
160                 .when(mAdapterService)
161                 .getBondState(any(BluetoothDevice.class));
162         doReturn(new ParcelUuid[] {BluetoothUuid.VOLUME_CONTROL})
163                 .when(mAdapterService)
164                 .getRemoteUuids(any(BluetoothDevice.class));
165     }
166 
167     @After
tearDown()168     public void tearDown() throws Exception {
169         if (mService == null) {
170             return;
171         }
172 
173         mService.stop();
174         VolumeControlNativeInterface.setInstance(null);
175         mTargetContext.unregisterReceiver(mVolumeControlIntentReceiver);
176         mDeviceQueueMap.clear();
177         TestUtils.clearAdapterService(mAdapterService);
178         reset(mAudioManager);
179     }
180 
181     private class VolumeControlIntentReceiver extends BroadcastReceiver {
182         @Override
onReceive(Context context, Intent intent)183         public void onReceive(Context context, Intent intent) {
184             try {
185                 BluetoothDevice device = intent.getParcelableExtra(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: " + e.getMessage());
192             }
193         }
194     }
195 
verifyConnectionStateIntent( int timeoutMs, BluetoothDevice device, int newState, int prevState)196     private void verifyConnectionStateIntent(
197             int timeoutMs, BluetoothDevice device, int newState, int prevState) {
198         Intent intent = TestUtils.waitForIntent(timeoutMs, mDeviceQueueMap.get(device));
199         Assert.assertNotNull(intent);
200         Assert.assertEquals(
201                 BluetoothVolumeControl.ACTION_CONNECTION_STATE_CHANGED, intent.getAction());
202         Assert.assertEquals(device, intent.getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
203         Assert.assertEquals(newState, intent.getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
204         Assert.assertEquals(
205                 prevState, intent.getIntExtra(BluetoothProfile.EXTRA_PREVIOUS_STATE, -1));
206     }
207 
verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device)208     private void verifyNoConnectionStateIntent(int timeoutMs, BluetoothDevice device) {
209         Intent intent = TestUtils.waitForNoIntent(timeoutMs, mDeviceQueueMap.get(device));
210         Assert.assertNull(intent);
211     }
212 
213     /** Test getting VolumeControl Service: getVolumeControlService() */
214     @Test
testGetVolumeControlService()215     public void testGetVolumeControlService() {
216         Assert.assertEquals(mService, VolumeControlService.getVolumeControlService());
217     }
218 
219     /** Test stop VolumeControl Service */
220     @Test
testStopVolumeControlService()221     public void testStopVolumeControlService() throws Exception {
222         // Prepare: connect
223         connectDevice(mDevice);
224         // VolumeControl Service is already running: test stop().
225         // Note: must be done on the main thread
226         InstrumentationRegistry.getInstrumentation().runOnMainSync(mService::stop);
227         // Try to restart the service. Note: must be done on the main thread
228         InstrumentationRegistry.getInstrumentation().runOnMainSync(mService::start);
229     }
230 
231     /** Test get/set policy for BluetoothDevice */
232     @Test
testGetSetPolicy()233     public void testGetSetPolicy() {
234         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
235         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
236                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
237         Assert.assertEquals(
238                 "Initial device policy",
239                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
240                 mService.getConnectionPolicy(mDevice));
241 
242         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
243         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
244                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
245         Assert.assertEquals(
246                 "Setting device policy to POLICY_FORBIDDEN",
247                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
248                 mService.getConnectionPolicy(mDevice));
249 
250         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
251         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
252                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
253         Assert.assertEquals(
254                 "Setting device policy to POLICY_ALLOWED",
255                 BluetoothProfile.CONNECTION_POLICY_ALLOWED,
256                 mService.getConnectionPolicy(mDevice));
257     }
258 
259     /** Test if getProfileConnectionPolicy works after the service is stopped. */
260     @Test
testGetPolicyAfterStopped()261     public void testGetPolicyAfterStopped() throws Exception {
262         mService.stop();
263         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
264                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
265         int policy = mServiceBinder.getConnectionPolicy(mDevice, mAttributionSource);
266         Assert.assertEquals(
267                 "Initial device policy", BluetoothProfile.CONNECTION_POLICY_UNKNOWN, policy);
268     }
269 
270     /** Test okToConnect method using various test cases */
271     @Test
testOkToConnect()272     public void testOkToConnect() {
273         int badPolicyValue = 1024;
274         int badBondState = 42;
275         testOkToConnectCase(
276                 mDevice,
277                 BluetoothDevice.BOND_NONE,
278                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
279                 false);
280         testOkToConnectCase(
281                 mDevice,
282                 BluetoothDevice.BOND_NONE,
283                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
284                 false);
285         testOkToConnectCase(
286                 mDevice,
287                 BluetoothDevice.BOND_NONE,
288                 BluetoothProfile.CONNECTION_POLICY_ALLOWED,
289                 false);
290         testOkToConnectCase(mDevice, BluetoothDevice.BOND_NONE, badPolicyValue, false);
291         testOkToConnectCase(
292                 mDevice,
293                 BluetoothDevice.BOND_BONDING,
294                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
295                 false);
296         testOkToConnectCase(
297                 mDevice,
298                 BluetoothDevice.BOND_BONDING,
299                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
300                 false);
301         testOkToConnectCase(
302                 mDevice,
303                 BluetoothDevice.BOND_BONDING,
304                 BluetoothProfile.CONNECTION_POLICY_ALLOWED,
305                 false);
306         testOkToConnectCase(mDevice, BluetoothDevice.BOND_BONDING, badPolicyValue, false);
307         testOkToConnectCase(
308                 mDevice,
309                 BluetoothDevice.BOND_BONDED,
310                 BluetoothProfile.CONNECTION_POLICY_UNKNOWN,
311                 true);
312         testOkToConnectCase(
313                 mDevice,
314                 BluetoothDevice.BOND_BONDED,
315                 BluetoothProfile.CONNECTION_POLICY_FORBIDDEN,
316                 false);
317         testOkToConnectCase(
318                 mDevice,
319                 BluetoothDevice.BOND_BONDED,
320                 BluetoothProfile.CONNECTION_POLICY_ALLOWED,
321                 true);
322         testOkToConnectCase(mDevice, BluetoothDevice.BOND_BONDED, badPolicyValue, false);
323         testOkToConnectCase(
324                 mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, false);
325         testOkToConnectCase(
326                 mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_FORBIDDEN, false);
327         testOkToConnectCase(
328                 mDevice, badBondState, BluetoothProfile.CONNECTION_POLICY_ALLOWED, false);
329         testOkToConnectCase(mDevice, badBondState, badPolicyValue, false);
330     }
331 
332     /**
333      * Test that an outgoing connection to device that does not have Volume Control UUID is rejected
334      */
335     @Test
testOutgoingConnectMissingVolumeControlUuid()336     public void testOutgoingConnectMissingVolumeControlUuid() {
337         // Update the device policy so okToConnect() returns true
338         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
339         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
340                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
341         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
342         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
343 
344         // Return No UUID
345         doReturn(new ParcelUuid[] {})
346                 .when(mAdapterService)
347                 .getRemoteUuids(any(BluetoothDevice.class));
348 
349         // Send a connect request
350         Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
351     }
352 
353     /** Test that an outgoing connection to device that have Volume Control UUID is successful */
354     @Test
testOutgoingConnectDisconnectExistingVolumeControlUuid()355     public void testOutgoingConnectDisconnectExistingVolumeControlUuid() throws Exception {
356         // Update the device policy so okToConnect() returns true
357         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
358         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
359                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
360         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
361         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
362 
363         // Return Volume Control UUID
364         doReturn(new ParcelUuid[] {BluetoothUuid.VOLUME_CONTROL})
365                 .when(mAdapterService)
366                 .getRemoteUuids(any(BluetoothDevice.class));
367 
368         // Send a connect request via binder
369         Assert.assertTrue(
370                 "Connect expected to succeed", mServiceBinder.connect(mDevice, mAttributionSource));
371 
372         // Verify the connection state broadcast, and that we are in Connecting state
373         verifyConnectionStateIntent(
374                 TIMEOUT_MS,
375                 mDevice,
376                 BluetoothProfile.STATE_CONNECTING,
377                 BluetoothProfile.STATE_DISCONNECTED);
378 
379         // Send a disconnect request via binder
380         Assert.assertTrue(
381                 "Disconnect expected to succeed",
382                 mServiceBinder.disconnect(mDevice, mAttributionSource));
383 
384         // Verify the connection state broadcast, and that we are in Connecting state
385         verifyConnectionStateIntent(
386                 TIMEOUT_MS,
387                 mDevice,
388                 BluetoothProfile.STATE_DISCONNECTED,
389                 BluetoothProfile.STATE_CONNECTING);
390     }
391 
392     /** Test that an outgoing connection to device with POLICY_FORBIDDEN is rejected */
393     @Test
testOutgoingConnectPolicyForbidden()394     public void testOutgoingConnectPolicyForbidden() {
395         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
396         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
397 
398         // Set the device policy to POLICY_FORBIDDEN so connect() should fail
399         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
400         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
401                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_FORBIDDEN);
402 
403         // Send a connect request
404         Assert.assertFalse("Connect expected to fail", mService.connect(mDevice));
405     }
406 
407     /** Test that an outgoing connection times out */
408     @Test
testOutgoingConnectTimeout()409     public void testOutgoingConnectTimeout() throws Exception {
410         // Update the device policy so okToConnect() returns true
411         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
412         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
413                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
414         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
415         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
416 
417         // Send a connect request
418         Assert.assertTrue("Connect failed", mService.connect(mDevice));
419 
420         // Verify the connection state broadcast, and that we are in Connecting state
421         verifyConnectionStateIntent(
422                 TIMEOUT_MS,
423                 mDevice,
424                 BluetoothProfile.STATE_CONNECTING,
425                 BluetoothProfile.STATE_DISCONNECTED);
426         Assert.assertEquals(
427                 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice));
428 
429         // Verify the connection state broadcast, and that we are in Disconnected state
430         verifyConnectionStateIntent(
431                 VolumeControlStateMachine.sConnectTimeoutMs * 2,
432                 mDevice,
433                 BluetoothProfile.STATE_DISCONNECTED,
434                 BluetoothProfile.STATE_CONNECTING);
435 
436         int state = mServiceBinder.getConnectionState(mDevice, mAttributionSource);
437         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, state);
438     }
439 
440     /**
441      * Test that only CONNECTION_STATE_CONNECTED or CONNECTION_STATE_CONNECTING Volume Control stack
442      * events will create a state machine.
443      */
444     @Test
testCreateStateMachineStackEvents()445     public void testCreateStateMachineStackEvents() {
446         // Update the device policy so okToConnect() returns true
447         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
448         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
449                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
450         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
451         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
452 
453         // stack event: CONNECTION_STATE_CONNECTING - state machine should be created
454         generateConnectionMessageFromNative(
455                 mDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
456         Assert.assertEquals(
457                 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice));
458         Assert.assertTrue(mService.getDevices().contains(mDevice));
459 
460         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
461         generateConnectionMessageFromNative(
462                 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING);
463         Assert.assertEquals(
464                 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice));
465         Assert.assertTrue(mService.getDevices().contains(mDevice));
466         mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE);
467         Assert.assertFalse(mService.getDevices().contains(mDevice));
468 
469         // stack event: CONNECTION_STATE_CONNECTED - state machine should be created
470         generateConnectionMessageFromNative(
471                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
472         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
473         Assert.assertTrue(mService.getDevices().contains(mDevice));
474 
475         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should be removed
476         generateConnectionMessageFromNative(
477                 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTED);
478         Assert.assertEquals(
479                 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice));
480         Assert.assertTrue(mService.getDevices().contains(mDevice));
481         mService.bondStateChanged(mDevice, BluetoothDevice.BOND_NONE);
482         Assert.assertFalse(mService.getDevices().contains(mDevice));
483 
484         // stack event: CONNECTION_STATE_DISCONNECTING - state machine should not be created
485         generateUnexpectedConnectionMessageFromNative(
486                 mDevice, BluetoothProfile.STATE_DISCONNECTING);
487         Assert.assertEquals(
488                 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice));
489         Assert.assertFalse(mService.getDevices().contains(mDevice));
490 
491         // stack event: CONNECTION_STATE_DISCONNECTED - state machine should not be created
492         generateUnexpectedConnectionMessageFromNative(mDevice, BluetoothProfile.STATE_DISCONNECTED);
493         Assert.assertEquals(
494                 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice));
495         Assert.assertFalse(mService.getDevices().contains(mDevice));
496     }
497 
498     /**
499      * Test that a CONNECTION_STATE_DISCONNECTED Volume Control stack event will remove the state
500      * machine only if the device is unbond.
501      */
502     @Test
testDeleteStateMachineDisconnectEvents()503     public void testDeleteStateMachineDisconnectEvents() {
504         // Update the device policy so okToConnect() returns true
505         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
506         when(mDatabaseManager.getProfileConnectionPolicy(mDevice, BluetoothProfile.VOLUME_CONTROL))
507                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
508         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
509         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
510 
511         // stack event: CONNECTION_STATE_CONNECTING - state machine should be created
512         generateConnectionMessageFromNative(
513                 mDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
514         Assert.assertEquals(
515                 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice));
516         Assert.assertTrue(mService.getDevices().contains(mDevice));
517 
518         // stack event: CONNECTION_STATE_DISCONNECTED - state machine is not removed
519         generateConnectionMessageFromNative(
520                 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING);
521         Assert.assertEquals(
522                 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice));
523         Assert.assertTrue(mService.getDevices().contains(mDevice));
524 
525         // stack event: CONNECTION_STATE_CONNECTING - state machine remains
526         generateConnectionMessageFromNative(
527                 mDevice, BluetoothProfile.STATE_CONNECTING, BluetoothProfile.STATE_DISCONNECTED);
528         Assert.assertEquals(
529                 BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(mDevice));
530         Assert.assertTrue(mService.getDevices().contains(mDevice));
531 
532         // device bond state marked as unbond - state machine is not removed
533         doReturn(BluetoothDevice.BOND_NONE)
534                 .when(mAdapterService)
535                 .getBondState(any(BluetoothDevice.class));
536         Assert.assertTrue(mService.getDevices().contains(mDevice));
537 
538         // stack event: CONNECTION_STATE_DISCONNECTED - state machine is removed
539         generateConnectionMessageFromNative(
540                 mDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.STATE_CONNECTING);
541         Assert.assertEquals(
542                 BluetoothProfile.STATE_DISCONNECTED, mService.getConnectionState(mDevice));
543         Assert.assertFalse(mService.getDevices().contains(mDevice));
544     }
545 
546     /** Test that various Volume Control stack events will broadcast related states. */
547     @Test
testVolumeControlStackEvents()548     public void testVolumeControlStackEvents() {
549         int group_id = -1;
550         int volume = 6;
551         boolean mute = false;
552 
553         // Send a message to trigger volume state changed broadcast
554         VolumeControlStackEvent stackEvent =
555                 new VolumeControlStackEvent(
556                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
557         stackEvent.device = mDevice;
558         stackEvent.valueInt1 = group_id;
559         stackEvent.valueInt2 = volume;
560         stackEvent.valueBool1 = mute;
561         mService.messageFromNative(stackEvent);
562     }
563 
getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType)564     int getLeAudioVolume(int index, int minIndex, int maxIndex, int streamType) {
565         // Note: This has to be the same as mBtHelper.setLeAudioVolume()
566         return (int) Math.round((double) index * BT_LE_AUDIO_MAX_VOL / maxIndex);
567     }
568 
testVolumeCalculations(int streamType, int minIdx, int maxIdx)569     void testVolumeCalculations(int streamType, int minIdx, int maxIdx) {
570         // Send a message to trigger volume state changed broadcast
571         final VolumeControlStackEvent stackEvent =
572                 new VolumeControlStackEvent(
573                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
574         stackEvent.device = null;
575         stackEvent.valueInt1 = 1; // groupId
576         stackEvent.valueBool1 = false; // isMuted
577         stackEvent.valueBool2 = true; // isAutonomous
578 
579         IntStream.range(minIdx, maxIdx)
580                 .forEach(
581                         idx -> {
582                             // Given the reference volume index, set the LeAudio Volume
583                             stackEvent.valueInt2 =
584                                     getLeAudioVolume(
585                                             idx,
586                                             mAudioManager.getStreamMinVolume(streamType),
587                                             mAudioManager.getStreamMaxVolume(streamType),
588                                             streamType);
589                             mService.messageFromNative(stackEvent);
590 
591                             // Verify that setting LeAudio Volume, sets the original volume index to
592                             // Audio FW
593                             verify(mAudioManager, times(1))
594                                     .setStreamVolume(eq(streamType), eq(idx), anyInt());
595                         });
596     }
597 
598     @Test
testAutonomousVolumeStateChange()599     public void testAutonomousVolumeStateChange() {
600         // TODO: b/329163385 - This test should be modified to run without having to set the flag to
601         // a specific value
602         mSetFlagsRule.disableFlags(
603                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
604         doReturn(AudioManager.MODE_IN_CALL).when(mAudioManager).getMode();
605         testVolumeCalculations(AudioManager.STREAM_VOICE_CALL, CALL_MIN_VOL, CALL_MAX_VOL);
606 
607         doReturn(AudioManager.MODE_NORMAL).when(mAudioManager).getMode();
608         testVolumeCalculations(AudioManager.STREAM_MUSIC, MEDIA_MIN_VOL, MEDIA_MAX_VOL);
609     }
610 
611     /** Test if autonomous Mute/Unmute propagates the event to audio manager. */
612     @Test
testAutonomousMuteUnmute()613     public void testAutonomousMuteUnmute() {
614         // TODO: b/329163385 - This test should be modified to run without having to set the flag to
615         // a specific value
616         mSetFlagsRule.disableFlags(
617                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
618         int streamType = AudioManager.STREAM_MUSIC;
619         int streamVol = getLeAudioVolume(19, MEDIA_MIN_VOL, MEDIA_MAX_VOL, streamType);
620 
621         // Send a message to trigger volume state changed broadcast
622         final VolumeControlStackEvent stackEvent =
623                 new VolumeControlStackEvent(
624                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
625         stackEvent.device = null;
626         stackEvent.valueInt1 = 1; // groupId
627         stackEvent.valueInt2 = streamVol;
628         stackEvent.valueBool1 = false; // isMuted
629         stackEvent.valueBool2 = true; // isAutonomous
630 
631         doReturn(false).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC));
632 
633         // Verify that muting LeAudio device, sets the mute state on the audio device
634         stackEvent.valueBool1 = true;
635         mService.messageFromNative(stackEvent);
636         verify(mAudioManager, times(1))
637                 .adjustStreamVolume(eq(streamType), eq(AudioManager.ADJUST_MUTE), anyInt());
638 
639         doReturn(true).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC));
640 
641         // Verify that unmuting LeAudio device, unsets the mute state on the audio device
642         stackEvent.valueBool1 = false;
643         mService.messageFromNative(stackEvent);
644         verify(mAudioManager, times(1))
645                 .adjustStreamVolume(eq(streamType), eq(AudioManager.ADJUST_UNMUTE), anyInt());
646     }
647 
648     /** Test Volume Control cache. */
649     @Test
testVolumeCache()650     public void testVolumeCache() throws Exception {
651         int groupId = 1;
652         int volume = 6;
653 
654         Assert.assertEquals(-1, mService.getGroupVolume(groupId));
655         mServiceBinder.setGroupVolume(groupId, volume, mAttributionSource);
656 
657         int groupVolume = mServiceBinder.getGroupVolume(groupId, mAttributionSource);
658         Assert.assertEquals(volume, groupVolume);
659 
660         volume = 10;
661         // Send autonomous volume change.
662         VolumeControlStackEvent stackEvent =
663                 new VolumeControlStackEvent(
664                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
665         stackEvent.device = null;
666         stackEvent.valueInt1 = groupId;
667         stackEvent.valueInt2 = volume;
668         stackEvent.valueBool1 = false;
669         stackEvent.valueBool2 = true; /* autonomous */
670         mService.messageFromNative(stackEvent);
671 
672         Assert.assertEquals(volume, mService.getGroupVolume(groupId));
673     }
674 
675     /** Test Active Group change */
676     @Test
testActiveGroupChange()677     public void testActiveGroupChange() throws Exception {
678         // TODO: b/329163385 - This test should be modified to run without having to set the flag to
679         // a specific value
680         mSetFlagsRule.disableFlags(
681                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
682         int groupId_1 = 1;
683         int volume_groupId_1 = 6;
684 
685         int groupId_2 = 2;
686         int volume_groupId_2 = 20;
687 
688         Assert.assertEquals(-1, mService.getGroupVolume(groupId_1));
689         Assert.assertEquals(-1, mService.getGroupVolume(groupId_2));
690         mServiceBinder.setGroupVolume(groupId_1, volume_groupId_1, mAttributionSource);
691 
692         mServiceBinder.setGroupVolume(groupId_2, volume_groupId_2, mAttributionSource);
693 
694         mServiceBinder.setGroupActive(groupId_1, true, mAttributionSource);
695 
696         // Expected index for STREAM_MUSIC
697         int expectedVol =
698                 (int) Math.round((double) (volume_groupId_1 * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL);
699         verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedVol), anyInt());
700 
701         mServiceBinder.setGroupActive(groupId_2, true, mAttributionSource);
702 
703         expectedVol =
704                 (int) Math.round((double) (volume_groupId_2 * MEDIA_MAX_VOL) / BT_LE_AUDIO_MAX_VOL);
705         verify(mAudioManager, times(1)).setStreamVolume(anyInt(), eq(expectedVol), anyInt());
706     }
707 
708     /** Test Volume Control Mute cache. */
709     @Test
testMuteCache()710     public void testMuteCache() throws Exception {
711         int groupId = 1;
712         int volume = 6;
713 
714         Assert.assertEquals(false, mService.getGroupMute(groupId));
715 
716         // Send autonomous volume change
717         VolumeControlStackEvent stackEvent =
718                 new VolumeControlStackEvent(
719                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
720         stackEvent.device = null;
721         stackEvent.valueInt1 = groupId;
722         stackEvent.valueInt2 = volume;
723         stackEvent.valueBool1 = false; /* unmuted */
724         stackEvent.valueBool2 = true; /* autonomous */
725         mService.messageFromNative(stackEvent);
726 
727         // Mute
728         mServiceBinder.muteGroup(groupId, mAttributionSource);
729         Assert.assertEquals(true, mService.getGroupMute(groupId));
730 
731         // Make sure the volume is kept even when muted
732         Assert.assertEquals(volume, mService.getGroupVolume(groupId));
733 
734         // Send autonomous unmute
735         stackEvent.valueBool1 = false; /* unmuted */
736         mService.messageFromNative(stackEvent);
737 
738         Assert.assertEquals(false, mService.getGroupMute(groupId));
739     }
740 
741     /** Test Volume Control with muted stream. */
742     @Test
testVolumeChangeWhileMuted()743     public void testVolumeChangeWhileMuted() throws Exception {
744         int groupId = 1;
745         int volume = 6;
746 
747         Assert.assertEquals(false, mService.getGroupMute(groupId));
748 
749         // Set the initial volume state
750         VolumeControlStackEvent stackEvent =
751                 new VolumeControlStackEvent(
752                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
753         stackEvent.device = null;
754         stackEvent.valueInt1 = groupId;
755         stackEvent.valueInt2 = volume;
756         stackEvent.valueBool1 = false; /* unmuted */
757         stackEvent.valueBool2 = true; /* autonomous */
758         mService.messageFromNative(stackEvent);
759 
760         // Mute
761         mService.muteGroup(groupId);
762         Assert.assertEquals(true, mService.getGroupMute(groupId));
763         verify(mNativeInterface, times(1)).muteGroup(eq(groupId));
764 
765         // Make sure the volume is kept even when muted
766         doReturn(true).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC));
767         Assert.assertEquals(volume, mService.getGroupVolume(groupId));
768 
769         // Lower the volume and keep it mute
770         mService.setGroupVolume(groupId, --volume);
771         Assert.assertEquals(true, mService.getGroupMute(groupId));
772         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume));
773         verify(mNativeInterface, times(0)).unmuteGroup(eq(groupId));
774 
775         // Don't unmute on consecutive calls either
776         mService.setGroupVolume(groupId, --volume);
777         Assert.assertEquals(true, mService.getGroupMute(groupId));
778         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume));
779         verify(mNativeInterface, times(0)).unmuteGroup(eq(groupId));
780 
781         // Raise the volume and unmute
782         volume += 10; // avoid previous volume levels and simplify mock verification
783         doReturn(false).when(mAudioManager).isStreamMute(eq(AudioManager.STREAM_MUSIC));
784         mService.setGroupVolume(groupId, ++volume);
785         Assert.assertEquals(false, mService.getGroupMute(groupId));
786         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume));
787         // Verify the number of unmute calls after the second volume change
788         mService.setGroupVolume(groupId, ++volume);
789         Assert.assertEquals(false, mService.getGroupMute(groupId));
790         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume));
791         // Make sure we unmuted only once
792         verify(mNativeInterface, times(1)).unmuteGroup(eq(groupId));
793     }
794 
795     /**
796      * Test setting volume for a group member who connects after the volume level for a group was
797      * already changed and cached.
798      */
799     @Test
testLateConnectingDevice()800     public void testLateConnectingDevice() throws Exception {
801         int groupId = 1;
802         int groupVolume = 56;
803 
804         // Both devices are in the same group
805         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
806         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
807 
808         // Update the device policy so okToConnect() returns true
809         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
810         when(mDatabaseManager.getProfileConnectionPolicy(
811                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
812                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
813         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
814         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
815 
816         generateConnectionMessageFromNative(
817                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
818         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
819         Assert.assertTrue(mService.getDevices().contains(mDevice));
820 
821         mService.setGroupVolume(groupId, groupVolume);
822         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume));
823         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));
824 
825         // Verify that second device gets the proper group volume level when connected
826         generateConnectionMessageFromNative(
827                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
828         Assert.assertEquals(
829                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
830         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
831         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
832     }
833 
834     /**
835      * Test setting volume for a new group member who is discovered after the volume level for a
836      * group was already changed and cached.
837      */
838     @Test
testLateDiscoveredGroupMember()839     public void testLateDiscoveredGroupMember() throws Exception {
840         int groupId = 1;
841         int groupVolume = 56;
842 
843         // For now only one device is in the group
844         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
845         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1);
846 
847         // Update the device policy so okToConnect() returns true
848         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
849         when(mDatabaseManager.getProfileConnectionPolicy(
850                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
851                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
852         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
853         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
854 
855         generateConnectionMessageFromNative(
856                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
857         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
858         Assert.assertTrue(mService.getDevices().contains(mDevice));
859 
860         // Set the group volume
861         mService.setGroupVolume(groupId, groupVolume);
862 
863         // Verify that second device will not get the group volume level if it is not a group member
864         generateConnectionMessageFromNative(
865                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
866         Assert.assertEquals(
867                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
868         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
869         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));
870 
871         // But gets the volume when it becomes the group member
872         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
873         mService.handleGroupNodeAdded(groupId, mDeviceTwo);
874         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
875     }
876 
877     /**
878      * Test setting volume to 0 for a group member who connects after the volume level for a group
879      * was already changed and cached. LeAudio has no knowledge of mute for anything else than
880      * telephony, thus setting volume level to 0 is considered as muting.
881      */
882     @Test
testMuteLateConnectingDevice()883     public void testMuteLateConnectingDevice() throws Exception {
884         int groupId = 1;
885         int volume = 100;
886 
887         // Both devices are in the same group
888         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
889         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
890 
891         // Update the device policy so okToConnect() returns true
892         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
893         when(mDatabaseManager.getProfileConnectionPolicy(
894                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
895                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
896         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
897         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
898 
899         generateConnectionMessageFromNative(
900                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
901         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
902         Assert.assertTrue(mService.getDevices().contains(mDevice));
903 
904         // Set the initial volume and mute conditions
905         doReturn(true).when(mAudioManager).isStreamMute(anyInt());
906         mService.setGroupVolume(groupId, volume);
907 
908         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(volume));
909         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(volume));
910         // Check if it was muted
911         verify(mNativeInterface, times(1)).muteGroup(eq(groupId));
912 
913         Assert.assertEquals(true, mService.getGroupMute(groupId));
914 
915         // Verify that second device gets the proper group volume level when connected
916         generateConnectionMessageFromNative(
917                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
918         Assert.assertEquals(
919                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
920         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
921         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(volume));
922         // Check if new device was muted
923         verify(mNativeInterface, times(1)).mute(eq(mDeviceTwo));
924     }
925 
926     /**
927      * Test setting volume to 0 for a new group member who is discovered after the volume level for
928      * a group was already changed and cached. LeAudio has no knowledge of mute for anything else
929      * than telephony, thus setting volume level to 0 is considered as muting.
930      */
931     @Test
testMuteLateDiscoveredGroupMember()932     public void testMuteLateDiscoveredGroupMember() throws Exception {
933         int groupId = 1;
934         int volume = 100;
935 
936         // For now only one device is in the group
937         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
938         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(-1);
939 
940         // Update the device policy so okToConnect() returns true
941         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
942         when(mDatabaseManager.getProfileConnectionPolicy(
943                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
944                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
945         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
946         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
947 
948         generateConnectionMessageFromNative(
949                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
950         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
951         Assert.assertTrue(mService.getDevices().contains(mDevice));
952 
953         // Set the initial volume and mute conditions
954         doReturn(true).when(mAudioManager).isStreamMute(anyInt());
955         mService.setGroupVolume(groupId, volume);
956 
957         // Verify that second device will not get the group volume level if it is not a group member
958         generateConnectionMessageFromNative(
959                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
960         Assert.assertEquals(
961                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
962         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
963         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(volume));
964         // Check if it was not muted
965         verify(mNativeInterface, times(0)).mute(eq(mDeviceTwo));
966 
967         // But gets the volume when it becomes the group member
968         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
969         mService.handleGroupNodeAdded(groupId, mDeviceTwo);
970         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(volume));
971         verify(mNativeInterface, times(1)).mute(eq(mDeviceTwo));
972     }
973 
974     @Test
testServiceBinderGetDevicesMatchingConnectionStates()975     public void testServiceBinderGetDevicesMatchingConnectionStates() throws Exception {
976         List<BluetoothDevice> devices =
977                 mServiceBinder.getDevicesMatchingConnectionStates(null, mAttributionSource);
978         Assert.assertEquals(0, devices.size());
979     }
980 
981     @Test
testServiceBinderSetConnectionPolicy()982     public void testServiceBinderSetConnectionPolicy() throws Exception {
983         Assert.assertTrue(
984                 mServiceBinder.setConnectionPolicy(
985                         mDevice, BluetoothProfile.CONNECTION_POLICY_UNKNOWN, mAttributionSource));
986         verify(mDatabaseManager)
987                 .setProfileConnectionPolicy(
988                         mDevice,
989                         BluetoothProfile.VOLUME_CONTROL,
990                         BluetoothProfile.CONNECTION_POLICY_UNKNOWN);
991     }
992 
993     @Test
testServiceBinderVolumeOffsetMethods()994     public void testServiceBinderVolumeOffsetMethods() throws Exception {
995         // Send a message to trigger connection completed
996         generateDeviceAvailableMessageFromNative(mDevice, 2);
997 
998         Assert.assertTrue(mServiceBinder.isVolumeOffsetAvailable(mDevice, mAttributionSource));
999 
1000         int numberOfInstances =
1001                 mServiceBinder.getNumberOfVolumeOffsetInstances(mDevice, mAttributionSource);
1002         Assert.assertEquals(2, numberOfInstances);
1003 
1004         int id = 1;
1005         int volumeOffset = 100;
1006         mServiceBinder.setVolumeOffset(mDevice, id, volumeOffset, mAttributionSource);
1007         verify(mNativeInterface).setExtAudioOutVolumeOffset(mDevice, id, volumeOffset);
1008     }
1009 
1010     @Test
testServiceBinderSetDeviceVolumeMethods()1011     public void testServiceBinderSetDeviceVolumeMethods() throws Exception {
1012         mSetFlagsRule.enableFlags(
1013                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
1014 
1015         int groupId = 1;
1016         int groupVolume = 56;
1017         int deviceOneVolume = 46;
1018         int deviceTwoVolume = 36;
1019 
1020         // Both devices are in the same group
1021         when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId);
1022         when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId);
1023 
1024         // Update the device policy so okToConnect() returns true
1025         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1026         when(mDatabaseManager.getProfileConnectionPolicy(
1027                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
1028                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1029         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
1030         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
1031 
1032         generateDeviceAvailableMessageFromNative(mDevice, 1);
1033         generateConnectionMessageFromNative(
1034                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1035         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
1036         Assert.assertTrue(mService.getDevices().contains(mDevice));
1037 
1038         mServiceBinder.setDeviceVolume(mDevice, groupVolume, true, mAttributionSource);
1039         verify(mNativeInterface).setGroupVolume(groupId, groupVolume);
1040         Assert.assertEquals(groupVolume, mService.getGroupVolume(groupId));
1041 
1042         mServiceBinder.setDeviceVolume(mDevice, deviceOneVolume, false, mAttributionSource);
1043         verify(mNativeInterface).setVolume(mDevice, deviceOneVolume);
1044         Assert.assertEquals(deviceOneVolume, mService.getDeviceVolume(mDevice));
1045         Assert.assertNotEquals(deviceOneVolume, mService.getDeviceVolume(mDeviceTwo));
1046 
1047         mServiceBinder.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false, mAttributionSource);
1048         verify(mNativeInterface).setVolume(mDeviceTwo, deviceTwoVolume);
1049         Assert.assertEquals(deviceTwoVolume, mService.getDeviceVolume(mDeviceTwo));
1050         Assert.assertNotEquals(deviceTwoVolume, mService.getDeviceVolume(mDevice));
1051     }
1052 
1053     @Test
testServiceBinderRegisterUnregisterCallback()1054     public void testServiceBinderRegisterUnregisterCallback() throws Exception {
1055         IBluetoothVolumeControlCallback callback =
1056                 Mockito.mock(IBluetoothVolumeControlCallback.class);
1057         Binder binder = Mockito.mock(Binder.class);
1058         when(callback.asBinder()).thenReturn(binder);
1059 
1060         int size = mService.mCallbacks.getRegisteredCallbackCount();
1061         mServiceBinder.registerCallback(callback, mAttributionSource);
1062         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
1063 
1064         mServiceBinder.unregisterCallback(callback, mAttributionSource);
1065         Assert.assertEquals(size, mService.mCallbacks.getRegisteredCallbackCount());
1066     }
1067 
1068     @Test
testServiceBinderRegisterCallbackWhenDeviceAlreadyConnected()1069     public void testServiceBinderRegisterCallbackWhenDeviceAlreadyConnected() throws Exception {
1070         mSetFlagsRule.enableFlags(Flags.FLAG_LEAUDIO_MULTIPLE_VOCS_INSTANCES_API);
1071 
1072         int groupId = 1;
1073         int groupVolume = 56;
1074 
1075         // Both devices are in the same group
1076         when(mCsipService.getGroupId(mDevice, BluetoothUuid.CAP)).thenReturn(groupId);
1077         when(mCsipService.getGroupId(mDeviceTwo, BluetoothUuid.CAP)).thenReturn(groupId);
1078 
1079         // Update the device policy so okToConnect() returns true
1080         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1081         when(mDatabaseManager.getProfileConnectionPolicy(
1082                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
1083                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1084         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
1085         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
1086 
1087         generateDeviceAvailableMessageFromNative(mDevice, 2);
1088         generateConnectionMessageFromNative(
1089                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1090         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
1091         Assert.assertTrue(mService.getDevices().contains(mDevice));
1092 
1093         mService.setGroupVolume(groupId, groupVolume);
1094         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume));
1095         verify(mNativeInterface, times(0)).setVolume(eq(mDeviceTwo), eq(groupVolume));
1096 
1097         // Verify that second device gets the proper group volume level when connected
1098         generateDeviceAvailableMessageFromNative(mDeviceTwo, 1);
1099         generateConnectionMessageFromNative(
1100                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1101         Assert.assertEquals(
1102                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
1103         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
1104         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(groupVolume));
1105 
1106         // Generate events for both devices
1107         generateDeviceOffsetChangedMessageFromNative(mDevice, 1, 100);
1108         generateDeviceLocationChangedMessageFromNative(mDevice, 1, 1);
1109         final String testDevice1Desc1 = "testDevice1Desc1";
1110         generateDeviceDescriptionChangedMessageFromNative(mDevice, 1, testDevice1Desc1);
1111 
1112         generateDeviceOffsetChangedMessageFromNative(mDevice, 2, 200);
1113         generateDeviceLocationChangedMessageFromNative(mDevice, 2, 2);
1114         final String testDevice1Desc2 = "testDevice1Desc2";
1115         generateDeviceDescriptionChangedMessageFromNative(mDevice, 2, testDevice1Desc2);
1116 
1117         generateDeviceOffsetChangedMessageFromNative(mDeviceTwo, 1, 250);
1118         generateDeviceLocationChangedMessageFromNative(mDeviceTwo, 1, 3);
1119         final String testDevice2Desc = "testDevice2Desc";
1120         generateDeviceDescriptionChangedMessageFromNative(mDeviceTwo, 1, testDevice2Desc);
1121 
1122         // Register callback and verify it is called with known devices
1123         IBluetoothVolumeControlCallback callback =
1124                 Mockito.mock(IBluetoothVolumeControlCallback.class);
1125         Binder binder = Mockito.mock(Binder.class);
1126         when(callback.asBinder()).thenReturn(binder);
1127 
1128         int size = mService.mCallbacks.getRegisteredCallbackCount();
1129         mServiceBinder.registerCallback(callback, mAttributionSource);
1130         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
1131 
1132         verify(callback).onVolumeOffsetChanged(eq(mDevice), eq(1), eq(100));
1133         verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDevice), eq(1), eq(1));
1134         verify(callback)
1135                 .onVolumeOffsetAudioDescriptionChanged(eq(mDevice), eq(1), eq(testDevice1Desc1));
1136 
1137         verify(callback).onVolumeOffsetChanged(eq(mDevice), eq(2), eq(200));
1138         verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDevice), eq(2), eq(2));
1139         verify(callback)
1140                 .onVolumeOffsetAudioDescriptionChanged(eq(mDevice), eq(2), eq(testDevice1Desc2));
1141 
1142         verify(callback).onVolumeOffsetChanged(eq(mDeviceTwo), eq(1), eq(250));
1143         verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDeviceTwo), eq(1), eq(3));
1144         verify(callback)
1145                 .onVolumeOffsetAudioDescriptionChanged(eq(mDeviceTwo), eq(1), eq(testDevice2Desc));
1146 
1147         generateDeviceOffsetChangedMessageFromNative(mDevice, 1, 50);
1148         generateDeviceLocationChangedMessageFromNative(mDevice, 1, 0);
1149         final String testDevice1Desc3 = "testDevice1Desc3";
1150         generateDeviceDescriptionChangedMessageFromNative(mDevice, 1, testDevice1Desc3);
1151 
1152         verify(callback).onVolumeOffsetChanged(eq(mDevice), eq(1), eq(50));
1153         verify(callback).onVolumeOffsetAudioLocationChanged(eq(mDevice), eq(1), eq(0));
1154         verify(callback)
1155                 .onVolumeOffsetAudioDescriptionChanged(eq(mDevice), eq(1), eq(testDevice1Desc3));
1156     }
1157 
1158     @Test
testServiceBinderRegisterVolumeChangedCallbackWhenDeviceAlreadyConnected()1159     public void testServiceBinderRegisterVolumeChangedCallbackWhenDeviceAlreadyConnected()
1160             throws Exception {
1161         mSetFlagsRule.enableFlags(
1162                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
1163         int groupId = 1;
1164         int deviceOneVolume = 46;
1165         int deviceTwoVolume = 36;
1166 
1167         // Update the device policy so okToConnect() returns true
1168         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1169         when(mDatabaseManager.getProfileConnectionPolicy(
1170                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
1171                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1172         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
1173         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
1174 
1175         generateDeviceAvailableMessageFromNative(mDevice, 1);
1176         generateConnectionMessageFromNative(
1177                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1178         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
1179         Assert.assertTrue(mService.getDevices().contains(mDevice));
1180         mService.setDeviceVolume(mDevice, deviceOneVolume, false);
1181         verify(mNativeInterface, times(1)).setVolume(eq(mDevice), eq(deviceOneVolume));
1182 
1183         // Verify that second device gets the proper group volume level when connected
1184         generateDeviceAvailableMessageFromNative(mDeviceTwo, 1);
1185         generateConnectionMessageFromNative(
1186                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1187         Assert.assertEquals(
1188                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
1189         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
1190         mService.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false);
1191         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(deviceTwoVolume));
1192 
1193         // Both devices are in the same group
1194         when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId);
1195         when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId);
1196 
1197         // Register callback and verify it is called with known devices
1198         IBluetoothVolumeControlCallback callback =
1199                 Mockito.mock(IBluetoothVolumeControlCallback.class);
1200         Binder binder = Mockito.mock(Binder.class);
1201         when(callback.asBinder()).thenReturn(binder);
1202 
1203         int size = mService.mCallbacks.getRegisteredCallbackCount();
1204         mServiceBinder.registerCallback(callback, mAttributionSource);
1205         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
1206 
1207         verify(callback, times(1)).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume));
1208         verify(callback, times(1)).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceTwoVolume));
1209     }
1210 
1211     @Test
testServiceBinderTestNotifyNewRegisteredCallback()1212     public void testServiceBinderTestNotifyNewRegisteredCallback() throws Exception {
1213         mSetFlagsRule.enableFlags(
1214                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
1215         int groupId = 1;
1216         int deviceOneVolume = 46;
1217         int deviceTwoVolume = 36;
1218 
1219         // Update the device policy so okToConnect() returns true
1220         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1221         when(mDatabaseManager.getProfileConnectionPolicy(
1222                         any(BluetoothDevice.class), eq(BluetoothProfile.VOLUME_CONTROL)))
1223                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1224         doReturn(true).when(mNativeInterface).connectVolumeControl(any(BluetoothDevice.class));
1225         doReturn(true).when(mNativeInterface).disconnectVolumeControl(any(BluetoothDevice.class));
1226 
1227         generateDeviceAvailableMessageFromNative(mDevice, 1);
1228         generateConnectionMessageFromNative(
1229                 mDevice, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1230         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDevice));
1231         Assert.assertTrue(mService.getDevices().contains(mDevice));
1232         mService.setDeviceVolume(mDevice, deviceOneVolume, false);
1233         verify(mNativeInterface, times(1)).setVolume(eq(mDevice), eq(deviceOneVolume));
1234 
1235         // Verify that second device gets the proper group volume level when connected
1236         generateDeviceAvailableMessageFromNative(mDeviceTwo, 1);
1237         generateConnectionMessageFromNative(
1238                 mDeviceTwo, BluetoothProfile.STATE_CONNECTED, BluetoothProfile.STATE_DISCONNECTED);
1239         Assert.assertEquals(
1240                 BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(mDeviceTwo));
1241         Assert.assertTrue(mService.getDevices().contains(mDeviceTwo));
1242         mService.setDeviceVolume(mDeviceTwo, deviceTwoVolume, false);
1243         verify(mNativeInterface, times(1)).setVolume(eq(mDeviceTwo), eq(deviceTwoVolume));
1244 
1245         // Both devices are in the same group
1246         when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId);
1247         when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId);
1248 
1249         // Register callback and verify it is called with known devices
1250         IBluetoothVolumeControlCallback callback =
1251                 Mockito.mock(IBluetoothVolumeControlCallback.class);
1252         Binder binder = Mockito.mock(Binder.class);
1253         when(callback.asBinder()).thenReturn(binder);
1254 
1255         int size = mService.mCallbacks.getRegisteredCallbackCount();
1256         mServiceBinder.registerCallback(callback, mAttributionSource);
1257         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
1258 
1259         IBluetoothVolumeControlCallback callback_new_client =
1260                 Mockito.mock(IBluetoothVolumeControlCallback.class);
1261         Binder binder_new_client = Mockito.mock(Binder.class);
1262         when(callback_new_client.asBinder()).thenReturn(binder_new_client);
1263 
1264         mServiceBinder.notifyNewRegisteredCallback(callback_new_client, mAttributionSource);
1265         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
1266 
1267         // This shall be done only once after mServiceBinder.registerCallback
1268         verify(callback, times(1)).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume));
1269         verify(callback, times(1)).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceTwoVolume));
1270 
1271         // This shall be done only once after mServiceBinder.updateNewRegistedCallback
1272         verify(callback_new_client, times(1))
1273                 .onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume));
1274         verify(callback_new_client, times(1))
1275                 .onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceTwoVolume));
1276     }
1277 
1278     @Test
testServiceBinderMuteMethods()1279     public void testServiceBinderMuteMethods() throws Exception {
1280         mServiceBinder.mute(mDevice, mAttributionSource);
1281         verify(mNativeInterface).mute(mDevice);
1282 
1283         mServiceBinder.unmute(mDevice, mAttributionSource);
1284         verify(mNativeInterface).unmute(mDevice);
1285 
1286         int groupId = 1;
1287         mServiceBinder.muteGroup(groupId, mAttributionSource);
1288         verify(mNativeInterface).muteGroup(groupId);
1289 
1290         mServiceBinder.unmuteGroup(groupId, mAttributionSource);
1291         verify(mNativeInterface).unmuteGroup(groupId);
1292     }
1293 
1294     @Test
testVolumeControlOffsetDescriptor()1295     public void testVolumeControlOffsetDescriptor() {
1296         VolumeControlService.VolumeControlOffsetDescriptor descriptor =
1297                 new VolumeControlService.VolumeControlOffsetDescriptor();
1298         int invalidId = -1;
1299         int validId = 10;
1300         int testValue = 100;
1301         String testDesc = "testDescription";
1302         int testLocation = 10000;
1303 
1304         Assert.assertEquals(0, descriptor.size());
1305         descriptor.add(validId);
1306         Assert.assertEquals(1, descriptor.size());
1307 
1308         Assert.assertFalse(descriptor.setValue(invalidId, testValue));
1309         Assert.assertTrue(descriptor.setValue(validId, testValue));
1310         Assert.assertEquals(0, descriptor.getValue(invalidId));
1311         Assert.assertEquals(testValue, descriptor.getValue(validId));
1312 
1313         Assert.assertFalse(descriptor.setDescription(invalidId, testDesc));
1314         Assert.assertTrue(descriptor.setDescription(validId, testDesc));
1315         Assert.assertEquals(null, descriptor.getDescription(invalidId));
1316         Assert.assertEquals(testDesc, descriptor.getDescription(validId));
1317 
1318         Assert.assertFalse(descriptor.setLocation(invalidId, testLocation));
1319         Assert.assertTrue(descriptor.setLocation(validId, testLocation));
1320         Assert.assertEquals(0, descriptor.getLocation(invalidId));
1321         Assert.assertEquals(testLocation, descriptor.getLocation(validId));
1322 
1323         StringBuilder sb = new StringBuilder();
1324         descriptor.dump(sb);
1325         Assert.assertTrue(sb.toString().contains(testDesc));
1326 
1327         descriptor.add(validId + 1);
1328         Assert.assertEquals(2, descriptor.size());
1329         descriptor.remove(validId);
1330         Assert.assertEquals(1, descriptor.size());
1331         descriptor.clear();
1332         Assert.assertEquals(0, descriptor.size());
1333     }
1334 
1335     @Test
testDump_doesNotCrash()1336     public void testDump_doesNotCrash() throws Exception {
1337         connectDevice(mDevice);
1338 
1339         StringBuilder sb = new StringBuilder();
1340         mService.dump(sb);
1341     }
1342 
1343     /** Test Volume Control changed callback. */
1344     @Test
testVolumeControlChangedCallback()1345     public void testVolumeControlChangedCallback() throws Exception {
1346         mSetFlagsRule.enableFlags(
1347                 Flags.FLAG_LEAUDIO_BROADCAST_VOLUME_CONTROL_FOR_CONNECTED_DEVICES);
1348 
1349         int groupId = 1;
1350         int groupVolume = 56;
1351         int deviceOneVolume = 46;
1352 
1353         // Both devices are in the same group
1354         when(mLeAudioService.getGroupId(mDevice)).thenReturn(groupId);
1355         when(mLeAudioService.getGroupId(mDeviceTwo)).thenReturn(groupId);
1356 
1357         // Send a message to trigger connection completed
1358         generateDeviceAvailableMessageFromNative(mDevice, 2);
1359 
1360         mServiceBinder.setDeviceVolume(mDevice, groupVolume, true, mAttributionSource);
1361         verify(mNativeInterface, times(1)).setGroupVolume(eq(groupId), eq(groupVolume));
1362 
1363         // Register callback and verify it is called with known devices
1364         IBluetoothVolumeControlCallback callback =
1365                 Mockito.mock(IBluetoothVolumeControlCallback.class);
1366         Binder binder = Mockito.mock(Binder.class);
1367         when(callback.asBinder()).thenReturn(binder);
1368 
1369         int size = mService.mCallbacks.getRegisteredCallbackCount();
1370         mServiceBinder.registerCallback(callback, mAttributionSource);
1371         Assert.assertEquals(size + 1, mService.mCallbacks.getRegisteredCallbackCount());
1372 
1373         when(mLeAudioService.getGroupDevices(groupId))
1374                 .thenReturn(Arrays.asList(mDevice, mDeviceTwo));
1375         // Send group volume change.
1376         VolumeControlStackEvent stackEvent =
1377                 new VolumeControlStackEvent(
1378                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
1379         stackEvent.device = null;
1380         stackEvent.valueInt1 = groupId;
1381         stackEvent.valueInt2 = groupVolume;
1382         stackEvent.valueBool1 = false;
1383         stackEvent.valueBool2 = true;
1384         mService.messageFromNative(stackEvent);
1385 
1386         verify(callback).onDeviceVolumeChanged(eq(mDeviceTwo), eq(groupVolume));
1387         verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(groupVolume));
1388 
1389         // Send device volume change only for one device
1390         VolumeControlStackEvent stackEvent2 =
1391                 new VolumeControlStackEvent(
1392                         VolumeControlStackEvent.EVENT_TYPE_VOLUME_STATE_CHANGED);
1393         stackEvent2.device = mDevice;
1394         stackEvent2.valueInt1 = -1;
1395         stackEvent2.valueInt2 = deviceOneVolume;
1396         stackEvent2.valueBool1 = false;
1397         stackEvent2.valueBool2 = false;
1398         mService.messageFromNative(stackEvent2);
1399 
1400         verify(callback).onDeviceVolumeChanged(eq(mDevice), eq(deviceOneVolume));
1401         verify(callback, never()).onDeviceVolumeChanged(eq(mDeviceTwo), eq(deviceOneVolume));
1402     }
1403 
connectDevice(BluetoothDevice device)1404     private void connectDevice(BluetoothDevice device) throws Exception {
1405         VolumeControlStackEvent connCompletedEvent;
1406 
1407         List<BluetoothDevice> prevConnectedDevices = mService.getConnectedDevices();
1408 
1409         // Update the device policy so okToConnect() returns true
1410         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1411         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL))
1412                 .thenReturn(BluetoothProfile.CONNECTION_POLICY_ALLOWED);
1413         doReturn(true).when(mNativeInterface).connectVolumeControl(device);
1414         doReturn(true).when(mNativeInterface).disconnectVolumeControl(device);
1415 
1416         // Send a connect request
1417         Assert.assertTrue("Connect failed", mService.connect(device));
1418 
1419         // Verify the connection state broadcast, and that we are in Connecting state
1420         verifyConnectionStateIntent(
1421                 TIMEOUT_MS,
1422                 device,
1423                 BluetoothProfile.STATE_CONNECTING,
1424                 BluetoothProfile.STATE_DISCONNECTED);
1425         Assert.assertEquals(BluetoothProfile.STATE_CONNECTING, mService.getConnectionState(device));
1426 
1427         // Send a message to trigger connection completed
1428         connCompletedEvent =
1429                 new VolumeControlStackEvent(
1430                         VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1431         connCompletedEvent.device = device;
1432         connCompletedEvent.valueInt1 = VolumeControlStackEvent.CONNECTION_STATE_CONNECTED;
1433         mService.messageFromNative(connCompletedEvent);
1434 
1435         // Verify the connection state broadcast, and that we are in Connected state
1436         verifyConnectionStateIntent(
1437                 TIMEOUT_MS,
1438                 device,
1439                 BluetoothProfile.STATE_CONNECTED,
1440                 BluetoothProfile.STATE_CONNECTING);
1441         Assert.assertEquals(BluetoothProfile.STATE_CONNECTED, mService.getConnectionState(device));
1442 
1443         // Verify that the device is in the list of connected devices
1444         List<BluetoothDevice> connectedDevices =
1445                 mServiceBinder.getConnectedDevices(mAttributionSource);
1446         Assert.assertTrue(connectedDevices.contains(device));
1447         // Verify the list of previously connected devices
1448         for (BluetoothDevice prevDevice : prevConnectedDevices) {
1449             Assert.assertTrue(connectedDevices.contains(prevDevice));
1450         }
1451     }
1452 
generateConnectionMessageFromNative( BluetoothDevice device, int newConnectionState, int oldConnectionState)1453     private void generateConnectionMessageFromNative(
1454             BluetoothDevice device, int newConnectionState, int oldConnectionState) {
1455         VolumeControlStackEvent stackEvent =
1456                 new VolumeControlStackEvent(
1457                         VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1458         stackEvent.device = device;
1459         stackEvent.valueInt1 = newConnectionState;
1460         mService.messageFromNative(stackEvent);
1461         // Verify the connection state broadcast
1462         verifyConnectionStateIntent(TIMEOUT_MS, device, newConnectionState, oldConnectionState);
1463     }
1464 
generateUnexpectedConnectionMessageFromNative( BluetoothDevice device, int newConnectionState)1465     private void generateUnexpectedConnectionMessageFromNative(
1466             BluetoothDevice device, int newConnectionState) {
1467         VolumeControlStackEvent stackEvent =
1468                 new VolumeControlStackEvent(
1469                         VolumeControlStackEvent.EVENT_TYPE_CONNECTION_STATE_CHANGED);
1470         stackEvent.device = device;
1471         stackEvent.valueInt1 = newConnectionState;
1472         mService.messageFromNative(stackEvent);
1473         // Verify the connection state broadcast
1474         verifyNoConnectionStateIntent(TIMEOUT_MS, device);
1475     }
1476 
generateDeviceAvailableMessageFromNative( BluetoothDevice device, int numberOfExtOffsets)1477     private void generateDeviceAvailableMessageFromNative(
1478             BluetoothDevice device, int numberOfExtOffsets) {
1479         // Send a message to trigger connection completed
1480         VolumeControlStackEvent event =
1481                 new VolumeControlStackEvent(VolumeControlStackEvent.EVENT_TYPE_DEVICE_AVAILABLE);
1482         event.device = device;
1483         event.valueInt1 = numberOfExtOffsets; // number of external outputs
1484         mService.messageFromNative(event);
1485     }
1486 
generateDeviceOffsetChangedMessageFromNative( BluetoothDevice device, int extOffsetIndex, int offset)1487     private void generateDeviceOffsetChangedMessageFromNative(
1488             BluetoothDevice device, int extOffsetIndex, int offset) {
1489         // Send a message to trigger connection completed
1490         VolumeControlStackEvent event =
1491                 new VolumeControlStackEvent(
1492                         VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_VOL_OFFSET_CHANGED);
1493         event.device = device;
1494         event.valueInt1 = extOffsetIndex; // external output index
1495         event.valueInt2 = offset; // offset value
1496         mService.messageFromNative(event);
1497     }
1498 
generateDeviceLocationChangedMessageFromNative( BluetoothDevice device, int extOffsetIndex, int location)1499     private void generateDeviceLocationChangedMessageFromNative(
1500             BluetoothDevice device, int extOffsetIndex, int location) {
1501         // Send a message to trigger connection completed
1502         VolumeControlStackEvent event =
1503                 new VolumeControlStackEvent(
1504                         VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_LOCATION_CHANGED);
1505         event.device = device;
1506         event.valueInt1 = extOffsetIndex; // external output index
1507         event.valueInt2 = location; // location
1508         mService.messageFromNative(event);
1509     }
1510 
generateDeviceDescriptionChangedMessageFromNative( BluetoothDevice device, int extOffsetIndex, String description)1511     private void generateDeviceDescriptionChangedMessageFromNative(
1512             BluetoothDevice device, int extOffsetIndex, String description) {
1513         // Send a message to trigger connection completed
1514         VolumeControlStackEvent event =
1515                 new VolumeControlStackEvent(
1516                         VolumeControlStackEvent.EVENT_TYPE_EXT_AUDIO_OUT_DESCRIPTION_CHANGED);
1517         event.device = device;
1518         event.valueInt1 = extOffsetIndex; // external output index
1519         event.valueString1 = description; // description
1520         mService.messageFromNative(event);
1521     }
1522 
1523     /**
1524      * Helper function to test okToConnect() method
1525      *
1526      * @param device test device
1527      * @param bondState bond state value, could be invalid
1528      * @param policy value, could be invalid
1529      * @param expected expected result from okToConnect()
1530      */
testOkToConnectCase( BluetoothDevice device, int bondState, int policy, boolean expected)1531     private void testOkToConnectCase(
1532             BluetoothDevice device, int bondState, int policy, boolean expected) {
1533         doReturn(bondState).when(mAdapterService).getBondState(device);
1534         when(mAdapterService.getDatabase()).thenReturn(mDatabaseManager);
1535         when(mDatabaseManager.getProfileConnectionPolicy(device, BluetoothProfile.VOLUME_CONTROL))
1536                 .thenReturn(policy);
1537         Assert.assertEquals(expected, mService.okToConnect(device));
1538     }
1539 }
1540