• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 HIMSA II K/S - www.himsa.com.
3  * Represented by EHIMA - www.ehima.com
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  *      http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  */
17 
18 package com.android.bluetooth.le_audio;
19 
20 import static org.mockito.Mockito.*;
21 
22 import android.annotation.Nullable;
23 import android.bluetooth.*;
24 import android.content.BroadcastReceiver;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.IntentFilter;
28 import android.media.AudioManager;
29 import android.os.Looper;
30 
31 import android.os.ParcelUuid;
32 import androidx.test.InstrumentationRegistry;
33 import androidx.test.filters.MediumTest;
34 import androidx.test.rule.ServiceTestRule;
35 import androidx.test.runner.AndroidJUnit4;
36 
37 import com.android.bluetooth.TestUtils;
38 import com.android.bluetooth.btservice.AdapterService;
39 import com.android.bluetooth.btservice.storage.DatabaseManager;
40 
41 import org.junit.After;
42 import org.junit.Assert;
43 import org.junit.Before;
44 import org.junit.Rule;
45 import org.junit.Test;
46 import org.junit.runner.RunWith;
47 import org.mockito.Mock;
48 import org.mockito.MockitoAnnotations;
49 import org.mockito.Spy;
50 
51 import java.util.concurrent.LinkedBlockingQueue;
52 import java.util.concurrent.TimeoutException;
53 import java.util.List;
54 
55 @MediumTest
56 @RunWith(AndroidJUnit4.class)
57 public class LeAudioBroadcastServiceTest {
58     private static final int TIMEOUT_MS = 1000;
59     @Rule
60     public final ServiceTestRule mServiceRule = new ServiceTestRule();
61     private BluetoothAdapter mAdapter;
62     private BluetoothDevice mDevice;
63     private Context mTargetContext;
64     private LeAudioService mService;
65     private LeAudioIntentReceiver mLeAudioIntentReceiver;
66     private LinkedBlockingQueue<Intent> mIntentQueue;
67     @Mock
68     private AdapterService mAdapterService;
69     @Mock
70     private DatabaseManager mDatabaseManager;
71     @Mock
72     private AudioManager mAudioManager;
73     @Mock
74     private LeAudioBroadcasterNativeInterface mNativeInterface;
75     @Mock private LeAudioTmapGattServer mTmapGattServer;
76     @Spy private LeAudioObjectsFactory mObjectsFactory = LeAudioObjectsFactory.getInstance();
77 
78     private static final String TEST_MAC_ADDRESS = "00:11:22:33:44:55";
79     private static final int TEST_BROADCAST_ID = 42;
80     private static final int TEST_ADVERTISER_SID = 1234;
81     private static final int TEST_PA_SYNC_INTERVAL = 100;
82     private static final int TEST_PRESENTATION_DELAY_MS = 345;
83 
84     private static final int TEST_CODEC_ID = 42;
85     private static final int TEST_CHANNEL_INDEX = 56;
86 
87     // For BluetoothLeAudioCodecConfigMetadata
88     private static final long TEST_AUDIO_LOCATION_FRONT_LEFT = 0x01;
89     private static final long TEST_AUDIO_LOCATION_FRONT_RIGHT = 0x02;
90 
91     // For BluetoothLeAudioContentMetadata
92     private static final String TEST_PROGRAM_INFO = "Test";
93     // German language code in ISO 639-3
94     private static final String TEST_LANGUAGE = "deu";
95     private static final String TEST_BROADCAST_NAME = "Name Test";
96 
97     private boolean mOnBroadcastStartedCalled = false;
98     private boolean mOnBroadcastStartFailedCalled = false;
99     private boolean mOnBroadcastStoppedCalled = false;
100     private boolean mOnBroadcastStopFailedCalled = false;
101     private boolean mOnPlaybackStartedCalled = false;
102     private boolean mOnPlaybackStoppedCalled = false;
103     private boolean mOnBroadcastUpdatedCalled = false;
104     private boolean mOnBroadcastUpdateFailedCalled = false;
105     private boolean mOnBroadcastMetadataChangedCalled = false;
106 
107     private final IBluetoothLeBroadcastCallback mCallbacks =
108             new IBluetoothLeBroadcastCallback.Stub() {
109         @Override
110         public void onBroadcastStarted(int reason, int broadcastId) {
111             mOnBroadcastStartedCalled = true;
112         }
113 
114         @Override
115         public void onBroadcastStartFailed(int reason) {
116             mOnBroadcastStartFailedCalled = true;
117         }
118 
119         @Override
120         public void onBroadcastStopped(int reason, int broadcastId) {
121             mOnBroadcastStoppedCalled = true;
122         }
123 
124         @Override
125         public void onBroadcastStopFailed(int reason) {
126             mOnBroadcastStopFailedCalled = true;
127         }
128 
129         @Override
130         public void onPlaybackStarted(int reason, int broadcastId) {
131             mOnPlaybackStartedCalled = true;
132         }
133 
134         @Override
135         public void onPlaybackStopped(int reason, int broadcastId) {
136             mOnPlaybackStoppedCalled = true;
137         }
138 
139         @Override
140         public void onBroadcastUpdated(int reason, int broadcastId) {
141             mOnBroadcastUpdatedCalled = true;
142         }
143 
144         @Override
145         public void onBroadcastUpdateFailed(int reason, int broadcastId) {
146             mOnBroadcastUpdateFailedCalled = true;
147         }
148 
149         @Override
150         public void onBroadcastMetadataChanged(int broadcastId,
151                 BluetoothLeBroadcastMetadata metadata) {
152             mOnBroadcastMetadataChangedCalled = true;
153         }
154     };
155 
156     @Before
setUp()157     public void setUp() throws Exception {
158         mTargetContext = InstrumentationRegistry.getTargetContext();
159 
160         // Set up mocks and test assets
161         MockitoAnnotations.initMocks(this);
162 
163         // Use spied objects factory
164         doNothing().when(mTmapGattServer).start(anyInt());
165         doNothing().when(mTmapGattServer).stop();
166         LeAudioObjectsFactory.setInstanceForTesting(mObjectsFactory);
167         doReturn(mTmapGattServer).when(mObjectsFactory).getTmapGattServer(any());
168 
169         if (Looper.myLooper() == null) {
170             Looper.prepare();
171         }
172 
173         TestUtils.setAdapterService(mAdapterService);
174         doReturn(mDatabaseManager).when(mAdapterService).getDatabase();
175         doReturn(true, false).when(mAdapterService).isStartedProfile(anyString());
176         doReturn(true).when(mAdapterService).isLeAudioBroadcastSourceSupported();
177         doReturn((long)(1 << BluetoothProfile.LE_AUDIO_BROADCAST) | (1 << BluetoothProfile.LE_AUDIO))
178                 .when(mAdapterService).getSupportedProfilesBitMask();
179 
180         mAdapter = BluetoothAdapter.getDefaultAdapter();
181 
182         startService();
183         mService.mAudioManager = mAudioManager;
184         mService.mLeAudioBroadcasterNativeInterface = mNativeInterface;
185 
186         // Set up the State Changed receiver
187         IntentFilter filter = new IntentFilter();
188         filter.setPriority(IntentFilter.SYSTEM_HIGH_PRIORITY);
189 
190         mLeAudioIntentReceiver = new LeAudioIntentReceiver();
191         mTargetContext.registerReceiver(mLeAudioIntentReceiver, filter);
192 
193         mDevice = TestUtils.getTestDevice(mAdapter, 0);
194         when(mNativeInterface.getDevice(any(byte[].class))).thenReturn(mDevice);
195 
196         mIntentQueue = new LinkedBlockingQueue<Intent>();
197     }
198 
199     @After
tearDown()200     public void tearDown() throws Exception {
201         if (mService == null) {
202             return;
203         }
204 
205         stopService();
206         mTargetContext.unregisterReceiver(mLeAudioIntentReceiver);
207         TestUtils.clearAdapterService(mAdapterService);
208         reset(mAudioManager);
209     }
210 
startService()211     private void startService() throws TimeoutException {
212         TestUtils.startService(mServiceRule, LeAudioService.class);
213         mService = LeAudioService.getLeAudioService();
214         Assert.assertNotNull(mService);
215     }
216 
stopService()217     private void stopService() throws TimeoutException {
218         TestUtils.stopService(mServiceRule, LeAudioService.class);
219         mService = LeAudioService.getLeAudioService();
220         Assert.assertNull(mService);
221     }
222 
223     /**
224      * Test getting LeAudio Service
225      */
226     @Test
testGetLeAudioService()227     public void testGetLeAudioService() {
228         Assert.assertEquals(mService, LeAudioService.getLeAudioService());
229     }
230 
231     @Test
testStopLeAudioService()232     public void testStopLeAudioService() {
233         Assert.assertEquals(mService, LeAudioService.getLeAudioService());
234 
235         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
236             public void run() {
237                 Assert.assertTrue(mService.stop());
238             }
239         });
240         InstrumentationRegistry.getInstrumentation().runOnMainSync(new Runnable() {
241             public void run() {
242                 Assert.assertTrue(mService.start());
243             }
244         });
245     }
246 
verifyBroadcastStarted(int broadcastId, BluetoothLeBroadcastSettings settings)247     void verifyBroadcastStarted(int broadcastId, BluetoothLeBroadcastSettings settings) {
248         mService.createBroadcast(settings);
249 
250         List<BluetoothLeBroadcastSubgroupSettings> settingsList =
251                 settings.getSubgroupSettings();
252 
253         int[] expectedQualityArray =
254                 settingsList.stream()
255                         .mapToInt(setting -> setting.getPreferredQuality()).toArray();
256         byte[][] expectedDataArray =
257                 settingsList.stream()
258                         .map(setting -> setting.getContentMetadata().getRawMetadata())
259                         .toArray(byte[][]::new);
260 
261         verify(mNativeInterface, times(1)).createBroadcast(eq(true), eq(TEST_BROADCAST_NAME),
262                 eq(settings.getBroadcastCode()),
263                 eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
264                 eq(expectedQualityArray),
265                 eq(expectedDataArray));
266 
267         // Check if broadcast is started automatically when created
268         LeAudioStackEvent create_event =
269                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
270         create_event.valueInt1 = broadcastId;
271         create_event.valueBool1 = true;
272         mService.messageFromNative(create_event);
273 
274         // Verify if broadcast is auto-started on start
275         verify(mNativeInterface, times(1)).startBroadcast(eq(broadcastId));
276 
277         // Notify initial paused state
278         LeAudioStackEvent state_event =
279                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);
280         state_event.valueInt1 = broadcastId;
281         state_event.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_PAUSED;
282         mService.messageFromNative(state_event);
283 
284         // Switch to active streaming
285         state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);
286         state_event.valueInt1 = broadcastId;
287         state_event.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_STREAMING;
288         mService.messageFromNative(state_event);
289 
290         // Check if metadata is requested when the broadcast starts to stream
291         verify(mNativeInterface, times(1)).getBroadcastMetadata(eq(broadcastId));
292         Assert.assertFalse(mOnBroadcastStartFailedCalled);
293         Assert.assertTrue(mOnBroadcastStartedCalled);
294     }
295 
verifyBroadcastStopped(int broadcastId)296     void verifyBroadcastStopped(int broadcastId) {
297         mService.stopBroadcast(broadcastId);
298         verify(mNativeInterface, times(1)).stopBroadcast(eq(broadcastId));
299 
300         LeAudioStackEvent state_event =
301                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_STATE);
302         state_event.valueInt1 = broadcastId;
303         state_event.valueInt2 = LeAudioStackEvent.BROADCAST_STATE_STOPPED;
304         mService.messageFromNative(state_event);
305 
306         // Verify if broadcast is auto-destroyed on stop
307         verify(mNativeInterface, times(1)).destroyBroadcast(eq(broadcastId));
308 
309         state_event = new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_DESTROYED);
310         state_event.valueInt1 = broadcastId;
311         mService.messageFromNative(state_event);
312 
313         Assert.assertTrue(mOnBroadcastStoppedCalled);
314         Assert.assertFalse(mOnBroadcastStopFailedCalled);
315     }
316 
317     @Test
testCreateBroadcastNative()318     public void testCreateBroadcastNative() {
319         int broadcastId = 243;
320         byte[] code = {0x00, 0x01, 0x00, 0x02};
321 
322         mService.mBroadcastCallbacks.register(mCallbacks);
323 
324         BluetoothLeAudioContentMetadata.Builder meta_builder =
325                 new BluetoothLeAudioContentMetadata.Builder();
326         meta_builder.setLanguage("deu");
327         meta_builder.setProgramInfo("Subgroup broadcast info");
328         BluetoothLeAudioContentMetadata meta = meta_builder.build();
329 
330         verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 1));
331     }
332 
333     @Test
testCreateBroadcastNativeMultiGroups()334     public void testCreateBroadcastNativeMultiGroups() {
335         int broadcastId = 243;
336         byte[] code = {0x00, 0x01, 0x00, 0x02};
337 
338         mService.mBroadcastCallbacks.register(mCallbacks);
339 
340         BluetoothLeAudioContentMetadata.Builder meta_builder =
341                 new BluetoothLeAudioContentMetadata.Builder();
342         meta_builder.setLanguage("deu");
343         meta_builder.setProgramInfo("Subgroup broadcast info");
344         BluetoothLeAudioContentMetadata meta = meta_builder.build();
345 
346         verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 3));
347     }
348 
349     @Test
testCreateBroadcastNativeFailed()350     public void testCreateBroadcastNativeFailed() {
351         int broadcastId = 243;
352         byte[] code = {0x00, 0x01, 0x00, 0x02};
353 
354         mService.mBroadcastCallbacks.register(mCallbacks);
355 
356         BluetoothLeAudioContentMetadata.Builder meta_builder =
357                 new BluetoothLeAudioContentMetadata.Builder();
358         meta_builder.setLanguage("deu");
359         meta_builder.setProgramInfo("Public broadcast info");
360         BluetoothLeAudioContentMetadata meta = meta_builder.build();
361         BluetoothLeBroadcastSettings settings = buildBroadcastSettingsFromMetadata(meta, code, 1);
362         mService.createBroadcast(settings);
363 
364         // Test data with only one subgroup
365         int[] expectedQualityArray =
366                 {settings.getSubgroupSettings().get(0).getPreferredQuality()};
367         byte[][] expectedDataArray =
368                 {settings.getSubgroupSettings().get(0).getContentMetadata().getRawMetadata()};
369 
370         verify(mNativeInterface, times(1)).createBroadcast(eq(true), eq(TEST_BROADCAST_NAME),
371                 eq(code), eq(settings.getPublicBroadcastMetadata().getRawMetadata()),
372                 eq(expectedQualityArray),
373                 eq(expectedDataArray));
374 
375         LeAudioStackEvent create_event =
376                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_CREATED);
377         create_event.valueInt1 = broadcastId;
378         create_event.valueBool1 = false;
379         mService.messageFromNative(create_event);
380 
381         Assert.assertFalse(mOnBroadcastStartedCalled);
382         Assert.assertTrue(mOnBroadcastStartFailedCalled);
383     }
384 
385     @Test
testStartStopBroadcastNative()386     public void testStartStopBroadcastNative() {
387         int broadcastId = 243;
388         byte[] code = {0x00, 0x01, 0x00, 0x02};
389 
390         mService.mBroadcastCallbacks.register(mCallbacks);
391 
392         BluetoothLeAudioContentMetadata.Builder meta_builder =
393                 new BluetoothLeAudioContentMetadata.Builder();
394         meta_builder.setLanguage("deu");
395         meta_builder.setProgramInfo("Subgroup broadcast info");
396         BluetoothLeAudioContentMetadata meta = meta_builder.build();
397 
398         verifyBroadcastStarted(broadcastId, buildBroadcastSettingsFromMetadata(meta, code, 1));
399         verifyBroadcastStopped(broadcastId);
400     }
401 
402     @Test
testBroadcastInvalidBroadcastIdRequest()403     public void testBroadcastInvalidBroadcastIdRequest() {
404         int broadcastId = 243;
405 
406         mService.mBroadcastCallbacks.register(mCallbacks);
407 
408         // Stop non-existing broadcast
409         mService.stopBroadcast(broadcastId);
410         Assert.assertFalse(mOnBroadcastStoppedCalled);
411         Assert.assertTrue(mOnBroadcastStopFailedCalled);
412 
413         // Update metadata for non-existing broadcast
414         BluetoothLeAudioContentMetadata.Builder meta_builder =
415         new BluetoothLeAudioContentMetadata.Builder();
416         meta_builder.setLanguage("eng");
417         meta_builder.setProgramInfo("Public broadcast info");
418         mService.updateBroadcast(broadcastId,
419                 buildBroadcastSettingsFromMetadata(meta_builder.build(), null, 1));
420         Assert.assertFalse(mOnBroadcastUpdatedCalled);
421         Assert.assertTrue(mOnBroadcastUpdateFailedCalled);
422     }
423 
createBroadcastSubgroup()424     private BluetoothLeBroadcastSubgroup createBroadcastSubgroup() {
425         BluetoothLeAudioCodecConfigMetadata codecMetadata =
426                 new BluetoothLeAudioCodecConfigMetadata.Builder()
427                         .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_LEFT).build();
428         BluetoothLeAudioContentMetadata contentMetadata =
429                 new BluetoothLeAudioContentMetadata.Builder()
430                         .setProgramInfo(TEST_PROGRAM_INFO).setLanguage(TEST_LANGUAGE).build();
431         BluetoothLeBroadcastSubgroup.Builder builder = new BluetoothLeBroadcastSubgroup.Builder()
432                 .setCodecId(TEST_CODEC_ID)
433                 .setCodecSpecificConfig(codecMetadata)
434                 .setContentMetadata(contentMetadata);
435 
436         BluetoothLeAudioCodecConfigMetadata channelCodecMetadata =
437                 new BluetoothLeAudioCodecConfigMetadata.Builder()
438                         .setAudioLocation(TEST_AUDIO_LOCATION_FRONT_RIGHT).build();
439 
440         // builder expect at least one channel
441         BluetoothLeBroadcastChannel channel =
442         new BluetoothLeBroadcastChannel.Builder()
443                 .setSelected(true)
444                 .setChannelIndex(TEST_CHANNEL_INDEX)
445                 .setCodecMetadata(channelCodecMetadata)
446                 .build();
447         builder.addChannel(channel);
448         return builder.build();
449     }
450 
createBroadcastMetadata()451     private BluetoothLeBroadcastMetadata createBroadcastMetadata() {
452         BluetoothDevice testDevice =
453         mAdapter.getRemoteLeDevice(TEST_MAC_ADDRESS, BluetoothDevice.ADDRESS_TYPE_RANDOM);
454 
455         BluetoothLeBroadcastMetadata.Builder builder = new BluetoothLeBroadcastMetadata.Builder()
456                         .setEncrypted(false)
457                         .setSourceDevice(testDevice, BluetoothDevice.ADDRESS_TYPE_RANDOM)
458                         .setSourceAdvertisingSid(TEST_ADVERTISER_SID)
459                         .setBroadcastId(TEST_BROADCAST_ID)
460                         .setBroadcastCode(null)
461                         .setPaSyncInterval(TEST_PA_SYNC_INTERVAL)
462                         .setPresentationDelayMicros(TEST_PRESENTATION_DELAY_MS);
463         // builder expect at least one subgroup
464         builder.addSubgroup(createBroadcastSubgroup());
465         return builder.build();
466     }
467 
468     @Test
testGetAllBroadcastMetadata()469     public void testGetAllBroadcastMetadata() {
470         int broadcastId = 243;
471         byte[] code = {0x00, 0x01, 0x00};
472 
473         BluetoothLeAudioContentMetadata.Builder meta_builder =
474                 new BluetoothLeAudioContentMetadata.Builder();
475         meta_builder.setLanguage("ENG");
476         meta_builder.setProgramInfo("Public broadcast info");
477         BluetoothLeAudioContentMetadata meta = meta_builder.build();
478         mService.createBroadcast(buildBroadcastSettingsFromMetadata(meta, code, 1));
479 
480         // Inject metadata stack event and verify if getter API works as expected
481         LeAudioStackEvent state_event =
482                 new LeAudioStackEvent(LeAudioStackEvent.EVENT_TYPE_BROADCAST_METADATA_CHANGED);
483         state_event.valueInt1 = broadcastId;
484         state_event.broadcastMetadata = createBroadcastMetadata();
485         mService.messageFromNative(state_event);
486 
487         List<BluetoothLeBroadcastMetadata> meta_list = mService.getAllBroadcastMetadata();
488         Assert.assertNotNull(meta_list);
489         Assert.assertNotEquals(meta_list.size(), 0);
490         Assert.assertEquals(meta_list.get(0), state_event.broadcastMetadata);
491     }
492 
493     private class LeAudioIntentReceiver extends BroadcastReceiver {
494         @Override
onReceive(Context context, Intent intent)495         public void onReceive(Context context, Intent intent) {
496             try {
497                 mIntentQueue.put(intent);
498             } catch (InterruptedException e) {
499                 Assert.fail("Cannot add Intent to the queue: " + e.getMessage());
500             }
501         }
502     }
503 
buildBroadcastSettingsFromMetadata( BluetoothLeAudioContentMetadata contentMetadata, @Nullable byte[] broadcastCode, int numOfGroups)504     private BluetoothLeBroadcastSettings buildBroadcastSettingsFromMetadata(
505             BluetoothLeAudioContentMetadata contentMetadata,
506             @Nullable byte[] broadcastCode,
507             int numOfGroups) {
508         BluetoothLeAudioContentMetadata.Builder publicMetaBuilder =
509                 new BluetoothLeAudioContentMetadata.Builder();
510         publicMetaBuilder.setProgramInfo("Public broadcast info");
511 
512         BluetoothLeBroadcastSubgroupSettings.Builder subgroupBuilder =
513                 new BluetoothLeBroadcastSubgroupSettings.Builder()
514                 .setContentMetadata(contentMetadata);
515 
516         BluetoothLeBroadcastSettings.Builder builder = new BluetoothLeBroadcastSettings.Builder()
517                         .setPublicBroadcast(true)
518                         .setBroadcastName(TEST_BROADCAST_NAME)
519                         .setBroadcastCode(broadcastCode)
520                         .setPublicBroadcastMetadata(publicMetaBuilder.build());
521         // builder expect at least one subgroup setting
522         for (int i = 0; i < numOfGroups; i++) {
523             // add subgroup settings with the same content
524             builder.addSubgroupSettings(subgroupBuilder.build());
525         }
526         return builder.build();
527     }
528 }
529