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

Commit ef2a534d authored by Jaewan Kim's avatar Jaewan Kim
Browse files

MediaSession2: Add a way to notify errors between session and player

This is proposed during the offline meeting

Test: Run all MediaComponents tests once
Change-Id: I3ebd6284792a934bf1411a447e65970ad53a1f42
parent d4fdc622
Loading
Loading
Loading
Loading
+55 −10
Original line number Diff line number Diff line
@@ -16,6 +16,7 @@

package android.media;

import android.annotation.CallbackExecutor;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.MediaSession2.PlaylistParams;
@@ -29,13 +30,56 @@ import java.util.concurrent.Executor;
 */
public interface MediaPlayerInterface {
    /**
     * Listens change in {@link PlaybackState2}.
     * Unspecified media player error.
     */
    interface PlaybackListener {
    int MEDIA_ERROR_UNKNOWN = MediaPlayer2.MEDIA_ERROR_UNKNOWN;

    /**
     * The video is streamed and its container is not valid for progressive
     * playback i.e the video's index (e.g moov atom) is not at the start of the
     * file.
     */
    int MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK =
            MediaPlayer2.MEDIA_ERROR_NOT_VALID_FOR_PROGRESSIVE_PLAYBACK;

    /**
     * File or network related operation errors.
     */
    int MEDIA_ERROR_IO = MediaPlayer2.MEDIA_ERROR_IO;

    /**
     * Bitstream is not conforming to the related coding standard or file spec.
     */
    int MEDIA_ERROR_MALFORMED = MediaPlayer2.MEDIA_ERROR_MALFORMED;

    /**
     * Bitstream is conforming to the related coding standard or file spec, but
     * the media framework does not support the feature.
     */
    int MEDIA_ERROR_UNSUPPORTED = MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;

    /**
     * Some operation takes too long to complete, usually more than 3-5 seconds.
     */
    int MEDIA_ERROR_TIMED_OUT = MediaPlayer2.MEDIA_ERROR_TIMED_OUT;

    /**
     * Callbacks to listens to the changes in {@link PlaybackState2} and error.
     */
    interface EventCallback {
        /**
         * Called when {@link PlaybackState2} for this player is changed.
         */
        void onPlaybackChanged(PlaybackState2 state);
        default void onPlaybackStateChanged(PlaybackState2 state) { }

        /**
         * Called to indicate an error.
         *
         * @param mediaId optional mediaId to indicate error
         * @param what what
         * @param extra
         */
        default void onError(@Nullable String mediaId, int what, int extra) { }
    }

