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