From dc9f4b4d5d28fc68b1b5e4e8500bf67d4b11621d Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Fri, 22 Mar 2019 11:15:44 +0000 Subject: [PATCH 1/2] Revert "MediaHTTPConnection: move connection states into an inner class" This reverts commit 621e7968adf0253d5e22406f02ccc8bcc0eda5ec. Many of the fields that were moved are annotated @UnsupportedAppUsage, so the CL would have had undesirable app compat impact. Further, because investigation has revealed that lock contention *is* possible, we need to always acquire the lock anyway so there is no longer a benefit in keeping all of the mutable state in a single field that can be atomically set to null. Test: Treehugger Change-Id: I202c5653cb086d99228491e161a159bad640105a --- .../android/media/MediaHTTPConnection.java | 209 ++++++++---------- .../jni/android_media_MediaHTTPConnection.cpp | 9 +- 2 files changed, 93 insertions(+), 125 deletions(-) diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index 3838a99969f0e..d72476269e18c 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -37,7 +37,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 { @@ -47,23 +46,27 @@ 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 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 mHeaders = null; - public URL mURL = null; - public long mTotalSize = -1; - public boolean mAllowCrossDomainRedirect = true; - public boolean mAllowCrossProtocolRedirect = true; - } - private final AtomicReference mConnectionStateHolder = - new AtomicReference(); - @UnsupportedAppUsage public MediaHTTPConnection() { CookieHandler cookieHandler = CookieHandler.getDefault(); @@ -81,23 +84,13 @@ 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(); - 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(); @@ -113,21 +106,18 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } /* returns true iff header is internal */ - private boolean filterOutInternalHeaders( - String key, String val, ConnectionState connectionState) { + private 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 convertHeaderStringToMap(String headers, - ConnectionState connectionState) { + private Map convertHeaderStringToMap(String headers) { HashMap map = new HashMap(); String[] pairs = headers.split("\r\n"); @@ -137,7 +127,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); } } @@ -149,28 +139,25 @@ 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; - } + teardownConnection(); + mHeaders = null; + mURL = null; } - private void teardownConnection(ConnectionState connectionState) { - if (connectionState.mConnection != null) { - if (connectionState.mInputStream != null) { + private 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; } } @@ -197,44 +184,42 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return false; } - private void seekTo(long offset, ConnectionState connectionState) throws IOException { - teardownConnection(connectionState); + private 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 entry : connectionState.mHeaders.entrySet()) { - connectionState.mConnection.setRequestProperty( + if (mHeaders != null) { + for (Map.Entry 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 && @@ -248,7 +233,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 @@ -256,35 +241,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) { @@ -292,9 +276,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 @@ -306,7 +291,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { contentRange.substring(lastSlashPos + 1); try { - connectionState.mTotalSize = Long.parseLong(total); + mTotalSize = Long.parseLong(total); } catch (NumberFormatException e) { } } @@ -314,7 +299,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) { @@ -323,14 +308,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; } @@ -339,14 +324,10 @@ 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; + return native_readAt(offset, size); } - private int readAt(long offset, byte[] data, int size, ConnectionState connectionState) { + private int readAt(long offset, byte[] data, int size) { StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build(); @@ -354,12 +335,12 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { 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 @@ -367,7 +348,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { n = 0; } - connectionState.mCurrentOffset += n; + mCurrentOffset += n; if (VERBOSE) { Log.d(TAG, "readAt " + offset + " / " + size + " => " + n); @@ -399,47 +380,35 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override public synchronized long getSize() { - ConnectionState connectionState = mConnectionStateHolder.get(); - if (connectionState != null) { - if (connectionState.mConnection == null) { - try { - seekTo(0, connectionState); - } catch (IOException e) { - return -1; - } + if (mConnection == null) { + try { + 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) { - try { - seekTo(0, connectionState); - } catch (IOException e) { - return "application/octet-stream"; - } + if (mConnection == null) { + try { + 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; + return mURL.toString(); } @Override @@ -452,7 +421,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"); diff --git a/media/jni/android_media_MediaHTTPConnection.cpp b/media/jni/android_media_MediaHTTPConnection.cpp index d28c15c98d162..365e045689f08 100644 --- a/media/jni/android_media_MediaHTTPConnection.cpp +++ b/media/jni/android_media_MediaHTTPConnection.cpp @@ -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( @@ -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 conn = getObject(env, thiz); if (size > JMediaHTTPConnection::kBufferSize) { size = JMediaHTTPConnection::kBufferSize; @@ -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( @@ -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", From 8d9fccee62e2c73abe952f2a1de575c28bcd9410 Mon Sep 17 00:00:00 2001 From: Tobias Thierer Date: Tue, 2 Apr 2019 20:14:20 +0100 Subject: [PATCH 2/2] Make MediaHTTPConnection thread safe. MediaHTTPConnection's public methods are called from multiple Binder threads. Since both HttpURLConnection and access to the various connection related fields is not thread safe, this CL guards most methods by a single lock. This means that the methods can now block when called, although this should be rare: - there are two processes that call these methods. One process only calls getSize(), and the other process calls methods from a single thread (ie. at not overlapping clock times). - should lock contention unexpectedly increase in future, then that would be bad (because Binder thread pool threads would be blocked/unavailable), but it would not be easy to detect. It would be easy to detect if we could stop getSize() being called at overlapping clock times, since we could then use ReentrantLock.tryLock() to assert that the lock is never contended outside of disconnect(). Because it's a requirement for disconnect() to quickly stop another thread that is blocked in readAt(), disconnect() is the only method that doesn't acquire the lock immediately; the mConnection field is marked volatile so that disconnect() has a high chance of reading that field and calling disconnect() on it without waiting for another thread (there's a small risk that another thread might acquire the lock and start a new connection while disconnect() is waiting for the lock; in that case, after acquiring the lock, disconnect() will also disconnect that new connection; this is subject to potential change in future. Initially, a ReentrantLock object was considered but for now this CL instead uses the synchronized lock on "this" because: - it minimizes churn on the lines of code in this file because synchronized (this) { } can be expressed by introduction of the word "synchronized" on the method header, whereas mLock.lock(); try { ... } finally { mLock.unlock(); } would indent all the lines in-between and thus pollute git annotate. - some methods were already synchronized. - ReentrantLock.tryLock() is not used for now; most of the time, lock acquisition should be uncontended but the two cases of lock contention mentioned above exist, which makes it difficult to distinguish surprising from unsurprising lock contention. While this is the case, it seems better to keep the code simple and to just unconditionally block. Fixes: 114337214 Fixes: 119900000 Fixes: 129444137 Fixes: 128758794 Fixes: 63002045 Test: Checked manually that bug 114337214 no longer reproduces on Android API level 27 (Oreo MR1) after cherrypicking this CL. Test: Ran the following on internal master with this CL: make cts && cts-tradefed run cts -m CtsMediaTestCases \ -t android.media.cts.NativeDecoderTest#testAMediaDataSourceClose \ --abi arm64-v8a Test: Ran the following both on AOSP (158 tests) and internal master (178): atest CtsMediaTestCases:android.media.cts.{MediaPlayer{,2},Routing}Test All these tests pass except that on AOSP only, the following test fails both before and after my CL (appears unrelated): android.media.cts.RoutingTest#test_MediaPlayer_RoutingChangedCallback Change-Id: I4e32a58891c3ce60ddfa72d36060486d37906f8d --- .../android/media/MediaHTTPConnection.java | 59 +++++++++++++------ 1 file changed, 42 insertions(+), 17 deletions(-) diff --git a/media/java/android/media/MediaHTTPConnection.java b/media/java/android/media/MediaHTTPConnection.java index d72476269e18c..b5c4cca12ff7d 100644 --- a/media/java/android/media/MediaHTTPConnection.java +++ b/media/java/android/media/MediaHTTPConnection.java @@ -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; @@ -46,20 +47,35 @@ 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 mHeaders = null; + + // volatile so that disconnect() can be called without acquiring a lock. + // All other access is @GuardedBy("this"). @UnsupportedAppUsage - private HttpURLConnection mConnection = null; + 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; @@ -79,7 +95,7 @@ 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); } @@ -96,7 +112,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return native_getIMemory(); } - private boolean parseBoolean(String val) { + private static boolean parseBoolean(String val) { try { return Long.parseLong(val) != 0; } catch (NumberFormatException e) { @@ -106,7 +122,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { } /* returns true iff header is internal */ - private boolean filterOutInternalHeaders(String key, String val) { + private synchronized 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 @@ -117,7 +133,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return true; } - private Map convertHeaderStringToMap(String headers) { + private synchronized Map convertHeaderStringToMap(String headers) { HashMap map = new HashMap(); String[] pairs = headers.split("\r\n"); @@ -139,12 +155,23 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage public void disconnect() { - teardownConnection(); - mHeaders = null; - 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() { + private synchronized void teardownConnection() { if (mConnection != null) { if (mInputStream != null) { try { @@ -184,7 +211,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { return false; } - private void seekTo(long offset) throws IOException { + private synchronized void seekTo(long offset) throws IOException { teardownConnection(); try { @@ -323,21 +350,19 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage - public int readAt(long offset, int size) { + public synchronized int readAt(long offset, int size) { return native_readAt(offset, size); } - private int readAt(long offset, byte[] data, int size) { + 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 != mCurrentOffset) { - seekTo(offset); - } + if (offset != mCurrentOffset) { + seekTo(offset); } int n = mInputStream.read(data, 0, size); @@ -407,7 +432,7 @@ public class MediaHTTPConnection extends IMediaHTTPConnection.Stub { @Override @UnsupportedAppUsage - public String getUri() { + public synchronized String getUri() { return mURL.toString(); }