Merge "MediaPlayer2: clean up more code"
diff --git a/media/java/android/media/MediaPlayer2.java b/media/java/android/media/MediaPlayer2.java
index 0b3c973..4e90162 100644
--- a/media/java/android/media/MediaPlayer2.java
+++ b/media/java/android/media/MediaPlayer2.java
@@ -294,7 +294,7 @@
      * reached end of stream and been paused, or never started before,
      * playback will start at the beginning.
      *
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object play();
@@ -305,21 +305,21 @@
      * After setting the datasource and the display surface, you need to
      * call prepare().
      *
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object prepare();
 
     /**
      * Pauses playback. Call play() to resume.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object pause();
 
     /**
      * Tries to play next data source if applicable.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object skipToNext();
@@ -404,7 +404,7 @@
      * You must call this method before {@link #play()} and {@link #pause()} in order
      * for the audio attributes to become effective thereafter.
      * @param attributes a non-null set of audio attributes
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setAudioAttributes(@NonNull AudioAttributes attributes);
@@ -419,7 +419,7 @@
      * Sets the data source as described by a DataSourceDesc.
      *
      * @param dsd the descriptor of data source you want to play
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setDataSource(@NonNull DataSourceDesc dsd);
@@ -429,7 +429,7 @@
      * after current data source is finished.
      *
      * @param dsd the descriptor of data source you want to play after current one
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setNextDataSource(@NonNull DataSourceDesc dsd);
@@ -438,14 +438,14 @@
      * Sets a list of data sources to be played sequentially after current data source is done.
      *
      * @param dsds the list of data sources you want to play after current one
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setNextDataSources(@NonNull List<DataSourceDesc> dsds);
 
     /**
      * Removes all data sources pending to be played.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object clearNextDataSources();
@@ -460,7 +460,7 @@
     /**
      * Configures the player to loop on the current data source.
      * @param loop true if the current data source is meant to loop.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object loopCurrent(boolean loop);
@@ -473,7 +473,7 @@
      * A value of 0.0f indicates muting, a value of 1.0f is the nominal unattenuated and unamplified
      * gain. See {@link #getMaxPlayerVolume()} for the volume range supported by this player.
      * @param volume a value between 0.0f and {@link #getMaxPlayerVolume()}.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setPlayerVolume(float volume);
@@ -502,7 +502,7 @@
      *
      * @param label An application specific Object used to help to identify the completeness
      * of a batch of commands.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object notifyWhenCommandLabelReached(@NonNull Object label);
@@ -518,7 +518,7 @@
      * played.
      *
      * @param sh the SurfaceHolder to use for video display
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     public abstract Object setDisplay(SurfaceHolder sh);
 
@@ -538,7 +538,7 @@
      *
      * @param surface The {@link Surface} to be used for the video portion of
      * the media.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setSurface(Surface surface);
@@ -558,7 +558,7 @@
      *
      * @param context the Context to use
      * @param mode    the power/wake mode to set
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      * @see android.os.PowerManager
      */
     // This is an asynchronous call.
@@ -572,7 +572,7 @@
      * access.
      *
      * @param screenOn Supply true to keep the screen on, false to allow it to turn off.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setScreenOnWhilePlaying(boolean screenOn);
@@ -667,22 +667,11 @@
      * available for the media being handled by this instance of MediaPlayer2
      * The attributes are descibed in {@link MetricsConstants}.
      *
-     *  Additional vendor-specific fields may also be present in
-     *  the return value.
+     * Additional vendor-specific fields may also be present in the return value.
      */
     public abstract PersistableBundle getMetrics();
 
     /**
-     * Checks whether the MediaPlayer2 is playing.
-     *
-     * @return true if currently playing, false otherwise
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized or has been released.
-     * @hide
-     */
-    public abstract boolean isPlaying();
-
-    /**
      * Gets the current buffering management params used by the source component.
      * Calling it only after {@code setDataSource} has been called.
      * Each type of data source might have different set of default params.
@@ -690,10 +679,10 @@
      * @return the current buffering management params used by the source component.
      * @throws IllegalStateException if the internal player engine has not been
      * initialized, or {@code setDataSource} has not been called.
-     * @hide
      */
+    // TODO: make it public when ready
     @NonNull
