1/*
2 * Copyright (C) 2011 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "ChromiumHTTPDataSource"
19#include <media/stagefright/foundation/ADebug.h>
20
21#include "include/ChromiumHTTPDataSource.h"
22
23#include <media/stagefright/foundation/ALooper.h>
24#include <media/stagefright/MediaErrors.h>
25
26#include "support.h"
27
28#include <cutils/properties.h> // for property_get
29
30namespace android {
31
32ChromiumHTTPDataSource::ChromiumHTTPDataSource(uint32_t flags)
33    : mFlags(flags),
34      mState(DISCONNECTED),
35      mDelegate(new SfDelegate),
36      mCurrentOffset(0),
37      mIOResult(OK),
38      mContentSize(-1),
39      mDecryptHandle(NULL),
40      mDrmManagerClient(NULL) {
41    mDelegate->setOwner(this);
42}
43
44ChromiumHTTPDataSource::~ChromiumHTTPDataSource() {
45    disconnect();
46
47    delete mDelegate;
48    mDelegate = NULL;
49
50    clearDRMState_l();
51
52    if (mDrmManagerClient != NULL) {
53        delete mDrmManagerClient;
54        mDrmManagerClient = NULL;
55    }
56}
57
58status_t ChromiumHTTPDataSource::connect(
59        const char *uri,
60        const KeyedVector<String8, String8> *headers,
61        off64_t offset) {
62    Mutex::Autolock autoLock(mLock);
63
64    uid_t uid;
65    if (getUID(&uid)) {
66        mDelegate->setUID(uid);
67    }
68
69#if defined(LOG_NDEBUG) && !LOG_NDEBUG
70    LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect on behalf of uid %d", uid);
71#endif
72
73    return connect_l(uri, headers, offset);
74}
75
76status_t ChromiumHTTPDataSource::connect_l(
77        const char *uri,
78        const KeyedVector<String8, String8> *headers,
79        off64_t offset) {
80    if (mState != DISCONNECTED) {
81        disconnect_l();
82    }
83
84#if defined(LOG_NDEBUG) && !LOG_NDEBUG
85    LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG,
86                "connect to <URL suppressed> @%lld", offset);
87#endif
88
89    mURI = uri;
90    mContentType = String8("application/octet-stream");
91
92    if (headers != NULL) {
93        mHeaders = *headers;
94    } else {
95        mHeaders.clear();
96    }
97
98    mState = CONNECTING;
99    mContentSize = -1;
100    mCurrentOffset = offset;
101
102    mDelegate->initiateConnection(mURI.c_str(), &mHeaders, offset);
103
104    while (mState == CONNECTING || mState == DISCONNECTING) {
105        mCondition.wait(mLock);
106    }
107
108    return mState == CONNECTED ? OK : mIOResult;
109}
110
111void ChromiumHTTPDataSource::onConnectionEstablished(
112        int64_t contentSize, const char *contentType) {
113    Mutex::Autolock autoLock(mLock);
114
115    if (mState != CONNECTING) {
116        // We may have initiated disconnection.
117        CHECK_EQ(mState, DISCONNECTING);
118        return;
119    }
120
121    mState = CONNECTED;
122    mContentSize = (contentSize < 0) ? -1 : contentSize + mCurrentOffset;
123    mContentType = String8(contentType);
124    mCondition.broadcast();
125}
126
127void ChromiumHTTPDataSource::onConnectionFailed(status_t err) {
128    Mutex::Autolock autoLock(mLock);
129    mState = DISCONNECTED;
130    mCondition.broadcast();
131
132    // mURI.clear();
133
134    mIOResult = err;
135}
136
137void ChromiumHTTPDataSource::disconnect() {
138    Mutex::Autolock autoLock(mLock);
139    disconnect_l();
140}
141
142void ChromiumHTTPDataSource::disconnect_l() {
143    if (mState == DISCONNECTED) {
144        return;
145    }
146
147    mState = DISCONNECTING;
148    mIOResult = -EINTR;
149
150    mDelegate->initiateDisconnect();
151
152    while (mState == DISCONNECTING) {
153        mCondition.wait(mLock);
154    }
155
156    CHECK_EQ((int)mState, (int)DISCONNECTED);
157}
158
159status_t ChromiumHTTPDataSource::initCheck() const {
160    Mutex::Autolock autoLock(mLock);
161
162    return mState == CONNECTED ? OK : NO_INIT;
163}
164
165ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) {
166    Mutex::Autolock autoLock(mLock);
167
168    if (mState != CONNECTED) {
169        return INVALID_OPERATION;
170    }
171
172#if 0
173    char value[PROPERTY_VALUE_MAX];
174    if (property_get("media.stagefright.disable-net", value, 0)
175            && (!strcasecmp(value, "true") || !strcmp(value, "1"))) {
176        LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Simulating that the network is down.");
177        disconnect_l();
178        return ERROR_IO;
179    }
180#endif
181
182    if (offset != mCurrentOffset) {
183        AString tmp = mURI;
184        KeyedVector<String8, String8> tmpHeaders = mHeaders;
185
186        disconnect_l();
187
188        status_t err = connect_l(tmp.c_str(), &tmpHeaders, offset);
189
190        if (err != OK) {
191            return err;
192        }
193    }
194
195    mState = READING;
196
197    int64_t startTimeUs = ALooper::GetNowUs();
198
199    mDelegate->initiateRead(data, size);
200
201    while (mState == READING) {
202        mCondition.wait(mLock);
203    }
204
205    if (mIOResult < OK) {
206        return mIOResult;
207    }
208
209    if (mState == CONNECTED) {
210        int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
211
212        // The read operation was successful, mIOResult contains
213        // the number of bytes read.
214        addBandwidthMeasurement(mIOResult, delayUs);
215
216        mCurrentOffset += mIOResult;
217        return mIOResult;
218    }
219
220    return ERROR_IO;
221}
222
223void ChromiumHTTPDataSource::onReadCompleted(ssize_t size) {
224    Mutex::Autolock autoLock(mLock);
225
226    mIOResult = size;
227
228    if (mState == READING) {
229        mState = CONNECTED;
230        mCondition.broadcast();
231    }
232}
233
234status_t ChromiumHTTPDataSource::getSize(off64_t *size) {
235    Mutex::Autolock autoLock(mLock);
236
237    if (mContentSize < 0) {
238        return ERROR_UNSUPPORTED;
239    }
240
241    *size = mContentSize;
242
243    return OK;
244}
245
246uint32_t ChromiumHTTPDataSource::flags() {
247    return kWantsPrefetching | kIsHTTPBasedSource;
248}
249
250// static
251void ChromiumHTTPDataSource::InitiateRead(
252        ChromiumHTTPDataSource *me, void *data, size_t size) {
253    me->initiateRead(data, size);
254}
255
256void ChromiumHTTPDataSource::initiateRead(void *data, size_t size) {
257    mDelegate->initiateRead(data, size);
258}
259
260void ChromiumHTTPDataSource::onDisconnectComplete() {
261    Mutex::Autolock autoLock(mLock);
262    CHECK_EQ((int)mState, (int)DISCONNECTING);
263
264    mState = DISCONNECTED;
265    // mURI.clear();
266    mIOResult = -ENOTCONN;
267
268    mCondition.broadcast();
269}
270
271sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization(const char* mime) {
272    Mutex::Autolock autoLock(mLock);
273
274    if (mDrmManagerClient == NULL) {
275        mDrmManagerClient = new DrmManagerClient();
276    }
277
278    if (mDrmManagerClient == NULL) {
279        return NULL;
280    }
281
282    if (mDecryptHandle == NULL) {
283        /* Note if redirect occurs, mUri is the redirect uri instead of the
284         * original one
285         */
286        mDecryptHandle = mDrmManagerClient->openDecryptSession(
287                String8(mURI.c_str()), mime);
288    }
289
290    if (mDecryptHandle == NULL) {
291        delete mDrmManagerClient;
292        mDrmManagerClient = NULL;
293    }
294
295    return mDecryptHandle;
296}
297
298void ChromiumHTTPDataSource::getDrmInfo(
299        sp<DecryptHandle> &handle, DrmManagerClient **client) {
300    Mutex::Autolock autoLock(mLock);
301
302    handle = mDecryptHandle;
303    *client = mDrmManagerClient;
304}
305
306String8 ChromiumHTTPDataSource::getUri() {
307    Mutex::Autolock autoLock(mLock);
308
309    return String8(mURI.c_str());
310}
311
312String8 ChromiumHTTPDataSource::getMIMEType() const {
313    Mutex::Autolock autoLock(mLock);
314
315    return mContentType;
316}
317
318void ChromiumHTTPDataSource::clearDRMState_l() {
319    if (mDecryptHandle != NULL) {
320        // To release mDecryptHandle
321        CHECK(mDrmManagerClient);
322        mDrmManagerClient->closeDecryptSession(mDecryptHandle);
323        mDecryptHandle = NULL;
324    }
325}
326
327status_t ChromiumHTTPDataSource::reconnectAtOffset(off64_t offset) {
328    Mutex::Autolock autoLock(mLock);
329
330    if (mURI.empty()) {
331        return INVALID_OPERATION;
332    }
333
334    LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnecting...");
335    status_t err = connect_l(mURI.c_str(), &mHeaders, offset);
336    if (err != OK) {
337        LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnect failed w/ err 0x%08x", err);
338    }
339
340    return err;
341}
342
343// static
344status_t ChromiumHTTPDataSource::UpdateProxyConfig(
345        const char *host, int32_t port, const char *exclusionList) {
346    return SfDelegate::UpdateProxyConfig(host, port, exclusionList);
347}
348
349}  // namespace android
350
351