Loading packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ oneway interface IMediaSession2Callback { void onPlaybackStateChanged(in Bundle state); void onPlaylistChanged(in List<Bundle> playlist); void onPlaylistParamsChanged(in Bundle params); void onPlaybackInfoChanged(in Bundle playbackInfo); /** * Called only when the controller is created with service's token. Loading packages/MediaComponents/src/com/android/media/MediaController2Impl.java +34 −6 Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ public class MediaController2Impl implements MediaController2Provider { private List<MediaItem2> mPlaylist; @GuardedBy("mLock") private PlaylistParams mPlaylistParams; @GuardedBy("mLock") private PlaybackInfo mPlaybackInfo; // Assignment should be used with the lock hold, but should be used without a lock to prevent // potential deadlock. Loading Loading @@ -292,12 +294,6 @@ public class MediaController2Impl implements MediaController2Provider { // TODO(jaewan): Implement } @Override public PlaybackInfo getPlaybackInfo_impl() { // TODO(jaewan): Implement return null; } @Override public void prepareFromUri_impl(Uri uri, Bundle extras) { // TODO(jaewan): Implement Loading Loading @@ -412,6 +408,13 @@ public class MediaController2Impl implements MediaController2Provider { } } @Override public PlaybackInfo getPlaybackInfo_impl() { synchronized (mLock) { return mPlaybackInfo; } } @Override public void setPlaylistParams_impl(PlaylistParams params) { if (params == null) { Loading Loading @@ -449,6 +452,18 @@ public class MediaController2Impl implements MediaController2Provider { }); } private void pushPlaybackInfoChanges(final PlaybackInfo info) { synchronized (mLock) { mPlaybackInfo = info; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlaybackInfoChanged(info); }); } private void pushPlaylistChanges(final List<Bundle> list) { final List<MediaItem2> playlist = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { Loading Loading @@ -602,6 +617,19 @@ public class MediaController2Impl implements MediaController2Provider { PlaylistParams.fromBundle(controller.getContext(), params)); } @Override public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } controller.pushPlaybackInfoChanges( PlaybackInfoImpl.fromBundle(controller.getContext(), playbackInfo)); } @Override public void onConnectionChanged(IMediaSession2 sessionBinder, Bundle commandGroup) throws RuntimeException { Loading packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java +2 −2 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ import android.media.MediaSession2; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSessionService2; import android.media.SessionToken2; import android.media.VolumeProvider; import android.media.VolumeProvider2; import android.media.update.MediaLibraryService2Provider; import android.os.Bundle; Loading Loading @@ -68,7 +68,7 @@ public class MediaLibraryService2Impl extends MediaSessionService2Impl implement private final MediaLibrarySessionCallback mCallback; public MediaLibrarySessionImpl(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, MediaLibrarySessionCallback callback) { super(context, player, id, volumeProvider, ratingType, sessionActivity, Loading packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +70 −16 Original line number Diff line number Diff line Loading @@ -21,15 +21,19 @@ import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE; import static android.media.SessionToken2.TYPE_SESSION; import static android.media.SessionToken2.TYPE_SESSION_SERVICE; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; import android.Manifest.permission; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaController2; import android.media.MediaController2.PlaybackInfo; import android.media.MediaItem2; import android.media.MediaLibraryService2; import android.media.MediaMetadata2; Loading @@ -48,14 +52,13 @@ import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; import android.media.PlaybackState2; import android.media.SessionToken2; import android.media.VolumeProvider; import android.media.VolumeProvider2; import android.media.session.MediaSessionManager; import android.media.update.MediaSession2Provider; import android.media.update.MediaSession2Provider.CommandButtonProvider; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.Process; import android.os.IBinder; import android.os.ResultReceiver; import android.support.annotation.GuardedBy; import android.text.TextUtils; Loading @@ -80,11 +83,16 @@ public class MediaSession2Impl implements MediaSession2Provider { private final SessionCallback mCallback; private final MediaSession2Stub mSessionStub; private final SessionToken2 mSessionToken; private final AudioManager mAudioManager; private final List<PlaybackListenerHolder> mListeners = new ArrayList<>(); @GuardedBy("mLock") private MediaPlayerInterface mPlayer; @GuardedBy("mLock") private VolumeProvider2 mVolumeProvider; @GuardedBy("mLock") private PlaybackInfo mPlaybackInfo; @GuardedBy("mLock") private MyPlaybackListener mListener; @GuardedBy("mLock") private PlaylistParams mPlaylistParams; Loading @@ -103,8 +111,8 @@ public class MediaSession2Impl implements MediaSession2Provider { * @param ratingType * @param sessionActivity */ public MediaSession2Impl(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, public MediaSession2Impl(Context context, MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, SessionCallback callback) { // TODO(jaewan): Keep other params. mInstance = createInstance(); Loading @@ -116,6 +124,7 @@ public class MediaSession2Impl implements MediaSession2Provider { mCallback = callback; mCallbackExecutor = callbackExecutor; mSessionStub = new MediaSession2Stub(this); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // Infer type from the id and package name. String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id); Loading @@ -135,6 +144,8 @@ public class MediaSession2Impl implements MediaSession2Provider { } setPlayerLocked(player); mVolumeProvider = volumeProvider; mPlaybackInfo = createPlaybackInfo(volumeProvider, player.getAudioAttributes()); // Ask server for the sanity check, and starts // Sanity check for making session ID unique 'per package' cannot be done in here. Loading Loading @@ -178,22 +189,46 @@ public class MediaSession2Impl implements MediaSession2Provider { return serviceName; } // TODO(jaewan): Add explicit release() and do not remove session object with the // setPlayer(null). Token can be available when player is null, and // controller can also attach to session. @Override public void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider) public void setPlayer_impl(MediaPlayerInterface player) { ensureCallingThread(); if (player == null) { throw new IllegalArgumentException("player shouldn't be null"); } if (player == mPlayer) { return; } PlaybackInfo info = createPlaybackInfo(null /* VolumeProvider */, player.getAudioAttributes()); synchronized (mLock) { setPlayerLocked(player); mVolumeProvider = null; mPlaybackInfo = info; } mSessionStub.notifyPlaybackInfoChanged(info); } @Override public void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider) throws IllegalArgumentException { ensureCallingThread(); if (player == null) { throw new IllegalArgumentException("player shouldn't be null"); } if (volumeProvider == null) { throw new IllegalArgumentException("volumeProvider shouldn't be null"); } if (player == mPlayer) { return; } PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes()); synchronized (mLock) { setPlayerLocked(player); mVolumeProvider = volumeProvider; mPlaybackInfo = info; } mSessionStub.notifyPlaybackInfoChanged(info); } private void setPlayerLocked(MediaPlayerInterface player) { Loading @@ -206,6 +241,29 @@ public class MediaSession2Impl implements MediaSession2Provider { player.addPlaybackListener(mCallbackExecutor, mListener); } private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) { PlaybackInfo info; if (volumeProvider == null) { int stream = attrs == null ? AudioManager.STREAM_MUSIC : attrs.getVolumeControlStream(); info = PlaybackInfoImpl.createPlaybackInfo( mContext, PlaybackInfo.PLAYBACK_TYPE_LOCAL, attrs, VolumeProvider2.VOLUME_CONTROL_ABSOLUTE, mAudioManager.getStreamMaxVolume(stream), mAudioManager.getStreamVolume(stream)); } else { info = PlaybackInfoImpl.createPlaybackInfo( mContext, PlaybackInfo.PLAYBACK_TYPE_REMOTE /* ControlType */, attrs, volumeProvider.getControlType(), volumeProvider.getMaxVolume(), volumeProvider.getCurrentVolume()); } return info; } @Override public void close_impl() { // Stop system service from listening this session first. Loading Loading @@ -320,10 +378,6 @@ public class MediaSession2Impl implements MediaSession2Provider { ////////////////////////////////////////////////////////////////////////////////////// // TODO(jaewan): Implement follows ////////////////////////////////////////////////////////////////////////////////////// @Override public void setPlayer_impl(MediaPlayerInterface player) { // TODO(jaewan): Implement } @Override public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) { Loading Loading @@ -1032,7 +1086,7 @@ public class MediaSession2Impl implements MediaSession2Provider { String mId; Executor mCallbackExecutor; C mCallback; VolumeProvider mVolumeProvider; VolumeProvider2 mVolumeProvider; int mRatingType; PendingIntent mSessionActivity; Loading @@ -1058,7 +1112,7 @@ public class MediaSession2Impl implements MediaSession2Provider { mId = ""; } public void setVolumeProvider_impl(VolumeProvider volumeProvider) { public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) { mVolumeProvider = volumeProvider; } Loading packages/MediaComponents/src/com/android/media/MediaSession2Stub.java +17 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.media; import android.content.Context; import android.media.MediaController2; import android.media.MediaItem2; import android.media.MediaLibraryService2.LibraryRoot; import android.media.MediaLibraryService2.MediaLibrarySessionCallback; Loading Loading @@ -130,6 +131,7 @@ public class MediaSession2Stub extends IMediaSession2.Stub { // Controller may be died prematurely. } if (accept) { // TODO(jaewan): We need to send current PlaybackInfo. // If connection is accepted, notify the current state to the controller. // It's needed because we cannot call synchronous calls between session/controller. // Note: We're doing this after the onConnectionChanged(), but there's no guarantee Loading Loading @@ -390,6 +392,21 @@ public class MediaSession2Stub extends IMediaSession2.Stub { } } public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { IMediaSession2Callback callbackBinder = ControllerInfoImpl.from(list.get(i)).getControllerBinder(); try { callbackBinder.onPlaybackInfoChanged( ((PlaybackInfoImpl) playbackInfo.getProvider()).toBundle()); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void sendCustomCommand(ControllerInfo controller, Command command, Bundle args, ResultReceiver receiver) { if (receiver != null && controller == null) { Loading Loading
packages/MediaComponents/src/com/android/media/IMediaSession2Callback.aidl +1 −0 Original line number Diff line number Diff line Loading @@ -31,6 +31,7 @@ oneway interface IMediaSession2Callback { void onPlaybackStateChanged(in Bundle state); void onPlaylistChanged(in List<Bundle> playlist); void onPlaylistParamsChanged(in Bundle params); void onPlaybackInfoChanged(in Bundle playbackInfo); /** * Called only when the controller is created with service's token. Loading
packages/MediaComponents/src/com/android/media/MediaController2Impl.java +34 −6 Original line number Diff line number Diff line Loading @@ -74,6 +74,8 @@ public class MediaController2Impl implements MediaController2Provider { private List<MediaItem2> mPlaylist; @GuardedBy("mLock") private PlaylistParams mPlaylistParams; @GuardedBy("mLock") private PlaybackInfo mPlaybackInfo; // Assignment should be used with the lock hold, but should be used without a lock to prevent // potential deadlock. Loading Loading @@ -292,12 +294,6 @@ public class MediaController2Impl implements MediaController2Provider { // TODO(jaewan): Implement } @Override public PlaybackInfo getPlaybackInfo_impl() { // TODO(jaewan): Implement return null; } @Override public void prepareFromUri_impl(Uri uri, Bundle extras) { // TODO(jaewan): Implement Loading Loading @@ -412,6 +408,13 @@ public class MediaController2Impl implements MediaController2Provider { } } @Override public PlaybackInfo getPlaybackInfo_impl() { synchronized (mLock) { return mPlaybackInfo; } } @Override public void setPlaylistParams_impl(PlaylistParams params) { if (params == null) { Loading Loading @@ -449,6 +452,18 @@ public class MediaController2Impl implements MediaController2Provider { }); } private void pushPlaybackInfoChanges(final PlaybackInfo info) { synchronized (mLock) { mPlaybackInfo = info; } mCallbackExecutor.execute(() -> { if (!mInstance.isConnected()) { return; } mCallback.onPlaybackInfoChanged(info); }); } private void pushPlaylistChanges(final List<Bundle> list) { final List<MediaItem2> playlist = new ArrayList<>(); for (int i = 0; i < list.size(); i++) { Loading Loading @@ -602,6 +617,19 @@ public class MediaController2Impl implements MediaController2Provider { PlaylistParams.fromBundle(controller.getContext(), params)); } @Override public void onPlaybackInfoChanged(Bundle playbackInfo) throws RuntimeException { final MediaController2Impl controller; try { controller = getController(); } catch (IllegalStateException e) { Log.w(TAG, "Don't fail silently here. Highly likely a bug"); return; } controller.pushPlaybackInfoChanges( PlaybackInfoImpl.fromBundle(controller.getContext(), playbackInfo)); } @Override public void onConnectionChanged(IMediaSession2 sessionBinder, Bundle commandGroup) throws RuntimeException { Loading
packages/MediaComponents/src/com/android/media/MediaLibraryService2Impl.java +2 −2 Original line number Diff line number Diff line Loading @@ -28,7 +28,7 @@ import android.media.MediaSession2; import android.media.MediaSession2.ControllerInfo; import android.media.MediaSessionService2; import android.media.SessionToken2; import android.media.VolumeProvider; import android.media.VolumeProvider2; import android.media.update.MediaLibraryService2Provider; import android.os.Bundle; Loading Loading @@ -68,7 +68,7 @@ public class MediaLibraryService2Impl extends MediaSessionService2Impl implement private final MediaLibrarySessionCallback mCallback; public MediaLibrarySessionImpl(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, MediaLibrarySessionCallback callback) { super(context, player, id, volumeProvider, ratingType, sessionActivity, Loading
packages/MediaComponents/src/com/android/media/MediaSession2Impl.java +70 −16 Original line number Diff line number Diff line Loading @@ -21,15 +21,19 @@ import static android.media.SessionToken2.TYPE_LIBRARY_SERVICE; import static android.media.SessionToken2.TYPE_SESSION; import static android.media.SessionToken2.TYPE_SESSION_SERVICE; import android.Manifest.permission; import android.annotation.NonNull; import android.annotation.Nullable; import android.Manifest.permission; import android.app.PendingIntent; import android.content.Context; import android.content.Intent; import android.content.pm.PackageManager; import android.content.pm.PackageManager.NameNotFoundException; import android.content.pm.ResolveInfo; import android.media.AudioAttributes; import android.media.AudioManager; import android.media.MediaController2; import android.media.MediaController2.PlaybackInfo; import android.media.MediaItem2; import android.media.MediaLibraryService2; import android.media.MediaMetadata2; Loading @@ -48,14 +52,13 @@ import android.media.MediaSession2.SessionCallback; import android.media.MediaSessionService2; import android.media.PlaybackState2; import android.media.SessionToken2; import android.media.VolumeProvider; import android.media.VolumeProvider2; import android.media.session.MediaSessionManager; import android.media.update.MediaSession2Provider; import android.media.update.MediaSession2Provider.CommandButtonProvider; import android.os.Bundle; import android.os.IBinder; import android.os.Parcelable; import android.os.Process; import android.os.IBinder; import android.os.ResultReceiver; import android.support.annotation.GuardedBy; import android.text.TextUtils; Loading @@ -80,11 +83,16 @@ public class MediaSession2Impl implements MediaSession2Provider { private final SessionCallback mCallback; private final MediaSession2Stub mSessionStub; private final SessionToken2 mSessionToken; private final AudioManager mAudioManager; private final List<PlaybackListenerHolder> mListeners = new ArrayList<>(); @GuardedBy("mLock") private MediaPlayerInterface mPlayer; @GuardedBy("mLock") private VolumeProvider2 mVolumeProvider; @GuardedBy("mLock") private PlaybackInfo mPlaybackInfo; @GuardedBy("mLock") private MyPlaybackListener mListener; @GuardedBy("mLock") private PlaylistParams mPlaylistParams; Loading @@ -103,8 +111,8 @@ public class MediaSession2Impl implements MediaSession2Provider { * @param ratingType * @param sessionActivity */ public MediaSession2Impl(Context context, MediaPlayerInterface player, String id, VolumeProvider volumeProvider, int ratingType, PendingIntent sessionActivity, public MediaSession2Impl(Context context, MediaPlayerInterface player, String id, VolumeProvider2 volumeProvider, int ratingType, PendingIntent sessionActivity, Executor callbackExecutor, SessionCallback callback) { // TODO(jaewan): Keep other params. mInstance = createInstance(); Loading @@ -116,6 +124,7 @@ public class MediaSession2Impl implements MediaSession2Provider { mCallback = callback; mCallbackExecutor = callbackExecutor; mSessionStub = new MediaSession2Stub(this); mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE); // Infer type from the id and package name. String libraryService = getServiceName(context, MediaLibraryService2.SERVICE_INTERFACE, id); Loading @@ -135,6 +144,8 @@ public class MediaSession2Impl implements MediaSession2Provider { } setPlayerLocked(player); mVolumeProvider = volumeProvider; mPlaybackInfo = createPlaybackInfo(volumeProvider, player.getAudioAttributes()); // Ask server for the sanity check, and starts // Sanity check for making session ID unique 'per package' cannot be done in here. Loading Loading @@ -178,22 +189,46 @@ public class MediaSession2Impl implements MediaSession2Provider { return serviceName; } // TODO(jaewan): Add explicit release() and do not remove session object with the // setPlayer(null). Token can be available when player is null, and // controller can also attach to session. @Override public void setPlayer_impl(MediaPlayerInterface player, VolumeProvider volumeProvider) public void setPlayer_impl(MediaPlayerInterface player) { ensureCallingThread(); if (player == null) { throw new IllegalArgumentException("player shouldn't be null"); } if (player == mPlayer) { return; } PlaybackInfo info = createPlaybackInfo(null /* VolumeProvider */, player.getAudioAttributes()); synchronized (mLock) { setPlayerLocked(player); mVolumeProvider = null; mPlaybackInfo = info; } mSessionStub.notifyPlaybackInfoChanged(info); } @Override public void setPlayer_impl(MediaPlayerInterface player, VolumeProvider2 volumeProvider) throws IllegalArgumentException { ensureCallingThread(); if (player == null) { throw new IllegalArgumentException("player shouldn't be null"); } if (volumeProvider == null) { throw new IllegalArgumentException("volumeProvider shouldn't be null"); } if (player == mPlayer) { return; } PlaybackInfo info = createPlaybackInfo(volumeProvider, player.getAudioAttributes()); synchronized (mLock) { setPlayerLocked(player); mVolumeProvider = volumeProvider; mPlaybackInfo = info; } mSessionStub.notifyPlaybackInfoChanged(info); } private void setPlayerLocked(MediaPlayerInterface player) { Loading @@ -206,6 +241,29 @@ public class MediaSession2Impl implements MediaSession2Provider { player.addPlaybackListener(mCallbackExecutor, mListener); } private PlaybackInfo createPlaybackInfo(VolumeProvider2 volumeProvider, AudioAttributes attrs) { PlaybackInfo info; if (volumeProvider == null) { int stream = attrs == null ? AudioManager.STREAM_MUSIC : attrs.getVolumeControlStream(); info = PlaybackInfoImpl.createPlaybackInfo( mContext, PlaybackInfo.PLAYBACK_TYPE_LOCAL, attrs, VolumeProvider2.VOLUME_CONTROL_ABSOLUTE, mAudioManager.getStreamMaxVolume(stream), mAudioManager.getStreamVolume(stream)); } else { info = PlaybackInfoImpl.createPlaybackInfo( mContext, PlaybackInfo.PLAYBACK_TYPE_REMOTE /* ControlType */, attrs, volumeProvider.getControlType(), volumeProvider.getMaxVolume(), volumeProvider.getCurrentVolume()); } return info; } @Override public void close_impl() { // Stop system service from listening this session first. Loading Loading @@ -320,10 +378,6 @@ public class MediaSession2Impl implements MediaSession2Provider { ////////////////////////////////////////////////////////////////////////////////////// // TODO(jaewan): Implement follows ////////////////////////////////////////////////////////////////////////////////////// @Override public void setPlayer_impl(MediaPlayerInterface player) { // TODO(jaewan): Implement } @Override public void setAllowedCommands_impl(ControllerInfo controller, CommandGroup commands) { Loading Loading @@ -1032,7 +1086,7 @@ public class MediaSession2Impl implements MediaSession2Provider { String mId; Executor mCallbackExecutor; C mCallback; VolumeProvider mVolumeProvider; VolumeProvider2 mVolumeProvider; int mRatingType; PendingIntent mSessionActivity; Loading @@ -1058,7 +1112,7 @@ public class MediaSession2Impl implements MediaSession2Provider { mId = ""; } public void setVolumeProvider_impl(VolumeProvider volumeProvider) { public void setVolumeProvider_impl(VolumeProvider2 volumeProvider) { mVolumeProvider = volumeProvider; } Loading
packages/MediaComponents/src/com/android/media/MediaSession2Stub.java +17 −0 Original line number Diff line number Diff line Loading @@ -17,6 +17,7 @@ package com.android.media; import android.content.Context; import android.media.MediaController2; import android.media.MediaItem2; import android.media.MediaLibraryService2.LibraryRoot; import android.media.MediaLibraryService2.MediaLibrarySessionCallback; Loading Loading @@ -130,6 +131,7 @@ public class MediaSession2Stub extends IMediaSession2.Stub { // Controller may be died prematurely. } if (accept) { // TODO(jaewan): We need to send current PlaybackInfo. // If connection is accepted, notify the current state to the controller. // It's needed because we cannot call synchronous calls between session/controller. // Note: We're doing this after the onConnectionChanged(), but there's no guarantee Loading Loading @@ -390,6 +392,21 @@ public class MediaSession2Stub extends IMediaSession2.Stub { } } public void notifyPlaybackInfoChanged(MediaController2.PlaybackInfo playbackInfo) { final List<ControllerInfo> list = getControllers(); for (int i = 0; i < list.size(); i++) { IMediaSession2Callback callbackBinder = ControllerInfoImpl.from(list.get(i)).getControllerBinder(); try { callbackBinder.onPlaybackInfoChanged( ((PlaybackInfoImpl) playbackInfo.getProvider()).toBundle()); } catch (RemoteException e) { Log.w(TAG, "Controller is gone", e); // TODO(jaewan): What to do when the controller is gone? } } } public void sendCustomCommand(ControllerInfo controller, Command command, Bundle args, ResultReceiver receiver) { if (receiver != null && controller == null) { Loading