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

Commit c6cfa0f7 authored by Sungsoo Lim's avatar Sungsoo Lim
Browse files

Implement cancelSessionCommand

Also this CL adds missing getter methods of MediaSession2.

Bug: 122055262
Test: build
Change-Id: I4a3108ca89982e9482b8c0aa8920caad9a4d79c8
parent 474dffd2
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