Merge "MediaPlayer2: clean up setDataSource"
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index b747291..0828ddb 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -2137,11 +2137,19 @@
      */
     public static final int CALL_STATUS_ERROR_IO = 4;
 
+    /** Status code represents that the call has been skipped. For example, a {@link #seekTo}
+     * request may be skipped if it is followed by another {@link #seekTo} request. Another
+     * example is that a call is made when the player is in {@link #PLAYER_STATE_ERROR} state.
+     * @see EventCallback#onCallCompleted
+     */
+    // TODO: skip commands such as prepare/play/pause/etc when in error state.
+    public static final int CALL_STATUS_SKIPPED = 5;
+
     /** Status code represents that DRM operation is called before preparing a DRM scheme through
      *  {@link #prepareDrm}.
      * @see android.media.MediaPlayer2.EventCallback#onCallCompleted
      */
-    public static final int CALL_STATUS_NO_DRM_SCHEME = 5;
+    public static final int CALL_STATUS_NO_DRM_SCHEME = 6;
 
     /**
      * @hide
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 5a71afd..da4c515 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -351,23 +351,24 @@
      * Sets the data source as described by a DataSourceDesc.
      *
      * @param dsd the descriptor of data source you want to play
-     * @throws IllegalStateException if it is called in an invalid state
-     * @throws NullPointerException if dsd is null
      */
     @Override
     public void setDataSource(@NonNull DataSourceDesc dsd) {
         addTask(new Task(CALL_COMPLETED_SET_DATA_SOURCE, false) {
             @Override
-            void process() {
+            void process() throws IOException {
                 Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
-                // TODO: setDataSource could update exist data source
+                int state = getState();
+                if (state == PLAYER_STATE_ERROR) {
+                    throw new CommandSkippedException("skipped due to error state");
+                } else if (state != PLAYER_STATE_IDLE) {
+                    throw new IllegalStateException("called in wrong state " + state);
+                }
+
                 synchronized (mSrcLock) {
                     mCurrentDSD = dsd;
                     mCurrentSrcId = mSrcIdGenerator++;
-                    try {
-                        handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
-                    } catch (IOException e) {
-                    }
+                    handleDataSource(true /* isCurrent */, dsd, mCurrentSrcId);
                 }
             }
         });
@@ -386,7 +387,7 @@
         addTask(new Task(CALL_COMPLETED_SET_NEXT_DATA_SOURCE, false) {
             @Override
             void process() {
-                Preconditions.checkNotNull(dsd, "the DataSourceDesc cannot be null");
+                Preconditions.checkArgument(dsd != null, "the DataSourceDesc cannot be null");
                 synchronized (mSrcLock) {
                     mNextDSDs = new ArrayList<DataSourceDesc>(1);
                     mNextDSDs.add(dsd);
@@ -4638,6 +4639,8 @@
                 status = CALL_STATUS_ERROR_IO;
             } catch (NoDrmSchemeException e) {
                 status = CALL_STATUS_NO_DRM_SCHEME;
+            } catch (CommandSkippedException e) {
+                status = CALL_STATUS_SKIPPED;
             } catch (Exception e) {
                 status = CALL_STATUS_ERROR_UNKNOWN;
             }
@@ -4671,4 +4674,10 @@
             }
         }
     };
+
+    private final class CommandSkippedException extends RuntimeException {
+        public CommandSkippedException(String detailMessage) {
+            super(detailMessage);
+        }
+    };
 }