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 
20 import static com.google.common.truth.Truth.assertThat;
21 
22 import static org.mockito.Mockito.*;
23 
24 import android.bluetooth.BluetoothAdapter;
25 import android.bluetooth.BluetoothAvrcpController;
26 import android.bluetooth.BluetoothDevice;
27 import android.bluetooth.BluetoothProfile;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.content.res.Resources;
31 import android.media.AudioManager;
32 import android.os.Bundle;
33 import android.os.Looper;
34 import android.platform.test.annotations.EnableFlags;
35 import android.platform.test.flag.junit.SetFlagsRule;
36 import android.support.v4.media.MediaMetadataCompat;
37 import android.support.v4.media.session.MediaControllerCompat;
38 import android.support.v4.media.session.MediaSessionCompat;
39 import android.support.v4.media.session.PlaybackStateCompat;
40 import android.util.SparseArray;
41 
42 import androidx.test.filters.FlakyTest;
43 import androidx.test.filters.MediumTest;
44 import androidx.test.rule.ServiceTestRule;
45 import androidx.test.runner.AndroidJUnit4;
46 
47 import com.android.bluetooth.R;
48 import com.android.bluetooth.TestUtils;
49 import com.android.bluetooth.a2dpsink.A2dpSinkService;
50 import com.android.bluetooth.btservice.AdapterService;
51 import com.android.bluetooth.flags.Flags;
52 
53 import org.hamcrest.core.IsInstanceOf;
54 import org.junit.After;
55 import org.junit.Assert;
56 import org.junit.Before;
57 import org.junit.Rule;
58 import org.junit.Test;
59 import org.junit.runner.RunWith;
60 import org.mockito.ArgumentCaptor;
61 import org.mockito.Mock;
62 import org.mockito.junit.MockitoJUnit;
63 import org.mockito.junit.MockitoRule;
64 
65 import java.util.ArrayList;
66 import java.util.List;
67 import java.util.UUID;
68 
69 @MediumTest
70 @RunWith(AndroidJUnit4.class)
71 public class AvrcpControllerStateMachineTest {
72     private static final int ASYNC_CALL_TIMEOUT_MILLIS = 100;
73     private static final int KEY_DOWN = 0;
74     private static final int KEY_UP = 1;
75     private static final int UUID_START = 0;
76     private static final int UUID_LENGTH = 25;
77 
78     private BluetoothAdapter mAdapter;
79 
80     @Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
81 
82     @Rule public MockitoRule mockitoRule = MockitoJUnit.rule();
83 
84     @Mock private AdapterService mAdapterService;
85 
86     @Mock private A2dpSinkService mA2dpSinkService;
87     @Mock private Resources mMockResources;
88 
89     @Mock private AvrcpControllerService mAvrcpControllerService;
90     @Mock private AvrcpControllerNativeInterface mNativeInterface;
91     @Mock private AvrcpCoverArtManager mCoverArtManager;
92     @Mock private AudioManager mAudioManager;
93 
94     @Rule
95     public final ServiceTestRule mBluetoothBrowserMediaServiceTestRule = new ServiceTestRule();
96 
97     private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class);
98 
99     private byte[] mTestAddress = new byte[] {01, 01, 01, 01, 01, 01};
100     private BluetoothDevice mTestDevice = null;
101     private AvrcpControllerStateMachine mAvrcpStateMachine = null;
102 
103     @Before
setUp()104     public void setUp() throws Exception {
105         if (Looper.myLooper() == null) {
106             Looper.prepare();
107         }
108         Assert.assertNotNull(Looper.myLooper());
109 
110         // Set a mock Adapter Service for profile state change notifications
111         TestUtils.setAdapterService(mAdapterService);
112 
113         // Set a mock A2dpSinkService for audio focus calls
114         A2dpSinkService.setA2dpSinkService(mA2dpSinkService);
115 
116         // Mock an AvrcpControllerService to give to all state machines
117         doReturn(BluetoothProfile.STATE_DISCONNECTED).when(mCoverArtManager).getState(any());
118         doReturn(15).when(mAudioManager).getStreamMaxVolume(anyInt());
119         doReturn(8).when(mAudioManager).getStreamVolume(anyInt());
120         doReturn(true).when(mAudioManager).isVolumeFixed();
121         when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
122                 .thenReturn(true);
123         doReturn(mMockResources).when(mAvrcpControllerService).getResources();
124         doReturn(mAudioManager)
125                 .when(mAvrcpControllerService)
126                 .getSystemService(Context.AUDIO_SERVICE);
127         doReturn(Context.AUDIO_SERVICE)
128                 .when(mAvrcpControllerService)
129                 .getSystemServiceName(AudioManager.class);
130         doReturn(mCoverArtManager).when(mAvrcpControllerService).getCoverArtManager();
131         mAvrcpControllerService.sBrowseTree = new BrowseTree(null);
132         AvrcpControllerService.setAvrcpControllerService(mAvrcpControllerService);
133 
134         // Start the Bluetooth Media Browser Service
135         final Intent bluetoothBrowserMediaServiceStartIntent =
136                 TestUtils.prepareIntentToStartBluetoothBrowserMediaService();
137         mBluetoothBrowserMediaServiceTestRule.startService(bluetoothBrowserMediaServiceStartIntent);
138 
139         // Ensure our MediaBrowserService starts with a blank state
140         BluetoothMediaBrowserService.reset();
141 
142         // This line must be called to make sure relevant objects are initialized properly
143         mAdapter = BluetoothAdapter.getDefaultAdapter();
144 
145         // Set up device and state machine under test
146         mTestDevice = mAdapter.getRemoteDevice(mTestAddress);
147         mAvrcpStateMachine = makeStateMachine(mTestDevice);
148 
149         setActiveDevice(mTestDevice);
150     }
151 
152     @After
tearDown()153     public void tearDown() throws Exception {
154         destroyStateMachine(mAvrcpStateMachine);
155         A2dpSinkService.setA2dpSinkService(null);
156         AvrcpControllerService.setAvrcpControllerService(null);
157         TestUtils.clearAdapterService(mAdapterService);
158     }
159 
160     /** Create a state machine to test */
makeStateMachine(BluetoothDevice device)161     private AvrcpControllerStateMachine makeStateMachine(BluetoothDevice device) {
162         AvrcpControllerStateMachine sm =
163                 new AvrcpControllerStateMachine(
164                         device, mAvrcpControllerService, mNativeInterface, false);
165         sm.start();
166         return sm;
167     }
168 
169     /** Destroy a state machine you created to test */
destroyStateMachine(AvrcpControllerStateMachine sm)170     private void destroyStateMachine(AvrcpControllerStateMachine sm) {
171         if (sm == null || sm.getState() == BluetoothProfile.STATE_DISCONNECTED) return;
172 
173         sm.disconnect();
174         TestUtils.waitForLooperToBeIdle(sm.getHandler().getLooper());
175 
176         // is disconnected
177         Assert.assertEquals(sm.getState(), BluetoothProfile.STATE_DISCONNECTED);
178 
179         // told mAvrcpControllerService to remove it
180         verify(mAvrcpControllerService).removeStateMachine(eq(sm));
181     }
182 
183     /** Set up which device the AvrcpControllerService will report as active */
setActiveDevice(BluetoothDevice device)184     private void setActiveDevice(BluetoothDevice device) {
185         doReturn(device).when(mAvrcpControllerService).getActiveDevice();
186         if (mTestDevice.equals(device)) {
187             mAvrcpStateMachine.setDeviceState(AvrcpControllerService.DEVICE_STATE_ACTIVE);
188         } else {
189             mAvrcpStateMachine.setDeviceState(AvrcpControllerService.DEVICE_STATE_INACTIVE);
190             BluetoothMediaBrowserService.reset();
191         }
192     }
193 
194     /** Send an audio focus changed event to the state machine under test */
sendAudioFocusUpdate(int state)195     private void sendAudioFocusUpdate(int state) {
196         when(mA2dpSinkService.getFocusState()).thenReturn(state);
197         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.AUDIO_FOCUS_STATE_CHANGE, state);
198     }
199 
200     /**
201      * Setup Connected State for a given state machine
202      *
203      * @return number of times mAvrcpControllerService.sendBroadcastAsUser() has been invoked
204      */
setUpConnectedState(boolean control, boolean browsing)205     private int setUpConnectedState(boolean control, boolean browsing) {
206 
207         Assert.assertThat(
208                 mAvrcpStateMachine.getCurrentState(),
209                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
210 
211         mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(control, browsing));
212 
213         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
214         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
215                 .sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
216         Assert.assertThat(
217                 mAvrcpStateMachine.getCurrentState(),
218                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Connected.class));
219         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_CONNECTED);
220 
221         return BluetoothProfile.STATE_CONNECTED;
222     }
223 
makeTrack( String title, String artist, String album, long trackNum, long totalTracks, String genre, long duration, String imageHandle)224     private AvrcpItem makeTrack(
225             String title,
226             String artist,
227             String album,
228             long trackNum,
229             long totalTracks,
230             String genre,
231             long duration,
232             String imageHandle) {
233         AvrcpItem.Builder builder = new AvrcpItem.Builder();
234         builder.setItemType(AvrcpItem.TYPE_MEDIA);
235         builder.setType(AvrcpItem.MEDIA_AUDIO);
236         builder.setDevice(mTestDevice);
237         builder.setPlayable(true);
238         builder.setUid(0);
239         builder.setUuid("AVRCP-ITEM-TEST-UUID");
240 
241         builder.setTitle(title);
242         builder.setArtistName(artist);
243         builder.setAlbumName(album);
244         builder.setTrackNumber(trackNum);
245         builder.setTotalNumberOfTracks(totalTracks);
246         builder.setGenre(genre);
247         builder.setPlayingTime(duration);
248         if (imageHandle != null) {
249             builder.setCoverArtHandle(imageHandle);
250         }
251 
252         return builder.build();
253     }
254 
makePlayer( BluetoothDevice device, int playerId, String playerName, byte[] playerFeatures, int playStatus)255     private AvrcpPlayer makePlayer(
256             BluetoothDevice device,
257             int playerId,
258             String playerName,
259             byte[] playerFeatures,
260             int playStatus) {
261         AvrcpPlayer.Builder apb = new AvrcpPlayer.Builder();
262         apb.setDevice(device);
263         apb.setPlayerId(playerId);
264         apb.setName(playerName);
265         apb.setSupportedFeatures(playerFeatures);
266         apb.setPlayStatus(playStatus);
267         return apb.build();
268     }
269 
270     /**
271      * Send a message to the state machine that the track has changed. Must be connected to do this.
272      */
setCurrentTrack(AvrcpItem track)273     private void setCurrentTrack(AvrcpItem track) {
274         mAvrcpStateMachine.sendMessage(
275                 AvrcpControllerStateMachine.MESSAGE_PROCESS_TRACK_CHANGED, track);
276         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
277         Assert.assertEquals(mAvrcpStateMachine.getCurrentTrack(), track);
278     }
279 
280     /** Set the current play status (Play, Pause, etc.) of the device */
setPlaybackState(int state)281     private void setPlaybackState(int state) {
282         mAvrcpStateMachine.sendMessage(
283                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, state);
284         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
285     }
286 
287     /** Set the current playback position of the device */
setPlaybackPosition(int position, int duration)288     private void setPlaybackPosition(int position, int duration) {
289         mAvrcpStateMachine.sendMessage(
290                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_POS_CHANGED, duration, position);
291         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
292     }
293 
294     /** Make an AvrcpItem suitable for being included in the Now Playing list for the test device */
makeNowPlayingItem(long uid, String name)295     private AvrcpItem makeNowPlayingItem(long uid, String name) {
296         AvrcpItem.Builder aib = new AvrcpItem.Builder();
297         aib.setDevice(mTestDevice);
298         aib.setItemType(AvrcpItem.TYPE_MEDIA);
299         aib.setType(AvrcpItem.MEDIA_AUDIO);
300         aib.setTitle(name);
301         aib.setUid(uid);
302         aib.setUuid(UUID.randomUUID().toString());
303         aib.setPlayable(true);
304         return aib.build();
305     }
306 
307     /** Get the current Now Playing list for the test device */
getNowPlayingList()308     private List<AvrcpItem> getNowPlayingList() {
309         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
310         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
311         for (BrowseTree.BrowseNode child : nowPlaying.getChildren()) {
312             nowPlayingList.add(child.mItem);
313         }
314         return nowPlayingList;
315     }
316 
317     /** Set the current Now Playing list for the test device */
setNowPlayingList(List<AvrcpItem> nowPlayingList)318     private void setNowPlayingList(List<AvrcpItem> nowPlayingList) {
319         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
320         mAvrcpStateMachine.requestContents(nowPlaying);
321         mAvrcpStateMachine.sendMessage(
322                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, nowPlayingList);
323         mAvrcpStateMachine.sendMessage(
324                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
325 
326         // Wait for the now playing list to be propagated
327         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
328 
329         // Make sure its set by re grabbing the node and checking its contents are cached
330         nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
331         Assert.assertTrue(nowPlaying.isCached());
332         assertNowPlayingList(nowPlayingList);
333     }
334 
avrcpItemListToString(List<AvrcpItem> items)335     private String avrcpItemListToString(List<AvrcpItem> items) {
336         StringBuilder s = new StringBuilder();
337         s.append("[");
338         if (items != null) {
339             for (int i = 0; i < items.size(); i++) {
340                 AvrcpItem item = items.get(i);
341                 s.append((item != null ? Long.toString(item.getUid()) : "null"));
342                 if (i != items.size() - 1) s.append(", ");
343             }
344         }
345         s.append("]");
346         return s.toString();
347     }
348 
349     /** Assert that the Now Playing list is a particular value */
assertNowPlayingList(List<AvrcpItem> expected)350     private void assertNowPlayingList(List<AvrcpItem> expected) {
351         List<AvrcpItem> current = getNowPlayingList();
352         String err =
353                 "Now playing list incorrect, expected="
354                         + avrcpItemListToString(expected)
355                         + ", actual="
356                         + avrcpItemListToString(current);
357         Assert.assertEquals(err, expected.size(), current.size());
358         for (int i = 0; i < expected.size(); i++) {
359             Assert.assertEquals(err, expected.get(i), current.get(i));
360         }
361     }
362 
363     /**
364      * Test to confirm that the state machine is capable of cycling through the 4 connection states,
365      * and that upon completion, it cleans up afterwards.
366      */
367     @Test
testDisconnect()368     public void testDisconnect() {
369         int numBroadcastsSent = setUpConnectedState(true, true);
370         testDisconnectInternal(numBroadcastsSent);
371     }
372 
373     /**
374      * Test to confirm that the state machine is capable of cycling through the 4 connection states
375      * with no crashes, even if the {@link AvrcpControllerService} is stopped and the {@code
376      * sBrowseTree} is null. This could happen if BT is disabled as the profile is being
377      * disconnected.
378      */
379     @Test
testDisconnectWithNullBrowseTree()380     public void testDisconnectWithNullBrowseTree() {
381         int numBroadcastsSent = setUpConnectedState(true, true);
382 
383         testDisconnectInternal(numBroadcastsSent);
384     }
385 
testDisconnectInternal(int numBroadcastsSent)386     private void testDisconnectInternal(int numBroadcastsSent) {
387         mAvrcpStateMachine.disconnect();
388         numBroadcastsSent += 2;
389         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
390                 .sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
391         Assert.assertEquals(
392                 mTestDevice,
393                 mIntentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
394         Assert.assertEquals(
395                 BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
396                 mIntentArgument.getValue().getAction());
397         Assert.assertEquals(
398                 BluetoothProfile.STATE_DISCONNECTED,
399                 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
400         Assert.assertThat(
401                 mAvrcpStateMachine.getCurrentState(),
402                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
403         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
404         verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
405     }
406 
407     /** Test to confirm that a control only device can be established (no browsing) */
408     @Test
testControlOnly()409     public void testControlOnly() {
410         int numBroadcastsSent = setUpConnectedState(true, false);
411         MediaControllerCompat.TransportControls transportControls =
412                 BluetoothMediaBrowserService.getTransportControls();
413         Assert.assertNotNull(transportControls);
414         Assert.assertEquals(
415                 PlaybackStateCompat.STATE_NONE,
416                 BluetoothMediaBrowserService.getPlaybackState().getState());
417         mAvrcpStateMachine.disconnect();
418         numBroadcastsSent += 2;
419         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
420                 .sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
421         Assert.assertEquals(
422                 mTestDevice,
423                 mIntentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
424         Assert.assertEquals(
425                 BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
426                 mIntentArgument.getValue().getAction());
427         Assert.assertEquals(
428                 BluetoothProfile.STATE_DISCONNECTED,
429                 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
430         Assert.assertThat(
431                 mAvrcpStateMachine.getCurrentState(),
432                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
433         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
434         verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
435     }
436 
437     /** Test to confirm that a browsing only device can be established (no control) */
438     @Test
439     @FlakyTest
testBrowsingOnly()440     public void testBrowsingOnly() {
441         Assert.assertEquals(0, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
442         int numBroadcastsSent = setUpConnectedState(false, true);
443         Assert.assertEquals(1, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount());
444         Assert.assertEquals(
445                 PlaybackStateCompat.STATE_NONE,
446                 BluetoothMediaBrowserService.getPlaybackState().getState());
447         mAvrcpStateMachine.disconnect();
448         numBroadcastsSent += 2;
449         verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent))
450                 .sendBroadcast(mIntentArgument.capture(), eq(BLUETOOTH_CONNECT), any(Bundle.class));
451         Assert.assertEquals(
452                 mTestDevice,
453                 mIntentArgument.getValue().getParcelableExtra(BluetoothDevice.EXTRA_DEVICE));
454         Assert.assertEquals(
455                 BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED,
456                 mIntentArgument.getValue().getAction());
457         Assert.assertEquals(
458                 BluetoothProfile.STATE_DISCONNECTED,
459                 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1));
460         Assert.assertThat(
461                 mAvrcpStateMachine.getCurrentState(),
462                 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class));
463         Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED);
464         verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine));
465     }
466 
467     /** Get the root of the device */
468     @Test
testGetDeviceRootNode_rootNodeMatchesUuidFormat()469     public void testGetDeviceRootNode_rootNodeMatchesUuidFormat() {
470         // create new state machine to follow current flags rule
471         mAvrcpStateMachine = makeStateMachine(mTestDevice);
472         setUpConnectedState(true, true);
473         final String rootName = "__ROOT__" + mTestDevice.getAddress().toString();
474         // Get the root of the device
475         BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;
476         assertThat((results.getID()).substring(UUID_START, UUID_LENGTH)).isEqualTo(rootName);
477     }
478 
479     /** Test to make sure the state machine is tracking the correct device */
480     @Test
testGetDevice()481     public void testGetDevice() {
482         Assert.assertEquals(mAvrcpStateMachine.getDevice(), mTestDevice);
483     }
484 
485     /** Test that dumpsys will generate information about connected devices */
486     @Test
testDump()487     public void testDump() {
488         StringBuilder sb = new StringBuilder();
489         mAvrcpStateMachine.dump(sb);
490         Assert.assertNotNull(sb.toString());
491     }
492 
493     /** Test media browser play command */
494     @Test
testPlay()495     public void testPlay() throws Exception {
496         setUpConnectedState(true, true);
497         MediaControllerCompat.TransportControls transportControls =
498                 BluetoothMediaBrowserService.getTransportControls();
499 
500         // Play
501         transportControls.play();
502         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
503                 .sendPassThroughCommand(
504                         eq(mTestAddress),
505                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
506                         eq(KEY_DOWN));
507         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
508                 .sendPassThroughCommand(
509                         eq(mTestAddress),
510                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
511                         eq(KEY_UP));
512     }
513 
514     /** Test media browser pause command */
515     @Test
testPause()516     public void testPause() throws Exception {
517         setUpConnectedState(true, true);
518         MediaControllerCompat.TransportControls transportControls =
519                 BluetoothMediaBrowserService.getTransportControls();
520 
521         // Pause
522         transportControls.pause();
523         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
524                 .sendPassThroughCommand(
525                         eq(mTestAddress),
526                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
527                         eq(KEY_DOWN));
528         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
529                 .sendPassThroughCommand(
530                         eq(mTestAddress),
531                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
532                         eq(KEY_UP));
533     }
534 
535     /** Test media browser stop command */
536     @Test
testStop()537     public void testStop() throws Exception {
538         setUpConnectedState(true, true);
539         MediaControllerCompat.TransportControls transportControls =
540                 BluetoothMediaBrowserService.getTransportControls();
541 
542         // Stop
543         transportControls.stop();
544         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
545                 .sendPassThroughCommand(
546                         eq(mTestAddress),
547                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
548                         eq(KEY_DOWN));
549         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
550                 .sendPassThroughCommand(
551                         eq(mTestAddress),
552                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
553                         eq(KEY_UP));
554     }
555 
556     /** Test media browser next command */
557     @Test
testNext()558     public void testNext() throws Exception {
559         setUpConnectedState(true, true);
560         MediaControllerCompat.TransportControls transportControls =
561                 BluetoothMediaBrowserService.getTransportControls();
562 
563         // Next
564         transportControls.skipToNext();
565         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
566                 .sendPassThroughCommand(
567                         eq(mTestAddress),
568                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD),
569                         eq(KEY_DOWN));
570         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
571                 .sendPassThroughCommand(
572                         eq(mTestAddress),
573                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD),
574                         eq(KEY_UP));
575     }
576 
577     /** Test media browser previous command */
578     @Test
testPrevious()579     public void testPrevious() throws Exception {
580         setUpConnectedState(true, true);
581         MediaControllerCompat.TransportControls transportControls =
582                 BluetoothMediaBrowserService.getTransportControls();
583 
584         // Previous
585         transportControls.skipToPrevious();
586         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
587                 .sendPassThroughCommand(
588                         eq(mTestAddress),
589                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD),
590                         eq(KEY_DOWN));
591         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
592                 .sendPassThroughCommand(
593                         eq(mTestAddress),
594                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD),
595                         eq(KEY_UP));
596     }
597 
598     /** Test media browser fast forward command */
599     @Test
600     @FlakyTest
testFastForward()601     public void testFastForward() throws Exception {
602         setUpConnectedState(true, true);
603         MediaControllerCompat.TransportControls transportControls =
604                 BluetoothMediaBrowserService.getTransportControls();
605 
606         // FastForward
607         transportControls.fastForward();
608         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
609                 .sendPassThroughCommand(
610                         eq(mTestAddress),
611                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF),
612                         eq(KEY_DOWN));
613         // Finish FastForwarding
614         transportControls.play();
615         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
616                 .sendPassThroughCommand(
617                         eq(mTestAddress),
618                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF),
619                         eq(KEY_UP));
620     }
621 
622     /** Test media browser rewind command */
623     @Test
testRewind()624     public void testRewind() throws Exception {
625         setUpConnectedState(true, true);
626         MediaControllerCompat.TransportControls transportControls =
627                 BluetoothMediaBrowserService.getTransportControls();
628 
629         // Rewind
630         transportControls.rewind();
631         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
632                 .sendPassThroughCommand(
633                         eq(mTestAddress),
634                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND),
635                         eq(KEY_DOWN));
636         // Finish Rewinding
637         transportControls.play();
638         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
639                 .sendPassThroughCommand(
640                         eq(mTestAddress),
641                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND),
642                         eq(KEY_UP));
643     }
644 
645     /** Test media browser skip to queue item */
646     @Test
testSkipToQueueInvalid()647     public void testSkipToQueueInvalid() throws Exception {
648         byte scope = 1;
649         int minSize = 0;
650         int maxSize = 255;
651         setUpConnectedState(true, true);
652         MediaControllerCompat.TransportControls transportControls =
653                 BluetoothMediaBrowserService.getTransportControls();
654 
655         // Play an invalid item below start
656         transportControls.skipToQueueItem(minSize - 1);
657         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
658                 .playItem(eq(mTestAddress), eq(scope), anyLong(), anyInt());
659 
660         // Play an invalid item beyond end
661         transportControls.skipToQueueItem(maxSize + 1);
662         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
663                 .playItem(eq(mTestAddress), eq(scope), anyLong(), anyInt());
664     }
665 
666     /** Test media browser shuffle command */
667     @Test
testShuffle()668     public void testShuffle() throws Exception {
669         byte[] shuffleSetting = new byte[] {3};
670         byte[] shuffleMode = new byte[] {2};
671 
672         setUpConnectedState(true, true);
673         MediaControllerCompat.TransportControls transportControls =
674                 BluetoothMediaBrowserService.getTransportControls();
675 
676         // Shuffle
677         transportControls.setShuffleMode(1);
678         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
679                 .setPlayerApplicationSettingValues(
680                         eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode));
681     }
682 
683     /** Test media browser repeat command */
684     @Test
testRepeat()685     public void testRepeat() throws Exception {
686         byte[] repeatSetting = new byte[] {2};
687         byte[] repeatMode = new byte[] {3};
688 
689         setUpConnectedState(true, true);
690         MediaControllerCompat.TransportControls transportControls =
691                 BluetoothMediaBrowserService.getTransportControls();
692 
693         // Shuffle
694         transportControls.setRepeatMode(2);
695         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
696                 .setPlayerApplicationSettingValues(
697                         eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode));
698     }
699 
700     /**
701      * Test media browsing Verify that a browse tree is created with the proper root Verify that a
702      * player can be fetched and added to the browse tree Verify that the contents of a player are
703      * fetched upon request
704      */
705     @Test
706     @FlakyTest
testBrowsingCommands()707     public void testBrowsingCommands() {
708         setUpConnectedState(true, true);
709         final String playerName = "Player 1";
710         BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;
711 
712         // Request fetch the list of players
713         mAvrcpStateMachine.requestContents(results);
714         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
715                 .getPlayerList(eq(mTestAddress), eq(0), eq(19));
716 
717         // Provide back a player object
718         byte[] playerFeatures =
719                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
720         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, playerName, playerFeatures, 1);
721         List<AvrcpPlayer> testPlayers = new ArrayList<>();
722         testPlayers.add(playerOne);
723         mAvrcpStateMachine.sendMessage(
724                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
725         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
726 
727         // Verify that the player object is available.
728         Assert.assertEquals(true, results.isCached());
729         Assert.assertEquals(
730                 "MediaItem{mFlags=1, mDescription=" + playerName + ", null, null}",
731                 results.getChildren().get(0).getMediaItem().toString());
732 
733         // Fetch contents of that player object
734         BrowseTree.BrowseNode playerOneNode =
735                 mAvrcpStateMachine.findNode(results.getChildren().get(0).getID());
736         mAvrcpStateMachine.requestContents(playerOneNode);
737         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
738                 .setBrowsedPlayer(eq(mTestAddress), eq(1));
739         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, 5);
740         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
741                 .getFolderList(eq(mTestAddress), eq(0), eq(4));
742     }
743 
744     /**
745      * Test our reaction to an available players changed event
746      *
747      * <p>Verify that we issue a command to fetch the new available players
748      */
749     @Test
testAvailablePlayersChanged()750     public void testAvailablePlayersChanged() {
751         setUpConnectedState(true, true);
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.mBrowseTree.mRootNode.isCached());
759         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
760                 .getPlayerList(eq(mTestAddress), eq(0), eq(19));
761     }
762 
763     /**
764      * Test how we handle receiving an available players list that contains the player we know to be
765      * the addressed player
766      */
767     @Test
testAvailablePlayersReceived_AddressedPlayerExists()768     public void testAvailablePlayersReceived_AddressedPlayerExists() {
769         setUpConnectedState(true, true);
770 
771         // Set an addressed player that will be in the available players set. A new player triggers
772         // a now playing list download, so send back nothing.
773         mAvrcpStateMachine.sendMessage(
774                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
775         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
776         mAvrcpStateMachine.sendMessage(
777                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
778         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
779         clearInvocations(mAvrcpControllerService);
780         clearInvocations(mNativeInterface);
781 
782         // Send an available players have changed event
783         mAvrcpStateMachine.sendMessage(
784                 AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
785 
786         // Verify we've uncached our browse root and made the call to fetch new players
787         Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
788         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
789                 .getPlayerList(eq(mTestAddress), eq(0), eq(19));
790 
791         // Send available players set that contains our addressed player
792         byte[] playerFeatures =
793                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
794         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", playerFeatures, 1);
795         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", playerFeatures, 1);
796         List<AvrcpPlayer> testPlayers = new ArrayList<>();
797         testPlayers.add(playerOne);
798         testPlayers.add(playerTwo);
799         mAvrcpStateMachine.sendMessage(
800                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
801 
802         // Wait for them to be processed
803         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
804 
805         // Verify we processed the first players properly. Note the addressed player should always
806         // be in the available player set.
807         Assert.assertTrue(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
808         SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
809         Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
810         Assert.assertEquals(testPlayers.size(), players.size());
811         for (AvrcpPlayer player : testPlayers) {
812             Assert.assertTrue(players.contains(player.getId()));
813         }
814 
815         // Verify we request metadata, playback state and now playing list
816         assertNowPlayingList(new ArrayList<AvrcpItem>());
817         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
818                 .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
819         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
820                 .getCurrentMetadata(eq(mTestAddress));
821         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
822                 .getPlaybackState(eq(mTestAddress));
823     }
824 
825     /**
826      * Test how we handle receiving an available players list that does not contain the player we
827      * know to be the addressed player
828      */
829     @Test
testAvailablePlayersReceived_AddressedPlayerDoesNotExist()830     public void testAvailablePlayersReceived_AddressedPlayerDoesNotExist() {
831         setUpConnectedState(true, true);
832 
833         // Send an available players have changed event
834         mAvrcpStateMachine.sendMessage(
835                 AvrcpControllerStateMachine.MESSAGE_PROCESS_AVAILABLE_PLAYER_CHANGED);
836 
837         // Verify we've uncached our browse root and made the call to fetch new players
838         Assert.assertFalse(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
839         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
840                 .getPlayerList(eq(mTestAddress), eq(0), eq(19));
841 
842         // Send available players set that does not contain the addressed player
843         byte[] playerFeatures =
844                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
845         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", playerFeatures, 1);
846         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", playerFeatures, 1);
847         List<AvrcpPlayer> testPlayers = new ArrayList<>();
848         testPlayers.add(playerOne);
849         testPlayers.add(playerTwo);
850         mAvrcpStateMachine.sendMessage(
851                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
852 
853         // Wait for them to be processed
854         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
855 
856         // Verify we processed the players properly. Note the addressed player is currently the
857         // default player and is not in the available player set sent. This means we'll have an
858         // extra player at ID -1.
859         Assert.assertTrue(mAvrcpStateMachine.mBrowseTree.mRootNode.isCached());
860         SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
861         Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
862         Assert.assertEquals(testPlayers.size() + 1, players.size());
863         for (AvrcpPlayer player : testPlayers) {
864             Assert.assertTrue(players.contains(player.getId()));
865         }
866 
867         // Verify we do not request metadata, playback state and now playing list because we're
868         // sure the addressed player and metadata we have isn't impacted by the new players
869         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
870                 .getNowPlayingList(any(), anyInt(), anyInt());
871         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
872                 .getCurrentMetadata(any());
873         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0))
874                 .getPlaybackState(any());
875     }
876 
877     /**
878      * Test addressed media player changing to a player we know about Verify when the addressed
879      * media player changes browsing data updates
880      */
881     @Test
testAddressedPlayerChangedToNewKnownPlayer()882     public void testAddressedPlayerChangedToNewKnownPlayer() {
883         setUpConnectedState(true, true);
884         // Get the root of the device
885         BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;
886 
887         // Request fetch the list of players
888         mAvrcpStateMachine.requestContents(results);
889         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
890                 .getPlayerList(eq(mTestAddress), eq(0), eq(19));
891 
892         // Provide back two player objects, IDs 1 and 2
893         byte[] playerFeatures =
894                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
895         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", playerFeatures, 1);
896         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", playerFeatures, 1);
897         List<AvrcpPlayer> testPlayers = new ArrayList<>();
898         testPlayers.add(playerOne);
899         testPlayers.add(playerTwo);
900         mAvrcpStateMachine.sendMessage(
901                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
902 
903         // Set something arbitrary for the current Now Playing list
904         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
905         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
906         nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
907         setNowPlayingList(nowPlayingList);
908         clearInvocations(mAvrcpControllerService);
909         clearInvocations(mNativeInterface);
910 
911         // Change players and verify that BT attempts to update the results
912         mAvrcpStateMachine.sendMessage(
913                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 2);
914         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
915 
916         // The addressed player should always be in the available player set
917         Assert.assertEquals(2, mAvrcpStateMachine.getAddressedPlayerId());
918         SparseArray<AvrcpPlayer> players = mAvrcpStateMachine.getAvailablePlayers();
919         Assert.assertTrue(players.contains(mAvrcpStateMachine.getAddressedPlayerId()));
920 
921         // Make sure the Now Playing list is now cleared
922         assertNowPlayingList(new ArrayList<AvrcpItem>());
923 
924         // Verify that a player change to a player with Now Playing support causes a refresh.
925         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
926                 .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
927 
928         // Verify we request metadata and playback state
929         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
930                 .getCurrentMetadata(eq(mTestAddress));
931         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
932                 .getPlaybackState(eq(mTestAddress));
933     }
934 
935     /**
936      * Test addressed media player change to a player we don't know about Verify when the addressed
937      * media player changes browsing data updates Verify that the contents of a player are fetched
938      * upon request
939      */
940     @Test
testAddressedPlayerChangedToUnknownPlayer()941     public void testAddressedPlayerChangedToUnknownPlayer() {
942         setUpConnectedState(true, true);
943 
944         // Get the root of the device
945         BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.mBrowseTree.mRootNode;
946 
947         // Request fetch the list of players
948         mAvrcpStateMachine.requestContents(rootNode);
949 
950         // Provide back a player object
951         byte[] playerFeatures =
952                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
953         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", playerFeatures, 1);
954         List<AvrcpPlayer> testPlayers = new ArrayList<>();
955         testPlayers.add(playerOne);
956         mAvrcpStateMachine.sendMessage(
957                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
958 
959         // Set something arbitrary for the current Now Playing list
960         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
961         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
962         nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
963         setNowPlayingList(nowPlayingList);
964 
965         // Change players
966         mAvrcpStateMachine.sendMessage(
967                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 4);
968         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
969 
970         // Make sure the Now Playing list is now cleared and we requested metadata
971         assertNowPlayingList(new ArrayList<AvrcpItem>());
972         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
973                 .getCurrentMetadata(eq(mTestAddress));
974         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
975                 .getPlaybackState(eq(mTestAddress));
976     }
977 
978     /**
979      * Test what we do when we receive an addressed player change to a player with the same ID as
980      * the current addressed play.
981      *
982      * <p>Verify we assume nothing and re-fetch the current metadata and playback status.
983      */
984     @Test
testAddressedPlayerChangedToSamePlayerId()985     public void testAddressedPlayerChangedToSamePlayerId() {
986         setUpConnectedState(true, true);
987 
988         // Set the addressed player so we can change to the same one
989         mAvrcpStateMachine.sendMessage(
990                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
991 
992         // Wait until idle so Now Playing List is queried for, resolve it
993         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
994         mAvrcpStateMachine.sendMessage(
995                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
996         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
997 
998         // Get the root of the device
999         BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.mBrowseTree.mRootNode;
1000 
1001         // Request fetch the list of players
1002         mAvrcpStateMachine.requestContents(rootNode);
1003 
1004         // Send available players set that contains our addressed player
1005         byte[] playerFeatures =
1006                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
1007         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "Player 1", playerFeatures, 1);
1008         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "Player 2", playerFeatures, 1);
1009         List<AvrcpPlayer> testPlayers = new ArrayList<>();
1010         testPlayers.add(playerOne);
1011         testPlayers.add(playerTwo);
1012         mAvrcpStateMachine.sendMessage(
1013                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
1014 
1015         // Wait until idle so Now Playing List is queried for again, resolve it again
1016         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1017         mAvrcpStateMachine.sendMessage(
1018                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
1019         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1020         clearInvocations(mAvrcpControllerService);
1021         clearInvocations(mNativeInterface);
1022 
1023         // Send an addressed player changed to the same player ID
1024         mAvrcpStateMachine.sendMessage(
1025                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
1026         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1027 
1028         // Verify we make no assumptions about the player ID and still fetch metadata, play status
1029         // and now playing list (since player 1 supports it)
1030         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1031                 .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1032         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1033                 .getCurrentMetadata(eq(mTestAddress));
1034         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1035                 .getPlaybackState(eq(mTestAddress));
1036     }
1037 
1038     /** Test that the Now Playing playlist is updated when it changes. */
1039     @Test
testNowPlaying()1040     public void testNowPlaying() {
1041         setUpConnectedState(true, true);
1042         mAvrcpStateMachine.nowPlayingContentChanged();
1043         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1044                 .getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1045     }
1046 
1047     /** Test that AVRCP events such as playback commands can execute while performing browsing. */
1048     @Test
testPlayWhileBrowsing()1049     public void testPlayWhileBrowsing() {
1050         setUpConnectedState(true, true);
1051 
1052         // Get the root of the device
1053         BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;
1054 
1055         // Request fetch the list of players
1056         mAvrcpStateMachine.requestContents(results);
1057 
1058         MediaControllerCompat.TransportControls transportControls =
1059                 BluetoothMediaBrowserService.getTransportControls();
1060         transportControls.play();
1061         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1062                 .sendPassThroughCommand(
1063                         eq(mTestAddress),
1064                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1065                         eq(KEY_DOWN));
1066         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1067                 .sendPassThroughCommand(
1068                         eq(mTestAddress),
1069                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1070                         eq(KEY_UP));
1071     }
1072 
1073     /** Test that Absolute Volume Registration is working */
1074     @Test
testRegisterAbsVolumeNotification()1075     public void testRegisterAbsVolumeNotification() {
1076         byte label = 42;
1077         setUpConnectedState(true, true);
1078         mAvrcpStateMachine.sendMessage(
1079                 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION, label);
1080         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1081                 .sendRegisterAbsVolRsp(any(), anyByte(), eq(127), eq((int) label));
1082     }
1083 
1084     /** Test that set absolute volume is working */
1085     @Test
testSetAbsoluteVolume_volumeIsFixed_setsAbsVolumeBase()1086     public void testSetAbsoluteVolume_volumeIsFixed_setsAbsVolumeBase() {
1087         byte label = 42;
1088         setUpConnectedState(true, true);
1089         mAvrcpStateMachine.sendMessage(
1090                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, 20, label);
1091         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1092                 .sendAbsVolRsp(any(), eq(127), eq((int) label));
1093     }
1094 
1095     /** Test that set absolute volume is working */
1096     @Test
testSetAbsoluteVolume_volumeIsNotFixed_setsAbsVolumeBase()1097     public void testSetAbsoluteVolume_volumeIsNotFixed_setsAbsVolumeBase() {
1098         doReturn(false).when(mAudioManager).isVolumeFixed();
1099         mAvrcpStateMachine =
1100                 new AvrcpControllerStateMachine(
1101                         mTestDevice, mAvrcpControllerService, mNativeInterface, false);
1102         mAvrcpStateMachine.start();
1103         byte label = 42;
1104         setUpConnectedState(true, true);
1105         doReturn(100).when(mAudioManager).getStreamMaxVolume(eq(AudioManager.STREAM_MUSIC));
1106         doReturn(25).when(mAudioManager).getStreamVolume(eq(AudioManager.STREAM_MUSIC));
1107         mAvrcpStateMachine.sendMessage(
1108                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, 20, label);
1109         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1110                 .sendAbsVolRsp(any(), eq(20), eq((int) label));
1111         verify(mAudioManager, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1112                 .setStreamVolume(
1113                         eq(AudioManager.STREAM_MUSIC), eq(15), eq(AudioManager.FLAG_SHOW_UI));
1114     }
1115 
1116     /** Test that set absolute volume is working */
1117     @Test
1118     public void
testSetAbsoluteVolume_volumeIsNotFixedSinkAbsoluteVolumeEnabled_setsAbsVolumeBase()1119             testSetAbsoluteVolume_volumeIsNotFixedSinkAbsoluteVolumeEnabled_setsAbsVolumeBase() {
1120         doReturn(false).when(mAudioManager).isVolumeFixed();
1121         mAvrcpStateMachine =
1122                 new AvrcpControllerStateMachine(
1123                         mTestDevice, mAvrcpControllerService, mNativeInterface, true);
1124         mAvrcpStateMachine.start();
1125         byte label = 42;
1126         setUpConnectedState(true, true);
1127         mAvrcpStateMachine.sendMessage(
1128                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_ABS_VOL_CMD, 20, label);
1129         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS))
1130                 .sendAbsVolRsp(any(), eq(127), eq((int) label));
1131     }
1132 
1133     /** Test playback does not request focus when another app is playing music. */
1134     @Test
testPlaybackWhileMusicPlaying()1135     public void testPlaybackWhileMusicPlaying() {
1136         when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
1137                 .thenReturn(false);
1138         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_NONE);
1139         doReturn(true).when(mAudioManager).isMusicActive();
1140         setUpConnectedState(true, true);
1141         mAvrcpStateMachine.sendMessage(
1142                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1143                 PlaybackStateCompat.STATE_PLAYING);
1144         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1145         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1146                 .sendPassThroughCommand(
1147                         eq(mTestAddress),
1148                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1149                         eq(KEY_DOWN));
1150         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1151     }
1152 
1153     /** Test playback requests focus while nothing is playing music. */
1154     @Test
testPlaybackWhileIdle()1155     public void testPlaybackWhileIdle() {
1156         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_NONE);
1157         doReturn(false).when(mAudioManager).isMusicActive();
1158         setUpConnectedState(true, true);
1159         mAvrcpStateMachine.sendMessage(
1160                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1161                 PlaybackStateCompat.STATE_PLAYING);
1162         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1163         verify(mA2dpSinkService).requestAudioFocus(mTestDevice, true);
1164     }
1165 
1166     /**
1167      * Test receiving a playback status of playing while we're in an error state as it relates to
1168      * getting audio focus.
1169      *
1170      * <p>Verify we send a pause command and never attempt to request audio focus
1171      */
1172     @Test
testPlaybackWhileErrorState()1173     public void testPlaybackWhileErrorState() {
1174         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.ERROR);
1175         setUpConnectedState(true, true);
1176         mAvrcpStateMachine.sendMessage(
1177                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1178                 PlaybackStateCompat.STATE_PLAYING);
1179         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1180         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1181                 .sendPassThroughCommand(
1182                         eq(mTestAddress),
1183                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1184                         eq(KEY_DOWN));
1185         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1186     }
1187 
1188     /**
1189      * Test receiving a playback status of playing while we have focus
1190      *
1191      * <p>Verify we do not send a pause command and never attempt to request audio focus
1192      */
1193     @Test
testPlaybackWhilePlayingState()1194     public void testPlaybackWhilePlayingState() {
1195         when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_GAIN);
1196         setUpConnectedState(true, true);
1197         Assert.assertTrue(mAvrcpStateMachine.isActive());
1198         mAvrcpStateMachine.sendMessage(
1199                 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED,
1200                 PlaybackStateCompat.STATE_PLAYING);
1201         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1202         verify(mNativeInterface, never())
1203                 .sendPassThroughCommand(
1204                         eq(mTestAddress),
1205                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1206                         eq(KEY_DOWN));
1207         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1208     }
1209 
1210     /** Test that isActive() reports the proper value when we're active */
1211     @Test
testIsActive_deviceActive()1212     public void testIsActive_deviceActive() {
1213         Assert.assertTrue(mAvrcpStateMachine.isActive());
1214     }
1215 
1216     /** Test that isActive() reports the proper value when we're inactive */
1217     @Test
testIsActive_deviceInactive()1218     public void testIsActive_deviceInactive() {
1219         setActiveDevice(null);
1220         Assert.assertFalse(mAvrcpStateMachine.isActive());
1221     }
1222 
1223     /** Test becoming active from the inactive state */
1224     @Test
testBecomeActive()1225     public void testBecomeActive() {
1226         // Note device starts as active in setUp() and state cascades come the CONNECTED state
1227         setUpConnectedState(true, true);
1228         Assert.assertTrue(mAvrcpStateMachine.isActive());
1229 
1230         // Make the device inactive
1231         setActiveDevice(null);
1232         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1233         Assert.assertFalse(mAvrcpStateMachine.isActive());
1234 
1235         // Change device state while inactive
1236         AvrcpItem track = makeTrack("title", "artist", "album", 1, 10, "none", 10, null);
1237         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1238         AvrcpItem queueItem1 = makeNowPlayingItem(0, "title");
1239         AvrcpItem queueItem2 = makeNowPlayingItem(1, "title 2");
1240         nowPlayingList.add(queueItem1);
1241         nowPlayingList.add(queueItem2);
1242         setCurrentTrack(track);
1243         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1244         setPlaybackPosition(7, 10);
1245         setNowPlayingList(nowPlayingList);
1246 
1247         // Make device active
1248         setActiveDevice(mTestDevice);
1249         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1250         Assert.assertTrue(mAvrcpStateMachine.isActive());
1251 
1252         // See that state from BluetoothMediaBrowserService is updated
1253         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1254         Assert.assertNotNull(session);
1255         MediaControllerCompat controller = session.getController();
1256         Assert.assertNotNull(controller);
1257 
1258         MediaMetadataCompat metadata = controller.getMetadata();
1259         Assert.assertNotNull(metadata);
1260         Assert.assertEquals("title", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
1261         Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
1262         Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
1263         Assert.assertEquals(1, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
1264         Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
1265         Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
1266         Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
1267 
1268         PlaybackStateCompat playbackState = controller.getPlaybackState();
1269         Assert.assertNotNull(playbackState);
1270         Assert.assertEquals(PlaybackStateCompat.STATE_PAUSED, playbackState.getState());
1271         Assert.assertEquals(7, playbackState.getPosition());
1272 
1273         List<MediaSessionCompat.QueueItem> queue = controller.getQueue();
1274         Assert.assertNotNull(queue);
1275         Assert.assertEquals(2, queue.size());
1276         Assert.assertEquals("title", queue.get(0).getDescription().getTitle().toString());
1277         Assert.assertEquals("title 2", queue.get(1).getDescription().getTitle().toString());
1278     }
1279 
1280     /** Test becoming inactive from the active state */
1281     @Test
testBecomeInactive()1282     public void testBecomeInactive() {
1283         // Note device starts as active in setUp()
1284         setUpConnectedState(true, true);
1285         Assert.assertTrue(mAvrcpStateMachine.isActive());
1286 
1287         // Set the active device to something else, verify we're inactive and send a pause upon
1288         // becoming inactive
1289         setActiveDevice(null);
1290         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1291         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1292                 .sendPassThroughCommand(
1293                         eq(mTestAddress),
1294                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1295                         eq(KEY_DOWN));
1296         Assert.assertFalse(mAvrcpStateMachine.isActive());
1297     }
1298 
1299     @Test
testTrackChangedWhileActive_currentTrackAndQueueNumberUpdated()1300     public void testTrackChangedWhileActive_currentTrackAndQueueNumberUpdated() {
1301         setUpConnectedState(true, true);
1302 
1303         // Set track
1304         AvrcpItem track = makeTrack("Song 1", "artist", "album", 1, 2, "none", 10, null);
1305         setCurrentTrack(track);
1306 
1307         // Set current Now Playing list
1308         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1309         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
1310         nowPlayingList.add(makeNowPlayingItem(2, "Song 2"));
1311         setNowPlayingList(nowPlayingList);
1312 
1313         // Set playing
1314         setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
1315 
1316         // Wait
1317         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1318 
1319         // Verify track and playback state
1320         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1321         Assert.assertNotNull(session);
1322         MediaControllerCompat controller = session.getController();
1323         Assert.assertNotNull(controller);
1324 
1325         MediaMetadataCompat metadata = controller.getMetadata();
1326         Assert.assertNotNull(metadata);
1327         Assert.assertEquals("Song 1", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
1328         Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
1329         Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
1330         Assert.assertEquals(1, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
1331         Assert.assertEquals(2, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
1332         Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
1333         Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
1334 
1335         PlaybackStateCompat playbackState = controller.getPlaybackState();
1336         Assert.assertNotNull(playbackState);
1337         Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState());
1338         Assert.assertEquals(0, playbackState.getActiveQueueItemId());
1339 
1340         // Track changes, with new metadata and new track number
1341         track = makeTrack("Song 2", "artist", "album", 2, 2, "none", 10, null);
1342         setCurrentTrack(track);
1343         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1344 
1345         // Assert new track metadata and active queue item
1346         metadata = controller.getMetadata();
1347         Assert.assertNotNull(metadata);
1348         Assert.assertEquals("Song 2", metadata.getString(MediaMetadataCompat.METADATA_KEY_TITLE));
1349         Assert.assertEquals("artist", metadata.getString(MediaMetadataCompat.METADATA_KEY_ARTIST));
1350         Assert.assertEquals("album", metadata.getString(MediaMetadataCompat.METADATA_KEY_ALBUM));
1351         Assert.assertEquals(2, metadata.getLong(MediaMetadataCompat.METADATA_KEY_TRACK_NUMBER));
1352         Assert.assertEquals(2, metadata.getLong(MediaMetadataCompat.METADATA_KEY_NUM_TRACKS));
1353         Assert.assertEquals("none", metadata.getString(MediaMetadataCompat.METADATA_KEY_GENRE));
1354         Assert.assertEquals(10, metadata.getLong(MediaMetadataCompat.METADATA_KEY_DURATION));
1355 
1356         playbackState = controller.getPlaybackState();
1357         Assert.assertNotNull(playbackState);
1358         Assert.assertEquals(PlaybackStateCompat.STATE_PLAYING, playbackState.getState());
1359         Assert.assertEquals(1, playbackState.getActiveQueueItemId());
1360     }
1361 
1362     /** Test receiving a track change update when we're not the active device */
1363     @Test
testTrackChangeWhileNotActiveDevice()1364     public void testTrackChangeWhileNotActiveDevice() {
1365         setUpConnectedState(true, true);
1366 
1367         // Set the active device to something else, verify we're inactive and send a pause upon
1368         // becoming inactive
1369         setActiveDevice(null);
1370         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1371         Assert.assertFalse(mAvrcpStateMachine.isActive());
1372 
1373         // Change track while inactive
1374         AvrcpItem track = makeTrack("title", "artist", "album", 1, 10, "none", 10, null);
1375         setCurrentTrack(track);
1376 
1377         // Since we're not active, verify BluetoothMediaBrowserService does not have these values
1378         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1379         Assert.assertNotNull(session);
1380         MediaControllerCompat controller = session.getController();
1381         Assert.assertNotNull(controller);
1382 
1383         MediaMetadataCompat metadata = controller.getMetadata();
1384         Assert.assertNull(metadata); // track starts as null and shouldn't change
1385     }
1386 
1387     /** Test receiving a playback status of playing when we're not the active device */
1388     @Test
testPlaybackWhileNotActiveDevice()1389     public void testPlaybackWhileNotActiveDevice() {
1390         setUpConnectedState(true, true);
1391 
1392         // Set the active device to something else, verify we're inactive
1393         setActiveDevice(null);
1394         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1395         Assert.assertFalse(mAvrcpStateMachine.isActive());
1396         clearInvocations(mAvrcpControllerService);
1397         clearInvocations(mNativeInterface);
1398 
1399         // Now that we're inactive, receive a playback status of playing
1400         setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
1401 
1402         // Verify we send a pause, never request audio focus, and the playback state on
1403         // BluetoothMediaBrowserService never updates.
1404         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1405                 .sendPassThroughCommand(
1406                         eq(mTestAddress),
1407                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1408                         eq(KEY_DOWN));
1409         verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true);
1410         Assert.assertEquals(
1411                 PlaybackStateCompat.STATE_ERROR,
1412                 BluetoothMediaBrowserService.getPlaybackState().getState());
1413     }
1414 
1415     /** Test receiving a play position update when we're not the active device */
1416     @Test
testPlayPositionChangeWhileNotActiveDevice()1417     public void testPlayPositionChangeWhileNotActiveDevice() {
1418         setUpConnectedState(true, true);
1419 
1420         // Set the active device to something else, verify we're inactive
1421         setActiveDevice(null);
1422         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1423         Assert.assertFalse(mAvrcpStateMachine.isActive());
1424         clearInvocations(mAvrcpControllerService);
1425         clearInvocations(mNativeInterface);
1426 
1427         // Now that we're inactive, receive a play position change
1428         setPlaybackPosition(1, 10);
1429 
1430         // Since we're not active, verify BluetoothMediaBrowserService does not have these values
1431         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1432         Assert.assertNotNull(session);
1433         MediaControllerCompat controller = session.getController();
1434         Assert.assertNotNull(controller);
1435 
1436         PlaybackStateCompat playbackState = controller.getPlaybackState();
1437         Assert.assertNotNull(playbackState);
1438         Assert.assertEquals(0, playbackState.getPosition());
1439     }
1440 
1441     /** Test receiving a now playing list update when we're not the active device */
1442     @Test
testNowPlayingListChangeWhileNotActiveDevice()1443     public void testNowPlayingListChangeWhileNotActiveDevice() {
1444         setUpConnectedState(true, true);
1445 
1446         // Set the active device to something else, verify we're inactive and send a pause upon
1447         // becoming inactive
1448         setActiveDevice(null);
1449         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1450         Assert.assertFalse(mAvrcpStateMachine.isActive());
1451 
1452         // Change queue while inactive
1453         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1454         AvrcpItem queueItem1 = makeNowPlayingItem(0, "title");
1455         AvrcpItem queueItem2 = makeNowPlayingItem(1, "title 2");
1456         AvrcpItem queueItem3 = makeNowPlayingItem(1, "title 3");
1457         nowPlayingList.add(queueItem1);
1458         nowPlayingList.add(queueItem2);
1459         nowPlayingList.add(queueItem3);
1460         setNowPlayingList(nowPlayingList);
1461 
1462         // Since we're not active, verify BluetoothMediaBrowserService does not have these values
1463         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
1464         Assert.assertNotNull(session);
1465         MediaControllerCompat controller = session.getController();
1466         Assert.assertNotNull(controller);
1467 
1468         List<MediaSessionCompat.QueueItem> queue = controller.getQueue();
1469         Assert.assertNull(queue);
1470     }
1471 
1472     /**
1473      * Test receiving an audio focus changed event when we are not recovering from a transient loss.
1474      * This should result in no play command being sent.
1475      */
1476     @Test
testOnAudioFocusGain_playNotSent()1477     public void testOnAudioFocusGain_playNotSent() {
1478         setUpConnectedState(true, true);
1479         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1480         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1481         verify(mNativeInterface, never())
1482                 .sendPassThroughCommand(
1483                         eq(mTestAddress),
1484                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1485                         eq(KEY_DOWN));
1486         verify(mNativeInterface, never())
1487                 .sendPassThroughCommand(
1488                         eq(mTestAddress),
1489                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1490                         eq(KEY_UP));
1491     }
1492 
1493     /**
1494      * Test receiving a transient loss audio focus changed event while playing. A pause should be
1495      * sent
1496      */
1497     @Test
testOnAudioFocusTransientLossWhilePlaying_pauseSent()1498     public void testOnAudioFocusTransientLossWhilePlaying_pauseSent() {
1499         when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus))
1500                 .thenReturn(false);
1501         setUpConnectedState(true, true);
1502         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1503         setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
1504         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
1505 
1506         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1507         verify(mNativeInterface)
1508                 .sendPassThroughCommand(
1509                         eq(mTestAddress),
1510                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1511                         eq(KEY_DOWN));
1512         verify(mNativeInterface)
1513                 .sendPassThroughCommand(
1514                         eq(mTestAddress),
1515                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1516                         eq(KEY_UP));
1517     }
1518 
1519     /**
1520      * Test receiving a transient loss audio focus changed event while paused. No pause should be
1521      * sent
1522      */
1523     @Test
testOnAudioFocusTransientLossWhilePaused_pauseNotSent()1524     public void testOnAudioFocusTransientLossWhilePaused_pauseNotSent() {
1525         setUpConnectedState(true, true);
1526         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1527         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1528         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS_TRANSIENT);
1529 
1530         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1531         verify(mNativeInterface, never())
1532                 .sendPassThroughCommand(
1533                         eq(mTestAddress),
1534                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1535                         eq(KEY_DOWN));
1536         verify(mNativeInterface, never())
1537                 .sendPassThroughCommand(
1538                         eq(mTestAddress),
1539                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1540                         eq(KEY_UP));
1541     }
1542 
1543     /** Test receiving an audio focus loss event. A pause should be sent if we were playing */
1544     @Test
testOnAudioFocusLossWhilePlaying_pauseSent()1545     public void testOnAudioFocusLossWhilePlaying_pauseSent() {
1546         setUpConnectedState(true, true);
1547         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1548         setPlaybackState(PlaybackStateCompat.STATE_PLAYING);
1549         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS);
1550 
1551         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1552         verify(mNativeInterface)
1553                 .sendPassThroughCommand(
1554                         eq(mTestAddress),
1555                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1556                         eq(KEY_DOWN));
1557         verify(mNativeInterface)
1558                 .sendPassThroughCommand(
1559                         eq(mTestAddress),
1560                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1561                         eq(KEY_UP));
1562     }
1563 
1564     /** Test receiving an audio focus loss event. A pause should not be sent if we were paused */
1565     @Test
testOnAudioFocusLossWhilePause_pauseNotSent()1566     public void testOnAudioFocusLossWhilePause_pauseNotSent() {
1567         setUpConnectedState(true, true);
1568         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1569         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1570         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_LOSS);
1571 
1572         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1573         verify(mNativeInterface, times(0))
1574                 .sendPassThroughCommand(
1575                         eq(mTestAddress),
1576                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1577                         eq(KEY_DOWN));
1578         verify(mNativeInterface, times(0))
1579                 .sendPassThroughCommand(
1580                         eq(mTestAddress),
1581                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1582                         eq(KEY_UP));
1583     }
1584 
1585     /**
1586      * Test receiving an audio focus gained event following a transient loss where we sent a pause
1587      * and no event happened in between that should cause us to remain paused.
1588      */
1589     @Test
testOnAudioFocusGainFromTransientLossWhilePlaying_playSent()1590     public void testOnAudioFocusGainFromTransientLossWhilePlaying_playSent() {
1591         testOnAudioFocusTransientLossWhilePlaying_pauseSent();
1592         clearInvocations(mAvrcpControllerService);
1593         clearInvocations(mNativeInterface);
1594         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1595         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1596 
1597         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1598         verify(mNativeInterface)
1599                 .sendPassThroughCommand(
1600                         eq(mTestAddress),
1601                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1602                         eq(KEY_DOWN));
1603         verify(mNativeInterface)
1604                 .sendPassThroughCommand(
1605                         eq(mTestAddress),
1606                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1607                         eq(KEY_UP));
1608     }
1609 
1610     /** Test receiving an audio focus changed event following a transient loss where */
1611     @Test
testOnAudioFocusGainFromTransientLossWhilePlayingWithPause_playNotSent()1612     public void testOnAudioFocusGainFromTransientLossWhilePlayingWithPause_playNotSent() {
1613         testOnAudioFocusTransientLossWhilePlaying_pauseSent();
1614         clearInvocations(mAvrcpControllerService);
1615         clearInvocations(mNativeInterface);
1616         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1617         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1618 
1619         MediaControllerCompat.TransportControls transportControls =
1620                 BluetoothMediaBrowserService.getTransportControls();
1621         transportControls.pause();
1622         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1623                 .sendPassThroughCommand(
1624                         eq(mTestAddress),
1625                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1626                         eq(KEY_DOWN));
1627         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1628                 .sendPassThroughCommand(
1629                         eq(mTestAddress),
1630                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE),
1631                         eq(KEY_UP));
1632 
1633         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1634         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1635 
1636         verify(mNativeInterface, never())
1637                 .sendPassThroughCommand(
1638                         eq(mTestAddress),
1639                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1640                         eq(KEY_DOWN));
1641         verify(mNativeInterface, never())
1642                 .sendPassThroughCommand(
1643                         eq(mTestAddress),
1644                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1645                         eq(KEY_UP));
1646     }
1647 
1648     /**
1649      * Test receiving an audio focus gain event coming out of a transient loss where a stop command
1650      * has been sent
1651      */
1652     @Test
testOnAudioFocusGainFromTransientLossWithStop_playNotSent()1653     public void testOnAudioFocusGainFromTransientLossWithStop_playNotSent() {
1654         testOnAudioFocusTransientLossWhilePlaying_pauseSent();
1655         clearInvocations(mAvrcpControllerService);
1656         clearInvocations(mNativeInterface);
1657         setPlaybackState(PlaybackStateCompat.STATE_PAUSED);
1658         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1659 
1660         MediaControllerCompat.TransportControls transportControls =
1661                 BluetoothMediaBrowserService.getTransportControls();
1662         transportControls.stop();
1663         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1664                 .sendPassThroughCommand(
1665                         eq(mTestAddress),
1666                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
1667                         eq(KEY_DOWN));
1668         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
1669                 .sendPassThroughCommand(
1670                         eq(mTestAddress),
1671                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP),
1672                         eq(KEY_UP));
1673 
1674         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1675         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1676 
1677         verify(mNativeInterface, never())
1678                 .sendPassThroughCommand(
1679                         eq(mTestAddress),
1680                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1681                         eq(KEY_DOWN));
1682         verify(mNativeInterface, never())
1683                 .sendPassThroughCommand(
1684                         eq(mTestAddress),
1685                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1686                         eq(KEY_UP));
1687     }
1688 
1689     /**
1690      * Test receiving an audio focus gain coming out of a transient loss where we were paused going
1691      * into the transient loss. No play should be sent because not play state needs to be recovered
1692      */
1693     @Test
testOnAudioFocusGainFromTransientLossWhilePaused_playNotSent()1694     public void testOnAudioFocusGainFromTransientLossWhilePaused_playNotSent() {
1695         testOnAudioFocusTransientLossWhilePaused_pauseNotSent();
1696         clearInvocations(mAvrcpControllerService);
1697         clearInvocations(mNativeInterface);
1698         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1699 
1700         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1701         verify(mNativeInterface, never())
1702                 .sendPassThroughCommand(
1703                         eq(mTestAddress),
1704                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1705                         eq(KEY_DOWN));
1706         verify(mNativeInterface, never())
1707                 .sendPassThroughCommand(
1708                         eq(mTestAddress),
1709                         eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY),
1710                         eq(KEY_UP));
1711     }
1712 
1713     /**
1714      * Test receiving a now playing content changed event while downloading now playing content and
1715      * make sure our final now playing content downloaded matches what's expected
1716      */
1717     @Test
testNowPlayingListChangedWhileFetchingNowPlayingList_fetchAbortedAndRestarted()1718     public void testNowPlayingListChangedWhileFetchingNowPlayingList_fetchAbortedAndRestarted() {
1719         setUpConnectedState(true, true);
1720         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1721 
1722         // Fill the list with songs 1 -> 25, more than download step size
1723         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1724         for (int i = 1; i <= 25; i++) {
1725             String title = "Song " + Integer.toString(i);
1726             nowPlayingList.add(makeNowPlayingItem(i, title));
1727         }
1728 
1729         // Fill the list with songs 26 -> 50
1730         List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
1731         for (int i = 26; i <= 50; i++) {
1732             String title = "Song " + Integer.toString(i);
1733             updatedNowPlayingList.add(makeNowPlayingItem(i, title));
1734         }
1735 
1736         // Hand hold the download events
1737         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1738         mAvrcpStateMachine.requestContents(nowPlaying);
1739         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1740 
1741         // Verify download attempt and send some elements over, verify next set is requested
1742         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1743         mAvrcpStateMachine.sendMessage(
1744                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1745                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
1746         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1747         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(20), eq(39));
1748 
1749         // Force a now playing content invalidation and verify attempted download
1750         mAvrcpStateMachine.nowPlayingContentChanged();
1751         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1752 
1753         // Send requested items, they're likely from the new list at this point, but it shouldn't
1754         // matter what they are because we should toss them out and restart our download next.
1755         mAvrcpStateMachine.sendMessage(
1756                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1757                 new ArrayList<AvrcpItem>(nowPlayingList.subList(20, 25)));
1758         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1759 
1760         verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1761 
1762         mAvrcpStateMachine.sendMessage(
1763                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1764                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
1765         mAvrcpStateMachine.sendMessage(
1766                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1767                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
1768         mAvrcpStateMachine.sendMessage(
1769                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
1770 
1771         // Wait for the now playing list to be propagated
1772         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1773 
1774         // Make sure its set by re grabbing the node and checking its contents are cached
1775         nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1776         Assert.assertTrue(nowPlaying.isCached());
1777         assertNowPlayingList(updatedNowPlayingList);
1778     }
1779 
1780     /**
1781      * Test receiving a now playing content changed event right after we queued a fetch of some now
1782      * playing items. Make sure our final now playing content downloaded matches what's expected
1783      */
1784     @Test
testNowPlayingListChangedQueuedFetchingNowPlayingList_fetchAbortedAndRestarted()1785     public void testNowPlayingListChangedQueuedFetchingNowPlayingList_fetchAbortedAndRestarted() {
1786         setUpConnectedState(true, true);
1787         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1788 
1789         // Fill the list with songs 1 -> 25, more than download step size
1790         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1791         for (int i = 1; i <= 25; i++) {
1792             String title = "Song " + Integer.toString(i);
1793             nowPlayingList.add(makeNowPlayingItem(i, title));
1794         }
1795 
1796         // Fill the list with songs 26 -> 50
1797         List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
1798         for (int i = 26; i <= 50; i++) {
1799             String title = "Song " + Integer.toString(i);
1800             updatedNowPlayingList.add(makeNowPlayingItem(i, title));
1801         }
1802 
1803         // Hand hold the download events
1804         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1805         mAvrcpStateMachine.requestContents(nowPlaying);
1806         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1807 
1808         // Verify download attempt and send some elements over, verify next set is requested
1809         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1810         mAvrcpStateMachine.nowPlayingContentChanged();
1811 
1812         mAvrcpStateMachine.sendMessage(
1813                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1814                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
1815         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1816 
1817         // Receiving the previous members should cause our fetch process to realize we're aborted
1818         // and a new (second) request should be triggered for the list from the beginning
1819         verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1820 
1821         // Send whole list
1822         mAvrcpStateMachine.sendMessage(
1823                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1824                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
1825         mAvrcpStateMachine.sendMessage(
1826                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1827                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
1828         mAvrcpStateMachine.sendMessage(
1829                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
1830 
1831         // Wait for the now playing list to be propagated
1832         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1833 
1834         // Make sure its set by re grabbing the node and checking its contents are cached
1835         nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1836         Assert.assertTrue(nowPlaying.isCached());
1837         assertNowPlayingList(updatedNowPlayingList);
1838     }
1839 
1840     /**
1841      * Test receiving an addressed player changed event while downloading now playing content and
1842      * make sure our final now playing content downloaded matches what's expected.
1843      */
1844     @Test
testAddressedPlayerChangedWhileFetchingNowPlayingList_fetchAbortedAndRestarted()1845     public void testAddressedPlayerChangedWhileFetchingNowPlayingList_fetchAbortedAndRestarted() {
1846         setUpConnectedState(true, true);
1847         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1848 
1849         // Fill the list with songs 1 -> 25, more than download step size
1850         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1851         for (int i = 1; i <= 25; i++) {
1852             String title = "Song " + Integer.toString(i);
1853             nowPlayingList.add(makeNowPlayingItem(i, title));
1854         }
1855 
1856         // Fill the list with songs 26 -> 50
1857         List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
1858         for (int i = 26; i <= 50; i++) {
1859             String title = "Song " + Integer.toString(i);
1860             updatedNowPlayingList.add(makeNowPlayingItem(i, title));
1861         }
1862 
1863         // Hand hold the download events
1864         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1865         mAvrcpStateMachine.requestContents(nowPlaying);
1866         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1867 
1868         // Verify download attempt and send some elements over, verify next set is requested
1869         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1870         mAvrcpStateMachine.sendMessage(
1871                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1872                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
1873         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1874         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(20), eq(39));
1875 
1876         // Force a now playing content invalidation due to addressed player change
1877         mAvrcpStateMachine.sendMessage(
1878                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
1879         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1880 
1881         // Send requested items, they're likely from the new list at this point, but it shouldn't
1882         // matter what they are because we should toss them out and restart our download next.
1883         mAvrcpStateMachine.sendMessage(
1884                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1885                 new ArrayList<AvrcpItem>(nowPlayingList.subList(20, 25)));
1886         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1887 
1888         verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1889 
1890         mAvrcpStateMachine.sendMessage(
1891                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1892                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
1893         mAvrcpStateMachine.sendMessage(
1894                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1895                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
1896         mAvrcpStateMachine.sendMessage(
1897                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
1898 
1899         // Wait for the now playing list to be propagated
1900         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1901 
1902         // Make sure its set by re grabbing the node and checking its contents are cached
1903         nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1904         Assert.assertTrue(nowPlaying.isCached());
1905         assertNowPlayingList(updatedNowPlayingList);
1906     }
1907 
1908     /**
1909      * Test receiving an addressed player changed event while downloading now playing content and
1910      * make sure our final now playing content downloaded matches what's expected.
1911      */
1912     @Test
testAddressedPlayerChangedQueuedFetchingNowPlayingList_fetchAbortedAndRestarted()1913     public void testAddressedPlayerChangedQueuedFetchingNowPlayingList_fetchAbortedAndRestarted() {
1914         setUpConnectedState(true, true);
1915         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1916 
1917         // Fill the list with songs 1 -> 25, more than download step size
1918         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1919         for (int i = 1; i <= 25; i++) {
1920             String title = "Song " + Integer.toString(i);
1921             nowPlayingList.add(makeNowPlayingItem(i, title));
1922         }
1923 
1924         // Fill the list with songs 26 -> 50
1925         List<AvrcpItem> updatedNowPlayingList = new ArrayList<AvrcpItem>();
1926         for (int i = 26; i <= 50; i++) {
1927             String title = "Song " + Integer.toString(i);
1928             updatedNowPlayingList.add(makeNowPlayingItem(i, title));
1929         }
1930 
1931         // Hand hold the download events
1932         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1933         mAvrcpStateMachine.requestContents(nowPlaying);
1934         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1935 
1936         // Verify download attempt and send some elements over, verify next set is requested
1937         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1938 
1939         // Force a now playing content invalidation due to addressed player change, happening
1940         // before we've received any items from the remote device.
1941         mAvrcpStateMachine.sendMessage(
1942                 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 1);
1943         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1944 
1945         // Now, send the items in and let it process
1946         mAvrcpStateMachine.sendMessage(
1947                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1948                 new ArrayList<AvrcpItem>(nowPlayingList.subList(0, 20)));
1949         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
1950 
1951         verify(mNativeInterface, times(2)).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1952 
1953         // Send requested items, they're likely from the new list at this point, but it shouldn't
1954         // matter what they are because we should toss them out and restart our download next.
1955         mAvrcpStateMachine.sendMessage(
1956                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1957                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(0, 20)));
1958         mAvrcpStateMachine.sendMessage(
1959                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS,
1960                 new ArrayList<AvrcpItem>(updatedNowPlayingList.subList(20, 25)));
1961         mAvrcpStateMachine.sendMessage(
1962                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
1963 
1964         // Wait for the now playing list to be propagated
1965         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1966 
1967         // Make sure its set by re grabbing the node and checking its contents are cached
1968         nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1969         Assert.assertTrue(nowPlaying.isCached());
1970         assertNowPlayingList(updatedNowPlayingList);
1971     }
1972 
1973     /**
1974      * Test making a browse request where results don't come back within the timeout window. The
1975      * node should still be notified on.
1976      */
1977     @Test
testMakeBrowseRequestWithTimeout_contentsCachedAndNotified()1978     public void testMakeBrowseRequestWithTimeout_contentsCachedAndNotified() {
1979         setUpConnectedState(true, true);
1980         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
1981 
1982         // Set something arbitrary for the current Now Playing list
1983         List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>();
1984         nowPlayingList.add(makeNowPlayingItem(1, "Song 1"));
1985         setNowPlayingList(nowPlayingList);
1986         clearInvocations(mAvrcpControllerService);
1987         clearInvocations(mNativeInterface);
1988 
1989         // Invalidate the contents by doing a new fetch
1990         BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING");
1991         mAvrcpStateMachine.requestContents(nowPlaying);
1992         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
1993 
1994         // Request for new contents should be sent
1995         verify(mNativeInterface).getNowPlayingList(eq(mTestAddress), eq(0), eq(19));
1996         Assert.assertFalse(nowPlaying.isCached());
1997 
1998         // Send timeout on our own instead of waiting 10 seconds
1999         mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_INTERNAL_CMD_TIMEOUT);
2000         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
2001 
2002         // Node should be set to cached and notified on
2003         assertNowPlayingList(new ArrayList<AvrcpItem>());
2004         Assert.assertTrue(nowPlaying.isCached());
2005 
2006         // See that state from BluetoothMediaBrowserService is updated to null (i.e. empty)
2007         MediaSessionCompat session = BluetoothMediaBrowserService.getSession();
2008         Assert.assertNotNull(session);
2009         MediaControllerCompat controller = session.getController();
2010         Assert.assertNotNull(controller);
2011         List<MediaSessionCompat.QueueItem> queue = controller.getQueue();
2012         Assert.assertNull(queue);
2013     }
2014 
2015     /**
2016      * Test making a browse request with a null node. The request should not generate any native
2017      * layer browse requests.
2018      */
2019     @Test
testNullBrowseRequest_requestDropped()2020     public void testNullBrowseRequest_requestDropped() {
2021         setUpConnectedState(true, true);
2022         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
2023         clearInvocations(mAvrcpControllerService);
2024         clearInvocations(mNativeInterface);
2025         mAvrcpStateMachine.requestContents(null);
2026         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
2027         verifyNoMoreInteractions(mAvrcpControllerService);
2028         verifyNoMoreInteractions(mNativeInterface);
2029     }
2030 
2031     /**
2032      * Test making a browse request with browsing disconnected. The request should not generate any
2033      * native layer browse requests.
2034      */
2035     @Test
testBrowseRequestWhileDisconnected_requestDropped()2036     public void testBrowseRequestWhileDisconnected_requestDropped() {
2037         setUpConnectedState(true, false);
2038         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
2039         clearInvocations(mAvrcpControllerService);
2040         clearInvocations(mNativeInterface);
2041         BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.mBrowseTree.mRootNode;
2042         mAvrcpStateMachine.requestContents(deviceRoot);
2043         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
2044         verifyNoMoreInteractions(mAvrcpControllerService);
2045         verifyNoMoreInteractions(mNativeInterface);
2046     }
2047 
2048     /**
2049      * Queue a control channel connection event, a request while browse is disconnected, a browse
2050      * connection event, and then another browse request and be sure the final request still is sent
2051      */
2052     @Test
testBrowseRequestWhileDisconnectedThenRequestWhileConnected_secondRequestSent()2053     public void testBrowseRequestWhileDisconnectedThenRequestWhileConnected_secondRequestSent() {
2054         setUpConnectedState(true, false);
2055         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
2056         clearInvocations(mAvrcpControllerService);
2057         clearInvocations(mNativeInterface);
2058         BrowseTree.BrowseNode deviceRoot = mAvrcpStateMachine.mBrowseTree.mRootNode;
2059         mAvrcpStateMachine.requestContents(deviceRoot);
2060         // issues a player list fetch
2061         mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(true, true));
2062         TestUtils.waitForLooperToBeIdle(mAvrcpStateMachine.getHandler().getLooper());
2063         verify(mNativeInterface).getPlayerList(eq(mTestAddress), eq(0), eq(19));
2064     }
2065 
2066     @Test
2067     @EnableFlags(Flags.FLAG_UNCACHE_PLAYER_WHEN_BROWSED_PLAYER_CHANGES)
testBrowsingContentsOfOtherBrowsablePlayer_browsedPlayerUncached()2068     public void testBrowsingContentsOfOtherBrowsablePlayer_browsedPlayerUncached() {
2069         setUpConnectedState(true, true);
2070         sendAudioFocusUpdate(AudioManager.AUDIOFOCUS_GAIN);
2071 
2072         BrowseTree.BrowseNode results = mAvrcpStateMachine.mBrowseTree.mRootNode;
2073 
2074         // Request fetch the list of players
2075         BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID());
2076         mAvrcpStateMachine.requestContents(playerNodes);
2077         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
2078                 .getPlayerList(eq(mTestAddress), eq(0), eq(19));
2079 
2080         // Provide back two player objects
2081         byte[] playerFeatures =
2082                 new byte[] {0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0};
2083         AvrcpPlayer playerOne = makePlayer(mTestDevice, 1, "player 1", playerFeatures, 1);
2084         AvrcpPlayer playerTwo = makePlayer(mTestDevice, 2, "player 2", playerFeatures, 1);
2085         List<AvrcpPlayer> testPlayers = new ArrayList<>();
2086         testPlayers.add(playerOne);
2087         testPlayers.add(playerTwo);
2088         mAvrcpStateMachine.sendMessage(
2089                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers);
2090         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
2091 
2092         // Verify that the player objects are both available and properly formatted
2093         playerNodes = mAvrcpStateMachine.findNode(results.getID());
2094         assertThat(playerNodes.isCached()).isTrue();
2095         assertThat(playerNodes.getChildren()).isNotNull();
2096         assertThat(playerNodes.getChildren().size()).isEqualTo(2);
2097         assertThat(playerNodes.getChildren().get(0).getMediaItem().toString())
2098                 .isEqualTo("MediaItem{mFlags=1, mDescription=player 1, null, null}");
2099         assertThat(playerNodes.getChildren().get(1).getMediaItem().toString())
2100                 .isEqualTo("MediaItem{mFlags=1, mDescription=player 2, null, null}");
2101 
2102         // Fetch contents of the first player object
2103         BrowseTree.BrowseNode playerOneNode =
2104                 mAvrcpStateMachine.findNode(results.getChildren().get(0).getID());
2105         mAvrcpStateMachine.requestContents(playerOneNode);
2106         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
2107                 .setBrowsedPlayer(eq(mTestAddress), eq(1));
2108         mAvrcpStateMachine.sendMessage(
2109                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER,
2110                 /* items= */ 5,
2111                 /* depth= */ 0);
2112         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
2113                 .getFolderList(eq(mTestAddress), eq(0), eq(4));
2114 
2115         // Return some results for Player One
2116         List<AvrcpItem> testFolderContents = new ArrayList<AvrcpItem>();
2117         for (int i = 0; i < 5; i++) {
2118             String title = "Song " + Integer.toString(i);
2119             testFolderContents.add(makeNowPlayingItem(i, title));
2120         }
2121         mAvrcpStateMachine.sendMessage(
2122                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, testFolderContents);
2123         mAvrcpStateMachine.sendMessage(
2124                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
2125         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
2126 
2127         // Make sure the player/folder is cached
2128         playerOneNode = mAvrcpStateMachine.findNode(results.getChildren().get(0).getID());
2129         assertThat(playerOneNode.isCached()).isTrue();
2130 
2131         // Browse to the Player Two
2132         BrowseTree.BrowseNode playerTwoNode =
2133                 mAvrcpStateMachine.findNode(results.getChildren().get(1).getID());
2134         mAvrcpStateMachine.requestContents(playerTwoNode);
2135         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1))
2136                 .setBrowsedPlayer(eq(mTestAddress), eq(2));
2137         mAvrcpStateMachine.sendMessage(
2138                 AvrcpControllerStateMachine.MESSAGE_PROCESS_SET_BROWSED_PLAYER,
2139                 /* items= */ 5,
2140                 /* depth= */ 0);
2141         verify(mNativeInterface, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2))
2142                 .getFolderList(eq(mTestAddress), eq(0), eq(4));
2143 
2144         // Make sure the first player is uncached
2145         playerOneNode = mAvrcpStateMachine.findNode(results.getChildren().get(0).getID());
2146         assertThat(playerOneNode.isCached()).isFalse();
2147 
2148         // Send items for Player Two
2149         testFolderContents = new ArrayList<AvrcpItem>();
2150         for (int i = 5; i < 10; i++) {
2151             String title = "Song " + Integer.toString(i);
2152             testFolderContents.add(makeNowPlayingItem(i, title));
2153         }
2154         mAvrcpStateMachine.sendMessage(
2155                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, testFolderContents);
2156         mAvrcpStateMachine.sendMessage(
2157                 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE);
2158         TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper());
2159 
2160         // make sure the second player is cached now
2161         playerTwoNode = mAvrcpStateMachine.findNode(results.getChildren().get(1).getID());
2162         assertThat(playerTwoNode.isCached()).isTrue();
2163     }
2164 }
2165