1 /*
2  * Copyright (C) 2021 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 package com.android.bluetooth.mcp;
18 
19 import static androidx.test.platform.app.InstrumentationRegistry.getInstrumentation;
20 
21 import static org.mockito.Mockito.*;
22 
23 import android.bluetooth.BluetoothAdapter;
24 import android.bluetooth.BluetoothDevice;
25 import android.bluetooth.BluetoothUuid;
26 import android.content.pm.ApplicationInfo;
27 import android.content.pm.PackageManager;
28 import android.media.session.PlaybackState;
29 import android.os.ParcelUuid;
30 
31 import androidx.test.filters.MediumTest;
32 import androidx.test.runner.AndroidJUnit4;
33 
34 import com.android.bluetooth.TestUtils;
35 import com.android.bluetooth.audio_util.MediaData;
36 import com.android.bluetooth.audio_util.MediaPlayerList;
37 import com.android.bluetooth.audio_util.MediaPlayerWrapper;
38 import com.android.bluetooth.audio_util.Metadata;
39 import com.android.bluetooth.btservice.AdapterService;
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.ArgumentCaptor;
48 import org.mockito.Captor;
49 import org.mockito.Mock;
50 import org.mockito.junit.MockitoJUnit;
51 import org.mockito.junit.MockitoRule;
52 
53 import java.util.HashMap;
54 import java.util.UUID;
55 
56 @MediumTest
57 @RunWith(AndroidJUnit4.class)
58 public class MediaControlProfileTest {
59     private final String mFlagDexmarker = System.getProperty("dexmaker.share_classloader", "false");
60 
61     private BluetoothAdapter mAdapter;
62     private MediaControlProfile mMediaControlProfile;
63 
64     private String packageName = "TestPackage";
65 
66     private String name = "TestPlayer";
67     private CharSequence charSequence = "TestPlayer";
68     private MediaControlServiceCallbacks mMcpServiceCallbacks;
69 
70     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
71 
72     @Mock private AdapterService mAdapterService;
73     @Mock private MediaData mMockMediaData;
74     @Mock private MediaPlayerList mMockMediaPlayerList;
75     @Mock private Metadata mMockMetadata;
76     @Mock private MediaPlayerWrapper mMockMediaPlayerWrapper;
77     @Mock private PackageManager mMockPackageManager;
78     @Mock private ApplicationInfo mMockApplicationInfo;
79     @Mock private MediaControlGattServiceInterface mMockGMcsService;
80     @Mock private McpService mMockMcpService;
81 
82     @Captor private ArgumentCaptor<HashMap> stateMapCaptor;
83     @Captor private ArgumentCaptor<Long> positionCaptor;
84     @Captor private ArgumentCaptor<MediaControlProfile.ListCallback> listCallbackCaptor;
85 
86     @Before
setUp()87     public void setUp() throws Exception {
88         if (!mFlagDexmarker.equals("true")) {
89             System.setProperty("dexmaker.share_classloader", "true");
90         }
91 
92         MediaControlProfile.ListCallback listCallback;
93 
94         TestUtils.setAdapterService(mAdapterService);
95         mAdapter = BluetoothAdapter.getDefaultAdapter();
96 
97         mMockMediaData.metadata = mMockMetadata;
98 
99         mMockMediaData.state = null;
100         mMockMetadata.duration = Long.toString(0);
101         mMockMetadata.title = null;
102         doReturn(mMockMediaPlayerWrapper).when(mMockMediaPlayerList).getActivePlayer();
103         doReturn(mMockMcpService).when(mMockMcpService).getApplicationContext();
104         doReturn(mMockPackageManager).when(mMockMcpService).getPackageManager();
105         doReturn(getInstrumentation().getTargetContext().getMainThreadHandler())
106                 .when(mMockMcpService)
107                 .getMainThreadHandler();
108         doReturn(packageName).when(mMockMcpService).getPackageName();
109         doReturn(name).when(mMockMediaPlayerWrapper).getPackageName();
110         doReturn(charSequence).when(mMockApplicationInfo).loadLabel(any(PackageManager.class));
111         doReturn(mMockApplicationInfo)
112                 .when(mMockPackageManager)
113                 .getApplicationInfo(anyString(), anyInt());
114 
115         mMediaControlProfile = new MediaControlProfile(mMockMcpService, mMockMediaPlayerList);
116 
117         // this is equivalent of what usually happens inside init class
118         mMediaControlProfile.injectGattServiceForTesting(packageName, mMockGMcsService);
119         mMediaControlProfile.onServiceInstanceRegistered(ServiceStatus.OK, mMockGMcsService);
120         mMcpServiceCallbacks = mMediaControlProfile;
121 
122         // Make sure callbacks are not called before it's fully initialized
123         verify(mMockMediaPlayerList, times(0)).init(any());
124         mMediaControlProfile.init();
125         verify(mMockMediaPlayerList).init(listCallbackCaptor.capture());
126 
127         listCallback = listCallbackCaptor.getValue();
128         listCallback.run(mMockMediaData);
129         // Give some time to verify if post function finishes on update player state method call
130         // TODO: Is there a possibility to get rid of this timeout?
131         verify(mMockGMcsService, timeout(100).times(1)).updatePlayerState(any(HashMap.class));
132     }
133 
134     @After
tearDown()135     public void tearDown() throws Exception {
136         TestUtils.clearAdapterService(mAdapterService);
137 
138         if (mMediaControlProfile == null) {
139             return;
140         }
141 
142         mMediaControlProfile.cleanup();
143         mMediaControlProfile = null;
144         reset(mMockMediaPlayerList);
145 
146         if (!mFlagDexmarker.equals("true")) {
147             System.setProperty("dexmaker.share_classloader", mFlagDexmarker);
148         }
149     }
150 
151     @Test
testGetCurrentTrackDuration()152     public void testGetCurrentTrackDuration() {
153         long duration = 10;
154 
155         // Some duration
156         mMockMetadata.duration = Long.toString(duration);
157         Assert.assertEquals(duration, mMediaControlProfile.getCurrentTrackDuration());
158 
159         // No metadata equals no track duration
160         mMockMediaData.metadata = null;
161         Assert.assertEquals(
162                 MediaControlGattServiceInterface.TRACK_DURATION_UNAVAILABLE,
163                 mMediaControlProfile.getCurrentTrackDuration());
164     }
165 
166     @Test
testPlayerState2McsState()167     public void testPlayerState2McsState() {
168         Assert.assertEquals(
169                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PLAYING),
170                 MediaState.PLAYING);
171         Assert.assertEquals(
172                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_NONE),
173                 MediaState.INACTIVE);
174         Assert.assertEquals(
175                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_STOPPED),
176                 MediaState.PAUSED);
177         Assert.assertEquals(
178                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PAUSED),
179                 MediaState.PAUSED);
180         Assert.assertEquals(
181                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_PLAYING),
182                 MediaState.PLAYING);
183         Assert.assertEquals(
184                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_FAST_FORWARDING),
185                 MediaState.SEEKING);
186         Assert.assertEquals(
187                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_REWINDING),
188                 MediaState.SEEKING);
189         Assert.assertEquals(
190                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_BUFFERING),
191                 MediaState.PAUSED);
192         Assert.assertEquals(
193                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_ERROR),
194                 MediaState.INACTIVE);
195         Assert.assertEquals(
196                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_CONNECTING),
197                 MediaState.INACTIVE);
198         Assert.assertEquals(
199                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_SKIPPING_TO_PREVIOUS),
200                 MediaState.PAUSED);
201         Assert.assertEquals(
202                 mMediaControlProfile.playerState2McsState(PlaybackState.STATE_SKIPPING_TO_NEXT),
203                 MediaState.PAUSED);
204         Assert.assertEquals(
205                 mMediaControlProfile.playerState2McsState(
206                         PlaybackState.STATE_SKIPPING_TO_QUEUE_ITEM),
207                 MediaState.PAUSED);
208     }
209 
210     @Test
testGetLatestTrackPosition()211     public void testGetLatestTrackPosition() {
212         int state = PlaybackState.STATE_PLAYING;
213         long position = 10;
214         float playback_speed = 1.5f;
215 
216         Assert.assertEquals(
217                 mMcpServiceCallbacks.onGetCurrentTrackPosition(),
218                 MediaControlGattServiceInterface.TRACK_POSITION_UNAVAILABLE);
219 
220         PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
221         bob.setState(state, position, playback_speed);
222         mMockMediaData.state = bob.build();
223         doReturn(mMockMediaData.state).when(mMockMediaPlayerWrapper).getPlaybackState();
224 
225         Assert.assertNotEquals(
226                 mMcpServiceCallbacks.onGetCurrentTrackPosition(),
227                 MediaControlGattServiceInterface.TRACK_POSITION_UNAVAILABLE);
228     }
229 
230     @Test
testOnCurrentPlayerStateUpdate()231     public void testOnCurrentPlayerStateUpdate() {
232         HashMap stateMap;
233         int state = PlaybackState.STATE_PLAYING;
234         long position = 10;
235         float playback_speed = 1.5f;
236         long update_time = 77;
237         long duration = 10;
238         String title = "TestTrackTitle";
239 
240         mMockMetadata.duration = Long.toString(duration);
241         mMockMetadata.title = title;
242 
243         PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
244         bob.setState(state, position, playback_speed, update_time);
245         mMockMediaData.state = bob.build();
246 
247         mMediaControlProfile.onCurrentPlayerStateUpdated(true, true);
248         // First time called from ListCallback. Give some time to verify if post function
249         // finishes on update player state method call
250         // TODO: Is there a possibility to get rid of this timeout?
251         verify(mMockGMcsService, timeout(100).times(2)).updatePlayerState(stateMapCaptor.capture());
252         stateMap = stateMapCaptor.getValue();
253 
254         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYER_NAME));
255 
256         // state changed
257         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_STATE));
258         Assert.assertTrue(stateMap.containsKey(PlayerStateField.OPCODES_SUPPORTED));
259         Assert.assertTrue(stateMap.containsKey(PlayerStateField.SEEKING_SPEED));
260         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_SPEED));
261         Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_POSITION));
262 
263         // metadata changed
264         Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_DURATION));
265         Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_TITLE));
266     }
267 
testHandleTrackPositionSetRequest(long position, long duration, int times)268     private void testHandleTrackPositionSetRequest(long position, long duration, int times) {
269         mMcpServiceCallbacks.onTrackPositionSetRequest(position);
270         verify(mMockMediaPlayerWrapper, timeout(100).times(times)).seekTo(positionCaptor.capture());
271 
272         // position cannot be negative and bigger than track duration
273         if (position < 0) Assert.assertEquals(positionCaptor.getValue().longValue(), 0);
274         else if (position > duration) {
275             Assert.assertEquals(positionCaptor.getValue().longValue(), duration);
276         } else {
277             Assert.assertEquals(positionCaptor.getValue().longValue(), position);
278         }
279     }
280 
281     @Test
testHandleTrackPositionsSetRequest()282     public void testHandleTrackPositionsSetRequest() {
283         long duration = 50;
284         long actions = PlaybackState.ACTION_SEEK_TO;
285         int times = 1;
286 
287         mMockMetadata.duration = Long.toString(duration);
288 
289         PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
290         bob.setActions(actions);
291         mMockMediaData.state = bob.build();
292 
293         testHandleTrackPositionSetRequest(-duration, duration, times++);
294         testHandleTrackPositionSetRequest(duration + duration, duration, times++);
295         testHandleTrackPositionSetRequest(duration / 2, duration, times++);
296 
297         actions = 0;
298         bob.setActions(actions);
299         mMockMediaData.state = bob.build();
300 
301         mMcpServiceCallbacks.onTrackPositionSetRequest(duration);
302         // First time called from ListCallback. Give some time to verify if post function
303         // finishes on update player state method call
304         // TODO: Is there a possibility to get rid of this timeout?
305         verify(mMockGMcsService, timeout(100).times(2)).updatePlayerState(any(HashMap.class));
306     }
307 
308     @Test
testHandlePlaybackSpeedSetRequest()309     public void testHandlePlaybackSpeedSetRequest() {
310         float speed = 1.5f;
311         int times = 1;
312 
313         mMcpServiceCallbacks.onPlaybackSpeedSetRequest(speed);
314         verify(mMockMediaPlayerWrapper, timeout(100).times(times)).setPlaybackSpeed(anyFloat());
315 
316         // Playback speed wouldn't be set if no active player
317         doReturn(null).when(mMockMediaPlayerList).getActivePlayer();
318         mMcpServiceCallbacks.onPlaybackSpeedSetRequest(speed);
319         verify(mMockMediaPlayerWrapper, timeout(100).times(times)).setPlaybackSpeed(anyFloat());
320     }
321 
322     @Test
testHandleMediaControlRequest()323     public void testHandleMediaControlRequest() {
324         long actions =
325                 PlaybackState.ACTION_PLAY
326                         | PlaybackState.ACTION_PAUSE
327                         | PlaybackState.ACTION_STOP
328                         | PlaybackState.ACTION_SKIP_TO_PREVIOUS
329                         | PlaybackState.ACTION_SKIP_TO_NEXT
330                         | PlaybackState.ACTION_REWIND
331                         | PlaybackState.ACTION_FAST_FORWARD
332                         | PlaybackState.ACTION_SEEK_TO;
333         long duration = 10;
334 
335         PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
336         bob.setActions(actions);
337         mMockMediaData.state = bob.build();
338 
339         Request request = new Request(Request.Opcodes.PLAY, 0);
340         mMcpServiceCallbacks.onMediaControlRequest(request);
341         verify(mMockMediaPlayerWrapper, timeout(100)).playCurrent();
342         request = new Request(Request.Opcodes.PAUSE, 0);
343         mMcpServiceCallbacks.onMediaControlRequest(request);
344         verify(mMockMediaPlayerWrapper, timeout(100)).pauseCurrent();
345         request = new Request(Request.Opcodes.STOP, 0);
346         mMcpServiceCallbacks.onMediaControlRequest(request);
347         verify(mMockMediaPlayerWrapper, timeout(100)).seekTo(0);
348         verify(mMockMediaPlayerWrapper).stopCurrent();
349         request = new Request(Request.Opcodes.PREVIOUS_TRACK, 0);
350         mMcpServiceCallbacks.onMediaControlRequest(request);
351         verify(mMockMediaPlayerWrapper, timeout(100)).skipToPrevious();
352         request = new Request(Request.Opcodes.NEXT_TRACK, 0);
353         mMcpServiceCallbacks.onMediaControlRequest(request);
354         verify(mMockMediaPlayerWrapper, timeout(100)).skipToNext();
355         request = new Request(Request.Opcodes.FAST_REWIND, 0);
356         mMcpServiceCallbacks.onMediaControlRequest(request);
357         verify(mMockMediaPlayerWrapper, timeout(100)).rewind();
358         request = new Request(Request.Opcodes.FAST_FORWARD, 0);
359         mMcpServiceCallbacks.onMediaControlRequest(request);
360         verify(mMockMediaPlayerWrapper, timeout(100)).fastForward();
361 
362         mMockMetadata.duration = Long.toString(duration);
363         Assert.assertEquals(duration, mMediaControlProfile.getCurrentTrackDuration());
364         request = new Request(Request.Opcodes.MOVE_RELATIVE, 100);
365         mMcpServiceCallbacks.onMediaControlRequest(request);
366         verify(mMockMediaPlayerWrapper, timeout(100)).seekTo(duration);
367 
368         // Verify toggle-style play/pause control support
369         clearInvocations(mMockMediaPlayerWrapper);
370         actions = PlaybackState.ACTION_PLAY_PAUSE;
371         bob = new PlaybackState.Builder(mMockMediaData.state);
372         bob.setActions(actions);
373         mMockMediaData.state = bob.build();
374 
375         request = new Request(Request.Opcodes.PLAY, 0);
376         mMcpServiceCallbacks.onMediaControlRequest(request);
377         verify(mMockMediaPlayerWrapper, timeout(100)).playCurrent();
378         request = new Request(Request.Opcodes.PAUSE, 0);
379         mMcpServiceCallbacks.onMediaControlRequest(request);
380         verify(mMockMediaPlayerWrapper, timeout(100)).pauseCurrent();
381     }
382 
383     @Test
testAvrcpCompatibleActionSet()384     public void testAvrcpCompatibleActionSet() {
385         long actions = PlaybackState.ACTION_SET_RATING;
386         PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
387         bob.setActions(actions);
388         mMockMediaData.state = bob.build();
389 
390         // Same base feature set as the player item features defined in `avrcp/get_foder_items.cc`
391         final long baseFeatures =
392                 PlaybackState.ACTION_PLAY
393                         | PlaybackState.ACTION_STOP
394                         | PlaybackState.ACTION_PAUSE
395                         | PlaybackState.ACTION_REWIND
396                         | PlaybackState.ACTION_FAST_FORWARD
397                         | PlaybackState.ACTION_SKIP_TO_NEXT
398                         | PlaybackState.ACTION_SKIP_TO_PREVIOUS;
399         Assert.assertEquals(
400                 actions | baseFeatures, mMediaControlProfile.getCurrentPlayerSupportedActions());
401     }
402 
403     @Test
testPlayerActions2McsSupportedOpcodes()404     public void testPlayerActions2McsSupportedOpcodes() {
405         long actions =
406                 PlaybackState.ACTION_PLAY
407                         | PlaybackState.ACTION_PAUSE
408                         | PlaybackState.ACTION_STOP
409                         | PlaybackState.ACTION_SKIP_TO_PREVIOUS
410                         | PlaybackState.ACTION_SKIP_TO_NEXT
411                         | PlaybackState.ACTION_REWIND
412                         | PlaybackState.ACTION_FAST_FORWARD
413                         | PlaybackState.ACTION_SEEK_TO;
414         int opcodes_supported =
415                 Request.SupportedOpcodes.STOP
416                         | Request.SupportedOpcodes.PAUSE
417                         | Request.SupportedOpcodes.PLAY
418                         | Request.SupportedOpcodes.FAST_REWIND
419                         | Request.SupportedOpcodes.PREVIOUS_TRACK
420                         | Request.SupportedOpcodes.NEXT_TRACK
421                         | Request.SupportedOpcodes.FAST_FORWARD
422                         | Request.SupportedOpcodes.MOVE_RELATIVE;
423 
424         Assert.assertEquals(
425                 mMediaControlProfile.playerActions2McsSupportedOpcodes(actions), opcodes_supported);
426 
427         // Verify toggle-style play/pause control support
428         actions = PlaybackState.ACTION_PLAY_PAUSE;
429         opcodes_supported = Request.SupportedOpcodes.PAUSE | Request.SupportedOpcodes.PLAY;
430 
431         Assert.assertEquals(
432                 mMediaControlProfile.playerActions2McsSupportedOpcodes(actions), opcodes_supported);
433     }
434 
435     @Test
testProcessPendingPlayerStateRequest()436     public void testProcessPendingPlayerStateRequest() {
437         HashMap stateMap;
438         int state = PlaybackState.STATE_PLAYING;
439         long position = 10;
440         float playback_speed = 1.5f;
441 
442         PlaybackState.Builder bob = new PlaybackState.Builder(mMockMediaData.state);
443         bob.setState(state, position, playback_speed);
444         mMockMediaData.state = bob.build();
445         doReturn(mMockMediaData.state).when(mMockMediaPlayerWrapper).getPlaybackState();
446 
447         PlayerStateField[] state_fields =
448                 new PlayerStateField[] {
449                     PlayerStateField.PLAYBACK_STATE,
450                     PlayerStateField.TRACK_DURATION,
451                     PlayerStateField.PLAYBACK_SPEED,
452                     PlayerStateField.SEEKING_SPEED,
453                     PlayerStateField.PLAYING_ORDER,
454                     PlayerStateField.TRACK_POSITION,
455                     PlayerStateField.PLAYER_NAME,
456                     PlayerStateField.PLAYING_ORDER_SUPPORTED,
457                     PlayerStateField.OPCODES_SUPPORTED
458                 };
459 
460         mMcpServiceCallbacks.onPlayerStateRequest(state_fields);
461         // First time called from ListCallback. Give some time to verify if post function
462         // finishes on update player state method call
463         // TODO: Is there a possibility to get rid of this timeout?
464         verify(mMockGMcsService, timeout(100).times(2)).updatePlayerState(stateMapCaptor.capture());
465         stateMap = stateMapCaptor.getValue();
466 
467         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_STATE));
468         Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_DURATION));
469         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYBACK_SPEED));
470         Assert.assertTrue(stateMap.containsKey(PlayerStateField.SEEKING_SPEED));
471         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYING_ORDER));
472         Assert.assertTrue(stateMap.containsKey(PlayerStateField.TRACK_POSITION));
473         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYER_NAME));
474         Assert.assertTrue(stateMap.containsKey(PlayerStateField.PLAYING_ORDER_SUPPORTED));
475         Assert.assertTrue(stateMap.containsKey(PlayerStateField.OPCODES_SUPPORTED));
476     }
477 
testGetCurrentPlayerPlayingOrder( PlayingOrder expected_value, boolean is_shuffle_set, boolean is_repeat_set)478     private void testGetCurrentPlayerPlayingOrder(
479             PlayingOrder expected_value, boolean is_shuffle_set, boolean is_repeat_set) {
480         doReturn(is_shuffle_set).when(mMockMediaPlayerWrapper).isShuffleSet();
481         doReturn(is_repeat_set).when(mMockMediaPlayerWrapper).isRepeatSet();
482         Assert.assertEquals(expected_value, mMediaControlProfile.getCurrentPlayerPlayingOrder());
483     }
484 
485     @Test
testGetCurrentPlayerPlayingOrders()486     public void testGetCurrentPlayerPlayingOrders() {
487         testGetCurrentPlayerPlayingOrder(PlayingOrder.SHUFFLE_REPEAT, true, true);
488         testGetCurrentPlayerPlayingOrder(PlayingOrder.SHUFFLE_ONCE, true, false);
489         testGetCurrentPlayerPlayingOrder(PlayingOrder.IN_ORDER_REPEAT, false, true);
490         testGetCurrentPlayerPlayingOrder(PlayingOrder.IN_ORDER_ONCE, false, false);
491     }
492 
testGetSupportedPlayingOrder(boolean is_shuffle_set, boolean is_repeat_set)493     private void testGetSupportedPlayingOrder(boolean is_shuffle_set, boolean is_repeat_set) {
494         int expected_value = SupportedPlayingOrder.IN_ORDER_ONCE;
495 
496         if (is_repeat_set) expected_value |= SupportedPlayingOrder.IN_ORDER_REPEAT;
497         if (is_shuffle_set) {
498             if (is_repeat_set) expected_value |= SupportedPlayingOrder.SHUFFLE_REPEAT;
499             else expected_value |= SupportedPlayingOrder.SHUFFLE_ONCE;
500         }
501 
502         doReturn(is_shuffle_set).when(mMockMediaPlayerWrapper).isShuffleSupported();
503         doReturn(is_repeat_set).when(mMockMediaPlayerWrapper).isRepeatSupported();
504         Assert.assertEquals(
505                 expected_value, mMediaControlProfile.getSupportedPlayingOrder().intValue());
506     }
507 
508     @Test
testGetSupportedPlayingOrders()509     public void testGetSupportedPlayingOrders() {
510         testGetSupportedPlayingOrder(true, true);
511         testGetSupportedPlayingOrder(true, false);
512         testGetSupportedPlayingOrder(false, true);
513         testGetSupportedPlayingOrder(false, false);
514     }
515 
516     @Test
testGmcsSetGetNotificationSubscriptionDoesNotCrash()517     public void testGmcsSetGetNotificationSubscriptionDoesNotCrash() {
518         final ParcelUuid charUuid1 = new ParcelUuid(UUID.randomUUID());
519         final int ccid1 = BluetoothDevice.METADATA_GMCS_CCCD;
520 
521         doReturn(ccid1).when(mMockGMcsService).getContentControlId();
522         doReturn(BluetoothUuid.GENERIC_MEDIA_CONTROL.getUuid())
523                 .when(mMockGMcsService)
524                 .getServiceUuid();
525 
526         // BluetoothDevice class is not mockable
527         BluetoothDevice bluetoothDevice = TestUtils.getTestDevice(mAdapter, 0);
528         mMediaControlProfile.setNotificationSubscription(ccid1, bluetoothDevice, charUuid1, true);
529         Assert.assertNotNull(
530                 mMediaControlProfile.getNotificationSubscriptions(ccid1, bluetoothDevice));
531     }
532 
533     @Test
testDumpDoesNotCrash()534     public void testDumpDoesNotCrash() {
535         mMediaControlProfile.dump(new StringBuilder());
536     }
537 }
538