Merge "Remove MediaPlayer2"

am: 2e51cefc7e

Change-Id: I43f2b99b916bf989025d01495c0d8eeb73762886
This commit is contained in:
Marco Nelissen
2019-09-25 10:30:20 -07:00
committed by android-build-merger
16 changed files with 1 additions and 8484 deletions

View File

@@ -61,26 +61,6 @@ filegroup {
path: "apex/java",
}
filegroup {
name: "mediaplayer2-srcs",
srcs: [
"apex/java/android/media/CloseGuard.java",
"apex/java/android/media/DataSourceCallback.java",
"apex/java/android/media/DataSourceDesc.java",
"apex/java/android/media/UriDataSourceDesc.java",
"apex/java/android/media/FileDataSourceDesc.java",
"apex/java/android/media/Media2Utils.java",
"apex/java/android/media/MediaPlayer2Utils.java",
"apex/java/android/media/MediaPlayer2.java",
"apex/java/android/media/Media2HTTPService.java",
"apex/java/android/media/Media2HTTPConnection.java",
"apex/java/android/media/RoutingDelegate.java",
"apex/java/android/media/BufferingParams.java",
"apex/java/android/media/ProxyDataSourceCallback.java",
],
path: "apex/java",
}
metalava_updatable_media_args = " --error UnhiddenSystemApi " +
"--hide RequiresPermission " +
"--hide MissingPermission --hide BroadcastBehavior " +

View File

@@ -1,384 +0,0 @@
/*
* Copyright 2018 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.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;
/**
* Data source descriptor.
*
* Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* @hide
*/
public class DataSourceDesc {
// intentionally less than long.MAX_VALUE
static final long LONG_MAX = 0x7ffffffffffffffL;
// keep consistent with native code
public static final long LONG_MAX_TIME_MS = LONG_MAX / 1000;
/**
* @hide
*/
public static final long LONG_MAX_TIME_US = LONG_MAX_TIME_MS * 1000;
public static final long POSITION_UNKNOWN = LONG_MAX_TIME_MS;
private String mMediaId;
private long mStartPositionMs = 0;
private long mEndPositionMs = POSITION_UNKNOWN;
DataSourceDesc(String mediaId, long startPositionMs, long endPositionMs) {
mMediaId = mediaId;
mStartPositionMs = startPositionMs;
mEndPositionMs = endPositionMs;
}
/**
* Releases the resources held by this {@code DataSourceDesc} object.
*/
void close() {
}
// Have to declare protected for finalize() since it is protected
// in the base class Object.
@Override
protected void finalize() throws Throwable {
close();
}
/**
* Return the media Id of data source.
* @return the media Id of data source
*/
public @Nullable String getMediaId() {
return mMediaId;
}
/**
* Return the position in milliseconds at which the playback will start.
* @return the position in milliseconds at which the playback will start
*/
public long getStartPosition() {
return mStartPositionMs;
}
/**
* Return the position in milliseconds at which the playback will end.
* {@link #POSITION_UNKNOWN} means ending at the end of source content.
* @return the position in milliseconds at which the playback will end
*/
public long getEndPosition() {
return mEndPositionMs;
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("DataSourceDesc{");
sb.append("mMediaId=").append(mMediaId);
sb.append(", mStartPositionMs=").append(mStartPositionMs);
sb.append(", mEndPositionMs=").append(mEndPositionMs);
sb.append('}');
return sb.toString();
}
/**
* Builder for {@link DataSourceDesc}.
* <p>
* Here is an example where <code>Builder</code> is used to define the
* {@link DataSourceDesc} to be used by a {@link MediaPlayer2} instance:
*
* <pre class="prettyprint">
* DataSourceDesc newDSD = new DataSourceDesc.Builder()
* .setDataSource(context, uri, headers, cookies)
* .setStartPosition(1000)
* .setEndPosition(15000)
* .build();
* mediaplayer2.setDataSourceDesc(newDSD);
* </pre>
*/
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 int mSourceType = SOURCE_TYPE_UNKNOWN;
private String mMediaId;
private long mStartPositionMs = 0;
private long mEndPositionMs = POSITION_UNKNOWN;
// For UriDataSourceDesc
private Uri mUri;
private Map<String, String> mHeader;
private List<HttpCookie> mCookies;
// For FileDataSourceDesc
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FileDataSourceDesc.FD_LENGTH_UNKNOWN;
/**
* Constructs a new BuilderBase with the defaults.
*/
public Builder() {
}
/**
* Constructs a new BuilderBase from a given {@link DataSourceDesc} instance
* @param dsd the {@link DataSourceDesc} object whose data will be reused
* in the new BuilderBase.
*/
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 {
throw new IllegalStateException("Unknown source type:" + mSourceType);
}
}
/**
* Sets all fields that have been set in the {@link DataSourceDesc} object.
* <code>IllegalStateException</code> will be thrown if there is conflict between fields.
*
* @return {@link DataSourceDesc}
*/
@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);
}
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 {
throw new IllegalStateException("Unknown source type:" + mSourceType);
}
return desc;
}
/**
* Sets the media Id of this data source.
*
* @param mediaId the media Id of this data source
* @return the same Builder instance.
*/
@NonNull
public Builder setMediaId(@Nullable String mediaId) {
mMediaId = mediaId;
return this;
}
/**
* Sets the start position in milliseconds at which the playback will start.
* Any negative number is treated as 0.
*
* @param position the start position in milliseconds at which the playback will start
* @return the same Builder instance.
*
*/
@NonNull
public Builder setStartPosition(long position) {
if (position < 0) {
position = 0;
}
mStartPositionMs = position;
return this;
}
/**
* Sets the end position in milliseconds at which the playback will end.
* Any negative number is treated as maximum duration {@link #LONG_MAX_TIME_MS}
* of the data source
*
* @param position the end position in milliseconds at which the playback will end
* @return the same Builder instance.
*/
@NonNull
public Builder setEndPosition(long position) {
if (position < 0) {
position = LONG_MAX_TIME_MS;
}
mEndPositionMs = position;
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
* managers CookieStore.
*
* <p><strong>Note</strong> 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<String, String> headers,
@Nullable List<HttpCookie> 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<String, String>(headers);
}
if (cookies != null) {
mCookies = new ArrayList<HttpCookie>(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 DataSourceDesc}
* 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 DataSourceDesc}
* 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);
if (pfd == null) {
throw new NullPointerException("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;
}
private void setSourceType(int type) {
if (mSourceType != SOURCE_TYPE_UNKNOWN) {
throw new IllegalStateException("Source is already set. type=" + mSourceType);
}
mSourceType = type;
}
}
}

