diff options
| -rw-r--r-- | media/java/android/media/MediaHTTPConnection.java | 209 | ||||
| -rw-r--r-- | media/jni/android_media_MediaHTTPConnection.cpp | 9 |
2 files changed, 125 insertions, 93 deletions
diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index d72476269e18..3838a99969f0 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -37,6 +37,7 @@ 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 { @@ -46,27 +47,23 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // connection timeout - 30 sec private static final int CONNECT_TIMEOUT_MS = 30 * 1000; - @UnsupportedAppUsage - private long mCurrentOffset = -1; - @UnsupportedAppUsage - private URL mURL = null; - @UnsupportedAppUsage - private Map<String, String> mHeaders = null; - @UnsupportedAppUsage - private HttpURLConnection mConnection = null; - @UnsupportedAppUsage - private long mTotalSize = -1; - private InputStream mInputStream = null; - - @UnsupportedAppUsage - private boolean mAllowCrossDomainRedirect = true; - @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(); @@ -84,13 +81,23 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { 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(); - mAllowCrossDomainRedirect = true; - mURL = new URL(uri); - mHeaders = convertHeaderStringToMap(headers); + connectionState.mAllowCrossDomainRedirect = true; + connectionState.mURL = new URL(uri); + connectionState.mHeaders = convertHeaderStringToMap(headers, connectionState); } catch (MalformedURLException e) { return null; + } finally { + mConnectionStateHolder.set(connectionState); } return native_getIMemory(); @@ -106,18 +113,21 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } /* returns true iff header is internal */ - private boolean filterOutInternalHeaders(String key, String val) { + private boolean filterOutInternalHeaders( + String key, String val, ConnectionState connectionState) { if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) { - mAllowCrossDomainRedirect = parseBoolean(val); + connectionState.mAllowCrossDomainRedirect = parseBoolean(val); // cross-protocol redirects are also controlled by this flag - mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect; + connectionState.mAllowCrossProtocolRedirect = + connectionState.mAllowCrossDomainRedirect; } else { return false; } return true; } - private Map<String, String> convertHeaderStringToMap(String headers) { + private Map<String, String> convertHeaderStringToMap(String headers, + ConnectionState connectionState) { HashMap<String, String> map = new HashMap<String, String>(); String[] pairs = headers.split("\r\n"); @@ -127,7 +137,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { String key = pair.substring(0, colonPos); String val = pair.substring(colonPos + 1); - if (!filterOutInternalHeaders(key, val)) { + if (!filterOutInternalHeaders(key, val, connectionState)) { map.put(key, val); } } @@ -139,25 +149,28 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { - teardownConnection(); - mHeaders = null; - mURL = null; + ConnectionState connectionState = mConnectionStateHolder.getAndSet(null); + if (connectionState != null) { + teardownConnection(connectionState); + connectionState.mHeaders = null; + connectionState.mURL = null; + } } - private void teardownConnection() { - if (mConnection != null) { - if (mInputStream != null) { + private void teardownConnection(ConnectionState connectionState) { + if (connectionState.mConnection != null) { + if (connectionState.mInputStream != null) { try { - mInputStream.close(); + connectionState.mInputStream.close(); } catch (IOException e) { } - mInputStream = null; + connectionState.mInputStream = null; } - mConnection.disconnect(); - mConnection = null; + connectionState.mConnection.disconnect(); + connectionState.mConnection = null; - mCurrentOffset = -1; + connectionState.mCurrentOffset = -1; } } @@ -184,42 +197,44 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return false; } - private void seekTo(long offset) throws IOException { - teardownConnection(); + private void seekTo(long offset, ConnectionState connectionState) throws IOException { + teardownConnection(connectionState); try { int response; int redirectCount = 0; - URL url = mURL; + URL url = connectionState.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); + connectionState.mConnection = + (HttpURLConnection) url.openConnection(Proxy.NO_PROXY); } else { - mConnection = (HttpURLConnection)url.openConnection(); + connectionState.mConnection = (HttpURLConnection) url.openConnection(); } - mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); + connectionState.mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS); // handle redirects ourselves if we do not allow cross-domain redirect - mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect); + connectionState.mConnection.setInstanceFollowRedirects( + connectionState.mAllowCrossDomainRedirect); - if (mHeaders != null) { - for (Map.Entry<String, String> entry : mHeaders.entrySet()) { - mConnection.setRequestProperty( + if (connectionState.mHeaders != null) { + for (Map.Entry<String, String> entry : connectionState.mHeaders.entrySet()) { + connectionState.mConnection.setRequestProperty( entry.getKey(), entry.getValue()); } } if (offset > 0) { - mConnection.setRequestProperty( + connectionState.mConnection.setRequestProperty( "Range", "bytes=" + offset + "-"); } - response = mConnection.getResponseCode(); + response = connectionState.mConnection.getResponseCode(); if (response != HttpURLConnection.HTTP_MULT_CHOICE && response != HttpURLConnection.HTTP_MOVED_PERM && response != HttpURLConnection.HTTP_MOVED_TEMP && @@ -233,7 +248,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new NoRouteToHostException("Too many redirects: " + redirectCount); } - String method = mConnection.getRequestMethod(); + String method = connectionState.mConnection.getRequestMethod(); if (response == HTTP_TEMP_REDIRECT && !method.equals("GET") && !method.equals("HEAD")) { // "If the 307 status code is received in response to a @@ -241,34 +256,35 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { // automatically redirect the request" throw new NoRouteToHostException("Invalid redirect"); } - String location = mConnection.getHeaderField("Location"); + String location = connectionState.mConnection.getHeaderField("Location"); if (location == null) { throw new NoRouteToHostException("Invalid redirect"); } - url = new URL(mURL /* TRICKY: don't use url! */, location); + url = new URL(connectionState.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) { + boolean sameProtocol = + connectionState.mURL.getProtocol().equals(url.getProtocol()); + if (!connectionState.mAllowCrossProtocolRedirect && !sameProtocol) { throw new NoRouteToHostException("Cross-protocol redirects are disallowed"); } - boolean sameHost = mURL.getHost().equals(url.getHost()); - if (!mAllowCrossDomainRedirect && !sameHost) { + boolean sameHost = connectionState.mURL.getHost().equals(url.getHost()); + if (!connectionState.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; + connectionState.mURL = url; } } - if (mAllowCrossDomainRedirect) { + if (connectionState.mAllowCrossDomainRedirect) { // remember the current, potentially redirected URL if redirects // were handled by HttpURLConnection - mURL = mConnection.getURL(); + connectionState.mURL = connectionState.mConnection.getURL(); } if (response == HttpURLConnection.HTTP_PARTIAL) { @@ -276,10 +292,9 @@ 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 = - mConnection.getHeaderField("Content-Range"); + String contentRange = connectionState.mConnection.getHeaderField("Content-Range"); - mTotalSize = -1; + connectionState.mTotalSize = -1; if (contentRange != null) { // format is "bytes xxx-yyy/zzz // where "zzz" is the total number of bytes of the @@ -291,7 +306,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { contentRange.substring(lastSlashPos + 1); try { - mTotalSize = Long.parseLong(total); + connectionState.mTotalSize = Long.parseLong(total); } catch (NumberFormatException e) { } } @@ -299,7 +314,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } else if (response != HttpURLConnection.HTTP_OK) { throw new IOException(); } else { - mTotalSize = mConnection.getContentLength(); + connectionState.mTotalSize = connectionState.mConnection.getContentLength(); } if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) { @@ -308,14 +323,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { throw new ProtocolException(); } - mInputStream = - new BufferedInputStream(mConnection.getInputStream()); + connectionState.mInputStream = + new BufferedInputStream(connectionState.mConnection.getInputStream()); - mCurrentOffset = offset; + connectionState.mCurrentOffset = offset; } catch (IOException e) { - mTotalSize = -1; - teardownConnection(); - mCurrentOffset = -1; + connectionState.mTotalSize = -1; + teardownConnection(connectionState); + connectionState.mCurrentOffset = -1; throw e; } @@ -324,10 +339,14 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public int readAt(long offset, int size) { - return native_readAt(offset, size); + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + return native_readAt(offset, size, connectionState); + } + return -1; } - private int readAt(long offset, byte[] data, int size) { + private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); @@ -335,12 +354,12 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { try { synchronized(this) { - if (offset != mCurrentOffset) { - seekTo(offset); + if (offset != connectionState.mCurrentOffset) { + seekTo(offset, connectionState); } } - int n = mInputStream.read(data, 0, size); + int n = connectionState.mInputStream.read(data, 0, size); if (n == -1) { // InputStream signals EOS using a -1 result, our semantics @@ -348,7 +367,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { n = 0; } - mCurrentOffset += n; + connectionState.mCurrentOffset += n; if (VERBOSE) { Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); @@ -380,35 +399,47 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override public synchronized long getSize() { - if (mConnection == null) { - try { - seekTo(0); - } catch (IOException e) { - return -1; + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + if (connectionState.mConnection == null) { + try { + seekTo(0, connectionState); + } catch (IOException e) { + return -1; + } } + return connectionState.mTotalSize; } - return mTotalSize; + return -1; } @Override @UnsupportedAppUsage public synchronized String getMIMEType() { - if (mConnection == null) { - try { - seekTo(0); - } catch (IOException e) { - return "application/octet-stream"; + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + if (connectionState.mConnection == null) { + try { + seekTo(0, connectionState); + } catch (IOException e) { + return "application/octet-stream"; + } } + return connectionState.mConnection.getContentType(); } - return mConnection.getContentType(); + return null; } @Override @UnsupportedAppUsage public String getUri() { - return mURL.toString(); + ConnectionState connectionState = mConnectionStateHolder.get(); + if (connectionState != null) { + return connectionState.mURL.toString(); + } + return null; } @Override @@ -421,7 +452,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { private native final void native_finalize(); private native final IBinder native_getIMemory(); - private native final int native_readAt(long offset, int size); + private native int native_readAt(long offset, int size, ConnectionState connectionState); static { System.loadLibrary("media_jni"); diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp index 365e045689f0..d28c15c98d16 100644 --- a/media/jni/android_media_MediaHTTPConnection.cpp +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -109,7 +109,8 @@ 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[BI)I"); + gFields.readAtMethodID = env->GetMethodID( + clazz.get(), "readAt", "(J[BILandroid/media/MediaHTTPConnection$ConnectionState;)I"); } static void android_media_MediaHTTPConnection_native_setup( @@ -132,7 +133,7 @@ static jobject android_media_MediaHTTPConnection_native_getIMemory( } static jint android_media_MediaHTTPConnection_native_readAt( - JNIEnv *env, jobject thiz, jlong offset, jint size) { + JNIEnv *env, jobject thiz, jlong offset, jint size, jobject connectionState) { sp<JMediaHTTPConnection> conn = getObject(env, thiz); if (size > JMediaHTTPConnection::kBufferSize) { size = JMediaHTTPConnection::kBufferSize; @@ -141,7 +142,7 @@ static jint android_media_MediaHTTPConnection_native_readAt( jbyteArray byteArrayObj = conn->getByteArrayObj(); jint n = env->CallIntMethod( - thiz, gFields.readAtMethodID, offset, byteArrayObj, size); + thiz, gFields.readAtMethodID, offset, byteArrayObj, size, connectionState); if (n > 0) { env->GetByteArrayRegion( @@ -158,7 +159,7 @@ static const JNINativeMethod gMethods[] = { { "native_getIMemory", "()Landroid/os/IBinder;", (void *)android_media_MediaHTTPConnection_native_getIMemory }, - { "native_readAt", "(JI)I", + { "native_readAt", "(JILandroid/media/MediaHTTPConnection$ConnectionState;)I", (void *)android_media_MediaHTTPConnection_native_readAt }, { "native_init", "()V", |