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    LOG_PRI(ANDROID_LOG_VERBOSE, LOG_TAG, "connect on behalf of uid %d", uid);
69
70    return connect_l(uri, headers, offset);
71}
72
73status_t ChromiumHTTPDataSource::connect_l(
74        const char *uri,
75        const KeyedVector<String8, String8> *headers,
76        off64_t offset) {
77    if (mState != DISCONNECTED) {
78        disconnect_l();
79    }
80
81    if (!(mFlags & kFlagIncognito)) {
82        LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "connect to %s @%lld", uri, offset);
83    } else {
84        LOG_PRI(ANDROID_LOG_INFO, LOG_TAG,
85                "connect to <URL suppressed> @%lld", offset);
86    }
87
88    mURI = uri;
89    mContentType = String8("application/octet-stream");
90
91    if (headers != NULL) {
92        mHeaders = *headers;
93    } else {
94        mHeaders.clear();
95    }
96
97    mState = CONNECTING;
98    mContentSize = -1;
99    mCurrentOffset = offset;
100
101    mDelegate->initiateConnection(mURI.c_str(), &mHeaders, offset);
102
103    while (mState == CONNECTING) {
104        mCondition.wait(mLock);
105    }
106
107    return mState == CONNECTED ? OK : mIOResult;
108}
109
110void ChromiumHTTPDataSource::onConnectionEstablished(
111        int64_t contentSize, const char *contentType) {
112    Mutex::Autolock autoLock(mLock);
113    mState = CONNECTED;
114    mContentSize = (contentSize < 0) ? -1 : contentSize + mCurrentOffset;
115    mContentType = String8(contentType);
116    mCondition.broadcast();
117}
118
119void ChromiumHTTPDataSource::onConnectionFailed(status_t err) {
120    Mutex::Autolock autoLock(mLock);
121    mState = DISCONNECTED;
122    mCondition.broadcast();
123
124    // mURI.clear();
125
126    mIOResult = err;
127}
128
129void ChromiumHTTPDataSource::disconnect() {
130    Mutex::Autolock autoLock(mLock);
131    disconnect_l();
132}
133
134void ChromiumHTTPDataSource::disconnect_l() {
135    if (mState == DISCONNECTED) {
136        return;
137    }
138
139    mState = DISCONNECTING;
140    mIOResult = -EINTR;
141
142    mDelegate->initiateDisconnect();
143
144    while (mState == DISCONNECTING) {
145        mCondition.wait(mLock);
146    }
147
148    CHECK_EQ((int)mState, (int)DISCONNECTED);
149}
150
151status_t ChromiumHTTPDataSource::initCheck() const {
152    Mutex::Autolock autoLock(mLock);
153
154    return mState == CONNECTED ? OK : NO_INIT;
155}
156
157ssize_t ChromiumHTTPDataSource::readAt(off64_t offset, void *data, size_t size) {
158    Mutex::Autolock autoLock(mLock);
159
160    if (mState != CONNECTED) {
161        return INVALID_OPERATION;
162    }
163
164#if 0
165    char value[PROPERTY_VALUE_MAX];
166    if (property_get("media.stagefright.disable-net", value, 0)
167            && (!strcasecmp(value, "true") || !strcmp(value, "1"))) {
168        LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Simulating that the network is down.");
169        disconnect_l();
170        return ERROR_IO;
171    }
172#endif
173
174    if (offset != mCurrentOffset) {
175        AString tmp = mURI;
176        KeyedVector<String8, String8> tmpHeaders = mHeaders;
177
178        disconnect_l();
179
180        status_t err = connect_l(tmp.c_str(), &tmpHeaders, offset);
181
182        if (err != OK) {
183            return err;
184        }
185    }
186
187    mState = READING;
188
189    int64_t startTimeUs = ALooper::GetNowUs();
190
191    mDelegate->initiateRead(data, size);
192
193    while (mState == READING) {
194        mCondition.wait(mLock);
195    }
196
197    if (mIOResult < OK) {
198        return mIOResult;
199    }
200
201    if (mState == CONNECTED) {
202        int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
203
204        // The read operation was successful, mIOResult contains
205        // the number of bytes read.
206        addBandwidthMeasurement(mIOResult, delayUs);
207
208        mCurrentOffset += mIOResult;
209        return mIOResult;
210    }
211
212    return ERROR_IO;
213}
214
215void ChromiumHTTPDataSource::onReadCompleted(ssize_t size) {
216    Mutex::Autolock autoLock(mLock);
217
218    mIOResult = size;
219
220    if (mState == READING) {
221        mState = CONNECTED;
222        mCondition.broadcast();
223    }
224}
225
226status_t ChromiumHTTPDataSource::getSize(off64_t *size) {
227    Mutex::Autolock autoLock(mLock);
228
229    if (mContentSize < 0) {
230        return ERROR_UNSUPPORTED;
231    }
232
233    *size = mContentSize;
234
235    return OK;
236}
237
238uint32_t ChromiumHTTPDataSource::flags() {
239    return kWantsPrefetching | kIsHTTPBasedSource;
240}
241
242// static
243void ChromiumHTTPDataSource::InitiateRead(
244        ChromiumHTTPDataSource *me, void *data, size_t size) {
245    me->initiateRead(data, size);
246}
247
248void ChromiumHTTPDataSource::initiateRead(void *data, size_t size) {
249    mDelegate->initiateRead(data, size);
250}
251
252void ChromiumHTTPDataSource::onDisconnectComplete() {
253    Mutex::Autolock autoLock(mLock);
254    CHECK_EQ((int)mState, (int)DISCONNECTING);
255
256    mState = DISCONNECTED;
257    // mURI.clear();
258
259    mCondition.broadcast();
260}
261
262sp<DecryptHandle> ChromiumHTTPDataSource::DrmInitialization() {
263    Mutex::Autolock autoLock(mLock);
264
265    if (mDrmManagerClient == NULL) {
266        mDrmManagerClient = new DrmManagerClient();
267    }
268
269    if (mDrmManagerClient == NULL) {
270        return NULL;
271    }
272
273    if (mDecryptHandle == NULL) {
274        /* Note if redirect occurs, mUri is the redirect uri instead of the
275         * original one
276         */
277        mDecryptHandle = mDrmManagerClient->openDecryptSession(
278                String8(mURI.c_str()));
279    }
280
281    if (mDecryptHandle == NULL) {
282        delete mDrmManagerClient;
283        mDrmManagerClient = NULL;
284    }
285
286    return mDecryptHandle;
287}
288
289void ChromiumHTTPDataSource::getDrmInfo(
290        sp<DecryptHandle> &handle, DrmManagerClient **client) {
291    Mutex::Autolock autoLock(mLock);
292
293    handle = mDecryptHandle;
294    *client = mDrmManagerClient;
295}
296
297String8 ChromiumHTTPDataSource::getUri() {
298    Mutex::Autolock autoLock(mLock);
299
300    return String8(mURI.c_str());
301}
302
303String8 ChromiumHTTPDataSource::getMIMEType() const {
304    Mutex::Autolock autoLock(mLock);
305
306    return mContentType;
307}
308
309void ChromiumHTTPDataSource::clearDRMState_l() {
310    if (mDecryptHandle != NULL) {
311        // To release mDecryptHandle
312        CHECK(mDrmManagerClient);
313        mDrmManagerClient->closeDecryptSession(mDecryptHandle);
314        mDecryptHandle = NULL;
315    }
316}
317
318status_t ChromiumHTTPDataSource::reconnectAtOffset(off64_t offset) {
319    Mutex::Autolock autoLock(mLock);
320
321    if (mURI.empty()) {
322        return INVALID_OPERATION;
323    }
324
325    LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnecting...");
326    status_t err = connect_l(mURI.c_str(), &mHeaders, offset);
327    if (err != OK) {
328        LOG_PRI(ANDROID_LOG_INFO, LOG_TAG, "Reconnect failed w/ err 0x%08x", err);
329    }
330
331    return err;
332}
333
334}  // namespace android
335
336