View File

@@ -1,134 +0,0 @@
/*
* Copyright 2018 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.NonNull;
import android.os.ParcelFileDescriptor;
import android.util.Log;
import java.io.IOException;
/**
* Structure of data source descriptor for sources using file descriptor.
*
* Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to create {@link FileDataSourceDesc}.
* @hide
*/
public class FileDataSourceDesc extends DataSourceDesc {
private static final String TAG = "FileDataSourceDesc";
/**
* Used when the length of file descriptor is unknown.
*
* @see #getLength()
*/
public static final long FD_LENGTH_UNKNOWN = LONG_MAX;
private ParcelFileDescriptor mPFD;
private long mOffset = 0;
private long mLength = FD_LENGTH_UNKNOWN;
private int mCount = 0;
private boolean mClosed = false;
FileDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
ParcelFileDescriptor pfd, long offset, long length) {
super(mediaId, startPositionMs, endPositionMs);
mPFD = pfd;
mOffset = offset;
mLength = length;
}
/**
* Releases the resources held by this {@code FileDataSourceDesc} object.
*/
@Override
void close() {
super.close();
decCount();
}
/**
* Decrements usage count by {@link MediaPlayer2}.
* If this is the last usage, also releases the file descriptor held by this
* {@code FileDataSourceDesc} object.
*/
void decCount() {
synchronized (this) {
--mCount;
if (mCount > 0) {
return;
}
try {
mPFD.close();
mClosed = true;
} catch (IOException e) {
Log.e(TAG, "failed to close pfd: " + e);
}
}
}
/**
* Increments usage count by {@link MediaPlayer2} if PFD has not been closed.
*/
void incCount() {
synchronized (this) {
if (!mClosed) {
++mCount;
}
}
}
/**
* Return the status of underline ParcelFileDescriptor
* @return true if underline ParcelFileDescriptor is closed, false otherwise.
*/
boolean isPFDClosed() {
synchronized (this) {
return mClosed;
}
}
/**
* Return the ParcelFileDescriptor of this data source.
* @return the ParcelFileDescriptor of this data source
*/
public @NonNull ParcelFileDescriptor getParcelFileDescriptor() {
return mPFD;
}
/**
* Return the offset associated with the ParcelFileDescriptor of this data source.
* It's meaningful only when it has been set by the {@link Builder}.
* @return the offset associated with the ParcelFileDescriptor of this data source
*/
public long getOffset() {
return mOffset;
}
/**
* Return the content length associated with the ParcelFileDescriptor of this data source.
* {@link #FD_LENGTH_UNKNOWN} means same as the length of source content.
* @return the content length associated with the ParcelFileDescriptor of this data source
*/
public long getLength() {
return mLength;
}
}

