1 /*
2  * Copyright 2019 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 package com.android.bluetooth.avrcpcontroller;
17 
18 import static android.Manifest.permission.BLUETOOTH_CONNECT;
19 import static org.mockito.Mockito.*;
20 
21 import android.bluetooth.BluetoothAdapter;
22 import android.bluetooth.BluetoothAvrcpController;
23 import android.bluetooth.BluetoothDevice;
24 import android.bluetooth.BluetoothProfile;
25 import android.content.Context;
26 import android.content.Intent;
27 import android.content.res.Resources;
28 import android.media.AudioManager;
29 import android.os.Bundle;
30 import android.os.Looper;
31 import android.support.v4.media.MediaMetadataCompat;
32 import android.support.v4.media.session.MediaControllerCompat;
33 import android.support.v4.media.session.MediaSessionCompat;
34 import android.support.v4.media.session.PlaybackStateCompat;
35 import android.util.SparseArray;
36 
37 import androidx.test.InstrumentationRegistry;
38 import androidx.test.filters.FlakyTest;
39 import androidx.test.filters.MediumTest;
40 import androidx.test.rule.ServiceTestRule;
41 import androidx.test.runner.AndroidJUnit4;
42 
43 import com.android.bluetooth.R;
44 import com.android.bluetooth.TestUtils;
45 import com.android.bluetooth.a2dpsink.A2dpSinkService;
46 import com.android.bluetooth.btservice.AdapterService;
47 import com.android.bluetooth.btservice.storage.DatabaseManager;
48 
49 import org.hamcrest.core.IsInstanceOf;
50 import org.junit.After;
51 import org.junit.Assert;
52 import org.junit.Assume;
53 import org.junit.Before;
54 import org.junit.Rule;
55 import org.junit.Test;
56 import org.junit.runner.RunWith;
57 import org.mockito.ArgumentCaptor;
58 import org.mockito.Mock;
59 import org.mockito.MockitoAnnotations;
60 
61 import java.util.ArrayList;
62 import java.util.List;
63 import java.util.UUID;
64 
65 @MediumTest
66 @RunWith(AndroidJUnit4.class)
67 public class AvrcpControllerStateMachineTest {
68     private static final int ASYNC_CALL_TIMEOUT_MILLIS = 100;
69     private static final int CONNECT_TIMEOUT_TEST_MILLIS = 1000;
70     private static final int KEY_DOWN = 0;
71     private static final int KEY_UP = 1;
72 
73     private BluetoothAdapter mAdapter;
74     private Context mTargetContext;
75 
76     @Rule public final ServiceTestRule mAvrcpServiceRule = new ServiceTestRule();
77     @Rule public final ServiceTestRule mA2dpServiceRule = new ServiceTestRule();
78     @Mock private AdapterService mA2dpAdapterService;
79     @Mock private AdapterService mAvrcpAdapterService;
80     @Mock private A2dpSinkService mA2dpSinkService;
81     @Mock private DatabaseManager mDatabaseManager;
82     @Mock private AudioManager mAudioManager;
83     @Mock private Resources mMockResources;
84     private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class);
85     @Mock private AvrcpControllerService mAvrcpControllerService;
86     @Mock private AvrcpCoverArtManager mCoverArtManager;
87 
88     private byte[] mTestAddress = new byte[]{01, 01, 01, 01, 01, 01};
89     private BluetoothDevice mTestDevice = null;
90     private AvrcpControllerStateMachine mAvrcpStateMachine = null;
91 
92     @Before
setUp()93     public void setUp() throws Exception {
94         mTargetContext = InstrumentationRegistry.getTargetContext();
95         Assume.assumeTrue("Ignore test when AVRCP Controller is not enabled",
96                 mTargetContext.getResources().getBoolean(
97                         R.bool.profile_supported_avrcp_controller));
98         if (Looper.myLooper() == null) {
99             Looper.prepare();
100         }
101         Assert.assertNotNull(Looper.myLooper());
102 
103         MockitoAnnotations.initMocks(this);
104 
105         // Start a real A2dpSinkService so we can replace the static instance with our mock
106         doReturn(mDatabaseManager).when(mA2dpAdapterService).getDatabase();
107         doReturn(true).when(mA2dpAdapterService).isStartedProfile(anyString());
108         TestUtils.setAdapterService(mA2dpAdapterService);
109         TestUtils.startService(mA2dpServiceRule, A2dpSinkService.class);
110         A2dpSinkService.setA2dpSinkService(mA2dpSinkService);
111         TestUtils.clearAdapterService(mA2dpAdapterService);
112 
113         // Start an AvrcpControllerService to get a real BluetoothMediaBrowserService up
114         doReturn(true).when(mAvrcpAdapterService).isStartedProfile(anyString());
115         TestUtils.setAdapterService(mAvrcpAdapterService);
116         TestUtils.startService(mAvrcpServiceRule, AvrcpControllerService.class);
117 
118         // Mock an AvrcpControllerService to give to all state machines
119         doReturn(BluetoothProfile.STATE_DISCONNECTED).when(mCoverArtManager).getState(any());
120         doReturn(15).when(mAudioManager).getStreamMaxVolume(anyInt());
121         doReturn(8).when(mAudioManager).getStreamVolume(anyInt());
122         doReturn(true).when(mAudioManager).isVolumeFixed();
123         when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
124                 .thenReturn(true);
125         doReturn(mMockResources).when(mAvrcpControllerService).getResources();
126         doReturn(mAudioManager).when(mAvrcpControllerService)
127                 .getSystemService(Context.AUDIO_SERVICE);
128         doReturn(mCoverArtManager).when(mAvrcpControllerService).getCoverArtManager();
129         mAvrcpControllerService.sBrowseTree = new BrowseTree(null);
130 
131         // Ensure our MediaBrowserService starts with a blank state
132         BluetoothMediaBrowserService.reset();
133 
134         // This line must be called to make sure relevant objects are initialized properly
135         mAdapter = BluetoothAdapter.getDefaultAdapter();
136 
137         // Set up device and state machine under test
138         mTestDevice = mAdapter.getRemoteDevice(mTestAddress);
139         mAvrcpStateMachine = makeStateMachine(mTestDevice);
140         setActiveDevice(mTestDevice);
141     }
142 
143     @After
tearDown()144     public void tearDown() throws Exception {
145         if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_avrcp_controller)) {
146             return;
147         }
148         destroyStateMachine(mAvrcpStateMachine);
149         TestUtils.clearAdapterService(mAvrcpAdapterService);
150     }
151 
152     /**
153      * Create a state machine to test
154      */
makeStateMachine(BluetoothDevice device)155     private AvrcpControllerStateMachine makeStateMachine(BluetoothDevice device) {
156         AvrcpControllerStateMachine sm =
157                  new AvrcpControllerStateMachine(device, mAvrcpControllerService);
158         sm.start();
159         return sm;
160     }
161 
162     /**
163      * Destroy a state machine you created to test
164      */
destroyStateMachine(AvrcpControllerStateMachine sm)165     private void destroyStateMachine(AvrcpControllerStateMachine sm) {
166         if (sm == null || sm.getState() == BluetoothProfile.STATE_DISCONNECTED) return;
167 
168         sm.disconnect();
169         TestUtils.waitForLooperToFinishScheduledTask(sm.getHandler().getLooper());
170 
171         // is disconnected
172         Assert.assertEquals(sm.getState(), BluetoothProfile.STATE_DISCONNECTED);
173 
174         // told mAvrcpControllerService to remove it
175         // verify(mAvrcpControllerService).removeStateMachine(eq(sm));
176     }
177 
178     /**
179      * Set up which device the AvrcpControllerService will report as active
180      */
setActiveDevice(BluetoothDevice device)181     private void setActiveDevice(BluetoothDevice device) {
182         doReturn(device).when(mAvrcpControllerService).getActiveDevice();
183         if (mTestDevice.equals(device)) {
184             mAvrcpStateMachine.setDeviceState(AvrcpControllerService.DEVICE_STATE_ACTIVE);
185         } else {
186             mAvrcpStateMachine.setDeviceState(AvrcpControllerService.DEVICE_STATE_INACTIVE);
187             BluetoothMediaBrowserService.reset();
188         }
189     }
190 
191     /**
192      * Setup Connected State for a given state machine
193      *
194      * @return number of times mAvrcpControllerService.sendBroadcastAsUser() has been invoked
195      */
setUpConnectedState(boolean control, boolean browsing)196     private int setUpConnectedState(boolean control, boolean browsing) {
197 
198         Assert.assertThat(mAvrcpStateMachine.getCurrentState(),
199                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
200 
201         mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(control, browsing));
202 
203         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
204         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast(
205                 mIntentArgument.capture(), eq(BLUETOOTH_CONNECT),
206                 any(Bundle.class));
207         Assert.assertThat(mAvrcpStateMachine.getCurrentState(),
208                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Connected.class));
209         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_CONNECTED);
210 
211         return BluetoothProfile.STATE_CONNECTED;
212     }
213 
makeTrack(String title, String artist, String album, long trackNum, long totalTracks, String genre, long duration, String imageHandle)214     private AvrcpItem makeTrack(String title, String artist, String album, long trackNum,
215             long totalTracks, String genre, long duration, String imageHandle) {
216         AvrcpItem.Builder builder = new AvrcpItem.Builder();
217         builder.setItemType(AvrcpItem.TYPE_MEDIA);
218         builder.setType(AvrcpItem.MEDIA_AUDIO);
219         builder.setDevice(mTestDevice);
220         builder.setPlayable(true);
221         builder.setUid(0);
222         builder.setUuid("AVRCP-ITEM-TEST-UUID");
223 
224         builder.setTitle(title);
225         builder.setArtistName(artist);
226         builder.setAlbumName(album);
227         builder.setTrackNumber(trackNum);
228         builder.setTotalNumberOfTracks(totalTracks);
229         builder.setGenre(genre);
230         builder.setPlayingTime(duration);
231         if (imageHandle != null) {
232             builder.setCoverArtHandle(imageHandle);
233         }
234 
235         return builder.build();
236     }
237 
makePlayer(BluetoothDevice device, int playerId, String playerName, int playerType, byte[] playerFeatures, int playStatus)238     private AvrcpPlayer makePlayer(BluetoothDevice device, int playerId, String playerName,
239             int playerType, byte[] playerFeatures, int playStatus) {
240         AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder();
241         apb.setDevice(device);
242         apb.setPlayerId(playerId);
243         apb.setName(playerName);
244         apb.setPlayerType(playerType);
245         apb.setSupportedFeatures(playerFeatures);
246         apb.setPlayStatus(playStatus);
247         return apb.build();
248     }
249 
250     /**
251      * Send a message to the state machine that the track has changed. Must be connected to
252      * do this.
253      */
setCurrentTrack(AvrcpItem track)254     private void setCurrentTrack(AvrcpItem track) {
255         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED,
256                 track);
257         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
258         Assert.assertEquals(mAvrcpStateMachine.getCurrentTrack(), track);
259     }
260 
261     /**
262      * Set the current play status (Play, Pause, etc.) of the device
263      */
setPlaybackState(int state)264     private void setPlaybackState(int state) {
265         mAvrcpStateMachine.sendMessage(
266                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, state);
267         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
268     }
269 
270     /**
271      * Set the current playback position of the device
272      */
setPlaybackPosition(int position, int duration)273     private void setPlaybackPosition(int position, int duration) {
274         mAvrcpStateMachine.sendMessage(
275                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED, duration, position);
276         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
277     }
278 
279     /**
280      * Make an AvrcpItem suitable for being included in the Now Playing list for the test device
281      */
makeNowPlayingItem(long uid, String name)282     private AvrcpItem makeNowPlayingItem(long uid, String name) {
283         AvrcpItem.Builder aib = new AvrcpItem.Builder();
284         aib.setDevice(mTestDevice);
285         aib.setItemType(AvrcpItem.TYPE_MEDIA);
286         aib.setType(AvrcpItem.MEDIA_AUDIO);
287         aib.setTitle(name);
288         aib.setUid(uid);
289         aib.setUuid(UUID.randomUUID().toString());
290         aib.setPlayable(true);
291         return aib.build();
292     }
293 
294     /**
295      * Get the current Now Playing list for the test device
296      */
getNowPlayingList()297     private List<AvrcpItem> getNowPlayingList() {
298         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
299         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
300         for (BrowseTree.BrowseNode child : nowPlaying.getChildren()) {
301             nowPlayingList.add(child.mItem);
302         }
303         return nowPlayingList;
304     }
305 
306     /**
307      * Set the current Now Playing list for the test device
308      */
setNowPlayingList(List<AvrcpItem> nowPlayingList)309     private void setNowPlayingList(List<AvrcpItem> nowPlayingList) {
310         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
311         mAvrcpStateMachine.requestContents(nowPlaying);
312         mAvrcpStateMachine.sendMessage(
313                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, nowPlayingList);
314         mAvrcpStateMachine.sendMessage(
315                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
316 
317         // Wait for the now playing list to be propagated
318         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
319 
320         // Make sure its set by re grabbing the node and checking its contents are cached
321         nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
322         Assert.assertTrue(nowPlaying.isCached());
323         assertNowPlayingList(nowPlayingList);
324     }
325 
326     /**
327      * Assert that the Now Playing list is a particular value
328      */
assertNowPlayingList(List<AvrcpItem> expected)329     private void assertNowPlayingList(List<AvrcpItem> expected) {
330         List<AvrcpItem> current = getNowPlayingList();
331         Assert.assertEquals(expected.size(), current.size());
332         for (int i = 0; i < expected.size(); i++) {
333             Assert.assertEquals(expected.get(i), current.get(i));
334         }
335     }
336 
337     /**
338      * Test to confirm that the state machine is capable of cycling through the 4
339      * connection states, and that upon completion, it cleans up afterwards.
340      */
341     @Test
testDisconnect()342     public void testDisconnect() {
343         int numBroadcastsSent = setUpConnectedState(true, true);
344         testDisconnectInternal(numBroadcastsSent);
345     }
346 
347     /**
348      * Test to confirm that the state machine is capable of cycling through the 4
349      * connection states with no crashes, even if the {@link AvrcpControllerService} is stopped and
350      * the {@code sBrowseTree} is null. This could happen if BT is disabled as the profile is being
351      * disconnected.
352      */
353     @Test
testDisconnectWithNullBrowseTree()354     public void testDisconnectWithNullBrowseTree() {
355         int numBroadcastsSent = setUpConnectedState(true, true);
356         mAvrcpControllerService.stop();
357 
358         testDisconnectInternal(numBroadcastsSent);
359     }
360 
testDisconnectInternal(int numBroadcastsSent)361     private void testDisconnectInternal(int numBroadcastsSent) {
362         mAvrcpStateMachine.disconnect();
363         numBroadcastsSent += 2;
364         verify(mAvrcpControllerService,
365                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent)).sendBroadcast(
366                 mIntentArgument.capture(), eq(BLUETOOTH_CONNECT),
367                 any(Bundle.class));
368         Assert.assertEquals(mTestDevice, mIntentArgument.getValue().getParcelableExtra(
369                 BluetoothDevice.EXTRA_DEVICE));
370         Assert.assertEquals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
371                 mIntentArgument.getValue().getAction());
372         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
373                 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
374         Assert.assertThat(mAvrcpStateMachine.getCurrentState(),
375                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
376         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
377         verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
378     }
379 
380     /**
381      * Test to confirm that a control only device can be established (no browsing)
382      */
383     @Test
testControlOnly()384     public void testControlOnly() {
385         int numBroadcastsSent = setUpConnectedState(true, false);
386         MediaControllerCompat.TransportControls transportControls =
387                 BluetoothMediaBrowserService.getTransportControls();
388         Assert.assertNotNull(transportControls);
389         Assert.assertEquals(PlaybackStateCompat.STATE_NONE,
390                 BluetoothMediaBrowserService.getPlaybackState());
391         mAvrcpStateMachine.disconnect();
392         numBroadcastsSent += 2;
393         verify(mAvrcpControllerService,
394                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent)).sendBroadcast(
395                 mIntentArgument.capture(), eq(BLUETOOTH_CONNECT),
396                 any(Bundle.class));
397         Assert.assertEquals(mTestDevice, mIntentArgument.getValue().getParcelableExtra(
398                 BluetoothDevice.EXTRA_DEVICE));
399         Assert.assertEquals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
400                 mIntentArgument.getValue().getAction());
401         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
402                 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
403         Assert.assertThat(mAvrcpStateMachine.getCurrentState(),
404                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
405         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
406         verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
407     }
408 
409     /**
410      * Test to confirm that a browsing only device can be established (no control)
411      */
412     @Test
413     @FlakyTest
testBrowsingOnly()414     public void testBrowsingOnly() {
415         Assert.assertEquals(0, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
416         int numBroadcastsSent = setUpConnectedState(false, true);
417         Assert.assertEquals(1, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
418         Assert.assertEquals(PlaybackStateCompat.STATE_NONE,
419                 BluetoothMediaBrowserService.getPlaybackState());
420         mAvrcpStateMachine.disconnect();
421         numBroadcastsSent += 2;
422         verify(mAvrcpControllerService,
423                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent)).sendBroadcast(
424                 mIntentArgument.capture(), eq(BLUETOOTH_CONNECT),
425                 any(Bundle.class));
426         Assert.assertEquals(mTestDevice, mIntentArgument.getValue().getParcelableExtra(
427                 BluetoothDevice.EXTRA_DEVICE));
428         Assert.assertEquals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
429                 mIntentArgument.getValue().getAction());
430         Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED,
431                 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
432         Assert.assertThat(mAvrcpStateMachine.getCurrentState(),
433                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
434         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
435         verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
436     }
437 
438     /**
439      * Test to make sure the state machine is tracking the correct device
440      */
441     @Test
testGetDevice()442     public void testGetDevice() {
443         Assert.assertEquals(mAvrcpStateMachine.getDevice(), mTestDevice);
444     }
445 
446     /**
447      * Test that dumpsys will generate information about connected devices
448      */
449     @Test
testDump()450     public void testDump() {
451         StringBuilder sb = new StringBuilder();
452         mAvrcpStateMachine.dump(sb);
453         Assert.assertNotNull(sb.toString());
454     }
455 
456     /**
457      * Test media browser play command
458      */
459     @Test
testPlay()460     public void testPlay() throws Exception {
461         setUpConnectedState(true, true);
462         MediaControllerCompat.TransportControls transportControls =
463                 BluetoothMediaBrowserService.getTransportControls();
464 
465         //Play
466         transportControls.play();
467         verify(mAvrcpControllerService,
468                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
469                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
470         verify(mAvrcpControllerService,
471                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
472                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
473     }
474 
475     /**
476      * Test media browser pause command
477      */
478     @Test
testPause()479     public void testPause() throws Exception {
480         setUpConnectedState(true, true);
481         MediaControllerCompat.TransportControls transportControls =
482                 BluetoothMediaBrowserService.getTransportControls();
483 
484         //Pause
485         transportControls.pause();
486         verify(mAvrcpControllerService,
487                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
488                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
489         verify(mAvrcpControllerService,
490                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
491                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP));
492     }
493 
494     /**
495      * Test media browser stop command
496      */
497     @Test
testStop()498     public void testStop() throws Exception {
499         setUpConnectedState(true, true);
500         MediaControllerCompat.TransportControls transportControls =
501                 BluetoothMediaBrowserService.getTransportControls();
502 
503         //Stop
504         transportControls.stop();
505         verify(mAvrcpControllerService,
506                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
507                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_DOWN));
508         verify(mAvrcpControllerService,
509                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
510                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_UP));
511     }
512 
513     /**
514      * Test media browser next command
515      */
516     @Test
testNext()517     public void testNext() throws Exception {
518         setUpConnectedState(true, true);
519         MediaControllerCompat.TransportControls transportControls =
520                 BluetoothMediaBrowserService.getTransportControls();
521 
522         //Next
523         transportControls.skipToNext();
524         verify(mAvrcpControllerService,
525                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
526                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD),
527                 eq(KEY_DOWN));
528         verify(mAvrcpControllerService,
529                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
530                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD), eq(KEY_UP));
531     }
532 
533     /**
534      * Test media browser previous command
535      */
536     @Test
testPrevious()537     public void testPrevious() throws Exception {
538         setUpConnectedState(true, true);
539         MediaControllerCompat.TransportControls transportControls =
540                 BluetoothMediaBrowserService.getTransportControls();
541 
542         //Previous
543         transportControls.skipToPrevious();
544         verify(mAvrcpControllerService,
545                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
546                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD),
547                 eq(KEY_DOWN));
548         verify(mAvrcpControllerService,
549                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
550                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD), eq(KEY_UP));
551     }
552 
553     /**
554      * Test media browser fast forward command
555      */
556     @Test
557     @FlakyTest
testFastForward()558     public void testFastForward() throws Exception {
559         setUpConnectedState(true, true);
560         MediaControllerCompat.TransportControls transportControls =
561                 BluetoothMediaBrowserService.getTransportControls();
562 
563         //FastForward
564         transportControls.fastForward();
565         verify(mAvrcpControllerService,
566                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
567                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF), eq(KEY_DOWN));
568         //Finish FastForwarding
569         transportControls.play();
570         verify(mAvrcpControllerService,
571                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
572                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF), eq(KEY_UP));
573     }
574 
575     /**
576      * Test media browser rewind command
577      */
578     @Test
testRewind()579     public void testRewind() throws Exception {
580         setUpConnectedState(true, true);
581         MediaControllerCompat.TransportControls transportControls =
582                 BluetoothMediaBrowserService.getTransportControls();
583 
584         //Rewind
585         transportControls.rewind();
586         verify(mAvrcpControllerService,
587                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
588                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND), eq(KEY_DOWN));
589         //Finish Rewinding
590         transportControls.play();
591         verify(mAvrcpControllerService,
592                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
593                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND), eq(KEY_UP));
594     }
595 
596     /**
597      * Test media browser skip to queue item
598      */
599     @Test
testSkipToQueueInvalid()600     public void testSkipToQueueInvalid() throws Exception {
601         byte scope = 1;
602         int minSize = 0;
603         int maxSize = 255;
604         setUpConnectedState(true, true);
605         MediaControllerCompat.TransportControls transportControls =
606                 BluetoothMediaBrowserService.getTransportControls();
607 
608         //Play an invalid item below start
609         transportControls.skipToQueueItem(minSize - 1);
610         verify(mAvrcpControllerService,
611                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative(
612                 eq(mTestAddress), eq(scope), anyLong(), anyInt());
613 
614         //Play an invalid item beyond end
615         transportControls.skipToQueueItem(maxSize + 1);
616         verify(mAvrcpControllerService,
617                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative(
618                 eq(mTestAddress), eq(scope), anyLong(), anyInt());
619     }
620 
621     /**
622      * Test media browser shuffle command
623      */
624     @Test
testShuffle()625     public void testShuffle() throws Exception {
626         byte[] shuffleSetting = new byte[]{3};
627         byte[] shuffleMode = new byte[]{2};
628 
629         setUpConnectedState(true, true);
630         MediaControllerCompat.TransportControls transportControls =
631                 BluetoothMediaBrowserService.getTransportControls();
632 
633         //Shuffle
634         transportControls.setShuffleMode(1);
635         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
636                 .setPlayerApplicationSettingValuesNative(
637                 eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
638     }
639 
640     /**
641      * Test media browser repeat command
642      */
643     @Test
testRepeat()644     public void testRepeat() throws Exception {
645         byte[] repeatSetting = new byte[]{2};
646         byte[] repeatMode = new byte[]{3};
647 
648         setUpConnectedState(true, true);
649         MediaControllerCompat.TransportControls transportControls =
650                 BluetoothMediaBrowserService.getTransportControls();
651 
652         //Shuffle
653         transportControls.setRepeatMode(2);
654         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
655                 .setPlayerApplicationSettingValuesNative(
656                 eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
657     }
658 
659     /**
660      * Test media browsing
661      * Verify that a browse tree is created with the proper root
662      * Verify that a player can be fetched and added to the browse tree
663      * Verify that the contents of a player are fetched upon request
664      */
665     @Test
666     @FlakyTest
testBrowsingCommands()667     public void testBrowsingCommands() {
668         setUpConnectedState(true, true);
669         final String rootName = "__ROOT__";
670         final String playerName = "Player 1";
671 
672         //Get the root of the device
673         BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
674         Assert.assertEquals(rootName + mTestDevice.toString(), results.getID());
675 
676         //Request fetch the list of players
677         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
678         mAvrcpStateMachine.requestContents(results);
679         verify(mAvrcpControllerService,
680                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
681                 eq(0), eq(19));
682 
683         //Provide back a player object
684         byte[] playerFeatures =
685                 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
686         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, playerName, 1, playerFeatures, 1);
687         List<AvrcpPlayer> testPlayers = new ArrayList<>();
688         testPlayers.add(playerOne);
689         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
690                 testPlayers);
691 
692         //Verify that the player object is available.
693         mAvrcpStateMachine.requestContents(results);
694         verify(mAvrcpControllerService,
695                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
696                 eq(1), eq(0));
697         mAvrcpStateMachine.sendMessage(
698                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
699         playerNodes = mAvrcpStateMachine.findNode(results.getID());
700         Assert.assertEquals(true, results.isCached());
701         Assert.assertEquals("MediaItem{mFlags=1, mDescription=" + playerName + ", null, null}",
702                 results.getChildren().get(0).getMediaItem().toString());
703 
704         //Fetch contents of that player object
705         BrowseTree.BrowseNode playerOneNode = mAvrcpStateMachine.findNode(
706                 results.getChildren().get(0).getID());
707         mAvrcpStateMachine.requestContents(playerOneNode);
708         verify(mAvrcpControllerService,
709                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).setBrowsedPlayerNative(
710                 eq(mTestAddress), eq(1));
711         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, 5);
712         verify(mAvrcpControllerService,
713                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getFolderListNative(eq(mTestAddress),
714                 eq(0), eq(4));
715     }
716 
717     /**
718      * Test our reaction to an available players changed event
719      *
720      * Verify that we issue a command to fetch the new available players
721      */
722     @Test
testAvailablePlayersChanged()723     public void testAvailablePlayersChanged() {
724         setUpConnectedState(true, true);
725         final String rootName = "__ROOT__";
726 
727         // Send an available players have changed event
728         mAvrcpStateMachine.sendMessage(
729                 AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
730 
731         // Verify we've uncached our browse root and made the call to fetch new players
732         Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
733         verify(mAvrcpControllerService,
734                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
735                 eq(0), eq(19));
736     }
737 
738     /**
739      * Test how we handle receiving an available players list that contains the player we know to
740      * be the addressed player
741      */
742     @Test
testAvailablePlayersReceived_AddressedPlayerExists()743     public void testAvailablePlayersReceived_AddressedPlayerExists() {
744         setUpConnectedState(true, true);
745         final String rootName = "__ROOT__";
746 
747         // Set an addressed player that will be in the available players set
748         mAvrcpStateMachine.sendMessage(
749                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
750         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
751         clearInvocations(mAvrcpControllerService);
752 
753         // Send an available players have changed event
754         mAvrcpStateMachine.sendMessage(
755                 AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
756 
757         // Verify we've uncached our browse root and made the call to fetch new players
758         Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
759         verify(mAvrcpControllerService,
760                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
761                 eq(0), eq(19));
762 
763         // Send available players set that contains our addressed player
764         byte[] playerFeatures =
765                 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
766         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", 1, playerFeatures, 1);
767         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", 1, playerFeatures, 1);
768         List<AvrcpPlayer> testPlayers = new ArrayList<>();
769         testPlayers.add(playerOne);
770         testPlayers.add(playerTwo);
771         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
772                 testPlayers);
773 
774         // Wait for them to be processed
775         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
776 
777         // Verify we processed the first players properly. Note the addressed player should always
778         // be in the available player set.
779         Assert.assertTrue(mAvrcpStateMachine.findNode(rootName).isCached());
780         SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
781         Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
782         Assert.assertEquals(testPlayers.size(), players.size());
783         for (AvrcpPlayer player : testPlayers) {
784             Assert.assertTrue(players.contains(player.getId()));
785         }
786 
787         // Verify we request metadata, playback state and now playing list
788         assertNowPlayingList(new ArrayList<AvrcpItem>());
789         verify(mAvrcpControllerService,
790                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
791                 eq(mTestAddress), eq(0), eq(19));
792         verify(mAvrcpControllerService,
793                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
794                 eq(mTestAddress));
795         verify(mAvrcpControllerService,
796                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
797                 eq(mTestAddress));
798     }
799 
800     /**
801      * Test how we handle receiving an available players list that does not contain the player we
802      * know to be the addressed player
803      */
804     @Test
testAvailablePlayersReceived_AddressedPlayerDoesNotExist()805     public void testAvailablePlayersReceived_AddressedPlayerDoesNotExist() {
806         setUpConnectedState(true, true);
807         final String rootName = "__ROOT__";
808 
809         // Send an available players have changed event
810         mAvrcpStateMachine.sendMessage(
811                 AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
812 
813         // Verify we've uncached our browse root and made the call to fetch new players
814         Assert.assertFalse(mAvrcpStateMachine.findNode(rootName).isCached());
815         verify(mAvrcpControllerService,
816                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
817                 eq(0), eq(19));
818 
819         // Send available players set that does not contain the addressed player
820         byte[] playerFeatures =
821                 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
822         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", 1, playerFeatures, 1);
823         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", 1, playerFeatures, 1);
824         List<AvrcpPlayer> testPlayers = new ArrayList<>();
825         testPlayers.add(playerOne);
826         testPlayers.add(playerTwo);
827         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
828                 testPlayers);
829 
830         // Wait for them to be processed
831         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
832 
833         // Verify we processed the players properly. Note the addressed player is currently the
834         // default player and is not in the available player set sent. This means we'll have an
835         // extra player at ID -1.
836         Assert.assertTrue(mAvrcpStateMachine.findNode(rootName).isCached());
837         SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
838         Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
839         Assert.assertEquals(testPlayers.size() + 1, players.size());
840         for (AvrcpPlayer player : testPlayers) {
841             Assert.assertTrue(players.contains(player.getId()));
842         }
843 
844         // Verify we do not request metadata, playback state and now playing list because we're
845         // sure the addressed player and metadata we have isn't impacted by the new players
846         verify(mAvrcpControllerService,
847                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).getNowPlayingListNative(
848                 any(), anyInt(), anyInt());
849         verify(mAvrcpControllerService,
850                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).getCurrentMetadataNative(any());
851         verify(mAvrcpControllerService,
852                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).getPlaybackStateNative(any());
853     }
854 
855     /**
856      * Test addressed media player changing to a player we know about
857      * Verify when the addressed media player changes browsing data updates
858      */
859     @Test
testAddressedPlayerChangedToNewKnownPlayer()860     public void testAddressedPlayerChangedToNewKnownPlayer() {
861         setUpConnectedState(true, true);
862         final String rootName = "__ROOT__";
863 
864         //Get the root of the device
865         BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
866         Assert.assertEquals(rootName + mTestDevice.toString(), results.getID());
867 
868         //Request fetch the list of players
869         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
870         mAvrcpStateMachine.requestContents(results);
871         verify(mAvrcpControllerService,
872                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress),
873                 eq(0), eq(19));
874 
875         //Provide back two player objects, IDs 1 and 2
876         byte[] playerFeatures =
877                 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
878         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", 1, playerFeatures, 1);
879         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", 1, playerFeatures, 1);
880         List<AvrcpPlayer> testPlayers = new ArrayList<>();
881         testPlayers.add(playerOne);
882         testPlayers.add(playerTwo);
883         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
884                 testPlayers);
885 
886         //Set something arbitrary for the current Now Playing list
887         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
888         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
889         nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
890         setNowPlayingList(nowPlayingList);
891         clearInvocations(mAvrcpControllerService);
892 
893         //Change players and verify that BT attempts to update the results
894         mAvrcpStateMachine.sendMessage(
895                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 2);
896         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
897 
898         // The addressed player should always be in the available player set
899         Assert.assertEquals(2, mAvrcpStateMachine.getAddressedPlayerId());
900         SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
901         Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
902 
903         //Make sure the Now Playing list is now cleared
904         assertNowPlayingList(new ArrayList<AvrcpItem>());
905 
906         //Verify that a player change to a player with Now Playing support causes a refresh.
907         verify(mAvrcpControllerService,
908                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
909                 eq(mTestAddress), eq(0), eq(19));
910 
911         //Verify we request metadata and playback state
912         verify(mAvrcpControllerService,
913                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
914                 eq(mTestAddress));
915         verify(mAvrcpControllerService,
916                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
917                 eq(mTestAddress));
918     }
919 
920     /**
921      * Test addressed media player change to a player we don't know about
922      * Verify when the addressed media player changes browsing data updates
923      * Verify that the contents of a player are fetched upon request
924      */
925     @Test
testAddressedPlayerChangedToUnknownPlayer()926     public void testAddressedPlayerChangedToUnknownPlayer() {
927         setUpConnectedState(true, true);
928         final String rootName = "__ROOT__";
929 
930         //Get the root of the device
931         BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.findNode(rootName);
932         Assert.assertEquals(rootName + mTestDevice.toString(), rootNode.getID());
933 
934         //Request fetch the list of players
935         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(rootNode.getID());
936         mAvrcpStateMachine.requestContents(rootNode);
937 
938         //Provide back a player object
939         byte[] playerFeatures =
940                 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
941         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", 1, playerFeatures, 1);
942         List<AvrcpPlayer> testPlayers = new ArrayList<>();
943         testPlayers.add(playerOne);
944         mAvrcpStateMachine.sendMessage(
945                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
946 
947         //Set something arbitrary for the current Now Playing list
948         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
949         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
950         nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
951         setNowPlayingList(nowPlayingList);
952 
953         //Change players
954         mAvrcpStateMachine.sendMessage(
955                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 4);
956         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
957 
958         //Make sure the Now Playing list is now cleared and we requested metadata
959         assertNowPlayingList(new ArrayList<AvrcpItem>());
960         verify(mAvrcpControllerService,
961                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
962                 eq(mTestAddress));
963         verify(mAvrcpControllerService,
964                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
965                 eq(mTestAddress));
966     }
967 
968     /**
969      * Test what we do when we receive an addressed player change to a player with the same ID as
970      * the current addressed play.
971      *
972      * Verify we assume nothing and re-fetch the current metadata and playback status.
973      */
974     @Test
testAddressedPlayerChangedToSamePlayerId()975     public void testAddressedPlayerChangedToSamePlayerId() {
976         setUpConnectedState(true, true);
977         final String rootName = "__ROOT__";
978 
979         // Set the addressed player so we can change to the same one
980         mAvrcpStateMachine.sendMessage(
981                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
982 
983         // Wait until idle so Now Playing List is queried for, resolve it
984         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
985         mAvrcpStateMachine.sendMessage(
986                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
987         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
988 
989         //Get the root of the device
990         BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.findNode(rootName);
991         Assert.assertEquals(rootName + mTestDevice.toString(), rootNode.getID());
992 
993         //Request fetch the list of players
994         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(rootNode.getID());
995         mAvrcpStateMachine.requestContents(rootNode);
996 
997         // Send available players set that contains our addressed player
998         byte[] playerFeatures =
999                 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
1000         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", 1, playerFeatures, 1);
1001         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", 1, playerFeatures, 1);
1002         List<AvrcpPlayer> testPlayers = new ArrayList<>();
1003         testPlayers.add(playerOne);
1004         testPlayers.add(playerTwo);
1005         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS,
1006                 testPlayers);
1007 
1008         // Wait until idle so Now Playing List is queried for again, resolve it again
1009         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1010         mAvrcpStateMachine.sendMessage(
1011                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
1012         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1013         clearInvocations(mAvrcpControllerService);
1014 
1015         // Send an addressed player changed to the same player ID
1016         mAvrcpStateMachine.sendMessage(
1017                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
1018         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1019 
1020         // Verify we make no assumptions about the player ID and still fetch metadata, play status
1021         // and now playing list (since player 1 supports it)
1022         verify(mAvrcpControllerService,
1023                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
1024                 eq(mTestAddress), eq(0), eq(19));
1025         verify(mAvrcpControllerService,
1026                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getCurrentMetadataNative(
1027                 eq(mTestAddress));
1028         verify(mAvrcpControllerService,
1029                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlaybackStateNative(
1030                 eq(mTestAddress));
1031     }
1032 
1033     /**
1034      * Test that the Now Playing playlist is updated when it changes.
1035      */
1036     @Test
testNowPlaying()1037     public void testNowPlaying() {
1038         setUpConnectedState(true, true);
1039         mAvrcpStateMachine.nowPlayingContentChanged();
1040         verify(mAvrcpControllerService,
1041                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative(
1042                 eq(mTestAddress), eq(0), eq(19));
1043     }
1044 
1045     /**
1046      * Test that AVRCP events such as playback commands can execute while performing browsing.
1047      */
1048     @Test
testPlayWhileBrowsing()1049     public void testPlayWhileBrowsing() {
1050         setUpConnectedState(true, true);
1051         final String rootName = "__ROOT__";
1052         final String playerName = "Player 1";
1053 
1054         //Get the root of the device
1055         BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName);
1056         Assert.assertEquals(rootName + mTestDevice.toString(), results.getID());
1057 
1058         //Request fetch the list of players
1059         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
1060         mAvrcpStateMachine.requestContents(results);
1061 
1062         MediaControllerCompat.TransportControls transportControls =
1063                 BluetoothMediaBrowserService.getTransportControls();
1064         transportControls.play();
1065         verify(mAvrcpControllerService,
1066                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
1067                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN));
1068         verify(mAvrcpControllerService,
1069                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
1070                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP));
1071     }
1072 
1073     /**
1074      * Test that Absolute Volume Registration is working
1075      */
1076     @Test
testRegisterAbsVolumeNotification()1077     public void testRegisterAbsVolumeNotification() {
1078         setUpConnectedState(true, true);
1079         mAvrcpStateMachine.sendMessage(
1080                 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION);
1081         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1082                 .sendRegisterAbsVolRspNative(any(), anyByte(), eq(127), anyInt());
1083     }
1084 
1085     /**
1086      * Test playback does not request focus when another app is playing music.
1087      */
1088     @Test
testPlaybackWhileMusicPlaying()1089     public void testPlaybackWhileMusicPlaying() {
1090         when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
1091                 .thenReturn(false);
1092         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_NONE);
1093         doReturn(true).when(mAudioManager).isMusicActive();
1094         setUpConnectedState(true, true);
1095         mAvrcpStateMachine.sendMessage(
1096                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1097                 PlaybackStateCompat.STATE_PLAYING);
1098         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1099         verify(mAvrcpControllerService,
1100                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
1101                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
1102         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1103     }
1104 
1105     /**
1106      * Test playback requests focus while nothing is playing music.
1107      */
1108     @Test
testPlaybackWhileIdle()1109     public void testPlaybackWhileIdle() {
1110         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_NONE);
1111         doReturn(false).when(mAudioManager).isMusicActive();
1112         setUpConnectedState(true, true);
1113         mAvrcpStateMachine.sendMessage(
1114                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1115                 PlaybackStateCompat.STATE_PLAYING);
1116         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1117         verify(mA2dpSinkService).requestAudioFocus(mTestDevice, true);
1118     }
1119 
1120     /**
1121      * Test receiving a playback status of playing while we're in an error state
1122      * as it relates to getting audio focus.
1123      *
1124      * Verify we send a pause command and never attempt to request audio focus
1125      */
1126     @Test
testPlaybackWhileErrorState()1127     public void testPlaybackWhileErrorState() {
1128         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.ERROR);
1129         setUpConnectedState(true, true);
1130         mAvrcpStateMachine.sendMessage(
1131                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1132                 PlaybackStateCompat.STATE_PLAYING);
1133         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1134         verify(mAvrcpControllerService,
1135                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
1136                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
1137         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1138     }
1139 
1140     /**
1141      * Test receiving a playback status of playing while we have focus
1142      *
1143      * Verify we do not send a pause command and never attempt to request audio focus
1144      */
1145     @Test
testPlaybackWhilePlayingState()1146     public void testPlaybackWhilePlayingState() {
1147         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_GAIN);
1148         setUpConnectedState(true, true);
1149         Assert.assertTrue(mAvrcpStateMachine.isActive());
1150         mAvrcpStateMachine.sendMessage(
1151                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1152                 PlaybackStateCompat.STATE_PLAYING);
1153         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1154         verify(mAvrcpControllerService, never()).sendPassThroughCommandNative(
1155                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
1156         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1157     }
1158 
1159     /**
1160      * Test that isActive() reports the proper value when we're active
1161      */
1162     @Test
testIsActive_deviceActive()1163     public void testIsActive_deviceActive() {
1164         Assert.assertTrue(mAvrcpStateMachine.isActive());
1165     }
1166 
1167     /**
1168      * Test that isActive() reports the proper value when we're inactive
1169      */
1170     @Test
testIsActive_deviceInactive()1171     public void testIsActive_deviceInactive() {
1172         setActiveDevice(null);
1173         Assert.assertFalse(mAvrcpStateMachine.isActive());
1174     }
1175 
1176     /**
1177      * Test becoming active from the inactive state
1178      */
1179     @Test
testBecomeActive()1180     public void testBecomeActive() {
1181         // Note device starts as active in setUp() and state cascades come the CONNECTED state
1182         setUpConnectedState(true, true);
1183         Assert.assertTrue(mAvrcpStateMachine.isActive());
1184 
1185         // Make the device inactive
1186         setActiveDevice(null);
1187         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1188         Assert.assertFalse(mAvrcpStateMachine.isActive());
1189 
1190         // Change device state while inactive
1191         AvrcpItem track = makeTrack("title", "artist", "album", 1, 10, "none", 10, null);
1192         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1193         AvrcpItem queueItem1 = makeNowPlayingItem(0, "title");
1194         AvrcpItem queueItem2 = makeNowPlayingItem(1, "title 2");
1195         nowPlayingList.add(queueItem1);
1196         nowPlayingList.add(queueItem2);
1197         setCurrentTrack(track);
1198         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1199         setPlaybackPosition(7, 10);
1200         setNowPlayingList(nowPlayingList);
1201 
1202         // Make device active
1203         setActiveDevice(mTestDevice);
1204         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1205         Assert.assertTrue(mAvrcpStateMachine.isActive());
1206 
1207         // See that state from BluetoothMediaBrowserService is updated
1208         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1209         Assert.assertNotNull(session);
1210         MediaControllerCompat controller = session.getController();
1211         Assert.assertNotNull(controller);
1212 
1213         MediaMetadataCompat metadata = controller.getMetadata();
1214         Assert.assertNotNull(metadata);
1215         Assert.assertEquals("title", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
1216         Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
1217         Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
1218         Assert.assertEquals(1, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
1219         Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
1220         Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
1221         Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
1222 
1223         PlaybackStateCompat playbackState = controller.getPlaybackState();
1224         Assert.assertNotNull(playbackState);
1225         Assert.assertEquals(PlaybackStateCompat.STATE_PAUSED, playbackState.getState());
1226         Assert.assertEquals(7, playbackState.getPosition());
1227 
1228         List<MediaSessionCompat.QueueItem> queue = controller.getQueue();
1229         Assert.assertNotNull(queue);
1230         Assert.assertEquals(2, queue.size());
1231         Assert.assertEquals("title", queue.get(0).getDescription().getTitle().toString());
1232         Assert.assertEquals("title 2", queue.get(1).getDescription().getTitle().toString());
1233     }
1234 
1235     /**
1236      * Test becoming inactive from the active state
1237      */
1238     @Test
testBecomeInactive()1239     public void testBecomeInactive() {
1240         // Note device starts as active in setUp()
1241         setUpConnectedState(true, true);
1242         Assert.assertTrue(mAvrcpStateMachine.isActive());
1243 
1244         // Set the active device to something else, verify we're inactive and send a pause upon
1245         // becoming inactive
1246         setActiveDevice(null);
1247         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1248         verify(mAvrcpControllerService,
1249                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
1250                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
1251         Assert.assertFalse(mAvrcpStateMachine.isActive());
1252     }
1253 
1254     /**
1255      * Test receiving a track change update when we're not the active device
1256      */
1257     @Test
testTrackChangeWhileNotActiveDevice()1258     public void testTrackChangeWhileNotActiveDevice() {
1259         setUpConnectedState(true, true);
1260 
1261         // Set the active device to something else, verify we're inactive and send a pause upon
1262         // becoming inactive
1263         setActiveDevice(null);
1264         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1265         Assert.assertFalse(mAvrcpStateMachine.isActive());
1266 
1267         // Change track while inactive
1268         AvrcpItem track = makeTrack("title", "artist", "album", 1, 10, "none", 10, null);
1269         setCurrentTrack(track);
1270 
1271         // Since we're not active, verify BluetoothMediaBrowserService does not have these values
1272         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1273         Assert.assertNotNull(session);
1274         MediaControllerCompat controller = session.getController();
1275         Assert.assertNotNull(controller);
1276 
1277         MediaMetadataCompat metadata = controller.getMetadata();
1278         Assert.assertNull(metadata); // track starts as null and shouldn't change
1279     }
1280 
1281     /**
1282      * Test receiving a playback status of playing when we're not the active device
1283      */
1284     @Test
testPlaybackWhileNotActiveDevice()1285     public void testPlaybackWhileNotActiveDevice() {
1286         setUpConnectedState(true, true);
1287 
1288         // Set the active device to something else, verify we're inactive
1289         setActiveDevice(null);
1290         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1291         Assert.assertFalse(mAvrcpStateMachine.isActive());
1292         clearInvocations(mAvrcpControllerService);
1293 
1294         // Now that we're inactive, receive a playback status of playing
1295         setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
1296 
1297         // Verify we send a pause, never request audio focus, and the playback state on
1298         // BluetoothMediaBrowserService never updates.
1299         verify(mAvrcpControllerService,
1300                 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative(
1301                 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN));
1302         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1303         Assert.assertEquals(PlaybackStateCompat.STATE_ERROR,
1304                 BluetoothMediaBrowserService.getPlaybackState());
1305     }
1306 
1307     /**
1308      * Test receiving a play position update when we're not the active device
1309      */
1310     @Test
testPlayPositionChangeWhileNotActiveDevice()1311     public void testPlayPositionChangeWhileNotActiveDevice() {
1312         setUpConnectedState(true, true);
1313 
1314         // Set the active device to something else, verify we're inactive
1315         setActiveDevice(null);
1316         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1317         Assert.assertFalse(mAvrcpStateMachine.isActive());
1318         clearInvocations(mAvrcpControllerService);
1319 
1320         // Now that we're inactive, receive a play position change
1321         setPlaybackPosition(1, 10);
1322 
1323         // Since we're not active, verify BluetoothMediaBrowserService does not have these values
1324         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1325         Assert.assertNotNull(session);
1326         MediaControllerCompat controller = session.getController();
1327         Assert.assertNotNull(controller);
1328 
1329         PlaybackStateCompat playbackState = controller.getPlaybackState();
1330         Assert.assertNotNull(playbackState);
1331         Assert.assertEquals(0, playbackState.getPosition());
1332     }
1333 
1334     /**
1335      * Test receiving a now playing list update when we're not the active device
1336      */
1337     @Test
testNowPlayingListChangeWhileNotActiveDevice()1338     public void testNowPlayingListChangeWhileNotActiveDevice() {
1339         setUpConnectedState(true, true);
1340 
1341         // Set the active device to something else, verify we're inactive and send a pause upon
1342         // becoming inactive
1343         setActiveDevice(null);
1344         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1345         Assert.assertFalse(mAvrcpStateMachine.isActive());
1346 
1347         // Change queue while inactive
1348         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1349         AvrcpItem queueItem1 = makeNowPlayingItem(0, "title");
1350         AvrcpItem queueItem2 = makeNowPlayingItem(1, "title 2");
1351         AvrcpItem queueItem3 = makeNowPlayingItem(1, "title 3");
1352         nowPlayingList.add(queueItem1);
1353         nowPlayingList.add(queueItem2);
1354         nowPlayingList.add(queueItem3);
1355         setNowPlayingList(nowPlayingList);
1356 
1357         // Since we're not active, verify BluetoothMediaBrowserService does not have these values
1358         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1359         Assert.assertNotNull(session);
1360         MediaControllerCompat controller = session.getController();
1361         Assert.assertNotNull(controller);
1362 
1363         List<MediaSessionCompat.QueueItem> queue = controller.getQueue();
1364         Assert.assertNull(queue);
1365     }
1366 }
1367