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

Commit a9ccb721 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Implement cancelSessionCommand"

parents 17b07674 c6cfa0f7
Loading
Loading
Loading
Loading
+23 −5
Original line number Diff line number Diff line
@@ -25,8 +25,7 @@ import android.os.ResultReceiver;
import java.util.Objects;

/**
 * Handles incoming commands from {@link MediaSession2} and {@link MediaLibrarySession}
 * to both {@link MediaController2} and {@link MediaBrowser2}.
 * Handles incoming commands from {@link MediaSession2} to both {@link MediaController2}.
 * @hide
 */
// @SystemApi
@@ -90,7 +89,7 @@ public final class Controller2Link implements Parcelable {
        try {
            mIController.notifyConnected(seq, connectionResult);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw new RuntimeException(e);
        }
    }

@@ -99,7 +98,7 @@ public final class Controller2Link implements Parcelable {
        try {
            mIController.notifyDisconnected(seq);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw new RuntimeException(e);
        }
    }

@@ -109,7 +108,16 @@ public final class Controller2Link implements Parcelable {
        try {
            mIController.sendSessionCommand(seq, command, args, resultReceiver);
        } catch (RemoteException e) {
            throw e.rethrowFromSystemServer();
            throw new RuntimeException(e);
        }
    }

    /** Interface method for IMediaController2.cancelSessionCommand */
    public void cancelSessionCommand(int seq) {
        try {
            mIController.cancelSessionCommand(seq);
        } catch (RemoteException e) {
            throw new RuntimeException(e);
        }
    }

@@ -133,6 +141,11 @@ public final class Controller2Link implements Parcelable {
        mController.onSessionCommand(seq, command, args, resultReceiver);
    }

    /** Stub implementation for IMediaController2.cancelSessionCommand */
    public void onCancelCommand(int seq) {
        mController.onCancelCommand(seq);
    }

    private class Controller2Stub extends IMediaController2.Stub {
        @Override
        public void notifyConnected(int seq, Bundle connectionResult) {
@@ -149,5 +162,10 @@ public final class Controller2Link implements Parcelable {
                ResultReceiver resultReceiver) {
            Controller2Link.this.onSessionCommand(seq, command, args, resultReceiver);
        }

        @Override
        public void cancelSessionCommand(int seq) {
            Controller2Link.this.onCancelCommand(seq);
        }
    }
}
+2 −0
Original line number Diff line number Diff line
@@ -33,4 +33,6 @@ oneway interface IMediaController2 {
    void notifyDisconnected(int seq) = 1;
    void sendSessionCommand(int seq, in Session2Command command, in Bundle args,
            in ResultReceiver resultReceiver) = 2;
    void cancelSessionCommand(int seq) = 3;
    // Next Id : 4
}
+2 −1
Original line number Diff line number Diff line
@@ -34,5 +34,6 @@ oneway interface IMediaSession2 {
    void disconnect(in Controller2Link caller, int seq) = 1;
    void sendSessionCommand(in Controller2Link caller, int seq, in Session2Command sessionCommand,
            in Bundle args, in ResultReceiver resultReceiver) = 2;
    // Next Id : 3
    void cancelSessionCommand(in Controller2Link caller, int seq) = 3;
    // Next Id : 4
}
+0 −1
Original line number Diff line number Diff line
@@ -16,7 +16,6 @@

package android.media;