View File

@@ -1,385 +0,0 @@
/*
* 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 static android.media.MediaPlayer2.MEDIA_ERROR_UNSUPPORTED;
import android.os.StrictMode;
import android.util.Log;
import java.io.BufferedInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.CookieHandler;
import java.net.HttpURLConnection;
import java.net.InetAddress;
import java.net.MalformedURLException;
import java.net.NoRouteToHostException;
import java.net.ProtocolException;
import java.net.Proxy;
import java.net.URL;
import java.net.UnknownHostException;
import java.net.UnknownServiceException;
import java.util.HashMap;
import java.util.Map;
/** @hide */
public class Media2HTTPConnection {
private static final String TAG = "Media2HTTPConnection";
private static final boolean VERBOSE = false;
// connection timeout - 30 sec
private static final int CONNECT_TIMEOUT_MS = 30 * 1000;
private long mCurrentOffset = -1;
private URL mURL = null;
private Map<String, String> mHeaders = null;
private HttpURLConnection mConnection = null;
private long mTotalSize = -1;
private InputStream mInputStream = null;
private boolean mAllowCrossDomainRedirect = true;
private boolean mAllowCrossProtocolRedirect = true;
// from com.squareup.okhttp.internal.http
private final static int HTTP_TEMP_REDIRECT = 307;
private final static int MAX_REDIRECTS = 20;
public Media2HTTPConnection() {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler == null) {
Log.w(TAG, "Media2HTTPConnection: Unexpected. No CookieHandler found.");
}
}
public boolean connect(String uri, String headers) {
if (VERBOSE) {
Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers);
}
try {
disconnect();
mAllowCrossDomainRedirect = true;
mURL = new URL(uri);
mHeaders = convertHeaderStringToMap(headers);
} catch (MalformedURLException e) {
return false;
}
return true;
}
private boolean parseBoolean(String val) {
try {
return Long.parseLong(val) != 0;
} catch (NumberFormatException e) {
return "true".equalsIgnoreCase(val) ||
"yes".equalsIgnoreCase(val);
}
}
/* returns true iff header is internal */
private boolean filterOutInternalHeaders(String key, String val) {
if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
mAllowCrossDomainRedirect = parseBoolean(val);
// cross-protocol redirects are also controlled by this flag
mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
} else {
return false;
}
return true;
}
private Map<String, String> convertHeaderStringToMap(String headers) {
HashMap<String, String> map = new HashMap<String, String>();
String[] pairs = headers.split("\r\n");
for (String pair : pairs) {
int colonPos = pair.indexOf(":");
if (colonPos >= 0) {
String key = pair.substring(0, colonPos);
String val = pair.substring(colonPos + 1);
if (!filterOutInternalHeaders(key, val)) {
map.put(key, val);
}
}
}
return map;
}
public void disconnect() {
teardownConnection();
mHeaders = null;
mURL = null;
}
private void teardownConnection() {
if (mConnection != null) {
if (mInputStream != null) {
try {
mInputStream.close();
} catch (IOException e) {
}
mInputStream = null;
}
mConnection.disconnect();
mConnection = null;
mCurrentOffset = -1;
}
}
private static final boolean isLocalHost(URL url) {
if (url == null) {
return false;
}
String host = url.getHost();
if (host == null) {
return false;
}
try {
if (host.equalsIgnoreCase("localhost")) {
return true;
}
if (InetAddress.getByName(host).isLoopbackAddress()) {
return true;
}
} catch (IllegalArgumentException | UnknownHostException e) {
}
return false;
}
private void seekTo(long offset) throws IOException {
teardownConnection();
try {
int response;
int redirectCount = 0;
URL url = mURL;
// do not use any proxy for localhost (127.0.0.1)
boolean noProxy = isLocalHost(url);
while (true) {
if (noProxy) {
mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
} else {
mConnection = (HttpURLConnection)url.openConnection();
}
mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
// handle redirects ourselves if we do not allow cross-domain redirect
mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
if (mHeaders != null) {
for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
mConnection.setRequestProperty(
entry.getKey(), entry.getValue());
}
}
if (offset > 0) {
mConnection.setRequestProperty(
"Range", "bytes=" + offset + "-");
}
response = mConnection.getResponseCode();
if (response != HttpURLConnection.HTTP_MULT_CHOICE &&
response != HttpURLConnection.HTTP_MOVED_PERM &&
response != HttpURLConnection.HTTP_MOVED_TEMP &&
response != HttpURLConnection.HTTP_SEE_OTHER &&
response != HTTP_TEMP_REDIRECT) {
// not a redirect, or redirect handled by HttpURLConnection
break;
}
if (++redirectCount > MAX_REDIRECTS) {
throw new NoRouteToHostException("Too many redirects: " + redirectCount);
}
String method = mConnection.getRequestMethod();
if (response == HTTP_TEMP_REDIRECT &&
!method.equals("GET") && !method.equals("HEAD")) {
// "If the 307 status code is received in response to a
// request other than GET or HEAD, the user agent MUST NOT
// automatically redirect the request"
throw new NoRouteToHostException("Invalid redirect");
}
String location = mConnection.getHeaderField("Location");
if (location == null) {
throw new NoRouteToHostException("Invalid redirect");
}
url = new URL(mURL /* TRICKY: don't use url! */, location);
if (!url.getProtocol().equals("https") &&
!url.getProtocol().equals("http")) {
throw new NoRouteToHostException("Unsupported protocol redirect");
}
boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol());
if (!mAllowCrossProtocolRedirect && !sameProtocol) {
throw new NoRouteToHostException("Cross-protocol redirects are disallowed");
}
boolean sameHost = mURL.getHost().equals(url.getHost());
if (!mAllowCrossDomainRedirect && !sameHost) {
throw new NoRouteToHostException("Cross-domain redirects are disallowed");
}
if (response != HTTP_TEMP_REDIRECT) {
// update effective URL, unless it is a Temporary Redirect
mURL = url;
}
}
if (mAllowCrossDomainRedirect) {
// remember the current, potentially redirected URL if redirects
// were handled by HttpURLConnection
mURL = mConnection.getURL();
}
if (response == HttpURLConnection.HTTP_PARTIAL) {
// Partial content, we cannot just use getContentLength
// because what we want is not just the length of the range
// returned but the size of the full content if available.
String contentRange =
mConnection.getHeaderField("Content-Range");
mTotalSize = -1;
if (contentRange != null) {
// format is "bytes xxx-yyy/zzz
// where "zzz" is the total number of bytes of the
// content or '*' if unknown.
int lastSlashPos = contentRange.lastIndexOf('/');
if (lastSlashPos >= 0) {
String total =
contentRange.substring(lastSlashPos + 1);
try {
mTotalSize = Long.parseLong(total);
} catch (NumberFormatException e) {
}
}
}
} else if (response != HttpURLConnection.HTTP_OK) {
throw new IOException();
} else {
mTotalSize = mConnection.getContentLength();
}
if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
// Some servers simply ignore "Range" requests and serve
// data from the start of the content.
throw new ProtocolException();
}
mInputStream =
new BufferedInputStream(mConnection.getInputStream());
mCurrentOffset = offset;
} catch (IOException e) {
mTotalSize = -1;
teardownConnection();
mCurrentOffset = -1;
throw e;
}
}
public int readAt(long offset, byte[] data, int size) {
StrictMode.ThreadPolicy policy =
new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
if (offset != mCurrentOffset) {
seekTo(offset);
}
int n = mInputStream.read(data, 0, size);
if (n == -1) {
// InputStream signals EOS using a -1 result, our semantics
// are to return a 0-length read.
n = 0;
}
mCurrentOffset += n;
if (VERBOSE) {
Log.d(TAG, "readAt " + offset + " / " + size + " => " + n);
}
return n;
} catch (ProtocolException e) {
Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
return MEDIA_ERROR_UNSUPPORTED;
} catch (NoRouteToHostException e) {
Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
return MEDIA_ERROR_UNSUPPORTED;
} catch (UnknownServiceException e) {
Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
return MEDIA_ERROR_UNSUPPORTED;
} catch (IOException e) {
if (VERBOSE) {
Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
}
return -1;
} catch (Exception e) {
if (VERBOSE) {
Log.d(TAG, "unknown exception " + e);
Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
}
return -1;
}
}
public long getSize() {
if (mConnection == null) {
try {
seekTo(0);
} catch (IOException e) {
return -1;
}
}
return mTotalSize;
}
public String getMIMEType() {
if (mConnection == null) {
try {
seekTo(0);
} catch (IOException e) {
return "application/octet-stream";
}
}
return mConnection.getContentType();
}
public String getUri() {
return mURL.toString();
}
}

