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.MediaSessionTestService.KEY_EXPECTED_QUEUE_SIZE;
20 import static android.media.cts.MediaSessionTestService.KEY_EXPECTED_TOTAL_NUMBER_OF_ITEMS;
21 import static android.media.cts.MediaSessionTestService.KEY_SESSION_TOKEN;
22 import static android.media.cts.MediaSessionTestService.STEP_CHECK;
23 import static android.media.cts.MediaSessionTestService.STEP_CLEAN_UP;
24 import static android.media.cts.MediaSessionTestService.STEP_SET_UP;
25 import static android.media.cts.MediaSessionTestService.TEST_SERIES_OF_SET_QUEUE;
26 import static android.media.cts.MediaSessionTestService.TEST_SET_QUEUE;
27 import static android.media.cts.Utils.compareRemoteUserInfo;
28 
29 import android.app.PendingIntent;
30 import android.content.ComponentName;
31 import android.content.Context;
32 import android.content.Intent;
33 import android.media.AudioAttributes;
34 import android.media.AudioManager;
35 import android.media.MediaDescription;
36 import android.media.MediaMetadata;
37 import android.media.MediaSession2;
38 import android.media.Rating;
39 import android.media.VolumeProvider;
40 import android.media.session.MediaController;
41 import android.media.session.MediaSession;
42 import android.media.session.MediaSession.QueueItem;
43 import android.media.session.MediaSessionManager;
44 import android.media.session.MediaSessionManager.RemoteUserInfo;
45 import android.media.session.PlaybackState;
46 import android.os.Bundle;
47 import android.os.Handler;
48 import android.os.Looper;
49 import android.os.Parcel;
50 import android.os.Process;
51 import android.platform.test.annotations.AppModeFull;
52 import android.test.AndroidTestCase;
53 import android.text.TextUtils;
54 import android.view.KeyEvent;
55 
56 import java.util.ArrayList;
57 import java.util.Collections;
58 import java.util.List;
59 import java.util.concurrent.CountDownLatch;
60 import java.util.concurrent.TimeUnit;
61 
62 @NonMediaMainlineTest
63 @AppModeFull(reason = "TODO: evaluate and port to instant")
64 public class MediaSessionTest extends AndroidTestCase {
65     // The maximum time to wait for an operation that is expected to succeed.
66     private static final long TIME_OUT_MS = 3000L;
67     // The maximum time to wait for an operation that is expected to fail.
68     private static final long WAIT_MS = 100L;
69     private static final int MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT = 10;
70     private static final String TEST_SESSION_TAG = "test-session-tag";
71     private static final String TEST_KEY = "test-key";
72     private static final String TEST_VALUE = "test-val";
73     private static final String TEST_SESSION_EVENT = "test-session-event";
74     private static final String TEST_VOLUME_CONTROL_ID = "test-volume-control-id";
75     private static final int TEST_CURRENT_VOLUME = 10;
76     private static final int TEST_MAX_VOLUME = 11;
77     private static final long TEST_QUEUE_ID = 12L;
78     private static final long TEST_ACTION = 55L;
79     private static final int TEST_TOO_MANY_SESSION_COUNT = 1000;
80 
81     private AudioManager mAudioManager;
82     private Handler mHandler = new Handler(Looper.getMainLooper());
83     private Object mWaitLock = new Object();
84     private MediaControllerCallback mCallback = new MediaControllerCallback();
85     private MediaSession mSession;
86     private RemoteUserInfo mKeyDispatcherInfo;
87 
88     @Override
setUp()89     protected void setUp() throws Exception {
90         super.setUp();
91         mAudioManager = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
92         mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
93         mKeyDispatcherInfo = new MediaSessionManager.RemoteUserInfo(
94                 getContext().getPackageName(), Process.myPid(), Process.myUid());
95     }
96 
97     @Override
tearDown()98     protected void tearDown() throws Exception {
99         // It is OK to call release() twice.
100         if (mSession != null) {
101             mSession.release();
102             mSession = null;
103         }
104         super.tearDown();
105     }
106 
107     /**
108      * Tests that a session can be created and that all the fields are
109      * initialized correctly.
110      */
testCreateSession()111     public void testCreateSession() throws Exception {
112         assertNotNull(mSession.getSessionToken());
113         assertFalse("New session should not be active", mSession.isActive());
114 
115         // Verify by getting the controller and checking all its fields
116         MediaController controller = mSession.getController();
117         assertNotNull(controller);
118         verifyNewSession(controller);
119     }
120 
testSessionTokenEquals()121     public void testSessionTokenEquals() {
122         MediaSession anotherSession = null;
123         try {
124             anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG);
125             MediaSession.Token sessionToken = mSession.getSessionToken();
126             MediaSession.Token anotherSessionToken = anotherSession.getSessionToken();
127 
128             assertTrue(sessionToken.equals(sessionToken));
129             assertFalse(sessionToken.equals(null));
130             assertFalse(sessionToken.equals(mSession));
131             assertFalse(sessionToken.equals(anotherSessionToken));
132         } finally {
133             if (anotherSession != null) {
134                 anotherSession.release();
135             }
136         }
137     }
138 
139     /**
140      * Tests MediaSession.Token created in the constructor of MediaSession.
141      */
testSessionToken()142     public void testSessionToken() throws Exception {
143         MediaSession.Token sessionToken = mSession.getSessionToken();
144 
145         assertNotNull(sessionToken);
146         assertEquals(0, sessionToken.describeContents());
147 
148         // Test writeToParcel
149         Parcel p = Parcel.obtain();
150         sessionToken.writeToParcel(p, 0);
151         p.setDataPosition(0);
152         MediaSession.Token tokenFromParcel = MediaSession.Token.CREATOR.createFromParcel(p);
153         assertEquals(tokenFromParcel, sessionToken);
154         p.recycle();
155 
156         final int arraySize = 5;
157         MediaSession.Token[] tokenArray = MediaSession.Token.CREATOR.newArray(arraySize);
158         assertNotNull(tokenArray);
159         assertEquals(arraySize, tokenArray.length);
160         for (MediaSession.Token tokenElement : tokenArray) {
161             assertNull(tokenElement);
162         }
163     }
164 
165     /**
166      * Tests that the various configuration bits on a session get passed to the
167      * controller.
168      */
testConfigureSession()169     public void testConfigureSession() throws Exception {
170         MediaController controller = mSession.getController();
171         controller.registerCallback(mCallback, mHandler);
172         final MediaController.Callback callback = (MediaController.Callback) mCallback;
173 
174         synchronized (mWaitLock) {
175             // test setExtras
176             mCallback.resetLocked();
177             final Bundle extras = new Bundle();
178             extras.putString(TEST_KEY, TEST_VALUE);
179             mSession.setExtras(extras);
180             mWaitLock.wait(TIME_OUT_MS);
181             assertTrue(mCallback.mOnExtraChangedCalled);
182             // just call the callback once directly so it's marked as tested
183             callback.onExtrasChanged(mCallback.mExtras);
184 
185             Bundle extrasOut = mCallback.mExtras;
186             assertNotNull(extrasOut);
187             assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
188 
189             extrasOut = controller.getExtras();
190             assertNotNull(extrasOut);
191             assertEquals(TEST_VALUE, extrasOut.get(TEST_KEY));
192 
193             // test setFlags
194             mSession.setFlags(5);
195             assertEquals(5, controller.getFlags());
196 
197             // test setMetadata
198             mCallback.resetLocked();
199             MediaMetadata metadata =
200                     new MediaMetadata.Builder().putString(TEST_KEY, TEST_VALUE).build();
201             mSession.setMetadata(metadata);
202             mWaitLock.wait(TIME_OUT_MS);
203             assertTrue(mCallback.mOnMetadataChangedCalled);
204             // just call the callback once directly so it's marked as tested
205             callback.onMetadataChanged(mCallback.mMediaMetadata);
206 
207             MediaMetadata metadataOut = mCallback.mMediaMetadata;
208             assertNotNull(metadataOut);
209             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
210 
211             metadataOut = controller.getMetadata();
212             assertNotNull(metadataOut);
213             assertEquals(TEST_VALUE, metadataOut.getString(TEST_KEY));
214 
215             // test setPlaybackState
216             mCallback.resetLocked();
217             PlaybackState state = new PlaybackState.Builder().setActions(TEST_ACTION).build();
218             mSession.setPlaybackState(state);
219             mWaitLock.wait(TIME_OUT_MS);
220             assertTrue(mCallback.mOnPlaybackStateChangedCalled);
221             // just call the callback once directly so it's marked as tested
222             callback.onPlaybackStateChanged(mCallback.mPlaybackState);
223 
224             PlaybackState stateOut = mCallback.mPlaybackState;
225             assertNotNull(stateOut);
226             assertEquals(TEST_ACTION, stateOut.getActions());
227 
228             stateOut = controller.getPlaybackState();
229             assertNotNull(stateOut);
230             assertEquals(TEST_ACTION, stateOut.getActions());
231 
232             // test setQueue and setQueueTitle
233             mCallback.resetLocked();
234             List<QueueItem> queue = new ArrayList<>();
235             QueueItem item = new QueueItem(new MediaDescription.Builder()
236                     .setMediaId(TEST_VALUE).setTitle("title").build(), TEST_QUEUE_ID);
237             queue.add(item);
238             mSession.setQueue(queue);
239             mWaitLock.wait(TIME_OUT_MS);
240             assertTrue(mCallback.mOnQueueChangedCalled);
241             // just call the callback once directly so it's marked as tested
242             callback.onQueueChanged(mCallback.mQueue);
243 
244             mSession.setQueueTitle(TEST_VALUE);
245             mWaitLock.wait(TIME_OUT_MS);
246             assertTrue(mCallback.mOnQueueTitleChangedCalled);
247 
248             assertEquals(TEST_VALUE, mCallback.mTitle);
249             assertEquals(queue.size(), mCallback.mQueue.size());
250             assertEquals(TEST_QUEUE_ID, mCallback.mQueue.get(0).getQueueId());
251             assertEquals(TEST_VALUE, mCallback.mQueue.get(0).getDescription().getMediaId());
252 
253             assertEquals(TEST_VALUE, controller.getQueueTitle());
254             assertEquals(queue.size(), controller.getQueue().size());
255             assertEquals(TEST_QUEUE_ID, controller.getQueue().get(0).getQueueId());
256             assertEquals(TEST_VALUE, controller.getQueue().get(0).getDescription().getMediaId());
257 
258             mCallback.resetLocked();
259             mSession.setQueue(null);
260             mWaitLock.wait(TIME_OUT_MS);
261             assertTrue(mCallback.mOnQueueChangedCalled);
262             // just call the callback once directly so it's marked as tested
263             callback.onQueueChanged(mCallback.mQueue);
264 
265             mSession.setQueueTitle(null);
266             mWaitLock.wait(TIME_OUT_MS);
267             assertTrue(mCallback.mOnQueueTitleChangedCalled);
268             // just call the callback once directly so it's marked as tested
269             callback.onQueueTitleChanged(mCallback.mTitle);
270 
271             assertNull(mCallback.mTitle);
272             assertNull(mCallback.mQueue);
273             assertNull(controller.getQueueTitle());
274             assertNull(controller.getQueue());
275 
276             // test setSessionActivity
277             Intent intent = new Intent("cts.MEDIA_SESSION_ACTION");
278             PendingIntent pi = PendingIntent.getActivity(getContext(), 555, intent,
279                     PendingIntent.FLAG_MUTABLE_UNAUDITED);
280             mSession.setSessionActivity(pi);
281             assertEquals(pi, controller.getSessionActivity());
282 
283             // test setActivity
284             mSession.setActive(true);
285             assertTrue(mSession.isActive());
286 
287             // test sendSessionEvent
288             mCallback.resetLocked();
289             mSession.sendSessionEvent(TEST_SESSION_EVENT, extras);
290             mWaitLock.wait(TIME_OUT_MS);
291 
292             assertTrue(mCallback.mOnSessionEventCalled);
293             assertEquals(TEST_SESSION_EVENT, mCallback.mEvent);
294             assertEquals(TEST_VALUE, mCallback.mExtras.getString(TEST_KEY));
295             // just call the callback once directly so it's marked as tested
296             callback.onSessionEvent(mCallback.mEvent, mCallback.mExtras);
297 
298             // test release
299             mCallback.resetLocked();
300             mSession.release();
301             mWaitLock.wait(TIME_OUT_MS);
302             assertTrue(mCallback.mOnSessionDestroyedCalled);
303             // just call the callback once directly so it's marked as tested
304             callback.onSessionDestroyed();
305         }
306     }
307 
308     /**
309      * Test whether media button receiver can be a explicit broadcast receiver via
310      * MediaSession.setMediaButtonReceiver(PendingIntent).
311      */
testSetMediaButtonReceiver_broadcastReceiver()312     public void testSetMediaButtonReceiver_broadcastReceiver() throws Exception {
313         Intent intent = new Intent(mContext.getApplicationContext(),
314                 MediaButtonBroadcastReceiver.class);
315         PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent,
316                 PendingIntent.FLAG_MUTABLE_UNAUDITED);
317 
318         // Play a sound so this session can get the priority.
319         Utils.assertMediaPlaybackStarted(getContext());
320 
321         // Sets the media button receiver. Framework will keep the broadcast receiver component name
322         // from the pending intent in persistent storage.
323         mSession.setMediaButtonReceiver(pi);
324 
325         // Call explicit release, so change in the media key event session can be notified with the
326         // pending intent.
327         mSession.release();
328 
329         int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
330         try {
331             CountDownLatch latch = new CountDownLatch(2);
332             MediaButtonBroadcastReceiver.setCallback((keyEvent) -> {
333                 assertEquals(keyCode, keyEvent.getKeyCode());
334                 switch ((int) latch.getCount()) {
335                     case 2:
336                         assertEquals(KeyEvent.ACTION_DOWN, keyEvent.getAction());
337                         break;
338                     case 1:
339                         assertEquals(KeyEvent.ACTION_UP, keyEvent.getAction());
340                         break;
341                 }
342                 latch.countDown();
343             });
344             // Also try to dispatch media key event.
345             // System would try to dispatch event.
346             simulateMediaKeyInput(keyCode);
347 
348             assertTrue(latch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
349         } finally {
350             MediaButtonBroadcastReceiver.setCallback(null);
351         }
352     }
353 
354     /**
355      * Test whether media button receiver can be a explicit service.
356      */
testSetMediaButtonReceiver_service()357     public void testSetMediaButtonReceiver_service() throws Exception {
358         Intent intent = new Intent(mContext.getApplicationContext(),
359                 MediaButtonReceiverService.class);
360         PendingIntent pi = PendingIntent.getService(mContext, 0, intent,
361                 PendingIntent.FLAG_MUTABLE_UNAUDITED);
362 
363         // Play a sound so this session can get the priority.
364         Utils.assertMediaPlaybackStarted(getContext());
365 
366         // Sets the media button receiver. Framework would try to keep the pending intent in the
367         // persistent store.
368         mSession.setMediaButtonReceiver(pi);
369 
370         // Call explicit release, so change in the media key event session can be notified with the
371         // pending intent.
372         mSession.release();
373 
374         int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
375         try {
376             CountDownLatch latch = new CountDownLatch(2);
377             MediaButtonReceiverService.setCallback((keyEvent) -> {
378                 assertEquals(keyCode, keyEvent.getKeyCode());
379                 switch ((int) latch.getCount()) {
380                     case 2:
381                         assertEquals(KeyEvent.ACTION_DOWN, keyEvent.getAction());
382                         break;
383                     case 1:
384                         assertEquals(KeyEvent.ACTION_UP, keyEvent.getAction());
385                         break;
386                 }
387                 latch.countDown();
388             });
389             // Also try to dispatch media key event.
390             // System would try to dispatch event.
391             simulateMediaKeyInput(keyCode);
392 
393             assertTrue(latch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
394         } finally {
395             MediaButtonReceiverService.setCallback(null);
396         }
397     }
398 
399     /**
400      * Test whether system doesn't crash by
401      * {@link MediaSession#setMediaButtonReceiver(PendingIntent)} with implicit intent.
402      */
testSetMediaButtonReceiver_implicitIntent()403     public void testSetMediaButtonReceiver_implicitIntent() throws Exception {
404         // Note: No such broadcast receiver exists.
405         Intent intent = new Intent("android.media.cts.ACTION_MEDIA_TEST");
406         PendingIntent pi = PendingIntent.getBroadcast(mContext, 0, intent,
407                 PendingIntent.FLAG_MUTABLE_UNAUDITED);
408 
409         // Play a sound so this session can get the priority.
410         Utils.assertMediaPlaybackStarted(getContext());
411 
412         // Sets the media button receiver. Framework would try to keep the pending intent in the
413         // persistent store.
414         mSession.setMediaButtonReceiver(pi);
415 
416         // Call explicit release, so change in the media key event session can be notified with the
417         // pending intent.
418         mSession.release();
419 
420         // Also try to dispatch media key event. System would try to send key event via pending
421         // intent, but it would no-op because there's no receiver.
422         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
423     }
424 
425     /**
426      * Test whether media button receiver can be a explicit broadcast receiver via
427      * MediaSession.setMediaButtonBroadcastReceiver(ComponentName)
428      */
testSetMediaButtonBroadcastReceiver_broadcastReceiver()429     public void testSetMediaButtonBroadcastReceiver_broadcastReceiver() throws Exception {
430         // Play a sound so this session can get the priority.
431         Utils.assertMediaPlaybackStarted(getContext());
432 
433         // Sets the broadcast receiver's component name. Framework will keep the component name in
434         // persistent storage.
435         mSession.setMediaButtonBroadcastReceiver(new ComponentName(mContext,
436                 MediaButtonBroadcastReceiver.class));
437 
438         // Call explicit release, so change in the media key event session can be notified using the
439         // component name.
440         mSession.release();
441 
442         int keyCode = KeyEvent.KEYCODE_MEDIA_PLAY;
443         try {
444             CountDownLatch latch = new CountDownLatch(2);
445             MediaButtonBroadcastReceiver.setCallback((keyEvent) -> {
446                 assertEquals(keyCode, keyEvent.getKeyCode());
447                 switch ((int) latch.getCount()) {
448                     case 2:
449                         assertEquals(KeyEvent.ACTION_DOWN, keyEvent.getAction());
450                         break;
451                     case 1:
452                         assertEquals(KeyEvent.ACTION_UP, keyEvent.getAction());
453                         break;
454                 }
455                 latch.countDown();
456             });
457             // Also try to dispatch media key event.
458             // System would try to dispatch event.
459             simulateMediaKeyInput(keyCode);
460 
461             assertTrue(latch.await(TIME_OUT_MS, TimeUnit.MILLISECONDS));
462         } finally {
463             MediaButtonBroadcastReceiver.setCallback(null);
464         }
465     }
466 
467     /**
468      * Test public APIs of {@link VolumeProvider}.
469      */
testVolumeProvider()470     public void testVolumeProvider() {
471         VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_RELATIVE,
472                 TEST_MAX_VOLUME, TEST_CURRENT_VOLUME, TEST_VOLUME_CONTROL_ID) {};
473         assertEquals(VolumeProvider.VOLUME_CONTROL_RELATIVE, vp.getVolumeControl());
474         assertEquals(TEST_MAX_VOLUME, vp.getMaxVolume());
475         assertEquals(TEST_CURRENT_VOLUME, vp.getCurrentVolume());
476         assertEquals(TEST_VOLUME_CONTROL_ID, vp.getVolumeControlId());
477     }
478 
479     /**
480      * Test {@link MediaSession#setPlaybackToLocal} and {@link MediaSession#setPlaybackToRemote}.
481      */
testPlaybackToLocalAndRemote()482     public void testPlaybackToLocalAndRemote() throws Exception {
483         MediaController controller = mSession.getController();
484         controller.registerCallback(mCallback, mHandler);
485 
486         synchronized (mWaitLock) {
487             // test setPlaybackToRemote, do this before testing setPlaybackToLocal
488             // to ensure it switches correctly.
489             mCallback.resetLocked();
490             try {
491                 mSession.setPlaybackToRemote(null);
492                 fail("Expected IAE for setPlaybackToRemote(null)");
493             } catch (IllegalArgumentException e) {
494                 // expected
495             }
496             VolumeProvider vp = new VolumeProvider(VolumeProvider.VOLUME_CONTROL_FIXED,
497                     TEST_MAX_VOLUME, TEST_CURRENT_VOLUME, TEST_VOLUME_CONTROL_ID) {};
498             mSession.setPlaybackToRemote(vp);
499 
500             MediaController.PlaybackInfo info = null;
501             for (int i = 0; i < MAX_AUDIO_INFO_CHANGED_CALLBACK_COUNT; ++i) {
502                 mCallback.mOnAudioInfoChangedCalled = false;
503                 mWaitLock.wait(TIME_OUT_MS);
504                 assertTrue(mCallback.mOnAudioInfoChangedCalled);
505                 info = mCallback.mPlaybackInfo;
506                 if (info != null && info.getCurrentVolume() == TEST_CURRENT_VOLUME
507                         && info.getMaxVolume() == TEST_MAX_VOLUME
508                         && info.getVolumeControl() == VolumeProvider.VOLUME_CONTROL_FIXED
509                         && info.getPlaybackType()
510                                 == MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE
511                         && TextUtils.equals(info.getVolumeControlId(),TEST_VOLUME_CONTROL_ID)) {
512                     break;
513                 }
514             }
515             assertNotNull(info);
516             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
517             assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
518             assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
519             assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
520             assertEquals(TEST_VOLUME_CONTROL_ID, info.getVolumeControlId());
521 
522             info = controller.getPlaybackInfo();
523             assertNotNull(info);
524             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
525             assertEquals(TEST_MAX_VOLUME, info.getMaxVolume());
526             assertEquals(TEST_CURRENT_VOLUME, info.getCurrentVolume());
527             assertEquals(VolumeProvider.VOLUME_CONTROL_FIXED, info.getVolumeControl());
528             assertEquals(TEST_VOLUME_CONTROL_ID, info.getVolumeControlId());
529 
530             // test setPlaybackToLocal
531             AudioAttributes attrs = new AudioAttributes.Builder().setUsage(USAGE_GAME).build();
532             mSession.setPlaybackToLocal(attrs);
533 
534             info = controller.getPlaybackInfo();
535             assertNotNull(info);
536             assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
537             assertEquals(attrs, info.getAudioAttributes());
538             assertNull(info.getVolumeControlId());
539         }
540     }
541 
542     /**
543      * Test {@link MediaSession.Callback#onMediaButtonEvent}.
544      */
testCallbackOnMediaButtonEvent()545     public void testCallbackOnMediaButtonEvent() throws Exception {
546         MediaSessionCallback sessionCallback = new MediaSessionCallback();
547         mSession.setCallback(sessionCallback, new Handler(Looper.getMainLooper()));
548         mSession.setFlags(MediaSession.FLAG_HANDLES_MEDIA_BUTTONS);
549         mSession.setActive(true);
550 
551         // Set state to STATE_PLAYING to get higher priority.
552         setPlaybackState(PlaybackState.STATE_PLAYING);
553 
554         // A media playback is also needed to receive media key events.
555         Utils.assertMediaPlaybackStarted(getContext());
556 
557         sessionCallback.reset(1);
558         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY);
559         assertTrue(sessionCallback.await(TIME_OUT_MS));
560         assertEquals(1, sessionCallback.mOnPlayCalledCount);
561         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
562 
563         sessionCallback.reset(1);
564         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PAUSE);
565         assertTrue(sessionCallback.await(TIME_OUT_MS));
566         assertTrue(sessionCallback.mOnPauseCalled);
567         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
568 
569         sessionCallback.reset(1);
570         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_NEXT);
571         assertTrue(sessionCallback.await(TIME_OUT_MS));
572         assertTrue(sessionCallback.mOnSkipToNextCalled);
573         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
574 
575         sessionCallback.reset(1);
576         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PREVIOUS);
577         assertTrue(sessionCallback.await(TIME_OUT_MS));
578         assertTrue(sessionCallback.mOnSkipToPreviousCalled);
579         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
580 
581         sessionCallback.reset(1);
582         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
583         assertTrue(sessionCallback.await(TIME_OUT_MS));
584         assertTrue(sessionCallback.mOnStopCalled);
585         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
586 
587         sessionCallback.reset(1);
588         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_FAST_FORWARD);
589         assertTrue(sessionCallback.await(TIME_OUT_MS));
590         assertTrue(sessionCallback.mOnFastForwardCalled);
591         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
592 
593         sessionCallback.reset(1);
594         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_REWIND);
595         assertTrue(sessionCallback.await(TIME_OUT_MS));
596         assertTrue(sessionCallback.mOnRewindCalled);
597         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
598 
599         // Test PLAY_PAUSE button twice.
600         // First, simulate PLAY_PAUSE button while in STATE_PAUSED.
601         sessionCallback.reset(1);
602         setPlaybackState(PlaybackState.STATE_PAUSED);
603         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
604         assertTrue(sessionCallback.await(TIME_OUT_MS));
605         assertEquals(1, sessionCallback.mOnPlayCalledCount);
606         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
607 
608         // Next, simulate PLAY_PAUSE button while in STATE_PLAYING.
609         sessionCallback.reset(1);
610         setPlaybackState(PlaybackState.STATE_PLAYING);
611         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
612         assertTrue(sessionCallback.await(TIME_OUT_MS));
613         assertTrue(sessionCallback.mOnPauseCalled);
614         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
615 
616         // Double tap of PLAY_PAUSE is the next track instead of changing PLAY/PAUSE.
617         sessionCallback.reset(2);
618         setPlaybackState(PlaybackState.STATE_PLAYING);
619         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
620         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
621         assertFalse(sessionCallback.await(WAIT_MS));
622         assertTrue(sessionCallback.mOnSkipToNextCalled);
623         assertEquals(0, sessionCallback.mOnPlayCalledCount);
624         assertFalse(sessionCallback.mOnPauseCalled);
625         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
626 
627         // Test if PLAY_PAUSE double tap is considered as two single taps when another media
628         // key is pressed.
629         sessionCallback.reset(3);
630         setPlaybackState(PlaybackState.STATE_PAUSED);
631         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
632         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
633         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
634         assertTrue(sessionCallback.await(TIME_OUT_MS));
635         assertEquals(2, sessionCallback.mOnPlayCalledCount);
636         assertTrue(sessionCallback.mOnStopCalled);
637         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
638 
639         // Test if media keys are handled in order.
640         sessionCallback.reset(2);
641         setPlaybackState(PlaybackState.STATE_PAUSED);
642         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE);
643         simulateMediaKeyInput(KeyEvent.KEYCODE_MEDIA_STOP);
644         assertTrue(sessionCallback.await(TIME_OUT_MS));
645         assertEquals(1, sessionCallback.mOnPlayCalledCount);
646         assertTrue(sessionCallback.mOnStopCalled);
647         assertTrue(compareRemoteUserInfo(mKeyDispatcherInfo, sessionCallback.mCallerInfo));
648         synchronized (mWaitLock) {
649             assertEquals(PlaybackState.STATE_STOPPED,
650                     mSession.getController().getPlaybackState().getState());
651         }
652     }
653 
654     /**
655      * Tests {@link MediaSession#setCallback} with {@code null}. No callbacks will be called
656      * once {@code setCallback(null)} is done.
657      */
testSetCallbackWithNull()658     public void testSetCallbackWithNull() throws Exception {
659         MediaSessionCallback sessionCallback = new MediaSessionCallback();
660         mSession.setCallback(sessionCallback, mHandler);
661         mSession.setFlags(MediaSession.FLAG_HANDLES_TRANSPORT_CONTROLS);
662         mSession.setActive(true);
663 
664         MediaController controller = mSession.getController();
665         setPlaybackState(PlaybackState.STATE_PLAYING);
666 
667         sessionCallback.reset(1);
668         mSession.setCallback(null, mHandler);
669 
670         controller.getTransportControls().pause();
671         assertFalse(sessionCallback.await(WAIT_MS));
672         assertFalse("Callback shouldn't be called.", sessionCallback.mOnPauseCalled);
673     }
674 
setPlaybackState(int state)675     private void setPlaybackState(int state) {
676         final long allActions = PlaybackState.ACTION_PLAY | PlaybackState.ACTION_PAUSE
677                 | PlaybackState.ACTION_PLAY_PAUSE | PlaybackState.ACTION_STOP
678                 | PlaybackState.ACTION_SKIP_TO_NEXT | PlaybackState.ACTION_SKIP_TO_PREVIOUS
679                 | PlaybackState.ACTION_FAST_FORWARD | PlaybackState.ACTION_REWIND;
680         PlaybackState playbackState = new PlaybackState.Builder().setActions(allActions)
681                 .setState(state, 0L, 0.0f).build();
682         synchronized (mWaitLock) {
683             mSession.setPlaybackState(playbackState);
684         }
685     }
686 
687     /**
688      * Test {@link MediaSession#release} doesn't crash when multiple media sessions are in the app
689      * which receives the media key events.
690      * See: b/36669550
691      */
testReleaseNoCrashWithMultipleSessions()692     public void testReleaseNoCrashWithMultipleSessions() throws Exception {
693         // Start a media playback for this app to receive media key events.
694         Utils.assertMediaPlaybackStarted(getContext());
695 
696         MediaSession anotherSession = null;
697         try {
698             anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG);
699             mSession.release();
700             anotherSession.release();
701 
702             // Try release with the different order.
703             mSession = new MediaSession(getContext(), TEST_SESSION_TAG);
704             anotherSession = new MediaSession(getContext(), TEST_SESSION_TAG);
705             anotherSession.release();
706             mSession.release();
707         } finally {
708             if (anotherSession != null) {
709                 anotherSession.release();
710                 anotherSession = null;
711             }
712         }
713     }
714 
715     // This uses public APIs to dispatch key events, so sessions would consider this as
716     // 'media key event from this application'.
simulateMediaKeyInput(int keyCode)717     private void simulateMediaKeyInput(int keyCode) {
718         long downTime = System.currentTimeMillis();
719         mAudioManager.dispatchMediaKeyEvent(
720                 new KeyEvent(downTime, downTime, KeyEvent.ACTION_DOWN, keyCode, 0));
721         mAudioManager.dispatchMediaKeyEvent(
722                 new KeyEvent(downTime, System.currentTimeMillis(), KeyEvent.ACTION_UP, keyCode, 0));
723     }
724 
725     /**
726      * Tests {@link MediaSession.QueueItem}.
727      */
testQueueItem()728     public void testQueueItem() {
729         MediaDescription.Builder descriptionBuilder = new MediaDescription.Builder()
730                 .setMediaId("media-id")
731                 .setTitle("title");
732 
733         try {
734             new QueueItem(/*description=*/null, TEST_QUEUE_ID);
735             fail();
736         } catch (IllegalArgumentException e) {
737             // Expected
738         }
739         try {
740             new QueueItem(descriptionBuilder.build(), QueueItem.UNKNOWN_ID);
741             fail();
742         } catch (IllegalArgumentException e) {
743             // Expected
744         }
745 
746         QueueItem item = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID);
747 
748         Parcel p = Parcel.obtain();
749         item.writeToParcel(p, 0);
750         p.setDataPosition(0);
751         QueueItem other = QueueItem.CREATOR.createFromParcel(p);
752         assertEquals(item.toString(), other.toString());
753         p.recycle();
754 
755         final int arraySize = 5;
756         QueueItem[] queueItemArray = QueueItem.CREATOR.newArray(arraySize);
757         assertNotNull(queueItemArray);
758         assertEquals(arraySize, queueItemArray.length);
759         for (QueueItem elem : queueItemArray) {
760             assertNull(elem);
761         }
762     }
763 
testQueueItemEquals()764     public void testQueueItemEquals() {
765         MediaDescription.Builder descriptionBuilder = new MediaDescription.Builder()
766                 .setMediaId("media-id")
767                 .setTitle("title");
768 
769         QueueItem item = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID);
770         assertEquals(TEST_QUEUE_ID, item.getQueueId());
771         assertEquals("media-id", item.getDescription().getMediaId());
772         assertEquals("title", item.getDescription().getTitle());
773         assertEquals(0, item.describeContents());
774 
775         assertFalse(item.equals(null));
776         assertFalse(item.equals(descriptionBuilder.build()));
777 
778         QueueItem sameItem = new QueueItem(descriptionBuilder.build(), TEST_QUEUE_ID);
779         assertTrue(item.equals(sameItem));
780 
781         QueueItem differentQueueId = new QueueItem(
782                 descriptionBuilder.build(), TEST_QUEUE_ID + 1);
783         assertFalse(item.equals(differentQueueId));
784 
785         QueueItem differentDescription = new QueueItem(
786                 descriptionBuilder.setTitle("title2").build(), TEST_QUEUE_ID);
787         assertFalse(item.equals(differentDescription));
788     }
789 
testSessionInfoWithFrameworkParcelable()790     public void testSessionInfoWithFrameworkParcelable() {
791         final String testKey = "test_key";
792         final AudioAttributes frameworkParcelable = new AudioAttributes.Builder().build();
793 
794         Bundle sessionInfo = new Bundle();
795         sessionInfo.putParcelable(testKey, frameworkParcelable);
796 
797         MediaSession session = null;
798         try {
799             session = new MediaSession(
800                     mContext, "testSessionInfoWithFrameworkParcelable", sessionInfo);
801             Bundle sessionInfoOut = session.getController().getSessionInfo();
802             assertTrue(sessionInfoOut.containsKey(testKey));
803             assertEquals(frameworkParcelable, sessionInfoOut.getParcelable(testKey));
804         } finally {
805             if (session != null) {
806                 session.release();
807             }
808         }
809 
810     }
811 
testSessionInfoWithCustomParcelable()812     public void testSessionInfoWithCustomParcelable() {
813         final String testKey = "test_key";
814         final MediaSession2Test.CustomParcelable customParcelable =
815                 new MediaSession2Test.CustomParcelable(1);
816 
817         Bundle sessionInfo = new Bundle();
818         sessionInfo.putParcelable(testKey, customParcelable);
819 
820         MediaSession session = null;
821         try {
822             session = new MediaSession(
823                     mContext, "testSessionInfoWithCustomParcelable", sessionInfo);
824             fail("Custom Parcelable shouldn't be accepted!");
825         } catch (IllegalArgumentException e) {
826             // Expected
827         } finally {
828             if (session != null) {
829                 session.release();
830             }
831         }
832     }
833 
834     /**
835      * An app should not be able to create too many sessions.
836      * See MediaSessionService#SESSION_CREATION_LIMIT_PER_UID
837      */
testSessionCreationLimit()838     public void testSessionCreationLimit() {
839         List<MediaSession> sessions = new ArrayList<>();
840         try {
841             for (int i = 0; i < TEST_TOO_MANY_SESSION_COUNT; i++) {
842                 sessions.add(new MediaSession(mContext, "testSessionCreationLimit"));
843             }
844             fail("The number of session should be limited!");
845         } catch (RuntimeException e) {
846             // Expected
847         } finally {
848             for (MediaSession session : sessions) {
849                 session.release();
850             }
851         }
852     }
853 
854     /**
855      * Check that calling {@link MediaSession#release()} multiple times for the same session
856      * does not decrement current session count multiple times.
857      */
testSessionCreationLimitWithMediaSessionRelease()858     public void testSessionCreationLimitWithMediaSessionRelease() {
859         List<MediaSession> sessions = new ArrayList<>();
860         MediaSession sessionToReleaseMultipleTimes = null;
861         try {
862             sessionToReleaseMultipleTimes = new MediaSession(
863                     mContext, "testSessionCreationLimitWithMediaSessionRelease");
864             for (int i = 0; i < TEST_TOO_MANY_SESSION_COUNT; i++) {
865                 sessions.add(new MediaSession(
866                         mContext, "testSessionCreationLimitWithMediaSessionRelease"));
867                 // Call release() many times with the same session.
868                 sessionToReleaseMultipleTimes.release();
869             }
870             fail("The number of session should be limited!");
871         } catch (RuntimeException e) {
872             // Expected
873         } finally {
874             for (MediaSession session : sessions) {
875                 session.release();
876             }
877             if (sessionToReleaseMultipleTimes != null) {
878                 sessionToReleaseMultipleTimes.release();
879             }
880         }
881     }
882 
883     /**
884      * Check that calling {@link MediaSession2#close()} does not decrement current session count.
885      */
testSessionCreationLimitWithMediaSession2Release()886     public void testSessionCreationLimitWithMediaSession2Release() {
887         List<MediaSession> sessions = new ArrayList<>();
888         try {
889             for (int i = 0; i < 1000; i++) {
890                 sessions.add(new MediaSession(
891                         mContext, "testSessionCreationLimitWithMediaSession2Release"));
892 
893                 try (MediaSession2 session2 = new MediaSession2.Builder(mContext).build()) {
894                     // Do nothing
895                 }
896             }
897             fail("The number of session should be limited!");
898         } catch (RuntimeException e) {
899             // Expected
900         } finally {
901             for (MediaSession session : sessions) {
902                 session.release();
903             }
904         }
905     }
906 
907     /**
908      * Check that a series of {@link MediaSession#setQueue} does not break {@link MediaController}
909      * on the remote process due to binder buffer overflow.
910      */
testSeriesOfSetQueue()911     public void testSeriesOfSetQueue() throws Exception {
912         int numberOfCalls = 100;
913         int queueSize = 1_000;
914         List<QueueItem> queue = new ArrayList<>();
915         for (int id = 0; id < queueSize; id++) {
916             MediaDescription description = new MediaDescription.Builder()
917                     .setMediaId(Integer.toString(id)).build();
918             queue.add(new QueueItem(description, id));
919         }
920 
921         try (RemoteService.Invoker invoker = new RemoteService.Invoker(mContext,
922                 MediaSessionTestService.class, TEST_SERIES_OF_SET_QUEUE)) {
923             Bundle args = new Bundle();
924             args.putParcelable(KEY_SESSION_TOKEN, mSession.getSessionToken());
925             args.putInt(KEY_EXPECTED_TOTAL_NUMBER_OF_ITEMS, numberOfCalls * queueSize);
926             invoker.run(STEP_SET_UP, args);
927             for (int i = 0; i < numberOfCalls; i++) {
928                 mSession.setQueue(queue);
929             }
930             invoker.run(STEP_CHECK);
931             invoker.run(STEP_CLEAN_UP);
932         }
933     }
934 
testSetQueueWithLargeNumberOfItems()935     public void testSetQueueWithLargeNumberOfItems() throws Exception {
936         int queueSize = 1_000_000;
937         List<QueueItem> queue = new ArrayList<>();
938         for (int id = 0; id < queueSize; id++) {
939             MediaDescription description = new MediaDescription.Builder()
940                     .setMediaId(Integer.toString(id)).build();
941             queue.add(new QueueItem(description, id));
942         }
943 
944         try (RemoteService.Invoker invoker = new RemoteService.Invoker(mContext,
945                 MediaSessionTestService.class, TEST_SET_QUEUE)) {
946             Bundle args = new Bundle();
947             args.putParcelable(KEY_SESSION_TOKEN, mSession.getSessionToken());
948             args.putInt(KEY_EXPECTED_QUEUE_SIZE, queueSize);
949             invoker.run(STEP_SET_UP, args);
950             mSession.setQueue(queue);
951             invoker.run(STEP_CHECK);
952             invoker.run(STEP_CLEAN_UP);
953         }
954     }
955 
testSetQueueWithEmptyQueue()956     public void testSetQueueWithEmptyQueue() throws Exception {
957         try (RemoteService.Invoker invoker = new RemoteService.Invoker(mContext,
958                 MediaSessionTestService.class, TEST_SET_QUEUE)) {
959             Bundle args = new Bundle();
960             args.putParcelable(KEY_SESSION_TOKEN, mSession.getSessionToken());
961             args.putInt(KEY_EXPECTED_QUEUE_SIZE, 0);
962             invoker.run(STEP_SET_UP, args);
963             mSession.setQueue(Collections.emptyList());
964             invoker.run(STEP_CHECK);
965             invoker.run(STEP_CLEAN_UP);
966         }
967     }
968 
969     /**
970      * Verifies that a new session hasn't had any configuration bits set yet.
971      *
972      * @param controller The controller for the session
973      */
verifyNewSession(MediaController controller)974     private void verifyNewSession(MediaController controller) {
975         assertEquals("New session has unexpected configuration", 0L, controller.getFlags());
976         assertNull("New session has unexpected configuration", controller.getExtras());
977         assertNull("New session has unexpected configuration", controller.getMetadata());
978         assertEquals("New session has unexpected configuration",
979                 getContext().getPackageName(), controller.getPackageName());
980         assertNull("New session has unexpected configuration", controller.getPlaybackState());
981         assertNull("New session has unexpected configuration", controller.getQueue());
982         assertNull("New session has unexpected configuration", controller.getQueueTitle());
983         assertEquals("New session has unexpected configuration", Rating.RATING_NONE,
984                 controller.getRatingType());
985         assertNull("New session has unexpected configuration", controller.getSessionActivity());
986 
987         assertNotNull(controller.getSessionToken());
988         assertNotNull(controller.getTransportControls());
989 
990         MediaController.PlaybackInfo info = controller.getPlaybackInfo();
991         assertNotNull(info);
992         info.toString(); // Test that calling PlaybackInfo.toString() does not crash.
993         assertEquals(MediaController.PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
994         AudioAttributes attrs = info.getAudioAttributes();
995         assertNotNull(attrs);
996         assertEquals(AudioAttributes.USAGE_MEDIA, attrs.getUsage());
997         assertEquals(mAudioManager.getStreamVolume(AudioManager.STREAM_MUSIC),
998                 info.getCurrentVolume());
999     }
1000 
1001     private class MediaControllerCallback extends MediaController.Callback {
1002         private volatile boolean mOnPlaybackStateChangedCalled;
1003         private volatile boolean mOnMetadataChangedCalled;
1004         private volatile boolean mOnQueueChangedCalled;
1005         private volatile boolean mOnQueueTitleChangedCalled;
1006         private volatile boolean mOnExtraChangedCalled;
1007         private volatile boolean mOnAudioInfoChangedCalled;
1008         private volatile boolean mOnSessionDestroyedCalled;
1009         private volatile boolean mOnSessionEventCalled;
1010 
1011         private volatile PlaybackState mPlaybackState;
1012         private volatile MediaMetadata mMediaMetadata;
1013         private volatile List<QueueItem> mQueue;
1014         private volatile CharSequence mTitle;
1015         private volatile String mEvent;
1016         private volatile Bundle mExtras;
1017         private volatile MediaController.PlaybackInfo mPlaybackInfo;
1018 
resetLocked()1019         public void resetLocked() {
1020             mOnPlaybackStateChangedCalled = false;
1021             mOnMetadataChangedCalled = false;
1022             mOnQueueChangedCalled = false;
1023             mOnQueueTitleChangedCalled = false;
1024             mOnExtraChangedCalled = false;
1025             mOnAudioInfoChangedCalled = false;
1026             mOnSessionDestroyedCalled = false;
1027             mOnSessionEventCalled = false;
1028 
1029             mPlaybackState = null;
1030             mMediaMetadata = null;
1031             mQueue = null;
1032             mTitle = null;
1033             mExtras = null;
1034             mPlaybackInfo = null;
1035         }
1036 
1037         @Override
onPlaybackStateChanged(PlaybackState state)1038         public void onPlaybackStateChanged(PlaybackState state) {
1039             synchronized (mWaitLock) {
1040                 mOnPlaybackStateChangedCalled = true;
1041                 mPlaybackState = state;
1042                 mWaitLock.notify();
1043             }
1044         }
1045 
1046         @Override
onMetadataChanged(MediaMetadata metadata)1047         public void onMetadataChanged(MediaMetadata metadata) {
1048             synchronized (mWaitLock) {
1049                 mOnMetadataChangedCalled = true;
1050                 mMediaMetadata = metadata;
1051                 mWaitLock.notify();
1052             }
1053         }
1054 
1055         @Override
onQueueChanged(List<QueueItem> queue)1056         public void onQueueChanged(List<QueueItem> queue) {
1057             synchronized (mWaitLock) {
1058                 mOnQueueChangedCalled = true;
1059                 mQueue = queue;
1060                 mWaitLock.notify();
1061             }
1062         }
1063 
1064         @Override
onQueueTitleChanged(CharSequence title)1065         public void onQueueTitleChanged(CharSequence title) {
1066             synchronized (mWaitLock) {
1067                 mOnQueueTitleChangedCalled = true;
1068                 mTitle = title;
1069                 mWaitLock.notify();
1070             }
1071         }
1072 
1073         @Override
onExtrasChanged(Bundle extras)1074         public void onExtrasChanged(Bundle extras) {
1075             synchronized (mWaitLock) {
1076                 mOnExtraChangedCalled = true;
1077                 mExtras = extras;
1078                 mWaitLock.notify();
1079             }
1080         }
1081 
1082         @Override
onAudioInfoChanged(MediaController.PlaybackInfo info)1083         public void onAudioInfoChanged(MediaController.PlaybackInfo info) {
1084             synchronized (mWaitLock) {
1085                 mOnAudioInfoChangedCalled = true;
1086                 mPlaybackInfo = info;
1087                 mWaitLock.notify();
1088             }
1089         }
1090 
1091         @Override
onSessionDestroyed()1092         public void onSessionDestroyed() {
1093             synchronized (mWaitLock) {
1094                 mOnSessionDestroyedCalled = true;
1095                 mWaitLock.notify();
1096             }
1097         }
1098 
1099         @Override
onSessionEvent(String event, Bundle extras)1100         public void onSessionEvent(String event, Bundle extras) {
1101             synchronized (mWaitLock) {
1102                 mOnSessionEventCalled = true;
1103                 mEvent = event;
1104                 mExtras = (Bundle) extras.clone();
1105                 mWaitLock.notify();
1106             }
1107         }
1108     }
1109 
1110     private class MediaSessionCallback extends MediaSession.Callback {
1111         private CountDownLatch mLatch;
1112         private int mOnPlayCalledCount;
1113         private boolean mOnPauseCalled;
1114         private boolean mOnStopCalled;
1115         private boolean mOnFastForwardCalled;
1116         private boolean mOnRewindCalled;
1117         private boolean mOnSkipToPreviousCalled;
1118         private boolean mOnSkipToNextCalled;
1119         private RemoteUserInfo mCallerInfo;
1120 
reset(int count)1121         public void reset(int count) {
1122             mLatch = new CountDownLatch(count);
1123             mOnPlayCalledCount = 0;
1124             mOnPauseCalled = false;
1125             mOnStopCalled = false;
1126             mOnFastForwardCalled = false;
1127             mOnRewindCalled = false;
1128             mOnSkipToPreviousCalled = false;
1129             mOnSkipToNextCalled = false;
1130         }
1131 
await(long waitMs)1132         public boolean await(long waitMs) {
1133             try {
1134                 return mLatch.await(waitMs, TimeUnit.MILLISECONDS);
1135             } catch (InterruptedException e) {
1136                 return false;
1137             }
1138         }
1139 
1140         @Override
onPlay()1141         public void onPlay() {
1142             mOnPlayCalledCount++;
1143             mCallerInfo = mSession.getCurrentControllerInfo();
1144             setPlaybackState(PlaybackState.STATE_PLAYING);
1145             mLatch.countDown();
1146         }
1147 
1148         @Override
onPause()1149         public void onPause() {
1150             mOnPauseCalled = true;
1151             mCallerInfo = mSession.getCurrentControllerInfo();
1152             setPlaybackState(PlaybackState.STATE_PAUSED);
1153             mLatch.countDown();
1154         }
1155 
1156         @Override
onStop()1157         public void onStop() {
1158             mOnStopCalled = true;
1159             mCallerInfo = mSession.getCurrentControllerInfo();
1160             setPlaybackState(PlaybackState.STATE_STOPPED);
1161             mLatch.countDown();
1162         }
1163 
1164         @Override
onFastForward()1165         public void onFastForward() {
1166             mOnFastForwardCalled = true;
1167             mCallerInfo = mSession.getCurrentControllerInfo();
1168             mLatch.countDown();
1169         }
1170 
1171         @Override
onRewind()1172         public void onRewind() {
1173             mOnRewindCalled = true;
1174             mCallerInfo = mSession.getCurrentControllerInfo();
1175             mLatch.countDown();
1176         }
1177 
1178         @Override
onSkipToPrevious()1179         public void onSkipToPrevious() {
1180             mOnSkipToPreviousCalled = true;
1181             mCallerInfo = mSession.getCurrentControllerInfo();
1182             mLatch.countDown();
1183         }
1184 
1185         @Override
onSkipToNext()1186         public void onSkipToNext() {
1187             mOnSkipToNextCalled = true;
1188             mCallerInfo = mSession.getCurrentControllerInfo();
1189             mLatch.countDown();
1190         }
1191     }
1192 }
1193