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