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