1d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber/*
2d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * Copyright (C) 2013 The Android Open Source Project
3d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber *
4d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * Licensed under the Apache License, Version 2.0 (the "License");
5d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * you may not use this file except in compliance with the License.
6d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * You may obtain a copy of the License at
7d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber *
8d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber *      http://www.apache.org/licenses/LICENSE-2.0
9d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber *
10d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * Unless required by applicable law or agreed to in writing, software
11d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * distributed under the License is distributed on an "AS IS" BASIS,
12d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * See the License for the specific language governing permissions and
14d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber * limitations under the License.
15d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber */
16d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
17d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberpackage android.media;
18d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
19a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhangimport android.net.NetworkUtils;
20d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport android.os.IBinder;
21d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport android.os.StrictMode;
22d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport android.util.Log;
23d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
24d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.io.BufferedInputStream;
25d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.io.InputStream;
26d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.io.IOException;
27d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.CookieHandler;
28d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.CookieManager;
29a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhangimport java.net.Proxy;
30d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.URL;
31d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.HttpURLConnection;
32d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.MalformedURLException;
33c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnarimport java.net.NoRouteToHostException;
348976a0954cce9cfc8bd8776893fc9067e240a91cChong Zhangimport java.net.ProtocolException;
356db04b369ec4a4d59315ba0207ecc53d8e8852ccAlex Klyubinimport java.net.UnknownServiceException;
36d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.util.HashMap;
37d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.util.Map;
38d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
39c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnarimport static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
40c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
41d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber/** @hide */
42d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberpublic class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
43d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private static final String TAG = "MediaHTTPConnection";
44d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private static final boolean VERBOSE = false;
45d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
4690b59750436bc23ff040d9287651e51bfd7fb470Chong Zhang    // connection timeout - 30 sec
4790b59750436bc23ff040d9287651e51bfd7fb470Chong Zhang    private static final int CONNECT_TIMEOUT_MS = 30 * 1000;
4890b59750436bc23ff040d9287651e51bfd7fb470Chong Zhang
49d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private long mCurrentOffset = -1;
50d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private URL mURL = null;
51d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private Map<String, String> mHeaders = null;
52d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private HttpURLConnection mConnection = null;
53d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private long mTotalSize = -1;
54d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private InputStream mInputStream = null;
55d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
56c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private boolean mAllowCrossDomainRedirect = true;
575b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu    private boolean mAllowCrossProtocolRedirect = true;
58c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
59c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    // from com.squareup.okhttp.internal.http
60c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private final static int HTTP_TEMP_REDIRECT = 307;
61c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private final static int MAX_REDIRECTS = 20;
62c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
63d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public MediaHTTPConnection() {
649bfb46f6ff1fb90593dc03678a7dc78671410a79Sungsoo Lim        CookieHandler cookieHandler = CookieHandler.getDefault();
659bfb46f6ff1fb90593dc03678a7dc78671410a79Sungsoo Lim        if (cookieHandler == null) {
669bfb46f6ff1fb90593dc03678a7dc78671410a79Sungsoo Lim            Log.w(TAG, "MediaHTTPConnection: Unexpected. No CookieHandler found.");
67d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
68d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
69d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        native_setup();
70d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
71d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
726e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
73d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public IBinder connect(String uri, String headers) {
74d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (VERBOSE) {
75d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers);
76d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
77d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
78d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        try {
79d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            disconnect();
80c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            mAllowCrossDomainRedirect = true;
81d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mURL = new URL(uri);
82d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mHeaders = convertHeaderStringToMap(headers);
83d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (MalformedURLException e) {
84d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return null;
85d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
86d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
87d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return native_getIMemory();
88d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
89d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
90c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private boolean parseBoolean(String val) {
91c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        try {
92c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return Long.parseLong(val) != 0;
93c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        } catch (NumberFormatException e) {
94c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return "true".equalsIgnoreCase(val) ||
95c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                "yes".equalsIgnoreCase(val);
96c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        }
97c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    }
98c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
99c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    /* returns true iff header is internal */
100c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private boolean filterOutInternalHeaders(String key, String val) {
101c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
102c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            mAllowCrossDomainRedirect = parseBoolean(val);
1035b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu            // cross-protocol redirects are also controlled by this flag
1045b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu            mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
105c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        } else {
106c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return false;
107c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        }
108c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        return true;
109c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    }
110c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
111d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private Map<String, String> convertHeaderStringToMap(String headers) {
112d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        HashMap<String, String> map = new HashMap<String, String>();
113d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
114d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        String[] pairs = headers.split("\r\n");
115d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        for (String pair : pairs) {
116d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            int colonPos = pair.indexOf(":");
117d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (colonPos >= 0) {
118d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                String key = pair.substring(0, colonPos);
119d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                String val = pair.substring(colonPos + 1);
120d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
121c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (!filterOutInternalHeaders(key, val)) {
122c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    map.put(key, val);
123c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
124d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
125d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
126d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
127d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return map;
128d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
129d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
1306e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
131d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public void disconnect() {
132d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        teardownConnection();
133d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        mHeaders = null;
134d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        mURL = null;
135d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
136d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
137d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private void teardownConnection() {
138d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (mConnection != null) {
13946132afee7783665ae12538edd024770dbd93ecbMarco Nelissen            if (mInputStream != null) {
14046132afee7783665ae12538edd024770dbd93ecbMarco Nelissen                try {
14146132afee7783665ae12538edd024770dbd93ecbMarco Nelissen                    mInputStream.close();
14246132afee7783665ae12538edd024770dbd93ecbMarco Nelissen                } catch (IOException e) {
14346132afee7783665ae12538edd024770dbd93ecbMarco Nelissen                }
14446132afee7783665ae12538edd024770dbd93ecbMarco Nelissen                mInputStream = null;
14546132afee7783665ae12538edd024770dbd93ecbMarco Nelissen            }
146d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
147d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mConnection.disconnect();
148d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mConnection = null;
149d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
150d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset = -1;
151d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
152d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
153d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
154a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang    private static final boolean isLocalHost(URL url) {
155a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        if (url == null) {
156a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            return false;
157a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        }
158a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
159a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        String host = url.getHost();
160a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
161a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        if (host == null) {
162a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            return false;
163a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        }
164a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
165a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        try {
166a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            if (host.equalsIgnoreCase("localhost")) {
167a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                return true;
168a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            }
169a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            if (NetworkUtils.numericToInetAddress(host).isLoopbackAddress()) {
170a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                return true;
171a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            }
172a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        } catch (IllegalArgumentException iex) {
173a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        }
174a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang        return false;
175a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang    }
176a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
177d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private void seekTo(long offset) throws IOException {
178d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        teardownConnection();
179d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
180d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        try {
181c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            int response;
182c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            int redirectCount = 0;
183c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
184c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            URL url = mURL;
185a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
186a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            // do not use any proxy for localhost (127.0.0.1)
187a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang            boolean noProxy = isLocalHost(url);
188a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
189c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            while (true) {
190a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                if (noProxy) {
191a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                    mConnection = (HttpURLConnection)url.openConnection(Proxy.NO_PROXY);
192a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                } else {
193a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                    mConnection = (HttpURLConnection)url.openConnection();
194a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang                }
19590b59750436bc23ff040d9287651e51bfd7fb470Chong Zhang                mConnection.setConnectTimeout(CONNECT_TIMEOUT_MS);
196a6ae8ba4a59f3f893fe8893335e7517388123c10Chong Zhang
197c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                // handle redirects ourselves if we do not allow cross-domain redirect
198c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
199c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
200c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (mHeaders != null) {
201c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
202c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        mConnection.setRequestProperty(
203c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                                entry.getKey(), entry.getValue());
204c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    }
205c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
206d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
207c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (offset > 0) {
208d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    mConnection.setRequestProperty(
209c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                            "Range", "bytes=" + offset + "-");
210c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
211c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
212c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                response = mConnection.getResponseCode();
213c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (response != HttpURLConnection.HTTP_MULT_CHOICE &&
214c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HttpURLConnection.HTTP_MOVED_PERM &&
215c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HttpURLConnection.HTTP_MOVED_TEMP &&
216c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HttpURLConnection.HTTP_SEE_OTHER &&
217c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HTTP_TEMP_REDIRECT) {
218c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // not a redirect, or redirect handled by HttpURLConnection
219c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    break;
220c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
221c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
222c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (++redirectCount > MAX_REDIRECTS) {
223c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Too many redirects: " + redirectCount);
224c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
225c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
226c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                String method = mConnection.getRequestMethod();
227c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (response == HTTP_TEMP_REDIRECT &&
228c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        !method.equals("GET") && !method.equals("HEAD")) {
229c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // "If the 307 status code is received in response to a
230c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // request other than GET or HEAD, the user agent MUST NOT
231c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // automatically redirect the request"
232c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Invalid redirect");
233c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
234c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                String location = mConnection.getHeaderField("Location");
235c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (location == null) {
236c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Invalid redirect");
237c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
238c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                url = new URL(mURL /* TRICKY: don't use url! */, location);
239c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (!url.getProtocol().equals("https") &&
240c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        !url.getProtocol().equals("http")) {
241c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Unsupported protocol redirect");
242c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
2435b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol());
2445b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                if (!mAllowCrossProtocolRedirect && !sameProtocol) {
2455b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                    throw new NoRouteToHostException("Cross-protocol redirects are disallowed");
2465b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                }
247c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                boolean sameHost = mURL.getHost().equals(url.getHost());
2485b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                if (!mAllowCrossDomainRedirect && !sameHost) {
249c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Cross-domain redirects are disallowed");
250d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                }
251d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
252c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (response != HTTP_TEMP_REDIRECT) {
253c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // update effective URL, unless it is a Temporary Redirect
254c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    mURL = url;
255c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
256d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
257d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
258c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            if (mAllowCrossDomainRedirect) {
259c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                // remember the current, potentially redirected URL if redirects
260c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                // were handled by HttpURLConnection
261c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                mURL = mConnection.getURL();
262c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            }
2636e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen
2646e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen            if (response == HttpURLConnection.HTTP_PARTIAL) {
265d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // Partial content, we cannot just use getContentLength
266d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // because what we want is not just the length of the range
267d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // returned but the size of the full content if available.
268d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
269d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                String contentRange =
270d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    mConnection.getHeaderField("Content-Range");
271d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
272d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                mTotalSize = -1;
273d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                if (contentRange != null) {
274d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    // format is "bytes xxx-yyy/zzz
275d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    // where "zzz" is the total number of bytes of the
276d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    // content or '*' if unknown.
277d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
278d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    int lastSlashPos = contentRange.lastIndexOf('/');
279d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    if (lastSlashPos >= 0) {
280d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        String total =
281d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                            contentRange.substring(lastSlashPos + 1);
282d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
283d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        try {
284d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                            mTotalSize = Long.parseLong(total);
285d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        } catch (NumberFormatException e) {
286d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        }
287d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    }
288d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                }
2896e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen            } else if (response != HttpURLConnection.HTTP_OK) {
290d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                throw new IOException();
291d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            } else {
292d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                mTotalSize = mConnection.getContentLength();
293d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
294d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
2956e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen            if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
296d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // Some servers simply ignore "Range" requests and serve
297d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // data from the start of the content.
2988976a0954cce9cfc8bd8776893fc9067e240a91cChong Zhang                throw new ProtocolException();
299d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
300d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
301d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mInputStream =
302d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                new BufferedInputStream(mConnection.getInputStream());
303d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
304d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset = offset;
305d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (IOException e) {
306d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mTotalSize = -1;
30746132afee7783665ae12538edd024770dbd93ecbMarco Nelissen            teardownConnection();
308d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset = -1;
309d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
310d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            throw e;
311d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
312d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
313d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
3146e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
315d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public int readAt(long offset, int size) {
316d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return native_readAt(offset, size);
317d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
318d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
319d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private int readAt(long offset, byte[] data, int size) {
320d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        StrictMode.ThreadPolicy policy =
321d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            new StrictMode.ThreadPolicy.Builder().permitAll().build();
322d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
323d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        StrictMode.setThreadPolicy(policy);
324d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
325d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        try {
326d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (offset != mCurrentOffset) {
327d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                seekTo(offset);
328d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
329d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
330d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            int n = mInputStream.read(data, 0, size);
331d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
332d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (n == -1) {
333d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // InputStream signals EOS using a -1 result, our semantics
334d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // are to return a 0-length read.
335d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                n = 0;
336d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
337d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
338d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset += n;
339d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
340d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (VERBOSE) {
341d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "readAt " + offset + " / " + size + " => " + n);
342d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
343d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
344d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return n;
3458976a0954cce9cfc8bd8776893fc9067e240a91cChong Zhang        } catch (ProtocolException e) {
3468976a0954cce9cfc8bd8776893fc9067e240a91cChong Zhang            Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
3478976a0954cce9cfc8bd8776893fc9067e240a91cChong Zhang            return MEDIA_ERROR_UNSUPPORTED;
348c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        } catch (NoRouteToHostException e) {
349c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
350c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return MEDIA_ERROR_UNSUPPORTED;
3516db04b369ec4a4d59315ba0207ecc53d8e8852ccAlex Klyubin        } catch (UnknownServiceException e) {
3526db04b369ec4a4d59315ba0207ecc53d8e8852ccAlex Klyubin            Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
3536db04b369ec4a4d59315ba0207ecc53d8e8852ccAlex Klyubin            return MEDIA_ERROR_UNSUPPORTED;
35490b59750436bc23ff040d9287651e51bfd7fb470Chong Zhang        } catch (IOException e) {
355d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (VERBOSE) {
356d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
357d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
358d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return -1;
359d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (Exception e) {
360d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (VERBOSE) {
361d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "unknown exception " + e);
362d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
363d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
364d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return -1;
365d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
366d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
367d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
3686e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
369d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public long getSize() {
370d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (mConnection == null) {
371d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            try {
372d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                seekTo(0);
373d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            } catch (IOException e) {
374d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                return -1;
375d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
376d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
377d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
378d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return mTotalSize;
379d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
380d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
3816e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
382d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public String getMIMEType() {
383d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (mConnection == null) {
384d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            try {
385d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                seekTo(0);
386d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            } catch (IOException e) {
387d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                return "application/octet-stream";
388d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
389d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
390d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
391d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return mConnection.getContentType();
392d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
393d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
394d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    @Override
3956e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    public String getUri() {
3966e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen        return mURL.toString();
3976e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    }
3986e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen
3996e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
400d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    protected void finalize() {
401d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        native_finalize();
402d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
403d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
404d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private static native final void native_init();
405d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final void native_setup();
406d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final void native_finalize();
407d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
408d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final IBinder native_getIMemory();
409d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final int native_readAt(long offset, int size);
410d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
411d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    static {
412d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        System.loadLibrary("media_jni");
413d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        native_init();
414d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
415d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
4165e8ff02ec93aa0f933d0082d9a0e066b552c6a80Colin Cross    private long mNativeContext;
4176e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen
418d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber}
419