Merge "MediaSession2: Implement skipTo APIs" into pi-dev
diff --git a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
index a241abc..7317b44 100644
--- a/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
+++ b/packages/MediaComponents/src/com/android/media/IMediaSession2.aidl
@@ -64,6 +64,9 @@
     void addPlaylistItem(IMediaController2 caller, int index, in Bundle mediaItem);
     void removePlaylistItem(IMediaController2 caller, in Bundle mediaItem);
     void replacePlaylistItem(IMediaController2 caller, int index, in Bundle mediaItem);
+    void skipToPlaylistItem(IMediaController2 caller, in Bundle mediaItem);
+    void skipToPreviousItem(IMediaController2 caller);
+    void skipToNextItem(IMediaController2 caller);
 
     //////////////////////////////////////////////////////////////////////////////////////////////
     // library service specific
diff --git a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
index b6d3e55..15922f7 100644
--- a/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaController2Impl.java
@@ -335,13 +335,48 @@
     }
 
     @Override
+    public void skipToPlaylistItem_impl(MediaItem2 item) {
+        if (item == null) {
+            throw new IllegalArgumentException("item shouldn't be null");
+        }
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder != null) {
+            try {
+                binder.skipToPlaylistItem(mControllerStub, item.toBundle());
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
+    }
+
+    @Override
     public void skipToPreviousItem_impl() {
-        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM);
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder != null) {
+            try {
+                binder.skipToPreviousItem(mControllerStub);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
     }
 
     @Override
     public void skipToNextItem_impl() {
-        sendTransportControlCommand(MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM);
+        final IMediaSession2 binder = mSessionBinder;
+        if (binder != null) {
+            try {
+                binder.skipToNextItem(mControllerStub);
+            } catch (RemoteException e) {
+                Log.w(TAG, "Cannot connect to the service or the session is gone", e);
+            }
+        } else {
+            Log.w(TAG, "Session isn't active", new IllegalStateException());
+        }
     }
 
     private void sendTransportControlCommand(int commandCode) {
@@ -361,9 +396,6 @@
         }
     }
 
-    //////////////////////////////////////////////////////////////////////////////////////
-    // TODO(jaewan): Implement follows
-    //////////////////////////////////////////////////////////////////////////////////////
     @Override
     public PendingIntent getSessionActivity_impl() {
         return mSessionActivity;
@@ -617,21 +649,6 @@
     }
 
     @Override
-    public void skipToPlaylistItem_impl(MediaItem2 item) {
-        if (item == null) {
-            throw new IllegalArgumentException("item shouldn't be null");
-        }
-
-        // TODO(jaewan): Implement this
-        /*
-        Bundle args = new Bundle();
-        args.putInt(MediaSession2Stub.ARGUMENT_KEY_ITEM_INDEX, item);
-        sendTransportControlCommand(
-                MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM, args);
-        */
-    }
-
-    @Override
     public void addPlaylistItem_impl(int index, MediaItem2 item) {
         if (index < 0) {
             throw new IllegalArgumentException("index shouldn't be negative");
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
index b8c185a..8957ea8 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Impl.java
@@ -386,32 +386,36 @@
     }
 
     @Override
-    public void skipToPreviousItem_impl() {
-        ensureCallingThread();
-        // TODO(jaewan): Implement this (b/74175632)
-        /*
-        final MediaPlayerBase player = mPlayer;
-        if (player != null) {
-            // TODO implement
-            //player.skipToPrevious();
+    public void skipToPlaylistItem_impl(MediaItem2 item) {
+        if (item == null) {
+            throw new IllegalArgumentException("item shouldn't be null");
+        }
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            agent.skipToPlaylistItem(item);
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
-        */
+    }
+
+    @Override
+    public void skipToPreviousItem_impl() {
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            agent.skipToPreviousItem();
+        } else if (DEBUG) {
+            Log.d(TAG, "API calls after the close()", new IllegalStateException());
+        }
     }
 
     @Override
     public void skipToNextItem_impl() {
-        ensureCallingThread();
-        // TODO(jaewan): Implement this (b/74175632)
-        /*
-        final MediaPlayerBase player = mPlayer;
-        if (player != null) {
-            player.skipToNext();
+        final MediaPlaylistAgent agent = mPlaylistAgent;
+        if (agent != null) {
+            agent.skipToNextItem();
         } else if (DEBUG) {
             Log.d(TAG, "API calls after the close()", new IllegalStateException());
         }
-        */
     }
 
     @Override
