MediaSession2Test.java revision fcda4bbadd0246854701e02e1752f4994f2bc474
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        final CountDownLatch latchForSessionCallback = new CountDownLatch(2);
243        try (MediaSession2 session = new MediaSession2.Builder(mContext)
244                .setPlayer(mPlayer)
245                .setPlaylistAgent(mMockAgent)
246                .setId("testCurrentDataSourceChanged")
247                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
248                    @Override
249                    public void onCurrentMediaItemChanged(MediaSession2 session,
250                            MediaPlayerBase player, MediaItem2 item) {
251                        switch ((int) latchForSessionCallback.getCount()) {
252                            case 2:
253                                assertEquals(currentItem, item);
254                                break;
255                            case 1:
256                                assertNull(item);
257                        }
258                        latchForSessionCallback.countDown();
259                    }
260                }).build()) {
261
262            final CountDownLatch latchForControllerCallback = new CountDownLatch(2);
263            final MediaController2 controller =
264                    createController(mSession.getToken(), true, new ControllerCallback() {
265                        @Override
266                        public void onCurrentMediaItemChanged(MediaController2 controller,
267                                MediaItem2 item) {
268                            switch ((int) latchForControllerCallback.getCount()) {
269                                case 2:
270                                    assertEquals(currentItem, item);
271                                    break;
272                                case 1:
273                                    assertNull(item);
274                            }
275                            latchForControllerCallback.countDown();
276                        }
277                    });
278
279            // Player notifies with the unknown dsd. Should be ignored.
280            mPlayer.notifyCurrentDataSourceChanged(TestUtils.createMediaItemWithMetadata()
281                    .getDataSourceDesc());
282            // Known DSD should be notified through the onCurrentMediaItemChanged.
283            mPlayer.notifyCurrentDataSourceChanged(currentItem.getDataSourceDesc());
284            // Null DSD becomes null MediaItem2.
285            mPlayer.notifyCurrentDataSourceChanged(null);
286            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
287            assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
288        }
289    }
290
291    @Test
292    public void testMediaPrepared() throws Exception {
293        prepareLooper();
294        final int listSize = 5;
295        final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
296        mMockAgent.setPlaylist(list, null);
297
298        final MediaItem2 currentItem = list.get(3);
299
300        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
301        try (MediaSession2 session = new MediaSession2.Builder(mContext)
302                .setPlayer(mPlayer)
303                .setPlaylistAgent(mMockAgent)
304                .setId("testMediaPrepared")
305                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
306                    @Override
307                    public void onMediaPrepared(MediaSession2 session, MediaPlayerBase player,
308                            MediaItem2 itemOut) {
309                        assertSame(currentItem, itemOut);
310                        latchForSessionCallback.countDown();
311                    }
312                }).build()) {
313
314            mPlayer.notifyMediaPrepared(currentItem.getDataSourceDesc());
315            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
316            // TODO(jaewan): Test that controllers are also notified. (b/74505936)
317        }
318    }
319
320    @Test
321    public void testBufferingStateChanged() throws Exception {
322        prepareLooper();
323        final int listSize = 5;
324        final List<MediaItem2> list = TestUtils.createPlaylist(listSize);
325        mMockAgent.setPlaylist(list, null);
326
327        final MediaItem2 currentItem = list.get(3);
328        final int buffState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_COMPLETE;
329
330        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
331        try (MediaSession2 session = new MediaSession2.Builder(mContext)
332                .setPlayer(mPlayer)
333                .setPlaylistAgent(mMockAgent)
334                .setId("testBufferingStateChanged")
335                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
336                    @Override
337                    public void onBufferingStateChanged(MediaSession2 session,
338                            MediaPlayerBase player, MediaItem2 itemOut, int stateOut) {
339                        assertSame(currentItem, itemOut);
340                        assertEquals(buffState, stateOut);
341                        latchForSessionCallback.countDown();
342                    }
343                }).build()) {
344
345            mPlayer.notifyBufferingStateChanged(currentItem.getDataSourceDesc(), buffState);
346            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
347            // TODO(jaewan): Test that controllers are also notified. (b/74505936)
348        }
349    }
350
351    /**
352     * This also tests {@link ControllerCallback#onPlaybackSpeedChanged(MediaController2, float)}
353     * and {@link MediaController2#getPlaybackSpeed()}.
354     */
355    @Test
356    public void testPlaybackSpeedChanged() throws Exception {
357        prepareLooper();
358        final float speed = 1.5f;
359        mPlayer.setPlaybackSpeed(speed);
360
361        final CountDownLatch latchForSessionCallback = new CountDownLatch(1);
362        try (MediaSession2 session = new MediaSession2.Builder(mContext)
363                .setPlayer(mPlayer)
364                .setId("testPlaybackSpeedChanged")
365                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
366                    @Override
367                    public void onPlaybackSpeedChanged(MediaSession2 session,
368                            MediaPlayerBase player, float speedOut) {
369                        assertEquals(speed, speedOut, 0.0f);
370                        latchForSessionCallback.countDown();
371                    }
372                }).build()) {
373
374            final CountDownLatch latchForControllerCallback = new CountDownLatch(1);
375            final MediaController2 controller =
376                    createController(mSession.getToken(), true, new ControllerCallback() {
377                        @Override
378                        public void onPlaybackSpeedChanged(MediaController2 controller,
379                                float speedOut) {
380                            assertEquals(speed, speedOut, 0.0f);
381                            latchForControllerCallback.countDown();
382                        }
383                    });
384
385            mPlayer.notifyPlaybackSpeedChanged(speed);
386            assertTrue(latchForSessionCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
387            assertTrue(latchForControllerCallback.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
388            assertEquals(speed, controller.getPlaybackSpeed(), 0.0f);
389        }
390    }
391
392    @Test
393    public void testUpdatePlayer() throws Exception {
394        prepareLooper();
395        final int targetState = MediaPlayerBase.PLAYER_STATE_PLAYING;
396        final CountDownLatch latch = new CountDownLatch(1);
397        sHandler.postAndSync(new Runnable() {
398            @Override
399            public void run() {
400                mSession.close();
401                mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
402                        .setSessionCallback(sHandlerExecutor, new SessionCallback() {
403                            @Override
404                            public void onPlayerStateChanged(MediaSession2 session,
405                                    MediaPlayerBase player, int state) {
406                                assertEquals(targetState, state);
407                                latch.countDown();
408                            }
409                        }).build();
410            }
411        });
412
413        MockPlayer player = new MockPlayer(0);
414
415        // Test if setPlayer doesn't crash with various situations.
416        mSession.updatePlayer(mPlayer, null, null);
417        assertEquals(mPlayer, mSession.getPlayer());
418        MediaPlaylistAgent agent = mSession.getPlaylistAgent();
419        assertNotNull(agent);
420
421        mSession.updatePlayer(player, null, null);
422        assertEquals(player, mSession.getPlayer());
423        assertNotNull(mSession.getPlaylistAgent());
424        assertNotEquals(agent, mSession.getPlaylistAgent());
425
426        player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PLAYING);
427        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
428    }
429
430    @Test
431    public void testUpdatePlayer_playbackInfo() throws Exception {
432        prepareLooper();
433        MockPlayer player = new MockPlayer(0);
434        final AudioAttributesCompat attrs = new AudioAttributesCompat.Builder()
435                .setContentType(CONTENT_TYPE_MUSIC)
436                .build();
437        player.setAudioAttributes(attrs);
438
439        final int maxVolume = 100;
440        final int currentVolume = 23;
441        final int volumeControlType = VOLUME_CONTROL_ABSOLUTE;
442        VolumeProviderCompat volumeProvider = new VolumeProviderCompat(
443                volumeControlType, maxVolume, currentVolume) { };
444
445        final CountDownLatch latch = new CountDownLatch(1);
446        final ControllerCallback callback = new ControllerCallback() {
447            @Override
448            public void onPlaybackInfoChanged(MediaController2 controller, PlaybackInfo info) {
449                Assert.assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
450                assertEquals(attrs, info.getAudioAttributes());
451                assertEquals(volumeControlType, info.getPlaybackType());
452                assertEquals(maxVolume, info.getMaxVolume());
453                assertEquals(currentVolume, info.getCurrentVolume());
454                latch.countDown();
455            }
456        };
457
458        mSession.updatePlayer(player, null, null);
459
460        final MediaController2 controller = createController(mSession.getToken(), true, callback);
461        PlaybackInfo info = controller.getPlaybackInfo();
462        assertNotNull(info);
463        assertEquals(PlaybackInfo.PLAYBACK_TYPE_LOCAL, info.getPlaybackType());
464        assertEquals(attrs, info.getAudioAttributes());
465        AudioManager manager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
466
467        int localVolumeControlType = VOLUME_CONTROL_ABSOLUTE;
468        if (Build.VERSION.SDK_INT >= 21 && manager.isVolumeFixed()) {
469            localVolumeControlType = VOLUME_CONTROL_FIXED;
470        }
471        assertEquals(localVolumeControlType, info.getControlType());
472        assertEquals(manager.getStreamMaxVolume(AudioManager.STREAM_MUSIC), info.getMaxVolume());
473        assertEquals(manager.getStreamVolume(AudioManager.STREAM_MUSIC), info.getCurrentVolume());
474
475        mSession.updatePlayer(player, null, volumeProvider);
476        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
477
478        info = controller.getPlaybackInfo();
479        assertNotNull(info);
480        assertEquals(PlaybackInfo.PLAYBACK_TYPE_REMOTE, info.getPlaybackType());
481        assertEquals(attrs, info.getAudioAttributes());
482        assertEquals(volumeControlType, info.getControlType());
483        assertEquals(maxVolume, info.getMaxVolume());
484        assertEquals(currentVolume, info.getCurrentVolume());
485    }
486
487    @Test
488    public void testPlay() throws Exception {
489        prepareLooper();
490        mSession.play();
491        assertTrue(mPlayer.mPlayCalled);
492    }
493
494    @Test
495    public void testPause() throws Exception {
496        prepareLooper();
497        mSession.pause();
498        assertTrue(mPlayer.mPauseCalled);
499    }
500
501    @Test
502    public void testReset() throws Exception {
503        prepareLooper();
504        mSession.reset();
505        assertTrue(mPlayer.mResetCalled);
506    }
507
508    @Test
509    public void testPrepare() throws Exception {
510        prepareLooper();
511        mSession.prepare();
512        assertTrue(mPlayer.mPrepareCalled);
513    }
514
515    @Test
516    public void testSeekTo() throws Exception {
517        prepareLooper();
518        final long pos = 1004L;
519        mSession.seekTo(pos);
520        assertTrue(mPlayer.mSeekToCalled);
521        assertEquals(pos, mPlayer.mSeekPosition);
522    }
523
524    @Test
525    public void testGetDuration() throws Exception {
526        prepareLooper();
527        final long testDuration = 9999;
528        mPlayer.mDuration = testDuration;
529        assertEquals(testDuration, mSession.getDuration());
530    }
531
532    @Test
533    public void testSessionCallback_onMediaPrepared() throws Exception {
534        prepareLooper();
535        final long testDuration = 9999;
536        final List<MediaItem2> list = TestUtils.createPlaylist(2);
537        final MediaItem2 testItem = list.get(1);
538        final CountDownLatch latch = new CountDownLatch(1);
539
540        mPlayer.mDuration = testDuration;
541        mMockAgent.setPlaylist(list, null);
542        mMockAgent.mCurrentMediaItem = testItem;
543
544        final SessionCallback sessionCallback = new SessionCallback() {
545            @Override
546            public void onMediaPrepared(MediaSession2 session, MediaPlayerBase player,
547                    MediaItem2 item) {
548                assertEquals(testItem, item);
549                assertEquals(testDuration,
550                        item.getMetadata().getLong(MediaMetadata2.METADATA_KEY_DURATION));
551                latch.countDown();
552            }
553        };
554        try (MediaSession2 session = new MediaSession2.Builder(mContext)
555                .setPlayer(mPlayer)
556                .setPlaylistAgent(mMockAgent)
557                .setId("testSessionCallback")
558                .setSessionCallback(sHandlerExecutor, sessionCallback)
559                .build()) {
560            mPlayer.notifyMediaPrepared(testItem.getDataSourceDesc());
561            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
562        }
563    }
564
565    @Test
566    public void testSetPlaybackSpeed() throws Exception {
567        prepareLooper();
568        final float speed = 1.5f;
569        mSession.setPlaybackSpeed(speed);
570        assertTrue(mPlayer.mSetPlaybackSpeedCalled);
571        assertEquals(speed, mPlayer.mPlaybackSpeed, 0.0f);
572    }
573
574    @Test
575    public void testGetPlaybackSpeed() throws Exception {
576        prepareLooper();
577        final float speed = 1.5f;
578        mPlayer.setPlaybackSpeed(speed);
579        assertEquals(speed, mSession.getPlaybackSpeed(), 0.0f);
580    }
581
582    @Test
583    public void testGetCurrentMediaItem() {
584        prepareLooper();
585        MediaItem2 item = TestUtils.createMediaItemWithMetadata();
586        mMockAgent.mCurrentMediaItem = item;
587        assertEquals(item, mSession.getCurrentMediaItem());
588    }
589
590    @Test
591    public void testSkipToPreviousItem() {
592        prepareLooper();
593        mSession.skipToPreviousItem();
594        assertTrue(mMockAgent.mSkipToPreviousItemCalled);
595    }
596
597    @Test
598    public void testSkipToNextItem() throws Exception {
599        prepareLooper();
600        mSession.skipToNextItem();
601        assertTrue(mMockAgent.mSkipToNextItemCalled);
602    }
603
604    @Test
605    public void testSkipToPlaylistItem() throws Exception {
606        prepareLooper();
607        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
608        mSession.skipToPlaylistItem(testMediaItem);
609        assertTrue(mMockAgent.mSkipToPlaylistItemCalled);
610        assertSame(testMediaItem, mMockAgent.mItem);
611    }
612
613    @Test
614    public void testGetPlayerState() {
615        prepareLooper();
616        final int state = MediaPlayerBase.PLAYER_STATE_PLAYING;
617        mPlayer.mLastPlayerState = state;
618        assertEquals(state, mSession.getPlayerState());
619    }
620
621    @Test
622    public void testGetBufferingState() {
623        prepareLooper();
624        final int bufferingState = MediaPlayerBase.BUFFERING_STATE_BUFFERING_AND_PLAYABLE;
625        mPlayer.mLastBufferingState = bufferingState;
626        assertEquals(bufferingState, mSession.getBufferingState());
627    }
628
629    @Test
630    public void testGetPosition() {
631        prepareLooper();
632        final long position = 150000;
633        mPlayer.mCurrentPosition = position;
634        assertEquals(position, mSession.getCurrentPosition());
635    }
636
637    @Test
638    public void testGetBufferedPosition() {
639        prepareLooper();
640        final long bufferedPosition = 900000;
641        mPlayer.mBufferedPosition = bufferedPosition;
642        assertEquals(bufferedPosition, mSession.getBufferedPosition());
643    }
644
645    @Test
646    public void testSetPlaylist() {
647        prepareLooper();
648        final List<MediaItem2> list = TestUtils.createPlaylist(2);
649        mSession.setPlaylist(list, null);
650        assertTrue(mMockAgent.mSetPlaylistCalled);
651        assertSame(list, mMockAgent.mPlaylist);
652        assertNull(mMockAgent.mMetadata);
653    }
654
655    @Test
656    public void testGetPlaylist() {
657        prepareLooper();
658        final List<MediaItem2> list = TestUtils.createPlaylist(2);
659        mMockAgent.mPlaylist = list;
660        assertEquals(list, mSession.getPlaylist());
661    }
662
663    @Test
664    public void testUpdatePlaylistMetadata() {
665        prepareLooper();
666        final MediaMetadata2 testMetadata = TestUtils.createMetadata();
667        mSession.updatePlaylistMetadata(testMetadata);
668        assertTrue(mMockAgent.mUpdatePlaylistMetadataCalled);
669        assertSame(testMetadata, mMockAgent.mMetadata);
670    }
671
672    @Test
673    public void testGetPlaylistMetadata() {
674        prepareLooper();
675        final MediaMetadata2 testMetadata = TestUtils.createMetadata();
676        mMockAgent.mMetadata = testMetadata;
677        assertEquals(testMetadata, mSession.getPlaylistMetadata());
678    }
679
680    @Test
681    public void testSessionCallback_onPlaylistChanged() throws InterruptedException {
682        prepareLooper();
683        final List<MediaItem2> list = TestUtils.createPlaylist(2);
684        final CountDownLatch latch = new CountDownLatch(1);
685        mMockAgent.setPlaylist(list, null);
686
687        final SessionCallback sessionCallback = new SessionCallback() {
688            @Override
689            public void onPlaylistChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent,
690                    List<MediaItem2> playlist, MediaMetadata2 metadata) {
691                assertEquals(mMockAgent, playlistAgent);
692                assertEquals(list, playlist);
693                assertNull(metadata);
694                latch.countDown();
695            }
696        };
697        try (MediaSession2 session = new MediaSession2.Builder(mContext)
698                .setPlayer(mPlayer)
699                .setPlaylistAgent(mMockAgent)
700                .setId("testSessionCallback")
701                .setSessionCallback(sHandlerExecutor, sessionCallback)
702                .build()) {
703            mMockAgent.notifyPlaylistChanged();
704            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
705        }
706    }
707
708    @Test
709    public void testAddPlaylistItem() {
710        prepareLooper();
711        final int testIndex = 12;
712        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
713        mSession.addPlaylistItem(testIndex, testMediaItem);
714        assertTrue(mMockAgent.mAddPlaylistItemCalled);
715        assertEquals(testIndex, mMockAgent.mIndex);
716        assertSame(testMediaItem, mMockAgent.mItem);
717    }
718
719    @Test
720    public void testRemovePlaylistItem() {
721        prepareLooper();
722        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
723        mSession.removePlaylistItem(testMediaItem);
724        assertTrue(mMockAgent.mRemovePlaylistItemCalled);
725        assertSame(testMediaItem, mMockAgent.mItem);
726    }
727
728    @Test
729    public void testReplacePlaylistItem() throws InterruptedException {
730        prepareLooper();
731        final int testIndex = 12;
732        final MediaItem2 testMediaItem = TestUtils.createMediaItemWithMetadata();
733        mSession.replacePlaylistItem(testIndex, testMediaItem);
734        assertTrue(mMockAgent.mReplacePlaylistItemCalled);
735        assertEquals(testIndex, mMockAgent.mIndex);
736        assertSame(testMediaItem, mMockAgent.mItem);
737    }
738
739    /**
740     * This also tests {@link SessionCallback#onShuffleModeChanged}
741     */
742    @Test
743    public void testGetShuffleMode() throws InterruptedException {
744        prepareLooper();
745        final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
746        mMockAgent.setShuffleMode(testShuffleMode);
747
748        final CountDownLatch latch = new CountDownLatch(1);
749        final SessionCallback sessionCallback = new SessionCallback() {
750            @Override
751            public void onShuffleModeChanged(MediaSession2 session,
752                    MediaPlaylistAgent playlistAgent, int shuffleMode) {
753                assertEquals(mMockAgent, playlistAgent);
754                assertEquals(testShuffleMode, shuffleMode);
755                latch.countDown();
756            }
757        };
758        try (MediaSession2 session = new MediaSession2.Builder(mContext)
759                .setPlayer(mPlayer)
760                .setPlaylistAgent(mMockAgent)
761                .setId("testGetShuffleMode")
762                .setSessionCallback(sHandlerExecutor, sessionCallback)
763                .build()) {
764            mMockAgent.notifyShuffleModeChanged();
765            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
766        }
767    }
768
769    @Test
770    public void testSetShuffleMode() {
771        prepareLooper();
772        final int testShuffleMode = MediaPlaylistAgent.SHUFFLE_MODE_GROUP;
773        mSession.setShuffleMode(testShuffleMode);
774        assertTrue(mMockAgent.mSetShuffleModeCalled);
775        assertEquals(testShuffleMode, mMockAgent.mShuffleMode);
776    }
777
778    /**
779     * This also tests {@link SessionCallback#onShuffleModeChanged}
780     */
781    @Test
782    public void testGetRepeatMode() throws InterruptedException {
783        prepareLooper();
784        final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
785        mMockAgent.setRepeatMode(testRepeatMode);
786
787        final CountDownLatch latch = new CountDownLatch(1);
788        final SessionCallback sessionCallback = new SessionCallback() {
789            @Override
790            public void onRepeatModeChanged(MediaSession2 session, MediaPlaylistAgent playlistAgent,
791                    int repeatMode) {
792                assertEquals(mMockAgent, playlistAgent);
793                assertEquals(testRepeatMode, repeatMode);
794                latch.countDown();
795            }
796        };
797        try (MediaSession2 session = new MediaSession2.Builder(mContext)
798                .setPlayer(mPlayer)
799                .setPlaylistAgent(mMockAgent)
800                .setId("testGetRepeatMode")
801                .setSessionCallback(sHandlerExecutor, sessionCallback)
802                .build()) {
803            mMockAgent.notifyRepeatModeChanged();
804            assertTrue(latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
805        }
806    }
807
808    @Test
809    public void testSetRepeatMode() {
810        prepareLooper();
811        final int testRepeatMode = MediaPlaylistAgent.REPEAT_MODE_GROUP;
812        mSession.setRepeatMode(testRepeatMode);
813        assertTrue(mMockAgent.mSetRepeatModeCalled);
814        assertEquals(testRepeatMode, mMockAgent.mRepeatMode);
815    }
816
817    // TODO(jaewan): Revisit
818    @Ignore
819    @Test
820    public void testBadPlayer() throws InterruptedException {
821        prepareLooper();
822        // TODO(jaewan): Add equivalent tests again
823        final CountDownLatch latch = new CountDownLatch(4); // expected call + 1
824        final BadPlayer player = new BadPlayer(0);
825
826        mSession.updatePlayer(player, null, null);
827        mSession.updatePlayer(mPlayer, null, null);
828        player.notifyPlaybackState(MediaPlayerBase.PLAYER_STATE_PAUSED);
829        assertFalse(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
830    }
831
832    // This bad player will keep push events to the listener that is previously
833    // registered by session.setPlayer().
834    private static class BadPlayer extends MockPlayer {
835        BadPlayer(int count) {
836            super(count);
837        }
838
839        @Override
840        public void unregisterPlayerEventCallback(
841                @NonNull MediaPlayerBase.PlayerEventCallback listener) {
842            // No-op.
843        }
844    }
845
846    @Test
847    public void testOnCommandCallback() throws InterruptedException {
848        prepareLooper();
849        final MockOnCommandCallback callback = new MockOnCommandCallback();
850        sHandler.postAndSync(new Runnable() {
851            @Override
852            public void run() {
853                mSession.close();
854                mPlayer = new MockPlayer(1);
855                mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
856                        .setSessionCallback(sHandlerExecutor, callback).build();
857            }
858        });
859        MediaController2 controller = createController(mSession.getToken());
860        controller.pause();
861        assertFalse(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
862        assertFalse(mPlayer.mPauseCalled);
863        assertEquals(1, callback.commands.size());
864        assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE,
865                (long) callback.commands.get(0).getCommandCode());
866
867        controller.play();
868        assertTrue(mPlayer.mCountDownLatch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
869        assertTrue(mPlayer.mPlayCalled);
870        assertFalse(mPlayer.mPauseCalled);
871        assertEquals(2, callback.commands.size());
872        assertEquals(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY,
873                (long) callback.commands.get(1).getCommandCode());
874    }
875
876    @Test
877    public void testOnConnectCallback() throws InterruptedException {
878        prepareLooper();
879        final MockOnConnectCallback sessionCallback = new MockOnConnectCallback();
880        sHandler.postAndSync(new Runnable() {
881            @Override
882            public void run() {
883                mSession.close();
884                mSession = new MediaSession2.Builder(mContext).setPlayer(mPlayer)
885                        .setSessionCallback(sHandlerExecutor, sessionCallback).build();
886            }
887        });
888        MediaController2 controller = createController(mSession.getToken(), false, null);
889        assertNotNull(controller);
890        waitForConnect(controller, false);
891        waitForDisconnect(controller, true);
892    }
893
894    @Test
895    public void testOnDisconnectCallback() throws InterruptedException {
896        prepareLooper();
897        final CountDownLatch latch = new CountDownLatch(1);
898        try (MediaSession2 session = new MediaSession2.Builder(mContext)
899                .setPlayer(mPlayer)
900                .setId("testOnDisconnectCallback")
901                .setSessionCallback(sHandlerExecutor, new SessionCallback() {
902                    @Override
903                    public void onDisconnected(MediaSession2 session,
904                            ControllerInfo controller) {
905                        assertEquals(Process.myUid(), controller.getUid());
906                        latch.countDown();
907                    }
908                }).build()) {
909            MediaController2 controller = createController(session.getToken());
910            controller.close();
911            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
912        }
913    }
914
915    @Test
916    public void testSetCustomLayout() throws InterruptedException {
917        prepareLooper();
918        final List<CommandButton> buttons = new ArrayList<>();
919        buttons.add(new CommandButton.Builder()
920                .setCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY))
921                .setDisplayName("button").build());
922        final CountDownLatch latch = new CountDownLatch(1);
923        final SessionCallback sessionCallback = new SessionCallback() {
924            @Override
925            public SessionCommandGroup2 onConnect(MediaSession2 session,
926                    ControllerInfo controller) {
927                if (mContext.getPackageName().equals(controller.getPackageName())) {
928                    mSession.setCustomLayout(controller, buttons);
929                }
930                return super.onConnect(session, controller);
931            }
932        };
933
934        try (MediaSession2 session = new MediaSession2.Builder(mContext)
935                .setPlayer(mPlayer)
936                .setId("testSetCustomLayout")
937                .setSessionCallback(sHandlerExecutor, sessionCallback)
938                .build()) {
939            if (mSession != null) {
940                mSession.close();
941                mSession = session;
942            }
943            final ControllerCallback callback = new ControllerCallback() {
944                @Override
945                public void onCustomLayoutChanged(MediaController2 controller2,
946                        List<CommandButton> layout) {
947                    assertEquals(layout.size(), buttons.size());
948                    for (int i = 0; i < layout.size(); i++) {
949                        assertEquals(layout.get(i).getCommand(), buttons.get(i).getCommand());
950                        assertEquals(layout.get(i).getDisplayName(),
951                                buttons.get(i).getDisplayName());
952                    }
953                    latch.countDown();
954                }
955            };
956            final MediaController2 controller =
957                    createController(session.getToken(), true, callback);
958            assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
959        }
960    }
961
962    @Test
963    public void testSetAllowedCommands() throws InterruptedException {
964        prepareLooper();
965        final SessionCommandGroup2 commands = new SessionCommandGroup2();
966        commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PLAY));
967        commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE));
968        commands.addCommand(new SessionCommand2(SessionCommand2.COMMAND_CODE_PLAYBACK_RESET));
969
970        final CountDownLatch latch = new CountDownLatch(1);
971        final ControllerCallback callback = new ControllerCallback() {
972            @Override
973            public void onAllowedCommandsChanged(MediaController2 controller,
974                    SessionCommandGroup2 commandsOut) {
975                assertNotNull(commandsOut);
976                Set<SessionCommand2> expected = commands.getCommands();
977                Set<SessionCommand2> actual = commandsOut.getCommands();
978
979                assertNotNull(actual);
980                assertEquals(expected.size(), actual.size());
981                for (SessionCommand2 command : expected) {
982                    assertTrue(actual.contains(command));
983                }
984                latch.countDown();
985            }
986        };
987
988        final MediaController2 controller = createController(mSession.getToken(), true, callback);
989        ControllerInfo controllerInfo = getTestControllerInfo();
990        assertNotNull(controllerInfo);
991
992        mSession.setAllowedCommands(controllerInfo, commands);
993        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
994    }
995
996    @Test
997    public void testSendCustomCommand() throws InterruptedException {
998        prepareLooper();
999        final SessionCommand2 testCommand = new SessionCommand2(
1000                SessionCommand2.COMMAND_CODE_PLAYBACK_PREPARE);
1001        final Bundle testArgs = new Bundle();
1002        testArgs.putString("args", "testSendCustomAction");
1003
1004        final CountDownLatch latch = new CountDownLatch(2);
1005        final ControllerCallback callback = new ControllerCallback() {
1006            @Override
1007            public void onCustomCommand(MediaController2 controller, SessionCommand2 command,
1008                    Bundle args, ResultReceiver receiver) {
1009                assertEquals(testCommand, command);
1010                assertTrue(TestUtils.equals(testArgs, args));
1011                assertNull(receiver);
1012                latch.countDown();
1013            }
1014        };
1015        final MediaController2 controller =
1016                createController(mSession.getToken(), true, callback);
1017        // TODO(jaewan): Test with multiple controllers
1018        mSession.sendCustomCommand(testCommand, testArgs);
1019
1020        ControllerInfo controllerInfo = getTestControllerInfo();
1021        assertNotNull(controllerInfo);
1022        // TODO(jaewan): Test receivers as well.
1023        mSession.sendCustomCommand(controllerInfo, testCommand, testArgs, null);
1024        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
1025    }
1026
1027    @Test
1028    public void testNotifyError() throws InterruptedException {
1029        prepareLooper();
1030        final int errorCode = MediaSession2.ERROR_CODE_NOT_AVAILABLE_IN_REGION;
1031        final Bundle extras = new Bundle();
1032        extras.putString("args", "testNotifyError");
1033
1034        final CountDownLatch latch = new CountDownLatch(1);
1035        final ControllerCallback callback = new ControllerCallback() {
1036            @Override
1037            public void onError(MediaController2 controller, int errorCodeOut, Bundle extrasOut) {
1038                assertEquals(errorCode, errorCodeOut);
1039                assertTrue(TestUtils.equals(extras, extrasOut));
1040                latch.countDown();
1041            }
1042        };
1043        final MediaController2 controller = createController(mSession.getToken(), true, callback);
1044        // TODO(jaewan): Test with multiple controllers
1045        mSession.notifyError(errorCode, extras);
1046        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
1047    }
1048
1049    @Test
1050    public void testNotifyRoutesInfoChanged() throws InterruptedException {
1051        prepareLooper();
1052        final CountDownLatch latch = new CountDownLatch(1);
1053        final ControllerCallback callback = new ControllerCallback() {
1054            @Override
1055            public void onRoutesInfoChanged(@NonNull MediaController2 controller,
1056                    @Nullable List<Bundle> routes) {
1057                assertNull(routes);
1058                latch.countDown();
1059            }
1060        };
1061        final MediaController2 controller = createController(mSession.getToken(), true, callback);
1062        ControllerInfo controllerInfo = getTestControllerInfo();
1063        mSession.notifyRoutesInfoChanged(controllerInfo, null);
1064        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
1065    }
1066
1067    private ControllerInfo getTestControllerInfo() {
1068        List<ControllerInfo> controllers = mSession.getConnectedControllers();
1069        assertNotNull(controllers);
1070        for (int i = 0; i < controllers.size(); i++) {
1071            if (Process.myUid() == controllers.get(i).getUid()) {
1072                return controllers.get(i);
1073            }
1074        }
1075        fail("Failed to get test controller info");
1076        return null;
1077    }
1078
1079    public class MockOnConnectCallback extends SessionCallback {
1080        @Override
1081        public SessionCommandGroup2 onConnect(MediaSession2 session,
1082                ControllerInfo controllerInfo) {
1083            if (Process.myUid() != controllerInfo.getUid()) {
1084                return null;
1085            }
1086            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
1087            assertEquals(Process.myUid(), controllerInfo.getUid());
1088            assertFalse(controllerInfo.isTrusted());
1089            // Reject all
1090            return null;
1091        }
1092    }
1093
1094    public class MockOnCommandCallback extends SessionCallback {
1095        public final ArrayList<SessionCommand2> commands = new ArrayList<>();
1096
1097        @Override
1098        public boolean onCommandRequest(MediaSession2 session, ControllerInfo controllerInfo,
1099                SessionCommand2 command) {
1100            assertEquals(mContext.getPackageName(), controllerInfo.getPackageName());
1101            assertEquals(Process.myUid(), controllerInfo.getUid());
1102            assertFalse(controllerInfo.isTrusted());
1103            commands.add(command);
1104            if (command.getCommandCode() == SessionCommand2.COMMAND_CODE_PLAYBACK_PAUSE) {
1105                return false;
1106            }
1107            return true;
1108        }
1109    }
1110
1111    private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) {
1112        if (a == null || b == null) {
1113            assertEquals(a, b);
1114        }
1115        assertEquals(a.size(), b.size());
1116
1117        for (int i = 0; i < a.size(); i++) {
1118            MediaItem2 aItem = a.get(i);
1119            MediaItem2 bItem = b.get(i);
1120
1121            if (aItem == null || bItem == null) {
1122                assertEquals(aItem, bItem);
1123                continue;
1124            }
1125
1126            assertEquals(aItem.getMediaId(), bItem.getMediaId());
1127            assertEquals(aItem.getFlags(), bItem.getFlags());
1128            TestUtils.equals(aItem.getMetadata().toBundle(), bItem.getMetadata().toBundle());
1129
1130            // Note: Here it does not check whether DataSourceDesc are equal,
1131            // since there DataSourceDec is not comparable.
1132        }
1133    }
1134}
1135