-    public BufferingParams getBufferingParams() {
+    BufferingParams getBufferingParams() {
         return new BufferingParams.Builder().build();
     }
 
@@ -705,80 +694,11 @@
      * The input is a hint to MediaPlayer2.
      *
      * @param params the buffering management params.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
-     *
-     * @hide
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
+    // TODO: make it public when ready
     // This is an asynchronous call.
-    public abstract Object setBufferingParams(@NonNull BufferingParams params);
-
-    /**
-     * Change playback speed of audio by resampling the audio.
-     * <p>
-     * Specifies resampling as audio mode for variable rate playback, i.e.,
-     * resample the waveform based on the requested playback rate to get
-     * a new waveform, and play back the new waveform at the original sampling
-     * frequency.
-     * When rate is larger than 1.0, pitch becomes higher.
-     * When rate is smaller than 1.0, pitch becomes lower.
-     *
-     * @hide
-     */
-    public static final int PLAYBACK_RATE_AUDIO_MODE_RESAMPLE = 2;
-
-    /**
-     * Change playback speed of audio without changing its pitch.
-     * <p>
-     * Specifies time stretching as audio mode for variable rate playback.
-     * Time stretching changes the duration of the audio samples without
-     * affecting its pitch.
-     * <p>
-     * This mode is only supported for a limited range of playback speed factors,
-     * e.g. between 1/2x and 2x.
-     *
-     * @hide
-     */
-    public static final int PLAYBACK_RATE_AUDIO_MODE_STRETCH = 1;
-
-    /**
-     * Change playback speed of audio without changing its pitch, and
-     * possibly mute audio if time stretching is not supported for the playback
-     * speed.
-     * <p>
-     * Try to keep audio pitch when changing the playback rate, but allow the
-     * system to determine how to change audio playback if the rate is out
-     * of range.
-     *
-     * @hide
-     */
-    public static final int PLAYBACK_RATE_AUDIO_MODE_DEFAULT = 0;
-
-    /** @hide */
-    @IntDef(flag = false, prefix = "PLAYBACK_RATE_AUDIO_MODE", value = {
-            PLAYBACK_RATE_AUDIO_MODE_DEFAULT,
-            PLAYBACK_RATE_AUDIO_MODE_STRETCH,
-            PLAYBACK_RATE_AUDIO_MODE_RESAMPLE,
-    })
-    @Retention(RetentionPolicy.SOURCE)
-    public @interface PlaybackRateAudioMode {}
-
-    /**
-     * Sets playback rate and audio mode.
-     *
-     * @param rate the ratio between desired playback rate and normal one.
-     * @param audioMode audio playback mode. Must be one of the supported
-     * audio modes.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     * @throws IllegalArgumentException if audioMode is not supported.
-     *
-     * @hide
-     */
-    @NonNull
-    public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
-        return new PlaybackParams();
-    }
+    abstract Object setBufferingParams(@NonNull BufferingParams params);
 
     /**
      * Sets playback rate using {@link PlaybackParams}. The object sets its internal
@@ -787,7 +707,7 @@
      * the object state.
      *
      * @param params the playback params.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setPlaybackParams(@NonNull PlaybackParams params);
@@ -796,6 +716,7 @@
      * Gets the playback params, containing the current playback rate.
      *
      * @return the playback params.
+     * @throws IllegalStateException if the internal player engine has not been initialized.
      */
     @NonNull
     public abstract PlaybackParams getPlaybackParams();
@@ -804,7 +725,7 @@
      * Sets A/V sync mode.
      *
      * @param params the A/V sync params to apply
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setSyncParams(@NonNull SyncParams params);
@@ -813,6 +734,7 @@
      * Gets the A/V sync mode.
      *
      * @return the A/V sync params
+     * @throws IllegalStateException if the internal player engine has not been initialized.
      */
     @NonNull
     public abstract SyncParams getSyncParams();
