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