MediaSession2Test.java revision c227e5c8c58de8656a0a31a1f48bf66e49c4a19d
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(targetState); 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 testBufferingStateChange() throws Exception { 165 prepareLooper(); 166 final List<MediaItem2> playlist = TestUtils.createPlaylist(5); 167 168 final MediaItem2 targetItem = playlist.get(3); 169 final int targetBufferingState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE; 170 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 171 sHandler.postAndSync(new Runnable() { 172 @Override 173 public void run() { 174 mSession.close(); 175 mMockAgent.setPlaylist(playlist, null); 176 mSession = new MediaSession2.Builder(mContext) 177 .setPlayer(mPlayer) 178 .setPlaylistAgent(mMockAgent) 179 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 180 @Override 181 public void onBufferingStateChanged(MediaSession2 session, 182 MediaPlayerBase player, MediaItem2 item, int state) { 183 assertEquals(targetItem, item); 184 assertEquals(targetBufferingState, state); 185 latchForSessionCallback.countDown(); 186 } 187 }).build(); 188 } 189 }); 190 191 final CountDownLatch latchForControllerCallback = new CountDownLatch(1); 192 final MediaController2 controller = 193 createController(mSession.getToken(), true, new ControllerCallback() { 194 @Override 195 public void onBufferingStateChanged(MediaController2 controller, 196 MediaItem2 item, int state) { 197 assertEquals(targetItem, item); 198 assertEquals(targetBufferingState, state); 199 latchForControllerCallback.countDown(); 200 } 201 }); 202 203 mPlayer.notifyBufferingStateChanged(targetItem.getDataSourceDesc(), targetBufferingState); 204 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 205 assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 206 assertEquals(targetBufferingState, controller.getBufferingState()); 207 } 208 209 @Test 210 public void testSeekCompleted() throws Exception { 211 prepareLooper(); 212 final long testPosition = 1001; 213 final CountDownLatch latch = new CountDownLatch(1); 214 final SessionCallback callback = new SessionCallback() { 215 @Override 216 public void onSeekCompleted(MediaSession2 session, MediaPlayerBase mpb, long position) { 217 assertEquals(mPlayer, mpb); 218 assertEquals(testPosition, position); 219 latch.countDown(); 220 } 221 }; 222 223 try (MediaSession2 session = new MediaSession2.Builder(mContext) 224 .setPlayer(mPlayer) 225 .setPlaylistAgent(mMockAgent) 226 .setId("testSeekCompleted") 227 .setSessionCallback(sHandlerExecutor, callback).build()) { 228 mPlayer.mCurrentPosition = testPosition; 229 mPlayer.notifySeekCompleted(testPosition); 230 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 231 } 232 } 233 234 @Test 235 public void testCurrentDataSourceChanged() throws Exception { 236 prepareLooper(); 237 final int listSize = 5; 238 final List<MediaItem2> list = TestUtils.createPlaylist(listSize); 239 mMockAgent.setPlaylist(list, null); 240 241 final MediaItem2 currentItem = list.get(3); 242 mMockAgent.mCurrentMediaItem = currentItem; 243 244 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 245 try (MediaSession2 session = new MediaSession2.Builder(mContext) 246 .setPlayer(mPlayer) 247 .setPlaylistAgent(mMockAgent) 248 .setId("testCurrentDataSourceChanged") 249 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 250 @Override 251 public void onCurrentMediaItemChanged(MediaSession2 session, 252 MediaPlayerBase player, MediaItem2 itemOut) { 253 assertSame(currentItem, itemOut); 254 latchForSessionCallback.countDown(); 255 } 256 }).build()) { 257 258 final CountDownLatch latchForControllerCallback = new CountDownLatch(1); 259 final MediaController2 controller = 260 createController(mSession.getToken(), true, new ControllerCallback() { 261 @Override 262 public void onCurrentMediaItemChanged(MediaController2 controller, 263 MediaItem2 item) { 264 assertEquals(currentItem, item); 265 latchForControllerCallback.countDown(); 266 } 267 }); 268 269 mPlayer.notifyCurrentDataSourceChanged(currentItem.getDataSourceDesc()); 270 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 271 assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 272 assertEquals(currentItem, controller.getCurrentMediaItem()); 273 } 274 } 275 276 @Test 277 public void testMediaPrepared() throws Exception { 278 prepareLooper(); 279 final int listSize = 5; 280 final List<MediaItem2> list = TestUtils.createPlaylist(listSize); 281 mMockAgent.setPlaylist(list, null); 282 283 final MediaItem2 currentItem = list.get(3); 284 285 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 286 try (MediaSession2 session = new MediaSession2.Builder(mContext) 287 .setPlayer(mPlayer) 288 .setPlaylistAgent(mMockAgent) 289 .setId("testMediaPrepared") 290 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 291 @Override 292 public void onMediaPrepared(MediaSession2 session, MediaPlayerBase player, 293 MediaItem2 itemOut) { 294 assertSame(currentItem, itemOut); 295 latchForSessionCallback.countDown(); 296 } 297 }).build()) { 298 299 mPlayer.notifyMediaPrepared(currentItem.getDataSourceDesc()); 300 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 301 // TODO(jaewan): Test that controllers are also notified. (b/74505936) 302 } 303 } 304 305 @Test 306 public void testBufferingStateChanged() throws Exception { 307 prepareLooper(); 308 final int listSize = 5; 309 final List<MediaItem2> list = TestUtils.createPlaylist(listSize); 310 mMockAgent.setPlaylist(list, null); 311 312 final MediaItem2 currentItem = list.get(3); 313 final int buffState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE; 314 315 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 316 try (MediaSession2 session = new MediaSession2.Builder(mContext) 317 .setPlayer(mPlayer) 318 .setPlaylistAgent(mMockAgent) 319 .setId("testBufferingStateChanged") 320 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 321 @Override 322 public void onBufferingStateChanged(MediaSession2 session, 323 MediaPlayerBase player, MediaItem2 itemOut, int stateOut) { 324 assertSame(currentItem, itemOut); 325 assertEquals(buffState, stateOut); 326 latchForSessionCallback.countDown(); 327 } 328 }).build()) { 329 330 mPlayer.notifyBufferingStateChanged(currentItem.getDataSourceDesc(), buffState); 331 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 332 // TODO(jaewan): Test that controllers are also notified. (b/74505936) 333 } 334 } 335 336 /** 337 * This also tests {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)} 338 * and {@link MediaController2#getPlaybackSpeed()}. 339 */ 340 @Test 341 public void testPlaybackSpeedChanged() throws Exception { 342 prepareLooper(); 343 final float speed = 1.5f; 344 mPlayer.setPlaybackSpeed(speed); 345 346 final CountDownLatch latchForSessionCallback = new CountDownLatch(1); 347 try (MediaSession2 session = new MediaSession2.Builder(mContext) 348 .setPlayer(mPlayer) 349 .setId("testPlaybackSpeedChanged") 350 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 351 @Override 352 public void onPlaybackSpeedChanged(MediaSession2 session, 353 MediaPlayerBase player, float speedOut) { 354 assertEquals(speed, speedOut, 0.0f); 355 latchForSessionCallback.countDown(); 356 } 357 }).build()) { 358 359 final CountDownLatch latchForControllerCallback = new CountDownLatch(1); 360 final MediaController2 controller = 361 createController(mSession.getToken(), true, new ControllerCallback() { 362 @Override 363 public void onPlaybackSpeedChanged(MediaController2 controller, 364 float speedOut) { 365 assertEquals(speed, speedOut, 0.0f); 366 latchForControllerCallback.countDown(); 367 } 368 }); 369 370 mPlayer.notifyPlaybackSpeedChanged(speed); 371 assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 372 assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 373 assertEquals(speed, controller.getPlaybackSpeed(), 0.0f); 374 } 375 } 376 377 @Test 378 public void testUpdatePlayer() throws Exception { 379 prepareLooper(); 380 final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING; 381 final CountDownLatch latch = new CountDownLatch(1); 382 sHandler.postAndSync(new Runnable() { 383 @Override 384 public void run() { 385 mSession.close(); 386 mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer) 387 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 388 @Override 389 public void onPlayerStateChanged(MediaSession2 session, 390 MediaPlayerBase player, int state) { 391 assertEquals(targetState, state); 392 latch.countDown(); 393 } 394 }).build(); 395 } 396 }); 397 398 MockPlayer player = new MockPlayer(0); 399 400 // Test if setPlayer doesn't crash with various situations. 401 mSession.updatePlayer(mPlayer, null, null); 402 assertEquals(mPlayer, mSession.getPlayer()); 403 MediaPlaylistAgent agent = mSession.getPlaylistAgent(); 404 assertNotNull(agent); 405 406 mSession.updatePlayer(player, null, null); 407 assertEquals(player, mSession.getPlayer()); 408 assertNotNull(mSession.getPlaylistAgent()); 409 assertNotEquals(agent, mSession.getPlaylistAgent()); 410 411 player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING); 412 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 413 } 414 415 @Test 416 public void testUpdatePlayer_playbackInfo() throws Exception { 417 prepareLooper(); 418 MockPlayer player = new MockPlayer(0); 419 final AudioAttributesCompat attrs = new AudioAttributesCompat.Builder() 420 .setContentType(CONTENT_TYPE_MUSIC) 421 .build(); 422 player.setAudioAttributes(attrs); 423 424 final int maxVolume = 100; 425 final int currentVolume = 23; 426 final int volumeControlType = VOLUME_CONTROL_ABSOLUTE; 427 VolumeProviderCompat volumeProvider = new VolumeProviderCompat( 428 volumeControlType, maxVolume, currentVolume) { }; 429 430 final CountDownLatch latch = new CountDownLatch(1); 431 final ControllerCallback callback = new ControllerCallback() { 432 @Override 433 public void onPlaybackInfoChanged(MediaController2 controller, PlaybackInfo info) { 434 Assert.assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType()); 435 assertEquals(attrs, info.getAudioAttributes()); 436 assertEquals(volumeControlType, info.getPlaybackType()); 437 assertEquals(maxVolume, info.getMaxVolume()); 438 assertEquals(currentVolume, info.getCurrentVolume()); 439 latch.countDown(); 440 } 441 }; 442 443 mSession.updatePlayer(player, null, null); 444 445 final MediaController2 controller = createController(mSession.getToken(), true, callback); 446 PlaybackInfo info = controller.getPlaybackInfo(); 447 assertNotNull(info); 448 assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType()); 449 assertEquals(attrs, info.getAudioAttributes()); 450 AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE); 451 452 int localVolumeControlType = VOLUME_CONTROL_ABSOLUTE; 453 if (Build.VERSION.SDK_INT >= 21 && manager.isVolumeFixed()) { 454 localVolumeControlType = VOLUME_CONTROL_FIXED; 455 } 456 assertEquals(localVolumeControlType, info.getControlType()); 457 assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume()); 458 assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume()); 459 460 mSession.updatePlayer(player, null, volumeProvider); 461 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 462 463 info = controller.getPlaybackInfo(); 464 assertNotNull(info); 465 assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType()); 466 assertEquals(attrs, info.getAudioAttributes()); 467 assertEquals(volumeControlType, info.getControlType()); 468 assertEquals(maxVolume, info.getMaxVolume()); 469 assertEquals(currentVolume, info.getCurrentVolume()); 470 } 471 472 @Test 473 public void testPlay() throws Exception { 474 prepareLooper(); 475 mSession.play(); 476 assertTrue(mPlayer.mPlayCalled); 477 } 478 479 @Test 480 public void testPause() throws Exception { 481 prepareLooper(); 482 mSession.pause(); 483 assertTrue(mPlayer.mPauseCalled); 484 } 485 486 @Test 487 public void testReset() throws Exception { 488 prepareLooper(); 489 mSession.reset(); 490 assertTrue(mPlayer.mResetCalled); 491 } 492 493 @Test 494 public void testPrepare() throws Exception { 495 prepareLooper(); 496 mSession.prepare(); 497 assertTrue(mPlayer.mPrepareCalled); 498 } 499 500 @Test 501 public void testSeekTo() throws Exception { 502 prepareLooper(); 503 final long pos = 1004L; 504 mSession.seekTo(pos); 505 assertTrue(mPlayer.mSeekToCalled); 506 assertEquals(pos, mPlayer.mSeekPosition); 507 } 508 509 @Test 510 public void testSetPlaybackSpeed() throws Exception { 511 prepareLooper(); 512 final float speed = 1.5f; 513 mSession.setPlaybackSpeed(speed); 514 assertTrue(mPlayer.mSetPlaybackSpeedCalled); 515 assertEquals(speed, mPlayer.mPlaybackSpeed, 0.0f); 516 } 517 518 @Test 519 public void testGetPlaybackSpeed() throws Exception { 520 prepareLooper(); 521 final float speed = 1.5f; 522 mPlayer.setPlaybackSpeed(speed); 523 assertEquals(speed, mSession.getPlaybackSpeed(), 0.0f); 524 } 525 526 @Test 527 public void testGetCurrentMediaItem() { 528 prepareLooper(); 529 MediaItem2 item = TestUtils.createMediaItemWithMetadata(); 530 mMockAgent.mCurrentMediaItem = item; 531 assertEquals(item, mSession.getCurrentMediaItem()); 532 } 533 534 @Test 535 public void testSkipToPreviousItem() { 536 prepareLooper(); 537 mSession.skipToPreviousItem(); 538 assertTrue(mMockAgent.mSkipToPreviousItemCalled); 539 } 540 541 @Test 542 public void testSkipToNextItem() throws Exception { 543 prepareLooper(); 544 mSession.skipToNextItem(); 545 assertTrue(mMockAgent.mSkipToNextItemCalled); 546 } 547 548 @Test 549 public void testSkipToPlaylistItem() throws Exception { 550 prepareLooper(); 551 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 552 mSession.skipToPlaylistItem(testMediaItem); 553 assertTrue(mMockAgent.mSkipToPlaylistItemCalled); 554 assertSame(testMediaItem, mMockAgent.mItem); 555 } 556 557 @Test 558 public void testGetPlayerState() { 559 prepareLooper(); 560 final int state = MediaPlayerBase.PLAYER_STATE_PLAYING; 561 mPlayer.mLastPlayerState = state; 562 assertEquals(state, mSession.getPlayerState()); 563 } 564 565 @Test 566 public void testGetBufferingState() { 567 prepareLooper(); 568 final int bufferingState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE; 569 mPlayer.mLastBufferingState = bufferingState; 570 assertEquals(bufferingState, mSession.getBufferingState()); 571 } 572 573 @Test 574 public void testGetPosition() { 575 prepareLooper(); 576 final long position = 150000; 577 mPlayer.mCurrentPosition = position; 578 assertEquals(position, mSession.getCurrentPosition()); 579 } 580 581 @Test 582 public void testGetBufferedPosition() { 583 prepareLooper(); 584 final long bufferedPosition = 900000; 585 mPlayer.mBufferedPosition = bufferedPosition; 586 assertEquals(bufferedPosition, mSession.getBufferedPosition()); 587 } 588 589 @Test 590 public void testSetPlaylist() { 591 prepareLooper(); 592 final List<MediaItem2> list = TestUtils.createPlaylist(2); 593 mSession.setPlaylist(list, null); 594 assertTrue(mMockAgent.mSetPlaylistCalled); 595 assertSame(list, mMockAgent.mPlaylist); 596 assertNull(mMockAgent.mMetadata); 597 } 598 599 @Test 600 public void testGetPlaylist() { 601 prepareLooper(); 602 final List<MediaItem2> list = TestUtils.createPlaylist(2); 603 mMockAgent.mPlaylist = list; 604 assertEquals(list, mSession.getPlaylist()); 605 } 606 607 @Test 608 public void testUpdatePlaylistMetadata() { 609 prepareLooper(); 610 final MediaMetadata2 testMetadata = TestUtils.createMetadata(); 611 mSession.updatePlaylistMetadata(testMetadata); 612 assertTrue(mMockAgent.mUpdatePlaylistMetadataCalled); 613 assertSame(testMetadata, mMockAgent.mMetadata); 614 } 615 616 @Test 617 public void testGetPlaylistMetadata() { 618 prepareLooper(); 619 final MediaMetadata2 testMetadata = TestUtils.createMetadata(); 620 mMockAgent.mMetadata = testMetadata; 621 assertEquals(testMetadata, mSession.getPlaylistMetadata()); 622 } 623 624 @Test 625 public void testSessionCallback_onPlaylistChanged() throws InterruptedException { 626 prepareLooper(); 627 final List<MediaItem2> list = TestUtils.createPlaylist(2); 628 final CountDownLatch latch = new CountDownLatch(1); 629 mMockAgent.setPlaylist(list, null); 630 631 final SessionCallback sessionCallback = new SessionCallback() { 632 @Override 633 public void onPlaylistChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent, 634 List<MediaItem2> playlist, MediaMetadata2 metadata) { 635 assertEquals(mMockAgent, playlistAgent); 636 assertEquals(list, playlist); 637 assertNull(metadata); 638 latch.countDown(); 639 } 640 }; 641 try (MediaSession2 session = new MediaSession2.Builder(mContext) 642 .setPlayer(mPlayer) 643 .setPlaylistAgent(mMockAgent) 644 .setId("testSessionCallback") 645 .setSessionCallback(sHandlerExecutor, sessionCallback) 646 .build()) { 647 mMockAgent.notifyPlaylistChanged(); 648 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 649 } 650 } 651 652 @Test 653 public void testAddPlaylistItem() { 654 prepareLooper(); 655 final int testIndex = 12; 656 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 657 mSession.addPlaylistItem(testIndex, testMediaItem); 658 assertTrue(mMockAgent.mAddPlaylistItemCalled); 659 assertEquals(testIndex, mMockAgent.mIndex); 660 assertSame(testMediaItem, mMockAgent.mItem); 661 } 662 663 @Test 664 public void testRemovePlaylistItem() { 665 prepareLooper(); 666 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 667 mSession.removePlaylistItem(testMediaItem); 668 assertTrue(mMockAgent.mRemovePlaylistItemCalled); 669 assertSame(testMediaItem, mMockAgent.mItem); 670 } 671 672 @Test 673 public void testReplacePlaylistItem() throws InterruptedException { 674 prepareLooper(); 675 final int testIndex = 12; 676 final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata(); 677 mSession.replacePlaylistItem(testIndex, testMediaItem); 678 assertTrue(mMockAgent.mReplacePlaylistItemCalled); 679 assertEquals(testIndex, mMockAgent.mIndex); 680 assertSame(testMediaItem, mMockAgent.mItem); 681 } 682 683 /** 684 * This also tests {@link SessionCallback#onShuffleModeChanged} 685 */ 686 @Test 687 public void testGetShuffleMode() throws InterruptedException { 688 prepareLooper(); 689 final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP; 690 mMockAgent.setShuffleMode(testShuffleMode); 691 692 final CountDownLatch latch = new CountDownLatch(1); 693 final SessionCallback sessionCallback = new SessionCallback() { 694 @Override 695 public void onShuffleModeChanged(MediaSession2 session, 696 MediaPlaylistAgent playlistAgent, int shuffleMode) { 697 assertEquals(mMockAgent, playlistAgent); 698 assertEquals(testShuffleMode, shuffleMode); 699 latch.countDown(); 700 } 701 }; 702 try (MediaSession2 session = new MediaSession2.Builder(mContext) 703 .setPlayer(mPlayer) 704 .setPlaylistAgent(mMockAgent) 705 .setId("testGetShuffleMode") 706 .setSessionCallback(sHandlerExecutor, sessionCallback) 707 .build()) { 708 mMockAgent.notifyShuffleModeChanged(); 709 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 710 } 711 } 712 713 @Test 714 public void testSetShuffleMode() { 715 prepareLooper(); 716 final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP; 717 mSession.setShuffleMode(testShuffleMode); 718 assertTrue(mMockAgent.mSetShuffleModeCalled); 719 assertEquals(testShuffleMode, mMockAgent.mShuffleMode); 720 } 721 722 /** 723 * This also tests {@link SessionCallback#onShuffleModeChanged} 724 */ 725 @Test 726 public void testGetRepeatMode() throws InterruptedException { 727 prepareLooper(); 728 final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP; 729 mMockAgent.setRepeatMode(testRepeatMode); 730 731 final CountDownLatch latch = new CountDownLatch(1); 732 final SessionCallback sessionCallback = new SessionCallback() { 733 @Override 734 public void onRepeatModeChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent, 735 int repeatMode) { 736 assertEquals(mMockAgent, playlistAgent); 737 assertEquals(testRepeatMode, repeatMode); 738 latch.countDown(); 739 } 740 }; 741 try (MediaSession2 session = new MediaSession2.Builder(mContext) 742 .setPlayer(mPlayer) 743 .setPlaylistAgent(mMockAgent) 744 .setId("testGetRepeatMode") 745 .setSessionCallback(sHandlerExecutor, sessionCallback) 746 .build()) { 747 mMockAgent.notifyRepeatModeChanged(); 748 assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 749 } 750 } 751 752 @Test 753 public void testSetRepeatMode() { 754 prepareLooper(); 755 final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP; 756 mSession.setRepeatMode(testRepeatMode); 757 assertTrue(mMockAgent.mSetRepeatModeCalled); 758 assertEquals(testRepeatMode, mMockAgent.mRepeatMode); 759 } 760 761 // TODO(jaewan): Revisit 762 @Ignore 763 @Test 764 public void testBadPlayer() throws InterruptedException { 765 prepareLooper(); 766 // TODO(jaewan): Add equivalent tests again 767 final CountDownLatch latch = new CountDownLatch(4); // expected call + 1 768 final BadPlayer player = new BadPlayer(0); 769 770 mSession.updatePlayer(player, null, null); 771 mSession.updatePlayer(mPlayer, null, null); 772 player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PAUSED); 773 assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 774 } 775 776 // This bad player will keep push events to the listener that is previously 777 // registered by session.setPlayer(). 778 private static class BadPlayer extends MockPlayer { 779 BadPlayer(int count) { 780 super(count); 781 } 782 783 @Override 784 public void unregisterPlayerEventCallback( 785 @NonNull MediaPlayerBase.PlayerEventCallback listener) { 786 // No-op. 787 } 788 } 789 790 @Test 791 public void testOnCommandCallback() throws InterruptedException { 792 prepareLooper(); 793 final MockOnCommandCallback callback = new MockOnCommandCallback(); 794 sHandler.postAndSync(new Runnable() { 795 @Override 796 public void run() { 797 mSession.close(); 798 mPlayer = new MockPlayer(1); 799 mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer) 800 .setSessionCallback(sHandlerExecutor, callback).build(); 801 } 802 }); 803 MediaController2 controller = createController(mSession.getToken()); 804 controller.pause(); 805 assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 806 assertFalse(mPlayer.mPauseCalled); 807 assertEquals(1, callback.commands.size()); 808 assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE, 809 (long) callback.commands.get(0).getCommandCode()); 810 811 controller.play(); 812 assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 813 assertTrue(mPlayer.mPlayCalled); 814 assertFalse(mPlayer.mPauseCalled); 815 assertEquals(2, callback.commands.size()); 816 assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY, 817 (long) callback.commands.get(1).getCommandCode()); 818 } 819 820 @Test 821 public void testOnConnectCallback() throws InterruptedException { 822 prepareLooper(); 823 final MockOnConnectCallback sessionCallback = new MockOnConnectCallback(); 824 sHandler.postAndSync(new Runnable() { 825 @Override 826 public void run() { 827 mSession.close(); 828 mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer) 829 .setSessionCallback(sHandlerExecutor, sessionCallback).build(); 830 } 831 }); 832 MediaController2 controller = createController(mSession.getToken(), false, null); 833 assertNotNull(controller); 834 waitForConnect(controller, false); 835 waitForDisconnect(controller, true); 836 } 837 838 @Test 839 public void testOnDisconnectCallback() throws InterruptedException { 840 prepareLooper(); 841 final CountDownLatch latch = new CountDownLatch(1); 842 try (MediaSession2 session = new MediaSession2.Builder(mContext) 843 .setPlayer(mPlayer) 844 .setId("testOnDisconnectCallback") 845 .setSessionCallback(sHandlerExecutor, new SessionCallback() { 846 @Override 847 public void onDisconnected(MediaSession2 session, 848 ControllerInfo controller) { 849 assertEquals(Process.myUid(), controller.getUid()); 850 latch.countDown(); 851 } 852 }).build()) { 853 MediaController2 controller = createController(session.getToken()); 854 controller.close(); 855 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 856 } 857 } 858 859 @Test 860 public void testSetCustomLayout() throws InterruptedException { 861 prepareLooper(); 862 final List<CommandButton> buttons = new ArrayList<>(); 863 buttons.add(new CommandButton.Builder() 864 .setCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY)) 865 .setDisplayName("button").build()); 866 final CountDownLatch latch = new CountDownLatch(1); 867 final SessionCallback sessionCallback = new SessionCallback() { 868 @Override 869 public SessionCommandGroup2 onConnect(MediaSession2 session, 870 ControllerInfo controller) { 871 if (mContext.getPackageName().equals(controller.getPackageName())) { 872 mSession.setCustomLayout(controller, buttons); 873 } 874 return super.onConnect(session, controller); 875 } 876 }; 877 878 try (MediaSession2 session = new MediaSession2.Builder(mContext) 879 .setPlayer(mPlayer) 880 .setId("testSetCustomLayout") 881 .setSessionCallback(sHandlerExecutor, sessionCallback) 882 .build()) { 883 if (mSession != null) { 884 mSession.close(); 885 mSession = session; 886 } 887 final ControllerCallback callback = new ControllerCallback() { 888 @Override 889 public void onCustomLayoutChanged(MediaController2 controller2, 890 List<CommandButton> layout) { 891 assertEquals(layout.size(), buttons.size()); 892 for (int i = 0; i < layout.size(); i++) { 893 assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand()); 894 assertEquals(layout.get(i).getDisplayName(), 895 buttons.get(i).getDisplayName()); 896 } 897 latch.countDown(); 898 } 899 }; 900 final MediaController2 controller = 901 createController(session.getToken(), true, callback); 902 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 903 } 904 } 905 906 @Test 907 public void testSetAllowedCommands() throws InterruptedException { 908 prepareLooper(); 909 final SessionCommandGroup2 commands = new SessionCommandGroup2(); 910 commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY)); 911 commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE)); 912 commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_RESET)); 913 914 final CountDownLatch latch = new CountDownLatch(1); 915 final ControllerCallback callback = new ControllerCallback() { 916 @Override 917 public void onAllowedCommandsChanged(MediaController2 controller, 918 SessionCommandGroup2 commandsOut) { 919 assertNotNull(commandsOut); 920 Set<SessionCommand2> expected = commands.getCommands(); 921 Set<SessionCommand2> actual = commandsOut.getCommands(); 922 923 assertNotNull(actual); 924 assertEquals(expected.size(), actual.size()); 925 for (SessionCommand2 command : expected) { 926 assertTrue(actual.contains(command)); 927 } 928 latch.countDown(); 929 } 930 }; 931 932 final MediaController2 controller = createController(mSession.getToken(), true, callback); 933 ControllerInfo controllerInfo = getTestControllerInfo(); 934 assertNotNull(controllerInfo); 935 936 mSession.setAllowedCommands(controllerInfo, commands); 937 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 938 } 939 940 @Test 941 public void testSendCustomCommand() throws InterruptedException { 942 prepareLooper(); 943 final SessionCommand2 testCommand = new SessionCommand2( 944 SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE); 945 final Bundle testArgs = new Bundle(); 946 testArgs.putString("args", "testSendCustomAction"); 947 948 final CountDownLatch latch = new CountDownLatch(2); 949 final ControllerCallback callback = new ControllerCallback() { 950 @Override 951 public void onCustomCommand(MediaController2 controller, SessionCommand2 command, 952 Bundle args, ResultReceiver receiver) { 953 assertEquals(testCommand, command); 954 assertTrue(TestUtils.equals(testArgs, args)); 955 assertNull(receiver); 956 latch.countDown(); 957 } 958 }; 959 final MediaController2 controller = 960 createController(mSession.getToken(), true, callback); 961 // TODO(jaewan): Test with multiple controllers 962 mSession.sendCustomCommand(testCommand, testArgs); 963 964 ControllerInfo controllerInfo = getTestControllerInfo(); 965 assertNotNull(controllerInfo); 966 // TODO(jaewan): Test receivers as well. 967 mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null); 968 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 969 } 970 971 @Test 972 public void testNotifyError() throws InterruptedException { 973 prepareLooper(); 974 final int errorCode = MediaSession2.ERROR_CODE_NOT_AVAILABLE_IN_REGION; 975 final Bundle extras = new Bundle(); 976 extras.putString("args", "testNotifyError"); 977 978 final CountDownLatch latch = new CountDownLatch(1); 979 final ControllerCallback callback = new ControllerCallback() { 980 @Override 981 public void onError(MediaController2 controller, int errorCodeOut, Bundle extrasOut) { 982 assertEquals(errorCode, errorCodeOut); 983 assertTrue(TestUtils.equals(extras, extrasOut)); 984 latch.countDown(); 985 } 986 }; 987 final MediaController2 controller = createController(mSession.getToken(), true, callback); 988 // TODO(jaewan): Test with multiple controllers 989 mSession.notifyError(errorCode, extras); 990 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 991 } 992 993 @Test 994 public void testNotifyRoutesInfoChanged() throws InterruptedException { 995 prepareLooper(); 996 final CountDownLatch latch = new CountDownLatch(1); 997 final ControllerCallback callback = new ControllerCallback() { 998 @Override 999 public void onRoutesInfoChanged(@NonNull MediaController2 controller, 1000 @Nullable List<Bundle> routes) { 1001 assertNull(routes); 1002 latch.countDown(); 1003 } 1004 }; 1005 final MediaController2 controller = createController(mSession.getToken(), true, callback); 1006 ControllerInfo controllerInfo = getTestControllerInfo(); 1007 mSession.notifyRoutesInfoChanged(controllerInfo, null); 1008 assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS)); 1009 } 1010 1011 private ControllerInfo getTestControllerInfo() { 1012 List<ControllerInfo> controllers = mSession.getConnectedControllers(); 1013 assertNotNull(controllers); 1014 for (int i = 0; i < controllers.size(); i++) { 1015 if (Process.myUid() == controllers.get(i).getUid()) { 1016 return controllers.get(i); 1017 } 1018 } 1019 fail("Failed to get test controller info"); 1020 return null; 1021 } 1022 1023 public class MockOnConnectCallback extends SessionCallback { 1024 @Override 1025 public SessionCommandGroup2 onConnect(MediaSession2 session, 1026 ControllerInfo controllerInfo) { 1027 if (Process.myUid() != controllerInfo.getUid()) { 1028 return null; 1029 } 1030 assertEquals(mContext.getPackageName(), controllerInfo.getPackageName()); 1031 assertEquals(Process.myUid(), controllerInfo.getUid()); 1032 assertFalse(controllerInfo.isTrusted()); 1033 // Reject all 1034 return null; 1035 } 1036 } 1037 1038 public class MockOnCommandCallback extends SessionCallback { 1039 public final ArrayList<SessionCommand2> commands = new ArrayList<>(); 1040 1041 @Override 1042 public boolean onCommandRequest(MediaSession2 session, ControllerInfo controllerInfo, 1043 SessionCommand2 command) { 1044 assertEquals(mContext.getPackageName(), controllerInfo.getPackageName()); 1045 assertEquals(Process.myUid(), controllerInfo.getUid()); 1046 assertFalse(controllerInfo.isTrusted()); 1047 commands.add(command); 1048 if (command.getCommandCode() == SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE) { 1049 return false; 1050 } 1051 return true; 1052 } 1053 } 1054 1055 private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) { 1056 if (a == null || b == null) { 1057 assertEquals(a, b); 1058 } 1059 assertEquals(a.size(), b.size()); 1060 1061 for (int i = 0; i < a.size(); i++) { 1062 MediaItem2 aItem = a.get(i); 1063 MediaItem2 bItem = b.get(i); 1064 1065 if (aItem == null || bItem == null) { 1066 assertEquals(aItem, bItem); 1067 continue; 1068 } 1069 1070 assertEquals(aItem.getMediaId(), bItem.getMediaId()); 1071 assertEquals(aItem.getFlags(), bItem.getFlags()); 1072 TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle()); 1073 1074 // Note: Here it does not check whether DataSourceDesc are equal, 1075 // since there DataSourceDec is not comparable. 1076 } 1077 } 1078} 1079