Loading media/java/android/media/MediaHTTPConnection.java +119 −125 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.os.IBinder; import android.os.StrictMode; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; Loading @@ -37,7 +38,6 @@ import java.net.URL; import java.net.UnknownServiceException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; /** @hide */ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { Loading @@ -47,23 +47,42 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // connection timeout - 30 sec private static final int CONNECT_TIMEOUT_MS = 30 * 1000; @GuardedBy("this") @UnsupportedAppUsage private long mCurrentOffset = -1; @GuardedBy("this") @UnsupportedAppUsage private URL mURL = null; @GuardedBy("this") @UnsupportedAppUsage private Map<String, String> mHeaders = null; // volatile so that disconnect() can be called without acquiring a lock. // All other access is @GuardedBy("this"). @UnsupportedAppUsage private volatile HttpURLConnection mConnection = null; @GuardedBy("this") @UnsupportedAppUsage private long mTotalSize = -1; @GuardedBy("this") private InputStream mInputStream = null; @GuardedBy("this") @UnsupportedAppUsage private boolean mAllowCrossDomainRedirect = true; @GuardedBy("this") @UnsupportedAppUsage 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; class ConnectionState { public HttpURLConnection mConnection = null; public InputStream mInputStream = null; public long mCurrentOffset = -1; public Map<String, String> mHeaders = null; public URL mURL = null; public long mTotalSize = -1; public boolean mAllowCrossDomainRedirect = true; public boolean mAllowCrossProtocolRedirect = true; } private final AtomicReference<ConnectionState> mConnectionStateHolder = new AtomicReference<ConnectionState>(); @UnsupportedAppUsage public MediaHTTPConnection() { CookieHandler cookieHandler = CookieHandler.getDefault(); Loading @@ -76,34 +95,24 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public IBinder connect(String uri, String headers) { public synchronized IBinder connect(String uri, String headers) { if (VERBOSE) { Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers); } ConnectionState connectionState = mConnectionStateHolder.get(); synchronized (this) { if (connectionState == null) { connectionState = new ConnectionState(); mConnectionStateHolder.set(connectionState); } } try { disconnect(); connectionState.mAllowCrossDomainRedirect = true; connectionState.mURL = new URL(uri); connectionState.mHeaders = convertHeaderStringToMap(headers, connectionState); mAllowCrossDomainRedirect = true; mURL = new URL(uri); mHeaders = convertHeaderStringToMap(headers); } catch (MalformedURLException e) { return null; } finally { mConnectionStateHolder.set(connectionState); } return native_getIMemory(); } private boolean parseBoolean(String val) { private static boolean parseBoolean(String val) { try { return Long.parseLong(val) != 0; } catch (NumberFormatException e) { Loading @@ -113,21 +122,18 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } /* returns true iff header is internal */ private boolean filterOutInternalHeaders( String key, String val, ConnectionState connectionState) { private synchronized boolean filterOutInternalHeaders(String key, String val) { if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) { connectionState.mAllowCrossDomainRedirect = parseBoolean(val); mAllowCrossDomainRedirect = parseBoolean(val); // cross-protocol redirects are also controlled by this flag connectionState.mAllowCrossProtocolRedirect = connectionState.mAllowCrossDomainRedirect; mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect; } else { return false; } return true; } private Map<String, String> convertHeaderStringToMap(String headers, ConnectionState connectionState) { private synchronized Map<String, String> convertHeaderStringToMap(String headers) { HashMap<String, String> map = new HashMap<String, String>(); String[] pairs = headers.split("\r\n"); Loading @@ -137,7 +143,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { String key = pair.substring(0, colonPos); String val = pair.substring(colonPos + 1); if (!filterOutInternalHeaders(key, val, connectionState)) { if (!filterOutInternalHeaders(key, val)) { map.put(key, val); } } Loading @@ -149,28 +155,36 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { ConnectionState connectionState = mConnectionStateHolder.getAndSet(null); if (connectionState != null) { teardownConnection(connectionState); connectionState.mHeaders = null; connectionState.mURL = null; HttpURLConnection connectionToDisconnect = mConnection; // Call disconnect() before blocking for the lock in order to ensure that any // other thread that is blocked in readAt() will return quickly. if (connectionToDisconnect != null) { connectionToDisconnect.disconnect(); } synchronized (this) { // It's unlikely but possible that while we were waiting to acquire the lock, another // thread concurrently started a new connection; if so, we're disconnecting that one // here, too. teardownConnection(); mHeaders = null; mURL = null; } } private void teardownConnection(ConnectionState connectionState) { if (connectionState.mConnection != null) { if (connectionState.mInputStream != null) { private synchronized void teardownConnection() { if (mConnection != null) { if (mInputStream != null) { try { connectionState.mInputStream.close(); mInputStream.close(); } catch (IOException e) { } connectionState.mInputStream = null; mInputStream = null; } connectionState.mConnection.disconnect(); connectionState.mConnection = null; mConnection.disconnect(); mConnection = null; connectionState.mCurrentOffset = -1; mCurrentOffset = -1; } } Loading @@ -197,44 +211,42 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return false; } private void seekTo(long offset, ConnectionState connectionState) throws IOException { teardownConnection(connectionState); private synchronized void seekTo(long offset) throws IOException { teardownConnection(); try { int response; int redirectCount = 0; URL url = connectionState.mURL; URL url = mURL; // do not use any proxy for localhost (127.0.0.1) boolean noProxy = isLocalHost(url); while (true) { if (noProxy) { connectionState.mConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY); } else { connectionState.mConnection = (HttpURLConnection) url.openConnection(); mConnection = (HttpURLConnection)url.openConnection(); } connectionState.mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); // handle redirects ourselves if we do not allow cross-domain redirect connectionState.mConnection.setInstanceFollowRedirects( connectionState.mAllowCrossDomainRedirect); mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect); if (connectionState.mHeaders != null) { for (Map.Entry<String, String> entry : connectionState.mHeaders.entrySet()) { connectionState.mConnection.setRequestProperty( if (mHeaders != null) { for (Map.Entry<String, String> entry : mHeaders.entrySet()) { mConnection.setRequestProperty( entry.getKey(), entry.getValue()); } } if (offset > 0) { connectionState.mConnection.setRequestProperty( mConnection.setRequestProperty( "Range", "bytes=" + offset + "-"); } response = connectionState.mConnection.getResponseCode(); response = mConnection.getResponseCode(); if (response != HttpURLConnection.HTTP_MULT_CHOICE && response != HttpURLConnection.HTTP_MOVED_PERM && response != HttpURLConnection.HTTP_MOVED_TEMP && Loading @@ -248,7 +260,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new NoRouteToHostException("Too many redirects: " + redirectCount); } String method = connectionState.mConnection.getRequestMethod(); 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 Loading @@ -256,35 +268,34 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // automatically redirect the request" throw new NoRouteToHostException("Invalid redirect"); } String location = connectionState.mConnection.getHeaderField("Location"); String location = mConnection.getHeaderField("Location"); if (location == null) { throw new NoRouteToHostException("Invalid redirect"); } url = new URL(connectionState.mURL /* TRICKY: don't use url! */, location); 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 = connectionState.mURL.getProtocol().equals(url.getProtocol()); if (!connectionState.mAllowCrossProtocolRedirect && !sameProtocol) { boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol()); if (!mAllowCrossProtocolRedirect && !sameProtocol) { throw new NoRouteToHostException("Cross-protocol redirects are disallowed"); } boolean sameHost = connectionState.mURL.getHost().equals(url.getHost()); if (!connectionState.mAllowCrossDomainRedirect && !sameHost) { 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 connectionState.mURL = url; mURL = url; } } if (connectionState.mAllowCrossDomainRedirect) { if (mAllowCrossDomainRedirect) { // remember the current, potentially redirected URL if redirects // were handled by HttpURLConnection connectionState.mURL = connectionState.mConnection.getURL(); mURL = mConnection.getURL(); } if (response == HttpURLConnection.HTTP_PARTIAL) { Loading @@ -292,9 +303,10 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // because what we want is not just the length of the range // returned but the size of the full content if available. String contentRange = connectionState.mConnection.getHeaderField("Content-Range"); String contentRange = mConnection.getHeaderField("Content-Range"); connectionState.mTotalSize = -1; mTotalSize = -1; if (contentRange != null) { // format is "bytes xxx-yyy/zzz // where "zzz" is the total number of bytes of the Loading @@ -306,7 +318,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { contentRange.substring(lastSlashPos + 1); try { connectionState.mTotalSize = Long.parseLong(total); mTotalSize = Long.parseLong(total); } catch (NumberFormatException e) { } } Loading @@ -314,7 +326,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } else if (response != HttpURLConnection.HTTP_OK) { throw new IOException(); } else { connectionState.mTotalSize = connectionState.mConnection.getContentLength(); mTotalSize = mConnection.getContentLength(); } if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) { Loading @@ -323,14 +335,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new ProtocolException(); } connectionState.mInputStream = new BufferedInputStream(connectionState.mConnection.getInputStream()); mInputStream = new BufferedInputStream(mConnection.getInputStream()); connectionState.mCurrentOffset = offset; mCurrentOffset = offset; } catch (IOException e) { connectionState.mTotalSize = -1; teardownConnection(connectionState); connectionState.mCurrentOffset = -1; mTotalSize = -1; teardownConnection(); mCurrentOffset = -1; throw e; } Loading @@ -338,28 +350,22 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public int readAt(long offset, int size) { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { return native_readAt(offset, size, connectionState); } return -1; public synchronized int readAt(long offset, int size) { return native_readAt(offset, size); } private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) { private synchronized int readAt(long offset, byte[] data, int size) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); try { synchronized(this) { if (offset != connectionState.mCurrentOffset) { seekTo(offset, connectionState); } if (offset != mCurrentOffset) { seekTo(offset); } int n = connectionState.mInputStream.read(data, 0, size); int n = mInputStream.read(data, 0, size); if (n == -1) { // InputStream signals EOS using a -1 result, our semantics Loading @@ -367,7 +373,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { n = 0; } connectionState.mCurrentOffset += n; mCurrentOffset += n; if (VERBOSE) { Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); Loading Loading @@ -399,47 +405,35 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override public synchronized long getSize() { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { if (connectionState.mConnection == null) { if (mConnection == null) { try { seekTo(0, connectionState); seekTo(0); } catch (IOException e) { return -1; } } return connectionState.mTotalSize; } return -1; return mTotalSize; } @Override @UnsupportedAppUsage public synchronized String getMIMEType() { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { if (connectionState.mConnection == null) { if (mConnection == null) { try { seekTo(0, connectionState); seekTo(0); } catch (IOException e) { return "application/octet-stream"; } } return connectionState.mConnection.getContentType(); } return null; return mConnection.getContentType(); } @Override @UnsupportedAppUsage public String getUri() { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { return connectionState.mURL.toString(); } return null; public synchronized String getUri() { return mURL.toString(); } @Override Loading @@ -452,7 +446,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private native final void native_finalize(); private native final IBinder native_getIMemory(); private native int native_readAt(long offset, int size, ConnectionState connectionState); private native final int native_readAt(long offset, int size); static { System.loadLibrary("media_jni"); Loading media/jni/android_media_MediaHTTPConnection.cpp +4 −5 Original line number Diff line number Diff line Loading @@ -109,8 +109,7 @@ static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) { gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); CHECK(gFields.context != NULL); gFields.readAtMethodID = env->GetMethodID( clazz.get(), "readAt", "(J[BILandroid/media/MediaHTTPConnection$ConnectionState;)I"); gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I"); } static void android_media_MediaHTTPConnection_native_setup( Loading @@ -133,7 +132,7 @@ static jobject android_media_MediaHTTPConnection_native_getIMemory( } static jint android_media_MediaHTTPConnection_native_readAt( JNIEnv *env, jobject thiz, jlong offset, jint size, jobject connectionState) { JNIEnv *env, jobject thiz, jlong offset, jint size) { sp<JMediaHTTPConnection> conn = getObject(env, thiz); if (size > JMediaHTTPConnection::kBufferSize) { size = JMediaHTTPConnection::kBufferSize; Loading @@ -142,7 +141,7 @@ static jint android_media_MediaHTTPConnection_native_readAt( jbyteArray byteArrayObj = conn->getByteArrayObj(); jint n = env->CallIntMethod( thiz, gFields.readAtMethodID, offset, byteArrayObj, size, connectionState); thiz, gFields.readAtMethodID, offset, byteArrayObj, size); if (n > 0) { env->GetByteArrayRegion( Loading @@ -159,7 +158,7 @@ static const JNINativeMethod gMethods[] = { { "native_getIMemory", "()Landroid/os/IBinder;", (void *)android_media_MediaHTTPConnection_native_getIMemory }, { "native_readAt", "(JILandroid/media/MediaHTTPConnection$ConnectionState;)I", { "native_readAt", "(JI)I", (void *)android_media_MediaHTTPConnection_native_readAt }, { "native_init", "()V", Loading Loading
media/java/android/media/MediaHTTPConnection.java +119 −125 Original line number Diff line number Diff line Loading @@ -24,6 +24,7 @@ import android.os.IBinder; import android.os.StrictMode; import android.util.Log; import com.android.internal.annotations.GuardedBy; import java.io.BufferedInputStream; import java.io.IOException; import java.io.InputStream; Loading @@ -37,7 +38,6 @@ import java.net.URL; import java.net.UnknownServiceException; import java.util.HashMap; import java.util.Map; import java.util.concurrent.atomic.AtomicReference; /** @hide */ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { Loading @@ -47,23 +47,42 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // connection timeout - 30 sec private static final int CONNECT_TIMEOUT_MS = 30 * 1000; @GuardedBy("this") @UnsupportedAppUsage private long mCurrentOffset = -1; @GuardedBy("this") @UnsupportedAppUsage private URL mURL = null; @GuardedBy("this") @UnsupportedAppUsage private Map<String, String> mHeaders = null; // volatile so that disconnect() can be called without acquiring a lock. // All other access is @GuardedBy("this"). @UnsupportedAppUsage private volatile HttpURLConnection mConnection = null; @GuardedBy("this") @UnsupportedAppUsage private long mTotalSize = -1; @GuardedBy("this") private InputStream mInputStream = null; @GuardedBy("this") @UnsupportedAppUsage private boolean mAllowCrossDomainRedirect = true; @GuardedBy("this") @UnsupportedAppUsage 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; class ConnectionState { public HttpURLConnection mConnection = null; public InputStream mInputStream = null; public long mCurrentOffset = -1; public Map<String, String> mHeaders = null; public URL mURL = null; public long mTotalSize = -1; public boolean mAllowCrossDomainRedirect = true; public boolean mAllowCrossProtocolRedirect = true; } private final AtomicReference<ConnectionState> mConnectionStateHolder = new AtomicReference<ConnectionState>(); @UnsupportedAppUsage public MediaHTTPConnection() { CookieHandler cookieHandler = CookieHandler.getDefault(); Loading @@ -76,34 +95,24 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public IBinder connect(String uri, String headers) { public synchronized IBinder connect(String uri, String headers) { if (VERBOSE) { Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers); } ConnectionState connectionState = mConnectionStateHolder.get(); synchronized (this) { if (connectionState == null) { connectionState = new ConnectionState(); mConnectionStateHolder.set(connectionState); } } try { disconnect(); connectionState.mAllowCrossDomainRedirect = true; connectionState.mURL = new URL(uri); connectionState.mHeaders = convertHeaderStringToMap(headers, connectionState); mAllowCrossDomainRedirect = true; mURL = new URL(uri); mHeaders = convertHeaderStringToMap(headers); } catch (MalformedURLException e) { return null; } finally { mConnectionStateHolder.set(connectionState); } return native_getIMemory(); } private boolean parseBoolean(String val) { private static boolean parseBoolean(String val) { try { return Long.parseLong(val) != 0; } catch (NumberFormatException e) { Loading @@ -113,21 +122,18 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } /* returns true iff header is internal */ private boolean filterOutInternalHeaders( String key, String val, ConnectionState connectionState) { private synchronized boolean filterOutInternalHeaders(String key, String val) { if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) { connectionState.mAllowCrossDomainRedirect = parseBoolean(val); mAllowCrossDomainRedirect = parseBoolean(val); // cross-protocol redirects are also controlled by this flag connectionState.mAllowCrossProtocolRedirect = connectionState.mAllowCrossDomainRedirect; mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect; } else { return false; } return true; } private Map<String, String> convertHeaderStringToMap(String headers, ConnectionState connectionState) { private synchronized Map<String, String> convertHeaderStringToMap(String headers) { HashMap<String, String> map = new HashMap<String, String>(); String[] pairs = headers.split("\r\n"); Loading @@ -137,7 +143,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { String key = pair.substring(0, colonPos); String val = pair.substring(colonPos + 1); if (!filterOutInternalHeaders(key, val, connectionState)) { if (!filterOutInternalHeaders(key, val)) { map.put(key, val); } } Loading @@ -149,28 +155,36 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { ConnectionState connectionState = mConnectionStateHolder.getAndSet(null); if (connectionState != null) { teardownConnection(connectionState); connectionState.mHeaders = null; connectionState.mURL = null; HttpURLConnection connectionToDisconnect = mConnection; // Call disconnect() before blocking for the lock in order to ensure that any // other thread that is blocked in readAt() will return quickly. if (connectionToDisconnect != null) { connectionToDisconnect.disconnect(); } synchronized (this) { // It's unlikely but possible that while we were waiting to acquire the lock, another // thread concurrently started a new connection; if so, we're disconnecting that one // here, too. teardownConnection(); mHeaders = null; mURL = null; } } private void teardownConnection(ConnectionState connectionState) { if (connectionState.mConnection != null) { if (connectionState.mInputStream != null) { private synchronized void teardownConnection() { if (mConnection != null) { if (mInputStream != null) { try { connectionState.mInputStream.close(); mInputStream.close(); } catch (IOException e) { } connectionState.mInputStream = null; mInputStream = null; } connectionState.mConnection.disconnect(); connectionState.mConnection = null; mConnection.disconnect(); mConnection = null; connectionState.mCurrentOffset = -1; mCurrentOffset = -1; } } Loading @@ -197,44 +211,42 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return false; } private void seekTo(long offset, ConnectionState connectionState) throws IOException { teardownConnection(connectionState); private synchronized void seekTo(long offset) throws IOException { teardownConnection(); try { int response; int redirectCount = 0; URL url = connectionState.mURL; URL url = mURL; // do not use any proxy for localhost (127.0.0.1) boolean noProxy = isLocalHost(url); while (true) { if (noProxy) { connectionState.mConnection = (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY); } else { connectionState.mConnection = (HttpURLConnection) url.openConnection(); mConnection = (HttpURLConnection)url.openConnection(); } connectionState.mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); // handle redirects ourselves if we do not allow cross-domain redirect connectionState.mConnection.setInstanceFollowRedirects( connectionState.mAllowCrossDomainRedirect); mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect); if (connectionState.mHeaders != null) { for (Map.Entry<String, String> entry : connectionState.mHeaders.entrySet()) { connectionState.mConnection.setRequestProperty( if (mHeaders != null) { for (Map.Entry<String, String> entry : mHeaders.entrySet()) { mConnection.setRequestProperty( entry.getKey(), entry.getValue()); } } if (offset > 0) { connectionState.mConnection.setRequestProperty( mConnection.setRequestProperty( "Range", "bytes=" + offset + "-"); } response = connectionState.mConnection.getResponseCode(); response = mConnection.getResponseCode(); if (response != HttpURLConnection.HTTP_MULT_CHOICE && response != HttpURLConnection.HTTP_MOVED_PERM && response != HttpURLConnection.HTTP_MOVED_TEMP && Loading @@ -248,7 +260,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new NoRouteToHostException("Too many redirects: " + redirectCount); } String method = connectionState.mConnection.getRequestMethod(); 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 Loading @@ -256,35 +268,34 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // automatically redirect the request" throw new NoRouteToHostException("Invalid redirect"); } String location = connectionState.mConnection.getHeaderField("Location"); String location = mConnection.getHeaderField("Location"); if (location == null) { throw new NoRouteToHostException("Invalid redirect"); } url = new URL(connectionState.mURL /* TRICKY: don't use url! */, location); 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 = connectionState.mURL.getProtocol().equals(url.getProtocol()); if (!connectionState.mAllowCrossProtocolRedirect && !sameProtocol) { boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol()); if (!mAllowCrossProtocolRedirect && !sameProtocol) { throw new NoRouteToHostException("Cross-protocol redirects are disallowed"); } boolean sameHost = connectionState.mURL.getHost().equals(url.getHost()); if (!connectionState.mAllowCrossDomainRedirect && !sameHost) { 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 connectionState.mURL = url; mURL = url; } } if (connectionState.mAllowCrossDomainRedirect) { if (mAllowCrossDomainRedirect) { // remember the current, potentially redirected URL if redirects // were handled by HttpURLConnection connectionState.mURL = connectionState.mConnection.getURL(); mURL = mConnection.getURL(); } if (response == HttpURLConnection.HTTP_PARTIAL) { Loading @@ -292,9 +303,10 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // because what we want is not just the length of the range // returned but the size of the full content if available. String contentRange = connectionState.mConnection.getHeaderField("Content-Range"); String contentRange = mConnection.getHeaderField("Content-Range"); connectionState.mTotalSize = -1; mTotalSize = -1; if (contentRange != null) { // format is "bytes xxx-yyy/zzz // where "zzz" is the total number of bytes of the Loading @@ -306,7 +318,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { contentRange.substring(lastSlashPos + 1); try { connectionState.mTotalSize = Long.parseLong(total); mTotalSize = Long.parseLong(total); } catch (NumberFormatException e) { } } Loading @@ -314,7 +326,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } else if (response != HttpURLConnection.HTTP_OK) { throw new IOException(); } else { connectionState.mTotalSize = connectionState.mConnection.getContentLength(); mTotalSize = mConnection.getContentLength(); } if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) { Loading @@ -323,14 +335,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new ProtocolException(); } connectionState.mInputStream = new BufferedInputStream(connectionState.mConnection.getInputStream()); mInputStream = new BufferedInputStream(mConnection.getInputStream()); connectionState.mCurrentOffset = offset; mCurrentOffset = offset; } catch (IOException e) { connectionState.mTotalSize = -1; teardownConnection(connectionState); connectionState.mCurrentOffset = -1; mTotalSize = -1; teardownConnection(); mCurrentOffset = -1; throw e; } Loading @@ -338,28 +350,22 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public int readAt(long offset, int size) { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { return native_readAt(offset, size, connectionState); } return -1; public synchronized int readAt(long offset, int size) { return native_readAt(offset, size); } private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) { private synchronized int readAt(long offset, byte[] data, int size) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); StrictMode.setThreadPolicy(policy); try { synchronized(this) { if (offset != connectionState.mCurrentOffset) { seekTo(offset, connectionState); } if (offset != mCurrentOffset) { seekTo(offset); } int n = connectionState.mInputStream.read(data, 0, size); int n = mInputStream.read(data, 0, size); if (n == -1) { // InputStream signals EOS using a -1 result, our semantics Loading @@ -367,7 +373,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { n = 0; } connectionState.mCurrentOffset += n; mCurrentOffset += n; if (VERBOSE) { Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); Loading Loading @@ -399,47 +405,35 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override public synchronized long getSize() { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { if (connectionState.mConnection == null) { if (mConnection == null) { try { seekTo(0, connectionState); seekTo(0); } catch (IOException e) { return -1; } } return connectionState.mTotalSize; } return -1; return mTotalSize; } @Override @UnsupportedAppUsage public synchronized String getMIMEType() { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { if (connectionState.mConnection == null) { if (mConnection == null) { try { seekTo(0, connectionState); seekTo(0); } catch (IOException e) { return "application/octet-stream"; } } return connectionState.mConnection.getContentType(); } return null; return mConnection.getContentType(); } @Override @UnsupportedAppUsage public String getUri() { ConnectionState connectionState = mConnectionStateHolder.get(); if (connectionState != null) { return connectionState.mURL.toString(); } return null; public synchronized String getUri() { return mURL.toString(); } @Override Loading @@ -452,7 +446,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private native final void native_finalize(); private native final IBinder native_getIMemory(); private native int native_readAt(long offset, int size, ConnectionState connectionState); private native final int native_readAt(long offset, int size); static { System.loadLibrary("media_jni"); Loading
media/jni/android_media_MediaHTTPConnection.cpp +4 −5 Original line number Diff line number Diff line Loading @@ -109,8 +109,7 @@ static void android_media_MediaHTTPConnection_native_init(JNIEnv *env) { gFields.context = env->GetFieldID(clazz.get(), "mNativeContext", "J"); CHECK(gFields.context != NULL); gFields.readAtMethodID = env->GetMethodID( clazz.get(), "readAt", "(J[BILandroid/media/MediaHTTPConnection$ConnectionState;)I"); gFields.readAtMethodID = env->GetMethodID(clazz.get(), "readAt", "(J[BI)I"); } static void android_media_MediaHTTPConnection_native_setup( Loading @@ -133,7 +132,7 @@ static jobject android_media_MediaHTTPConnection_native_getIMemory( } static jint android_media_MediaHTTPConnection_native_readAt( JNIEnv *env, jobject thiz, jlong offset, jint size, jobject connectionState) { JNIEnv *env, jobject thiz, jlong offset, jint size) { sp<JMediaHTTPConnection> conn = getObject(env, thiz); if (size > JMediaHTTPConnection::kBufferSize) { size = JMediaHTTPConnection::kBufferSize; Loading @@ -142,7 +141,7 @@ static jint android_media_MediaHTTPConnection_native_readAt( jbyteArray byteArrayObj = conn->getByteArrayObj(); jint n = env->CallIntMethod( thiz, gFields.readAtMethodID, offset, byteArrayObj, size, connectionState); thiz, gFields.readAtMethodID, offset, byteArrayObj, size); if (n > 0) { env->GetByteArrayRegion( Loading @@ -159,7 +158,7 @@ static const JNINativeMethod gMethods[] = { { "native_getIMemory", "()Landroid/os/IBinder;", (void *)android_media_MediaHTTPConnection_native_getIMemory }, { "native_readAt", "(JILandroid/media/MediaHTTPConnection$ConnectionState;)I", { "native_readAt", "(JI)I", (void *)android_media_MediaHTTPConnection_native_readAt }, { "native_init", "()V", Loading