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

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

Make MediaSession2.CommandGroup updatable

Bug: 72665979
Test: build & runtest-MediaComponents
Change-Id: Ibee285230c40d74e26237d427df2609e529bdb03
parent 78e25771
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -27,6 +27,7 @@ import com.android.media.IMediaSession2Callback;
 * Keep this interface oneway. Otherwise a malicious app may implement fake version of this,
 * and holds calls from session to make session owner(s) frozen.
 */
// TODO: Consider to make some methods oneway
interface IMediaSession2 {
    // TODO(jaewan): add onCommand() to send private command
    // TODO(jaewan): Due to the nature of oneway calls, APIs can be called in out of order
+111 −3
Original line number Diff line number Diff line
@@ -48,11 +48,13 @@ import android.media.VolumeProvider;
import android.media.session.MediaSessionManager;
import android.media.update.MediaSession2Provider;
import android.os.Bundle;
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;
import android.util.ArraySet;
import android.util.Log;

import java.lang.ref.WeakReference;
@@ -108,6 +110,8 @@ public class MediaSession2Impl implements MediaSession2Provider {
        mId = id;
        mCallback = callback;
        mCallbackExecutor = callbackExecutor;
        // Only remember player. Actual settings will be done in the initialize().
        mPlayer = player;
        mSessionStub = new MediaSession2Stub(this);

        // Infer type from the id and package name.
@@ -126,9 +130,6 @@ public class MediaSession2Impl implements MediaSession2Provider {
            mSessionToken = new SessionToken2Impl(context, Process.myUid(), TYPE_SESSION,
                    mContext.getPackageName(), null, id, mSessionStub).getInstance();
        }

        // Only remember player. Actual settings will be done in the initialize().
        mPlayer = player;
    }

    private static String getServiceName(Context context, String serviceAction, String id) {
@@ -612,6 +613,113 @@ public class MediaSession2Impl implements MediaSession2Provider {
        }
    }

    /**
     * Represent set of {@link Command}.
     */
    public static class CommandGroupImpl implements CommandGroupProvider {
        private static final String KEY_COMMANDS =
                "android.media.mediasession2.commandgroup.commands";
        private ArraySet<Command> mCommands = new ArraySet<>();
        private final Context mContext;
        private final CommandGroup mInstance;

        public CommandGroupImpl(Context context, CommandGroup instance, Object other) {
            mContext = context;
            mInstance = instance;
            if (other != null && other instanceof CommandGroupImpl) {
                mCommands.addAll(((CommandGroupImpl) other).mCommands);
            }
        }

        @Override
        public void addCommand_impl(Command command) {
            mCommands.add(command);
        }

        @Override
        public void addAllPredefinedCommands_impl() {
            // TODO(jaewan): Is there any better way than this?
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_START));
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PAUSE));
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_STOP));
            mCommands.add(new Command(mContext,
                    MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM));
            mCommands.add(new Command(mContext,
                    MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM));
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE));
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_FAST_FORWARD));
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_REWIND));
            mCommands.add(new Command(mContext, MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO));
            mCommands.add(new Command(mContext,
                    MediaSession2.COMMAND_CODE_PLAYBACK_SET_CURRENT_PLAYLIST_ITEM));
        }

        @Override
        public void removeCommand_impl(Command command) {
            mCommands.remove(command);
        }

        @Override
        public boolean hasCommand_impl(Command command) {
            return mCommands.contains(command);
        }

        @Override
        public boolean hasCommand_impl(int code) {
            if (code == MediaSession2.COMMAND_CODE_CUSTOM) {
                throw new IllegalArgumentException("Use hasCommand(Command) for custom command");
            }
            for (int i = 0; i < mCommands.size(); i++) {
                if (mCommands.valueAt(i).getCommandCode() == code) {
                    return true;
                }
            }
            return false;
        }

        /**
         * @return new bundle from the CommandGroup
         * @hide
         */
        @Override
        public Bundle toBundle_impl() {
            ArrayList<Bundle> list = new ArrayList<>();
            for (int i = 0; i < mCommands.size(); i++) {
                list.add(mCommands.valueAt(i).toBundle());
            }
            Bundle bundle = new Bundle();
            bundle.putParcelableArrayList(KEY_COMMANDS, list);
            return bundle;
        }

        /**
         * @return new instance of CommandGroup from the bundle
         * @hide
         */
        public static @Nullable CommandGroup fromBundle_impl(Context context, Bundle commands) {
            if (commands == null) {
                return null;
            }
            List<Parcelable> list = commands.getParcelableArrayList(KEY_COMMANDS);
            if (list == null) {
                return null;
            }
            CommandGroup commandGroup = new CommandGroup(context);
            for (int i = 0; i < list.size(); i++) {
                Parcelable parcelable = list.get(i);
                if (!(parcelable instanceof Bundle)) {
                    continue;
                }
                Bundle commandBundle = (Bundle) parcelable;
                Command command = Command.fromBundle(context, commandBundle);
                if (command != null) {
                    commandGroup.addCommand(command);
                }
            }
            return commandGroup;
        }
    }

    public static class ControllerInfoImpl implements ControllerInfoProvider {
        private final ControllerInfo mInstance;
        private final int mUid;
+23 −10
Original line number Diff line number Diff line
@@ -33,6 +33,7 @@ import android.media.MediaMetadata2;
import android.media.MediaPlayerInterface;
import android.media.MediaSession2;
import android.media.MediaSession2.Command;
import android.media.MediaSession2.CommandGroup;
import android.media.MediaSession2.ControllerInfo;
import android.media.MediaSession2.SessionCallback;
import android.media.MediaSessionService2;
@@ -103,16 +104,8 @@ public class ApiFactory implements StaticProvider {
    }

    @Override
    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
            Context context, ControllerInfo instance, int uid, int pid, String packageName,
            IInterface callback) {
        return new MediaSession2Impl.ControllerInfoImpl(context,
                instance, uid, pid, packageName, (IMediaSession2Callback) callback);
    }

    @Override
    public MediaSession2Provider.CommandProvider createMediaSession2Command(Command instance,
            int commandCode, String action, Bundle extra) {
    public MediaSession2Provider.CommandProvider createMediaSession2Command(
            Command instance, int commandCode, String action, Bundle extra) {
        if (action == null && extra == null) {
            return new MediaSession2Impl.CommandImpl(instance, commandCode);
        }
@@ -124,6 +117,26 @@ public class ApiFactory implements StaticProvider {
        return MediaSession2Impl.CommandImpl.fromBundle_impl(context, command);
    }

    @Override
    public MediaSession2Provider.CommandGroupProvider createMediaSession2CommandGroup(
            Context context, CommandGroup instance, CommandGroup other) {
        return new MediaSession2Impl.CommandGroupImpl(context, instance,
                (other == null) ? null : other.getProvider());
    }

    @Override
    public CommandGroup fromBundle_MediaSession2CommandGroup(Context context, Bundle commands) {
        return MediaSession2Impl.CommandGroupImpl.fromBundle_impl(context, commands);
    }

    @Override
    public MediaSession2Provider.ControllerInfoProvider createMediaSession2ControllerInfoProvider(
            Context context, ControllerInfo instance, int uid, int pid, String packageName,
            IInterface callback) {
        return new MediaSession2Impl.ControllerInfoImpl(context,
                instance, uid, pid, packageName, (IMediaSession2Callback) callback);
    }

    @Override
    public MediaSessionService2Provider createMediaSessionService2(
            MediaSessionService2 instance) {