Merge "Make MediaSession2.CommandGroup updatable"
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index fc62a68..d5bd354 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -27,6 +27,7 @@
* 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
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index 5bb608d..53b35cc 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -48,11 +48,13 @@
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 @@
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 @@
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 @@
}
}
+ /**
+ * 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;
diff --git a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
index 0fc1ac1..8fbbe5f 100644
--- a/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
+++ b/packages/MediaComponents/src/com/android/media/update/ApiFactory.java
@@ -33,6 +33,7 @@
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 @@
}
@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);
}
@@ -125,6 +118,26 @@
}
@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) {
return new MediaSessionService2Impl(instance);