View File

@@ -1,58 +0,0 @@
/*
* Copyright (C) 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.util.Log;
import java.net.HttpCookie;
import java.util.List;
/** @hide */
public class Media2HTTPService {
private static final String TAG = "Media2HTTPService";
private List<HttpCookie> mCookies;
private Boolean mCookieStoreInitialized = new Boolean(false);
public Media2HTTPService(List<HttpCookie> cookies) {
mCookies = cookies;
Log.v(TAG, "Media2HTTPService(" + this + "): Cookies: " + cookies);
}
public Media2HTTPConnection makeHTTPConnection() {
synchronized (mCookieStoreInitialized) {
Media2Utils.storeCookies(mCookies);
}
return new Media2HTTPConnection();
}
/* package private */ static Media2HTTPService createHTTPService(String path) {
return createHTTPService(path, null);
}
// when cookies are provided
static Media2HTTPService createHTTPService(String path, List<HttpCookie> cookies) {
if (path.startsWith("http://") || path.startsWith("https://")) {
return (new Media2HTTPService(cookies));
} else if (path.startsWith("widevine://")) {
Log.d(TAG, "Widevine classic is no longer supported");
}
return null;
}
}

View File

@@ -1,78 +0,0 @@
/*
* Copyright (C) 2018 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.util.Log;
import java.net.CookieHandler;
import java.net.CookieManager;
import java.net.CookieStore;
import java.net.HttpCookie;
import java.util.List;
/** @hide */
public class Media2Utils {
private static final String TAG = "Media2Utils";
private Media2Utils() {
}
/**
* Ensures that an expression checking an argument is true.
*
* @param expression the expression to check
* @param errorMessage the exception message to use if the check fails; will
* be converted to a string using {@link String#valueOf(Object)}
* @throws IllegalArgumentException if {@code expression} is false
*/
public static void checkArgument(boolean expression, String errorMessage) {
if (!expression) {
throw new IllegalArgumentException(errorMessage);
}
}
public static synchronized void storeCookies(List<HttpCookie> cookies) {
CookieHandler cookieHandler = CookieHandler.getDefault();
if (cookieHandler == null) {
cookieHandler = new CookieManager();
CookieHandler.setDefault(cookieHandler);
Log.v(TAG, "storeCookies: CookieManager created: " + cookieHandler);
} else {
Log.v(TAG, "storeCookies: CookieHandler (" + cookieHandler + ") exists.");
}
if (cookies != null) {
if (cookieHandler instanceof CookieManager) {
CookieManager cookieManager = (CookieManager)cookieHandler;
CookieStore store = cookieManager.getCookieStore();
for (HttpCookie cookie : cookies) {
try {
store.add(null, cookie);
} catch (Exception e) {
Log.v(TAG, "storeCookies: CookieStore.add" + cookie, e);
}
}
} else {
Log.w(TAG, "storeCookies: The installed CookieHandler is not a CookieManager."
+ " Cant add the provided cookies to the cookie store.");
}
} // cookies
Log.v(TAG, "storeCookies: cookieHandler: " + cookieHandler + " Cookies: " + cookies);
}
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,43 +0,0 @@
/*
* Copyright (C) 2018 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;
/**
* Helper class used by native code to reduce JNI calls from native side.
* @hide
*/
public class MediaPlayer2Utils {
/**
* Returns whether audio offloading is supported for the given audio format.
*
* @param encoding the type of encoding defined in {@link AudioFormat}
* @param sampleRate the sampling rate of the stream
* @param channelMask the channel mask defined in {@link AudioFormat}
*/
// @CalledByNative
public static boolean isOffloadedAudioPlaybackSupported(
int encoding, int sampleRate, int channelMask) {
final AudioFormat format = new AudioFormat.Builder()
.setEncoding(encoding)
.setSampleRate(sampleRate)
.setChannelMask(channelMask)
.build();
//TODO MP2 needs to pass AudioAttributes for this query, instead of using default attr
return AudioManager.isOffloadedPlaybackSupported(format,
(new AudioAttributes.Builder()).build());
}
}

View File

@@ -1,80 +0,0 @@
/*
* Copyright 2018 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.NonNull;
import android.annotation.Nullable;
import android.net.Uri;
import java.net.HttpCookie;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Structure of data source descriptor for sources using URI.
*
* Used by {@link MediaPlayer2#setDataSource}, {@link MediaPlayer2#setNextDataSource} and
* {@link MediaPlayer2#setNextDataSources} to set data source for playback.
*
* <p>Users should use {@link Builder} to change {@link UriDataSourceDesc}.
* @hide
*/
public class UriDataSourceDesc extends DataSourceDesc {
private Uri mUri;
private Map<String, String> mHeader;
private List<HttpCookie> mCookies;
UriDataSourceDesc(String mediaId, long startPositionMs, long endPositionMs,
Uri uri, Map<String, String> header, List<HttpCookie> cookies) {
super(mediaId, startPositionMs, endPositionMs);
mUri = uri;
mHeader = header;
mCookies = cookies;
}
/**
* Return the Uri of this data source.
* @return the Uri of this data source
*/
public @NonNull Uri getUri() {
return mUri;
}
/**
* Return the Uri headers of this data source.
* @return the Uri headers of this data source
*/
public @Nullable Map<String, String> getHeaders() {
if (mHeader == null) {
return null;
}
return new HashMap<String, String>(mHeader);
}
/**
* Return the Uri cookies of this data source.
* @return the Uri cookies of this data source
*/
public @Nullable List<HttpCookie> getCookies() {
if (mCookies == null) {
return null;
}
return new ArrayList<HttpCookie>(mCookies);
}
}

View File

@@ -113,88 +113,6 @@ cc_library_shared {
],
}
cc_library_shared {
name: "libmedia2_jni",
srcs: [
"android_media_DataSourceCallback.cpp",
"android_media_MediaMetricsJNI.cpp",
"android_media_MediaPlayer2.cpp",
"android_media_SyncParams.cpp",
],
shared_libs: [
// NDK or LLNDK or NDK-compliant
"libandroid",
"libbinder_ndk",
"libcgrouprc",
"libmediandk",
"libmediametrics",
"libnativehelper_compat_libc++",
"liblog",
"libvndksupport",
],
header_libs: [
"libhardware_headers",
"libnativewindow_headers",
],
static_libs: [
// MediaCas
"android.hidl.allocator@1.0",
"android.hidl.memory@1.0",
"libhidlbase",
"libhidlmemory",
"libbinderthreadstate",
// MediaPlayer2 implementation
"libbase",
"libcrypto",
"libcutils",
"libjsoncpp",
"libmedia_player2_util",
"libmediaplayer2",
"libmediaplayer2-protos",
"libmediandk_utils",
"libmediautils",
"libprocessgroup",
"libprotobuf-cpp-lite",
"libstagefright_esds",
"libstagefright_foundation_without_imemory",
"libstagefright_httplive",
"libstagefright_id3",
"libstagefright_mpeg2support",
"libstagefright_nuplayer2",
"libstagefright_player2",
"libstagefright_rtsp_player2",
"libstagefright_timedtext2",
"libutils",
"libmedia2_jni_core",
],
group_static_libs: true,
include_dirs: [
"frameworks/base/core/jni",
"frameworks/native/include/media/openmax",
"system/media/camera/include",
],
export_include_dirs: ["."],
cflags: [
"-Wall",
"-Werror",
"-Wno-error=deprecated-declarations",
"-Wunused",
"-Wunreachable-code",
"-fvisibility=hidden",
],
ldflags: ["-Wl,--exclude-libs=ALL,-error-limit=0"],
}
subdirs = [
"audioeffect",
"soundpool",

View File

@@ -1,159 +0,0 @@
/*
* 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.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "JDataSourceCallback-JNI"
#include <utils/Log.h>
#include "android_media_DataSourceCallback.h"
#include "log/log.h"
#include "jni.h"
#include <nativehelper/JNIHelp.h>
#include <drm/drm_framework_common.h>
#include <mediaplayer2/JavaVMHelper.h>
#include <media/stagefright/foundation/ADebug.h>
#include <nativehelper/ScopedLocalRef.h>
namespace android {
static const size_t kBufferSize = 64 * 1024;
JDataSourceCallback::JDataSourceCallback(JNIEnv* env, jobject source)
: mJavaObjStatus(OK),
mSizeIsCached(false),
mCachedSize(0) {
mDataSourceCallbackObj = env->NewGlobalRef(source);
CHECK(mDataSourceCallbackObj != NULL);
ScopedLocalRef<jclass> media2DataSourceClass(env, env->GetObjectClass(mDataSourceCallbackObj));
CHECK(media2DataSourceClass.get() != NULL);
mReadAtMethod = env->GetMethodID(media2DataSourceClass.get(), "readAt", "(J[BII)I");
CHECK(mReadAtMethod != NULL);
mGetSizeMethod = env->GetMethodID(media2DataSourceClass.get(), "getSize", "()J");
CHECK(mGetSizeMethod != NULL);
mCloseMethod = env->GetMethodID(media2DataSourceClass.get(), "close", "()V");
CHECK(mCloseMethod != NULL);
ScopedLocalRef<jbyteArray> tmp(env, env->NewByteArray(kBufferSize));
mByteArrayObj = (jbyteArray)env->NewGlobalRef(tmp.get());
CHECK(mByteArrayObj != NULL);
}
JDataSourceCallback::~JDataSourceCallback() {
JNIEnv* env = JavaVMHelper::getJNIEnv();
env->DeleteGlobalRef(mDataSourceCallbackObj);
env->DeleteGlobalRef(mByteArrayObj);
}
status_t JDataSourceCallback::initCheck() const {
return OK;
}
ssize_t JDataSourceCallback::readAt(off64_t offset, void *data, size_t size) {
Mutex::Autolock lock(mLock);
if (mJavaObjStatus != OK) {
return -1;
}
if (size > kBufferSize) {
size = kBufferSize;
}
JNIEnv* env = JavaVMHelper::getJNIEnv();
jint numread = env->CallIntMethod(mDataSourceCallbackObj, mReadAtMethod,
(jlong)offset, mByteArrayObj, (jint)0, (jint)size);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in readAt()");
jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
env->ExceptionClear();
mJavaObjStatus = UNKNOWN_ERROR;
return -1;
}
if (numread < 0) {
if (numread != -1) {
ALOGW("An error occurred in readAt()");
mJavaObjStatus = UNKNOWN_ERROR;
return -1;
} else {
// numread == -1 indicates EOF
return 0;
}
}
if ((size_t)numread > size) {
ALOGE("readAt read too many bytes.");
mJavaObjStatus = UNKNOWN_ERROR;
return -1;
}
ALOGV("readAt %lld / %zu => %d.", (long long)offset, size, numread);
env->GetByteArrayRegion(mByteArrayObj, 0, numread, (jbyte*)data);
return numread;
}
status_t JDataSourceCallback::getSize(off64_t* size) {
Mutex::Autolock lock(mLock);
if (mJavaObjStatus != OK) {
return UNKNOWN_ERROR;
}
if (mSizeIsCached) {
*size = mCachedSize;
return OK;
}
JNIEnv* env = JavaVMHelper::getJNIEnv();
*size = env->CallLongMethod(mDataSourceCallbackObj, mGetSizeMethod);
if (env->ExceptionCheck()) {
ALOGW("An exception occurred in getSize()");
jniLogException(env, ANDROID_LOG_WARN, LOG_TAG);
env->ExceptionClear();
// After returning an error, size shouldn't be used by callers.
*size = UNKNOWN_ERROR;
mJavaObjStatus = UNKNOWN_ERROR;
return UNKNOWN_ERROR;
}
// The minimum size should be -1, which indicates unknown size.
if (*size < 0) {
*size = -1;
}
mCachedSize = *size;
mSizeIsCached = true;
return OK;
}
void JDataSourceCallback::close() {
Mutex::Autolock lock(mLock);
JNIEnv* env = JavaVMHelper::getJNIEnv();
env->CallVoidMethod(mDataSourceCallbackObj, mCloseMethod);
// The closed state is effectively the same as an error state.
mJavaObjStatus = UNKNOWN_ERROR;
}
String8 JDataSourceCallback::toString() {
return String8::format("JDataSourceCallback(pid %d, uid %d)", getpid(), getuid());
}
String8 JDataSourceCallback::getMIMEType() const {
return String8("application/octet-stream");
}
} // namespace android

View File

@@ -23,9 +23,8 @@
#include <media/MediaAnalyticsItem.h>
// This source file is compiled and linked into both:
// This source file is compiled and linked into:
// core/jni/ (libandroid_runtime.so)
// media/jni (libmedia2_jni.so)
namespace android {

File diff suppressed because it is too large Load Diff

View File

@@ -1,20 +0,0 @@
java_library_static {
name: "mediaplayer2-protos",
host_supported: true,
proto: {
type: "lite",
},
srcs: ["mediaplayer2.proto"],
jarjar_rules: "jarjar-rules.txt",
sdk_version: "28",
}
cc_library_static {
name: "libmediaplayer2-protos",
host_supported: true,
proto: {
export_proto_headers: true,
type: "lite",
},
srcs: ["mediaplayer2.proto"],
}

View File

@@ -1,2 +0,0 @@
rule com.google.protobuf.** android.media.protobuf.@1

View File

@@ -1,53 +0,0 @@
/*
* Copyright (C) 2018 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.
*/
syntax = "proto2";
option optimize_for = LITE_RUNTIME;
// C++ namespace: android::media:MediaPlayer2Proto:
package android.media.MediaPlayer2Proto;
option java_package = "android.media";
option java_outer_classname = "MediaPlayer2Proto";
message Value {
// The kind of value.
oneof kind {
// Represents a boolean value.
bool bool_value = 1;
// Represents an int32 value.
int32 int32_value = 2;
// Represents an uint32 value.
uint32 uint32_value = 3;
// Represents an int64 value.
int64 int64_value = 4;
// Represents an uint64 value.
uint64 uint64_value = 5;
// Represents a float value.
double float_value = 6;
// Represents a double value.
double double_value = 7;
// Represents a string value.
string string_value = 8;
// Represents a bytes value.
bytes bytes_value = 9;
}
}
message PlayerMessage {
repeated Value values = 1;
}