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
gugelfrei
Hyundo Moon 7 years ago committed by Jaewan Kim
parent 5830d047ed
commit 3923b1b716

@ -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);
/**

@ -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;

@ -77,11 +77,14 @@ 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()}.
*
*
* @param instance
* @param context
* @param player
@ -293,7 +296,11 @@ public class MediaSession2Impl implements MediaSession2Provider {
if (params == null) {
throw new IllegalArgumentException("PlaylistParams should not be null!");
}
mPlaylistParams = params;
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

@ -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++) {

@ -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.
}
}
}

@ -61,6 +61,7 @@ abstract class MediaSession2TestBase {
interface TestControllerCallbackInterface {
// Add methods in ControllerCallback/BrowserCallback that you want to test.
default void onPlaylistChanged(List<MediaItem2> playlist) {}
default void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {}
// Currently empty. Add methods in ControllerCallback/BrowserCallback that you want to test.
@ -216,6 +217,13 @@ abstract class MediaSession2TestBase {
}
}
@Override
public void onPlaylistChanged(List<MediaItem2> params) {
if (mCallbackProxy != null) {
mCallbackProxy.onPlaylistChanged(params);
}
}
@Override
public void onPlaylistParamsChanged(MediaSession2.PlaylistParams params) {
if (mCallbackProxy != null) {

@ -16,7 +16,6 @@
package android.media;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2.PlaylistParams;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -44,10 +43,13 @@ public class MockPlayer implements MediaPlayerInterface {
public long mSeekPosition;
public boolean mSetCurrentPlaylistItemCalled;
public int mItemIndex;
public boolean mSetPlaylistCalled;
public boolean mSetPlaylistParamsCalled;
public List<PlaybackListenerHolder> mListeners = new ArrayList<>();
public List<MediaItem2> mPlaylist;
public PlaylistParams mPlaylistParams;
private PlaybackState2 mLastPlaybackState;
private AudioAttributes mAudioAttributes;
@ -194,11 +196,13 @@ public class MockPlayer implements MediaPlayerInterface {
}
@Override
public void setPlaylist(List<MediaItem2> item, PlaylistParams param) {
public void setPlaylist(List<MediaItem2> playlist) {
mSetPlaylistCalled = true;
mPlaylist = playlist;
}
@Override
public List<MediaItem2> getPlaylist() {
return null;
return mPlaylist;
}
}

Loading…
Cancel
Save