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 org.mockito.Mockito.*; 19 20 import android.bluetooth.BluetoothAdapter; 21 import android.bluetooth.BluetoothAvrcpController; 22 import android.bluetooth.BluetoothDevice; 23 import android.bluetooth.BluetoothProfile; 24 import android.content.Context; 25 import android.content.Intent; 26 import android.content.res.Resources; 27 import android.media.AudioManager; 28 import android.os.Looper; 29 import android.support.v4.media.session.MediaControllerCompat; 30 import android.support.v4.media.session.PlaybackStateCompat; 31 32 import androidx.test.InstrumentationRegistry; 33 import androidx.test.filters.FlakyTest; 34 import androidx.test.filters.MediumTest; 35 import androidx.test.rule.ServiceTestRule; 36 import androidx.test.runner.AndroidJUnit4; 37 38 import com.android.bluetooth.R; 39 import com.android.bluetooth.TestUtils; 40 import com.android.bluetooth.a2dpsink.A2dpSinkService; 41 import com.android.bluetooth.btservice.AdapterService; 42 import com.android.bluetooth.btservice.ProfileService; 43 44 import org.hamcrest.core.IsInstanceOf; 45 import org.junit.After; 46 import org.junit.Assert; 47 import org.junit.Assume; 48 import org.junit.Before; 49 import org.junit.Rule; 50 import org.junit.Test; 51 import org.junit.runner.RunWith; 52 import org.mockito.ArgumentCaptor; 53 import org.mockito.Mock; 54 import org.mockito.MockitoAnnotations; 55 56 import java.util.ArrayList; 57 import java.util.List; 58 import java.util.UUID; 59 60 @MediumTest 61 @RunWith(AndroidJUnit4.class) 62 public class AvrcpControllerStateMachineTest { 63 private static final int ASYNC_CALL_TIMEOUT_MILLIS = 100; 64 private static final int CONNECT_TIMEOUT_TEST_MILLIS = 1000; 65 private static final int KEY_DOWN = 0; 66 private static final int KEY_UP = 1; 67 private AvrcpControllerStateMachine mAvrcpStateMachine; 68 private BluetoothAdapter mAdapter; 69 private Context mTargetContext; 70 private BluetoothDevice mTestDevice; 71 private ArgumentCaptor<Intent> mIntentArgument = ArgumentCaptor.forClass(Intent.class); 72 private byte[] mTestAddress = new byte[]{00, 01, 02, 03, 04, 05}; 73 74 @Rule public final ServiceTestRule mAvrcpServiceRule = new ServiceTestRule(); 75 @Rule public final ServiceTestRule mA2dpServiceRule = new ServiceTestRule(); 76 77 @Mock 78 private AdapterService mAvrcpAdapterService; 79 80 @Mock 81 private AdapterService mA2dpAdapterService; 82 83 @Mock 84 private AudioManager mAudioManager; 85 @Mock 86 private AvrcpControllerService mAvrcpControllerService; 87 @Mock 88 private A2dpSinkService mA2dpSinkService; 89 90 @Mock 91 private Resources mMockResources; 92 93 94 @Before setUp()95 public void setUp() throws Exception { 96 mTargetContext = InstrumentationRegistry.getTargetContext(); 97 Assume.assumeTrue("Ignore test when AVRCP Controller is not enabled", 98 mTargetContext.getResources().getBoolean( 99 R.bool.profile_supported_avrcp_controller)); 100 if (Looper.myLooper() == null) { 101 Looper.prepare(); 102 } 103 Assert.assertNotNull(Looper.myLooper()); 104 105 // Setup mocks and test assets 106 MockitoAnnotations.initMocks(this); 107 TestUtils.setAdapterService(mAvrcpAdapterService); 108 TestUtils.startService(mAvrcpServiceRule, AvrcpControllerService.class); 109 TestUtils.clearAdapterService(mAvrcpAdapterService); 110 TestUtils.setAdapterService(mA2dpAdapterService); 111 TestUtils.startService(mA2dpServiceRule, A2dpSinkService.class); 112 when(mA2dpSinkService.setActiveDeviceNative(any())).thenReturn(true); 113 114 when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus)) 115 .thenReturn(true); 116 doReturn(mMockResources).when(mAvrcpControllerService).getResources(); 117 A2dpSinkService.setA2dpSinkService(mA2dpSinkService); 118 doReturn(15).when(mAudioManager).getStreamMaxVolume(anyInt()); 119 doReturn(8).when(mAudioManager).getStreamVolume(anyInt()); 120 doReturn(true).when(mAudioManager).isVolumeFixed(); 121 doReturn(mAudioManager).when(mAvrcpControllerService) 122 .getSystemService(Context.AUDIO_SERVICE); 123 124 // This line must be called to make sure relevant objects are initialized properly 125 mAdapter = BluetoothAdapter.getDefaultAdapter(); 126 // Get a device for testing 127 mTestDevice = mAdapter.getRemoteDevice(mTestAddress); 128 mAvrcpControllerService.start(); 129 mAvrcpControllerService.sBrowseTree = new BrowseTree(null); 130 mAvrcpStateMachine = new AvrcpControllerStateMachine(mTestDevice, mAvrcpControllerService); 131 mAvrcpStateMachine.start(); 132 } 133 134 @After tearDown()135 public void tearDown() throws Exception { 136 if (!mTargetContext.getResources().getBoolean(R.bool.profile_supported_avrcp_controller)) { 137 return; 138 } 139 140 mAvrcpStateMachine.disconnect(); 141 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 142 Assert.assertFalse(mAvrcpStateMachine.isActive()); 143 144 TestUtils.clearAdapterService(mA2dpAdapterService); 145 } 146 147 /** 148 * Test to confirm that the state machine is capable of cycling through the 4 149 * connection states, and that upon completion, it cleans up aftwards. 150 */ 151 @Test testDisconnect()152 public void testDisconnect() { 153 int numBroadcastsSent = setUpConnectedState(true, true); 154 StackEvent event = 155 StackEvent.connectionStateChanged(false, false); 156 157 mAvrcpStateMachine.disconnect(); 158 numBroadcastsSent += 2; 159 verify(mAvrcpControllerService, 160 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent)).sendBroadcast( 161 mIntentArgument.capture(), eq(ProfileService.BLUETOOTH_PERM)); 162 Assert.assertEquals(mTestDevice, mIntentArgument.getValue().getParcelableExtra( 163 BluetoothDevice.EXTRA_DEVICE)); 164 Assert.assertEquals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED, 165 mIntentArgument.getValue().getAction()); 166 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 167 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 168 Assert.assertThat(mAvrcpStateMachine.getCurrentState(), 169 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class)); 170 Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED); 171 verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine)); 172 MediaControllerCompat.TransportControls transportControls = 173 BluetoothMediaBrowserService.getTransportControls(); 174 Assert.assertEquals(PlaybackStateCompat.STATE_ERROR, 175 BluetoothMediaBrowserService.getPlaybackState()); 176 } 177 178 /** 179 * Test to confirm that a control only device can be established (no browsing) 180 */ 181 @Test testControlOnly()182 public void testControlOnly() { 183 int numBroadcastsSent = setUpConnectedState(true, false); 184 MediaControllerCompat.TransportControls transportControls = 185 BluetoothMediaBrowserService.getTransportControls(); 186 Assert.assertNotNull(transportControls); 187 Assert.assertEquals(PlaybackStateCompat.STATE_NONE, 188 BluetoothMediaBrowserService.getPlaybackState()); 189 StackEvent event = 190 StackEvent.connectionStateChanged(false, false); 191 mAvrcpStateMachine.disconnect(); 192 numBroadcastsSent += 2; 193 verify(mAvrcpControllerService, 194 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent)).sendBroadcast( 195 mIntentArgument.capture(), eq(ProfileService.BLUETOOTH_PERM)); 196 Assert.assertEquals(mTestDevice, mIntentArgument.getValue().getParcelableExtra( 197 BluetoothDevice.EXTRA_DEVICE)); 198 Assert.assertEquals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED, 199 mIntentArgument.getValue().getAction()); 200 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 201 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 202 Assert.assertThat(mAvrcpStateMachine.getCurrentState(), 203 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class)); 204 Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED); 205 verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine)); 206 Assert.assertEquals(PlaybackStateCompat.STATE_ERROR, 207 BluetoothMediaBrowserService.getPlaybackState()); 208 } 209 210 /** 211 * Test to confirm that a browsing only device can be established (no control) 212 */ 213 @Test 214 @FlakyTest testBrowsingOnly()215 public void testBrowsingOnly() { 216 Assert.assertEquals(0, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount()); 217 int numBroadcastsSent = setUpConnectedState(false, true); 218 Assert.assertEquals(1, mAvrcpControllerService.sBrowseTree.mRootNode.getChildrenCount()); 219 Assert.assertEquals(PlaybackStateCompat.STATE_NONE, 220 BluetoothMediaBrowserService.getPlaybackState()); 221 StackEvent event = 222 StackEvent.connectionStateChanged(false, false); 223 mAvrcpStateMachine.disconnect(); 224 numBroadcastsSent += 2; 225 verify(mAvrcpControllerService, 226 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(numBroadcastsSent)).sendBroadcast( 227 mIntentArgument.capture(), eq(ProfileService.BLUETOOTH_PERM)); 228 Assert.assertEquals(mTestDevice, mIntentArgument.getValue().getParcelableExtra( 229 BluetoothDevice.EXTRA_DEVICE)); 230 Assert.assertEquals(BluetoothAvrcpController.ACTION_CONNECTION_STATE_CHANGED, 231 mIntentArgument.getValue().getAction()); 232 Assert.assertEquals(BluetoothProfile.STATE_DISCONNECTED, 233 mIntentArgument.getValue().getIntExtra(BluetoothProfile.EXTRA_STATE, -1)); 234 Assert.assertThat(mAvrcpStateMachine.getCurrentState(), 235 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class)); 236 Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_DISCONNECTED); 237 verify(mAvrcpControllerService).removeStateMachine(eq(mAvrcpStateMachine)); 238 MediaControllerCompat.TransportControls transportControls = 239 BluetoothMediaBrowserService.getTransportControls(); 240 Assert.assertEquals(PlaybackStateCompat.STATE_ERROR, 241 BluetoothMediaBrowserService.getPlaybackState()); 242 } 243 244 /** 245 * Test to make sure the state machine is tracking the correct device 246 */ 247 @Test testGetDevice()248 public void testGetDevice() { 249 Assert.assertEquals(mAvrcpStateMachine.getDevice(), mTestDevice); 250 } 251 252 /** 253 * Test that dumpsys will generate information about connected devices 254 */ 255 @Test testDump()256 public void testDump() { 257 StringBuilder sb = new StringBuilder(); 258 mAvrcpStateMachine.dump(sb); 259 Assert.assertEquals(sb.toString(), 260 " mDevice: " + mTestDevice.toString() 261 + "(null) name=AvrcpControllerStateMachine state=Disconnected\n" 262 + " isActive: false\n"); 263 } 264 265 /** 266 * Test media browser play command 267 */ 268 @Test testPlay()269 public void testPlay() throws Exception { 270 setUpConnectedState(true, true); 271 MediaControllerCompat.TransportControls transportControls = 272 BluetoothMediaBrowserService.getTransportControls(); 273 274 //Play 275 transportControls.play(); 276 verify(mAvrcpControllerService, 277 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 278 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN)); 279 verify(mAvrcpControllerService, 280 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 281 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP)); 282 } 283 284 /** 285 * Test media browser pause command 286 */ 287 @Test testPause()288 public void testPause() throws Exception { 289 setUpConnectedState(true, true); 290 MediaControllerCompat.TransportControls transportControls = 291 BluetoothMediaBrowserService.getTransportControls(); 292 293 //Pause 294 transportControls.pause(); 295 verify(mAvrcpControllerService, 296 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 297 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN)); 298 verify(mAvrcpControllerService, 299 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 300 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_UP)); 301 } 302 303 /** 304 * Test media browser stop command 305 */ 306 @Test testStop()307 public void testStop() throws Exception { 308 setUpConnectedState(true, true); 309 MediaControllerCompat.TransportControls transportControls = 310 BluetoothMediaBrowserService.getTransportControls(); 311 312 //Stop 313 transportControls.stop(); 314 verify(mAvrcpControllerService, 315 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 316 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_DOWN)); 317 verify(mAvrcpControllerService, 318 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 319 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_STOP), eq(KEY_UP)); 320 } 321 322 /** 323 * Test media browser next command 324 */ 325 @Test testNext()326 public void testNext() throws Exception { 327 setUpConnectedState(true, true); 328 MediaControllerCompat.TransportControls transportControls = 329 BluetoothMediaBrowserService.getTransportControls(); 330 331 //Next 332 transportControls.skipToNext(); 333 verify(mAvrcpControllerService, 334 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 335 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD), 336 eq(KEY_DOWN)); 337 verify(mAvrcpControllerService, 338 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 339 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FORWARD), eq(KEY_UP)); 340 } 341 342 /** 343 * Test media browser previous command 344 */ 345 @Test testPrevious()346 public void testPrevious() throws Exception { 347 setUpConnectedState(true, true); 348 MediaControllerCompat.TransportControls transportControls = 349 BluetoothMediaBrowserService.getTransportControls(); 350 351 //Previous 352 transportControls.skipToPrevious(); 353 verify(mAvrcpControllerService, 354 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 355 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD), 356 eq(KEY_DOWN)); 357 verify(mAvrcpControllerService, 358 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 359 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_BACKWARD), eq(KEY_UP)); 360 } 361 362 /** 363 * Test media browser fast forward command 364 */ 365 @Test 366 @FlakyTest testFastForward()367 public void testFastForward() throws Exception { 368 setUpConnectedState(true, true); 369 MediaControllerCompat.TransportControls transportControls = 370 BluetoothMediaBrowserService.getTransportControls(); 371 372 //FastForward 373 transportControls.fastForward(); 374 verify(mAvrcpControllerService, 375 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 376 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF), eq(KEY_DOWN)); 377 //Finish FastForwarding 378 transportControls.play(); 379 verify(mAvrcpControllerService, 380 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 381 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_FF), eq(KEY_UP)); 382 } 383 384 /** 385 * Test media browser rewind command 386 */ 387 @Test testRewind()388 public void testRewind() throws Exception { 389 setUpConnectedState(true, true); 390 MediaControllerCompat.TransportControls transportControls = 391 BluetoothMediaBrowserService.getTransportControls(); 392 393 //Rewind 394 transportControls.rewind(); 395 verify(mAvrcpControllerService, 396 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 397 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND), eq(KEY_DOWN)); 398 //Finish Rewinding 399 transportControls.play(); 400 verify(mAvrcpControllerService, 401 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 402 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_REWIND), eq(KEY_UP)); 403 } 404 405 /** 406 * Test media browser skip to queue item 407 */ 408 @Test testSkipToQueueInvalid()409 public void testSkipToQueueInvalid() throws Exception { 410 byte scope = 1; 411 int minSize = 0; 412 int maxSize = 255; 413 setUpConnectedState(true, true); 414 MediaControllerCompat.TransportControls transportControls = 415 BluetoothMediaBrowserService.getTransportControls(); 416 417 //Play an invalid item below start 418 transportControls.skipToQueueItem(minSize - 1); 419 verify(mAvrcpControllerService, 420 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative( 421 eq(mTestAddress), eq(scope), anyLong(), anyInt()); 422 423 //Play an invalid item beyond end 424 transportControls.skipToQueueItem(maxSize + 1); 425 verify(mAvrcpControllerService, 426 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(0)).playItemNative( 427 eq(mTestAddress), eq(scope), anyLong(), anyInt()); 428 } 429 430 /** 431 * Test media browser shuffle command 432 */ 433 @Test testShuffle()434 public void testShuffle() throws Exception { 435 byte[] shuffleSetting = new byte[]{3}; 436 byte[] shuffleMode = new byte[]{2}; 437 438 setUpConnectedState(true, true); 439 MediaControllerCompat.TransportControls transportControls = 440 BluetoothMediaBrowserService.getTransportControls(); 441 442 //Shuffle 443 transportControls.setShuffleMode(1); 444 verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) 445 .setPlayerApplicationSettingValuesNative( 446 eq(mTestAddress), eq((byte) 1), eq(shuffleSetting), eq(shuffleMode)); 447 } 448 449 /** 450 * Test media browser repeat command 451 */ 452 @Test testRepeat()453 public void testRepeat() throws Exception { 454 byte[] repeatSetting = new byte[]{2}; 455 byte[] repeatMode = new byte[]{3}; 456 457 setUpConnectedState(true, true); 458 MediaControllerCompat.TransportControls transportControls = 459 BluetoothMediaBrowserService.getTransportControls(); 460 461 //Shuffle 462 transportControls.setRepeatMode(2); 463 verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) 464 .setPlayerApplicationSettingValuesNative( 465 eq(mTestAddress), eq((byte) 1), eq(repeatSetting), eq(repeatMode)); 466 } 467 468 /** 469 * Test media browsing 470 * Verify that a browse tree is created with the proper root 471 * Verify that a player can be fetched and added to the browse tree 472 * Verify that the contents of a player are fetched upon request 473 */ 474 @Test 475 @FlakyTest testBrowsingCommands()476 public void testBrowsingCommands() { 477 setUpConnectedState(true, true); 478 final String rootName = "__ROOT__"; 479 final String playerName = "Player 1"; 480 481 //Get the root of the device 482 BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName); 483 Assert.assertEquals(rootName + mTestDevice.toString(), results.getID()); 484 485 //Request fetch the list of players 486 BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID()); 487 mAvrcpStateMachine.requestContents(results); 488 verify(mAvrcpControllerService, 489 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress), 490 eq(0), eq(19)); 491 492 //Provide back a player object 493 byte[] playerFeatures = 494 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0}; 495 AvrcpPlayer playerOne = new AvrcpPlayer(mTestDevice, 1, playerName, playerFeatures, 1, 1); 496 List<AvrcpPlayer> testPlayers = new ArrayList<>(); 497 testPlayers.add(playerOne); 498 mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, 499 testPlayers); 500 501 //Verify that the player object is available. 502 mAvrcpStateMachine.requestContents(results); 503 verify(mAvrcpControllerService, 504 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress), 505 eq(1), eq(0)); 506 mAvrcpStateMachine.sendMessage( 507 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE); 508 playerNodes = mAvrcpStateMachine.findNode(results.getID()); 509 Assert.assertEquals(true, results.isCached()); 510 Assert.assertEquals("MediaItem{mFlags=1, mDescription=" + playerName + ", null, null}", 511 results.getChildren().get(0).getMediaItem().toString()); 512 513 //Fetch contents of that player object 514 BrowseTree.BrowseNode playerOneNode = mAvrcpStateMachine.findNode( 515 results.getChildren().get(0).getID()); 516 mAvrcpStateMachine.requestContents(playerOneNode); 517 verify(mAvrcpControllerService, 518 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).setBrowsedPlayerNative( 519 eq(mTestAddress), eq(1)); 520 mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_FOLDER_PATH, 5); 521 verify(mAvrcpControllerService, 522 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getFolderListNative(eq(mTestAddress), 523 eq(0), eq(4)); 524 } 525 526 /** 527 * Make an AvrcpItem suitable for being included in the Now Playing list for the test device 528 */ makeNowPlayingItem(long uid, String name)529 private AvrcpItem makeNowPlayingItem(long uid, String name) { 530 AvrcpItem.Builder aib = new AvrcpItem.Builder(); 531 aib.setDevice(mTestDevice); 532 aib.setItemType(AvrcpItem.TYPE_MEDIA); 533 aib.setType(AvrcpItem.MEDIA_AUDIO); 534 aib.setTitle(name); 535 aib.setUid(uid); 536 aib.setUuid(UUID.randomUUID().toString()); 537 aib.setPlayable(true); 538 return aib.build(); 539 } 540 541 /** 542 * Get the current Now Playing list for the test device 543 */ getNowPlayingList()544 private List<AvrcpItem> getNowPlayingList() { 545 BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); 546 List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>(); 547 for (BrowseTree.BrowseNode child : nowPlaying.getChildren()) { 548 nowPlayingList.add(child.mItem); 549 } 550 return nowPlayingList; 551 } 552 553 /** 554 * Set the current Now Playing list for the test device 555 */ setNowPlayingList(List<AvrcpItem> nowPlayingList)556 private void setNowPlayingList(List<AvrcpItem> nowPlayingList) { 557 BrowseTree.BrowseNode nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); 558 mAvrcpStateMachine.requestContents(nowPlaying); 559 mAvrcpStateMachine.sendMessage( 560 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS, nowPlayingList); 561 mAvrcpStateMachine.sendMessage( 562 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_FOLDER_ITEMS_OUT_OF_RANGE); 563 564 // Wait for the now playing list to be propagated 565 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 566 567 // Make sure its set by re grabbing the node and checking its contents are cached 568 nowPlaying = mAvrcpStateMachine.findNode("NOW_PLAYING"); 569 Assert.assertTrue(nowPlaying.isCached()); 570 assertNowPlayingList(nowPlayingList); 571 } 572 573 /** 574 * Assert that the Now Playing list is a particular value 575 */ assertNowPlayingList(List<AvrcpItem> expected)576 private void assertNowPlayingList(List<AvrcpItem> expected) { 577 List<AvrcpItem> current = getNowPlayingList(); 578 Assert.assertEquals(expected.size(), current.size()); 579 for (int i = 0; i < expected.size(); i++) { 580 Assert.assertEquals(expected.get(i), current.get(i)); 581 } 582 } 583 584 /** 585 * Test addressed media player changing to a player we know about 586 * Verify when the addressed media player changes browsing data updates 587 */ 588 @Test testPlayerChanged()589 public void testPlayerChanged() { 590 setUpConnectedState(true, true); 591 final String rootName = "__ROOT__"; 592 593 //Get the root of the device 594 BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName); 595 Assert.assertEquals(rootName + mTestDevice.toString(), results.getID()); 596 597 //Request fetch the list of players 598 BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID()); 599 mAvrcpStateMachine.requestContents(results); 600 verify(mAvrcpControllerService, 601 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress), 602 eq(0), eq(19)); 603 604 //Provide back two player objects, IDs 1 and 2 605 byte[] playerFeatures = 606 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0}; 607 AvrcpPlayer playerOne = new AvrcpPlayer(mTestDevice, 1, "Player 1", playerFeatures, 1, 1); 608 AvrcpPlayer playerTwo = new AvrcpPlayer(mTestDevice, 2, "Player 2", playerFeatures, 1, 1); 609 List<AvrcpPlayer> testPlayers = new ArrayList<>(); 610 testPlayers.add(playerOne); 611 testPlayers.add(playerTwo); 612 mAvrcpStateMachine.sendMessage(AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, 613 testPlayers); 614 615 //Set something arbitrary for the current Now Playing list 616 List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>(); 617 nowPlayingList.add(makeNowPlayingItem(1, "Song 1")); 618 nowPlayingList.add(makeNowPlayingItem(2, "Song 2")); 619 setNowPlayingList(nowPlayingList); 620 621 //Change players and verify that BT attempts to update the results 622 mAvrcpStateMachine.sendMessage( 623 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 2); 624 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 625 626 //Make sure the Now Playing list is now cleared 627 assertNowPlayingList(new ArrayList<AvrcpItem>()); 628 629 //Verify that a player change to a player with Now Playing support causes a refresh. This 630 //should be called twice, once to give data and once to ensure we're out of elements 631 verify(mAvrcpControllerService, 632 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).getNowPlayingListNative( 633 eq(mTestAddress), eq(0), eq(19)); 634 } 635 636 /** 637 * Test addressed media player change to a player we don't know about 638 * Verify when the addressed media player changes browsing data updates 639 * Verify that the contents of a player are fetched upon request 640 */ 641 @Test testPlayerChangedToUnknownPlayer()642 public void testPlayerChangedToUnknownPlayer() { 643 setUpConnectedState(true, true); 644 final String rootName = "__ROOT__"; 645 646 //Get the root of the device 647 BrowseTree.BrowseNode rootNode = mAvrcpStateMachine.findNode(rootName); 648 Assert.assertEquals(rootName + mTestDevice.toString(), rootNode.getID()); 649 650 //Request fetch the list of players 651 BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(rootNode.getID()); 652 mAvrcpStateMachine.requestContents(rootNode); 653 verify(mAvrcpControllerService, 654 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getPlayerListNative(eq(mTestAddress), 655 eq(0), eq(19)); 656 657 //Provide back a player object 658 byte[] playerFeatures = 659 new byte[]{0, 0, 0, 0, 0, (byte) 0xb7, 0x01, 0x0c, 0x0a, 0, 0, 0, 0, 0, 0, 0}; 660 AvrcpPlayer playerOne = new AvrcpPlayer(mTestDevice, 1, "Player 1", playerFeatures, 1, 1); 661 List<AvrcpPlayer> testPlayers = new ArrayList<>(); 662 testPlayers.add(playerOne); 663 mAvrcpStateMachine.sendMessage( 664 AvrcpControllerStateMachine.MESSAGE_PROCESS_GET_PLAYER_ITEMS, testPlayers); 665 666 //Set something arbitrary for the current Now Playing list 667 List<AvrcpItem> nowPlayingList = new ArrayList<AvrcpItem>(); 668 nowPlayingList.add(makeNowPlayingItem(1, "Song 1")); 669 nowPlayingList.add(makeNowPlayingItem(2, "Song 2")); 670 setNowPlayingList(nowPlayingList); 671 672 //Change players 673 mAvrcpStateMachine.sendMessage( 674 AvrcpControllerStateMachine.MESSAGE_PROCESS_ADDRESSED_PLAYER_CHANGED, 4); 675 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 676 677 //Make sure the Now Playing list is now cleared 678 assertNowPlayingList(new ArrayList<AvrcpItem>()); 679 680 //Make sure the root node is no longer cached 681 rootNode = mAvrcpStateMachine.findNode(rootName); 682 Assert.assertFalse(rootNode.isCached()); 683 } 684 685 /** 686 * Test that the Now Playing playlist is updated when it changes. 687 */ 688 @Test testNowPlaying()689 public void testNowPlaying() { 690 setUpConnectedState(true, true); 691 mAvrcpStateMachine.nowPlayingContentChanged(); 692 verify(mAvrcpControllerService, 693 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).getNowPlayingListNative( 694 eq(mTestAddress), eq(0), eq(19)); 695 } 696 697 /** 698 * Test that AVRCP events such as playback commands can execute while performing browsing. 699 */ 700 @Test testPlayWhileBrowsing()701 public void testPlayWhileBrowsing() { 702 setUpConnectedState(true, true); 703 final String rootName = "__ROOT__"; 704 final String playerName = "Player 1"; 705 706 //Get the root of the device 707 BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName); 708 Assert.assertEquals(rootName + mTestDevice.toString(), results.getID()); 709 710 //Request fetch the list of players 711 BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID()); 712 mAvrcpStateMachine.requestContents(results); 713 714 MediaControllerCompat.TransportControls transportControls = 715 BluetoothMediaBrowserService.getTransportControls(); 716 transportControls.play(); 717 verify(mAvrcpControllerService, 718 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 719 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_DOWN)); 720 verify(mAvrcpControllerService, 721 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 722 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PLAY), eq(KEY_UP)); 723 } 724 725 /** 726 * Test that Absolute Volume Registration is working 727 */ 728 @Test testRegisterAbsVolumeNotification()729 public void testRegisterAbsVolumeNotification() { 730 setUpConnectedState(true, true); 731 mAvrcpStateMachine.sendMessage( 732 AvrcpControllerStateMachine.MESSAGE_PROCESS_REGISTER_ABS_VOL_NOTIFICATION); 733 verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)) 734 .sendRegisterAbsVolRspNative(any(), anyByte(), eq(127), anyInt()); 735 } 736 737 /** 738 * Test playback does not request focus when another app is playing music. 739 */ 740 @Test testPlaybackWhileMusicPlaying()741 public void testPlaybackWhileMusicPlaying() { 742 when(mMockResources.getBoolean(R.bool.a2dp_sink_automatically_request_audio_focus)) 743 .thenReturn(false); 744 when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_NONE); 745 doReturn(true).when(mAudioManager).isMusicActive(); 746 setUpConnectedState(true, true); 747 mAvrcpStateMachine.sendMessage( 748 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, 749 PlaybackStateCompat.STATE_PLAYING); 750 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 751 verify(mAvrcpControllerService, 752 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 753 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN)); 754 verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true); 755 } 756 757 /** 758 * Test playback requests focus while nothing is playing music. 759 */ 760 @Test testPlaybackWhileIdle()761 public void testPlaybackWhileIdle() { 762 when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_NONE); 763 doReturn(false).when(mAudioManager).isMusicActive(); 764 setUpConnectedState(true, true); 765 mAvrcpStateMachine.sendMessage( 766 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, 767 PlaybackStateCompat.STATE_PLAYING); 768 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 769 verify(mA2dpSinkService).requestAudioFocus(mTestDevice, true); 770 } 771 772 /** 773 * Test receiving a playback status of playing while we're in an error state 774 * as it relates to getting audio focus. 775 * 776 * Verify we send a pause command and never attempt to request audio focus 777 */ 778 @Test testPlaybackWhileErrorState()779 public void testPlaybackWhileErrorState() { 780 when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.ERROR); 781 setUpConnectedState(true, true); 782 mAvrcpStateMachine.sendMessage( 783 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, 784 PlaybackStateCompat.STATE_PLAYING); 785 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 786 verify(mAvrcpControllerService, 787 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 788 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN)); 789 verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true); 790 } 791 792 /** 793 * Test receiving a playback status of playing while we have focus 794 * 795 * Verify we do not send a pause command and never attempt to request audio focus 796 */ 797 @Test testPlaybackWhilePlayingState()798 public void testPlaybackWhilePlayingState() { 799 when(mA2dpSinkService.getFocusState()).thenReturn(AudioManager.AUDIOFOCUS_GAIN); 800 setUpConnectedState(true, true); 801 Assert.assertTrue(mAvrcpStateMachine.isActive()); 802 mAvrcpStateMachine.sendMessage( 803 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, 804 PlaybackStateCompat.STATE_PLAYING); 805 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 806 verify(mAvrcpControllerService, never()).sendPassThroughCommandNative( 807 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN)); 808 verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true); 809 } 810 811 /** 812 * Test receiving a playback status of playing from a device that isn't active 813 * 814 * Verify we do not send a pause command and never attempt to request audio focus 815 */ 816 @Test testPlaybackWhileNotActiveDevice()817 public void testPlaybackWhileNotActiveDevice() { 818 byte[] secondTestAddress = new byte[]{00, 01, 02, 03, 04, 06}; 819 BluetoothDevice secondTestDevice = mAdapter.getRemoteDevice(secondTestAddress); 820 AvrcpControllerStateMachine secondAvrcpStateMachine = 821 new AvrcpControllerStateMachine(secondTestDevice, mAvrcpControllerService); 822 secondAvrcpStateMachine.start(); 823 824 setUpConnectedState(true, true); 825 secondAvrcpStateMachine.connect(StackEvent.connectionStateChanged(true, true)); 826 TestUtils.waitForLooperToFinishScheduledTask(secondAvrcpStateMachine.getHandler() 827 .getLooper()); 828 829 Assert.assertTrue(secondAvrcpStateMachine.isActive()); 830 Assert.assertFalse(mAvrcpStateMachine.isActive()); 831 832 mAvrcpStateMachine.sendMessage( 833 AvrcpControllerStateMachine.MESSAGE_PROCESS_PLAY_STATUS_CHANGED, 834 PlaybackStateCompat.STATE_PLAYING); 835 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 836 verify(mAvrcpControllerService, 837 timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(1)).sendPassThroughCommandNative( 838 eq(mTestAddress), eq(AvrcpControllerService.PASS_THRU_CMD_ID_PAUSE), eq(KEY_DOWN)); 839 verify(mA2dpSinkService, never()).requestAudioFocus(mTestDevice, true); 840 841 secondAvrcpStateMachine.disconnect(); 842 TestUtils.waitForLooperToFinishScheduledTask(secondAvrcpStateMachine.getHandler() 843 .getLooper()); 844 Assert.assertFalse(secondAvrcpStateMachine.isActive()); 845 Assert.assertFalse(mAvrcpStateMachine.isActive()); 846 } 847 848 /** 849 * Test that the correct device becomes active 850 * 851 * The first connected device is automatically active, additional ones are not. 852 * After an explicit play command a device becomes active. 853 */ 854 @Test testActiveDeviceManagement()855 public void testActiveDeviceManagement() { 856 // Setup structures and verify initial conditions 857 final String rootName = "__ROOT__"; 858 final String playerName = "Player 1"; 859 byte[] secondTestAddress = new byte[]{00, 01, 02, 03, 04, 06}; 860 BluetoothDevice secondTestDevice = mAdapter.getRemoteDevice(secondTestAddress); 861 AvrcpControllerStateMachine secondAvrcpStateMachine = 862 new AvrcpControllerStateMachine(secondTestDevice, mAvrcpControllerService); 863 secondAvrcpStateMachine.start(); 864 Assert.assertFalse(mAvrcpStateMachine.isActive()); 865 866 // Connect device 1 and 2 and verify second one is set as active 867 setUpConnectedState(true, true); 868 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 869 Assert.assertTrue(mAvrcpStateMachine.isActive()); 870 871 secondAvrcpStateMachine.connect(StackEvent.connectionStateChanged(true, true)); 872 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 873 TestUtils.waitForLooperToFinishScheduledTask(secondAvrcpStateMachine.getHandler() 874 .getLooper()); 875 876 Assert.assertFalse(mAvrcpStateMachine.isActive()); 877 Assert.assertTrue(secondAvrcpStateMachine.isActive()); 878 879 // Request the second device to play an item and verify active device switched 880 BrowseTree.BrowseNode results = mAvrcpStateMachine.findNode(rootName); 881 Assert.assertEquals(rootName + mTestDevice.toString(), results.getID()); 882 BrowseTree.BrowseNode playerNodes = mAvrcpStateMachine.findNode(results.getID()); 883 secondAvrcpStateMachine.playItem(playerNodes); 884 TestUtils.waitForLooperToFinishScheduledTask(secondAvrcpStateMachine.getHandler() 885 .getLooper()); 886 Assert.assertFalse(mAvrcpStateMachine.isActive()); 887 Assert.assertTrue(secondAvrcpStateMachine.isActive()); 888 889 secondAvrcpStateMachine.disconnect(); 890 TestUtils.waitForLooperToFinishScheduledTask(secondAvrcpStateMachine.getHandler() 891 .getLooper()); 892 Assert.assertFalse(secondAvrcpStateMachine.isActive()); 893 Assert.assertFalse(mAvrcpStateMachine.isActive()); 894 } 895 896 /** 897 * Setup Connected State 898 * 899 * @return number of times mAvrcpControllerService.sendBroadcastAsUser() has been invoked 900 */ setUpConnectedState(boolean control, boolean browsing)901 private int setUpConnectedState(boolean control, boolean browsing) { 902 // Put test state machine into connected state 903 Assert.assertThat(mAvrcpStateMachine.getCurrentState(), 904 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Disconnected.class)); 905 906 mAvrcpStateMachine.connect(StackEvent.connectionStateChanged(control, browsing)); 907 TestUtils.waitForLooperToFinishScheduledTask(mAvrcpStateMachine.getHandler().getLooper()); 908 verify(mAvrcpControllerService, timeout(ASYNC_CALL_TIMEOUT_MILLIS).times(2)).sendBroadcast( 909 mIntentArgument.capture(), eq(ProfileService.BLUETOOTH_PERM)); 910 Assert.assertThat(mAvrcpStateMachine.getCurrentState(), 911 IsInstanceOf.instanceOf(AvrcpControllerStateMachine.Connected.class)); 912 Assert.assertEquals(mAvrcpStateMachine.getState(), BluetoothProfile.STATE_CONNECTED); 913 914 return BluetoothProfile.STATE_CONNECTED; 915 } 916 917 } 918