// Code for AML only
class MediaConstants {
    // Bundle key for int
    static final String KEY_PID = "android.media.key.PID";
+93 −24
Original line number Diff line number Diff line
@@ -20,6 +20,8 @@ import static android.media.MediaConstants.KEY_ALLOWED_COMMANDS;
import static android.media.MediaConstants.KEY_PACKAGE_NAME;
import static android.media.MediaConstants.KEY_PID;
import static android.media.MediaConstants.KEY_SESSION2_STUB;
import static android.media.Session2Command.RESULT_ERROR_UNKNOWN_ERROR;
import static android.media.Session2Command.RESULT_INFO_SKIPPED;
import static android.media.Session2Token.TYPE_SESSION;

import android.annotation.NonNull;
@@ -31,6 +33,8 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.Process;
import android.os.ResultReceiver;
import android.util.ArrayMap;
import android.util.ArraySet;
import android.util.Log;

import java.util.concurrent.Executor;
@@ -69,6 +73,21 @@ public class MediaController2 implements AutoCloseable {
    private Session2CommandGroup mAllowedCommands;
    //@GuardedBy("mLock")
    private Session2Token mConnectedToken;
    //@GuardedBy("mLock")
    private ArrayMap<ResultReceiver, Integer> mPendingCommands;
    //@GuardedBy("mLock")
    private ArraySet<Integer> mRequestedCommandSeqNumbers;

    /**
     * Create a {@link MediaController2} from the {@link Session2Token}.
     * This connects to the session and may wake up the service if it's not available.
     *
     * @param context Context
     * @param token token to connect to
     */
    public MediaController2(@NonNull Context context, @NonNull Session2Token token) {
        this(context, token, context.getMainExecutor(), new ControllerCallback() {});
    }

    /**
     * Create a {@link MediaController2} from the {@link Session2Token}.
@@ -77,31 +96,27 @@ public class MediaController2 implements AutoCloseable {
     * @param context Context
     * @param token token to connect to
     * @param executor executor to run callbacks on.
     * @param callback controller callback to receive changes in
     * @param callback controller callback to receive changes in.
     */
    public MediaController2(@NonNull final Context context, @NonNull final Session2Token token,
            @NonNull final Executor executor, @NonNull final ControllerCallback callback) {
    public MediaController2(@NonNull Context context, @NonNull Session2Token token,
            @NonNull Executor executor, @NonNull ControllerCallback callback) {
        if (context == null) {
            throw new IllegalArgumentException("context shouldn't be null");
        }
        if (token == null) {
            throw new IllegalArgumentException("token shouldn't be null");
        }
        if (callback == null) {
            throw new IllegalArgumentException("callback shouldn't be null");
        }
        if (executor == null) {
            throw new IllegalArgumentException("executor shouldn't be null");
        }
        mContext = context;
        mSessionToken = token;
        mCallbackExecutor = executor;
        mCallback = callback;
        mCallbackExecutor = (executor == null) ? context.getMainExecutor() : executor;
        mCallback = (callback == null) ? new ControllerCallback() { } : callback;
        mControllerStub = new Controller2Link(this);
        // NOTE: mResultHandler uses main looper, so this MUST NOT be blocked.
        mResultHandler = new Handler(context.getMainLooper());

        mNextSeqNumber = 0;
        mPendingCommands = new ArrayMap<>();
        mRequestedCommandSeqNumbers = new ArraySet<>();

        if (token.getType() == TYPE_SESSION) {
            connectToSession();
@@ -116,11 +131,13 @@ public class MediaController2 implements AutoCloseable {
            if (mSessionBinder != null) {
                try {
                    mSessionBinder.unlinkToDeath(mDeathRecipient, 0);
                    mSessionBinder.disconnect(mControllerStub, mNextSeqNumber++);
                    mSessionBinder.disconnect(mControllerStub, getNextSeqNumber());
                } catch (RuntimeException e)  {
                    // No-op
                }
            }
            mPendingCommands.clear();
            mRequestedCommandSeqNumbers.clear();
            mCallbackExecutor.execute(() -> {
                mCallback.onDisconnected(MediaController2.this);
            });
@@ -136,7 +153,6 @@ public class MediaController2 implements AutoCloseable {
     * @return a token which will be sent together in {@link ControllerCallback#onCommandResult}
     *        when its result is received.
     */
    // TODO: make cancelable.
    public Object sendSessionCommand(@NonNull Session2Command command, @Nullable Bundle args) {
        if (command == null) {
            throw new IllegalArgumentException("command shouldn't be null");
@@ -144,26 +160,50 @@ public class MediaController2 implements AutoCloseable {

        ResultReceiver resultReceiver = new ResultReceiver(mResultHandler) {
            protected void onReceiveResult(int resultCode, Bundle resultData) {
                synchronized (mLock) {
                    mPendingCommands.remove(this);
                }
                mCallbackExecutor.execute(() -> {
                    mCallback.onCommandResult(MediaController2.this, this,
                            command, resultData);
                            command, new Session2Command.Result(resultCode, resultData));
                });
            }
        };

        synchronized (mLock) {
            if (mSessionBinder != null) {
                int seq = getNextSeqNumber();
                mPendingCommands.put(resultReceiver, seq);
                try {
                    mSessionBinder.sendSessionCommand(mControllerStub, mNextSeqNumber++,
                            command, args, resultReceiver);
                    mSessionBinder.sendSessionCommand(mControllerStub, seq, command, args,
                            resultReceiver);
                } catch (RuntimeException e)  {
                    // No-op
                    mPendingCommands.remove(resultReceiver);
                    resultReceiver.send(RESULT_ERROR_UNKNOWN_ERROR, null);
                }
            }
        }
        return resultReceiver;
    }

    /**
     * Cancels the session command previously sent.
     *
     * @param token the token which is returned from {@link #sendSessionCommand}.
     */
    public void cancelSessionCommand(@NonNull Object token) {
        if (token == null) {
            throw new IllegalArgumentException("token shouldn't be null");
        }
        synchronized (mLock) {
            if (mSessionBinder == null) return;
            Integer seq = mPendingCommands.remove(token);
            if (seq != null) {
                mSessionBinder.cancelSessionCommand(mControllerStub, seq);
            }
        }
    }

    // Called by Controller2Link.onConnected
    void onConnected(int seq, Bundle connectionResult) {
        final long token = Binder.clearCallingIdentity();
@@ -213,10 +253,26 @@ public class MediaController2 implements AutoCloseable {
            @Nullable ResultReceiver resultReceiver) {
        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                mRequestedCommandSeqNumbers.add(seq);
            }
            mCallbackExecutor.execute(() -> {
                Bundle result = mCallback.onSessionCommand(MediaController2.this, command, args);
                boolean isCanceled;
                synchronized (mLock) {
                    isCanceled = !mRequestedCommandSeqNumbers.remove(seq);
                }
                if (isCanceled) {
                    resultReceiver.send(RESULT_INFO_SKIPPED, null);
                    return;
                }
                Session2Command.Result result = mCallback.onSessionCommand(
                        MediaController2.this, command, args);
                if (resultReceiver != null) {
                    resultReceiver.send(0, result);
                    if (result == null) {
                        throw new RuntimeException("onSessionCommand shouldn't return null");
                    } else {
                        resultReceiver.send(result.getResultCode(), result.getResultData());
                    }
                }
            });
        } finally {
@@ -224,6 +280,18 @@ public class MediaController2 implements AutoCloseable {
        }
    }

    // Called by Controller2Link.onSessionCommand
    void onCancelCommand(int seq) {
        final long token = Binder.clearCallingIdentity();
        try {
            synchronized (mLock) {
                mRequestedCommandSeqNumbers.remove(seq);
            }
        } finally {
            Binder.restoreCallingIdentity(token);
        }
    }

    private int getNextSeqNumber() {
        synchronized (mLock) {
            return mNextSeqNumber++;
@@ -278,9 +346,11 @@ public class MediaController2 implements AutoCloseable {
         * @param controller the controller for this event
         * @param command the session command
         * @param args optional arguments
         * @return the result for the session command
         * @return the result for the session command. A runtime exception will be thrown if null
         *         is returned.
         */
        public Bundle onSessionCommand(@NonNull MediaController2 controller,
        @NonNull
        public Session2Command.Result onSessionCommand(@NonNull MediaController2 controller,
                @NonNull Session2Command command, @Nullable Bundle args) {
            return null;
        }
@@ -294,7 +364,6 @@ public class MediaController2 implements AutoCloseable {
         * @param result the result of the session command
         */
        public void onCommandResult(@NonNull MediaController2 controller, @NonNull Object token,
                @NonNull Session2Command command, @Nullable Bundle result) {
        }
                @NonNull Session2Command command, @NonNull Session2Command.Result result) { }
    }
}
Loading