Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 11ba3119 authored by Dichen Zhang's avatar Dichen Zhang Committed by android-build-merger
Browse files

Merge "MediaHTTPConnection: move connection states into an inner class" am:...

Merge "MediaHTTPConnection: move connection states into an inner class" am: abe16aff am: 6be164a3
am: 33435110

Change-Id: I8042e41f795a49ce421cb59229b1974bf0afc19a
parents 6625e37e 33435110
Loading
Loading
Loading
Loading
+120 −89
Original line number Diff line number Diff line
@@ -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) {
        ConnectionState connectionState = mConnectionStateHolder.get();
        if (connectionState != null) {
            if (connectionState.mConnection == null) {
                try {
                seekTo(0);
                    seekTo(0, connectionState);
                } catch (IOException e) {
                    return -1;
                }
            }
            return connectionState.mTotalSize;
        }

        return mTotalSize;
        return -1;
    }

    @Override
    @UnsupportedAppUsage
    public synchronized String getMIMEType() {
        if (mConnection == null) {
        ConnectionState connectionState = mConnectionStateHolder.get();
        if (connectionState != null) {
            if (connectionState.mConnection == null) {
                try {
                seekTo(0);
                    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");
+5 −4
Original line number Diff line number Diff line
@@ -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",