MediaSession2Test.java revision f66f34b424baf93d223bb905184b5b6e1d086c5d
1/* 2 * Copyright 2018 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 17package androidx.media; 18 19import static android.media.AudioAttributes.CONTENT_TYPE_MUSIC; 20 21import static androidx.media.VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE; 22import static androidx.media.VolumeProviderCompat.VOLUME_CONTROL_FIXED; 23 24import static org.junit.Assert.assertEquals; 25import static org.junit.Assert.assertFalse; 26import static org.junit.Assert.assertNotEquals; 27import static org.junit.Assert.assertNotNull; 28import static org.junit.Assert.assertNull; 29import static org.junit.Assert.assertSame; 30import static org.junit.Assert.assertTrue; 31import static org.junit.Assert.fail; 32 33import android.content.Context; 34import android.media.AudioManager; 35import android.os.Build; 36import android.os.Bundle; 37import android.os.Process; 38import android.os.ResultReceiver; 39import android.support.test.filters.SdkSuppress; 40import android.support.test.filters.SmallTest; 41import android.support.test.runner.AndroidJUnit4; 42 43import androidx.annotation.NonNull; 44import androidx.annotation.Nullable; 45import androidx.media.MediaController2.ControllerCallback; 46import androidx.media.MediaController2.PlaybackInfo; 47import androidx.media.MediaSession2.CommandButton; 48import androidx.media.MediaSession2.ControllerInfo; 49import androidx.media.MediaSession2.SessionCallback; 50 51import junit.framework.Assert; 52 53import org.junit.After; 54import org.junit.Before; 55import org.junit.Ignore; 56import org.junit.Test; 57import org.junit.runner.RunWith; 58 59import java.util.ArrayList; 60import java.util.List; 61import java.util.Set; 62import java.util.concurrent.CountDownLatch; 63import java.util.concurrent.TimeUnit; 64 65/** 66 * Tests {@link MediaSession2}. 67 */ 68@SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT) 69@RunWith(AndroidJUnit4.class) 70@SmallTest 71public class MediaSession2Test extends MediaSession2TestBase { 72 private static final String TAG = "MediaSession2Test"; 73 74 private MediaSession2 mSession; 75 private MockPlayer mPlayer; 76 private MockPlaylistAgent mMockAgent; 77 78 @Before 79 @Override 80 public void setUp() throws Exception { 81 super.setUp(); 82 mPlayer = new MockPlayer(0); 83 mMockAgent = new MockPlaylistAgent(); 84 85 mSession = new MediaSession2.Builder(mContext) 86 .setPlayer(mPlayer) 87 .setPlaylistAgent(mMockAgent) 88 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 89 @Override 90 public SessionCommandGroup2 onConnect(MediaSession2 session, 91 ControllerInfo controller) { 92 if (Process.myUid() == controller.getUid()) { 93 return super.onConnect(session, controller); 94 } 95 return null; 96 } 97 }).build(); 98 } 99 100 @After 101 @Override 102 public void cleanUp() throws Exception { 103 super.cleanUp(); 104 mSession.close(); 105 } 106 107 @Test 108 public void testBuilder() { 109 prepareLooper(); 110 MediaSession2.Builder builder = new MediaSession2.Builder(mContext); 111 try { 112 builder.setPlayer(null); 113 fail("null player shouldn't be allowed"); 114 } catch (IllegalArgumentException e) { 115 // expected. pass-through 116 } 117 try { 118 builder.setId(null); 119 fail("null id shouldn't be allowed"); 120 } catch (IllegalArgumentException e) { 121 // expected. pass-through 122 } 123 } 124 125 @Test 126 public void testPlayerStateChange() throws Exception { 127 prepareLooper(); 128 final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING; 129 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 130 sHandler.postAndSync(new Runnable() { 131 @Override 132 public void run() { 133 mSession.close(); 134 mSession = new MediaSession2.Builder(mContext) 135 .setPlayer(mPlayer) 136 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 137 @Override 138 public void onPlayerStateChanged(MediaSession2 session, 139 MediaPlayerBase player, int state) { 140 assertEquals(targetState, state); 141 latchForSessionCallback.countDown(); 142 } 143 }).build(); 144 } 145 }); 146 147 final CountDownLatch latchForControllerCallback = new CountDownLatch(1); 148 final MediaController2 controller = 149 createController(mSession.getToken(), true, new ControllerCallback() { 150 @Override 151 public void onPlayerStateChanged(MediaController2 controllerOut, int state) { 152 assertEquals(targetState, state); 153 latchForControllerCallback.countDown(); 154 } 155 }); 156 157 mPlayer.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING); 158 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 159 assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 160 assertEquals(targetState, controller.getPlayerState()); 161 } 162 163 @Test 164 public void testCurrentDataSourceChanged() throws Exception { 165 prepareLooper(); 166 final int listSize = 5; 167 final List<MediaItem2> list = TestUtils.createPlaylist(listSize); 168 mMockAgent.setPlaylist(list, null); 169 170 final MediaItem2 currentItem = list.get(3); 171 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 172 try (MediaSession2 session = new MediaSession2.Builder(mContext) 173 .setPlayer(mPlayer) 174 .setPlaylistAgent(mMockAgent) 175 .setId("testCurrentDataSourceChanged") 176 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 177 @Override 178 public void onCurrentMediaItemChanged(MediaSession2 session, 179 MediaPlayerBase player, MediaItem2 itemOut) { 180 assertSame(currentItem, itemOut); 181 latchForSessionCallback.countDown(); 182 } 183 }).build()) { 184 185 mPlayer.notifyCurrentDataSourceChanged(currentItem.getDataSourceDesc()); 186 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 187 // TODO(jaewan): Test that controllers are also notified. (b/74505936) 188 } 189 } 190 191 @Test 192 public void testMediaPrepared() throws Exception { 193 prepareLooper(); 194 final int listSize = 5; 195 final List<MediaItem2> list = TestUtils.createPlaylist(listSize); 196 mMockAgent.setPlaylist(list, null); 197 198 final MediaItem2 currentItem = list.get(3); 199 200 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 201 try (MediaSession2 session = new MediaSession2.Builder(mContext) 202 .setPlayer(mPlayer) 203 .setPlaylistAgent(mMockAgent) 204 .setId("testMediaPrepared") 205 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 206 @Override 207 public void onMediaPrepared(MediaSession2 session, MediaPlayerBase player, 208 MediaItem2 itemOut) { 209 assertSame(currentItem, itemOut); 210 latchForSessionCallback.countDown(); 211 } 212 }).build()) { 213 214 mPlayer.notifyMediaPrepared(currentItem.getDataSourceDesc()); 215 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 216 // TODO(jaewan): Test that controllers are also notified. (b/74505936) 217 } 218 } 219 220 @Test 221 public void testBufferingStateChanged() throws Exception { 222 prepareLooper(); 223 final int listSize = 5; 224 final List<MediaItem2> list = TestUtils.createPlaylist(listSize); 225 mMockAgent.setPlaylist(list, null); 226 227 final MediaItem2 currentItem = list.get(3); 228 final int buffState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE; 229 230 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 231 try (MediaSession2 session = new MediaSession2.Builder(mContext) 232 .setPlayer(mPlayer) 233 .setPlaylistAgent(mMockAgent) 234 .setId("testBufferingStateChanged") 235 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 236 @Override 237 public void onBufferingStateChanged(MediaSession2 session, 238 MediaPlayerBase player, MediaItem2 itemOut, int stateOut) { 239 assertSame(currentItem, itemOut); 240 assertEquals(buffState, stateOut); 241 latchForSessionCallback.countDown(); 242 } 243 }).build()) { 244 245 mPlayer.notifyBufferingStateChanged(currentItem.getDataSourceDesc(), buffState); 246 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 247 // TODO(jaewan): Test that controllers are also notified. (b/74505936) 248 } 249 } 250 251 @Test 252 public void testUpdatePlayer() throws Exception { 253 prepareLooper(); 254 final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING; 255 final CountDownLatch latch = new CountDownLatch(1); 256 sHandler.postAndSync(new Runnable() { 257 @Override 258 public void run() { 259 mSession.close(); 260 mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer) 261 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 262 @Override 263 public void onPlayerStateChanged(MediaSession2 session, 264 MediaPlayerBase player, int state) { 265 assertEquals(targetState, state); 266 latch.countDown(); 267 } 268 }).build(); 269 } 270 }); 271 272 MockPlayer player = new MockPlayer(0); 273 274 // Test if setPlayer doesn't crash with various situations. 275 mSession.updatePlayer(mPlayer, null, null); 276 assertEquals(mPlayer, mSession.getPlayer()); 277 MediaPlaylistAgent agent = mSession.getPlaylistAgent(); 278 assertNotNull(agent); 279 280 mSession.updatePlayer(player, null, null); 281 assertEquals(player, mSession.getPlayer()); 282 assertNotNull(mSession.getPlaylistAgent()); 283 assertNotEquals(agent, mSession.getPlaylistAgent()); 284 285 player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING); 286 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 287 } 288 289 @Test 290 public void testSetPlayer_playbackInfo() throws Exception { 291 prepareLooper(); 292 MockPlayer player = new MockPlayer(0); 293 final AudioAttributesCompat attrs = new AudioAttributesCompat.Builder() 294 .setContentType(CONTENT_TYPE_MUSIC) 295 .build(); 296 player.setAudioAttributes(attrs); 297 298 final int maxVolume = 100; 299 final int currentVolume = 23; 300 final int volumeControlType = VOLUME_CONTROL_ABSOLUTE; 301 VolumeProviderCompat volumeProvider = new VolumeProviderCompat( 302 volumeControlType, maxVolume, currentVolume) { }; 303 304 final CountDownLatch latch = new CountDownLatch(1); 305 final ControllerCallback callback = new ControllerCallback() { 306 @Override 307 public void onPlaybackInfoChanged(MediaController2 controller, PlaybackInfo info) { 308 Assert.assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType()); 309 assertEquals(attrs, info.getAudioAttributes()); 310 assertEquals(volumeControlType, info.getPlaybackType()); 311 assertEquals(maxVolume, info.getMaxVolume()); 312 assertEquals(currentVolume, info.getCurrentVolume()); 313 latch.countDown(); 314 } 315 }; 316 317 mSession.updatePlayer(player, null, null); 318 319 final MediaController2 controller = createController(mSession.getToken(), true, callback); 320 PlaybackInfo info = controller.getPlaybackInfo(); 321 assertNotNull(info); 322 assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType()); 323 assertEquals(attrs, info.getAudioAttributes()); 324 AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 325 326 int localVolumeControlType = VOLUME_CONTROL_ABSOLUTE; 327 if (Build.VERSION.SDK_INT >= 21 && manager.isVolumeFixed()) { 328 localVolumeControlType = VOLUME_CONTROL_FIXED; 329 } 330 assertEquals(localVolumeControlType, info.getControlType()); 331 assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume()); 332 assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume()); 333 334 mSession.updatePlayer(player, null, volumeProvider); 335 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 336 337 info = controller.getPlaybackInfo(); 338 assertNotNull(info); 339 assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType()); 340 assertEquals(attrs, info.getAudioAttributes()); 341 assertEquals(volumeControlType, info.getControlType()); 342 assertEquals(maxVolume, info.getMaxVolume()); 343 assertEquals(currentVolume, info.getCurrentVolume()); 344 } 345 346 @Test 347 public void testPlay() throws Exception { 348 prepareLooper(); 349 mSession.play(); 350 assertTrue(mPlayer.mPlayCalled); 351 } 352 353 @Test 354 public void testPause() throws Exception { 355 prepareLooper(); 356 mSession.pause(); 357 assertTrue(mPlayer.mPauseCalled); 358 } 359 360 @Test 361 public void testReset() throws Exception { 362 prepareLooper(); 363 mSession.reset(); 364 assertTrue(mPlayer.mResetCalled); 365 } 366 367 @Test 368 public void testPrepare() throws Exception { 369 prepareLooper(); 370 mSession.prepare(); 371 assertTrue(mPlayer.mPrepareCalled); 372 } 373 374 @Test 375 public void testSeekTo() throws Exception { 376 prepareLooper(); 377 final long pos = 1004L; 378 mSession.seekTo(pos); 379 assertTrue(mPlayer.mSeekToCalled); 380 assertEquals(pos, mPlayer.mSeekPosition); 381 } 382 383 @Test 384 public void testSkipToPreviousItem() { 385 prepareLooper(); 386 mSession.skipToPreviousItem(); 387 assertTrue(mMockAgent.mSkipToPreviousItemCalled); 388 } 389 390 @Test 391 public void testSkipToNextItem() throws Exception { 392 prepareLooper(); 393 mSession.skipToNextItem(); 394 assertTrue(mMockAgent.mSkipToNextItemCalled); 395 } 396 397 @Test 398 public void testSkipToPlaylistItem() throws Exception { 399 prepareLooper(); 400 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 401 mSession.skipToPlaylistItem(testMediaItem); 402 assertTrue(mMockAgent.mSkipToPlaylistItemCalled); 403 assertSame(testMediaItem, mMockAgent.mItem); 404 } 405 406 @Test 407 public void testGetPlayerState() { 408 prepareLooper(); 409 final int state = MediaPlayerBase.PLAYER_STATE_PLAYING; 410 mPlayer.mLastPlayerState = state; 411 assertEquals(state, mSession.getPlayerState()); 412 } 413 414 @Test 415 public void testGetPosition() { 416 prepareLooper(); 417 final long position = 150000; 418 mPlayer.mCurrentPosition = position; 419 assertEquals(position, mSession.getCurrentPosition()); 420 } 421 422 @Test 423 public void testGetBufferedPosition() { 424 prepareLooper(); 425 final long bufferedPosition = 900000; 426 mPlayer.mBufferedPosition = bufferedPosition; 427 assertEquals(bufferedPosition, mSession.getBufferedPosition()); 428 } 429 430 @Test 431 public void testSetPlaylist() { 432 prepareLooper(); 433 final List<MediaItem2> list = TestUtils.createPlaylist(2); 434 mSession.setPlaylist(list, null); 435 assertTrue(mMockAgent.mSetPlaylistCalled); 436 assertSame(list, mMockAgent.mPlaylist); 437 assertNull(mMockAgent.mMetadata); 438 } 439 440 @Test 441 public void testGetPlaylist() { 442 prepareLooper(); 443 final List<MediaItem2> list = TestUtils.createPlaylist(2); 444 mMockAgent.mPlaylist = list; 445 assertEquals(list, mSession.getPlaylist()); 446 } 447 448 @Test 449 public void testUpdatePlaylistMetadata() { 450 prepareLooper(); 451 final MediaMetadata2 testMetadata = TestUtils.createMetadata(); 452 mSession.updatePlaylistMetadata(testMetadata); 453 assertTrue(mMockAgent.mUpdatePlaylistMetadataCalled); 454 assertSame(testMetadata, mMockAgent.mMetadata); 455 } 456 457 @Test 458 public void testGetPlaylistMetadata() { 459 prepareLooper(); 460 final MediaMetadata2 testMetadata = TestUtils.createMetadata(); 461 mMockAgent.mMetadata = testMetadata; 462 assertEquals(testMetadata, mSession.getPlaylistMetadata()); 463 } 464 465 @Test 466 public void testSessionCallback_onPlaylistChanged() throws InterruptedException { 467 prepareLooper(); 468 final List<MediaItem2> list = TestUtils.createPlaylist(2); 469 final CountDownLatch latch = new CountDownLatch(1); 470 mMockAgent.setPlaylist(list, null); 471 472 final SessionCallback sessionCallback = new SessionCallback() { 473 @Override 474 public void onPlaylistChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent, 475 List<MediaItem2> playlist, MediaMetadata2 metadata) { 476 assertEquals(mMockAgent, playlistAgent); 477 assertEquals(list, playlist); 478 assertNull(metadata); 479 latch.countDown(); 480 } 481 }; 482 try (MediaSession2 session = new MediaSession2.Builder(mContext) 483 .setPlayer(mPlayer) 484 .setPlaylistAgent(mMockAgent) 485 .setId("testSessionCallback") 486 .setSessionCallback(sHandlerExecutor, sessionCallback) 487 .build()) { 488 mMockAgent.notifyPlaylistChanged(); 489 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 490 } 491 } 492 493 @Test 494 public void testAddPlaylistItem() { 495 prepareLooper(); 496 final int testIndex = 12; 497 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 498 mSession.addPlaylistItem(testIndex, testMediaItem); 499 assertTrue(mMockAgent.mAddPlaylistItemCalled); 500 assertEquals(testIndex, mMockAgent.mIndex); 501 assertSame(testMediaItem, mMockAgent.mItem); 502 } 503 504 @Test 505 public void testRemovePlaylistItem() { 506 prepareLooper(); 507 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 508 mSession.removePlaylistItem(testMediaItem); 509 assertTrue(mMockAgent.mRemovePlaylistItemCalled); 510 assertSame(testMediaItem, mMockAgent.mItem); 511 } 512 513 @Test 514 public void testReplacePlaylistItem() throws InterruptedException { 515 prepareLooper(); 516 final int testIndex = 12; 517 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 518 mSession.replacePlaylistItem(testIndex, testMediaItem); 519 assertTrue(mMockAgent.mReplacePlaylistItemCalled); 520 assertEquals(testIndex, mMockAgent.mIndex); 521 assertSame(testMediaItem, mMockAgent.mItem); 522 } 523 524 /** 525 * This also tests {@link SessionCallback#onShuffleModeChanged} 526 */ 527 @Test 528 public void testGetShuffleMode() throws InterruptedException { 529 prepareLooper(); 530 final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP; 531 mMockAgent.setShuffleMode(testShuffleMode); 532 533 final CountDownLatch latch = new CountDownLatch(1); 534 final SessionCallback sessionCallback = new SessionCallback() { 535 @Override 536 public void onShuffleModeChanged(MediaSession2 session, 537 MediaPlaylistAgent playlistAgent, int shuffleMode) { 538 assertEquals(mMockAgent, playlistAgent); 539 assertEquals(testShuffleMode, shuffleMode); 540 latch.countDown(); 541 } 542 }; 543 try (MediaSession2 session = new MediaSession2.Builder(mContext) 544 .setPlayer(mPlayer) 545 .setPlaylistAgent(mMockAgent) 546 .setId("testGetShuffleMode") 547 .setSessionCallback(sHandlerExecutor, sessionCallback) 548 .build()) { 549 mMockAgent.notifyShuffleModeChanged(); 550 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 551 } 552 } 553 554 @Test 555 public void testSetShuffleMode() { 556 prepareLooper(); 557 final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP; 558 mSession.setShuffleMode(testShuffleMode); 559 assertTrue(mMockAgent.mSetShuffleModeCalled); 560 assertEquals(testShuffleMode, mMockAgent.mShuffleMode); 561 } 562 563 /** 564 * This also tests {@link SessionCallback#onShuffleModeChanged} 565 */ 566 @Test 567 public void testGetRepeatMode() throws InterruptedException { 568 prepareLooper(); 569 final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP; 570 mMockAgent.setRepeatMode(testRepeatMode); 571 572 final CountDownLatch latch = new CountDownLatch(1); 573 final SessionCallback sessionCallback = new SessionCallback() { 574 @Override 575 public void onRepeatModeChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent, 576 int repeatMode) { 577 assertEquals(mMockAgent, playlistAgent); 578 assertEquals(testRepeatMode, repeatMode); 579 latch.countDown(); 580 } 581 }; 582 try (MediaSession2 session = new MediaSession2.Builder(mContext) 583 .setPlayer(mPlayer) 584 .setPlaylistAgent(mMockAgent) 585 .setId("testGetRepeatMode") 586 .setSessionCallback(sHandlerExecutor, sessionCallback) 587 .build()) { 588 mMockAgent.notifyRepeatModeChanged(); 589 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 590 } 591 } 592 593 @Test 594 public void testSetRepeatMode() { 595 prepareLooper(); 596 final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP; 597 mSession.setRepeatMode(testRepeatMode); 598 assertTrue(mMockAgent.mSetRepeatModeCalled); 599 assertEquals(testRepeatMode, mMockAgent.mRepeatMode); 600 } 601 602 // TODO(jaewan): Revisit 603 @Ignore 604 @Test 605 public void testBadPlayer() throws InterruptedException { 606 prepareLooper(); 607 // TODO(jaewan): Add equivalent tests again 608 final CountDownLatch latch = new CountDownLatch(4); // expected call + 1 609 final BadPlayer player = new BadPlayer(0); 610 611 mSession.updatePlayer(player, null, null); 612 mSession.updatePlayer(mPlayer, null, null); 613 player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PAUSED); 614 assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 615 } 616 617 // This bad player will keep push events to the listener that is previously 618 // registered by session.setPlayer(). 619 private static class BadPlayer extends MockPlayer { 620 BadPlayer(int count) { 621 super(count); 622 } 623 624 @Override 625 public void unregisterPlayerEventCallback( 626 @NonNull MediaPlayerBase.PlayerEventCallback listener) { 627 // No-op. 628 } 629 } 630 631 @Test 632 public void testOnCommandCallback() throws InterruptedException { 633 prepareLooper(); 634 final MockOnCommandCallback callback = new MockOnCommandCallback(); 635 sHandler.postAndSync(new Runnable() { 636 @Override 637 public void run() { 638 mSession.close(); 639 mPlayer = new MockPlayer(1); 640 mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer) 641 .setSessionCallback(sHandlerExecutor, callback).build(); 642 } 643 }); 644 MediaController2 controller = createController(mSession.getToken()); 645 controller.pause(); 646 assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 647 assertFalse(mPlayer.mPauseCalled); 648 assertEquals(1, callback.commands.size()); 649 assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE, 650 (long) callback.commands.get(0).getCommandCode()); 651 652 controller.play(); 653 assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 654 assertTrue(mPlayer.mPlayCalled); 655 assertFalse(mPlayer.mPauseCalled); 656 assertEquals(2, callback.commands.size()); 657 assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY, 658 (long) callback.commands.get(1).getCommandCode()); 659 } 660 661 @Test 662 public void testOnConnectCallback() throws InterruptedException { 663 prepareLooper(); 664 final MockOnConnectCallback sessionCallback = new MockOnConnectCallback(); 665 sHandler.postAndSync(new Runnable() { 666 @Override 667 public void run() { 668 mSession.close(); 669 mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer) 670 .setSessionCallback(sHandlerExecutor, sessionCallback).build(); 671 } 672 }); 673 MediaController2 controller = createController(mSession.getToken(), false, null); 674 assertNotNull(controller); 675 waitForConnect(controller, false); 676 waitForDisconnect(controller, true); 677 } 678 679 @Test 680 public void testOnDisconnectCallback() throws InterruptedException { 681 prepareLooper(); 682 final CountDownLatch latch = new CountDownLatch(1); 683 try (MediaSession2 session = new MediaSession2.Builder(mContext) 684 .setPlayer(mPlayer) 685 .setId("testOnDisconnectCallback") 686 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 687 @Override 688 public void onDisconnected(MediaSession2 session, 689 ControllerInfo controller) { 690 assertEquals(Process.myUid(), controller.getUid()); 691 latch.countDown(); 692 } 693 }).build()) { 694 MediaController2 controller = createController(session.getToken()); 695 controller.close(); 696 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 697 } 698 } 699 700 @Test 701 public void testSetCustomLayout() throws InterruptedException { 702 prepareLooper(); 703 final List<CommandButton> buttons = new ArrayList<>(); 704 buttons.add(new CommandButton.Builder() 705 .setCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY)) 706 .setDisplayName("button").build()); 707 final CountDownLatch latch = new CountDownLatch(1); 708 final SessionCallback sessionCallback = new SessionCallback() { 709 @Override 710 public SessionCommandGroup2 onConnect(MediaSession2 session, 711 ControllerInfo controller) { 712 if (mContext.getPackageName().equals(controller.getPackageName())) { 713 mSession.setCustomLayout(controller, buttons); 714 } 715 return super.onConnect(session, controller); 716 } 717 }; 718 719 try (MediaSession2 session = new MediaSession2.Builder(mContext) 720 .setPlayer(mPlayer) 721 .setId("testSetCustomLayout") 722 .setSessionCallback(sHandlerExecutor, sessionCallback) 723 .build()) { 724 if (mSession != null) { 725 mSession.close(); 726 mSession = session; 727 } 728 final ControllerCallback callback = new ControllerCallback() { 729 @Override 730 public void onCustomLayoutChanged(MediaController2 controller2, 731 List<CommandButton> layout) { 732 assertEquals(layout.size(), buttons.size()); 733 for (int i = 0; i < layout.size(); i++) { 734 assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand()); 735 assertEquals(layout.get(i).getDisplayName(), 736 buttons.get(i).getDisplayName()); 737 } 738 latch.countDown(); 739 } 740 }; 741 final MediaController2 controller = 742 createController(session.getToken(), true, callback); 743 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 744 } 745 } 746 747 @Test 748 public void testSetAllowedCommands() throws InterruptedException { 749 prepareLooper(); 750 final SessionCommandGroup2 commands = new SessionCommandGroup2(); 751 commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY)); 752 commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE)); 753 commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_RESET)); 754 755 final CountDownLatch latch = new CountDownLatch(1); 756 final ControllerCallback callback = new ControllerCallback() { 757 @Override 758 public void onAllowedCommandsChanged(MediaController2 controller, 759 SessionCommandGroup2 commandsOut) { 760 assertNotNull(commandsOut); 761 Set<SessionCommand2> expected = commands.getCommands(); 762 Set<SessionCommand2> actual = commandsOut.getCommands(); 763 764 assertNotNull(actual); 765 assertEquals(expected.size(), actual.size()); 766 for (SessionCommand2 command : expected) { 767 assertTrue(actual.contains(command)); 768 } 769 latch.countDown(); 770 } 771 }; 772 773 final MediaController2 controller = createController(mSession.getToken(), true, callback); 774 ControllerInfo controllerInfo = getTestControllerInfo(); 775 assertNotNull(controllerInfo); 776 777 mSession.setAllowedCommands(controllerInfo, commands); 778 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 779 } 780 781 @Test 782 public void testSendCustomCommand() throws InterruptedException { 783 prepareLooper(); 784 final SessionCommand2 testCommand = new SessionCommand2( 785 SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE); 786 final Bundle testArgs = new Bundle(); 787 testArgs.putString("args", "testSendCustomAction"); 788 789 final CountDownLatch latch = new CountDownLatch(2); 790 final ControllerCallback callback = new ControllerCallback() { 791 @Override 792 public void onCustomCommand(MediaController2 controller, SessionCommand2 command, 793 Bundle args, ResultReceiver receiver) { 794 assertEquals(testCommand, command); 795 assertTrue(TestUtils.equals(testArgs, args)); 796 assertNull(receiver); 797 latch.countDown(); 798 } 799 }; 800 final MediaController2 controller = 801 createController(mSession.getToken(), true, callback); 802 // TODO(jaewan): Test with multiple controllers 803 mSession.sendCustomCommand(testCommand, testArgs); 804 805 ControllerInfo controllerInfo = getTestControllerInfo(); 806 assertNotNull(controllerInfo); 807 // TODO(jaewan): Test receivers as well. 808 mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null); 809 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 810 } 811 812 @Test 813 public void testNotifyError() throws InterruptedException { 814 prepareLooper(); 815 final int errorCode = MediaSession2.ERROR_CODE_NOT_AVAILABLE_IN_REGION; 816 final Bundle extras = new Bundle(); 817 extras.putString("args", "testNotifyError"); 818 819 final CountDownLatch latch = new CountDownLatch(1); 820 final ControllerCallback callback = new ControllerCallback() { 821 @Override 822 public void onError(MediaController2 controller, int errorCodeOut, Bundle extrasOut) { 823 assertEquals(errorCode, errorCodeOut); 824 assertTrue(TestUtils.equals(extras, extrasOut)); 825 latch.countDown(); 826 } 827 }; 828 final MediaController2 controller = createController(mSession.getToken(), true, callback); 829 // TODO(jaewan): Test with multiple controllers 830 mSession.notifyError(errorCode, extras); 831 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 832 } 833 834 @Test 835 public void testNotifyRoutesInfoChanged() throws InterruptedException { 836 prepareLooper(); 837 final CountDownLatch latch = new CountDownLatch(1); 838 final ControllerCallback callback = new ControllerCallback() { 839 @Override 840 public void onRoutesInfoChanged(@NonNull MediaController2 controller, 841 @Nullable List<Bundle> routes) { 842 assertNull(routes); 843 latch.countDown(); 844 } 845 }; 846 final MediaController2 controller = createController(mSession.getToken(), true, callback); 847 ControllerInfo controllerInfo = getTestControllerInfo(); 848 mSession.notifyRoutesInfoChanged(controllerInfo, null); 849 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 850 } 851 852 private ControllerInfo getTestControllerInfo() { 853 List<ControllerInfo> controllers = mSession.getConnectedControllers(); 854 assertNotNull(controllers); 855 for (int i = 0; i < controllers.size(); i++) { 856 if (Process.myUid() == controllers.get(i).getUid()) { 857 return controllers.get(i); 858 } 859 } 860 fail("Failed to get test controller info"); 861 return null; 862 } 863 864 public class MockOnConnectCallback extends SessionCallback { 865 @Override 866 public SessionCommandGroup2 onConnect(MediaSession2 session, 867 ControllerInfo controllerInfo) { 868 if (Process.myUid() != controllerInfo.getUid()) { 869 return null; 870 } 871 assertEquals(mContext.getPackageName(), controllerInfo.getPackageName()); 872 assertEquals(Process.myUid(), controllerInfo.getUid()); 873 assertFalse(controllerInfo.isTrusted()); 874 // Reject all 875 return null; 876 } 877 } 878 879 public class MockOnCommandCallback extends SessionCallback { 880 public final ArrayList<SessionCommand2> commands = new ArrayList<>(); 881 882 @Override 883 public boolean onCommandRequest(MediaSession2 session, ControllerInfo controllerInfo, 884 SessionCommand2 command) { 885 assertEquals(mContext.getPackageName(), controllerInfo.getPackageName()); 886 assertEquals(Process.myUid(), controllerInfo.getUid()); 887 assertFalse(controllerInfo.isTrusted()); 888 commands.add(command); 889 if (command.getCommandCode() == SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE) { 890 return false; 891 } 892 return true; 893 } 894 } 895 896 private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) { 897 if (a == null || b == null) { 898 assertEquals(a, b); 899 } 900 assertEquals(a.size(), b.size()); 901 902 for (int i = 0; i < a.size(); i++) { 903 MediaItem2 aItem = a.get(i); 904 MediaItem2 bItem = b.get(i); 905 906 if (aItem == null || bItem == null) { 907 assertEquals(aItem, bItem); 908 continue; 909 } 910 911 assertEquals(aItem.getMediaId(), bItem.getMediaId()); 912 assertEquals(aItem.getFlags(), bItem.getFlags()); 913 TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle()); 914 915 // Note: Here it does not check whether DataSourceDesc are equal, 916 // since there DataSourceDec is not comparable. 917 } 918 } 919} 920