@@ -822,7 +744,7 @@
      * Same as {@link #seekTo(long, int)} with {@code mode = SEEK_PREVIOUS_SYNC}.
      *
      * @param msec the offset in milliseconds from the start to seek to
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public Object seekTo(long msec) {
@@ -882,7 +804,8 @@
     /**
      * Moves the media to specified time position by considering the given mode.
      * <p>
-     * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
+     * When seekTo is finished, the user will be notified via
+     * {@link EventCallback#onCallCompleted} with {@link #CALL_COMPLETED_SEEK_TO}.
      * There is at most one active seekTo processed at any time. If there is a to-be-completed
      * seekTo, new seekTo requests will be queued in such a way that only the last request
      * is kept. When current seekTo is completed, the queued request will be processed if
@@ -895,7 +818,7 @@
      * If msec is negative, time position zero will be used.
      * If msec is larger than duration, duration will be used.
      * @param mode the mode indicating where exactly to seek to.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object seekTo(long msec, @SeekMode int mode);
@@ -953,7 +876,7 @@
      * However, it is possible to force this player to be part of an already existing audio session
      * by calling this method.
      * This method must be called before one of the overloaded <code> setDataSource </code> methods.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setAudioSessionId(int sessionId);
@@ -979,7 +902,7 @@
      * <p>This method must be called after one of the overloaded <code> setDataSource </code>
      * methods.
      * @param effectId system wide unique id of the effect to attach
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object attachAuxEffect(int effectId);
@@ -996,7 +919,7 @@
      * x == 0 -> level = 0
      * 0 < x <= R -> level = 10^(72*(x-R)/20/R)
      * @param level send level scalar
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object setAuxEffectSendLevel(float level);
@@ -1122,7 +1045,7 @@
      * @param index the index of the track to be selected. The valid range of the index
      * is 0..total number of track - 1. The total number of tracks as well as the type of
      * each individual track can be found by calling {@link #getTrackInfo()} method.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
@@ -1139,7 +1062,7 @@
      * @param index the index of the track to be deselected. The valid range of the index
      * is 0..total number of tracks - 1. The total number of tracks as well as the type of
      * each individual track can be found by calling {@link #getTrackInfo()} method.
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      *
      * @see MediaPlayer2#getTrackInfo
      */
@@ -1872,7 +1795,7 @@
      * from the source through {@code getDrmInfo} or registering a
      * {@link DrmEventCallback#onDrmInfo}.
      *
-     * @return a token which can be used to cancel the operation later with {@link #cancel}.
+     * @return a token which can be used to cancel the operation later with {@link #cancelCommand}.
      */
     // This is an asynchronous call.
     public abstract Object prepareDrm(@NonNull UUID uuid);
diff --git a/media/java/android/media/MediaPlayer2Impl.java b/media/java/android/media/MediaPlayer2Impl.java
index 8f475a1..0431820 100644
--- a/media/java/android/media/MediaPlayer2Impl.java
+++ b/media/java/android/media/MediaPlayer2Impl.java
@@ -131,6 +131,13 @@
     @GuardedBy("mTaskLock")
     private Task mCurrentTask;
 
+    @GuardedBy("mTaskLock")
+    boolean mIsPreviousCommandSeekTo = false;
+    // |mPreviousSeekPos| and |mPreviousSeekMode| are valid only when |mIsPreviousCommandSeekTo|
+    // is true, and they are accessed on |mHandlerThread| only.
+    long mPreviousSeekPos = -1;
+    int mPreviousSeekMode = SEEK_PREVIOUS_SYNC;
+
     @GuardedBy("this")
     private boolean mReleased;
 
@@ -927,14 +934,11 @@
     private native PersistableBundle native_getMetrics();
 
     @Override
-    public native boolean isPlaying();
-
-    @Override
     @NonNull
-    public native BufferingParams getBufferingParams();
+    native BufferingParams getBufferingParams();
 
     @Override
-    public Object setBufferingParams(@NonNull BufferingParams params) {
+    Object setBufferingParams(@NonNull BufferingParams params) {
         return addTask(new Task(CALL_COMPLETED_SET_BUFFERING_PARAMS, false) {
             @Override
             void process() {
@@ -946,42 +950,6 @@
 
     private native void _setBufferingParams(@NonNull BufferingParams params);
 
-    /**
-     * Sets playback rate and audio mode.
-     *
-     * @param rate the ratio between desired playback rate and normal one.
-     * @param audioMode audio playback mode. Must be one of the supported
-     * audio modes.
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     * @throws IllegalArgumentException if audioMode is not supported.
-     *
-     * @hide
-     */
-    @Override
-    @NonNull
-    public PlaybackParams easyPlaybackParams(float rate, @PlaybackRateAudioMode int audioMode) {
-        PlaybackParams params = new PlaybackParams();
-        params.allowDefaults();
-        switch (audioMode) {
-        case PLAYBACK_RATE_AUDIO_MODE_DEFAULT:
-            params.setSpeed(rate).setPitch(1.0f);
-            break;
-        case PLAYBACK_RATE_AUDIO_MODE_STRETCH:
-            params.setSpeed(rate).setPitch(1.0f)
-                    .setAudioFallbackMode(params.AUDIO_FALLBACK_MODE_FAIL);
-            break;
-        case PLAYBACK_RATE_AUDIO_MODE_RESAMPLE:
-            params.setSpeed(rate).setPitch(rate);
-            break;
-        default:
-            final String msg = "Audio playback mode " + audioMode + " is not supported";
-            throw new IllegalArgumentException(msg);
-        }
-        return params;
-    }
-
     @Override
     public Object setPlaybackParams(@NonNull PlaybackParams params) {
         return addTask(new Task(CALL_COMPLETED_SET_PLAYBACK_PARAMS, false) {
@@ -995,26 +963,10 @@
 
     private native void _setPlaybackParams(@NonNull PlaybackParams params);
 
-    /**
-     * Gets the playback params, containing the current playback rate.
-     *
-     * @return the playback params.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     */
     @Override
     @NonNull
     public native PlaybackParams getPlaybackParams();
 
-    /**
-     * Sets A/V sync mode.
-     *
-     * @param params the A/V sync params to apply
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     * @throws IllegalArgumentException if params are not supported.
-     */
     @Override
     public Object setSyncParams(@NonNull SyncParams params) {
         return addTask(new Task(CALL_COMPLETED_SET_SYNC_PARAMS, false) {
@@ -1028,48 +980,10 @@
 
     private native void _setSyncParams(@NonNull SyncParams params);
 
-    /**
-     * Gets the A/V sync mode.
-     *
-     * @return the A/V sync params
-     *
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized.
-     */
     @Override
     @NonNull
     public native SyncParams getSyncParams();
 
-    /**
-     * Moves the media to specified time position by considering the given mode.
-     * <p>
-     * When seekTo is finished, the user will be notified via OnSeekComplete supplied by the user.
-     * There is at most one active seekTo processed at any time. If there is a to-be-completed
-     * seekTo, new seekTo requests will be queued in such a way that only the last request
-     * is kept. When current seekTo is completed, the queued request will be processed if
-     * that request is different from just-finished seekTo operation, i.e., the requested
-     * position or mode is different.
-     *
-     * @param msec the offset in milliseconds from the start to seek to.
-     * When seeking to the given time position, there is no guarantee that the data source
-     * has a frame located at the position. When this happens, a frame nearby will be rendered.
-     * If msec is negative, time position zero will be used.
-     * If msec is larger than duration, duration will be used.
-     * @param mode the mode indicating where exactly to seek to.
-     * Use {@link #SEEK_PREVIOUS_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp earlier than or the same as msec. Use
-     * {@link #SEEK_NEXT_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp later than or the same as msec. Use
-     * {@link #SEEK_CLOSEST_SYNC} if one wants to seek to a sync frame
-     * that has a timestamp closest to or the same as msec. Use
-     * {@link #SEEK_CLOSEST} if one wants to seek to a frame that may
-     * or may not be a sync frame but is closest to or the same as msec.
-     * {@link #SEEK_CLOSEST} often has larger performance overhead compared
-     * to the other options if there is no sync frame located at msec.
-     * @throws IllegalStateException if the internal player engine has not been
-     * initialized
-     * @throws IllegalArgumentException if the mode is invalid.
-     */
     @Override
     public Object seekTo(final long msec, @SeekMode int mode) {
         return addTask(new Task(CALL_COMPLETED_SEEK_TO, true) {
@@ -1090,7 +1004,23 @@
                             + Integer.MIN_VALUE);
                     posMs = Integer.MIN_VALUE;
                 }
+
+                synchronized (mTaskLock) {
+                    if (mIsPreviousCommandSeekTo
+                            && mPreviousSeekPos == posMs
+                            && mPreviousSeekMode == mode) {
+                        throw new CommandSkippedException(
+                                "same as previous seekTo");
+                    }
+                }
+
                 _seekTo(posMs, mode);
+
+                synchronized (mTaskLock) {
+                    mIsPreviousCommandSeekTo = true;
+                    mPreviousSeekPos = posMs;
+                    mPreviousSeekMode = mode;
+                }
             }
         });
     }
@@ -1124,7 +1054,7 @@
             return new MediaTimestamp(
                     getCurrentPosition() * 1000L,
                     System.nanoTime(),
-                    isPlaying() ? getPlaybackParams().getSpeed() : 0.f);
+                    getState() == PLAYER_STATE_PLAYING ? getPlaybackParams().getSpeed() : 0.f);
         } catch (IllegalStateException e) {
             return null;
         }
@@ -1152,6 +1082,11 @@
             mNextSourceState = NEXT_SOURCE_STATE_INIT;
         }
 
+        synchronized (mTaskLock) {
+            mPendingTasks.clear();
+            mIsPreviousCommandSeekTo = false;
+        }
+
         stayAwake(false);
         _reset();
         // make sure none of the listeners get called anymore
@@ -1748,6 +1683,12 @@
                 case MEDIA_SEEK_COMPLETE:
                 {
                     synchronized (mTaskLock) {
+                        if (!mPendingTasks.isEmpty()
+                                && mPendingTasks.get(0).mMediaCallType != CALL_COMPLETED_SEEK_TO
+                                && getState() == PLAYER_STATE_PLAYING) {
+                            mIsPreviousCommandSeekTo = false;
+                        }
+
                         if (mCurrentTask != null
                                 && mCurrentTask.mMediaCallType == CALL_COMPLETED_SEEK_TO
                                 && mCurrentTask.mNeedToWaitForEventToComplete) {
@@ -3150,6 +3091,12 @@
                 mDSD = mCurrentDSD;
             }
 
+            if (mMediaCallType != CALL_COMPLETED_SEEK_TO) {
+                synchronized (mTaskLock) {
+                    mIsPreviousCommandSeekTo = false;
+                }
+            }
+
             // TODO: Make native implementations asynchronous and let them send notifications.
             if (!mNeedToWaitForEventToComplete || status != CALL_STATUS_NO_ERROR) {
 
diff --git a/media/jni/android_media_MediaPlayer2.cpp b/media/jni/android_media_MediaPlayer2.cpp
index 520077f..8e30455 100644
--- a/media/jni/android_media_MediaPlayer2.cpp
+++ b/media/jni/android_media_MediaPlayer2.cpp
@@ -598,20 +598,6 @@
     process_media_player_call( env, thiz, mp->pause(), NULL, NULL );
 }
 
-static jboolean
-android_media_MediaPlayer2_isPlaying(JNIEnv *env, jobject thiz)
-{
-    sp<MediaPlayer2> mp = getMediaPlayer(env, thiz);
-    if (mp == NULL ) {
-        jniThrowException(env, "java/lang/IllegalStateException", NULL);
-        return JNI_FALSE;
-    }
-    const jboolean is_playing = mp->isPlaying();
-
-    ALOGV("isPlaying: %d", is_playing);
-    return is_playing;
-}
-
 static void
 android_media_MediaPlayer2_setPlaybackParams(JNIEnv *env, jobject thiz, jobject params)
 {
@@ -1447,7 +1433,6 @@
     {"getSyncParams",     "()Landroid/media/SyncParams;",   (void *)android_media_MediaPlayer2_getSyncParams},
     {"_seekTo",             "(JI)V",                            (void *)android_media_MediaPlayer2_seekTo},
     {"_pause",              "()V",                              (void *)android_media_MediaPlayer2_pause},
-    {"isPlaying",           "()Z",                              (void *)android_media_MediaPlayer2_isPlaying},
     {"getCurrentPosition",  "()J",                              (void *)android_media_MediaPlayer2_getCurrentPosition},
     {"getDuration",         "()J",                              (void *)android_media_MediaPlayer2_getDuration},
     {"_release",            "()V",                              (void *)android_media_MediaPlayer2_release},