1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.media.cts; 17 18 import static android.media.AudioAttributes.USAGE_GAME; 19 import static android.media.cts.Utils.compareRemoteUserInfo; 20 21 import static org.junit.Assert.fail; 22 23 import android.app.PendingIntent; 24 import android.content.ComponentName; 25 import android.content.Context; 26 import android.content.Intent; 27 import android.media.AudioAttributes; 28 import android.media.AudioManager; 29 import android.media.MediaDescription; 30 import android.media.MediaMetadata; 31 import android.media.MediaSession2; 32 import android.media.Rating; 33 import android.media.VolumeProvider; 34 import android.media.session.MediaController; 35 import android.media.session.MediaSession; 36 import android.media.session.MediaSession.QueueItem; 37 import android.media.session.MediaSessionManager; 38 import android.media.session.MediaSessionManager.RemoteUserInfo; 39 import android.media.session.PlaybackState; 40 import android.os.Bundle; 41 import android.os.Handler; 42 import android.os.Looper; 43 import android.os.Parcel; 44 import android.os.Process; 45 import android.platform.test.annotations.AppModeFull; 46 import android.test.AndroidTestCase; 47 import android.view.KeyEvent; 48 49 import java.util.ArrayList; 50 import java.util.List; 51 import java.util.concurrent.CountDownLatch; 52 import java.util.concurrent.TimeUnit; 53 54 @NonMediaMainlineTest 55 @AppModeFull(reason = "TODO: evaluate and port to instant") 56 public class MediaSessionTest extends AndroidTestCase { 57 // The maximum time to wait for an operation that is expected to succeed. 58 private static final long TIME_OUT_MS = 3000L; 59 // The maximum time to wait for an operation that is expected to fail. 60 private static final long WAIT_MS = 100L; 61 private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10; 62 private static final String TEST_SESSION_TAG = "test-session-tag"; 63 private static final String TEST_KEY = "test-key"; 64 private static final String TEST_VALUE = "test-val"; 65 private static final String TEST_SESSION_EVENT = "test-session-event"; 66 private static final int TEST_CURRENT_VOLUME = 10; 67 private static final int TEST_MAX_VOLUME = 11; 68 private static final long TEST_QUEUE_ID = 12L; 69 private static final long TEST_ACTION = 55L; 70 71 private AudioManager mAudioManager; 72 private Handler mHandler = new Handler(Looper.getMainLooper()); 73 private Object mWaitLock = new Object(); 74 private MediaControllerCallback mCallback = new MediaControllerCallback(); 75 private MediaSession mSession; 76 private RemoteUserInfo mKeyDispatcherInfo; 77 78 @Override setUp()79 protected void setUp() throws Exception { 80 super.setUp(); 81 mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE); 82 mSession = new MediaSession(getContext(), TEST_SESSION_TAG); 83 mKeyDispatcherInfo = new MediaSessionManager.RemoteUserInfo( 84 getContext().getPackageName(), Process.myPid(), Process.myUid()); 85 } 86 87 @Override tearDown()88 protected void tearDown() throws Exception { 89 // It is OK to call release() twice. 90 mSession.release(); 91 super.tearDown(); 92 } 93 94 /** 95 * Tests that a session can be created and that all the fields are 96 * initialized correctly. 97 */ testCreateSession()98 public void testCreateSession() throws Exception { 99 assertNotNull(mSession.getSessionToken()); 100 assertFalse("New session should not be active", mSession.isActive()); 101 102 // Verify by getting the controller and checking all its fields 103 MediaController controller = mSession.getController(); 104 assertNotNull(controller); 105 verifyNewSession(controller); 106 } 107 108 /** 109 * Tests MediaSession.Token created in the constructor of MediaSession. 110 */ testSessionToken()111 public void testSessionToken() throws Exception { 112 MediaSession.Token sessionToken = mSession.getSessionToken(); 113 114 assertNotNull(sessionToken); 115 assertEquals(0, sessionToken.describeContents()); 116 117 // Test writeToParcel 118 Parcel p = Parcel.obtain(); 119 sessionToken.writeToParcel(p, 0); 120 p.setDataPosition(0); 121 MediaSession.Token token = MediaSession.Token.CREATOR.createFromParcel(p); 122 assertEquals(token, sessionToken); 123 p.recycle(); 124 } 125 126 /** 127 * Tests that the various configuration bits on a session get passed to the 128 * controller. 129 */ testConfigureSession()130 public void testConfigureSession() throws Exception { 131 MediaController controller = mSession.getController(); 132 controller.registerCallback(mCallback, mHandler); 133 final MediaController.Callback callback = (MediaController.Callback) mCallback; 134 135 synchronized (mWaitLock) { 136 // test setExtras 137 mCallback.resetLocked(); 138 final Bundle extras = new Bundle(); 139 extras.putString(TEST_KEY, TEST_VALUE); 140 mSession.setExtras(extras); 141 mWaitLock.wait(TIME_OUT_MS); 142 assertTrue(mCallback.mOnExtraChangedCalled); 143 // just call the callback once directly so it's marked as tested 144 callback.onExtrasChanged(mCallback.mExtras); 145 146 Bundle extrasOut = mCallback.mExtras; 147 assertNotNull(extrasOut); 148 assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY)); 149 150 extrasOut = controller.getExtras(); 151 assertNotNull(extrasOut); 152 assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY)); 153 154 // test setFlags 155 mSession.setFlags(5); 156 assertEquals(5, controller.getFlags()); 157 158 // test setMetadata 159 mCallback.resetLocked(); 160 MediaMetadata metadata = 161 new MediaMetadata.Builder().putString(TEST_KEY, TEST_VALUE).build(); 162 mSession.setMetadata(metadata); 163 mWaitLock.wait(TIME_OUT_MS); 164 assertTrue(mCallback.mOnMetadataChangedCalled); 165 // just call the callback once directly so it's marked as tested 166 callback.onMetadataChanged(mCallback.mMediaMetadata); 167 168 MediaMetadata metadataOut = mCallback.mMediaMetadata; 169 assertNotNull(metadataOut); 170 assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY)); 171 172 metadataOut = controller.getMetadata(); 173 assertNotNull(metadataOut); 174 assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY)); 175 176 // test setPlaybackState 177 mCallback.resetLocked(); 178 PlaybackState state = new PlaybackState.Builder().setActions(TEST_ACTION).build(); 179 mSession.setPlaybackState(state); 180 mWaitLock.wait(TIME_OUT_MS); 181 assertTrue(mCallback.mOnPlaybackStateChangedCalled); 182 // just call the callback once directly so it's marked as tested 183 callback.onPlaybackStateChanged(mCallback.mPlaybackState); 184 185 PlaybackState stateOut = mCallback.mPlaybackState; 186 assertNotNull(stateOut); 187 assertEquals(TEST_ACTION, stateOut.getActions()); 188 189 stateOut = controller.getPlaybackState(); 190 assertNotNull(stateOut); 191 assertEquals(TEST_ACTION, stateOut.getActions()); 192 193 // test setQueue and setQueueTitle 194 mCallback.resetLocked(); 195 List<QueueItem> queue = new ArrayList<>(); 196 QueueItem item = new QueueItem(new MediaDescription.Builder() 197 .setMediaId(TEST_VALUE).setTitle("title").build(), TEST_QUEUE_ID); 198 queue.add(item); 199 mSession.setQueue(queue); 200 mWaitLock.wait(TIME_OUT_MS); 201 assertTrue(mCallback.mOnQueueChangedCalled); 202 // just call the callback once directly so it's marked as tested 203 callback.onQueueChanged(mCallback.mQueue); 204 205 mSession.setQueueTitle(TEST_VALUE); 206 mWaitLock.wait(TIME_OUT_MS); 207 assertTrue(mCallback.mOnQueueTitleChangedCalled); 208 209 assertEquals(TEST_VALUE, mCallback.mTitle); 210 assertEquals(queue.size(), mCallback.mQueue.size()); 211 assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId()); 212 assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId()); 213 214 assertEquals(TEST_VALUE, controller.getQueueTitle()); 215 assertEquals(queue.size(), controller.getQueue().size()); 216 assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId()); 217 assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId()); 218 219 mCallback.resetLocked(); 220 mSession.setQueue(null); 221 mWaitLock.wait(TIME_OUT_MS); 222 assertTrue(mCallback.mOnQueueChangedCalled); 223 // just call the callback once directly so it's marked as tested 224 callback.onQueueChanged(mCallback.mQueue); 225 226 mSession.setQueueTitle(null); 227 mWaitLock.wait(TIME_OUT_MS); 228 assertTrue(mCallback.mOnQueueTitleChangedCalled); 229 // just call the callback once directly so it's marked as tested 230 callback.onQueueTitleChanged(mCallback.mTitle); 231 232 assertNull(mCallback.mTitle); 233 assertNull(mCallback.mQueue); 234 assertNull(controller.getQueueTitle()); 235 assertNull(controller.getQueue()); 236 237 // test setSessionActivity 238 Intent intent = new Intent("cts.MEDIA_SESSION_ACTION"); 239 PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent, 0); 240 mSession.setSessionActivity(pi); 241 assertEquals(pi, controller.getSessionActivity()); 242 243 // test setActivity 244 mSession.setActive(true); 245 assertTrue(mSession.isActive()); 246 247 // test sendSessionEvent 248 mCallback.resetLocked(); 249 mSession.sendSessionEvent(TEST_SESSION_EVENT, extras); 250 mWaitLock.wait(TIME_OUT_MS); 251 252 assertTrue(mCallback.mOnSessionEventCalled); 253 assertEquals(TEST_SESSION_EVENT, mCallback.mEvent); 254 assertEquals(TEST_VALUE, mCallback.mExtras.getString(TEST_KEY)); 255 // just call the callback once directly so it's marked as tested 256 callback.onSessionEvent(mCallback.mEvent, mCallback.mExtras); 257 258 // test release 259 mCallback.resetLocked(); 260 mSession.release(); 261 mWaitLock.wait(TIME_OUT_MS); 262 assertTrue(mCallback.mOnSessionDestroyedCalled); 263 // just call the callback once directly so it's marked as tested 264 callback.onSessionDestroyed(); 265 } 266 } 267 268 /** 269 * Test {@link MediaSession#setPlaybackToLocal} and {@link MediaSession#setPlaybackToRemote}. 270 */ testPlaybackToLocalAndRemote()271 public void testPlaybackToLocalAndRemote() throws Exception { 272 MediaController controller = mSession.getController(); 273 controller.registerCallback(mCallback, mHandler); 274 275 synchronized (mWaitLock) { 276 // test setPlaybackToRemote, do this before testing setPlaybackToLocal 277 // to ensure it switches correctly. 278 mCallback.resetLocked(); 279 try { 280 mSession.setPlaybackToRemote(null); 281 fail("Expected IAE for setPlaybackToRemote(null)"); 282 } catch (IllegalArgumentException e) { 283 // expected 284 } 285 VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED, 286 TEST_MAX_VOLUME, TEST_CURRENT_VOLUME) {}; 287 mSession.setPlaybackToRemote(vp); 288 289 MediaController.PlaybackInfo info = null; 290 for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) { 291 mCallback.mOnAudioInfoChangedCalled = false; 292 mWaitLock.wait(TIME_OUT_MS); 293 assertTrue(mCallback.mOnAudioInfoChangedCalled); 294 info = mCallback.mPlaybackInfo; 295 if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME 296 && info.getMaxVolume() == TEST_MAX_VOLUME 297 && info.getVolumeControl() == VolumeProvider.VOLUME_CONTROL_FIXED 298 && info.getPlaybackType() 299 == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE) { 300 break; 301 } 302 } 303 assertNotNull(info); 304 assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType()); 305 assertEquals(TEST_MAX_VOLUME, info.getMaxVolume()); 306 assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume()); 307 assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl()); 308 309 info = controller.getPlaybackInfo(); 310 assertNotNull(info); 311 assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType()); 312 assertEquals(TEST_MAX_VOLUME, info.getMaxVolume()); 313 assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume()); 314 assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl()); 315 316 // test setPlaybackToLocal 317 AudioAttributes attrs = new AudioAttributes.Builder().setUsage(USAGE_GAME).build(); 318 mSession.setPlaybackToLocal(attrs); 319 320 info = controller.getPlaybackInfo(); 321 assertNotNull(info); 322 assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType()); 323 assertEquals(attrs, info.getAudioAttributes()); 324 } 325 } 326 327 /** 328 * Test {@link MediaSession.Callback#onMediaButtonEvent}. 329 */ testCallbackOnMediaButtonEvent()330 public void testCallbackOnMediaButtonEvent() throws Exception { 331 MediaSessionCallback sessionCallback = new MediaSessionCallback(); 332 mSession.setCallback(sessionCallback, new Handler(Looper.getMainLooper())); 333 mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS); 334 mSession.setActive(true); 335 336 Intent mediaButtonIntent = new Intent(Intent.ACTION_MEDIA_BUTTON).setComponent( 337 new ComponentName(getContext(), getContext().getClass())); 338 PendingIntent pi = PendingIntent.getBroadcast(getContext(), 0, mediaButtonIntent, 0); 339 mSession.setMediaButtonReceiver(pi); 340 341 // Set state to STATE_PLAYING to get higher priority. 342 setPlaybackState(PlaybackState.STATE_PLAYING); 343 344 // A media playback is also needed to receive media key events. 345 Utils.assertMediaPlaybackStarted(getContext()); 346 347 sessionCallback.reset(1); 348 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY); 349 assertTrue(sessionCallback.await(TIME_OUT_MS)); 350 assertEquals(1, sessionCallback.mOnPlayCalledCount); 351 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 352 353 sessionCallback.reset(1); 354 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PAUSE); 355 assertTrue(sessionCallback.await(TIME_OUT_MS)); 356 assertTrue(sessionCallback.mOnPauseCalled); 357 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 358 359 sessionCallback.reset(1); 360 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_NEXT); 361 assertTrue(sessionCallback.await(TIME_OUT_MS)); 362 assertTrue(sessionCallback.mOnSkipToNextCalled); 363 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 364 365 sessionCallback.reset(1); 366 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PREVIOUS); 367 assertTrue(sessionCallback.await(TIME_OUT_MS)); 368 assertTrue(sessionCallback.mOnSkipToPreviousCalled); 369 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 370 371 sessionCallback.reset(1); 372 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP); 373 assertTrue(sessionCallback.await(TIME_OUT_MS)); 374 assertTrue(sessionCallback.mOnStopCalled); 375 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 376 377 sessionCallback.reset(1); 378 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD); 379 assertTrue(sessionCallback.await(TIME_OUT_MS)); 380 assertTrue(sessionCallback.mOnFastForwardCalled); 381 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 382 383 sessionCallback.reset(1); 384 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_REWIND); 385 assertTrue(sessionCallback.await(TIME_OUT_MS)); 386 assertTrue(sessionCallback.mOnRewindCalled); 387 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 388 389 // Test PLAY_PAUSE button twice. 390 // First, simulate PLAY_PAUSE button while in STATE_PAUSED. 391 sessionCallback.reset(1); 392 setPlaybackState(PlaybackState.STATE_PAUSED); 393 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 394 assertTrue(sessionCallback.await(TIME_OUT_MS)); 395 assertEquals(1, sessionCallback.mOnPlayCalledCount); 396 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 397 398 // Next, simulate PLAY_PAUSE button while in STATE_PLAYING. 399 sessionCallback.reset(1); 400 setPlaybackState(PlaybackState.STATE_PLAYING); 401 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 402 assertTrue(sessionCallback.await(TIME_OUT_MS)); 403 assertTrue(sessionCallback.mOnPauseCalled); 404 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 405 406 // Double tap of PLAY_PAUSE is the next track instead of changing PLAY/PAUSE. 407 sessionCallback.reset(2); 408 setPlaybackState(PlaybackState.STATE_PLAYING); 409 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 410 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 411 assertFalse(sessionCallback.await(WAIT_MS)); 412 assertTrue(sessionCallback.mOnSkipToNextCalled); 413 assertEquals(0, sessionCallback.mOnPlayCalledCount); 414 assertFalse(sessionCallback.mOnPauseCalled); 415 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 416 417 // Test if PLAY_PAUSE double tap is considered as two single taps when another media 418 // key is pressed. 419 sessionCallback.reset(3); 420 setPlaybackState(PlaybackState.STATE_PAUSED); 421 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 422 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP); 423 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 424 assertTrue(sessionCallback.await(TIME_OUT_MS)); 425 assertEquals(2, sessionCallback.mOnPlayCalledCount); 426 assertTrue(sessionCallback.mOnStopCalled); 427 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 428 429 // Test if media keys are handled in order. 430 sessionCallback.reset(2); 431 setPlaybackState(PlaybackState.STATE_PAUSED); 432 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE); 433 simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP); 434 assertTrue(sessionCallback.await(TIME_OUT_MS)); 435 assertEquals(1, sessionCallback.mOnPlayCalledCount); 436 assertTrue(sessionCallback.mOnStopCalled); 437 assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo)); 438 synchronized (mWaitLock) { 439 assertEquals(PlaybackState.STATE_STOPPED, 440 mSession.getController().getPlaybackState().getState()); 441 } 442 } 443 444 /** 445 * Tests {@link MediaSession#setCallback} with {@code null}. No callbacks will be called 446 * once {@code setCallback(null)} is done. 447 */ testSetCallbackWithNull()448 public void testSetCallbackWithNull() throws Exception { 449 MediaSessionCallback sessionCallback = new MediaSessionCallback(); 450 mSession.setCallback(sessionCallback, mHandler); 451 mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS); 452 mSession.setActive(true); 453 454 MediaController controller = mSession.getController(); 455 setPlaybackState(PlaybackState.STATE_PLAYING); 456 457 sessionCallback.reset(1); 458 mSession.setCallback(null, mHandler); 459 460 controller.getTransportControls().pause(); 461 assertFalse(sessionCallback.await(WAIT_MS)); 462 assertFalse("Callback shouldn't be called.", sessionCallback.mOnPauseCalled); 463 } 464 setPlaybackState(int state)465 private void setPlaybackState(int state) { 466 final long allActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE 467 | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP 468 | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS 469 | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND; 470 PlaybackState playbackState = new PlaybackState.Builder().setActions(allActions) 471 .setState(state, 0L, 0.0f).build(); 472 synchronized (mWaitLock) { 473 mSession.setPlaybackState(playbackState); 474 } 475 } 476 477 /** 478 * Test {@link MediaSession#release} doesn't crash when multiple media sessions are in the app 479 * which receives the media key events. 480 * See: b/36669550 481 */ testReleaseNoCrashWithMultipleSessions()482 public void testReleaseNoCrashWithMultipleSessions() throws Exception { 483 // Start a media playback for this app to receive media key events. 484 Utils.assertMediaPlaybackStarted(getContext()); 485 486 MediaSession anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG); 487 mSession.release(); 488 anotherSession.release(); 489 490 // Try release with the different order. 491 mSession = new MediaSession(getContext(), TEST_SESSION_TAG); 492 anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG); 493 anotherSession.release(); 494 mSession.release(); 495 } 496 497 // This uses public APIs to dispatch key events, so sessions would consider this as 498 // 'media key event from this application'. simulateMediaKeyInput(int keyCode)499 private void simulateMediaKeyInput(int keyCode) { 500 mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_DOWN, keyCode)); 501 mAudioManager.dispatchMediaKeyEvent(new KeyEvent(KeyEvent.ACTION_UP, keyCode)); 502 } 503 504 /** 505 * Tests {@link MediaSession.QueueItem}. 506 */ testQueueItem()507 public void testQueueItem() { 508 MediaDescription.Builder descriptionBuilder = new MediaDescription.Builder() 509 .setMediaId("media-id") 510 .setTitle("title"); 511 512 QueueItem item = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID); 513 assertEquals(TEST_QUEUE_ID, item.getQueueId()); 514 assertEquals("media-id", item.getDescription().getMediaId()); 515 assertEquals("title", item.getDescription().getTitle()); 516 assertEquals(0, item.describeContents()); 517 518 QueueItem sameItem = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID); 519 assertTrue(item.equals(sameItem)); 520 521 QueueItem differentQueueId = new QueueItem( 522 descriptionBuilder.build(), TEST_QUEUE_ID + 1); 523 assertFalse(item.equals(differentQueueId)); 524 525 QueueItem differentDescription = new QueueItem( 526 descriptionBuilder.setTitle("title2").build(), TEST_QUEUE_ID); 527 assertFalse(item.equals(differentDescription)); 528 529 Parcel p = Parcel.obtain(); 530 item.writeToParcel(p, 0); 531 p.setDataPosition(0); 532 QueueItem other = QueueItem.CREATOR.createFromParcel(p); 533 assertEquals(item.toString(), other.toString()); 534 p.recycle(); 535 } 536 testSessionInfoWithFrameworkParcelable()537 public void testSessionInfoWithFrameworkParcelable() { 538 final String testKey = "test_key"; 539 final AudioAttributes frameworkParcelable = new AudioAttributes.Builder().build(); 540 541 Bundle sessionInfo = new Bundle(); 542 sessionInfo.putParcelable(testKey, frameworkParcelable); 543 544 MediaSession session = null; 545 try { 546 session = new MediaSession( 547 mContext, "testSessionInfoWithFrameworkParcelable", sessionInfo); 548 Bundle sessionInfoOut = session.getController().getSessionInfo(); 549 assertTrue(sessionInfoOut.containsKey(testKey)); 550 assertEquals(frameworkParcelable, sessionInfoOut.getParcelable(testKey)); 551 } finally { 552 if (session != null) { 553 session.release(); 554 } 555 } 556 557 } 558 testSessionInfoWithCustomParcelable()559 public void testSessionInfoWithCustomParcelable() { 560 final String testKey = "test_key"; 561 final MediaSession2Test.CustomParcelable customParcelable = 562 new MediaSession2Test.CustomParcelable(1); 563 564 Bundle sessionInfo = new Bundle(); 565 sessionInfo.putParcelable(testKey, customParcelable); 566 567 try { 568 MediaSession session = new MediaSession( 569 mContext, "testSessionInfoWithCustomParcelable", sessionInfo); 570 fail("Custom Parcelable shouldn't be accepted!"); 571 } catch (IllegalArgumentException e) { 572 // Expected 573 } 574 } 575 576 /** 577 * Verifies that a new session hasn't had any configuration bits set yet. 578 * 579 * @param controller The controller for the session 580 */ verifyNewSession(MediaController controller)581 private void verifyNewSession(MediaController controller) { 582 assertEquals("New session has unexpected configuration", 0L, controller.getFlags()); 583 assertNull("New session has unexpected configuration", controller.getExtras()); 584 assertNull("New session has unexpected configuration", controller.getMetadata()); 585 assertEquals("New session has unexpected configuration", 586 getContext().getPackageName(), controller.getPackageName()); 587 assertNull("New session has unexpected configuration", controller.getPlaybackState()); 588 assertNull("New session has unexpected configuration", controller.getQueue()); 589 assertNull("New session has unexpected configuration", controller.getQueueTitle()); 590 assertEquals("New session has unexpected configuration", Rating.RATING_NONE, 591 controller.getRatingType()); 592 assertNull("New session has unexpected configuration", controller.getSessionActivity()); 593 594 assertNotNull(controller.getSessionToken()); 595 assertNotNull(controller.getTransportControls()); 596 597 MediaController.PlaybackInfo info = controller.getPlaybackInfo(); 598 assertNotNull(info); 599 assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType()); 600 AudioAttributes attrs = info.getAudioAttributes(); 601 assertNotNull(attrs); 602 assertEquals(AudioAttributes.USAGE_MEDIA, attrs.getUsage()); 603 assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC), 604 info.getCurrentVolume()); 605 } 606 607 private class MediaControllerCallback extends MediaController.Callback { 608 private volatile boolean mOnPlaybackStateChangedCalled; 609 private volatile boolean mOnMetadataChangedCalled; 610 private volatile boolean mOnQueueChangedCalled; 611 private volatile boolean mOnQueueTitleChangedCalled; 612 private volatile boolean mOnExtraChangedCalled; 613 private volatile boolean mOnAudioInfoChangedCalled; 614 private volatile boolean mOnSessionDestroyedCalled; 615 private volatile boolean mOnSessionEventCalled; 616 617 private volatile PlaybackState mPlaybackState; 618 private volatile MediaMetadata mMediaMetadata; 619 private volatile List<QueueItem> mQueue; 620 private volatile CharSequence mTitle; 621 private volatile String mEvent; 622 private volatile Bundle mExtras; 623 private volatile MediaController.PlaybackInfo mPlaybackInfo; 624 resetLocked()625 public void resetLocked() { 626 mOnPlaybackStateChangedCalled = false; 627 mOnMetadataChangedCalled = false; 628 mOnQueueChangedCalled = false; 629 mOnQueueTitleChangedCalled = false; 630 mOnExtraChangedCalled = false; 631 mOnAudioInfoChangedCalled = false; 632 mOnSessionDestroyedCalled = false; 633 mOnSessionEventCalled = false; 634 635 mPlaybackState = null; 636 mMediaMetadata = null; 637 mQueue = null; 638 mTitle = null; 639 mExtras = null; 640 mPlaybackInfo = null; 641 } 642 643 @Override onPlaybackStateChanged(PlaybackState state)644 public void onPlaybackStateChanged(PlaybackState state) { 645 synchronized (mWaitLock) { 646 mOnPlaybackStateChangedCalled = true; 647 mPlaybackState = state; 648 mWaitLock.notify(); 649 } 650 } 651 652 @Override onMetadataChanged(MediaMetadata metadata)653 public void onMetadataChanged(MediaMetadata metadata) { 654 synchronized (mWaitLock) { 655 mOnMetadataChangedCalled = true; 656 mMediaMetadata = metadata; 657 mWaitLock.notify(); 658 } 659 } 660 661 @Override onQueueChanged(List<QueueItem> queue)662 public void onQueueChanged(List<QueueItem> queue) { 663 synchronized (mWaitLock) { 664 mOnQueueChangedCalled = true; 665 mQueue = queue; 666 mWaitLock.notify(); 667 } 668 } 669 670 @Override onQueueTitleChanged(CharSequence title)671 public void onQueueTitleChanged(CharSequence title) { 672 synchronized (mWaitLock) { 673 mOnQueueTitleChangedCalled = true; 674 mTitle = title; 675 mWaitLock.notify(); 676 } 677 } 678 679 @Override onExtrasChanged(Bundle extras)680 public void onExtrasChanged(Bundle extras) { 681 synchronized (mWaitLock) { 682 mOnExtraChangedCalled = true; 683 mExtras = extras; 684 mWaitLock.notify(); 685 } 686 } 687 688 @Override onAudioInfoChanged(MediaController.PlaybackInfo info)689 public void onAudioInfoChanged(MediaController.PlaybackInfo info) { 690 synchronized (mWaitLock) { 691 mOnAudioInfoChangedCalled = true; 692 mPlaybackInfo = info; 693 mWaitLock.notify(); 694 } 695 } 696 697 @Override onSessionDestroyed()698 public void onSessionDestroyed() { 699 synchronized (mWaitLock) { 700 mOnSessionDestroyedCalled = true; 701 mWaitLock.notify(); 702 } 703 } 704 705 @Override onSessionEvent(String event, Bundle extras)706 public void onSessionEvent(String event, Bundle extras) { 707 synchronized (mWaitLock) { 708 mOnSessionEventCalled = true; 709 mEvent = event; 710 mExtras = (Bundle) extras.clone(); 711 mWaitLock.notify(); 712 } 713 } 714 } 715 716 private class MediaSessionCallback extends MediaSession.Callback { 717 private CountDownLatch mLatch; 718 private int mOnPlayCalledCount; 719 private boolean mOnPauseCalled; 720 private boolean mOnStopCalled; 721 private boolean mOnFastForwardCalled; 722 private boolean mOnRewindCalled; 723 private boolean mOnSkipToPreviousCalled; 724 private boolean mOnSkipToNextCalled; 725 private RemoteUserInfo mCallerInfo; 726 reset(int count)727 public void reset(int count) { 728 mLatch = new CountDownLatch(count); 729 mOnPlayCalledCount = 0; 730 mOnPauseCalled = false; 731 mOnStopCalled = false; 732 mOnFastForwardCalled = false; 733 mOnRewindCalled = false; 734 mOnSkipToPreviousCalled = false; 735 mOnSkipToNextCalled = false; 736 } 737 await(long waitMs)738 public boolean await(long waitMs) { 739 try { 740 return mLatch.await(waitMs, TimeUnit.MILLISECONDS); 741 } catch (InterruptedException e) { 742 return false; 743 } 744 } 745 746 @Override onPlay()747 public void onPlay() { 748 mOnPlayCalledCount++; 749 mCallerInfo = mSession.getCurrentControllerInfo(); 750 setPlaybackState(PlaybackState.STATE_PLAYING); 751 mLatch.countDown(); 752 } 753 754 @Override onPause()755 public void onPause() { 756 mOnPauseCalled = true; 757 mCallerInfo = mSession.getCurrentControllerInfo(); 758 setPlaybackState(PlaybackState.STATE_PAUSED); 759 mLatch.countDown(); 760 } 761 762 @Override onStop()763 public void onStop() { 764 mOnStopCalled = true; 765 mCallerInfo = mSession.getCurrentControllerInfo(); 766 setPlaybackState(PlaybackState.STATE_STOPPED); 767 mLatch.countDown(); 768 } 769 770 @Override onFastForward()771 public void onFastForward() { 772 mOnFastForwardCalled = true; 773 mCallerInfo = mSession.getCurrentControllerInfo(); 774 mLatch.countDown(); 775 } 776 777 @Override onRewind()778 public void onRewind() { 779 mOnRewindCalled = true; 780 mCallerInfo = mSession.getCurrentControllerInfo(); 781 mLatch.countDown(); 782 } 783 784 @Override onSkipToPrevious()785 public void onSkipToPrevious() { 786 mOnSkipToPreviousCalled = true; 787 mCallerInfo = mSession.getCurrentControllerInfo(); 788 mLatch.countDown(); 789 } 790 791 @Override onSkipToNext()792 public void onSkipToNext() { 793 mOnSkipToNextCalled = true; 794 mCallerInfo = mSession.getCurrentControllerInfo(); 795 mLatch.countDown(); 796 } 797 } 798 } 799