    // Transport controls that session will send command directly to this player.
@@ -75,17 +119,18 @@ public interface MediaPlayerInterface {
    PlaylistParams getPlaylistParams();

    /**
     * Add a {@link PlaybackListener} to be invoked when the playback state is changed.
     * Register a {@link EventCallback}.
     *
     * @param executor the Handler that will receive the listener
     * @param listener the listener that will be run
     * @param executor a callback executor
     * @param callback a EventCallback
     */
    void addPlaybackListener(Executor executor, PlaybackListener listener);
    void registerEventCallback(@NonNull @CallbackExecutor Executor executor,
            @NonNull EventCallback callback);

    /**
     * Remove previously added {@link PlaybackListener}.
     * Unregister previously registered {@link EventCallback}.
     *
     * @param listener the listener to be removed
     * @param callback a EventCallback
     */
    void removePlaybackListener(PlaybackListener listener);
    void unregisterEventCallback(@NonNull EventCallback callback);
}
+18 −18
Original line number Diff line number Diff line
@@ -24,7 +24,7 @@ import android.annotation.SystemApi;
import android.app.PendingIntent;
import android.content.Context;
import android.content.Intent;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaPlayerInterface.EventCallback;
import android.media.session.MediaSession;
import android.media.session.MediaSession.Callback;
import android.media.session.PlaybackState;
@@ -41,7 +41,6 @@ import android.os.Bundle;
import android.os.Handler;
import android.os.IInterface;
import android.os.ResultReceiver;
import android.text.TextUtils;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
@@ -1058,8 +1057,9 @@ public class MediaSession2 implements AutoCloseable {
     * to. Events from the {@link MediaController2} will be sent directly to the underlying
     * player on the {@link Handler} where the session is created on.
     * <p>
     * If the new player is successfully set, {@link PlaybackListener}
     * will be called to tell the current playback state of the new player.
     * If the new player is successfully set,
     * {@link EventCallback#onPlaybackStateChanged(PlaybackState2)} will be called to tell the
     * current playback state of the new player.
     * <p>
     * For the remote playback case which you want to handle volume by yourself, use
     * {@link #setPlayer(MediaPlayerInterface, VolumeProvider2)}.
@@ -1325,28 +1325,28 @@ public class MediaSession2 implements AutoCloseable {
    }

    /*
     * Add a {@link PlaybackListener} to listen changes in the underlying
     * {@link MediaPlayerInterface}. Listener will be called immediately to tell the current value.
     * Register {@link EventCallback} to listen changes in the underlying
     * {@link MediaPlayerInterface}, regardless of the change in the underlying player.
     * <p>
     * Added listeners will be also called when the underlying player is changed.
     * Registered callbacks will be also called when the underlying player is changed.
     *
     * @param executor the call listener
     * @param listener the listener that will be run
     * @throws IllegalArgumentException when either the listener or handler is {@code null}.
     * @param executor a callback Executor
     * @param callback a EventCallback
     * @throws IllegalArgumentException if executor or callback is {@code null}.
     */
    public void addPlaybackListener(@NonNull @CallbackExecutor Executor executor,
            @NonNull PlaybackListener listener) {
        mProvider.addPlaybackListener_impl(executor, listener);
    public void registerPlayerEventCallback(@NonNull @CallbackExecutor Executor executor,
            @NonNull EventCallback callback) {
        mProvider.registerPlayerEventCallback_impl(executor, callback);
    }

    /**
     * Remove previously added {@link PlaybackListener}.
     * Unregister the previously registered {@link EventCallback}.
     *
     * @param listener the listener to be removed
     * @throws IllegalArgumentException if the listener is {@code null}.
     * @param callback the callback to be removed
     * @throws IllegalArgumentException if the callback is {@code null}.
     */
    public void removePlaybackListener(@NonNull PlaybackListener listener) {
        mProvider.removePlaybackListener_impl(listener);
    public void unregisterPlayerEventCallback(@NonNull EventCallback callback) {
        mProvider.unregisterPlayerEventCallback_impl(callback);
    }

    /**
+5 −18
Original line number Diff line number Diff line
@@ -34,7 +34,7 @@ import java.lang.annotation.RetentionPolicy;
 * @hide
 */
public final class PlaybackState2 {
    // Similar to the PlaybackState2 with following changes
    // Similar to the PlaybackState with following changes
    //    - Not implement Parcelable and added from/toBundle()
    //    - Removed playback state that doesn't match with the MediaPlayer2
    //      Full list should be finalized when the MediaPlayer2 has getter for the playback state.
@@ -73,6 +73,7 @@ public final class PlaybackState2 {
    //         |    EventCallback.onBufferingUpdate()   |                                        |
    //         +----------------------------------------+----------------------------------------+
    //    - Removed actions and custom actions.
    //    - Removed error string
    //    - Repeat mode / shuffle mode is now in the PlaylistParams
    // TODO(jaewan): Replace states from MediaPlayer2
    /**
@@ -110,8 +111,7 @@ public final class PlaybackState2 {
    public final static int STATE_BUFFERING = 4;

    /**
     * State indicating this item is currently in an error state. The error
     * message should also be set when entering this state.
     * State indicating this item is currently in an error state.
     */
    public final static int STATE_ERROR = 5;

@@ -122,13 +122,10 @@ public final class PlaybackState2 {

    private final PlaybackState2Provider mProvider;

    // TODO(jaewan): Better error handling?
    //               E.g. media item at #2 has issue, but continue playing #3
    //                    login error. fire intent xxx to login
    public PlaybackState2(@NonNull Context context, int state, long position, long updateTime,
            float speed, long bufferedPosition, long activeItemId, CharSequence error) {
            float speed, long bufferedPosition, long activeItemId) {
        mProvider = ApiLoader.getProvider(context).createPlaybackState2(context, this, state,
                position, updateTime, speed, bufferedPosition, activeItemId, error);
                position, updateTime, speed, bufferedPosition, activeItemId);
    }

    @Override
@@ -141,10 +138,8 @@ public final class PlaybackState2 {
     * <ul>
     * <li> {@link PlaybackState2#STATE_NONE}</li>
     * <li> {@link PlaybackState2#STATE_STOPPED}</li>
     * <li> {@link PlaybackState2#STATE_PREPARED}</li>
     * <li> {@link PlaybackState2#STATE_PAUSED}</li>
     * <li> {@link PlaybackState2#STATE_PLAYING}</li>
     * <li> {@link PlaybackState2#STATE_FINISH}</li>
     * <li> {@link PlaybackState2#STATE_BUFFERING}</li>
     * <li> {@link PlaybackState2#STATE_ERROR}</li>
     * </ul>
@@ -181,14 +176,6 @@ public final class PlaybackState2 {
        return mProvider.getPlaybackSpeed_impl();
    }

    /**
     * Get a user readable error message. This should be set when the state is
     * {@link PlaybackState2#STATE_ERROR}.
     */
    public CharSequence getErrorMessage() {
        return mProvider.getErrorMessage_impl();
    }

    /**
     * Get the elapsed real time at which position was last updated. If the
     * position has never been set this will return 0;
+3 −3
Original line number Diff line number Diff line
@@ -20,7 +20,7 @@ import android.app.PendingIntent;
import android.media.MediaItem2;
import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
import android.media.MediaPlayerInterface.PlaybackListener;
import android.media.MediaPlayerInterface.EventCallback;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandButton;
@@ -61,8 +61,8 @@ public interface MediaSession2Provider extends TransportControlProvider {
    void setPlaylistParams_impl(PlaylistParams params);
    PlaylistParams getPlaylistParams_impl();

    void addPlaybackListener_impl(Executor executor, PlaybackListener listener);
    void removePlaybackListener_impl(PlaybackListener listener);
    void registerPlayerEventCallback_impl(Executor executor, EventCallback callback);
    void unregisterPlayerEventCallback_impl(EventCallback callback);

    interface CommandProvider {
        int getCommandCode_impl();
+0 −2
Original line number Diff line number Diff line
@@ -33,8 +33,6 @@ public interface PlaybackState2Provider {

    float getPlaybackSpeed_impl();

    CharSequence getErrorMessage_impl();

    long getLastPositionUpdateTime_impl();

    long getCurrentPlaylistItemIndex_impl();
Loading