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