1/*
2 * Copyright (C) 2013 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 "MediaHTTP"
19#include <utils/Log.h>
20
21#include <media/stagefright/MediaHTTP.h>
22
23#include <binder/IServiceManager.h>
24#include <media/stagefright/foundation/ADebug.h>
25#include <media/stagefright/foundation/ALooper.h>
26#include <media/stagefright/Utils.h>
27
28#include <media/IMediaHTTPConnection.h>
29
30namespace android {
31
32MediaHTTP::MediaHTTP(const sp<IMediaHTTPConnection> &conn)
33    : mInitCheck(NO_INIT),
34      mHTTPConnection(conn),
35      mCachedSizeValid(false),
36      mCachedSize(0ll),
37      mDrmManagerClient(NULL) {
38    mInitCheck = OK;
39}
40
41MediaHTTP::~MediaHTTP() {
42    clearDRMState_l();
43}
44
45status_t MediaHTTP::connect(
46        const char *uri,
47        const KeyedVector<String8, String8> *headers,
48        off64_t /* offset */) {
49    if (mInitCheck != OK) {
50        return mInitCheck;
51    }
52
53    KeyedVector<String8, String8> extHeaders;
54    if (headers != NULL) {
55        extHeaders = *headers;
56    }
57    extHeaders.add(String8("User-Agent"), String8(MakeUserAgent().c_str()));
58
59    bool success = mHTTPConnection->connect(uri, &extHeaders);
60
61    mLastHeaders = extHeaders;
62    mLastURI = uri;
63
64    mCachedSizeValid = false;
65
66    return success ? OK : UNKNOWN_ERROR;
67}
68
69void MediaHTTP::disconnect() {
70    if (mInitCheck != OK) {
71        return;
72    }
73
74    mHTTPConnection->disconnect();
75}
76
77status_t MediaHTTP::initCheck() const {
78    return mInitCheck;
79}
80
81ssize_t MediaHTTP::readAt(off64_t offset, void *data, size_t size) {
82    if (mInitCheck != OK) {
83        return mInitCheck;
84    }
85
86    int64_t startTimeUs = ALooper::GetNowUs();
87
88    size_t numBytesRead = 0;
89    while (numBytesRead < size) {
90        size_t copy = size - numBytesRead;
91
92        if (copy > 64 * 1024) {
93            // limit the buffer sizes transferred across binder boundaries
94            // to avoid spurious transaction failures.
95            copy = 64 * 1024;
96        }
97
98        ssize_t n = mHTTPConnection->readAt(
99                offset + numBytesRead, (uint8_t *)data + numBytesRead, copy);
100
101        if (n < 0) {
102            return n;
103        } else if (n == 0) {
104            break;
105        }
106
107        numBytesRead += n;
108    }
109
110    int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
111
112    addBandwidthMeasurement(numBytesRead, delayUs);
113
114    return numBytesRead;
115}
116
117status_t MediaHTTP::getSize(off64_t *size) {
118    if (mInitCheck != OK) {
119        return mInitCheck;
120    }
121
122    // Caching the returned size so that it stays valid even after a
123    // disconnect. NuCachedSource2 relies on this.
124
125    if (!mCachedSizeValid) {
126        mCachedSize = mHTTPConnection->getSize();
127        mCachedSizeValid = true;
128    }
129
130    *size = mCachedSize;
131
132    return *size < 0 ? *size : OK;
133}
134
135uint32_t MediaHTTP::flags() {
136    return kWantsPrefetching | kIsHTTPBasedSource;
137}
138
139status_t MediaHTTP::reconnectAtOffset(off64_t offset) {
140    return connect(mLastURI.c_str(), &mLastHeaders, offset);
141}
142
143// DRM...
144
145sp<DecryptHandle> MediaHTTP::DrmInitialization(const char* mime) {
146    if (mDrmManagerClient == NULL) {
147        mDrmManagerClient = new DrmManagerClient();
148    }
149
150    if (mDrmManagerClient == NULL) {
151        return NULL;
152    }
153
154    if (mDecryptHandle == NULL) {
155        mDecryptHandle = mDrmManagerClient->openDecryptSession(
156                String8(mLastURI.c_str()), mime);
157    }
158
159    if (mDecryptHandle == NULL) {
160        delete mDrmManagerClient;
161        mDrmManagerClient = NULL;
162    }
163
164    return mDecryptHandle;
165}
166
167void MediaHTTP::getDrmInfo(
168        sp<DecryptHandle> &handle, DrmManagerClient **client) {
169    handle = mDecryptHandle;
170    *client = mDrmManagerClient;
171}
172
173String8 MediaHTTP::getUri() {
174    String8 uri;
175    if (OK == mHTTPConnection->getUri(&uri)) {
176        return uri;
177    }
178    return String8(mLastURI.c_str());
179}
180
181String8 MediaHTTP::getMIMEType() const {
182    if (mInitCheck != OK) {
183        return String8("application/octet-stream");
184    }
185
186    String8 mimeType;
187    status_t err = mHTTPConnection->getMIMEType(&mimeType);
188
189    if (err != OK) {
190        return String8("application/octet-stream");
191    }
192
193    return mimeType;
194}
195
196void MediaHTTP::clearDRMState_l() {
197    if (mDecryptHandle != NULL) {
198        // To release mDecryptHandle
199        CHECK(mDrmManagerClient);
200        mDrmManagerClient->closeDecryptSession(mDecryptHandle);
201        mDecryptHandle = NULL;
202    }
203}
204
205}  // namespace android
206