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