mediaplayer: add buffering settings API

Test: compiles, ensured that params is passed all the way to source
Bug:32524218

Change-Id: I90d65e10012ecc1a78989eba80d025d513ceaadf
diff --git a/api/current.txt b/api/current.txt
index bca65ee..6faf179 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -20711,6 +20711,40 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialBufferingMode();
+    method public int getInitialBufferingWatermarkKB();
+    method public int getInitialBufferingWatermarkMs();
+    method public int getRebufferingMode();
+    method public int getRebufferingWatermarkHighKB();
+    method public int getRebufferingWatermarkHighMs();
+    method public int getRebufferingWatermarkLowKB();
+    method public int getRebufferingWatermarkLowMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUFFERING_MODE_NONE = 0; // 0x0
+    field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2
+    field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1
+    field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialBufferingMode(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingMode(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -21883,7 +21917,9 @@
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method public int getAudioSessionId();
+    method public android.media.BufferingParams getBufferingParams();
     method public int getCurrentPosition();
+    method public android.media.BufferingParams getDefaultBufferingParams();
     method public int getDuration();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -21906,6 +21942,7 @@
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public deprecated void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
+    method public void setBufferingParams(android.media.BufferingParams);
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
diff --git a/api/system-current.txt b/api/system-current.txt
index cf4db76..34c22d1 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -22296,6 +22296,40 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialBufferingMode();
+    method public int getInitialBufferingWatermarkKB();
+    method public int getInitialBufferingWatermarkMs();
+    method public int getRebufferingMode();
+    method public int getRebufferingWatermarkHighKB();
+    method public int getRebufferingWatermarkHighMs();
+    method public int getRebufferingWatermarkLowKB();
+    method public int getRebufferingWatermarkLowMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUFFERING_MODE_NONE = 0; // 0x0
+    field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2
+    field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1
+    field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialBufferingMode(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingMode(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -23468,7 +23502,9 @@
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method public int getAudioSessionId();
+    method public android.media.BufferingParams getBufferingParams();
     method public int getCurrentPosition();
+    method public android.media.BufferingParams getDefaultBufferingParams();
     method public int getDuration();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -23491,6 +23527,7 @@
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public deprecated void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
+    method public void setBufferingParams(android.media.BufferingParams);
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
diff --git a/api/test-current.txt b/api/test-current.txt
index d18af4c..db3b244 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -20801,6 +20801,40 @@
     method public default void onRoutingChanged(android.media.AudioRouting);
   }
 
+  public final class BufferingParams implements android.os.Parcelable {
+    method public int describeContents();
+    method public int getInitialBufferingMode();
+    method public int getInitialBufferingWatermarkKB();
+    method public int getInitialBufferingWatermarkMs();
+    method public int getRebufferingMode();
+    method public int getRebufferingWatermarkHighKB();
+    method public int getRebufferingWatermarkHighMs();
+    method public int getRebufferingWatermarkLowKB();
+    method public int getRebufferingWatermarkLowMs();
+    method public void writeToParcel(android.os.Parcel, int);
+    field public static final int BUFFERING_MODE_NONE = 0; // 0x0
+    field public static final int BUFFERING_MODE_SIZE_ONLY = 2; // 0x2
+    field public static final int BUFFERING_MODE_TIME_ONLY = 1; // 0x1
+    field public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3; // 0x3
+    field public static final android.os.Parcelable.Creator<android.media.BufferingParams> CREATOR;
+  }
+
+  public static class BufferingParams.Builder {
+    ctor public BufferingParams.Builder();
+    ctor public BufferingParams.Builder(android.media.BufferingParams);
+    method public android.media.BufferingParams build();
+    method public android.media.BufferingParams.Builder setInitialBufferingMode(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkKB(int);
+    method public android.media.BufferingParams.Builder setInitialBufferingWatermarkMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingMode(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkHighMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowKB(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarkLowMs(int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksKB(int, int);
+    method public android.media.BufferingParams.Builder setRebufferingWatermarksMs(int, int);
+  }
+
   public class CamcorderProfile {
     method public static android.media.CamcorderProfile get(int);
     method public static android.media.CamcorderProfile get(int, int);
@@ -21973,7 +22007,9 @@
     method public static android.media.MediaPlayer create(android.content.Context, int, android.media.AudioAttributes, int);
     method public void deselectTrack(int) throws java.lang.IllegalStateException;
     method public int getAudioSessionId();
+    method public android.media.BufferingParams getBufferingParams();
     method public int getCurrentPosition();
+    method public android.media.BufferingParams getDefaultBufferingParams();
     method public int getDuration();
     method public android.media.PlaybackParams getPlaybackParams();
     method public int getSelectedTrack(int) throws java.lang.IllegalStateException;
@@ -21996,6 +22032,7 @@
     method public void setAudioSessionId(int) throws java.lang.IllegalArgumentException, java.lang.IllegalStateException;
     method public deprecated void setAudioStreamType(int);
     method public void setAuxEffectSendLevel(float);
+    method public void setBufferingParams(android.media.BufferingParams);
     method public void setDataSource(android.content.Context, android.net.Uri) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(android.content.Context, android.net.Uri, java.util.Map<java.lang.String, java.lang.String>) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
     method public void setDataSource(java.lang.String) throws java.io.IOException, java.lang.IllegalArgumentException, java.lang.IllegalStateException, java.lang.SecurityException;
diff --git a/media/java/android/media/BufferingParams.aidl b/media/java/android/media/BufferingParams.aidl
new file mode 100644
index 0000000..d156d44
--- /dev/null
+++ b/media/java/android/media/BufferingParams.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+parcelable BufferingParams;
diff --git a/media/java/android/media/BufferingParams.java b/media/java/android/media/BufferingParams.java
new file mode 100644
index 0000000..fdcd6ba
--- /dev/null
+++ b/media/java/android/media/BufferingParams.java
@@ -0,0 +1,459 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media;
+
+import android.annotation.IntDef;
+import android.os.Parcel;
+import android.os.Parcelable;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * Structure for source buffering management params.
+ *
+ * Used by {@link MediaPlayer#getDefaultBufferingParams()},
+ * {@link MediaPlayer#getBufferingParams()} and
+ * {@link MediaPlayer#setBufferingParams(BufferingParams)}
+ * to control source buffering behavior.
+ *
+ * <p>There are two stages of source buffering in {@link MediaPlayer}: initial buffering
+ * (when {@link MediaPlayer} is being prepared) and rebuffering (when {@link MediaPlayer}
+ * is playing back source). {@link BufferingParams} includes mode and corresponding
+ * watermarks for each stage of source buffering. The watermarks could be either size
+ * based (in milliseconds), or time based (in kilobytes) or both, depending on the mode.
+ *
+ * <p>There are 4 buffering modes: {@link #BUFFERING_MODE_NONE},
+ * {@link #BUFFERING_MODE_TIME_ONLY}, {@link #BUFFERING_MODE_SIZE_ONLY} and
+ * {@link #BUFFERING_MODE_TIME_THEN_SIZE}.
+ * {@link MediaPlayer} source component has default buffering modes which can be queried
+ * by calling {@link MediaPlayer#getDefaultBufferingParams()}.
+ * Users should always use those default modes or their downsized version when trying to
+ * change buffering params. For example, {@link #BUFFERING_MODE_TIME_THEN_SIZE} can be
+ * downsized to {@link #BUFFERING_MODE_NONE}, {@link #BUFFERING_MODE_TIME_ONLY} or
+ * {@link #BUFFERING_MODE_SIZE_ONLY}. But {@link #BUFFERING_MODE_TIME_ONLY} can not be
+ * downsized to {@link #BUFFERING_MODE_SIZE_ONLY}.
+ * <ul>
+ * <li><strong>initial buffering stage:</strong> has one watermark which is used when
+ * {@link MediaPlayer} is being prepared. When cached data amount exceeds this watermark,
+ * {@link MediaPlayer} is prepared.</li>
+ * <li><strong>rebuffering stage:</strong> has two watermarks, low and high, which are
+ * used when {@link MediaPlayer} is playing back content.
+ * <ul>
+ * <li> When cached data amount exceeds high watermark, {@link MediaPlayer} will pause
+ * buffering. Buffering will resume when cache runs below some limit which could be low
+ * watermark or some intermediate value decided by the source component.</li>
+ * <li> When cached data amount runs below low watermark, {@link MediaPlayer} will paused
+ * playback. Playback will resume when cached data amount exceeds high watermark
+ * or reaches end of stream.</li>
+ * </ul>
+ * </ul>
+ * <p>Users should use {@link Builder} to change {@link BufferingParams}.
+ */
+public final class BufferingParams implements Parcelable {
+    /**
+     * This mode indicates that source buffering is not supported.
+     */
+    public static final int BUFFERING_MODE_NONE = 0;
+    /**
+     * This mode indicates that only time based source buffering is supported. This means
+     * the watermark(s) are time based.
+     */
+    public static final int BUFFERING_MODE_TIME_ONLY = 1;
+    /**
+     * This mode indicates that only size based source buffering is supported. This means
+     * the watermark(s) are size based.
+     */
+    public static final int BUFFERING_MODE_SIZE_ONLY = 2;
+    /**
+     * This mode indicates that both time and size based source buffering are supported,
+     * and time based calculation precedes size based. Size based calculation will be used
+     * only when time information is not available from the source.
+     */
+    public static final int BUFFERING_MODE_TIME_THEN_SIZE = 3;
+
+    /** @hide */
+    @IntDef(
+        value = {
+                BUFFERING_MODE_NONE,
+                BUFFERING_MODE_TIME_ONLY,
+                BUFFERING_MODE_SIZE_ONLY,
+                BUFFERING_MODE_TIME_THEN_SIZE,
+        }
+    )
+    @Retention(RetentionPolicy.SOURCE)
+    public @interface BufferingMode {}
+
+    private static final int BUFFERING_NO_WATERMARK = -1;
+
+    // params
+    private int mInitialBufferingMode = BUFFERING_MODE_NONE;
+    private int mRebufferingMode = BUFFERING_MODE_NONE;
+
+    private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
+    private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
+
+    private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
+    private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
+    private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
+    private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+
+    private BufferingParams() {
+    }
+
+    /**
+     * Return the initial buffering mode used when {@link MediaPlayer} is being prepared.
+     * @return one of the values that can be set in {@link Builder#setInitialBufferingMode(int)}
+     */
+    public int getInitialBufferingMode() {
+        return mInitialBufferingMode;
+    }
+
+    /**
+     * Return the rebuffering mode used when {@link MediaPlayer} is playing back source.
+     * @return one of the values that can be set in {@link Builder#setRebufferingMode(int)}
+     */
+    public int getRebufferingMode() {
+        return mRebufferingMode;
+    }
+
+    /**
+     * Return the time based initial buffering watermark in milliseconds.
+     * It is meaningful only when initial buffering mode obatined from
+     * {@link #getInitialBufferingMode()} is time based.
+     * @return time based initial buffering watermark in milliseconds
+     */
+    public int getInitialBufferingWatermarkMs() {
+        return mInitialWatermarkMs;
+    }
+
+    /**
+     * Return the size based initial buffering watermark in kilobytes.
+     * It is meaningful only when initial buffering mode obatined from
+     * {@link #getInitialBufferingMode()} is size based.
+     * @return size based initial buffering watermark in kilobytes
+     */
+    public int getInitialBufferingWatermarkKB() {
+        return mInitialWatermarkKB;
+    }
+
+    /**
+     * Return the time based low watermark in milliseconds for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is time based.
+     * @return time based low watermark for rebuffering in milliseconds
+     */
+    public int getRebufferingWatermarkLowMs() {
+        return mRebufferingWatermarkLowMs;
+    }
+
+    /**
+     * Return the time based high watermark in milliseconds for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is time based.
+     * @return time based high watermark for rebuffering in milliseconds
+     */
+    public int getRebufferingWatermarkHighMs() {
+        return mRebufferingWatermarkHighMs;
+    }
+
+    /**
+     * Return the size based low watermark in kilobytes for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is size based.
+     * @return size based low watermark for rebuffering in kilobytes
+     */
+    public int getRebufferingWatermarkLowKB() {
+        return mRebufferingWatermarkLowKB;
+    }
+
+    /**
+     * Return the size based high watermark in kilobytes for rebuffering.
+     * It is meaningful only when rebuffering mode obatined from
+     * {@link #getRebufferingMode()} is size based.
+     * @return size based high watermark for rebuffering in kilobytes
+     */
+    public int getRebufferingWatermarkHighKB() {
+        return mRebufferingWatermarkHighKB;
+    }
+
+    /**
+     * Builder class for {@link BufferingParams} objects.
+     * <p> Here is an example where <code>Builder</code> is used to define the
+     * {@link BufferingParams} to be used by a {@link MediaPlayer} instance:
+     *
+     * <pre class="prettyprint">
+     * BufferingParams myParams = mediaplayer.getDefaultBufferingParams();
+     * myParams = new BufferingParams.Builder(myParams)
+     *             .setInitialBufferingWatermarkMs(10000)
+     *             .build();
+     * mediaplayer.setBufferingParams(myParams);
+     * </pre>
+     */
+    public static class Builder {
+        private int mInitialBufferingMode = BUFFERING_MODE_NONE;
+        private int mRebufferingMode = BUFFERING_MODE_NONE;
+
+        private int mInitialWatermarkMs = BUFFERING_NO_WATERMARK;
+        private int mInitialWatermarkKB = BUFFERING_NO_WATERMARK;
+
+        private int mRebufferingWatermarkLowMs = BUFFERING_NO_WATERMARK;
+        private int mRebufferingWatermarkHighMs = BUFFERING_NO_WATERMARK;
+        private int mRebufferingWatermarkLowKB = BUFFERING_NO_WATERMARK;
+        private int mRebufferingWatermarkHighKB = BUFFERING_NO_WATERMARK;
+
+        /**
+         * Constructs a new Builder with the defaults.
+         * By default, both initial buffering mode and rebuffering mode are
+         * {@link BufferingParams#BUFFERING_MODE_NONE}, and all watermarks are -1.
+         */
+        public Builder() {
+        }
+
+        /**
+         * Constructs a new Builder from a given {@link BufferingParams} instance
+         * @param bp the {@link BufferingParams} object whose data will be reused
+         * in the new Builder.
+         */
+        public Builder(BufferingParams bp) {
+            mInitialBufferingMode = bp.mInitialBufferingMode;
+            mRebufferingMode = bp.mRebufferingMode;
+
+            mInitialWatermarkMs = bp.mInitialWatermarkMs;
+            mInitialWatermarkKB = bp.mInitialWatermarkKB;
+
+            mRebufferingWatermarkLowMs = bp.mRebufferingWatermarkLowMs;
+            mRebufferingWatermarkHighMs = bp.mRebufferingWatermarkHighMs;
+            mRebufferingWatermarkLowKB = bp.mRebufferingWatermarkLowKB;
+            mRebufferingWatermarkHighKB = bp.mRebufferingWatermarkHighKB;
+        }
+
+        /**
+         * Combines all of the fields that have been set and return a new
+         * {@link BufferingParams} object. <code>IllegalStateException</code> will be
+         * thrown if there is conflict between fields.
+         * @return a new {@link BufferingParams} object
+         */
+        public BufferingParams build() {
+            if (isTimeBasedMode(mRebufferingMode)
+                    && mRebufferingWatermarkLowMs > mRebufferingWatermarkHighMs) {
+                throw new IllegalStateException("Illegal watermark:"
+                        + mRebufferingWatermarkLowMs + " : " + mRebufferingWatermarkHighMs);
+            }
+            if (isSizeBasedMode(mRebufferingMode)
+                    && mRebufferingWatermarkLowKB > mRebufferingWatermarkHighKB) {
+                throw new IllegalStateException("Illegal watermark:"
+                        + mRebufferingWatermarkLowKB + " : " + mRebufferingWatermarkHighKB);
+            }
+
+            BufferingParams bp = new BufferingParams();
+            bp.mInitialBufferingMode = mInitialBufferingMode;
+            bp.mRebufferingMode = mRebufferingMode;
+
+            bp.mInitialWatermarkMs = mInitialWatermarkMs;
+            bp.mInitialWatermarkKB = mInitialWatermarkKB;
+
+            bp.mRebufferingWatermarkLowMs = mRebufferingWatermarkLowMs;
+            bp.mRebufferingWatermarkHighMs = mRebufferingWatermarkHighMs;
+            bp.mRebufferingWatermarkLowKB = mRebufferingWatermarkLowKB;
+            bp.mRebufferingWatermarkHighKB = mRebufferingWatermarkHighKB;
+            return bp;
+        }
+
+        private boolean isTimeBasedMode(int mode) {
+            return (mode == BUFFERING_MODE_TIME_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
+        }
+
+        private boolean isSizeBasedMode(int mode) {
+            return (mode == BUFFERING_MODE_SIZE_ONLY || mode == BUFFERING_MODE_TIME_THEN_SIZE);
+        }
+
+        /**
+         * Sets the initial buffering mode.
+         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+         * @return the same Builder instance.
+         */
+        public Builder setInitialBufferingMode(@BufferingMode int mode) {
+            switch (mode) {
+                case BUFFERING_MODE_NONE:
+                case BUFFERING_MODE_TIME_ONLY:
+                case BUFFERING_MODE_SIZE_ONLY:
+                case BUFFERING_MODE_TIME_THEN_SIZE:
+                     mInitialBufferingMode = mode;
+                     break;
+                default:
+                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
+            }
+            return this;
+        }
+
+        /**
+         * Sets the rebuffering mode.
+         * @param mode one of {@link BufferingParams#BUFFERING_MODE_NONE},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_SIZE_ONLY},
+         *     {@link BufferingParams#BUFFERING_MODE_TIME_THEN_SIZE},
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingMode(@BufferingMode int mode) {
+            switch (mode) {
+                case BUFFERING_MODE_NONE:
+                case BUFFERING_MODE_TIME_ONLY:
+                case BUFFERING_MODE_SIZE_ONLY:
+                case BUFFERING_MODE_TIME_THEN_SIZE:
+                     mRebufferingMode = mode;
+                     break;
+                default:
+                     throw new IllegalArgumentException("Illegal buffering mode " + mode);
+            }
+            return this;
+        }
+
+        /**
+         * Sets the time based watermark in milliseconds for initial buffering.
+         * @param watermarkMs time based watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setInitialBufferingWatermarkMs(int watermarkMs) {
+            mInitialWatermarkMs = watermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the size based watermark in kilobytes for initial buffering.
+         * @param watermarkKB size based watermark in kilobytes
+         * @return the same Builder instance.
+         */
+        public Builder setInitialBufferingWatermarkKB(int watermarkKB) {
+            mInitialWatermarkKB = watermarkKB;
+            return this;
+        }
+
+        /**
+         * Sets the time based low watermark in milliseconds for rebuffering.
+         * @param watermarkMs time based low watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkLowMs(int watermarkMs) {
+            mRebufferingWatermarkLowMs = watermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the time based high watermark in milliseconds for rebuffering.
+         * @param watermarkMs time based high watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkHighMs(int watermarkMs) {
+            mRebufferingWatermarkHighMs = watermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the size based low watermark in milliseconds for rebuffering.
+         * @param watermarkKB size based low watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkLowKB(int watermarkKB) {
+            mRebufferingWatermarkLowKB = watermarkKB;
+            return this;
+        }
+
+        /**
+         * Sets the size based high watermark in milliseconds for rebuffering.
+         * @param watermarkKB size based high watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarkHighKB(int watermarkKB) {
+            mRebufferingWatermarkHighKB = watermarkKB;
+            return this;
+        }
+
+        /**
+         * Sets the time based low and high watermarks in milliseconds for rebuffering.
+         * @param lowWatermarkMs time based low watermark in milliseconds
+         * @param highWatermarkMs time based high watermark in milliseconds
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarksMs(int lowWatermarkMs, int highWatermarkMs) {
+            mRebufferingWatermarkLowMs = lowWatermarkMs;
+            mRebufferingWatermarkHighMs = highWatermarkMs;
+            return this;
+        }
+
+        /**
+         * Sets the size based low and high watermarks in kilobytes for rebuffering.
+         * @param lowWatermarkKB size based low watermark in kilobytes
+         * @param highWatermarkKB size based high watermark in kilobytes
+         * @return the same Builder instance.
+         */
+        public Builder setRebufferingWatermarksKB(int lowWatermarkKB, int highWatermarkKB) {
+            mRebufferingWatermarkLowKB = lowWatermarkKB;
+            mRebufferingWatermarkHighKB = highWatermarkKB;
+            return this;
+        }
+    }
+
+    private BufferingParams(Parcel in) {
+        mInitialBufferingMode = in.readInt();
+        mRebufferingMode = in.readInt();
+
+        mInitialWatermarkMs = in.readInt();
+        mInitialWatermarkKB = in.readInt();
+
+        mRebufferingWatermarkLowMs = in.readInt();
+        mRebufferingWatermarkHighMs = in.readInt();
+        mRebufferingWatermarkLowKB = in.readInt();
+        mRebufferingWatermarkHighKB = in.readInt();
+    }
+
+    public static final Parcelable.Creator<BufferingParams> CREATOR =
+            new Parcelable.Creator<BufferingParams>() {
+                @Override
+                public BufferingParams createFromParcel(Parcel in) {
+                    return new BufferingParams(in);
+                }
+
+                @Override
+                public BufferingParams[] newArray(int size) {
+                    return new BufferingParams[size];
+                }
+            };
+
+
+    @Override
+    public int describeContents() {
+        return 0;
+    }
+
+    @Override
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(mInitialBufferingMode);
+        dest.writeInt(mRebufferingMode);
+
+        dest.writeInt(mInitialWatermarkMs);
+        dest.writeInt(mInitialWatermarkKB);
+
+        dest.writeInt(mRebufferingWatermarkLowMs);
+        dest.writeInt(mRebufferingWatermarkHighMs);
+        dest.writeInt(mRebufferingWatermarkLowKB);
+        dest.writeInt(mRebufferingWatermarkHighKB);
+    }
+}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index e3a0f25..4023400 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -45,6 +45,7 @@
 import android.widget.VideoView;
 import android.graphics.SurfaceTexture;
 import android.media.AudioManager;
+import android.media.BufferingParams;
 import android.media.MediaFormat;
 import android.media.MediaTimeProvider;
 import android.media.PlaybackParams;
@@ -479,6 +480,11 @@
  *     <td>{} </p></td>
  *     <td>This method can be called in any state and calling it does not change
  *         the object state. </p></td></tr>
+ * <tr><td>setBufferingParams</p></td>
+ *     <td>{Initialized, Prepared, Started, Paused, Stopped, PlaybackCompleted, Error}</p></td>
+ *     <td>{Idle} </p></td>
+ *     <td>This method does not change the object state.
+ *         </p></td></tr>
  * <tr><td>setPlaybackParams</p></td>
  *     <td>{Initialized, Prepared, Started, Paused, PlaybackCompleted, Error}</p></td>
  *     <td>{Idle, Stopped} </p></td>
@@ -1390,6 +1396,45 @@
     public native boolean isPlaying();
 
     /**
+     * Gets the default buffering management params.
+     * Calling it only after {@code setDataSource} has been called.
+     * Each type of data source might have different set of default params.
+     *
+     * @return the default buffering management params supported by the source component.
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized, or {@code setDataSource} has not been called.
+     */
+    @NonNull
+    public native BufferingParams getDefaultBufferingParams();
+
+    /**
+     * Gets the current buffering management params used by the source component.
+     * Calling it only after {@code setDataSource} has been called.
+     *
+     * @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.
+     */
+    @NonNull
+    public native BufferingParams getBufferingParams();
+
+    /**
+     * Sets buffering management params.
+     * The object sets its internal BufferingParams to the input, except that the input is
+     * invalid or not supported.
+     * Call it only after {@code setDataSource} has been called.
+     * Users should only use supported mode returned by {@link #getDefaultBufferingParams()}
+     * or its downsized version as described in {@link BufferingParams}.
+     *
+     * @param params the buffering management params.
+     *
+     * @throws IllegalStateException if the internal player engine has not been
+     * initialized or has been released, or {@code setDataSource} has not been called.
+     * @throws IllegalArgumentException if params is invalid or not supported.
+     */
+    public native void 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.,
diff --git a/media/jni/android_media_BufferingParams.h b/media/jni/android_media_BufferingParams.h
new file mode 100644
index 0000000..24c51f5
--- /dev/null
+++ b/media/jni/android_media_BufferingParams.h
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2017, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef _ANDROID_MEDIA_BUFFERING_PARAMS_H_
+#define _ANDROID_MEDIA_BUFFERING_PARAMS_H_
+
+#include <media/BufferingSettings.h>
+
+namespace android {
+
+// This entire class is inline
+struct BufferingParams {
+    BufferingSettings settings;
+
+    struct fields_t {
+        jclass      clazz;
+        jmethodID   constructID;
+
+        jfieldID    initial_buffering_mode;
+        jfieldID    rebuffering_mode;
+        jfieldID    initial_watermark_ms;
+        jfieldID    initial_watermark_kb;
+        jfieldID    rebuffering_watermark_low_ms;
+        jfieldID    rebuffering_watermark_high_ms;
+        jfieldID    rebuffering_watermark_low_kb;
+        jfieldID    rebuffering_watermark_high_kb;
+
+        void init(JNIEnv *env) {
+            jclass lclazz = env->FindClass("android/media/BufferingParams");
+            if (lclazz == NULL) {
+                return;
+            }
+
+            clazz = (jclass)env->NewGlobalRef(lclazz);
+            if (clazz == NULL) {
+                return;
+            }
+
+            constructID = env->GetMethodID(clazz, "<init>", "()V");
+
+            initial_buffering_mode = env->GetFieldID(clazz, "mInitialBufferingMode", "I");
+            rebuffering_mode = env->GetFieldID(clazz, "mRebufferingMode", "I");
+            initial_watermark_ms = env->GetFieldID(clazz, "mInitialWatermarkMs", "I");
+            initial_watermark_kb = env->GetFieldID(clazz, "mInitialWatermarkKB", "I");
+            rebuffering_watermark_low_ms = env->GetFieldID(clazz, "mRebufferingWatermarkLowMs", "I");
+            rebuffering_watermark_high_ms = env->GetFieldID(clazz, "mRebufferingWatermarkHighMs", "I");
+            rebuffering_watermark_low_kb = env->GetFieldID(clazz, "mRebufferingWatermarkLowKB", "I");
+            rebuffering_watermark_high_kb = env->GetFieldID(clazz, "mRebufferingWatermarkHighKB", "I");
+
+            env->DeleteLocalRef(lclazz);
+        }
+
+        void exit(JNIEnv *env) {
+            env->DeleteGlobalRef(clazz);
+            clazz = NULL;
+        }
+    };
+
+    void fillFromJobject(JNIEnv *env, const fields_t& fields, jobject params) {
+        settings.mInitialBufferingMode =
+            (BufferingMode)env->GetIntField(params, fields.initial_buffering_mode);
+        settings.mRebufferingMode =
+            (BufferingMode)env->GetIntField(params, fields.rebuffering_mode);
+        settings.mInitialWatermarkMs =
+            env->GetIntField(params, fields.initial_watermark_ms);
+        settings.mInitialWatermarkKB =
+            env->GetIntField(params, fields.initial_watermark_kb);
+        settings.mRebufferingWatermarkLowMs =
+            env->GetIntField(params, fields.rebuffering_watermark_low_ms);
+        settings.mRebufferingWatermarkHighMs =
+            env->GetIntField(params, fields.rebuffering_watermark_high_ms);
+        settings.mRebufferingWatermarkLowKB =
+            env->GetIntField(params, fields.rebuffering_watermark_low_kb);
+        settings.mRebufferingWatermarkHighKB =
+            env->GetIntField(params, fields.rebuffering_watermark_high_kb);
+    }
+
+    jobject asJobject(JNIEnv *env, const fields_t& fields) {
+        jobject params = env->NewObject(fields.clazz, fields.constructID);
+        if (params == NULL) {
+            return NULL;
+        }
+        env->SetIntField(params, fields.initial_buffering_mode, (jint)settings.mInitialBufferingMode);
+        env->SetIntField(params, fields.rebuffering_mode, (jint)settings.mRebufferingMode);
+        env->SetIntField(params, fields.initial_watermark_ms, (jint)settings.mInitialWatermarkMs);
+        env->SetIntField(params, fields.initial_watermark_kb, (jint)settings.mInitialWatermarkKB);
+        env->SetIntField(params, fields.rebuffering_watermark_low_ms, (jint)settings.mRebufferingWatermarkLowMs);
+        env->SetIntField(params, fields.rebuffering_watermark_high_ms, (jint)settings.mRebufferingWatermarkHighMs);
+        env->SetIntField(params, fields.rebuffering_watermark_low_kb, (jint)settings.mRebufferingWatermarkLowKB);
+        env->SetIntField(params, fields.rebuffering_watermark_high_kb, (jint)settings.mRebufferingWatermarkHighKB);
+
+        return params;
+    }
+};
+
+}  // namespace android
+
+#endif  // _ANDROID_MEDIA_BUFFERING_PARAMS_H_
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index c52ed94..8225052 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -37,6 +37,7 @@
 #include "utils/Errors.h"  // for status_t
 #include "utils/KeyedVector.h"
 #include "utils/String8.h"
+#include "android_media_BufferingParams.h"
 #include "android_media_MediaDataSource.h"
 #include "android_media_PlaybackParams.h"
 #include "android_media_SyncParams.h"
@@ -69,6 +70,7 @@
 };
 static fields_t fields;
 
+static BufferingParams::fields_t gBufferingParamsFields;
 static PlaybackParams::fields_t gPlaybackParamsFields;
 static SyncParams::fields_t gSyncParamsFields;
 
@@ -343,6 +345,66 @@
     setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
 }
 
+static jobject
+android_media_MediaPlayer_getDefaultBufferingParams(JNIEnv *env, jobject thiz)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    BufferingParams bp;
+    BufferingSettings &settings = bp.settings;
+    process_media_player_call(
+            env, thiz, mp->getDefaultBufferingSettings(&settings),
+            "java/lang/IllegalStateException", "unexpected error");
+    ALOGV("getDefaultBufferingSettings:{%s}", settings.toString().string());
+
+    return bp.asJobject(env, gBufferingParamsFields);
+}
+
+static jobject
+android_media_MediaPlayer_getBufferingParams(JNIEnv *env, jobject thiz)
+{
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return NULL;
+    }
+
+    BufferingParams bp;
+    BufferingSettings &settings = bp.settings;
+    process_media_player_call(
+            env, thiz, mp->getBufferingSettings(&settings),
+            "java/lang/IllegalStateException", "unexpected error");
+    ALOGV("getBufferingSettings:{%s}", settings.toString().string());
+
+    return bp.asJobject(env, gBufferingParamsFields);
+}
+
+static void
+android_media_MediaPlayer_setBufferingParams(JNIEnv *env, jobject thiz, jobject params)
+{
+    if (params == NULL) {
+        return;
+    }
+
+    sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+    if (mp == NULL) {
+        jniThrowException(env, "java/lang/IllegalStateException", NULL);
+        return;
+    }
+
+    BufferingParams bp;
+    bp.fillFromJobject(env, gBufferingParamsFields, params);
+    ALOGV("setBufferingParams:{%s}", bp.settings.toString().string());
+
+    process_media_player_call(
+            env, thiz, mp->setBufferingSettings(bp.settings),
+            "java/lang/IllegalStateException", "unexpected error");
+}
+
 static void
 android_media_MediaPlayer_prepare(JNIEnv *env, jobject thiz)
 {
@@ -860,6 +922,7 @@
 
     env->DeleteLocalRef(clazz);
 
+    gBufferingParamsFields.init(env);
     gPlaybackParamsFields.init(env);
     gSyncParamsFields.init(env);
 }
@@ -1046,6 +1109,9 @@
     {"_setDataSource",      "(Ljava/io/FileDescriptor;JJ)V",    (void *)android_media_MediaPlayer_setDataSourceFD},
     {"_setDataSource",      "(Landroid/media/MediaDataSource;)V",(void *)android_media_MediaPlayer_setDataSourceCallback },
     {"_setVideoSurface",    "(Landroid/view/Surface;)V",        (void *)android_media_MediaPlayer_setVideoSurface},
+    {"getDefaultBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getDefaultBufferingParams},
+    {"getBufferingParams", "()Landroid/media/BufferingParams;", (void *)android_media_MediaPlayer_getBufferingParams},
+    {"setBufferingParams", "(Landroid/media/BufferingParams;)V", (void *)android_media_MediaPlayer_setBufferingParams},
     {"_prepare",            "()V",                              (void *)android_media_MediaPlayer_prepare},
     {"prepareAsync",        "()V",                              (void *)android_media_MediaPlayer_prepareAsync},
     {"_start",              "()V",                              (void *)android_media_MediaPlayer_start},