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.mcp;
19 
20 import static org.mockito.Mockito.*;
21 
22 import android.bluetooth.BluetoothAdapter;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothGatt;
25 import android.bluetooth.BluetoothGattCharacteristic;
26 import android.bluetooth.BluetoothGattDescriptor;
27 import android.bluetooth.BluetoothGattService;
28 import android.bluetooth.BluetoothLeBroadcastMetadata;
29 import android.os.Looper;
30 import android.platform.test.flag.junit.SetFlagsRule;
31 
32 import androidx.test.filters.MediumTest;
33 import androidx.test.runner.AndroidJUnit4;
34 
35 import com.android.bluetooth.TestUtils;
36 import com.android.bluetooth.btservice.AdapterService;
37 import com.android.bluetooth.le_audio.LeAudioService;
38 
39 import org.junit.After;
40 import org.junit.Assert;
41 import org.junit.Before;
42 import org.junit.Rule;
43 import org.junit.Test;
44 import org.junit.runner.RunWith;
45 import org.mockito.ArgumentCaptor;
46 import org.mockito.Captor;
47 import org.mockito.Mock;
48 import org.mockito.junit.MockitoJUnit;
49 import org.mockito.junit.MockitoRule;
50 
51 import java.nio.ByteBuffer;
52 import java.nio.ByteOrder;
53 import java.util.ArrayList;
54 import java.util.HashMap;
55 import java.util.List;
56 import java.util.Map;
57 import java.util.UUID;
58 
59 @MediumTest
60 @RunWith(AndroidJUnit4.class)
61 public class MediaControlGattServiceTest {
62     private BluetoothAdapter mAdapter;
63     private BluetoothDevice mCurrentDevice;
64 
65     private static final UUID UUID_GMCS = UUID.fromString("00001849-0000-1000-8000-00805f9b34fb");
66     private static final UUID UUID_CCCD = UUID.fromString("00002902-0000-1000-8000-00805f9b34fb");
67     public static final int TEST_CCID = 1;
68 
69     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
70 
71     private MediaControlGattService mMcpService;
72 
73     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
74 
75     @Mock private AdapterService mAdapterService;
76     @Mock private MediaControlGattService.BluetoothGattServerProxy mMockGattServer;
77     @Mock private McpService mMockMcpService;
78     @Mock private LeAudioService mMockLeAudioService;
79     @Mock private MediaControlServiceCallbacks mMockMcsCallbacks;
80 
81     @Captor private ArgumentCaptor<BluetoothGattService> mGattServiceCaptor;
82 
83     @Before
setUp()84     public void setUp() throws Exception {
85         if (Looper.myLooper() == null) {
86             Looper.prepare();
87         }
88 
89         TestUtils.setAdapterService(mAdapterService);
90         mAdapter = BluetoothAdapter.getDefaultAdapter();
91 
92         doReturn(true).when(mMockGattServer).addService(any(BluetoothGattService.class));
93         doReturn(new BluetoothDevice[0]).when(mAdapterService).getBondedDevices();
94 
95         mMcpService = new MediaControlGattService(mMockMcpService, mMockMcsCallbacks, TEST_CCID);
96         mMcpService.setBluetoothGattServerForTesting(mMockGattServer);
97         mMcpService.setServiceManagerForTesting(mMockMcpService);
98         mMcpService.setLeAudioServiceForTesting(mMockLeAudioService);
99 
100         when(mMockMcpService.getDeviceAuthorization(any(BluetoothDevice.class)))
101                 .thenReturn(BluetoothDevice.ACCESS_ALLOWED);
102     }
103 
104     @After
tearDown()105     public void tearDown() throws Exception {
106         mMcpService = null;
107         reset(mMockGattServer);
108         reset(mMockMcpService);
109         reset(mMockMcsCallbacks);
110         reset(mMockLeAudioService);
111         TestUtils.clearAdapterService(mAdapterService);
112     }
113 
prepareConnectedDevice()114     private void prepareConnectedDevice() {
115         if (mCurrentDevice == null) {
116             mCurrentDevice = TestUtils.getTestDevice(mAdapter, 0);
117             List<BluetoothDevice> devices = new ArrayList<BluetoothDevice>();
118             devices.add(mCurrentDevice);
119             doReturn(devices).when(mMockGattServer).getConnectedDevices();
120             doReturn(true).when(mMockGattServer).isDeviceConnected(eq(mCurrentDevice));
121         }
122     }
123 
prepareConnectedDevicesCccVal( BluetoothGattCharacteristic characteristic, byte[] value)124     private void prepareConnectedDevicesCccVal(
125             BluetoothGattCharacteristic characteristic, byte[] value) {
126         prepareConnectedDevice();
127         mMcpService.setCcc(mCurrentDevice, characteristic.getUuid(), 0, value, true);
128     }
129 
130     @Test
testInit()131     public void testInit() {
132         long mMandatoryFeatures = ServiceFeature.ALL_MANDATORY_SERVICE_FEATURES;
133 
134         doReturn(mMandatoryFeatures).when(mMockMcsCallbacks).onGetFeatureFlags();
135         Assert.assertTrue(mMcpService.init(UUID_GMCS));
136         Assert.assertEquals(mMcpService.getServiceUuid(), UUID_GMCS);
137         Assert.assertEquals(mMcpService.getContentControlId(), TEST_CCID);
138 
139         doReturn(true).when(mMockGattServer).removeService(any(BluetoothGattService.class));
140         mMcpService.destroy();
141         verify(mMockMcsCallbacks).onServiceInstanceUnregistered(eq(ServiceStatus.OK));
142     }
143 
144     @Test
testFailingInit()145     public void testFailingInit() {
146         long mMandatoryFeatures = 0;
147 
148         doReturn(mMandatoryFeatures).when(mMockMcsCallbacks).onGetFeatureFlags();
149         Assert.assertFalse(mMcpService.init(UUID_GMCS));
150     }
151 
initAllFeaturesGattService()152     private BluetoothGattService initAllFeaturesGattService() {
153         long features =
154                 ServiceFeature.ALL_MANDATORY_SERVICE_FEATURES
155                         | ServiceFeature.PLAYER_ICON_OBJ_ID
156                         | ServiceFeature.PLAYER_ICON_URL
157                         | ServiceFeature.PLAYBACK_SPEED
158                         | ServiceFeature.SEEKING_SPEED
159                         | ServiceFeature.CURRENT_TRACK_SEGMENT_OBJ_ID
160                         | ServiceFeature.CURRENT_TRACK_OBJ_ID
161                         | ServiceFeature.NEXT_TRACK_OBJ_ID
162                         | ServiceFeature.CURRENT_GROUP_OBJ_ID
163                         | ServiceFeature.PARENT_GROUP_OBJ_ID
164                         | ServiceFeature.PLAYING_ORDER
165                         | ServiceFeature.PLAYING_ORDER_SUPPORTED
166                         | ServiceFeature.MEDIA_CONTROL_POINT
167                         | ServiceFeature.MEDIA_CONTROL_POINT_OPCODES_SUPPORTED
168                         | ServiceFeature.SEARCH_RESULT_OBJ_ID
169                         | ServiceFeature.SEARCH_CONTROL_POINT
170                         // Notifications
171                         | ServiceFeature.PLAYER_NAME_NOTIFY
172                         | ServiceFeature.TRACK_TITLE_NOTIFY
173                         | ServiceFeature.TRACK_DURATION_NOTIFY
174                         | ServiceFeature.TRACK_POSITION_NOTIFY
175                         | ServiceFeature.PLAYBACK_SPEED_NOTIFY
176                         | ServiceFeature.SEEKING_SPEED_NOTIFY
177                         | ServiceFeature.CURRENT_TRACK_OBJ_ID_NOTIFY
178                         | ServiceFeature.NEXT_TRACK_OBJ_ID_NOTIFY
179                         | ServiceFeature.CURRENT_GROUP_OBJ_ID_NOTIFY
180                         | ServiceFeature.PARENT_GROUP_OBJ_ID_NOTIFY
181                         | ServiceFeature.PLAYING_ORDER_NOTIFY
182                         | ServiceFeature.MEDIA_CONTROL_POINT_OPCODES_SUPPORTED_NOTIFY;
183 
184         doReturn(features).when(mMockMcsCallbacks).onGetFeatureFlags();
185         Assert.assertTrue(mMcpService.init(UUID_GMCS));
186 
187         verify(mMockGattServer).addService(mGattServiceCaptor.capture());
188 
189         // Capture GATT Service definition for verification
190         BluetoothGattService service = mGattServiceCaptor.getValue();
191         Assert.assertNotNull(service);
192 
193         // Call back the low level GATT callback and expect proper higher level callback to be
194         // called
195         mMcpService.mServerCallback.onServiceAdded(BluetoothGatt.GATT_SUCCESS, service);
196         verify(mMockMcsCallbacks)
197                 .onServiceInstanceRegistered(
198                         any(ServiceStatus.class), any(MediaControlGattServiceInterface.class));
199 
200         return service;
201     }
202 
203     @Test
testGattServerFullInitialState()204     public void testGattServerFullInitialState() {
205         BluetoothGattService service = initAllFeaturesGattService();
206 
207         // Check initial state of all mandatory characteristics
208         BluetoothGattCharacteristic characteristic =
209                 service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME);
210         Assert.assertNotNull(characteristic);
211         Assert.assertEquals(
212                 characteristic.getProperties(),
213                 BluetoothGattCharacteristic.PROPERTY_READ
214                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
215         Assert.assertEquals("", characteristic.getStringValue(0));
216 
217         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE);
218         Assert.assertNotNull(characteristic);
219         Assert.assertEquals(
220                 characteristic.getProperties(),
221                 BluetoothGattCharacteristic.PROPERTY_READ
222                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
223         Assert.assertEquals("", characteristic.getStringValue(0));
224 
225         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION);
226         Assert.assertNotNull(characteristic);
227         Assert.assertEquals(
228                 characteristic.getProperties(),
229                 BluetoothGattCharacteristic.PROPERTY_READ
230                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
231         Assert.assertEquals(
232                 0xFFFFFFFF,
233                 characteristic
234                         .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
235                         .intValue());
236 
237         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
238         Assert.assertNotNull(characteristic);
239         Assert.assertEquals(
240                 characteristic.getProperties(),
241                 BluetoothGattCharacteristic.PROPERTY_READ
242                         | BluetoothGattCharacteristic.PROPERTY_WRITE
243                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
244                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
245         Assert.assertEquals(
246                 0xFFFFFFFF,
247                 characteristic
248                         .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
249                         .intValue());
250 
251         characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
252         Assert.assertNotNull(characteristic);
253         Assert.assertEquals(
254                 characteristic.getProperties(),
255                 BluetoothGattCharacteristic.PROPERTY_READ
256                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
257         Assert.assertEquals(
258                 MediaState.INACTIVE.getValue(),
259                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
260 
261         characteristic = service.getCharacteristic(MediaControlGattService.UUID_CONTENT_CONTROL_ID);
262         Assert.assertNotNull(characteristic);
263         Assert.assertEquals(
264                 characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
265         Assert.assertEquals(
266                 TEST_CCID,
267                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
268 
269         // Check initial state of all optional characteristics
270         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_OBJ_ID);
271         Assert.assertNotNull(characteristic);
272         Assert.assertEquals(
273                 characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
274         Assert.assertTrue(characteristic.getValue().length == 0);
275 
276         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_URL);
277         Assert.assertNotNull(characteristic);
278         Assert.assertEquals(
279                 characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
280         Assert.assertEquals("", characteristic.getStringValue(0));
281 
282         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_CHANGED);
283         Assert.assertNotNull(characteristic);
284         Assert.assertEquals(
285                 characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_NOTIFY);
286 
287         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
288         Assert.assertNotNull(characteristic);
289         Assert.assertEquals(
290                 characteristic.getProperties(),
291                 BluetoothGattCharacteristic.PROPERTY_READ
292                         | BluetoothGattCharacteristic.PROPERTY_WRITE
293                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
294                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
295         Assert.assertEquals(
296                 0,
297                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0).intValue());
298 
299         characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED);
300         Assert.assertNotNull(characteristic);
301         Assert.assertEquals(
302                 characteristic.getProperties(),
303                 BluetoothGattCharacteristic.PROPERTY_READ
304                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
305         Assert.assertEquals(
306                 0,
307                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_SINT8, 0).intValue());
308 
309         characteristic =
310                 service.getCharacteristic(
311                         MediaControlGattService.UUID_CURRENT_TRACK_SEGMENT_OBJ_ID);
312         Assert.assertNotNull(characteristic);
313         Assert.assertEquals(
314                 characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
315         Assert.assertTrue(characteristic.getValue().length == 0);
316 
317         characteristic =
318                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_TRACK_OBJ_ID);
319         Assert.assertNotNull(characteristic);
320         Assert.assertEquals(
321                 characteristic.getProperties(),
322                 BluetoothGattCharacteristic.PROPERTY_READ
323                         | BluetoothGattCharacteristic.PROPERTY_WRITE
324                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
325                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
326         Assert.assertTrue(characteristic.getValue().length == 0);
327 
328         characteristic = service.getCharacteristic(MediaControlGattService.UUID_NEXT_TRACK_OBJ_ID);
329         Assert.assertNotNull(characteristic);
330         Assert.assertEquals(
331                 characteristic.getProperties(),
332                 BluetoothGattCharacteristic.PROPERTY_READ
333                         | BluetoothGattCharacteristic.PROPERTY_WRITE
334                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
335                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
336         Assert.assertTrue(characteristic.getValue().length == 0);
337 
338         characteristic =
339                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_GROUP_OBJ_ID);
340         Assert.assertNotNull(characteristic);
341         Assert.assertEquals(
342                 characteristic.getProperties(),
343                 BluetoothGattCharacteristic.PROPERTY_READ
344                         | BluetoothGattCharacteristic.PROPERTY_WRITE
345                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
346                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
347         Assert.assertTrue(characteristic.getValue().length == 0);
348 
349         characteristic =
350                 service.getCharacteristic(MediaControlGattService.UUID_PARENT_GROUP_OBJ_ID);
351         Assert.assertNotNull(characteristic);
352         Assert.assertEquals(
353                 characteristic.getProperties(),
354                 BluetoothGattCharacteristic.PROPERTY_READ
355                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
356         Assert.assertTrue(characteristic.getValue().length == 0);
357 
358         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
359         Assert.assertNotNull(characteristic);
360         Assert.assertEquals(
361                 characteristic.getProperties(),
362                 BluetoothGattCharacteristic.PROPERTY_READ
363                         | BluetoothGattCharacteristic.PROPERTY_WRITE
364                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE
365                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
366         Assert.assertEquals(
367                 PlayingOrder.SINGLE_ONCE.getValue(),
368                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
369 
370         characteristic =
371                 service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED);
372         Assert.assertNotNull(characteristic);
373         Assert.assertEquals(
374                 characteristic.getProperties(), BluetoothGattCharacteristic.PROPERTY_READ);
375         Assert.assertEquals(
376                 SupportedPlayingOrder.SINGLE_ONCE,
377                 characteristic
378                         .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)
379                         .intValue());
380 
381         characteristic =
382                 service.getCharacteristic(MediaControlGattService.UUID_MEDIA_CONTROL_POINT);
383         Assert.assertNotNull(characteristic);
384         Assert.assertEquals(
385                 characteristic.getProperties(),
386                 BluetoothGattCharacteristic.PROPERTY_NOTIFY
387                         | BluetoothGattCharacteristic.PROPERTY_WRITE
388                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
389 
390         characteristic =
391                 service.getCharacteristic(
392                         MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
393         Assert.assertNotNull(characteristic);
394         Assert.assertEquals(
395                 characteristic.getProperties(),
396                 BluetoothGattCharacteristic.PROPERTY_READ
397                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
398         Assert.assertEquals(
399                 MediaControlGattService.INITIAL_SUPPORTED_OPCODES,
400                 characteristic
401                         .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
402                         .intValue());
403 
404         characteristic =
405                 service.getCharacteristic(MediaControlGattService.UUID_SEARCH_RESULT_OBJ_ID);
406         Assert.assertNotNull(characteristic);
407         Assert.assertEquals(
408                 characteristic.getProperties(),
409                 BluetoothGattCharacteristic.PROPERTY_READ
410                         | BluetoothGattCharacteristic.PROPERTY_NOTIFY);
411         Assert.assertTrue(characteristic.getValue().length == 0);
412 
413         characteristic =
414                 service.getCharacteristic(MediaControlGattService.UUID_SEARCH_CONTROL_POINT);
415         Assert.assertNotNull(characteristic);
416         Assert.assertEquals(
417                 characteristic.getProperties(),
418                 BluetoothGattCharacteristic.PROPERTY_NOTIFY
419                         | BluetoothGattCharacteristic.PROPERTY_WRITE
420                         | BluetoothGattCharacteristic.PROPERTY_WRITE_NO_RESPONSE);
421     }
422 
423     @Test
testUpdatePlayerState()424     public void testUpdatePlayerState() {
425         BluetoothGattService service = initAllFeaturesGattService();
426         Map<PlayerStateField, Object> state_map = new HashMap<>();
427         float playback_speed = 0.5f;
428         PlayingOrder playing_order = PlayingOrder.IN_ORDER_REPEAT;
429         long track_position = 100;
430         String player_name = "TestPlayerName";
431         String icon_url = "www.testiconurl.com";
432         Long icon_obj_id = 7L;
433         Integer playing_order_supported =
434                 SupportedPlayingOrder.IN_ORDER_REPEAT
435                         | SupportedPlayingOrder.SINGLE_ONCE
436                         | SupportedPlayingOrder.SINGLE_REPEAT
437                         | SupportedPlayingOrder.IN_ORDER_ONCE
438                         | SupportedPlayingOrder.IN_ORDER_REPEAT
439                         | SupportedPlayingOrder.OLDEST_ONCE
440                         | SupportedPlayingOrder.OLDEST_REPEAT
441                         | SupportedPlayingOrder.NEWEST_ONCE
442                         | SupportedPlayingOrder.NEWEST_REPEAT
443                         | SupportedPlayingOrder.SHUFFLE_ONCE
444                         | SupportedPlayingOrder.SHUFFLE_REPEAT;
445         Integer opcodes_supported =
446                 Request.SupportedOpcodes.NONE
447                         | Request.SupportedOpcodes.PLAY
448                         | Request.SupportedOpcodes.PAUSE
449                         | Request.SupportedOpcodes.FAST_REWIND
450                         | Request.SupportedOpcodes.FAST_FORWARD
451                         | Request.SupportedOpcodes.STOP
452                         | Request.SupportedOpcodes.MOVE_RELATIVE
453                         | Request.SupportedOpcodes.PREVIOUS_SEGMENT
454                         | Request.SupportedOpcodes.NEXT_SEGMENT
455                         | Request.SupportedOpcodes.FIRST_SEGMENT
456                         | Request.SupportedOpcodes.LAST_SEGMENT
457                         | Request.SupportedOpcodes.GOTO_SEGMENT
458                         | Request.SupportedOpcodes.PREVIOUS_TRACK
459                         | Request.SupportedOpcodes.NEXT_TRACK
460                         | Request.SupportedOpcodes.FIRST_TRACK
461                         | Request.SupportedOpcodes.LAST_TRACK
462                         | Request.SupportedOpcodes.GOTO_TRACK
463                         | Request.SupportedOpcodes.PREVIOUS_GROUP
464                         | Request.SupportedOpcodes.NEXT_GROUP
465                         | Request.SupportedOpcodes.FIRST_GROUP
466                         | Request.SupportedOpcodes.LAST_GROUP;
467         String track_title = "Test Song";
468         long track_duration = 1000;
469         MediaState playback_state = MediaState.SEEKING;
470         float seeking_speed = 2.0f;
471 
472         state_map.put(PlayerStateField.PLAYBACK_SPEED, playback_speed);
473         state_map.put(PlayerStateField.PLAYING_ORDER, playing_order);
474         state_map.put(PlayerStateField.TRACK_POSITION, track_position);
475         state_map.put(PlayerStateField.PLAYER_NAME, player_name);
476         state_map.put(PlayerStateField.ICON_URL, icon_url);
477         state_map.put(PlayerStateField.ICON_OBJ_ID, icon_obj_id);
478         state_map.put(PlayerStateField.PLAYING_ORDER_SUPPORTED, playing_order_supported);
479         state_map.put(PlayerStateField.OPCODES_SUPPORTED, opcodes_supported);
480         state_map.put(PlayerStateField.TRACK_TITLE, track_title);
481         state_map.put(PlayerStateField.TRACK_DURATION, track_duration);
482         state_map.put(PlayerStateField.PLAYBACK_STATE, playback_state);
483         state_map.put(PlayerStateField.SEEKING_SPEED, seeking_speed);
484         mMcpService.updatePlayerState(state_map);
485 
486         BluetoothGattCharacteristic characteristic =
487                 service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
488         Assert.assertNotNull(characteristic);
489         Assert.assertEquals(
490                 playback_speed, mMcpService.getPlaybackSpeedChar().floatValue(), 0.001f);
491 
492         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
493         Assert.assertNotNull(characteristic);
494         Assert.assertEquals(
495                 playing_order.getValue(),
496                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
497 
498         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
499         Assert.assertNotNull(characteristic);
500         // Set value as ms, kept in characteristic as 0.01s
501         Assert.assertEquals(
502                 track_position / 10,
503                 characteristic
504                         .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
505                         .intValue());
506 
507         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME);
508         Assert.assertNotNull(characteristic);
509         Assert.assertEquals(player_name, characteristic.getStringValue(0));
510 
511         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_URL);
512         Assert.assertNotNull(characteristic);
513         Assert.assertEquals(icon_url, characteristic.getStringValue(0));
514 
515         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYER_ICON_OBJ_ID);
516         Assert.assertNotNull(characteristic);
517         Assert.assertEquals(
518                 icon_obj_id.longValue(), mMcpService.byteArray2ObjId(characteristic.getValue()));
519 
520         characteristic =
521                 service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED);
522         Assert.assertNotNull(characteristic);
523         Assert.assertEquals(
524                 playing_order_supported.intValue(),
525                 characteristic
526                         .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT16, 0)
527                         .intValue());
528 
529         characteristic =
530                 service.getCharacteristic(
531                         MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
532         Assert.assertNotNull(characteristic);
533         Assert.assertEquals(
534                 opcodes_supported.intValue(),
535                 characteristic
536                         .getIntValue(BluetoothGattCharacteristic.FORMAT_UINT32, 0)
537                         .intValue());
538 
539         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE);
540         Assert.assertNotNull(characteristic);
541         Assert.assertEquals(track_title, characteristic.getStringValue(0));
542 
543         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION);
544         Assert.assertNotNull(characteristic);
545         // Set value as ms, kept in characteristic as 0.01s
546         Assert.assertEquals(
547                 track_duration / 10,
548                 characteristic
549                         .getIntValue(BluetoothGattCharacteristic.FORMAT_SINT32, 0)
550                         .intValue());
551 
552         characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
553         Assert.assertNotNull(characteristic);
554         Assert.assertEquals(
555                 playback_state.getValue(),
556                 characteristic.getIntValue(BluetoothGattCharacteristic.FORMAT_UINT8, 0).intValue());
557 
558         characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED);
559         Assert.assertNotNull(characteristic);
560         Assert.assertEquals(seeking_speed, mMcpService.getSeekingSpeedChar().floatValue(), 0.001f);
561     }
562 
verifyWriteObjIdsValid( BluetoothGattCharacteristic characteristic, long value, int id)563     private void verifyWriteObjIdsValid(
564             BluetoothGattCharacteristic characteristic, long value, int id) {
565         mMcpService.mServerCallback.onCharacteristicWriteRequest(
566                 mCurrentDevice,
567                 1,
568                 characteristic,
569                 false,
570                 true,
571                 0,
572                 mMcpService.objId2ByteArray(value));
573 
574         verify(mMockMcsCallbacks).onSetObjectIdRequest(eq(id), eq(value));
575 
576         verify(mMockGattServer)
577                 .sendResponse(
578                         eq(mCurrentDevice),
579                         eq(1),
580                         eq(BluetoothGatt.GATT_SUCCESS),
581                         eq(0),
582                         eq(mMcpService.objId2ByteArray(value)));
583     }
584 
585     @Test
testWriteCallbacksValid()586     public void testWriteCallbacksValid() {
587         BluetoothGattService service = initAllFeaturesGattService();
588         int track_position = 100;
589         byte playback_speed = 64;
590         long current_track_obj_id = 7;
591         long next_track_obj_id = 77;
592         long current_group_obj_id = 777;
593         PlayingOrder playing_order = PlayingOrder.IN_ORDER_REPEAT;
594         Integer playing_order_supported = SupportedPlayingOrder.IN_ORDER_REPEAT;
595 
596         BluetoothGattCharacteristic characteristic =
597                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
598 
599         ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES).order(ByteOrder.LITTLE_ENDIAN);
600         bb.putInt((int) track_position);
601 
602         prepareConnectedDevice();
603         mMcpService.mServerCallback.onCharacteristicWriteRequest(
604                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
605 
606         verify(mMockMcsCallbacks).onTrackPositionSetRequest(eq(track_position * 10L));
607 
608         verify(mMockGattServer)
609                 .sendResponse(
610                         eq(mCurrentDevice),
611                         eq(1),
612                         eq(BluetoothGatt.GATT_SUCCESS),
613                         eq(0),
614                         eq(bb.array()));
615 
616         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
617 
618         bb = ByteBuffer.allocate(Byte.BYTES);
619         bb.put(playback_speed);
620 
621         mMcpService.mServerCallback.onCharacteristicWriteRequest(
622                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
623 
624         verify(mMockMcsCallbacks)
625                 .onPlaybackSpeedSetRequest(eq((float) Math.pow(2, playback_speed / 64.0)));
626 
627         verify(mMockGattServer)
628                 .sendResponse(
629                         eq(mCurrentDevice),
630                         eq(1),
631                         eq(BluetoothGatt.GATT_SUCCESS),
632                         eq(0),
633                         eq(bb.array()));
634 
635         characteristic =
636                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_TRACK_OBJ_ID);
637         verifyWriteObjIdsValid(
638                 characteristic, current_track_obj_id, ObjectIds.CURRENT_TRACK_OBJ_ID);
639 
640         characteristic = service.getCharacteristic(MediaControlGattService.UUID_NEXT_TRACK_OBJ_ID);
641         verifyWriteObjIdsValid(characteristic, next_track_obj_id, ObjectIds.NEXT_TRACK_OBJ_ID);
642 
643         characteristic =
644                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_GROUP_OBJ_ID);
645         verifyWriteObjIdsValid(
646                 characteristic, current_group_obj_id, ObjectIds.CURRENT_GROUP_OBJ_ID);
647 
648         characteristic =
649                 service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED);
650         characteristic.setValue(
651                 playing_order_supported, BluetoothGattCharacteristic.FORMAT_UINT16, 0);
652         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
653         bb = ByteBuffer.allocate(Byte.BYTES);
654         bb.put((byte) playing_order.getValue());
655 
656         mMcpService.mServerCallback.onCharacteristicWriteRequest(
657                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
658 
659         verify(mMockMcsCallbacks).onPlayingOrderSetRequest(eq(playing_order.getValue()));
660 
661         verify(mMockGattServer)
662                 .sendResponse(
663                         eq(mCurrentDevice),
664                         eq(1),
665                         eq(BluetoothGatt.GATT_SUCCESS),
666                         eq(0),
667                         eq(bb.array()));
668     }
669 
verifyWriteObjIdsInvalid( BluetoothGattCharacteristic characteristic, byte diffByte)670     private void verifyWriteObjIdsInvalid(
671             BluetoothGattCharacteristic characteristic, byte diffByte) {
672         byte[] value = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, diffByte};
673         mMcpService.mServerCallback.onCharacteristicWriteRequest(
674                 mCurrentDevice, 1, characteristic, false, true, 0, value);
675 
676         verify(mMockGattServer)
677                 .sendResponse(
678                         eq(mCurrentDevice),
679                         eq(1),
680                         eq(BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH),
681                         eq(0),
682                         eq(value));
683     }
684 
685     @Test
testWriteCallbacksInvalid()686     public void testWriteCallbacksInvalid() {
687         BluetoothGattService service = initAllFeaturesGattService();
688         int track_position = 100;
689         byte playback_speed = 64;
690         PlayingOrder playing_order = PlayingOrder.IN_ORDER_REPEAT;
691         byte diff_byte = 0x00;
692 
693         BluetoothGattCharacteristic characteristic =
694                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
695 
696         ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES + 1).order(ByteOrder.LITTLE_ENDIAN);
697         bb.putInt((int) track_position);
698         bb.put((byte) 0);
699 
700         prepareConnectedDevice();
701         mMcpService.mServerCallback.onCharacteristicWriteRequest(
702                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
703 
704         verify(mMockGattServer)
705                 .sendResponse(
706                         eq(mCurrentDevice),
707                         eq(1),
708                         eq(BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH),
709                         eq(0),
710                         eq(bb.array()));
711 
712         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
713 
714         bb = ByteBuffer.allocate(Byte.BYTES + 1);
715         bb.put(playback_speed);
716         bb.put((byte) 0);
717 
718         mMcpService.mServerCallback.onCharacteristicWriteRequest(
719                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
720 
721         verify(mMockGattServer)
722                 .sendResponse(
723                         eq(mCurrentDevice),
724                         eq(1),
725                         eq(BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH),
726                         eq(0),
727                         eq(bb.array()));
728 
729         characteristic =
730                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_TRACK_OBJ_ID);
731         verifyWriteObjIdsInvalid(characteristic, diff_byte++);
732 
733         characteristic = service.getCharacteristic(MediaControlGattService.UUID_NEXT_TRACK_OBJ_ID);
734         verifyWriteObjIdsInvalid(characteristic, diff_byte++);
735 
736         characteristic =
737                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_GROUP_OBJ_ID);
738         verifyWriteObjIdsInvalid(characteristic, diff_byte++);
739 
740         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
741         bb = ByteBuffer.allocate(Byte.BYTES + 1);
742         bb.put((byte) playing_order.getValue());
743         bb.put((byte) 0);
744 
745         mMcpService.mServerCallback.onCharacteristicWriteRequest(
746                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
747 
748         verify(mMockGattServer)
749                 .sendResponse(
750                         eq(mCurrentDevice),
751                         eq(1),
752                         eq(BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH),
753                         eq(0),
754                         eq(bb.array()));
755     }
756 
testNotify(boolean registerForNotification)757     private void testNotify(boolean registerForNotification) {
758         BluetoothGattService service = initAllFeaturesGattService();
759         String player_name = "TestPlayerName";
760         String track_title = "Test Song";
761         long track_duration = 1000;
762         long track_position = 100;
763         float playback_speed = 0.5f;
764         float seeking_speed = 2.0f;
765         Long obj_id = 7L;
766         PlayingOrder playing_order = PlayingOrder.IN_ORDER_REPEAT;
767         int playing_order_supported = SupportedPlayingOrder.IN_ORDER_REPEAT;
768         int playback_state = MediaState.SEEKING.getValue();
769         Integer opcodes_supported =
770                 Request.SupportedOpcodes.NONE
771                         | Request.SupportedOpcodes.PLAY
772                         | Request.SupportedOpcodes.PAUSE
773                         | Request.SupportedOpcodes.FAST_REWIND
774                         | Request.SupportedOpcodes.FAST_FORWARD
775                         | Request.SupportedOpcodes.STOP
776                         | Request.SupportedOpcodes.MOVE_RELATIVE
777                         | Request.SupportedOpcodes.PREVIOUS_SEGMENT
778                         | Request.SupportedOpcodes.NEXT_SEGMENT
779                         | Request.SupportedOpcodes.FIRST_SEGMENT
780                         | Request.SupportedOpcodes.LAST_SEGMENT
781                         | Request.SupportedOpcodes.GOTO_SEGMENT
782                         | Request.SupportedOpcodes.PREVIOUS_TRACK
783                         | Request.SupportedOpcodes.NEXT_TRACK
784                         | Request.SupportedOpcodes.FIRST_TRACK
785                         | Request.SupportedOpcodes.LAST_TRACK
786                         | Request.SupportedOpcodes.GOTO_TRACK
787                         | Request.SupportedOpcodes.PREVIOUS_GROUP
788                         | Request.SupportedOpcodes.NEXT_GROUP
789                         | Request.SupportedOpcodes.FIRST_GROUP
790                         | Request.SupportedOpcodes.LAST_GROUP;
791         byte[] ccc_val =
792                 registerForNotification
793                         ? BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone()
794                         : BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE.clone();
795         int times_cnt = registerForNotification ? 1 : 0;
796         int media_control_request_opcode = Request.Opcodes.MOVE_RELATIVE;
797 
798         BluetoothGattCharacteristic characteristic =
799                 service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME);
800         prepareConnectedDevicesCccVal(characteristic, ccc_val);
801         mMcpService.updatePlayerNameChar(player_name, true);
802         verify(mMockGattServer, times(times_cnt))
803                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
804 
805         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_TITLE);
806         prepareConnectedDevicesCccVal(characteristic, ccc_val);
807         mMcpService.updateTrackTitleChar(track_title, true);
808         verify(mMockGattServer, times(times_cnt))
809                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
810 
811         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_DURATION);
812         prepareConnectedDevicesCccVal(characteristic, ccc_val);
813         mMcpService.updateTrackDurationChar(track_duration, true);
814         verify(mMockGattServer, times(times_cnt))
815                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
816 
817         characteristic = service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
818         prepareConnectedDevicesCccVal(characteristic, ccc_val);
819         mMcpService.updateMediaStateChar(playback_state);
820         verify(mMockGattServer, times(times_cnt))
821                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
822 
823         characteristic = service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
824         prepareConnectedDevicesCccVal(characteristic, ccc_val);
825         mMcpService.updateTrackPositionChar(track_position, false);
826         verify(mMockGattServer, times(times_cnt))
827                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
828 
829         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
830         prepareConnectedDevicesCccVal(characteristic, ccc_val);
831         mMcpService.updatePlaybackSpeedChar(playback_speed, true);
832         verify(mMockGattServer, times(times_cnt))
833                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
834 
835         characteristic = service.getCharacteristic(MediaControlGattService.UUID_SEEKING_SPEED);
836         prepareConnectedDevicesCccVal(characteristic, ccc_val);
837         mMcpService.updateSeekingSpeedChar(seeking_speed, true);
838         verify(mMockGattServer, times(times_cnt))
839                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
840 
841         characteristic =
842                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_TRACK_OBJ_ID);
843         prepareConnectedDevicesCccVal(characteristic, ccc_val);
844         mMcpService.updateObjectID(ObjectIds.CURRENT_TRACK_OBJ_ID, obj_id);
845         verify(mMockGattServer, times(times_cnt))
846                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
847 
848         characteristic = service.getCharacteristic(MediaControlGattService.UUID_NEXT_TRACK_OBJ_ID);
849         prepareConnectedDevicesCccVal(characteristic, ccc_val);
850         mMcpService.updateObjectID(ObjectIds.NEXT_TRACK_OBJ_ID, obj_id);
851         verify(mMockGattServer, times(times_cnt))
852                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
853 
854         characteristic =
855                 service.getCharacteristic(MediaControlGattService.UUID_CURRENT_GROUP_OBJ_ID);
856         prepareConnectedDevicesCccVal(characteristic, ccc_val);
857         mMcpService.updateObjectID(ObjectIds.CURRENT_GROUP_OBJ_ID, obj_id);
858         verify(mMockGattServer, times(times_cnt))
859                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
860 
861         characteristic =
862                 service.getCharacteristic(MediaControlGattService.UUID_PARENT_GROUP_OBJ_ID);
863         prepareConnectedDevicesCccVal(characteristic, ccc_val);
864         mMcpService.updateObjectID(ObjectIds.PARENT_GROUP_OBJ_ID, obj_id);
865         verify(mMockGattServer, times(times_cnt))
866                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
867 
868         characteristic = service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
869         prepareConnectedDevicesCccVal(characteristic, ccc_val);
870         mMcpService.updatePlayingOrderSupportedChar(playing_order_supported);
871         mMcpService.updatePlayingOrderChar(playing_order, true);
872         verify(mMockGattServer, times(times_cnt))
873                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
874 
875         characteristic =
876                 service.getCharacteristic(MediaControlGattService.UUID_MEDIA_CONTROL_POINT);
877         prepareConnectedDevicesCccVal(characteristic, ccc_val);
878         mMcpService.setMediaControlRequestResult(
879                 new Request(media_control_request_opcode, 0), Request.Results.SUCCESS);
880         verify(mMockGattServer, times(times_cnt))
881                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
882 
883         characteristic =
884                 service.getCharacteristic(
885                         MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
886         prepareConnectedDevicesCccVal(characteristic, ccc_val);
887         mMcpService.updateSupportedOpcodesChar(opcodes_supported, true);
888         verify(mMockGattServer, times(times_cnt))
889                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
890 
891         characteristic =
892                 service.getCharacteristic(MediaControlGattService.UUID_SEARCH_RESULT_OBJ_ID);
893         prepareConnectedDevicesCccVal(characteristic, ccc_val);
894         mMcpService.updateObjectID(ObjectIds.SEARCH_RESULT_OBJ_ID, obj_id);
895         verify(mMockGattServer, times(times_cnt))
896                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
897 
898         characteristic =
899                 service.getCharacteristic(MediaControlGattService.UUID_SEARCH_CONTROL_POINT);
900         prepareConnectedDevicesCccVal(characteristic, ccc_val);
901         mMcpService.setSearchRequestResult(null, SearchRequest.Results.SUCCESS, obj_id);
902         verify(mMockGattServer, times(times_cnt))
903                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
904     }
905 
906     @Test
testNotifyRegistered()907     public void testNotifyRegistered() {
908         testNotify(true);
909     }
910 
911     @Test
testNotifyNotRegistered()912     public void testNotifyNotRegistered() {
913         testNotify(false);
914     }
915 
verifyMediaControlPointRequest( int opcode, Integer value, int expectedGattResult, int invocation_count)916     private void verifyMediaControlPointRequest(
917             int opcode, Integer value, int expectedGattResult, int invocation_count) {
918         ByteBuffer bb;
919 
920         if (expectedGattResult == BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH) {
921             bb = ByteBuffer.allocate(6).order(ByteOrder.LITTLE_ENDIAN);
922         } else {
923             bb =
924                     ByteBuffer.allocate(value != null ? (Integer.BYTES + Byte.BYTES) : Byte.BYTES)
925                             .order(ByteOrder.LITTLE_ENDIAN);
926         }
927         bb.put((byte) opcode);
928         if (value != null) {
929             bb.putInt(value);
930         }
931 
932         Assert.assertEquals(
933                 expectedGattResult,
934                 mMcpService.handleMediaControlPointRequest(mCurrentDevice, bb.array()));
935 
936         if (expectedGattResult == BluetoothGatt.GATT_SUCCESS) {
937             // Verify if callback comes to profile
938             verify(mMockMcsCallbacks, times(invocation_count++))
939                     .onMediaControlRequest(any(Request.class));
940         }
941     }
942 
verifyMediaControlPointRequests(int expectedGattResult)943     private void verifyMediaControlPointRequests(int expectedGattResult) {
944         BluetoothGattService service = initAllFeaturesGattService();
945         int invocation_count = 1;
946         Integer opcodes_supported =
947                 Request.SupportedOpcodes.PLAY
948                         | Request.SupportedOpcodes.PAUSE
949                         | Request.SupportedOpcodes.FAST_REWIND
950                         | Request.SupportedOpcodes.FAST_FORWARD
951                         | Request.SupportedOpcodes.STOP
952                         | Request.SupportedOpcodes.MOVE_RELATIVE
953                         | Request.SupportedOpcodes.PREVIOUS_SEGMENT
954                         | Request.SupportedOpcodes.NEXT_SEGMENT
955                         | Request.SupportedOpcodes.FIRST_SEGMENT
956                         | Request.SupportedOpcodes.LAST_SEGMENT
957                         | Request.SupportedOpcodes.GOTO_SEGMENT
958                         | Request.SupportedOpcodes.PREVIOUS_TRACK
959                         | Request.SupportedOpcodes.NEXT_TRACK
960                         | Request.SupportedOpcodes.FIRST_TRACK
961                         | Request.SupportedOpcodes.LAST_TRACK
962                         | Request.SupportedOpcodes.GOTO_TRACK
963                         | Request.SupportedOpcodes.PREVIOUS_GROUP
964                         | Request.SupportedOpcodes.NEXT_GROUP
965                         | Request.SupportedOpcodes.FIRST_GROUP
966                         | Request.SupportedOpcodes.LAST_GROUP
967                         | Request.SupportedOpcodes.GOTO_GROUP;
968 
969         BluetoothGattCharacteristic characteristic =
970                 service.getCharacteristic(
971                         MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
972         prepareConnectedDevicesCccVal(
973                 characteristic, BluetoothGattDescriptor.DISABLE_NOTIFICATION_VALUE.clone());
974         mMcpService.updateSupportedOpcodesChar(opcodes_supported, true);
975         verify(mMockGattServer, times(0))
976                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
977 
978         verifyMediaControlPointRequest(
979                 Request.Opcodes.PLAY, null, expectedGattResult, invocation_count++);
980         verifyMediaControlPointRequest(
981                 Request.Opcodes.PAUSE, null, expectedGattResult, invocation_count++);
982         verifyMediaControlPointRequest(
983                 Request.Opcodes.FAST_REWIND, null, expectedGattResult, invocation_count++);
984         verifyMediaControlPointRequest(
985                 Request.Opcodes.FAST_FORWARD, null, expectedGattResult, invocation_count++);
986         verifyMediaControlPointRequest(
987                 Request.Opcodes.STOP, null, expectedGattResult, invocation_count++);
988         verifyMediaControlPointRequest(
989                 Request.Opcodes.MOVE_RELATIVE, 100, expectedGattResult, invocation_count++);
990         verifyMediaControlPointRequest(
991                 Request.Opcodes.PREVIOUS_SEGMENT, null, expectedGattResult, invocation_count++);
992         verifyMediaControlPointRequest(
993                 Request.Opcodes.NEXT_SEGMENT, null, expectedGattResult, invocation_count++);
994         verifyMediaControlPointRequest(
995                 Request.Opcodes.FIRST_SEGMENT, null, expectedGattResult, invocation_count++);
996         verifyMediaControlPointRequest(
997                 Request.Opcodes.LAST_SEGMENT, null, expectedGattResult, invocation_count++);
998         verifyMediaControlPointRequest(
999                 Request.Opcodes.GOTO_SEGMENT, 10, expectedGattResult, invocation_count++);
1000         verifyMediaControlPointRequest(
1001                 Request.Opcodes.PREVIOUS_TRACK, null, expectedGattResult, invocation_count++);
1002         verifyMediaControlPointRequest(
1003                 Request.Opcodes.NEXT_TRACK, null, expectedGattResult, invocation_count++);
1004         verifyMediaControlPointRequest(
1005                 Request.Opcodes.FIRST_TRACK, null, expectedGattResult, invocation_count++);
1006         verifyMediaControlPointRequest(
1007                 Request.Opcodes.LAST_TRACK, null, expectedGattResult, invocation_count++);
1008         verifyMediaControlPointRequest(
1009                 Request.Opcodes.GOTO_TRACK, 7, expectedGattResult, invocation_count++);
1010         verifyMediaControlPointRequest(
1011                 Request.Opcodes.PREVIOUS_GROUP, null, expectedGattResult, invocation_count++);
1012         verifyMediaControlPointRequest(
1013                 Request.Opcodes.NEXT_GROUP, null, expectedGattResult, invocation_count++);
1014         verifyMediaControlPointRequest(
1015                 Request.Opcodes.FIRST_GROUP, null, expectedGattResult, invocation_count++);
1016         verifyMediaControlPointRequest(
1017                 Request.Opcodes.LAST_GROUP, null, expectedGattResult, invocation_count++);
1018         verifyMediaControlPointRequest(
1019                 Request.Opcodes.GOTO_GROUP, 10, expectedGattResult, invocation_count++);
1020     }
1021 
1022     @Test
testMediaControlPointRequestValid()1023     public void testMediaControlPointRequestValid() {
1024         verifyMediaControlPointRequests(BluetoothGatt.GATT_SUCCESS);
1025     }
1026 
1027     @Test
testMediaControlPointRequestInvalidLength()1028     public void testMediaControlPointRequestInvalidLength() {
1029         verifyMediaControlPointRequests(BluetoothGatt.GATT_INVALID_ATTRIBUTE_LENGTH);
1030     }
1031 
1032     @Test
testMediaControlPointRequestInvalid()1033     public void testMediaControlPointRequestInvalid() {
1034         Assert.assertFalse(mMcpService.isOpcodeSupported(Request.Opcodes.PLAY));
1035     }
1036 
1037     @Test
testMediaControlPointeRequest_OpcodePlayCallLeAudioServiceSetActiveDevice()1038     public void testMediaControlPointeRequest_OpcodePlayCallLeAudioServiceSetActiveDevice() {
1039         initAllFeaturesGattService();
1040         prepareConnectedDevice();
1041         mMcpService.updateSupportedOpcodesChar(Request.SupportedOpcodes.PLAY, true);
1042         verifyMediaControlPointRequest(Request.Opcodes.PLAY, null, BluetoothGatt.GATT_SUCCESS, 1);
1043 
1044         final List<BluetoothLeBroadcastMetadata> metadataList = mock(List.class);
1045         when(mMockLeAudioService.getAllBroadcastMetadata()).thenReturn(metadataList);
1046         verify(mMockMcsCallbacks).onMediaControlRequest(any(Request.class));
1047     }
1048 
1049     @Test
testPlaybackSpeedWrite()1050     public void testPlaybackSpeedWrite() {
1051         BluetoothGattService service = initAllFeaturesGattService();
1052         byte playback_speed = -64;
1053 
1054         BluetoothGattCharacteristic characteristic =
1055                 service.getCharacteristic(MediaControlGattService.UUID_PLAYBACK_SPEED);
1056         prepareConnectedDevicesCccVal(
1057                 characteristic, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone());
1058 
1059         ByteBuffer bb = ByteBuffer.allocate(Byte.BYTES);
1060         bb.put(playback_speed);
1061 
1062         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1063                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1064 
1065         verify(mMockMcsCallbacks)
1066                 .onPlaybackSpeedSetRequest(eq((float) Math.pow(2, playback_speed / 64.0)));
1067 
1068         // Fake characteristic write - this is done by player status update
1069         characteristic.setValue(playback_speed, BluetoothGattCharacteristic.FORMAT_SINT8, 0);
1070 
1071         // Second set of the same value - does not bother player only sends notification
1072         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1073                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1074 
1075         verify(mMockGattServer)
1076                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
1077     }
1078 
1079     @Test
testUpdateSupportedOpcodesChar()1080     public void testUpdateSupportedOpcodesChar() {
1081         BluetoothGattService service = initAllFeaturesGattService();
1082         Integer opcodes_supported =
1083                 Request.SupportedOpcodes.PLAY
1084                         | Request.SupportedOpcodes.PAUSE
1085                         | Request.SupportedOpcodes.FAST_REWIND
1086                         | Request.SupportedOpcodes.FAST_FORWARD
1087                         | Request.SupportedOpcodes.STOP
1088                         | Request.SupportedOpcodes.MOVE_RELATIVE
1089                         | Request.SupportedOpcodes.PREVIOUS_SEGMENT
1090                         | Request.SupportedOpcodes.NEXT_SEGMENT
1091                         | Request.SupportedOpcodes.FIRST_SEGMENT
1092                         | Request.SupportedOpcodes.LAST_SEGMENT
1093                         | Request.SupportedOpcodes.GOTO_SEGMENT
1094                         | Request.SupportedOpcodes.PREVIOUS_TRACK
1095                         | Request.SupportedOpcodes.NEXT_TRACK
1096                         | Request.SupportedOpcodes.FIRST_TRACK
1097                         | Request.SupportedOpcodes.LAST_TRACK
1098                         | Request.SupportedOpcodes.GOTO_TRACK
1099                         | Request.SupportedOpcodes.PREVIOUS_GROUP
1100                         | Request.SupportedOpcodes.NEXT_GROUP
1101                         | Request.SupportedOpcodes.FIRST_GROUP
1102                         | Request.SupportedOpcodes.LAST_GROUP
1103                         | Request.SupportedOpcodes.GOTO_GROUP;
1104 
1105         BluetoothGattCharacteristic characteristic =
1106                 service.getCharacteristic(
1107                         MediaControlGattService.UUID_MEDIA_CONTROL_POINT_OPCODES_SUPPORTED);
1108         prepareConnectedDevicesCccVal(
1109                 characteristic, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone());
1110 
1111         mMcpService.updateSupportedOpcodesChar(opcodes_supported, true);
1112         verify(mMockGattServer)
1113                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
1114 
1115         // Verify if there will be no new notification triggered when nothing changes
1116         mMcpService.updateSupportedOpcodesChar(opcodes_supported, true);
1117         verify(mMockGattServer)
1118                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
1119 
1120         opcodes_supported = 0;
1121         mMcpService.updateSupportedOpcodesChar(opcodes_supported, true);
1122         verify(mMockGattServer, times(2))
1123                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
1124     }
1125 
1126     @Test
testPlayingOrderSupportedChar()1127     public void testPlayingOrderSupportedChar() {
1128         BluetoothGattService service = initAllFeaturesGattService();
1129         int playing_order_supported =
1130                 SupportedPlayingOrder.IN_ORDER_REPEAT | SupportedPlayingOrder.NEWEST_ONCE;
1131         PlayingOrder playing_order = PlayingOrder.IN_ORDER_REPEAT;
1132         ByteBuffer bb = ByteBuffer.allocate(Byte.BYTES);
1133 
1134         BluetoothGattCharacteristic characteristic =
1135                 service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER);
1136         prepareConnectedDevicesCccVal(
1137                 characteristic, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone());
1138 
1139         mMcpService.updatePlayingOrderSupportedChar(playing_order_supported);
1140 
1141         bb.put((byte) playing_order.getValue());
1142         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1143                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1144         verify(mMockMcsCallbacks).onPlayingOrderSetRequest(anyInt());
1145 
1146         // Not supported playing order should be ignored
1147         playing_order = PlayingOrder.SHUFFLE_ONCE;
1148         bb.put(0, (byte) playing_order.getValue());
1149         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1150                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1151         verify(mMockMcsCallbacks).onPlayingOrderSetRequest(anyInt());
1152 
1153         playing_order = PlayingOrder.NEWEST_ONCE;
1154         bb.put(0, (byte) playing_order.getValue());
1155         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1156                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1157         verify(mMockMcsCallbacks, times(2)).onPlayingOrderSetRequest(anyInt());
1158     }
1159 
1160     @Test
testCharacteristicReadRejectedUnauthorized()1161     public void testCharacteristicReadRejectedUnauthorized() {
1162         BluetoothGattService service = initAllFeaturesGattService();
1163 
1164         BluetoothGattCharacteristic characteristic =
1165                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
1166 
1167         prepareConnectedDevice();
1168         doReturn(BluetoothDevice.ACCESS_REJECTED)
1169                 .when(mMockMcpService)
1170                 .getDeviceAuthorization(any(BluetoothDevice.class));
1171 
1172         mMcpService.mServerCallback.onCharacteristicReadRequest(
1173                 mCurrentDevice, 1, 0, characteristic);
1174 
1175         verify(mMockGattServer)
1176                 .sendResponse(
1177                         eq(mCurrentDevice),
1178                         eq(1),
1179                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1180                         eq(0),
1181                         any());
1182     }
1183 
1184     @Test
testCharacteristicNotifyOnAuthorization()1185     public void testCharacteristicNotifyOnAuthorization() {
1186         BluetoothGattService service = initAllFeaturesGattService();
1187         prepareConnectedDevice();
1188 
1189         // Leave it as unauthorized yet
1190         doReturn(BluetoothDevice.ACCESS_REJECTED)
1191                 .when(mMockMcpService)
1192                 .getDeviceAuthorization(any(BluetoothDevice.class));
1193 
1194         BluetoothGattCharacteristic characteristic =
1195                 service.getCharacteristic(MediaControlGattService.UUID_PLAYING_ORDER_SUPPORTED);
1196         prepareConnectedDevicesCccVal(
1197                 characteristic, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone());
1198 
1199         // Assume no update on some of the characteristics
1200         BluetoothGattCharacteristic characteristic2 =
1201                 service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
1202         prepareConnectedDevicesCccVal(
1203                 characteristic2, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone());
1204         characteristic2.setValue((byte[]) null);
1205 
1206         BluetoothGattCharacteristic characteristic3 =
1207                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_CHANGED);
1208         prepareConnectedDevicesCccVal(
1209                 characteristic3, BluetoothGattDescriptor.ENABLE_NOTIFICATION_VALUE.clone());
1210         characteristic3.setValue((byte[]) null);
1211 
1212         // Call it once but expect no notification for the unauthorized device
1213         int playing_order_supported =
1214                 SupportedPlayingOrder.IN_ORDER_REPEAT | SupportedPlayingOrder.NEWEST_ONCE;
1215         mMcpService.updatePlayingOrderSupportedChar(playing_order_supported);
1216         verify(mMockGattServer, times(0))
1217                 .notifyCharacteristicChanged(eq(mCurrentDevice), any(), eq(false));
1218 
1219         // Expect a single notification for the just authorized device
1220         doReturn(BluetoothDevice.ACCESS_ALLOWED)
1221                 .when(mMockMcpService)
1222                 .getDeviceAuthorization(any(BluetoothDevice.class));
1223         mMcpService.onDeviceAuthorizationSet(mCurrentDevice);
1224         verify(mMockGattServer, times(0))
1225                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic2), eq(false));
1226         verify(mMockGattServer, times(0))
1227                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic3), eq(false));
1228         verify(mMockGattServer)
1229                 .notifyCharacteristicChanged(eq(mCurrentDevice), eq(characteristic), eq(false));
1230     }
1231 
1232     @Test
testCharacteristicReadUnknownUnauthorized()1233     public void testCharacteristicReadUnknownUnauthorized() {
1234         BluetoothGattService service = initAllFeaturesGattService();
1235 
1236         BluetoothGattCharacteristic characteristic =
1237                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
1238 
1239         prepareConnectedDevice();
1240         doReturn(BluetoothDevice.ACCESS_UNKNOWN)
1241                 .when(mMockMcpService)
1242                 .getDeviceAuthorization(any(BluetoothDevice.class));
1243 
1244         mMcpService.mServerCallback.onCharacteristicReadRequest(
1245                 mCurrentDevice, 1, 0, characteristic);
1246         verify(mMockMcpService, times(0)).onDeviceUnauthorized(eq(mCurrentDevice));
1247         verify(mMockGattServer, times(0))
1248                 .sendResponse(
1249                         eq(mCurrentDevice),
1250                         eq(1),
1251                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1252                         eq(0),
1253                         any());
1254     }
1255 
1256     @Test
testCharacteristicWriteRejectedUnauthorized()1257     public void testCharacteristicWriteRejectedUnauthorized() {
1258         BluetoothGattService service = initAllFeaturesGattService();
1259         int track_position = 100;
1260 
1261         BluetoothGattCharacteristic characteristic =
1262                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
1263 
1264         ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES + 1).order(ByteOrder.LITTLE_ENDIAN);
1265         bb.putInt((int) track_position);
1266         bb.put((byte) 0);
1267 
1268         prepareConnectedDevice();
1269         doReturn(BluetoothDevice.ACCESS_REJECTED)
1270                 .when(mMockMcpService)
1271                 .getDeviceAuthorization(any(BluetoothDevice.class));
1272 
1273         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1274                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1275 
1276         verify(mMockGattServer)
1277                 .sendResponse(
1278                         eq(mCurrentDevice),
1279                         eq(1),
1280                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1281                         eq(0),
1282                         any());
1283     }
1284 
1285     @Test
testCharacteristicWriteUnknownUnauthorized()1286     public void testCharacteristicWriteUnknownUnauthorized() {
1287         BluetoothGattService service = initAllFeaturesGattService();
1288         int track_position = 100;
1289 
1290         BluetoothGattCharacteristic characteristic =
1291                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION);
1292 
1293         ByteBuffer bb = ByteBuffer.allocate(Integer.BYTES + 1).order(ByteOrder.LITTLE_ENDIAN);
1294         bb.putInt((int) track_position);
1295         bb.put((byte) 0);
1296 
1297         prepareConnectedDevice();
1298         doReturn(BluetoothDevice.ACCESS_UNKNOWN)
1299                 .when(mMockMcpService)
1300                 .getDeviceAuthorization(any(BluetoothDevice.class));
1301 
1302         mMcpService.mServerCallback.onCharacteristicWriteRequest(
1303                 mCurrentDevice, 1, characteristic, false, true, 0, bb.array());
1304         verify(mMockMcpService).onDeviceUnauthorized(eq(mCurrentDevice));
1305     }
1306 
1307     @Test
testDescriptorReadRejectedUnauthorized()1308     public void testDescriptorReadRejectedUnauthorized() {
1309         BluetoothGattService service = initAllFeaturesGattService();
1310 
1311         BluetoothGattDescriptor descriptor =
1312                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION)
1313                         .getDescriptor(UUID_CCCD);
1314         Assert.assertNotNull(descriptor);
1315 
1316         prepareConnectedDevice();
1317         doReturn(BluetoothDevice.ACCESS_REJECTED)
1318                 .when(mMockMcpService)
1319                 .getDeviceAuthorization(any(BluetoothDevice.class));
1320 
1321         mMcpService.mServerCallback.onDescriptorReadRequest(mCurrentDevice, 1, 0, descriptor);
1322 
1323         verify(mMockGattServer)
1324                 .sendResponse(
1325                         eq(mCurrentDevice),
1326                         eq(1),
1327                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1328                         eq(0),
1329                         any());
1330     }
1331 
1332     @Test
testDescriptorReadUnknownUnauthorized()1333     public void testDescriptorReadUnknownUnauthorized() {
1334         BluetoothGattService service = initAllFeaturesGattService();
1335 
1336         BluetoothGattDescriptor descriptor =
1337                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION)
1338                         .getDescriptor(UUID_CCCD);
1339         Assert.assertNotNull(descriptor);
1340 
1341         prepareConnectedDevice();
1342         doReturn(BluetoothDevice.ACCESS_UNKNOWN)
1343                 .when(mMockMcpService)
1344                 .getDeviceAuthorization(any(BluetoothDevice.class));
1345 
1346         mMcpService.mServerCallback.onDescriptorReadRequest(mCurrentDevice, 1, 0, descriptor);
1347         verify(mMockMcpService, times(0)).onDeviceUnauthorized(eq(mCurrentDevice));
1348         verify(mMockGattServer, times(0))
1349                 .sendResponse(
1350                         eq(mCurrentDevice),
1351                         eq(1),
1352                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1353                         eq(0),
1354                         any());
1355     }
1356 
1357     @Test
testDescriptorWriteRejectedUnauthorized()1358     public void testDescriptorWriteRejectedUnauthorized() {
1359         BluetoothGattService service = initAllFeaturesGattService();
1360 
1361         BluetoothGattDescriptor descriptor =
1362                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION)
1363                         .getDescriptor(UUID_CCCD);
1364         Assert.assertNotNull(descriptor);
1365 
1366         prepareConnectedDevice();
1367         doReturn(BluetoothDevice.ACCESS_REJECTED)
1368                 .when(mMockMcpService)
1369                 .getDeviceAuthorization(any(BluetoothDevice.class));
1370 
1371         ByteBuffer bb = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
1372         bb.put((byte) 0);
1373         bb.put((byte) 1);
1374 
1375         mMcpService.mServerCallback.onDescriptorWriteRequest(
1376                 mCurrentDevice, 1, descriptor, false, true, 0, bb.array());
1377 
1378         verify(mMockGattServer)
1379                 .sendResponse(
1380                         eq(mCurrentDevice),
1381                         eq(1),
1382                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1383                         eq(0),
1384                         any());
1385     }
1386 
1387     @Test
testDescriptorWriteUnknownUnauthorized()1388     public void testDescriptorWriteUnknownUnauthorized() {
1389         BluetoothGattService service = initAllFeaturesGattService();
1390 
1391         BluetoothGattDescriptor descriptor =
1392                 service.getCharacteristic(MediaControlGattService.UUID_TRACK_POSITION)
1393                         .getDescriptor(UUID_CCCD);
1394         Assert.assertNotNull(descriptor);
1395 
1396         prepareConnectedDevice();
1397         doReturn(BluetoothDevice.ACCESS_UNKNOWN)
1398                 .when(mMockMcpService)
1399                 .getDeviceAuthorization(any(BluetoothDevice.class));
1400 
1401         ByteBuffer bb = ByteBuffer.allocate(2).order(ByteOrder.LITTLE_ENDIAN);
1402         bb.put((byte) 0);
1403         bb.put((byte) 1);
1404 
1405         mMcpService.mServerCallback.onDescriptorWriteRequest(
1406                 mCurrentDevice, 1, descriptor, false, true, 0, bb.array());
1407         verify(mMockMcpService, times(0)).onDeviceUnauthorized(eq(mCurrentDevice));
1408         verify(mMockGattServer, times(0))
1409                 .sendResponse(
1410                         eq(mCurrentDevice),
1411                         eq(1),
1412                         eq(BluetoothGatt.GATT_INSUFFICIENT_AUTHORIZATION),
1413                         eq(0),
1414                         any());
1415     }
1416 
1417     @Test
testUpdatePlayerNameFromNull()1418     public void testUpdatePlayerNameFromNull() {
1419         BluetoothGattService service = initAllFeaturesGattService();
1420 
1421         BluetoothGattCharacteristic characteristic =
1422                 service.getCharacteristic(MediaControlGattService.UUID_PLAYER_NAME);
1423         Assert.assertNotNull(characteristic);
1424         byte[] nullname = null;
1425         characteristic.setValue(nullname);
1426 
1427         prepareConnectedDevice();
1428         doReturn(BluetoothDevice.ACCESS_ALLOWED)
1429                 .when(mMockMcpService)
1430                 .getDeviceAuthorization(any(BluetoothDevice.class));
1431 
1432         Map<PlayerStateField, Object> state_map = new HashMap<>();
1433         String player_name = "TestPlayerName";
1434         state_map.put(PlayerStateField.PLAYER_NAME, player_name);
1435         mMcpService.updatePlayerState(state_map);
1436     }
1437 
1438     @Test
testUpdatePlayerStateFromNullStateChar()1439     public void testUpdatePlayerStateFromNullStateChar() {
1440         BluetoothGattService service = initAllFeaturesGattService();
1441 
1442         BluetoothGattCharacteristic characteristic =
1443                 service.getCharacteristic(MediaControlGattService.UUID_MEDIA_STATE);
1444         Assert.assertNotNull(characteristic);
1445         byte[] nullBytes = null;
1446         characteristic.setValue(nullBytes);
1447 
1448         prepareConnectedDevice();
1449         doReturn(BluetoothDevice.ACCESS_ALLOWED)
1450                 .when(mMockMcpService)
1451                 .getDeviceAuthorization(any(BluetoothDevice.class));
1452 
1453         Map<PlayerStateField, Object> state_map = new HashMap<>();
1454         MediaState playback_state = MediaState.SEEKING;
1455         state_map.put(PlayerStateField.PLAYBACK_STATE, playback_state);
1456         mMcpService.updatePlayerState(state_map);
1457     }
1458 
1459     @Test
testDumpDoesNotCrash()1460     public void testDumpDoesNotCrash() {
1461         mMcpService.dump(new StringBuilder());
1462         initAllFeaturesGattService();
1463         mMcpService.dump(new StringBuilder());
1464     }
1465 }
1466