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