MediaSession2Test.java revision c227e5c8c58de8656a0a31a1f48bf66e49c4a19d
1/*
2 * Copyright 2018 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package androidx.media;
18
19import static android.media.AudioAttributes.CONTENT_TYPE_MUSIC;
20
21import static androidx.media.VolumeProviderCompat.VOLUME_CONTROL_ABSOLUTE;
22import static androidx.media.VolumeProviderCompat.VOLUME_CONTROL_FIXED;
23
24import static org.junit.Assert.assertEquals;
25import static org.junit.Assert.assertFalse;
26import static org.junit.Assert.assertNotEquals;
27import static org.junit.Assert.assertNotNull;
28import static org.junit.Assert.assertNull;
29import static org.junit.Assert.assertSame;
30import static org.junit.Assert.assertTrue;
31import static org.junit.Assert.fail;
32
33import android.content.Context;
34import android.media.AudioManager;
35import android.os.Build;
36import android.os.Bundle;
37import android.os.Process;
38import android.os.ResultReceiver;
39import android.support.test.filters.SdkSuppress;
40import android.support.test.filters.SmallTest;
41import android.support.test.runner.AndroidJUnit4;
42
43import androidx.annotation.NonNull;
44import androidx.annotation.Nullable;
45import androidx.media.MediaController2.ControllerCallback;
46import androidx.media.MediaController2.PlaybackInfo;
47import androidx.media.MediaSession2.CommandButton;
48import androidx.media.MediaSession2.ControllerInfo;
49import androidx.media.MediaSession2.SessionCallback;
50
51import junit.framework.Assert;
52
53import org.junit.After;
54import org.junit.Before;
55import org.junit.Ignore;
56import org.junit.Test;
57import org.junit.runner.RunWith;
58
59import java.util.ArrayList;
60import java.util.List;
61import java.util.Set;
62import java.util.concurrent.CountDownLatch;
63import java.util.concurrent.TimeUnit;
64
65/**
66 * Tests {@link MediaSession2}.
67 */
68@SdkSuppress(minSdkVersion = Build.VERSION_CODES.KITKAT)
69@RunWith(AndroidJUnit4.class)
70@SmallTest
71public class MediaSession2Test extends MediaSession2TestBase {
72    private static final String TAG = "MediaSession2Test";
73
74    private MediaSession2 mSession;
75    private MockPlayer mPlayer;
76    private MockPlaylistAgent mMockAgent;
77
78    @Before
79    @Override
80    public void setUp() throws Exception {
81        super.setUp();
82        mPlayer = new MockPlayer(0);
83        mMockAgent = new MockPlaylistAgent();
84
85        mSession = new MediaSession2.Builder(mContext)
86                .setPlayer(mPlayer)
87                .setPlaylistAgent(mMockAgent)
88                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
89                    @Override
90                    public SessionCommandGroup2 onConnect(MediaSession2 session,
91                            ControllerInfo controller) {
92                        if (Process.myUid() == controller.getUid()) {
93                            return super.onConnect(session, controller);
94                        }
95                        return null;
96                    }
97                }).build();
98    }
99
100    @After
101    @Override
102    public void cleanUp() throws Exception {
103        super.cleanUp();
104        mSession.close();
105    }
106
107    @Test
108    public void testBuilder() {
109        prepareLooper();
110        MediaSession2.Builder builder = new MediaSession2.Builder(mContext);
111        try {
112            builder.setPlayer(null);
113            fail("null player shouldn't be allowed");
114        } catch (IllegalArgumentException e) {
115            // expected. pass-through
116        }
117        try {
118            builder.setId(null);
119            fail("null id shouldn't be allowed");
120        } catch (IllegalArgumentException e) {
121            // expected. pass-through
122        }
123    }
124
125    @Test
126    public void testPlayerStateChange() throws Exception {
127        prepareLooper();
128        final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING;
129        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
130        sHandler.postAndSync(new Runnable() {
131            @Override
132            public void run() {
133                mSession.close();
134                mSession = new MediaSession2.Builder(mContext)
135                        .setPlayer(mPlayer)
136                        .setSessionCallback(sHandlerExecutor, new SessionCallback() {
137                            @Override
138                            public void onPlayerStateChanged(MediaSession2 session,
139                                    MediaPlayerBase player, int state) {
140                                assertEquals(targetState, state);
141                                latchForSessionCallback.countDown();
142                            }
143                        }).build();
144            }
145        });
146
147        final CountDownLatch latchForControllerCallback = new CountDownLatch(1);
148        final MediaController2 controller =
149                createController(mSession.getToken(), true, new ControllerCallback() {
150                    @Override
151                    public void onPlayerStateChanged(MediaController2 controllerOut, int state) {
152                        assertEquals(targetState, state);
153                        latchForControllerCallback.countDown();
154                    }
155                });
156
157        mPlayer.notifyPlaybackState(targetState);
158        assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
159        assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
160        assertEquals(targetState, controller.getPlayerState());
161    }
162
163    @Test
164    public void testBufferingStateChange() throws Exception {
165        prepareLooper();
166        final List<MediaItem2> playlist = TestUtils.createPlaylist(5);
167
168        final MediaItem2 targetItem = playlist.get(3);
169        final int targetBufferingState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE;
170        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
171        sHandler.postAndSync(new Runnable() {
172            @Override
173            public void run() {
174                mSession.close();
175                mMockAgent.setPlaylist(playlist, null);
176                mSession = new MediaSession2.Builder(mContext)
177                        .setPlayer(mPlayer)
178                        .setPlaylistAgent(mMockAgent)
179                        .setSessionCallback(sHandlerExecutor, new SessionCallback() {
180                            @Override
181                            public void onBufferingStateChanged(MediaSession2 session,
182                                    MediaPlayerBase player, MediaItem2 item, int state) {
183                                assertEquals(targetItem, item);
184                                assertEquals(targetBufferingState, state);
185                                latchForSessionCallback.countDown();
186                            }
187                        }).build();
188            }
189        });
190
191        final CountDownLatch latchForControllerCallback = new CountDownLatch(1);
192        final MediaController2 controller =
193                createController(mSession.getToken(), true, new ControllerCallback() {
194                    @Override
195                    public void onBufferingStateChanged(MediaController2 controller,
196                            MediaItem2 item, int state) {
197                        assertEquals(targetItem, item);
198                        assertEquals(targetBufferingState, state);
199                        latchForControllerCallback.countDown();
200                    }
201                });
202
203        mPlayer.notifyBufferingStateChanged(targetItem.getDataSourceDesc(), targetBufferingState);
204        assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
205        assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
206        assertEquals(targetBufferingState, controller.getBufferingState());
207    }
208
209    @Test
210    public void testSeekCompleted() throws Exception {
211        prepareLooper();
212        final long testPosition = 1001;
213        final CountDownLatch latch = new CountDownLatch(1);
214        final SessionCallback callback = new SessionCallback() {
215            @Override
216            public void onSeekCompleted(MediaSession2 session, MediaPlayerBase mpb, long position) {
217                assertEquals(mPlayer, mpb);
218                assertEquals(testPosition, position);
219                latch.countDown();
220            }
221        };
222
223        try (MediaSession2 session = new MediaSession2.Builder(mContext)
224                .setPlayer(mPlayer)
225                .setPlaylistAgent(mMockAgent)
226                .setId("testSeekCompleted")
227                .setSessionCallback(sHandlerExecutor, callback).build()) {
228            mPlayer.mCurrentPosition = testPosition;
229            mPlayer.notifySeekCompleted(testPosition);
230            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
231        }
232    }
233
234    @Test
235    public void testCurrentDataSourceChanged() throws Exception {
236        prepareLooper();
237        final int listSize = 5;
238        final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
239        mMockAgent.setPlaylist(list, null);
240
241        final MediaItem2 currentItem = list.get(3);
242        mMockAgent.mCurrentMediaItem = currentItem;
243
244        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
245        try (MediaSession2 session = new MediaSession2.Builder(mContext)
246                .setPlayer(mPlayer)
247                .setPlaylistAgent(mMockAgent)
248                .setId("testCurrentDataSourceChanged")
249                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
250                    @Override
251                    public void onCurrentMediaItemChanged(MediaSession2 session,
252                            MediaPlayerBase player, MediaItem2 itemOut) {
253                        assertSame(currentItem, itemOut);
254                        latchForSessionCallback.countDown();
255                    }
256                }).build()) {
257
258            final CountDownLatch latchForControllerCallback = new CountDownLatch(1);
259            final MediaController2 controller =
260                    createController(mSession.getToken(), true, new ControllerCallback() {
261                        @Override
262                        public void onCurrentMediaItemChanged(MediaController2 controller,
263                                MediaItem2 item) {
264                            assertEquals(currentItem, item);
265                            latchForControllerCallback.countDown();
266                        }
267                    });
268
269            mPlayer.notifyCurrentDataSourceChanged(currentItem.getDataSourceDesc());
270            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
271            assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
272            assertEquals(currentItem, controller.getCurrentMediaItem());
273        }
274    }
275
276    @Test
277    public void testMediaPrepared() throws Exception {
278        prepareLooper();
279        final int listSize = 5;
280        final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
281        mMockAgent.setPlaylist(list, null);
282
283        final MediaItem2 currentItem = list.get(3);
284
285        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
286        try (MediaSession2 session = new MediaSession2.Builder(mContext)
287                .setPlayer(mPlayer)
288                .setPlaylistAgent(mMockAgent)
289                .setId("testMediaPrepared")
290                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
291                    @Override
292                    public void onMediaPrepared(MediaSession2 session, MediaPlayerBase player,
293                            MediaItem2 itemOut) {
294                        assertSame(currentItem, itemOut);
295                        latchForSessionCallback.countDown();
296                    }
297                }).build()) {
298
299            mPlayer.notifyMediaPrepared(currentItem.getDataSourceDesc());
300            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
301            // TODO(jaewan): Test that controllers are also notified. (b/74505936)
302        }
303    }
304
305    @Test
306    public void testBufferingStateChanged() throws Exception {
307        prepareLooper();
308        final int listSize = 5;
309        final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
310        mMockAgent.setPlaylist(list, null);
311
312        final MediaItem2 currentItem = list.get(3);
313        final int buffState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE;
314
315        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
316        try (MediaSession2 session = new MediaSession2.Builder(mContext)
317                .setPlayer(mPlayer)
318                .setPlaylistAgent(mMockAgent)
319                .setId("testBufferingStateChanged")
320                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
321                    @Override
322                    public void onBufferingStateChanged(MediaSession2 session,
323                            MediaPlayerBase player, MediaItem2 itemOut, int stateOut) {
324                        assertSame(currentItem, itemOut);
325                        assertEquals(buffState, stateOut);
326                        latchForSessionCallback.countDown();
327                    }
328                }).build()) {
329
330            mPlayer.notifyBufferingStateChanged(currentItem.getDataSourceDesc(), buffState);
331            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
332            // TODO(jaewan): Test that controllers are also notified. (b/74505936)
333        }
334    }
335
336    /**
337     * This also tests {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}
338     * and {@link MediaController2#getPlaybackSpeed()}.
339     */
340    @Test
341    public void testPlaybackSpeedChanged() throws Exception {
342        prepareLooper();
343        final float speed = 1.5f;
344        mPlayer.setPlaybackSpeed(speed);
345
346        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
347        try (MediaSession2 session = new MediaSession2.Builder(mContext)
348                .setPlayer(mPlayer)
349                .setId("testPlaybackSpeedChanged")
350                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
351                    @Override
352                    public void onPlaybackSpeedChanged(MediaSession2 session,
353                            MediaPlayerBase player, float speedOut) {
354                        assertEquals(speed, speedOut, 0.0f);
355                        latchForSessionCallback.countDown();
356                    }
357                }).build()) {
358
359            final CountDownLatch latchForControllerCallback = new CountDownLatch(1);
360            final MediaController2 controller =
361                    createController(mSession.getToken(), true, new ControllerCallback() {
362                        @Override
363                        public void onPlaybackSpeedChanged(MediaController2 controller,
364                                float speedOut) {
365                            assertEquals(speed, speedOut, 0.0f);
366                            latchForControllerCallback.countDown();
367                        }
368                    });
369
370            mPlayer.notifyPlaybackSpeedChanged(speed);
371            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
372            assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
373            assertEquals(speed, controller.getPlaybackSpeed(), 0.0f);
374        }
375    }
376
377    @Test
378    public void testUpdatePlayer() throws Exception {
379        prepareLooper();
380        final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING;
381        final CountDownLatch latch = new CountDownLatch(1);
382        sHandler.postAndSync(new Runnable() {
383            @Override
384            public void run() {
385                mSession.close();
386                mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
387                        .setSessionCallback(sHandlerExecutor, new SessionCallback() {
388                            @Override
389                            public void onPlayerStateChanged(MediaSession2 session,
390                                    MediaPlayerBase player, int state) {
391                                assertEquals(targetState, state);
392                                latch.countDown();
393                            }
394                        }).build();
395            }
396        });
397
398        MockPlayer player = new MockPlayer(0);
399
400        // Test if setPlayer doesn't crash with various situations.
401        mSession.updatePlayer(mPlayer, null, null);
402        assertEquals(mPlayer, mSession.getPlayer());
403        MediaPlaylistAgent agent = mSession.getPlaylistAgent();
404        assertNotNull(agent);
405
406        mSession.updatePlayer(player, null, null);
407        assertEquals(player, mSession.getPlayer());
408        assertNotNull(mSession.getPlaylistAgent());
409        assertNotEquals(agent, mSession.getPlaylistAgent());
410
411        player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING);
412        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
413    }
414
415    @Test
416    public void testUpdatePlayer_playbackInfo() throws Exception {
417        prepareLooper();
418        MockPlayer player = new MockPlayer(0);
419        final AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
420                .setContentType(CONTENT_TYPE_MUSIC)
421                .build();
422        player.setAudioAttributes(attrs);
423
424        final int maxVolume = 100;
425        final int currentVolume = 23;
426        final int volumeControlType = VOLUME_CONTROL_ABSOLUTE;
427        VolumeProviderCompat volumeProvider = new VolumeProviderCompat(
428                volumeControlType, maxVolume, currentVolume) { };
429
430        final CountDownLatch latch = new CountDownLatch(1);
431        final ControllerCallback callback = new ControllerCallback() {
432            @Override
433            public void onPlaybackInfoChanged(MediaController2 controller, PlaybackInfo info) {
434                Assert.assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
435                assertEquals(attrs, info.getAudioAttributes());
436                assertEquals(volumeControlType, info.getPlaybackType());
437                assertEquals(maxVolume, info.getMaxVolume());
438                assertEquals(currentVolume, info.getCurrentVolume());
439                latch.countDown();
440            }
441        };
442
443        mSession.updatePlayer(player, null, null);
444
445        final MediaController2 controller = createController(mSession.getToken(), true, callback);
446        PlaybackInfo info = controller.getPlaybackInfo();
447        assertNotNull(info);
448        assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
449        assertEquals(attrs, info.getAudioAttributes());
450        AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
451
452        int localVolumeControlType = VOLUME_CONTROL_ABSOLUTE;
453        if (Build.VERSION.SDK_INT >= 21 && manager.isVolumeFixed()) {
454            localVolumeControlType = VOLUME_CONTROL_FIXED;
455        }
456        assertEquals(localVolumeControlType, info.getControlType());
457        assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume());
458        assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume());
459
460        mSession.updatePlayer(player, null, volumeProvider);
461        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
462
463        info = controller.getPlaybackInfo();
464        assertNotNull(info);
465        assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
466        assertEquals(attrs, info.getAudioAttributes());
467        assertEquals(volumeControlType, info.getControlType());
468        assertEquals(maxVolume, info.getMaxVolume());
469        assertEquals(currentVolume, info.getCurrentVolume());
470    }
471
472    @Test
473    public void testPlay() throws Exception {
474        prepareLooper();
475        mSession.play();
476        assertTrue(mPlayer.mPlayCalled);
477    }
478
479    @Test
480    public void testPause() throws Exception {
481        prepareLooper();
482        mSession.pause();
483        assertTrue(mPlayer.mPauseCalled);
484    }
485
486    @Test
487    public void testReset() throws Exception {
488        prepareLooper();
489        mSession.reset();
490        assertTrue(mPlayer.mResetCalled);
491    }
492
493    @Test
494    public void testPrepare() throws Exception {
495        prepareLooper();
496        mSession.prepare();
497        assertTrue(mPlayer.mPrepareCalled);
498    }
499
500    @Test
501    public void testSeekTo() throws Exception {
502        prepareLooper();
503        final long pos = 1004L;
504        mSession.seekTo(pos);
505        assertTrue(mPlayer.mSeekToCalled);
506        assertEquals(pos, mPlayer.mSeekPosition);
507    }
508
509    @Test
510    public void testSetPlaybackSpeed() throws Exception {
511        prepareLooper();
512        final float speed = 1.5f;
513        mSession.setPlaybackSpeed(speed);
514        assertTrue(mPlayer.mSetPlaybackSpeedCalled);
515        assertEquals(speed, mPlayer.mPlaybackSpeed, 0.0f);
516    }
517
518    @Test
519    public void testGetPlaybackSpeed() throws Exception {
520        prepareLooper();
521        final float speed = 1.5f;
522        mPlayer.setPlaybackSpeed(speed);
523        assertEquals(speed, mSession.getPlaybackSpeed(), 0.0f);
524    }
525
526    @Test
527    public void testGetCurrentMediaItem() {
528        prepareLooper();
529        MediaItem2 item = TestUtils.createMediaItemWithMetadata();
530        mMockAgent.mCurrentMediaItem = item;
531        assertEquals(item, mSession.getCurrentMediaItem());
532    }
533
534    @Test
535    public void testSkipToPreviousItem() {
536        prepareLooper();
537        mSession.skipToPreviousItem();
538        assertTrue(mMockAgent.mSkipToPreviousItemCalled);
539    }
540
541    @Test
542    public void testSkipToNextItem() throws Exception {
543        prepareLooper();
544        mSession.skipToNextItem();
545        assertTrue(mMockAgent.mSkipToNextItemCalled);
546    }
547
548    @Test
549    public void testSkipToPlaylistItem() throws Exception {
550        prepareLooper();
551        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
552        mSession.skipToPlaylistItem(testMediaItem);
553        assertTrue(mMockAgent.mSkipToPlaylistItemCalled);
554        assertSame(testMediaItem, mMockAgent.mItem);
555    }
556
557    @Test
558    public void testGetPlayerState() {
559        prepareLooper();
560        final int state = MediaPlayerBase.PLAYER_STATE_PLAYING;
561        mPlayer.mLastPlayerState = state;
562        assertEquals(state, mSession.getPlayerState());
563    }
564
565    @Test
566    public void testGetBufferingState() {
567        prepareLooper();
568        final int bufferingState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE;
569        mPlayer.mLastBufferingState = bufferingState;
570        assertEquals(bufferingState, mSession.getBufferingState());
571    }
572
573    @Test
574    public void testGetPosition() {
575        prepareLooper();
576        final long position = 150000;
577        mPlayer.mCurrentPosition = position;
578        assertEquals(position, mSession.getCurrentPosition());
579    }
580
581    @Test
582    public void testGetBufferedPosition() {
583        prepareLooper();
584        final long bufferedPosition = 900000;
585        mPlayer.mBufferedPosition = bufferedPosition;
586        assertEquals(bufferedPosition, mSession.getBufferedPosition());
587    }
588
589    @Test
590    public void testSetPlaylist() {
591        prepareLooper();
592        final List<MediaItem2> list = TestUtils.createPlaylist(2);
593        mSession.setPlaylist(list, null);
594        assertTrue(mMockAgent.mSetPlaylistCalled);
595        assertSame(list, mMockAgent.mPlaylist);
596        assertNull(mMockAgent.mMetadata);
597    }
598
599    @Test
600    public void testGetPlaylist() {
601        prepareLooper();
602        final List<MediaItem2> list = TestUtils.createPlaylist(2);
603        mMockAgent.mPlaylist = list;
604        assertEquals(list, mSession.getPlaylist());
605    }
606
607    @Test
608    public void testUpdatePlaylistMetadata() {
609        prepareLooper();
610        final MediaMetadata2 testMetadata = TestUtils.createMetadata();
611        mSession.updatePlaylistMetadata(testMetadata);
612        assertTrue(mMockAgent.mUpdatePlaylistMetadataCalled);
613        assertSame(testMetadata, mMockAgent.mMetadata);
614    }
615
616    @Test
617    public void testGetPlaylistMetadata() {
618        prepareLooper();
619        final MediaMetadata2 testMetadata = TestUtils.createMetadata();
620        mMockAgent.mMetadata = testMetadata;
621        assertEquals(testMetadata, mSession.getPlaylistMetadata());
622    }
623
624    @Test
625    public void testSessionCallback_onPlaylistChanged() throws InterruptedException {
626        prepareLooper();
627        final List<MediaItem2> list = TestUtils.createPlaylist(2);
628        final CountDownLatch latch = new CountDownLatch(1);
629        mMockAgent.setPlaylist(list, null);
630
631        final SessionCallback sessionCallback = new SessionCallback() {
632            @Override
633            public void onPlaylistChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent,
634                    List<MediaItem2> playlist, MediaMetadata2 metadata) {
635                assertEquals(mMockAgent, playlistAgent);
636                assertEquals(list, playlist);
637                assertNull(metadata);
638                latch.countDown();
639            }
640        };
641        try (MediaSession2 session = new MediaSession2.Builder(mContext)
642                .setPlayer(mPlayer)
643                .setPlaylistAgent(mMockAgent)
644                .setId("testSessionCallback")
645                .setSessionCallback(sHandlerExecutor, sessionCallback)
646                .build()) {
647            mMockAgent.notifyPlaylistChanged();
648            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
649        }
650    }
651
652    @Test
653    public void testAddPlaylistItem() {
654        prepareLooper();
655        final int testIndex = 12;
656        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
657        mSession.addPlaylistItem(testIndex, testMediaItem);
658        assertTrue(mMockAgent.mAddPlaylistItemCalled);
659        assertEquals(testIndex, mMockAgent.mIndex);
660        assertSame(testMediaItem, mMockAgent.mItem);
661    }
662
663    @Test
664    public void testRemovePlaylistItem() {
665        prepareLooper();
666        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
667        mSession.removePlaylistItem(testMediaItem);
668        assertTrue(mMockAgent.mRemovePlaylistItemCalled);
669        assertSame(testMediaItem, mMockAgent.mItem);
670    }
671
672    @Test
673    public void testReplacePlaylistItem() throws InterruptedException {
674        prepareLooper();
675        final int testIndex = 12;
676        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
677        mSession.replacePlaylistItem(testIndex, testMediaItem);
678        assertTrue(mMockAgent.mReplacePlaylistItemCalled);
679        assertEquals(testIndex, mMockAgent.mIndex);
680        assertSame(testMediaItem, mMockAgent.mItem);
681    }
682
683    /**
684     * This also tests {@link SessionCallback#onShuffleModeChanged}
685     */
686    @Test
687    public void testGetShuffleMode() throws InterruptedException {
688        prepareLooper();
689        final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
690        mMockAgent.setShuffleMode(testShuffleMode);
691
692        final CountDownLatch latch = new CountDownLatch(1);
693        final SessionCallback sessionCallback = new SessionCallback() {
694            @Override
695            public void onShuffleModeChanged(MediaSession2 session,
696                    MediaPlaylistAgent playlistAgent, int shuffleMode) {
697                assertEquals(mMockAgent, playlistAgent);
698                assertEquals(testShuffleMode, shuffleMode);
699                latch.countDown();
700            }
701        };
702        try (MediaSession2 session = new MediaSession2.Builder(mContext)
703                .setPlayer(mPlayer)
704                .setPlaylistAgent(mMockAgent)
705                .setId("testGetShuffleMode")
706                .setSessionCallback(sHandlerExecutor, sessionCallback)
707                .build()) {
708            mMockAgent.notifyShuffleModeChanged();
709            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
710        }
711    }
712
713    @Test
714    public void testSetShuffleMode() {
715        prepareLooper();
716        final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
717        mSession.setShuffleMode(testShuffleMode);
718        assertTrue(mMockAgent.mSetShuffleModeCalled);
719        assertEquals(testShuffleMode, mMockAgent.mShuffleMode);
720    }
721
722    /**
723     * This also tests {@link SessionCallback#onShuffleModeChanged}
724     */
725    @Test
726    public void testGetRepeatMode() throws InterruptedException {
727        prepareLooper();
728        final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
729        mMockAgent.setRepeatMode(testRepeatMode);
730
731        final CountDownLatch latch = new CountDownLatch(1);
732        final SessionCallback sessionCallback = new SessionCallback() {
733            @Override
734            public void onRepeatModeChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent,
735                    int repeatMode) {
736                assertEquals(mMockAgent, playlistAgent);
737                assertEquals(testRepeatMode, repeatMode);
738                latch.countDown();
739            }
740        };
741        try (MediaSession2 session = new MediaSession2.Builder(mContext)
742                .setPlayer(mPlayer)
743                .setPlaylistAgent(mMockAgent)
744                .setId("testGetRepeatMode")
745                .setSessionCallback(sHandlerExecutor, sessionCallback)
746                .build()) {
747            mMockAgent.notifyRepeatModeChanged();
748            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
749        }
750    }
751
752    @Test
753    public void testSetRepeatMode() {
754        prepareLooper();
755        final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
756        mSession.setRepeatMode(testRepeatMode);
757        assertTrue(mMockAgent.mSetRepeatModeCalled);
758        assertEquals(testRepeatMode, mMockAgent.mRepeatMode);
759    }
760
761    // TODO(jaewan): Revisit
762    @Ignore
763    @Test
764    public void testBadPlayer() throws InterruptedException {
765        prepareLooper();
766        // TODO(jaewan): Add equivalent tests again
767        final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
768        final BadPlayer player = new BadPlayer(0);
769
770        mSession.updatePlayer(player, null, null);
771        mSession.updatePlayer(mPlayer, null, null);
772        player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PAUSED);
773        assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
774    }
775
776    // This bad player will keep push events to the listener that is previously
777    // registered by session.setPlayer().
778    private static class BadPlayer extends MockPlayer {
779        BadPlayer(int count) {
780            super(count);
781        }
782
783        @Override
784        public void unregisterPlayerEventCallback(
785                @NonNull MediaPlayerBase.PlayerEventCallback listener) {
786            // No-op.
787        }
788    }
789
790    @Test
791    public void testOnCommandCallback() throws InterruptedException {
792        prepareLooper();
793        final MockOnCommandCallback callback = new MockOnCommandCallback();
794        sHandler.postAndSync(new Runnable() {
795            @Override
796            public void run() {
797                mSession.close();
798                mPlayer = new MockPlayer(1);
799                mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
800                        .setSessionCallback(sHandlerExecutor, callback).build();
801            }
802        });
803        MediaController2 controller = createController(mSession.getToken());
804        controller.pause();
805        assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
806        assertFalse(mPlayer.mPauseCalled);
807        assertEquals(1, callback.commands.size());
808        assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE,
809                (long) callback.commands.get(0).getCommandCode());
810
811        controller.play();
812        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
813        assertTrue(mPlayer.mPlayCalled);
814        assertFalse(mPlayer.mPauseCalled);
815        assertEquals(2, callback.commands.size());
816        assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY,
817                (long) callback.commands.get(1).getCommandCode());
818    }
819
820    @Test
821    public void testOnConnectCallback() throws InterruptedException {
822        prepareLooper();
823        final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
824        sHandler.postAndSync(new Runnable() {
825            @Override
826            public void run() {
827                mSession.close();
828                mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
829                        .setSessionCallback(sHandlerExecutor, sessionCallback).build();
830            }
831        });
832        MediaController2 controller = createController(mSession.getToken(), false, null);
833        assertNotNull(controller);
834        waitForConnect(controller, false);
835        waitForDisconnect(controller, true);
836    }
837
838    @Test
839    public void testOnDisconnectCallback() throws InterruptedException {
840        prepareLooper();
841        final CountDownLatch latch = new CountDownLatch(1);
842        try (MediaSession2 session = new MediaSession2.Builder(mContext)
843                .setPlayer(mPlayer)
844                .setId("testOnDisconnectCallback")
845                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
846                    @Override
847                    public void onDisconnected(MediaSession2 session,
848                            ControllerInfo controller) {
849                        assertEquals(Process.myUid(), controller.getUid());
850                        latch.countDown();
851                    }
852                }).build()) {
853            MediaController2 controller = createController(session.getToken());
854            controller.close();
855            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
856        }
857    }
858
859    @Test
860    public void testSetCustomLayout() throws InterruptedException {
861        prepareLooper();
862        final List<CommandButton> buttons = new ArrayList<>();
863        buttons.add(new CommandButton.Builder()
864                .setCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY))
865                .setDisplayName("button").build());
866        final CountDownLatch latch = new CountDownLatch(1);
867        final SessionCallback sessionCallback = new SessionCallback() {
868            @Override
869            public SessionCommandGroup2 onConnect(MediaSession2 session,
870                    ControllerInfo controller) {
871                if (mContext.getPackageName().equals(controller.getPackageName())) {
872                    mSession.setCustomLayout(controller, buttons);
873                }
874                return super.onConnect(session, controller);
875            }
876        };
877
878        try (MediaSession2 session = new MediaSession2.Builder(mContext)
879                .setPlayer(mPlayer)
880                .setId("testSetCustomLayout")
881                .setSessionCallback(sHandlerExecutor, sessionCallback)
882                .build()) {
883            if (mSession != null) {
884                mSession.close();
885                mSession = session;
886            }
887            final ControllerCallback callback = new ControllerCallback() {
888                @Override
889                public void onCustomLayoutChanged(MediaController2 controller2,
890                        List<CommandButton> layout) {
891                    assertEquals(layout.size(), buttons.size());
892                    for (int i = 0; i < layout.size(); i++) {
893                        assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand());
894                        assertEquals(layout.get(i).getDisplayName(),
895                                buttons.get(i).getDisplayName());
896                    }
897                    latch.countDown();
898                }
899            };
900            final MediaController2 controller =
901                    createController(session.getToken(), true, callback);
902            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
903        }
904    }
905
906    @Test
907    public void testSetAllowedCommands() throws InterruptedException {
908        prepareLooper();
909        final SessionCommandGroup2 commands = new SessionCommandGroup2();
910        commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY));
911        commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE));
912        commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_RESET));
913
914        final CountDownLatch latch = new CountDownLatch(1);
915        final ControllerCallback callback = new ControllerCallback() {
916            @Override
917            public void onAllowedCommandsChanged(MediaController2 controller,
918                    SessionCommandGroup2 commandsOut) {
919                assertNotNull(commandsOut);
920                Set<SessionCommand2> expected = commands.getCommands();
921                Set<SessionCommand2> actual = commandsOut.getCommands();
922
923                assertNotNull(actual);
924                assertEquals(expected.size(), actual.size());
925                for (SessionCommand2 command : expected) {
926                    assertTrue(actual.contains(command));
927                }
928                latch.countDown();
929            }
930        };
931
932        final MediaController2 controller = createController(mSession.getToken(), true, callback);
933        ControllerInfo controllerInfo = getTestControllerInfo();
934        assertNotNull(controllerInfo);
935
936        mSession.setAllowedCommands(controllerInfo, commands);
937        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
938    }
939
940    @Test
941    public void testSendCustomCommand() throws InterruptedException {
942        prepareLooper();
943        final SessionCommand2 testCommand = new SessionCommand2(
944                SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE);
945        final Bundle testArgs = new Bundle();
946        testArgs.putString("args", "testSendCustomAction");
947
948        final CountDownLatch latch = new CountDownLatch(2);
949        final ControllerCallback callback = new ControllerCallback() {
950            @Override
951            public void onCustomCommand(MediaController2 controller, SessionCommand2 command,
952                    Bundle args, ResultReceiver receiver) {
953                assertEquals(testCommand, command);
954                assertTrue(TestUtils.equals(testArgs, args));
955                assertNull(receiver);
956                latch.countDown();
957            }
958        };
959        final MediaController2 controller =
960                createController(mSession.getToken(), true, callback);
961        // TODO(jaewan): Test with multiple controllers
962        mSession.sendCustomCommand(testCommand, testArgs);
963
964        ControllerInfo controllerInfo = getTestControllerInfo();
965        assertNotNull(controllerInfo);
966        // TODO(jaewan): Test receivers as well.
967        mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null);
968        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
969    }
970
971    @Test
972    public void testNotifyError() throws InterruptedException {
973        prepareLooper();
974        final int errorCode = MediaSession2.ERROR_CODE_NOT_AVAILABLE_IN_REGION;
975        final Bundle extras = new Bundle();
976        extras.putString("args", "testNotifyError");
977
978        final CountDownLatch latch = new CountDownLatch(1);
979        final ControllerCallback callback = new ControllerCallback() {
980            @Override
981            public void onError(MediaController2 controller, int errorCodeOut, Bundle extrasOut) {
982                assertEquals(errorCode, errorCodeOut);
983                assertTrue(TestUtils.equals(extras, extrasOut));
984                latch.countDown();
985            }
986        };
987        final MediaController2 controller = createController(mSession.getToken(), true, callback);
988        // TODO(jaewan): Test with multiple controllers
989        mSession.notifyError(errorCode, extras);
990        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
991    }
992
993    @Test
994    public void testNotifyRoutesInfoChanged() throws InterruptedException {
995        prepareLooper();
996        final CountDownLatch latch = new CountDownLatch(1);
997        final ControllerCallback callback = new ControllerCallback() {
998            @Override
999            public void onRoutesInfoChanged(@NonNull MediaController2 controller,
1000                    @Nullable List<Bundle> routes) {
1001                assertNull(routes);
1002                latch.countDown();
1003            }
1004        };
1005        final MediaController2 controller = createController(mSession.getToken(), true, callback);
1006        ControllerInfo controllerInfo = getTestControllerInfo();
1007        mSession.notifyRoutesInfoChanged(controllerInfo, null);
1008        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
1009    }
1010
1011    private ControllerInfo getTestControllerInfo() {
1012        List<ControllerInfo> controllers = mSession.getConnectedControllers();
1013        assertNotNull(controllers);
1014        for (int i = 0; i < controllers.size(); i++) {
1015            if (Process.myUid() == controllers.get(i).getUid()) {
1016                return controllers.get(i);
1017            }
1018        }
1019        fail("Failed to get test controller info");
1020        return null;
1021    }
1022
1023    public class MockOnConnectCallback extends SessionCallback {
1024        @Override
1025        public SessionCommandGroup2 onConnect(MediaSession2 session,
1026                ControllerInfo controllerInfo) {
1027            if (Process.myUid() != controllerInfo.getUid()) {
1028                return null;
1029            }
1030            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
1031            assertEquals(Process.myUid(), controllerInfo.getUid());
1032            assertFalse(controllerInfo.isTrusted());
1033            // Reject all
1034            return null;
1035        }
1036    }
1037
1038    public class MockOnCommandCallback extends SessionCallback {
1039        public final ArrayList<SessionCommand2> commands = new ArrayList<>();
1040
1041        @Override
1042        public boolean onCommandRequest(MediaSession2 session, ControllerInfo controllerInfo,
1043                SessionCommand2 command) {
1044            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
1045            assertEquals(Process.myUid(), controllerInfo.getUid());
1046            assertFalse(controllerInfo.isTrusted());
1047            commands.add(command);
1048            if (command.getCommandCode() == SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE) {
1049                return false;
1050            }
1051            return true;
1052        }
1053    }
1054
1055    private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) {
1056        if (a == null || b == null) {
1057            assertEquals(a, b);
1058        }
1059        assertEquals(a.size(), b.size());
1060
1061        for (int i = 0; i < a.size(); i++) {
1062            MediaItem2 aItem = a.get(i);
1063            MediaItem2 bItem = b.get(i);
1064
1065            if (aItem == null || bItem == null) {
1066                assertEquals(aItem, bItem);
1067                continue;
1068            }
1069
1070            assertEquals(aItem.getMediaId(), bItem.getMediaId());
1071            assertEquals(aItem.getFlags(), bItem.getFlags());
1072            TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());
1073
1074            // Note: Here it does not check whether DataSourceDesc are equal,
1075            // since there DataSourceDec is not comparable.
1076        }
1077    }
1078}
1079