@@ -646,24 +650,6 @@
     }
 
     @Override
-    public void skipToPlaylistItem_impl(MediaItem2 item) {
-        ensureCallingThread();
-        if (item == null) {
-            throw new IllegalArgumentException("item shouldn't be null");
-        }
-        // TODO: Uncomment or remove
-        /*
-        final MediaPlayerBase player = mPlayer;
-        if (player != null) {
-            // TODO implement
-            //player.setCurrentPlaylistItem(item);
-        } else if (DEBUG) {
-            Log.d(TAG, "API calls after the close()", new IllegalStateException());
-        }
-        */
-    }
-
-    @Override
     public void registerPlayerEventCallback_impl(Executor executor, PlayerEventCallback callback) {
         if (executor == null) {
             throw new IllegalArgumentException("executor shouldn't be null");
diff --git a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
index 8b5d5b0..4ec0da9 100644
--- a/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
+++ b/packages/MediaComponents/src/com/android/media/MediaSession2Stub.java
@@ -495,12 +495,6 @@
                 case MediaSession2.COMMAND_CODE_PLAYBACK_STOP:
                     session.getInstance().stop();
                     break;
-                case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM:
-                    session.getInstance().skipToPreviousItem();
-                    break;
-                case MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM:
-                    session.getInstance().skipToNextItem();
-                    break;
                 case MediaSession2.COMMAND_CODE_PLAYBACK_PREPARE:
                     session.getInstance().prepare();
                     break;
@@ -513,13 +507,6 @@
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SEEK_TO:
                     session.getInstance().seekTo(args.getLong(ARGUMENT_KEY_POSITION));
                     break;
-                case MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM:
-                    // TODO(jaewan): Implement
-                    /*
-                    session.getInstance().skipToPlaylistItem(
-                            args.getInt(ARGUMENT_KEY_ITEM_INDEX));
-                    */
-                    break;
                     // TODO(jaewan): Remove (b/74116823)
                     /*
                 case MediaSession2.COMMAND_CODE_PLAYBACK_SET_PLAYLIST_PARAMS:
@@ -708,9 +695,8 @@
         onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_REMOVE_ITEM,
                 (session, controller) -> {
             MediaItem2 item = MediaItem2.fromBundle(session.getContext(), mediaItem);
-            List<MediaItem2> list = session.getInstance().getPlaylist();
-            // Trick to use the same reference for calls from the controller.
-            session.getInstance().removePlaylistItem(list.get(list.indexOf(item)));
+            // Note: MediaItem2 has hidden UUID to identify it across the processes.
+            session.getInstance().removePlaylistItem(item);
         });
     }
 
@@ -721,10 +707,41 @@
                     // Resets the UUID from the incoming media id, so controller may reuse a media
                     // item multiple times for replacePlaylistItem.
                     session.getInstance().replacePlaylistItem(index,
+                            MediaItem2Impl.fromBundle(session.getContext(), mediaItem, null));
+                });
+    }
+
+    @Override
+    public void skipToPlaylistItem(IMediaController2 caller, Bundle mediaItem) {
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYLIST_SKIP_TO_PLAYLIST_ITEM,
+                (session, controller) -> {
+                    if (mediaItem == null) {
+                        Log.w(TAG, "skipToPlaylistItem(): Ignoring null mediaItem from "
+                                + controller);
+                    }
+                    // Note: MediaItem2 has hidden UUID to identify it across the processes.
+                    session.getInstance().skipToPlaylistItem(
                             MediaItem2.fromBundle(session.getContext(), mediaItem));
                 });
     }
 
+    @Override
+    public void skipToPreviousItem(IMediaController2 caller) {
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_PREV_ITEM,
+                (session, controller) -> {
+                    session.getInstance().skipToPreviousItem();
+                });
+    }
+
+    @Override
+    public void skipToNextItem(IMediaController2 caller) {
+        onCommand(caller, MediaSession2.COMMAND_CODE_PLAYBACK_SKIP_NEXT_ITEM,
+                (session, controller) -> {
+                    session.getInstance().skipToNextItem();
+                });
+    }
+
+
     //////////////////////////////////////////////////////////////////////////////////////////////
     // AIDL methods for LibrarySession overrides
     //////////////////////////////////////////////////////////////////////////////////////////////