diff --git a/api/current.txt b/api/current.txt index c58a15d3fca97..a9e1f95a49902 100644 --- a/api/current.txt +++ b/api/current.txt @@ -23706,17 +23706,6 @@ package android.media { method public void onTearDown(@NonNull android.media.AudioTrack); } - public class CallbackDataSourceDesc extends android.media.DataSourceDesc { - method @NonNull public android.media.DataSourceCallback getDataSourceCallback(); - } - - public static class CallbackDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase { - ctor public CallbackDataSourceDesc.Builder(); - ctor public CallbackDataSourceDesc.Builder(@Nullable android.media.CallbackDataSourceDesc); - method @NonNull public android.media.CallbackDataSourceDesc build(); - method @NonNull public android.media.CallbackDataSourceDesc.Builder setDataSource(@NonNull android.media.DataSourceCallback); - } - public class CamcorderProfile { method public static android.media.CamcorderProfile get(int); method public static android.media.CamcorderProfile get(int, int); @@ -23783,10 +23772,18 @@ package android.media { field public static final long POSITION_UNKNOWN = 576460752303423L; // 0x20c49ba5e353fL } - protected static class DataSourceDesc.BuilderBase { - method @NonNull public T setEndPosition(long); - method @NonNull public T setMediaId(@Nullable String); - method @NonNull public T setStartPosition(long); + public static final class DataSourceDesc.Builder { + ctor public DataSourceDesc.Builder(); + ctor public DataSourceDesc.Builder(@Nullable android.media.DataSourceDesc); + method @NonNull public android.media.DataSourceDesc build(); + method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.net.Uri); + method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.net.Uri, @Nullable java.util.Map, @Nullable java.util.List); + method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor); + method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor, long, long); + method @NonNull public android.media.DataSourceDesc.Builder setDataSource(@NonNull android.media.DataSourceCallback); + method @NonNull public android.media.DataSourceDesc.Builder setEndPosition(long); + method @NonNull public android.media.DataSourceDesc.Builder setMediaId(@Nullable String); + method @NonNull public android.media.DataSourceDesc.Builder setStartPosition(long); } public final class DeniedByServerException extends android.media.MediaDrmException { @@ -23991,21 +23988,6 @@ package android.media { field public static final int EULER_Z = 2; // 0x2 } - public class FileDataSourceDesc extends android.media.DataSourceDesc { - method public long getLength(); - method public long getOffset(); - method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor(); - field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL - } - - public static class FileDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase { - ctor public FileDataSourceDesc.Builder(); - ctor public FileDataSourceDesc.Builder(@Nullable android.media.FileDataSourceDesc); - method @NonNull public android.media.FileDataSourceDesc build(); - method @NonNull public android.media.FileDataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor); - method @NonNull public android.media.FileDataSourceDesc.Builder setDataSource(@NonNull android.os.ParcelFileDescriptor, long, long); - } - public abstract class Image implements java.lang.AutoCloseable { method public abstract void close(); method public android.graphics.Rect getCropRect(); @@ -26639,21 +26621,6 @@ package android.media { ctor public UnsupportedSchemeException(String); } - public class UriDataSourceDesc extends android.media.DataSourceDesc { - method @NonNull public android.content.Context getContext(); - method @Nullable public java.util.List getCookies(); - method @Nullable public java.util.Map getHeaders(); - method @NonNull public android.net.Uri getUri(); - } - - public static class UriDataSourceDesc.Builder extends android.media.DataSourceDesc.BuilderBase { - ctor public UriDataSourceDesc.Builder(); - ctor public UriDataSourceDesc.Builder(@Nullable android.media.UriDataSourceDesc); - method @NonNull public android.media.UriDataSourceDesc build(); - method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri); - method @NonNull public android.media.UriDataSourceDesc.Builder setDataSource(@NonNull android.content.Context, @NonNull android.net.Uri, @Nullable java.util.Map, @Nullable java.util.List); - } - public interface VolumeAutomation { method @NonNull public android.media.VolumeShaper createVolumeShaper(@NonNull android.media.VolumeShaper.Configuration); } diff --git a/api/test-current.txt b/api/test-current.txt index 583bf62160dec..fbb483b6cf4d6 100644 --- a/api/test-current.txt +++ b/api/test-current.txt @@ -1077,6 +1077,17 @@ package android.media { method public android.media.BufferingParams.Builder setResumePlaybackMarkMs(int); } + public class CallbackDataSourceDesc extends android.media.DataSourceDesc { + method @NonNull public android.media.DataSourceCallback getDataSourceCallback(); + } + + public class FileDataSourceDesc extends android.media.DataSourceDesc { + method public long getLength(); + method public long getOffset(); + method @NonNull public android.os.ParcelFileDescriptor getParcelFileDescriptor(); + field public static final long FD_LENGTH_UNKNOWN = 576460752303423487L; // 0x7ffffffffffffffL + } + public static final class MediaCodecInfo.VideoCapabilities.PerformancePoint { ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(int, int, int, int, @NonNull android.util.Size); ctor public MediaCodecInfo.VideoCapabilities.PerformancePoint(@NonNull android.media.MediaCodecInfo.VideoCapabilities.PerformancePoint, @NonNull android.util.Size); @@ -1101,6 +1112,12 @@ package android.media { method public android.media.PlaybackParams setAudioStretchMode(int); } + public class UriDataSourceDesc extends android.media.DataSourceDesc { + method @Nullable public java.util.List getCookies(); + method @Nullable public java.util.Map getHeaders(); + method @NonNull public android.net.Uri getUri(); + } + public static final class VolumeShaper.Configuration.Builder { method @NonNull public android.media.VolumeShaper.Configuration.Builder setOptionFlags(int); } diff --git a/media/apex/java/android/media/CallbackDataSourceDesc.java b/media/apex/java/android/media/CallbackDataSourceDesc.java index 9209ca94897f7..d9db62e99126e 100644 --- a/media/apex/java/android/media/CallbackDataSourceDesc.java +++ b/media/apex/java/android/media/CallbackDataSourceDesc.java @@ -17,7 +17,7 @@ package android.media; import android.annotation.NonNull; -import android.annotation.Nullable; +import android.annotation.TestApi; /** * Structure of data source descriptor for sources using callback. @@ -26,12 +26,16 @@ import android.annotation.Nullable; * {@link MediaPlayer2#setNextDataSources} to set data source for playback. * *

Users should use {@link Builder} to create {@link CallbackDataSourceDesc}. - * + * @hide */ +@TestApi public class CallbackDataSourceDesc extends DataSourceDesc { private DataSourceCallback mDataSourceCallback; - private CallbackDataSourceDesc() { + CallbackDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs, + DataSourceCallback dataSourceCallback) { + super(mediaId, startPositionMs, endPositionMs); + mDataSourceCallback = dataSourceCallback; } /** @@ -41,75 +45,4 @@ public class CallbackDataSourceDesc extends DataSourceDesc { public @NonNull DataSourceCallback getDataSourceCallback() { return mDataSourceCallback; } - - /** - * Builder class for {@link CallbackDataSourceDesc} objects. - *

Here is an example where Builder is used to define the - * {@link CallbackDataSourceDesc} to be used by a {@link MediaPlayer2} instance: - * - *

-     * CallbackDataSourceDesc newDSD = new CallbackDataSourceDesc.Builder()
-     *         .setDataSource(media2DataSource)
-     *         .setStartPosition(1000)
-     *         .setEndPosition(15000)
-     *         .build();
-     * mediaplayer2.setDataSourceDesc(newDSD);
-     * 
- */ - public static class Builder extends BuilderBase { - private DataSourceCallback mDataSourceCallback; - - /** - * Constructs a new Builder with the defaults. - */ - public Builder() { - super(); - } - - /** - * Constructs a new Builder from a given {@link CallbackDataSourceDesc} instance - * @param dsd the {@link CallbackDataSourceDesc} object whose data will be reused - * in the new Builder. - */ - public Builder(@Nullable CallbackDataSourceDesc dsd) { - super(dsd); - if (dsd == null) { - return; // use default - } - mDataSourceCallback = dsd.mDataSourceCallback; - } - - /** - * Combines all of the fields that have been set and return a new - * {@link CallbackDataSourceDesc} object. IllegalStateException will be - * thrown if there is conflict between fields. - * - * @return a new {@link CallbackDataSourceDesc} object - */ - public @NonNull CallbackDataSourceDesc build() { - if (mDataSourceCallback == null) { - throw new IllegalStateException( - "DataSourceCallback should not be null"); - } - - CallbackDataSourceDesc dsd = new CallbackDataSourceDesc(); - super.build(dsd); - dsd.mDataSourceCallback = mDataSourceCallback; - - return dsd; - } - - /** - * Sets the data source (DataSourceCallback) to use. - * - * @param dscb the DataSourceCallback for the media to play - * @return the same Builder instance. - * @throws NullPointerException if dscb is null. - */ - public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) { - Media2Utils.checkArgument(dscb != null, "data source cannot be null."); - mDataSourceCallback = dscb; - return this; - } - } } diff --git a/media/apex/java/android/media/DataSourceDesc.java b/media/apex/java/android/media/DataSourceDesc.java index e6fd120b6d5bf..be80c22558445 100644 --- a/media/apex/java/android/media/DataSourceDesc.java +++ b/media/apex/java/android/media/DataSourceDesc.java @@ -18,15 +18,22 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; +import android.net.Uri; +import android.os.ParcelFileDescriptor; + +import java.net.CookieHandler; +import java.net.CookieManager; +import java.net.HttpCookie; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; /** - * Base class of data source descriptor. + * Data source descriptor. * * Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and * {@link MediaPlayer2#setNextDataSources} to set data source for playback. - * - *

Users should use subclasses' builder to change {@link DataSourceDesc}. - * */ public class DataSourceDesc { // intentionally less than long.MAX_VALUE @@ -45,7 +52,10 @@ public class DataSourceDesc { private long mStartPositionMs = 0; private long mEndPositionMs = POSITION_UNKNOWN; - DataSourceDesc() { + DataSourceDesc(String mediaId, long startPositionMs, long endPositionMs) { + mMediaId = mediaId; + mStartPositionMs = startPositionMs; + mEndPositionMs = endPositionMs; } /** @@ -97,17 +107,48 @@ public class DataSourceDesc { } /** - * Base class for Builders in the subclasses of {@link DataSourceDesc}. + * Builder for {@link DataSourceDesc}. + *

+ * Here is an example where Builder is used to define the + * {@link DataSourceDesc} to be used by a {@link MediaPlayer2} instance: + * + *

+     * DataSourceDesc newDSD = new DataSourceDesc.Builder()
+     *         .setDataSource(context, uri, headers, cookies)
+     *         .setStartPosition(1000)
+     *         .setEndPosition(15000)
+     *         .build();
+     * mediaplayer2.setDataSourceDesc(newDSD);
+     * 
*/ - protected static class BuilderBase { + public static final class Builder { + private static final int SOURCE_TYPE_UNKNOWN = 0; + private static final int SOURCE_TYPE_URI = 1; + private static final int SOURCE_TYPE_FILE = 2; + private static final int SOURCE_TYPE_CALLBACK = 3; + + private int mSourceType = SOURCE_TYPE_UNKNOWN; private String mMediaId; private long mStartPositionMs = 0; private long mEndPositionMs = POSITION_UNKNOWN; + // For UriDataSourceDesc + private Uri mUri; + private Map mHeader; + private List mCookies; + + // For FileDataSourceDesc + private ParcelFileDescriptor mPFD; + private long mOffset = 0; + private long mLength = FileDataSourceDesc.FD_LENGTH_UNKNOWN; + + // For CallbackDataSourceDesc + private DataSourceCallback mDataSourceCallback; + /** * Constructs a new BuilderBase with the defaults. */ - BuilderBase() { + public Builder() { } /** @@ -115,33 +156,61 @@ public class DataSourceDesc { * @param dsd the {@link DataSourceDesc} object whose data will be reused * in the new BuilderBase. */ - BuilderBase(DataSourceDesc dsd) { + public Builder(@Nullable DataSourceDesc dsd) { if (dsd == null) { return; } mMediaId = dsd.mMediaId; mStartPositionMs = dsd.mStartPositionMs; mEndPositionMs = dsd.mEndPositionMs; + if (dsd instanceof FileDataSourceDesc) { + mSourceType = SOURCE_TYPE_FILE; + mPFD = ((FileDataSourceDesc) dsd).getParcelFileDescriptor(); + mOffset = ((FileDataSourceDesc) dsd).getOffset(); + mLength = ((FileDataSourceDesc) dsd).getLength(); + } else if (dsd instanceof UriDataSourceDesc) { + mSourceType = SOURCE_TYPE_URI; + mUri = ((UriDataSourceDesc) dsd).getUri(); + mHeader = ((UriDataSourceDesc) dsd).getHeaders(); + mCookies = ((UriDataSourceDesc) dsd).getCookies(); + } else if (dsd instanceof CallbackDataSourceDesc) { + mSourceType = SOURCE_TYPE_CALLBACK; + mDataSourceCallback = ((CallbackDataSourceDesc) dsd).getDataSourceCallback(); + } else { + throw new IllegalStateException("Unknown source type:" + mSourceType); + } } /** * Sets all fields that have been set in the {@link DataSourceDesc} object. * IllegalStateException will be thrown if there is conflict between fields. * - * @param dsd an instance of subclass of {@link DataSourceDesc} whose data will be set - * @return the same instance of subclass of {@link DataSourceDesc} + * @return {@link DataSourceDesc} */ - void build(@NonNull DataSourceDesc dsd) { - Media2Utils.checkArgument(dsd != null, "dsd cannot be null."); - + @NonNull + public DataSourceDesc build() { + if (mSourceType == SOURCE_TYPE_UNKNOWN) { + throw new IllegalStateException("Source is not set."); + } if (mStartPositionMs > mEndPositionMs) { throw new IllegalStateException("Illegal start/end position: " + mStartPositionMs + " : " + mEndPositionMs); } - dsd.mMediaId = mMediaId; - dsd.mStartPositionMs = mStartPositionMs; - dsd.mEndPositionMs = mEndPositionMs; + DataSourceDesc desc; + if (mSourceType == SOURCE_TYPE_FILE) { + desc = new FileDataSourceDesc( + mMediaId, mStartPositionMs, mEndPositionMs, mPFD, mOffset, mLength); + } else if (mSourceType == SOURCE_TYPE_URI) { + desc = new UriDataSourceDesc( + mMediaId, mStartPositionMs, mEndPositionMs, mUri, mHeader, mCookies); + } else if (mSourceType == SOURCE_TYPE_CALLBACK) { + desc = new CallbackDataSourceDesc( + mMediaId, mStartPositionMs, mEndPositionMs, mDataSourceCallback); + } else { + throw new IllegalStateException("Unknown source type:" + mSourceType); + } + return desc; } /** @@ -150,9 +219,10 @@ public class DataSourceDesc { * @param mediaId the media Id of this data source * @return the same Builder instance. */ - public @NonNull T setMediaId(@Nullable String mediaId) { + @NonNull + public Builder setMediaId(@Nullable String mediaId) { mMediaId = mediaId; - return (T) this; + return this; } /** @@ -163,12 +233,13 @@ public class DataSourceDesc { * @return the same Builder instance. * */ - public @NonNull T setStartPosition(long position) { + @NonNull + public Builder setStartPosition(long position) { if (position < 0) { position = 0; } mStartPositionMs = position; - return (T) this; + return this; } /** @@ -179,12 +250,155 @@ public class DataSourceDesc { * @param position the end position in milliseconds at which the playback will end * @return the same Builder instance. */ - public @NonNull T setEndPosition(long position) { + @NonNull + public Builder setEndPosition(long position) { if (position < 0) { position = LONG_MAX_TIME_MS; } mEndPositionMs = position; - return (T) this; + return this; + } + + /** + * Sets the data source as a content Uri. + * + * @param uri the Content URI of the data you want to play + * @return the same Builder instance. + * @throws NullPointerException if context or uri is null. + */ + @NonNull + public Builder setDataSource(@NonNull Uri uri) { + setSourceType(SOURCE_TYPE_URI); + Media2Utils.checkArgument(uri != null, "uri cannot be null"); + mUri = uri; + return this; + } + + /** + * Sets the data source as a content Uri. + * + * To provide cookies for the subsequent HTTP requests, you can install your own default + * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you + * can use this API to pass the cookies as a list of HttpCookie. If the app has not + * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager + * and populates its CookieStore with the provided cookies when this data source is passed + * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler + * is required to be of CookieManager type such that {@link MediaPlayer2} can update the + * manager’s CookieStore. + * + *

Note that the cross domain redirection is allowed by default, + * but that can be changed with key/value pairs through the headers parameter with + * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to + * disallow or allow cross domain redirection. + * + * @param uri the Content URI of the data you want to play + * @param headers the headers to be sent together with the request for the data + * The headers must not include cookies. Instead, use the cookies param. + * @param cookies the cookies to be sent together with the request + * @return the same Builder instance. + * @throws NullPointerException if context or uri is null. + * @throws IllegalArgumentException if the cookie handler is not of CookieManager type + * when cookies are provided. + */ + @NonNull + public Builder setDataSource(@NonNull Uri uri, @Nullable Map headers, + @Nullable List cookies) { + setSourceType(SOURCE_TYPE_URI); + Media2Utils.checkArgument(uri != null, "uri cannot be null"); + if (cookies != null) { + CookieHandler cookieHandler = CookieHandler.getDefault(); + if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) { + throw new IllegalArgumentException( + "The cookie handler has to be of CookieManager type " + + "when cookies are provided."); + } + } + + mUri = uri; + if (headers != null) { + mHeader = new HashMap(headers); + } + if (cookies != null) { + mCookies = new ArrayList(cookies); + } + return this; + } + + /** + * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} + * created by this builder is passed to {@link MediaPlayer2} via + * {@link MediaPlayer2#setDataSource}, + * {@link MediaPlayer2#setNextDataSource} or + * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will + * close the ParcelFileDescriptor. + * + * @param pfd the ParcelFileDescriptor for the file to play + * @return the same Builder instance. + * @throws NullPointerException if pfd is null. + */ + @NonNull + public Builder setDataSource(@NonNull ParcelFileDescriptor pfd) { + setSourceType(SOURCE_TYPE_FILE); + Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); + mPFD = pfd; + return this; + } + + /** + * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be + * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} + * created by this builder is passed to {@link MediaPlayer2} via + * {@link MediaPlayer2#setDataSource}, + * {@link MediaPlayer2#setNextDataSource} or + * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will + * close the ParcelFileDescriptor. + * + * Any negative number for offset is treated as 0. + * Any negative number for length is treated as maximum length of the data source. + * + * @param pfd the ParcelFileDescriptor for the file to play + * @param offset the offset into the file where the data to be played starts, in bytes + * @param length the length in bytes of the data to be played + * @return the same Builder instance. + * @throws NullPointerException if pfd is null. + */ + @NonNull + public Builder setDataSource( + @NonNull ParcelFileDescriptor pfd, long offset, long length) { + setSourceType(SOURCE_TYPE_FILE); + Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); + if (offset < 0) { + offset = 0; + } + if (length < 0) { + length = FileDataSourceDesc.FD_LENGTH_UNKNOWN; + } + mPFD = pfd; + mOffset = offset; + mLength = length; + return this; + } + + /** + * Sets the data source (DataSourceCallback) to use. + * + * @param dscb the DataSourceCallback for the media to play + * @return the same Builder instance. + * @throws NullPointerException if dscb is null. + */ + public @NonNull Builder setDataSource(@NonNull DataSourceCallback dscb) { + setSourceType(SOURCE_TYPE_CALLBACK); + Media2Utils.checkArgument(dscb != null, "data source cannot be null."); + mDataSourceCallback = dscb; + return this; + } + + private void setSourceType(int type) { + if (mSourceType != SOURCE_TYPE_UNKNOWN) { + throw new IllegalStateException("Source is already set. type=" + mSourceType); + } + mSourceType = type; } } } diff --git a/media/apex/java/android/media/FileDataSourceDesc.java b/media/apex/java/android/media/FileDataSourceDesc.java index 4b703670abf11..feb67e136d9bc 100644 --- a/media/apex/java/android/media/FileDataSourceDesc.java +++ b/media/apex/java/android/media/FileDataSourceDesc.java @@ -17,7 +17,7 @@ package android.media; import android.annotation.NonNull; -import android.annotation.Nullable; +import android.annotation.TestApi; import android.os.ParcelFileDescriptor; import android.util.Log; @@ -30,8 +30,9 @@ import java.io.IOException; * {@link MediaPlayer2#setNextDataSources} to set data source for playback. * *

Users should use {@link Builder} to create {@link FileDataSourceDesc}. - * + * @hide */ +@TestApi public class FileDataSourceDesc extends DataSourceDesc { private static final String TAG = "FileDataSourceDesc"; @@ -48,8 +49,12 @@ public class FileDataSourceDesc extends DataSourceDesc { private int mCount = 0; private boolean mClosed = false; - private FileDataSourceDesc() { - super(); + FileDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs, + ParcelFileDescriptor pfd, long offset, long length) { + super(mediaId, startPositionMs, endPositionMs); + mPFD = pfd; + mOffset = offset; + mLength = length; } /** @@ -128,133 +133,4 @@ public class FileDataSourceDesc extends DataSourceDesc { public long getLength() { return mLength; } - - /** - * Builder class for {@link FileDataSourceDesc} objects. - *

Here is an example where Builder is used to define the - * {@link FileDataSourceDesc} to be used by a {@link MediaPlayer2} instance: - * - *

-     * FileDataSourceDesc newDSD = new FileDataSourceDesc.Builder()
-     *         .setDataSource(pfd, 0, srcLength)
-     *         .setStartPosition(1000)
-     *         .setEndPosition(15000)
-     *         .build();
-     * mediaplayer2.setDataSourceDesc(newDSD);
-     * 
- */ - public static class Builder extends BuilderBase { - private ParcelFileDescriptor mPFD; - private long mOffset = 0; - private long mLength = FD_LENGTH_UNKNOWN; - - /** - * Constructs a new Builder with the defaults. - */ - public Builder() { - super(); - } - - /** - * Constructs a new Builder from a given {@link FileDataSourceDesc} instance - * @param dsd the {@link FileDataSourceDesc} object whose data will be reused - * in the new Builder. - */ - public Builder(@Nullable FileDataSourceDesc dsd) { - super(dsd); - if (dsd == null) { - return; // use default - } - mPFD = dsd.mPFD; - mOffset = dsd.mOffset; - mLength = dsd.mLength; - } - - /** - * Combines all of the fields that have been set and return a new - * {@link FileDataSourceDesc} object. IllegalStateException will be - * thrown if there is conflict between fields. - * - * @return a new {@link FileDataSourceDesc} object - */ - public @NonNull FileDataSourceDesc build() { - if (mPFD == null) { - throw new IllegalStateException( - "underline ParcelFileDescriptor should not be null"); - } - try { - mPFD.getFd(); - } catch (IllegalStateException e) { - throw new IllegalStateException("ParcelFileDescriptor has been closed"); - } - - FileDataSourceDesc dsd = new FileDataSourceDesc(); - super.build(dsd); - dsd.mPFD = mPFD; - dsd.mOffset = mOffset; - dsd.mLength = mLength; - - return dsd; - } - - /** - * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} - * created by this builder is passed to {@link MediaPlayer2} via - * {@link MediaPlayer2#setDataSource}, - * {@link MediaPlayer2#setNextDataSource} or - * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will - * close the ParcelFileDescriptor. - * - * @param pfd the ParcelFileDescriptor for the file to play - * @return the same Builder instance. - * @throws NullPointerException if pfd is null. - */ - public @NonNull Builder setDataSource(@NonNull ParcelFileDescriptor pfd) { - Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); - resetDataSource(); - mPFD = pfd; - return this; - } - - /** - * Sets the data source (ParcelFileDescriptor) to use. The ParcelFileDescriptor must be - * seekable (N.B. a LocalSocket is not seekable). When the {@link FileDataSourceDesc} - * created by this builder is passed to {@link MediaPlayer2} via - * {@link MediaPlayer2#setDataSource}, - * {@link MediaPlayer2#setNextDataSource} or - * {@link MediaPlayer2#setNextDataSources}, MediaPlayer2 will - * close the ParcelFileDescriptor. - * - * Any negative number for offset is treated as 0. - * Any negative number for length is treated as maximum length of the data source. - * - * @param pfd the ParcelFileDescriptor for the file to play - * @param offset the offset into the file where the data to be played starts, in bytes - * @param length the length in bytes of the data to be played - * @return the same Builder instance. - * @throws NullPointerException if pfd is null. - */ - public @NonNull Builder setDataSource( - @NonNull ParcelFileDescriptor pfd, long offset, long length) { - Media2Utils.checkArgument(pfd != null, "pfd cannot be null."); - if (offset < 0) { - offset = 0; - } - if (length < 0) { - length = FD_LENGTH_UNKNOWN; - } - resetDataSource(); - mPFD = pfd; - mOffset = offset; - mLength = length; - return this; - } - - private void resetDataSource() { - mPFD = null; - mOffset = 0; - mLength = FD_LENGTH_UNKNOWN; - } - } } diff --git a/media/apex/java/android/media/MediaPlayer2.java b/media/apex/java/android/media/MediaPlayer2.java index 13ff108b6e4fc..68a46ed5b8b88 100644 --- a/media/apex/java/android/media/MediaPlayer2.java +++ b/media/apex/java/android/media/MediaPlayer2.java @@ -898,7 +898,7 @@ public class MediaPlayer2 implements AutoCloseable, AudioRouting { UriDataSourceDesc uriDSD = (UriDataSourceDesc) dsd; handleDataSource(isCurrent, srcId, - uriDSD.getContext(), + mContext, uriDSD.getUri(), uriDSD.getHeaders(), uriDSD.getCookies(), diff --git a/media/apex/java/android/media/UriDataSourceDesc.java b/media/apex/java/android/media/UriDataSourceDesc.java index c0b3c825323ca..eaedf1e377e2e 100644 --- a/media/apex/java/android/media/UriDataSourceDesc.java +++ b/media/apex/java/android/media/UriDataSourceDesc.java @@ -18,11 +18,9 @@ package android.media; import android.annotation.NonNull; import android.annotation.Nullable; -import android.content.Context; +import android.annotation.TestApi; import android.net.Uri; -import java.net.CookieHandler; -import java.net.CookieManager; import java.net.HttpCookie; import java.util.ArrayList; import java.util.HashMap; @@ -36,15 +34,20 @@ import java.util.Map; * {@link MediaPlayer2#setNextDataSources} to set data source for playback. * *

Users should use {@link Builder} to change {@link UriDataSourceDesc}. - * + * @hide */ +@TestApi public class UriDataSourceDesc extends DataSourceDesc { private Uri mUri; private Map mHeader; private List mCookies; - private Context mContext; - private UriDataSourceDesc() { + UriDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs, + Uri uri, Map header, List cookies) { + super(mediaId, startPositionMs, endPositionMs); + mUri = uri; + mHeader = header; + mCookies = cookies; } /** @@ -76,155 +79,4 @@ public class UriDataSourceDesc extends DataSourceDesc { } return new ArrayList(mCookies); } - - /** - * Return the Context used for resolving the Uri of this data source. - * @return the Context used for resolving the Uri of this data source - */ - public @NonNull Context getContext() { - return mContext; - } - - /** - * Builder class for {@link UriDataSourceDesc} objects. - *

Here is an example where Builder is used to define the - * {@link UriDataSourceDesc} to be used by a {@link MediaPlayer2} instance: - * - *

-     * UriDataSourceDesc newDSD = new UriDataSourceDesc.Builder()
-     *         .setDataSource(context, uri, headers, cookies)
-     *         .setStartPosition(1000)
-     *         .setEndPosition(15000)
-     *         .build();
-     * mediaplayer2.setDataSourceDesc(newDSD);
-     * 
- */ - public static class Builder extends BuilderBase { - private Uri mUri; - private Map mHeader; - private List mCookies; - private Context mContext; - - /** - * Constructs a new Builder with the defaults. - */ - public Builder() { - super(); - } - - /** - * Constructs a new Builder from a given {@link UriDataSourceDesc} instance - * @param dsd the {@link UriDataSourceDesc} object whose data will be reused - * in the new Builder. - */ - public Builder(@Nullable UriDataSourceDesc dsd) { - super(dsd); - if (dsd == null) { - return; // use default - } - mUri = dsd.mUri; - mHeader = dsd.mHeader; - mCookies = dsd.mCookies; - mContext = dsd.mContext; - } - - /** - * Combines all of the fields that have been set and return a new - * {@link UriDataSourceDesc} object. IllegalStateException will be - * thrown if there is conflict between fields. - * - * @return a new {@link UriDataSourceDesc} object - */ - public @NonNull UriDataSourceDesc build() { - if (mUri == null || mContext == null) { - throw new IllegalStateException( - "Uri and Context should not be null"); - } - - UriDataSourceDesc dsd = new UriDataSourceDesc(); - super.build(dsd); - dsd.mUri = mUri; - dsd.mHeader = mHeader; - dsd.mCookies = mCookies; - dsd.mContext = mContext; - - return dsd; - } - - /** - * Sets the data source as a content Uri. - * - * @param context the Context to use when resolving the Uri - * @param uri the Content URI of the data you want to play - * @return the same Builder instance. - * @throws NullPointerException if context or uri is null. - */ - public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri) { - Media2Utils.checkArgument(context != null, "context cannot be null"); - Media2Utils.checkArgument(uri != null, "uri cannot be null"); - resetDataSource(); - mUri = uri; - mContext = context; - return this; - } - - /** - * Sets the data source as a content Uri. - * - * To provide cookies for the subsequent HTTP requests, you can install your own default - * cookie handler and use other variants of setDataSource APIs instead. Alternatively, you - * can use this API to pass the cookies as a list of HttpCookie. If the app has not - * installed a CookieHandler already, {@link MediaPlayer2} will create a CookieManager - * and populates its CookieStore with the provided cookies when this data source is passed - * to {@link MediaPlayer2}. If the app has installed its own handler already, the handler - * is required to be of CookieManager type such that {@link MediaPlayer2} can update the - * manager’s CookieStore. - * - *

Note that the cross domain redirection is allowed by default, - * but that can be changed with key/value pairs through the headers parameter with - * "android-allow-cross-domain-redirect" as the key and "0" or "1" as the value to - * disallow or allow cross domain redirection. - * - * @param context the Context to use when resolving the Uri - * @param uri the Content URI of the data you want to play - * @param headers the headers to be sent together with the request for the data - * The headers must not include cookies. Instead, use the cookies param. - * @param cookies the cookies to be sent together with the request - * @return the same Builder instance. - * @throws NullPointerException if context or uri is null. - * @throws IllegalArgumentException if the cookie handler is not of CookieManager type - * when cookies are provided. - */ - public @NonNull Builder setDataSource(@NonNull Context context, @NonNull Uri uri, - @Nullable Map headers, @Nullable List cookies) { - Media2Utils.checkArgument(context != null, "context cannot be null"); - Media2Utils.checkArgument(uri != null, "uri cannot be null"); - if (cookies != null) { - CookieHandler cookieHandler = CookieHandler.getDefault(); - if (cookieHandler != null && !(cookieHandler instanceof CookieManager)) { - throw new IllegalArgumentException( - "The cookie handler has to be of CookieManager type " - + "when cookies are provided."); - } - } - - resetDataSource(); - mUri = uri; - if (headers != null) { - mHeader = new HashMap(headers); - } - if (cookies != null) { - mCookies = new ArrayList(cookies); - } - mContext = context; - return this; - } - - private void resetDataSource() { - mUri = null; - mHeader = null; - mCookies = null; - mContext = null; - } - } }