Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 3923b1b7 authored by Hyundo Moon's avatar Hyundo Moon Committed by Jaewan Kim
Browse files

MediaSession2/Controller2: Add playlist support

This CL implements following APIs:
 - MediaSession2.get/setPlaylist
 - MediaController2.getPlaylist
 - MediaController2.ControllerCallback.onPlaylistChanged

Bug: 72537268
Test: Passed MediaSession2Test
Change-Id: I206bb1018cde38d7db296df0912d02272fe1c6c7
parent 5830d047
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import com.android.media.IMediaSession2;
 */
oneway interface IMediaSession2Callback {
    void onPlaybackStateChanged(in Bundle state);
    void onPlaylistChanged(in List<Bundle> playlist);
    void onPlaylistParamsChanged(in Bundle params);

    /**
+40 −2
Original line number Diff line number Diff line
@@ -70,6 +70,8 @@ public class MediaController2Impl implements MediaController2Provider {
    @GuardedBy("mLock")
    private PlaybackState2 mPlaybackState;
    @GuardedBy("mLock")
    private List<MediaItem2> mPlaylist;
    @GuardedBy("mLock")
    private PlaylistParams mPlaylistParams;

    // Assignment should be used with the lock hold, but should be used without a lock to prevent
@@ -346,8 +348,9 @@ public class MediaController2Impl implements MediaController2Provider {

    @Override
    public List<MediaItem2> getPlaylist_impl() {
        // TODO(jaewan): Implement
        return null;
        synchronized (mLock) {
            return mPlaylist;
        }
    }

    @Override
@@ -441,6 +444,26 @@ public class MediaController2Impl implements MediaController2Provider {
        });
    }

    private void pushPlaylistChanges(final List<Bundle> list) {
        final List<MediaItem2> playlist = new ArrayList<>();
        for (int i = 0; i < list.size(); i++) {
            MediaItem2 item = MediaItem2.fromBundle(mContext, list.get(i));
            if (item != null) {
                playlist.add(item);
            }
        }

        synchronized (mLock) {
            mPlaylist = playlist;
            mCallbackExecutor.execute(() -> {
                if (!mInstance.isConnected()) {
                    return;
                }
                mCallback.onPlaylistChanged(playlist);
            });
        }
    }

    // Called when the result for connecting to the session was delivered.
    // Should be used without a lock to prevent potential deadlock.
    private void onConnectionChangedNotLocked(IMediaSession2 sessionBinder,
@@ -545,6 +568,21 @@ public class MediaController2Impl implements MediaController2Provider {
            controller.pushPlaybackStateChanges(PlaybackState2.fromBundle(state));
        }

        @Override
        public void onPlaylistChanged(List<Bundle> playlist) throws RuntimeException {
            final MediaController2Impl controller;
            try {
                controller = getController();
            } catch (IllegalStateException e) {
                Log.w(TAG, "Don't fail silently here. Highly likely a bug");
                return;
            }
            if (playlist == null) {
                return;
            }
            controller.pushPlaylistChanges(playlist);
        }

        @Override
        public void onPlaylistParamsChanged(Bundle params) throws RuntimeException {
            final MediaController2Impl controller;
+23 −6
Original line number Diff line number Diff line
@@ -77,7 +77,10 @@ public class MediaSession2Impl implements MediaSession2Provider {
    private MediaPlayerInterface mPlayer;
    @GuardedBy("mLock")
    private MyPlaybackListener mListener;
    @GuardedBy("mLock")
    private PlaylistParams mPlaylistParams;
    @GuardedBy("mLock")
    private List<MediaItem2> mPlaylist;

    /**
     * Can be only called by the {@link Builder#build()}.
@@ -293,7 +296,11 @@ public class MediaSession2Impl implements MediaSession2Provider {
        if (params == null) {
            throw new IllegalArgumentException("PlaylistParams should not be null!");
        }
        ensureCallingThread();
        ensurePlayer();
        synchronized (mLock) {
            mPlaylistParams = params;
        }
        mPlayer.setPlaylistParams(params);
        mSessionStub.notifyPlaylistParamsChanged(params);
    }
@@ -334,14 +341,24 @@ public class MediaSession2Impl implements MediaSession2Provider {
    }

    @Override
    public void setPlaylist_impl(List<MediaItem2> playlist, PlaylistParams param) {
        // TODO(jaewan): Implement
    public void setPlaylist_impl(List<MediaItem2> playlist) {
        if (playlist == null) {
            throw new IllegalArgumentException("Playlist should not be null!");
        }
        ensureCallingThread();
        ensurePlayer();
        synchronized (mLock) {
            mPlaylist = playlist;
        }
        mPlayer.setPlaylist(playlist);
        mSessionStub.notifyPlaylistChanged(playlist);
    }

    @Override
    public List<MediaItem2> getPlaylist_impl() {
        // TODO(jaewan): Implement this
        return null;
        synchronized (mLock) {
            return mPlaylist;
        }
    }

    @Override
+27 −0
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package com.android.media;

import android.media.MediaItem2;
import android.media.MediaLibraryService2.BrowserRoot;
import android.media.MediaLibraryService2.MediaLibrarySessionCallback;
import android.media.MediaSession2;
@@ -345,6 +346,32 @@ public class MediaSession2Stub extends IMediaSession2.Stub {
        }
    }

    public void notifyPlaylistChanged(List<MediaItem2> playlist) {
        if (playlist == null) {
            return;
        }
        final List<Bundle> bundleList = new ArrayList<>();
        for (int i = 0; i < playlist.size(); i++) {
            if (playlist.get(i) != null) {
                Bundle bundle = playlist.get(i).toBundle();
                if (bundle != null) {
                    bundleList.add(bundle);
                }
            }
        }
        final List<ControllerInfo> list = getControllers();
        for (int i = 0; i < list.size(); i++) {
            IMediaSession2Callback callbackBinder =
                    ControllerInfoImpl.from(list.get(i)).getControllerBinder();
            try {
                callbackBinder.onPlaylistChanged(bundleList);
            } catch (RemoteException e) {
                Log.w(TAG, "Controller is gone", e);
                // TODO(jaewan): What to do when the controller is gone?
            }
        }
    }

    public void notifyPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
        final List<ControllerInfo> list = getControllers();
        for (int i = 0; i < list.size(); i++) {
+48 −0
Original line number Diff line number Diff line
@@ -144,6 +144,30 @@ public class MediaSession2Test extends MediaSession2TestBase {
        });
    }

    @Test
    public void testSetPlaylist() throws Exception {
        final List<MediaItem2> playlist = new ArrayList<>();

        final CountDownLatch latch = new CountDownLatch(1);
        final TestControllerCallbackInterface callback = new TestControllerCallbackInterface() {
            @Override
            public void onPlaylistChanged(List<MediaItem2> givenList) {
                assertMediaItemListEquals(playlist, givenList);
                latch.countDown();
            }
        };

        final MediaController2 controller = createController(mSession.getToken(), true, callback);
        mSession.setPlaylist(playlist);

        assertTrue(mPlayer.mSetPlaylistCalled);
        assertMediaItemListEquals(playlist, mPlayer.mPlaylist);
        assertMediaItemListEquals(playlist, mSession.getPlaylist());

        assertTrue(latch.await(WAIT_TIME_MS, TimeUnit.MILLISECONDS));
        assertMediaItemListEquals(playlist, controller.getPlaylist());
    }

    @Test
    public void testSetPlaylistParams() throws Exception {
        final PlaylistParams params = new PlaylistParams(
@@ -339,4 +363,28 @@ public class MediaSession2Test extends MediaSession2TestBase {
            return true;
        }
    }

    private static void assertMediaItemListEquals(List<MediaItem2> a, List<MediaItem2> b) {
        if (a == null || b == null) {
            assertEquals(a, b);
        }
        assertEquals(a.size(), b.size());

        for (int i = 0; i < a.size(); i++) {
            MediaItem2 aItem = a.get(i);
            MediaItem2 bItem = b.get(i);

            if (aItem == null || bItem == null) {
                assertEquals(aItem, bItem);
                continue;
            }

            assertEquals(aItem.getMediaId(), bItem.getMediaId());
            assertEquals(aItem.getFlags(), bItem.getFlags());
            TestUtils.equals(aItem.getMetadata().getBundle(), bItem.getMetadata().getBundle());

            // Note: Here it does not check whether DataSourceDesc are equal,
            // since there DataSourceDec is not comparable.
        }
    }
}
Loading