Loading packages/MediaComponents/src/com/android/media/IMediaController2.aidl +8 −4 Original line number Diff line number Diff line Loading @@ -31,15 +31,19 @@ import com.android.media.IMediaSession2; // TODO(jaewan): (Post P) Handle when the playlist becomes too huge. // Note that ParcelledSliceList isn't a good idea for the purpose. (see: b/37493677) oneway interface IMediaController2 { void onPlaybackStateChanged(in Bundle state); void onPlayerStateChanged(int state); void onPositionChanged(long eventTimeMs, long positionMs); void onPlaybackSpeedChanged(float speed); void onBufferedPositionChanged(long bufferedPositionMs); void onPlaylistChanged(in List<Bundle> playlist, in Bundle metadata); void onPlaylistMetadataChanged(in Bundle metadata); void onPlaylistParamsChanged(in Bundle params); void onPlaybackInfoChanged(in Bundle playbackInfo); void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState, in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, in PendingIntent sessionActivity); void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, long bufferedPositionMs, in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, in PendingIntent sessionActivity); void onDisconnected(); void onCustomLayoutChanged(in List<Bundle> commandButtonlist); Loading packages/MediaComponents/src/com/android/media/MediaController2Impl.java +88 −24 Original line number Diff line number Diff line Loading @@ -16,7 +16,18 @@ package com.android.media; import static android.media.MediaSession2.*; import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_ADD_ITEM; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA; import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID; import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH; import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI; import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID; import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH; import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_URI; import android.app.PendingIntent; import android.content.ComponentName; Loading @@ -35,7 +46,6 @@ import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.PlaylistParams; import android.media.MediaSessionService2; import android.media.PlaybackState2; import android.media.Rating2; import android.media.SessionToken2; import android.media.update.MediaController2Provider; Loading Loading @@ -73,14 +83,22 @@ public class MediaController2Impl implements MediaController2Provider { @GuardedBy("mLock") private boolean mIsReleased; @GuardedBy("mLock") private PlaybackState2 mPlaybackState; @GuardedBy("mLock") private List<MediaItem2> mPlaylist; @GuardedBy("mLock") private MediaMetadata2 mPlaylistMetadata; @GuardedBy("mLock") private PlaylistParams mPlaylistParams; @GuardedBy("mLock") private int mPlayerState; @GuardedBy("mLock") private long mPositionEventTimeMs; @GuardedBy("mLock") private long mPositionMs; @GuardedBy("mLock") private float mPlaybackSpeed; @GuardedBy("mLock") private long mBufferedPositionMs; @GuardedBy("mLock") private PlaybackInfo mPlaybackInfo; @GuardedBy("mLock") private PendingIntent mSessionActivity; Loading Loading @@ -613,13 +631,6 @@ public class MediaController2Impl implements MediaController2Provider { */ } @Override public PlaybackState2 getPlaybackState_impl() { synchronized (mLock) { return mPlaybackState; } } @Override public void addPlaylistItem_impl(int index, MediaItem2 item) { if (index < 0) { Loading Loading @@ -703,26 +714,32 @@ public class MediaController2Impl implements MediaController2Provider { @Override public int getPlayerState_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { return mPlayerState; } } @Override public long getPosition_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { long timeDiff = System.currentTimeMillis() - mPositionEventTimeMs; long expectedPosition = mPositionMs + (long) (mPlaybackSpeed * timeDiff); return Math.max(0, expectedPosition); } } @Override public float getPlaybackSpeed_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { return mPlaybackSpeed; } } @Override public long getBufferedPosition_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { return mBufferedPositionMs; } } @Override Loading @@ -731,15 +748,52 @@ public class MediaController2Impl implements MediaController2Provider { return null; } void pushPlaybackStateChanges(final PlaybackState2 state) { void pushPlayerStateChanges(final int state) { synchronized (mLock) { mPlayerState = state; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlayerStateChanged(mInstance, state); }); } void pushPositionChanges(final long eventTimeMs, final long positionMs) { synchronized (mLock) { mPositionEventTimeMs = eventTimeMs; mPositionMs = positionMs; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPositionChanged(mInstance, eventTimeMs, positionMs); }); } void pushPlaybackSpeedChanges(final float speed) { synchronized (mLock) { mPlaybackSpeed = speed; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlaybackSpeedChanged(mInstance, speed); }); } void pushBufferedPositionChanges(final long bufferedPositionMs) { synchronized (mLock) { mPlaybackState = state; mBufferedPositionMs = bufferedPositionMs; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlaybackStateChanged(mInstance, state); mCallback.onBufferedPositionChanged(mInstance, bufferedPositionMs); }); } Loading Loading @@ -796,7 +850,13 @@ public class MediaController2Impl implements MediaController2Provider { // Should be used without a lock to prevent potential deadlock. void onConnectedNotLocked(IMediaSession2 sessionBinder, final CommandGroup allowedCommands, final PlaybackState2 state, final PlaybackInfo info, final CommandGroup allowedCommands, final int playerState, final long positionEventTimeMs, final long positionMs, final float playbackSpeed, final long bufferedPositionMs, final PlaybackInfo info, final PlaylistParams params, final List<MediaItem2> playlist, final PendingIntent sessionActivity) { if (DEBUG) { Loading @@ -821,7 +881,11 @@ public class MediaController2Impl implements MediaController2Provider { return; } mAllowedCommands = allowedCommands; mPlaybackState = state; mPlayerState = playerState; mPositionEventTimeMs = positionEventTimeMs; mPositionMs = positionMs; mPlaybackSpeed = playbackSpeed; mBufferedPositionMs = bufferedPositionMs; mPlaybackInfo = info; mPlaylistParams = params; mPlaylist = playlist; Loading packages/MediaComponents/src/com/android/media/MediaController2Stub.java +54 −7 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.PlaylistParams; import android.media.PlaybackState2; import android.os.Bundle; import android.os.ResultReceiver; import android.text.TextUtils; Loading Loading @@ -69,7 +68,7 @@ public class MediaController2Stub extends IMediaController2.Stub { } @Override public void onPlaybackStateChanged(Bundle state) throws RuntimeException { public void onPlayerStateChanged(int state) { final MediaController2Impl controller; try { controller = getController(); Loading @@ -77,8 +76,55 @@ public class MediaController2Stub extends IMediaController2.Stub { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } controller.pushPlaybackStateChanges( PlaybackState2.fromBundle(controller.getContext(), state)); controller.pushPlayerStateChanges(state); } @Override public void onPositionChanged(long eventTimeMs, long positionMs) { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } if (eventTimeMs < 0) { Log.w(TAG, "onPositionChanged(): Ignoring negative eventTimeMs"); return; } if (positionMs < 0) { Log.w(TAG, "onPositionChanged(): Ignoring negative positionMs"); return; } controller.pushPositionChanges(eventTimeMs, positionMs); } @Override public void onPlaybackSpeedChanged(float speed) { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } controller.pushPlaybackSpeedChanges(speed); } @Override public void onBufferedPositionChanged(long bufferedPositionMs) { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } if (bufferedPositionMs < 0) { Log.w(TAG, "onBufferedPositionChanged(): Ignoring negative bufferedPositionMs"); return; } controller.pushBufferedPositionChanges(bufferedPositionMs); } @Override Loading Loading @@ -163,8 +209,9 @@ public class MediaController2Stub extends IMediaController2.Stub { @Override public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup, Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle> itemBundleList, PendingIntent sessionActivity) { int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, long bufferedPositionMs, Bundle playbackInfo, Bundle playlistParams, List<Bundle> itemBundleList, PendingIntent sessionActivity) { final MediaController2Impl controller = mController.get(); if (controller == null) { if (DEBUG) { Loading @@ -185,7 +232,7 @@ public class MediaController2Stub extends IMediaController2.Stub { } controller.onConnectedNotLocked(sessionBinder, CommandGroup.fromBundle(context, commandGroup), PlaybackState2.fromBundle(context, playbackState), playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs, PlaybackInfoImpl.fromBundle(context, playbackInfo), PlaylistParams.fromBundle(context, playlistParams), itemList, sessionActivity); Loading packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +74 −41 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ import android.media.MediaSession2.PlaylistParams.RepeatMode; import android.media.MediaSession2.PlaylistParams.ShuffleMode; import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; import android.media.PlaybackState2; import android.media.SessionToken2; import android.media.VolumeProvider2; import android.media.session.MediaSessionManager; Loading Loading @@ -248,11 +247,11 @@ public class MediaSession2Impl implements MediaSession2Provider { oldAgent.unregisterPlaylistEventCallback(mPlaylistEventCallback); } } // TODO(jaewan): Notify controllers about the change in the media player base (b/74370608) // Note that notification will be done indirectly by telling player state, // position, buffered position, etc. if (oldPlayer != null) { mSessionStub.notifyPlaybackInfoChanged(info); notifyPlaybackStateChangedNotLocked(mInstance.getPlaybackState()); notifyPlayerUpdatedNotLocked(oldPlayer); } } private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) { Loading Loading @@ -676,12 +675,6 @@ public class MediaSession2Impl implements MediaSession2Provider { return; } mCallbacks.put(callback, executor); // TODO: Uncomment or remove /* // TODO(jaewan): Double check if we need this. final PlaybackState2 state = getInstance().getPlaybackState(); executor.execute(() -> callback.onPlaybackStateChanged(state)); */ } @Override Loading @@ -693,25 +686,6 @@ public class MediaSession2Impl implements MediaSession2Provider { mCallbacks.remove(callback); } @Override public PlaybackState2 getPlaybackState_impl() { ensureCallingThread(); // TODO: Uncomment or remove /* final MediaPlayerBase player = mPlayer; if (player != null) { // TODO(jaewan): Is it safe to be called on any thread? // Otherwise MediaSession2 should cache the result from listener. // TODO implement //return player.getPlaybackState(); return null; } else if (DEBUG) { Log.d(TAG, "API calls after the close()", new IllegalStateException()); } */ return null; } @Override public void notifyError_impl(int errorCode, Bundle extras) { // TODO(jaewan): Implement Loading Loading @@ -761,9 +735,11 @@ public class MediaSession2Impl implements MediaSession2Provider { mSessionStub.notifyPlaylistMetadataChangedNotLocked(metadata); } private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) { private void notifyPlayerUpdatedNotLocked(MediaPlayerBase oldPlayer) { ArrayMap<PlayerEventCallback, Executor> callbacks = new ArrayMap<>(); MediaPlayerBase player; synchronized (mLock) { player = mPlayer; callbacks.putAll(mCallbacks); } // Notify to callbacks added directly to this session Loading @@ -774,7 +750,26 @@ public class MediaSession2Impl implements MediaSession2Provider { //executor.execute(() -> callback.onPlaybackStateChanged(state)); } // Notify to controllers as well. mSessionStub.notifyPlaybackStateChangedNotLocked(state); final int state = player.getPlayerState(); if (state != oldPlayer.getPlayerState()) { mSessionStub.notifyPlayerStateChangedNotLocked(state); } final long currentTimeMs = System.currentTimeMillis(); final long position = player.getCurrentPosition(); if (position != oldPlayer.getCurrentPosition()) { mSessionStub.notifyPositionChangedNotLocked(currentTimeMs, position); } final float speed = player.getPlaybackSpeed(); if (speed != oldPlayer.getPlaybackSpeed()) { mSessionStub.notifyPlaybackSpeedChangedNotLocked(speed); } final long bufferedPosition = player.getBufferedPosition(); if (bufferedPosition != oldPlayer.getBufferedPosition()) { mSessionStub.notifyBufferedPositionChangedNotLocked(bufferedPosition); } } private void notifyErrorNotLocked(String mediaId, int what, int extra) { Loading Loading @@ -843,26 +838,64 @@ public class MediaSession2Impl implements MediaSession2Provider { @Override public void onCurrentDataSourceChanged(MediaPlayerBase mpb, DataSourceDesc dsd) { super.onCurrentDataSourceChanged(mpb, dsd); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { // TODO (jaewan): Convert dsd to MediaItem (b/74506462) // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) session.getCallback().onCurrentMediaItemChanged( session.getInstance(), mpb, null /* MediaItem */); }); } @Override public void onMediaPrepared(MediaPlayerBase mpb, DataSourceDesc dsd) { super.onMediaPrepared(mpb, dsd); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { // TODO (jaewan): Convert dsd to MediaItem (b/74506462) // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) session.getCallback().onMediaPrepared( session.getInstance(), mpb, null /* MediaItem */); }); } @Override public void onPlayerStateChanged(MediaPlayerBase mpb, int state) { super.onPlayerStateChanged(mpb, state); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { session.getCallback().onPlayerStateChanged(session.getInstance(), mpb, state); session.getSessionStub().notifyPlayerStateChangedNotLocked(state); }); } @Override public void onBufferingStateChanged(MediaPlayerBase mpb, DataSourceDesc dsd, int state) { super.onBufferingStateChanged(mpb, dsd, state); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { // TODO (jaewan): Convert dsd to MediaItem (b/74506462) // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) session.getCallback().onBufferingStateChanged( session.getInstance(), mpb, null /* MediaItem */, state); }); } private MediaSession2Impl getSession() { final MediaSession2Impl session = mSession.get(); if (session == null && DEBUG) { Log.d(TAG, "Session is closed", new IllegalStateException()); } return session; } } Loading packages/MediaComponents/src/com/android/media/MediaSession2Stub.java +60 −10 Original line number Diff line number Diff line Loading @@ -22,13 +22,13 @@ import android.media.MediaController2; import android.media.MediaItem2; import android.media.MediaLibraryService2.LibraryRoot; import android.media.MediaMetadata2; import android.media.MediaPlayerBase; import android.media.MediaSession2; import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSession2.PlaylistParams; import android.media.PlaybackState2; import android.media.Rating2; import android.media.VolumeProvider2; import android.net.Uri; Loading Loading @@ -259,9 +259,11 @@ public class MediaSession2Stub extends IMediaSession2.Stub { // that events here are notified after the onConnected() because // IMediaController2 is oneway (i.e. async call) and Stub will // use thread poll for incoming calls. // TODO(jaewan): Should we protect getting playback state? final PlaybackState2 state = session.getInstance().getPlaybackState(); final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null; final int playerState = session.getInstance().getPlayerState(); final long positionEventTimeMs = System.currentTimeMillis(); final long positionMs = session.getInstance().getCurrentPosition(); final float playbackSpeed = session.getInstance().getPlaybackSpeed(); final long bufferedPositionMs = session.getInstance().getBufferedPosition(); final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle(); final PlaylistParams params = session.getInstance().getPlaylistParams(); Loading Loading @@ -293,9 +295,10 @@ public class MediaSession2Stub extends IMediaSession2.Stub { return; } try { caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle, paramsBundle, playlistBundle, sessionActivity); caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(), playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs, playbackInfoBundle, paramsBundle, playlistBundle, sessionActivity); } catch (RemoteException e) { // Controller may be died prematurely. // TODO(jaewan): Handle here. Loading Loading @@ -1082,7 +1085,7 @@ public class MediaSession2Stub extends IMediaSession2.Stub { } // Should be used without a lock to prevent potential deadlock. public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) { public void notifyPlayerStateChangedNotLocked(int state) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); Loading @@ -1090,8 +1093,55 @@ public class MediaSession2Stub extends IMediaSession2.Stub { return; } try { final Bundle bundle = state != null ? state.toBundle() : null; controllerBinder.onPlaybackStateChanged(bundle); controllerBinder.onPlayerStateChanged(state); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void notifyPositionChangedNotLocked(long eventTimeMs, long positionMs) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); if (controllerBinder == null) { return; } try { controllerBinder.onPositionChanged(eventTimeMs, positionMs); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void notifyPlaybackSpeedChangedNotLocked(float speed) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); if (controllerBinder == null) { return; } try { controllerBinder.onPlaybackSpeedChanged(speed); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void notifyBufferedPositionChangedNotLocked(long bufferedPositionMs) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); if (controllerBinder == null) { return; } try { controllerBinder.onBufferedPositionChanged(bufferedPositionMs); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? Loading Loading
packages/MediaComponents/src/com/android/media/IMediaController2.aidl +8 −4 Original line number Diff line number Diff line Loading @@ -31,15 +31,19 @@ import com.android.media.IMediaSession2; // TODO(jaewan): (Post P) Handle when the playlist becomes too huge. // Note that ParcelledSliceList isn't a good idea for the purpose. (see: b/37493677) oneway interface IMediaController2 { void onPlaybackStateChanged(in Bundle state); void onPlayerStateChanged(int state); void onPositionChanged(long eventTimeMs, long positionMs); void onPlaybackSpeedChanged(float speed); void onBufferedPositionChanged(long bufferedPositionMs); void onPlaylistChanged(in List<Bundle> playlist, in Bundle metadata); void onPlaylistMetadataChanged(in Bundle metadata); void onPlaylistParamsChanged(in Bundle params); void onPlaybackInfoChanged(in Bundle playbackInfo); void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, in Bundle playbackState, in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, in PendingIntent sessionActivity); void onConnected(IMediaSession2 sessionBinder, in Bundle commandGroup, int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, long bufferedPositionMs, in Bundle playbackInfo, in Bundle params, in List<Bundle> playlist, in PendingIntent sessionActivity); void onDisconnected(); void onCustomLayoutChanged(in List<Bundle> commandButtonlist); Loading
packages/MediaComponents/src/com/android/media/MediaController2Impl.java +88 −24 Original line number Diff line number Diff line Loading @@ -16,7 +16,18 @@ package com.android.media; import static android.media.MediaSession2.*; import static android.media.MediaSession2.COMMAND_CODE_PLAYBACK_SET_VOLUME; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_ADD_ITEM; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_REPLACE_ITEM; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST; import static android.media.MediaSession2.COMMAND_CODE_PLAYLIST_SET_LIST_METADATA; import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_MEDIA_ID; import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_SEARCH; import static android.media.MediaSession2.COMMAND_CODE_PLAY_FROM_URI; import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_MEDIA_ID; import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_SEARCH; import static android.media.MediaSession2.COMMAND_CODE_PREPARE_FROM_URI; import android.app.PendingIntent; import android.content.ComponentName; Loading @@ -35,7 +46,6 @@ import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.PlaylistParams; import android.media.MediaSessionService2; import android.media.PlaybackState2; import android.media.Rating2; import android.media.SessionToken2; import android.media.update.MediaController2Provider; Loading Loading @@ -73,14 +83,22 @@ public class MediaController2Impl implements MediaController2Provider { @GuardedBy("mLock") private boolean mIsReleased; @GuardedBy("mLock") private PlaybackState2 mPlaybackState; @GuardedBy("mLock") private List<MediaItem2> mPlaylist; @GuardedBy("mLock") private MediaMetadata2 mPlaylistMetadata; @GuardedBy("mLock") private PlaylistParams mPlaylistParams; @GuardedBy("mLock") private int mPlayerState; @GuardedBy("mLock") private long mPositionEventTimeMs; @GuardedBy("mLock") private long mPositionMs; @GuardedBy("mLock") private float mPlaybackSpeed; @GuardedBy("mLock") private long mBufferedPositionMs; @GuardedBy("mLock") private PlaybackInfo mPlaybackInfo; @GuardedBy("mLock") private PendingIntent mSessionActivity; Loading Loading @@ -613,13 +631,6 @@ public class MediaController2Impl implements MediaController2Provider { */ } @Override public PlaybackState2 getPlaybackState_impl() { synchronized (mLock) { return mPlaybackState; } } @Override public void addPlaylistItem_impl(int index, MediaItem2 item) { if (index < 0) { Loading Loading @@ -703,26 +714,32 @@ public class MediaController2Impl implements MediaController2Provider { @Override public int getPlayerState_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { return mPlayerState; } } @Override public long getPosition_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { long timeDiff = System.currentTimeMillis() - mPositionEventTimeMs; long expectedPosition = mPositionMs + (long) (mPlaybackSpeed * timeDiff); return Math.max(0, expectedPosition); } } @Override public float getPlaybackSpeed_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { return mPlaybackSpeed; } } @Override public long getBufferedPosition_impl() { // TODO(jaewan): Implement return 0; synchronized (mLock) { return mBufferedPositionMs; } } @Override Loading @@ -731,15 +748,52 @@ public class MediaController2Impl implements MediaController2Provider { return null; } void pushPlaybackStateChanges(final PlaybackState2 state) { void pushPlayerStateChanges(final int state) { synchronized (mLock) { mPlayerState = state; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlayerStateChanged(mInstance, state); }); } void pushPositionChanges(final long eventTimeMs, final long positionMs) { synchronized (mLock) { mPositionEventTimeMs = eventTimeMs; mPositionMs = positionMs; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPositionChanged(mInstance, eventTimeMs, positionMs); }); } void pushPlaybackSpeedChanges(final float speed) { synchronized (mLock) { mPlaybackSpeed = speed; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlaybackSpeedChanged(mInstance, speed); }); } void pushBufferedPositionChanges(final long bufferedPositionMs) { synchronized (mLock) { mPlaybackState = state; mBufferedPositionMs = bufferedPositionMs; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlaybackStateChanged(mInstance, state); mCallback.onBufferedPositionChanged(mInstance, bufferedPositionMs); }); } Loading Loading @@ -796,7 +850,13 @@ public class MediaController2Impl implements MediaController2Provider { // Should be used without a lock to prevent potential deadlock. void onConnectedNotLocked(IMediaSession2 sessionBinder, final CommandGroup allowedCommands, final PlaybackState2 state, final PlaybackInfo info, final CommandGroup allowedCommands, final int playerState, final long positionEventTimeMs, final long positionMs, final float playbackSpeed, final long bufferedPositionMs, final PlaybackInfo info, final PlaylistParams params, final List<MediaItem2> playlist, final PendingIntent sessionActivity) { if (DEBUG) { Loading @@ -821,7 +881,11 @@ public class MediaController2Impl implements MediaController2Provider { return; } mAllowedCommands = allowedCommands; mPlaybackState = state; mPlayerState = playerState; mPositionEventTimeMs = positionEventTimeMs; mPositionMs = positionMs; mPlaybackSpeed = playbackSpeed; mBufferedPositionMs = bufferedPositionMs; mPlaybackInfo = info; mPlaylistParams = params; mPlaylist = playlist; Loading
packages/MediaComponents/src/com/android/media/MediaController2Stub.java +54 −7 Original line number Diff line number Diff line Loading @@ -25,7 +25,6 @@ import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.PlaylistParams; import android.media.PlaybackState2; import android.os.Bundle; import android.os.ResultReceiver; import android.text.TextUtils; Loading Loading @@ -69,7 +68,7 @@ public class MediaController2Stub extends IMediaController2.Stub { } @Override public void onPlaybackStateChanged(Bundle state) throws RuntimeException { public void onPlayerStateChanged(int state) { final MediaController2Impl controller; try { controller = getController(); Loading @@ -77,8 +76,55 @@ public class MediaController2Stub extends IMediaController2.Stub { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } controller.pushPlaybackStateChanges( PlaybackState2.fromBundle(controller.getContext(), state)); controller.pushPlayerStateChanges(state); } @Override public void onPositionChanged(long eventTimeMs, long positionMs) { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } if (eventTimeMs < 0) { Log.w(TAG, "onPositionChanged(): Ignoring negative eventTimeMs"); return; } if (positionMs < 0) { Log.w(TAG, "onPositionChanged(): Ignoring negative positionMs"); return; } controller.pushPositionChanges(eventTimeMs, positionMs); } @Override public void onPlaybackSpeedChanged(float speed) { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } controller.pushPlaybackSpeedChanges(speed); } @Override public void onBufferedPositionChanged(long bufferedPositionMs) { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } if (bufferedPositionMs < 0) { Log.w(TAG, "onBufferedPositionChanged(): Ignoring negative bufferedPositionMs"); return; } controller.pushBufferedPositionChanges(bufferedPositionMs); } @Override Loading Loading @@ -163,8 +209,9 @@ public class MediaController2Stub extends IMediaController2.Stub { @Override public void onConnected(IMediaSession2 sessionBinder, Bundle commandGroup, Bundle playbackState, Bundle playbackInfo, Bundle playlistParams, List<Bundle> itemBundleList, PendingIntent sessionActivity) { int playerState, long positionEventTimeMs, long positionMs, float playbackSpeed, long bufferedPositionMs, Bundle playbackInfo, Bundle playlistParams, List<Bundle> itemBundleList, PendingIntent sessionActivity) { final MediaController2Impl controller = mController.get(); if (controller == null) { if (DEBUG) { Loading @@ -185,7 +232,7 @@ public class MediaController2Stub extends IMediaController2.Stub { } controller.onConnectedNotLocked(sessionBinder, CommandGroup.fromBundle(context, commandGroup), PlaybackState2.fromBundle(context, playbackState), playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs, PlaybackInfoImpl.fromBundle(context, playbackInfo), PlaylistParams.fromBundle(context, playlistParams), itemList, sessionActivity); Loading
packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +74 −41 Original line number Diff line number Diff line Loading @@ -52,7 +52,6 @@ import android.media.MediaSession2.PlaylistParams.RepeatMode; import android.media.MediaSession2.PlaylistParams.ShuffleMode; import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; import android.media.PlaybackState2; import android.media.SessionToken2; import android.media.VolumeProvider2; import android.media.session.MediaSessionManager; Loading Loading @@ -248,11 +247,11 @@ public class MediaSession2Impl implements MediaSession2Provider { oldAgent.unregisterPlaylistEventCallback(mPlaylistEventCallback); } } // TODO(jaewan): Notify controllers about the change in the media player base (b/74370608) // Note that notification will be done indirectly by telling player state, // position, buffered position, etc. if (oldPlayer != null) { mSessionStub.notifyPlaybackInfoChanged(info); notifyPlaybackStateChangedNotLocked(mInstance.getPlaybackState()); notifyPlayerUpdatedNotLocked(oldPlayer); } } private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) { Loading Loading @@ -676,12 +675,6 @@ public class MediaSession2Impl implements MediaSession2Provider { return; } mCallbacks.put(callback, executor); // TODO: Uncomment or remove /* // TODO(jaewan): Double check if we need this. final PlaybackState2 state = getInstance().getPlaybackState(); executor.execute(() -> callback.onPlaybackStateChanged(state)); */ } @Override Loading @@ -693,25 +686,6 @@ public class MediaSession2Impl implements MediaSession2Provider { mCallbacks.remove(callback); } @Override public PlaybackState2 getPlaybackState_impl() { ensureCallingThread(); // TODO: Uncomment or remove /* final MediaPlayerBase player = mPlayer; if (player != null) { // TODO(jaewan): Is it safe to be called on any thread? // Otherwise MediaSession2 should cache the result from listener. // TODO implement //return player.getPlaybackState(); return null; } else if (DEBUG) { Log.d(TAG, "API calls after the close()", new IllegalStateException()); } */ return null; } @Override public void notifyError_impl(int errorCode, Bundle extras) { // TODO(jaewan): Implement Loading Loading @@ -761,9 +735,11 @@ public class MediaSession2Impl implements MediaSession2Provider { mSessionStub.notifyPlaylistMetadataChangedNotLocked(metadata); } private void notifyPlaybackStateChangedNotLocked(final PlaybackState2 state) { private void notifyPlayerUpdatedNotLocked(MediaPlayerBase oldPlayer) { ArrayMap<PlayerEventCallback, Executor> callbacks = new ArrayMap<>(); MediaPlayerBase player; synchronized (mLock) { player = mPlayer; callbacks.putAll(mCallbacks); } // Notify to callbacks added directly to this session Loading @@ -774,7 +750,26 @@ public class MediaSession2Impl implements MediaSession2Provider { //executor.execute(() -> callback.onPlaybackStateChanged(state)); } // Notify to controllers as well. mSessionStub.notifyPlaybackStateChangedNotLocked(state); final int state = player.getPlayerState(); if (state != oldPlayer.getPlayerState()) { mSessionStub.notifyPlayerStateChangedNotLocked(state); } final long currentTimeMs = System.currentTimeMillis(); final long position = player.getCurrentPosition(); if (position != oldPlayer.getCurrentPosition()) { mSessionStub.notifyPositionChangedNotLocked(currentTimeMs, position); } final float speed = player.getPlaybackSpeed(); if (speed != oldPlayer.getPlaybackSpeed()) { mSessionStub.notifyPlaybackSpeedChangedNotLocked(speed); } final long bufferedPosition = player.getBufferedPosition(); if (bufferedPosition != oldPlayer.getBufferedPosition()) { mSessionStub.notifyBufferedPositionChangedNotLocked(bufferedPosition); } } private void notifyErrorNotLocked(String mediaId, int what, int extra) { Loading Loading @@ -843,26 +838,64 @@ public class MediaSession2Impl implements MediaSession2Provider { @Override public void onCurrentDataSourceChanged(MediaPlayerBase mpb, DataSourceDesc dsd) { super.onCurrentDataSourceChanged(mpb, dsd); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { // TODO (jaewan): Convert dsd to MediaItem (b/74506462) // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) session.getCallback().onCurrentMediaItemChanged( session.getInstance(), mpb, null /* MediaItem */); }); } @Override public void onMediaPrepared(MediaPlayerBase mpb, DataSourceDesc dsd) { super.onMediaPrepared(mpb, dsd); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { // TODO (jaewan): Convert dsd to MediaItem (b/74506462) // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) session.getCallback().onMediaPrepared( session.getInstance(), mpb, null /* MediaItem */); }); } @Override public void onPlayerStateChanged(MediaPlayerBase mpb, int state) { super.onPlayerStateChanged(mpb, state); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { session.getCallback().onPlayerStateChanged(session.getInstance(), mpb, state); session.getSessionStub().notifyPlayerStateChangedNotLocked(state); }); } @Override public void onBufferingStateChanged(MediaPlayerBase mpb, DataSourceDesc dsd, int state) { super.onBufferingStateChanged(mpb, dsd, state); // TODO(jaewan): Handle this b/74370608 MediaSession2Impl session = getSession(); if (session == null) { return; } session.getCallbackExecutor().execute(() -> { // TODO (jaewan): Convert dsd to MediaItem (b/74506462) // TODO (jaewan): Notify controllers through appropriate callback. (b/74505936) session.getCallback().onBufferingStateChanged( session.getInstance(), mpb, null /* MediaItem */, state); }); } private MediaSession2Impl getSession() { final MediaSession2Impl session = mSession.get(); if (session == null && DEBUG) { Log.d(TAG, "Session is closed", new IllegalStateException()); } return session; } } Loading
packages/MediaComponents/src/com/android/media/MediaSession2Stub.java +60 −10 Original line number Diff line number Diff line Loading @@ -22,13 +22,13 @@ import android.media.MediaController2; import android.media.MediaItem2; import android.media.MediaLibraryService2.LibraryRoot; import android.media.MediaMetadata2; import android.media.MediaPlayerBase; import android.media.MediaSession2; import android.media.MediaSession2.Command; import android.media.MediaSession2.CommandButton; import android.media.MediaSession2.CommandGroup; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSession2.PlaylistParams; import android.media.PlaybackState2; import android.media.Rating2; import android.media.VolumeProvider2; import android.net.Uri; Loading Loading @@ -259,9 +259,11 @@ public class MediaSession2Stub extends IMediaSession2.Stub { // that events here are notified after the onConnected() because // IMediaController2 is oneway (i.e. async call) and Stub will // use thread poll for incoming calls. // TODO(jaewan): Should we protect getting playback state? final PlaybackState2 state = session.getInstance().getPlaybackState(); final Bundle playbackStateBundle = (state != null) ? state.toBundle() : null; final int playerState = session.getInstance().getPlayerState(); final long positionEventTimeMs = System.currentTimeMillis(); final long positionMs = session.getInstance().getCurrentPosition(); final float playbackSpeed = session.getInstance().getPlaybackSpeed(); final long bufferedPositionMs = session.getInstance().getBufferedPosition(); final Bundle playbackInfoBundle = ((MediaController2Impl.PlaybackInfoImpl) session.getPlaybackInfo().getProvider()).toBundle(); final PlaylistParams params = session.getInstance().getPlaylistParams(); Loading Loading @@ -293,9 +295,10 @@ public class MediaSession2Stub extends IMediaSession2.Stub { return; } try { caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(), playbackStateBundle, playbackInfoBundle, paramsBundle, playlistBundle, sessionActivity); caller.onConnected(MediaSession2Stub.this, allowedCommands.toBundle(), playerState, positionEventTimeMs, positionMs, playbackSpeed, bufferedPositionMs, playbackInfoBundle, paramsBundle, playlistBundle, sessionActivity); } catch (RemoteException e) { // Controller may be died prematurely. // TODO(jaewan): Handle here. Loading Loading @@ -1082,7 +1085,7 @@ public class MediaSession2Stub extends IMediaSession2.Stub { } // Should be used without a lock to prevent potential deadlock. public void notifyPlaybackStateChangedNotLocked(PlaybackState2 state) { public void notifyPlayerStateChangedNotLocked(int state) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); Loading @@ -1090,8 +1093,55 @@ public class MediaSession2Stub extends IMediaSession2.Stub { return; } try { final Bundle bundle = state != null ? state.toBundle() : null; controllerBinder.onPlaybackStateChanged(bundle); controllerBinder.onPlayerStateChanged(state); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void notifyPositionChangedNotLocked(long eventTimeMs, long positionMs) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); if (controllerBinder == null) { return; } try { controllerBinder.onPositionChanged(eventTimeMs, positionMs); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void notifyPlaybackSpeedChangedNotLocked(float speed) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); if (controllerBinder == null) { return; } try { controllerBinder.onPlaybackSpeedChanged(speed); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void notifyBufferedPositionChangedNotLocked(long bufferedPositionMs) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { final IMediaController2 controllerBinder = getControllerBinderIfAble(list.get(i)); if (controllerBinder == null) { return; } try { controllerBinder.onBufferedPositionChanged(bufferedPositionMs); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? Loading