MediaHTTPConnection.java revision 5b9305fe9bbe8bb6916bd8daff085c0675b626f3
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
19d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport android.os.IBinder;
20d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport android.os.StrictMode;
21d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport android.util.Log;
22d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
23d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.io.BufferedInputStream;
24d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.io.InputStream;
25d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.io.IOException;
26d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.CookieHandler;
27d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.CookieManager;
28d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.URL;
29d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.HttpURLConnection;
30d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.net.MalformedURLException;
31c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnarimport java.net.NoRouteToHostException;
32d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.util.HashMap;
33d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberimport java.util.Map;
34d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
35c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnarimport static android.media.MediaPlayer.MEDIA_ERROR_UNSUPPORTED;
36c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
37d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber/** @hide */
38d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huberpublic class MediaHTTPConnection extends IMediaHTTPConnection.Stub {
39d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private static final String TAG = "MediaHTTPConnection";
40d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private static final boolean VERBOSE = false;
41d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
42d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private long mCurrentOffset = -1;
43d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private URL mURL = null;
44d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private Map<String, String> mHeaders = null;
45d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private HttpURLConnection mConnection = null;
46d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private long mTotalSize = -1;
47d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private InputStream mInputStream = null;
48d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
49c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private boolean mAllowCrossDomainRedirect = true;
505b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu    private boolean mAllowCrossProtocolRedirect = true;
51c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
52c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    // from com.squareup.okhttp.internal.http
53c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private final static int HTTP_TEMP_REDIRECT = 307;
54c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private final static int MAX_REDIRECTS = 20;
55c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
56d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public MediaHTTPConnection() {
57d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (CookieHandler.getDefault() == null) {
58d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            CookieHandler.setDefault(new CookieManager());
59d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
60d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
61d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        native_setup();
62d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
63d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
646e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
65d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public IBinder connect(String uri, String headers) {
66d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (VERBOSE) {
67d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            Log.d(TAG, "connect: uri=" + uri + ", headers=" + headers);
68d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
69d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
70d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        try {
71d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            disconnect();
72c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            mAllowCrossDomainRedirect = true;
73d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mURL = new URL(uri);
74d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mHeaders = convertHeaderStringToMap(headers);
75d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (MalformedURLException e) {
76d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return null;
77d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
78d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
79d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return native_getIMemory();
80d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
81d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
82c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private boolean parseBoolean(String val) {
83c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        try {
84c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return Long.parseLong(val) != 0;
85c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        } catch (NumberFormatException e) {
86c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return "true".equalsIgnoreCase(val) ||
87c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                "yes".equalsIgnoreCase(val);
88c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        }
89c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    }
90c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
91c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    /* returns true iff header is internal */
92c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    private boolean filterOutInternalHeaders(String key, String val) {
93c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        if ("android-allow-cross-domain-redirect".equalsIgnoreCase(key)) {
94c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            mAllowCrossDomainRedirect = parseBoolean(val);
955b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu            // cross-protocol redirects are also controlled by this flag
965b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu            mAllowCrossProtocolRedirect = mAllowCrossDomainRedirect;
97c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        } else {
98c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return false;
99c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        }
100c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        return true;
101c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar    }
102c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
103d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private Map<String, String> convertHeaderStringToMap(String headers) {
104d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        HashMap<String, String> map = new HashMap<String, String>();
105d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
106d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        String[] pairs = headers.split("\r\n");
107d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        for (String pair : pairs) {
108d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            int colonPos = pair.indexOf(":");
109d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (colonPos >= 0) {
110d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                String key = pair.substring(0, colonPos);
111d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                String val = pair.substring(colonPos + 1);
112d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
113c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (!filterOutInternalHeaders(key, val)) {
114c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    map.put(key, val);
115c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
116d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
117d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
118d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
119d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return map;
120d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
121d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
1226e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
123d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public void disconnect() {
124d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        teardownConnection();
125d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        mHeaders = null;
126d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        mURL = null;
127d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
128d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
129d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private void teardownConnection() {
130d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (mConnection != null) {
131d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mInputStream = null;
132d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
133d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mConnection.disconnect();
134d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mConnection = null;
135d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
136d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset = -1;
137d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
138d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
139d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
140d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private void seekTo(long offset) throws IOException {
141d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        teardownConnection();
142d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
143d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        try {
144c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            int response;
145c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            int redirectCount = 0;
146c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
147c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            URL url = mURL;
148c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            while (true) {
149c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                mConnection = (HttpURLConnection)url.openConnection();
150c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                // handle redirects ourselves if we do not allow cross-domain redirect
151c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                mConnection.setInstanceFollowRedirects(mAllowCrossDomainRedirect);
152c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
153c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (mHeaders != null) {
154c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    for (Map.Entry<String, String> entry : mHeaders.entrySet()) {
155c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        mConnection.setRequestProperty(
156c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                                entry.getKey(), entry.getValue());
157c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    }
158c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
159d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
160c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (offset > 0) {
161d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    mConnection.setRequestProperty(
162c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                            "Range", "bytes=" + offset + "-");
163c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
164c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
165c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                response = mConnection.getResponseCode();
166c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (response != HttpURLConnection.HTTP_MULT_CHOICE &&
167c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HttpURLConnection.HTTP_MOVED_PERM &&
168c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HttpURLConnection.HTTP_MOVED_TEMP &&
169c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HttpURLConnection.HTTP_SEE_OTHER &&
170c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        response != HTTP_TEMP_REDIRECT) {
171c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // not a redirect, or redirect handled by HttpURLConnection
172c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    break;
173c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
174c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
175c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (++redirectCount > MAX_REDIRECTS) {
176c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Too many redirects: " + redirectCount);
177c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
178c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar
179c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                String method = mConnection.getRequestMethod();
180c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (response == HTTP_TEMP_REDIRECT &&
181c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        !method.equals("GET") && !method.equals("HEAD")) {
182c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // "If the 307 status code is received in response to a
183c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // request other than GET or HEAD, the user agent MUST NOT
184c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // automatically redirect the request"
185c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Invalid redirect");
186c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
187c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                String location = mConnection.getHeaderField("Location");
188c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (location == null) {
189c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Invalid redirect");
190c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
191c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                url = new URL(mURL /* TRICKY: don't use url! */, location);
192c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (!url.getProtocol().equals("https") &&
193c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                        !url.getProtocol().equals("http")) {
194c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Unsupported protocol redirect");
195c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
1965b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                boolean sameProtocol = mURL.getProtocol().equals(url.getProtocol());
1975b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                if (!mAllowCrossProtocolRedirect && !sameProtocol) {
1985b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                    throw new NoRouteToHostException("Cross-protocol redirects are disallowed");
1995b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                }
200c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                boolean sameHost = mURL.getHost().equals(url.getHost());
2015b9305fe9bbe8bb6916bd8daff085c0675b626f3Ronghua Wu                if (!mAllowCrossDomainRedirect && !sameHost) {
202c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    throw new NoRouteToHostException("Cross-domain redirects are disallowed");
203d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                }
204d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
205c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                if (response != HTTP_TEMP_REDIRECT) {
206c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    // update effective URL, unless it is a Temporary Redirect
207c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                    mURL = url;
208c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                }
209d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
210d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
211c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            if (mAllowCrossDomainRedirect) {
212c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                // remember the current, potentially redirected URL if redirects
213c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                // were handled by HttpURLConnection
214c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar                mURL = mConnection.getURL();
215c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            }
2166e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen
2176e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen            if (response == HttpURLConnection.HTTP_PARTIAL) {
218d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // Partial content, we cannot just use getContentLength
219d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // because what we want is not just the length of the range
220d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // returned but the size of the full content if available.
221d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
222d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                String contentRange =
223d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    mConnection.getHeaderField("Content-Range");
224d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
225d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                mTotalSize = -1;
226d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                if (contentRange != null) {
227d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    // format is "bytes xxx-yyy/zzz
228d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    // where "zzz" is the total number of bytes of the
229d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    // content or '*' if unknown.
230d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
231d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    int lastSlashPos = contentRange.lastIndexOf('/');
232d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    if (lastSlashPos >= 0) {
233d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        String total =
234d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                            contentRange.substring(lastSlashPos + 1);
235d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
236d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        try {
237d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                            mTotalSize = Long.parseLong(total);
238d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        } catch (NumberFormatException e) {
239d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                        }
240d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                    }
241d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                }
2426e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen            } else if (response != HttpURLConnection.HTTP_OK) {
243d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                throw new IOException();
244d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            } else {
245d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                mTotalSize = mConnection.getContentLength();
246d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
247d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
2486e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen            if (offset > 0 && response != HttpURLConnection.HTTP_PARTIAL) {
249d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // Some servers simply ignore "Range" requests and serve
250d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // data from the start of the content.
251d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                throw new IOException();
252d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
253d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
254d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mInputStream =
255d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                new BufferedInputStream(mConnection.getInputStream());
256d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
257d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset = offset;
258d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (IOException e) {
259d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mTotalSize = -1;
260d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mInputStream = null;
261d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mConnection = null;
262d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset = -1;
263d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
264d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            throw e;
265d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
266d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
267d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
2686e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
269d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public int readAt(long offset, int size) {
270d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return native_readAt(offset, size);
271d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
272d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
273d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private int readAt(long offset, byte[] data, int size) {
274d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        StrictMode.ThreadPolicy policy =
275d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            new StrictMode.ThreadPolicy.Builder().permitAll().build();
276d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
277d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        StrictMode.setThreadPolicy(policy);
278d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
279d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        try {
280d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (offset != mCurrentOffset) {
281d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                seekTo(offset);
282d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
283d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
284d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            int n = mInputStream.read(data, 0, size);
285d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
286d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (n == -1) {
287d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // InputStream signals EOS using a -1 result, our semantics
288d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                // are to return a 0-length read.
289d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                n = 0;
290d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
291d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
292d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            mCurrentOffset += n;
293d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
294d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (VERBOSE) {
295d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "readAt " + offset + " / " + size + " => " + n);
296d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
297d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
298d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return n;
299c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar        } catch (NoRouteToHostException e) {
300c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            Log.w(TAG, "readAt " + offset + " / " + size + " => " + e);
301c446dc32742de388b95ff7cf97f4dc1576cb57c5Lajos Molnar            return MEDIA_ERROR_UNSUPPORTED;
302d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (IOException e) {
303d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (VERBOSE) {
304d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
305d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
306d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return -1;
307d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        } catch (Exception e) {
308d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            if (VERBOSE) {
309d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "unknown exception " + e);
310d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                Log.d(TAG, "readAt " + offset + " / " + size + " => -1");
311d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
312d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            return -1;
313d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
314d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
315d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
3166e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
317d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public long getSize() {
318d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (mConnection == null) {
319d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            try {
320d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                seekTo(0);
321d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            } catch (IOException e) {
322d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                return -1;
323d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
324d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
325d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
326d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return mTotalSize;
327d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
328d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
3296e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
330d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    public String getMIMEType() {
331d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        if (mConnection == null) {
332d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            try {
333d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                seekTo(0);
334d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            } catch (IOException e) {
335d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber                return "application/octet-stream";
336d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber            }
337d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        }
338d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
339d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        return mConnection.getContentType();
340d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
341d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
342d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    @Override
3436e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    public String getUri() {
3446e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen        return mURL.toString();
3456e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    }
3466e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen
3476e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen    @Override
348d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    protected void finalize() {
349d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        native_finalize();
350d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
351d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
352d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private static native final void native_init();
353d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final void native_setup();
354d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final void native_finalize();
355d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
356d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final IBinder native_getIMemory();
357d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    private native final int native_readAt(long offset, int size);
358d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
359d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    static {
360d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        System.loadLibrary("media_jni");
361d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber        native_init();
362d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber    }
363d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber
3645e8ff02ec93aa0f933d0082d9a0e066b552c6a80Colin Cross    private long mNativeContext;
3656e89ddc0468495aa15c8408980bb7a86bf2ad604Marco Nelissen
366d2506a506303ed94fd1991cf986b825b870a67c5Andreas Huber}
367