blob: 821ba9b9aa336cc8c5f715fe27510ca8f33013b0 [file] [log] [blame]
James Donge2693e5d2011-03-05 06:01:44 -08001/*
2 * Copyright (C) 2010 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
Andreas Huber163c4932010-06-10 11:08:43 -070017//#define LOG_NDEBUG 0
18#define LOG_TAG "NuHTTPDataSource"
19#include <utils/Log.h>
20
21#include "include/NuHTTPDataSource.h"
22
Andreas Huber3a53dc52010-06-11 09:57:46 -070023#include <cutils/properties.h>
Andreas Huber2381a8a2010-11-15 08:59:23 -080024#include <media/stagefright/foundation/ALooper.h>
Andreas Huber163c4932010-06-10 11:08:43 -070025#include <media/stagefright/MediaDebug.h>
26#include <media/stagefright/MediaErrors.h>
27
28namespace android {
29
30static bool ParseSingleUnsignedLong(
31 const char *from, unsigned long *x) {
32 char *end;
33 *x = strtoul(from, &end, 10);
34
35 if (end == from || *end != '\0') {
36 return false;
37 }
38
39 return true;
40}
41
42static bool ParseURL(
Andreas Huber118a1502011-02-17 13:35:08 -080043 const char *url, String8 *host, unsigned *port,
44 String8 *path, bool *https) {
Andreas Huber163c4932010-06-10 11:08:43 -070045 host->setTo("");
46 *port = 0;
47 path->setTo("");
48
Andreas Huber118a1502011-02-17 13:35:08 -080049 size_t hostStart;
50 if (!strncasecmp("http://", url, 7)) {
51 hostStart = 7;
52 *https = false;
53 } else if (!strncasecmp("https://", url, 8)) {
54 hostStart = 8;
55 *https = true;
56 } else {
Andreas Huber163c4932010-06-10 11:08:43 -070057 return false;
58 }
59
Andreas Huber118a1502011-02-17 13:35:08 -080060 const char *slashPos = strchr(&url[hostStart], '/');
Andreas Huber163c4932010-06-10 11:08:43 -070061
62 if (slashPos == NULL) {
Andreas Huber118a1502011-02-17 13:35:08 -080063 host->setTo(&url[hostStart]);
Andreas Huber163c4932010-06-10 11:08:43 -070064 path->setTo("/");
65 } else {
Andreas Huber118a1502011-02-17 13:35:08 -080066 host->setTo(&url[hostStart], slashPos - &url[hostStart]);
Andreas Huber163c4932010-06-10 11:08:43 -070067 path->setTo(slashPos);
68 }
69
Mike Lockwood5a23f8c2010-07-15 14:58:25 -040070 const char *colonPos = strchr(host->string(), ':');
Andreas Huber163c4932010-06-10 11:08:43 -070071
72 if (colonPos != NULL) {
73 unsigned long x;
74 if (!ParseSingleUnsignedLong(colonPos + 1, &x) || x >= 65536) {
75 return false;
76 }
77
78 *port = x;
79
80 size_t colonOffset = colonPos - host->string();
81 String8 tmp(host->string(), colonOffset);
82 *host = tmp;
83 } else {
Andreas Huber118a1502011-02-17 13:35:08 -080084 *port = (*https) ? 443 : 80;
Andreas Huber163c4932010-06-10 11:08:43 -070085 }
86
87 return true;
88}
89
Andreas Huber53182c42011-02-24 14:42:48 -080090NuHTTPDataSource::NuHTTPDataSource(uint32_t flags)
91 : mFlags(flags),
92 mState(DISCONNECTED),
Andreas Huber163c4932010-06-10 11:08:43 -070093 mPort(0),
Andreas Huber118a1502011-02-17 13:35:08 -080094 mHTTPS(false),
Andreas Huber163c4932010-06-10 11:08:43 -070095 mOffset(0),
96 mContentLength(0),
Gloria Wangc2c22e72010-11-01 15:53:16 -070097 mContentLengthValid(false),
Andreas Huber9d202762010-11-18 11:03:48 -080098 mHasChunkedTransferEncoding(false),
99 mChunkDataBytesLeft(0),
Andreas Huber2381a8a2010-11-15 08:59:23 -0800100 mNumBandwidthHistoryItems(0),
101 mTotalTransferTimeUs(0),
102 mTotalTransferBytes(0),
Gloria Wangc2c22e72010-11-01 15:53:16 -0700103 mDecryptHandle(NULL),
104 mDrmManagerClient(NULL) {
Andreas Huber163c4932010-06-10 11:08:43 -0700105}
106
107NuHTTPDataSource::~NuHTTPDataSource() {
Gloria Wangc2dc4722011-02-07 11:41:11 -0800108 if (mDecryptHandle != NULL) {
109 // To release mDecryptHandle
Gloria Wangadc4d9c2011-02-08 13:24:08 -0800110 CHECK(mDrmManagerClient);
Gloria Wangc2dc4722011-02-07 11:41:11 -0800111 mDrmManagerClient->closeDecryptSession(mDecryptHandle);
112 mDecryptHandle = NULL;
113 }
114
115 if (mDrmManagerClient != NULL) {
116 delete mDrmManagerClient;
117 mDrmManagerClient = NULL;
118 }
Andreas Huber163c4932010-06-10 11:08:43 -0700119}
120
Andreas Huber3a53dc52010-06-11 09:57:46 -0700121status_t NuHTTPDataSource::connect(
122 const char *uri,
123 const KeyedVector<String8, String8> *overrides,
James Dongb1262a82010-11-16 14:04:54 -0800124 off64_t offset) {
Andreas Huber3a53dc52010-06-11 09:57:46 -0700125 String8 headers;
126 MakeFullHeaders(overrides, &headers);
127
128 return connect(uri, headers, offset);
129}
130
131status_t NuHTTPDataSource::connect(
132 const char *uri,
133 const String8 &headers,
James Dongb1262a82010-11-16 14:04:54 -0800134 off64_t offset) {
Andreas Huber163c4932010-06-10 11:08:43 -0700135 String8 host, path;
136 unsigned port;
Gloria Wangc2c22e72010-11-01 15:53:16 -0700137
138 mUri = uri;
Andreas Hubera2e57ca2011-03-30 11:15:27 -0700139 mContentType = String8("application/octet-stream");
Gloria Wangc2c22e72010-11-01 15:53:16 -0700140
Andreas Huber118a1502011-02-17 13:35:08 -0800141 bool https;
142 if (!ParseURL(uri, &host, &port, &path, &https)) {
Andreas Huber163c4932010-06-10 11:08:43 -0700143 return ERROR_MALFORMED;
144 }
145
Andreas Huber118a1502011-02-17 13:35:08 -0800146 return connect(host, port, path, https, headers, offset);
Andreas Huber163c4932010-06-10 11:08:43 -0700147}
148
Andreas Huberab2116c2010-09-27 08:17:40 -0700149static bool IsRedirectStatusCode(int httpStatus) {
150 return httpStatus == 301 || httpStatus == 302
151 || httpStatus == 303 || httpStatus == 307;
152}
153
Andreas Huber163c4932010-06-10 11:08:43 -0700154status_t NuHTTPDataSource::connect(
Andreas Huber3a53dc52010-06-11 09:57:46 -0700155 const char *host, unsigned port, const char *path,
Andreas Huber118a1502011-02-17 13:35:08 -0800156 bool https,
Andreas Huber3a53dc52010-06-11 09:57:46 -0700157 const String8 &headers,
James Dongb1262a82010-11-16 14:04:54 -0800158 off64_t offset) {
Andreas Huber53182c42011-02-24 14:42:48 -0800159 if (!(mFlags & kFlagIncognito)) {
160 LOGI("connect to %s:%u%s @%lld", host, port, path, offset);
161 } else {
162 LOGI("connect to <URL suppressed> @%lld", offset);
163 }
Andreas Huber163c4932010-06-10 11:08:43 -0700164
165 bool needsToReconnect = true;
166
167 if (mState == CONNECTED && host == mHost && port == mPort
Andreas Huber118a1502011-02-17 13:35:08 -0800168 && https == mHTTPS && offset == mOffset) {
Andreas Huber163c4932010-06-10 11:08:43 -0700169 if (mContentLengthValid && mOffset == mContentLength) {
170 LOGI("Didn't have to reconnect, old one's still good.");
171 needsToReconnect = false;
172 }
173 }
174
175 mHost = host;
176 mPort = port;
177 mPath = path;
Andreas Huber118a1502011-02-17 13:35:08 -0800178 mHTTPS = https;
Andreas Huber3a53dc52010-06-11 09:57:46 -0700179 mHeaders = headers;
Andreas Huber163c4932010-06-10 11:08:43 -0700180
181 status_t err = OK;
182
183 mState = CONNECTING;
184
185 if (needsToReconnect) {
186 mHTTP.disconnect();
Andreas Huber118a1502011-02-17 13:35:08 -0800187 err = mHTTP.connect(host, port, https);
Andreas Huber163c4932010-06-10 11:08:43 -0700188 }
189
190 if (err != OK) {
191 mState = DISCONNECTED;
192 } else if (mState != CONNECTING) {
193 err = UNKNOWN_ERROR;
194 } else {
195 mState = CONNECTED;
196
197 mOffset = offset;
198 mContentLength = 0;
199 mContentLengthValid = false;
200
201 String8 request("GET ");
202 request.append(mPath);
203 request.append(" HTTP/1.1\r\n");
204 request.append("Host: ");
205 request.append(mHost);
Andreas Huber3abb7dd2010-12-16 10:45:24 -0800206 if (mPort != 80) {
207 request.append(StringPrintf(":%u", mPort).c_str());
208 }
Andreas Huber163c4932010-06-10 11:08:43 -0700209 request.append("\r\n");
210
211 if (offset != 0) {
212 char rangeHeader[128];
James Dongb1262a82010-11-16 14:04:54 -0800213 sprintf(rangeHeader, "Range: bytes=%lld-\r\n", offset);
Andreas Huber163c4932010-06-10 11:08:43 -0700214 request.append(rangeHeader);
215 }
216
Andreas Huber3a53dc52010-06-11 09:57:46 -0700217 request.append(mHeaders);
Andreas Huber163c4932010-06-10 11:08:43 -0700218 request.append("\r\n");
219
220 int httpStatus;
221 if ((err = mHTTP.send(request.string(), request.size())) != OK
222 || (err = mHTTP.receive_header(&httpStatus)) != OK) {
223 mHTTP.disconnect();
224 mState = DISCONNECTED;
225 return err;
226 }
227
Andreas Huberab2116c2010-09-27 08:17:40 -0700228 if (IsRedirectStatusCode(httpStatus)) {
Andreas Huber61c79b62010-11-19 09:36:13 -0800229 AString value;
Andreas Huber163c4932010-06-10 11:08:43 -0700230 CHECK(mHTTP.find_header_value("Location", &value));
231
232 mState = DISCONNECTED;
233
234 mHTTP.disconnect();
235
Andreas Huber3a53dc52010-06-11 09:57:46 -0700236 return connect(value.c_str(), headers, offset);
Andreas Huber163c4932010-06-10 11:08:43 -0700237 }
238
Andreas Huber3a53dc52010-06-11 09:57:46 -0700239 if (httpStatus < 200 || httpStatus >= 300) {
240 mState = DISCONNECTED;
241 mHTTP.disconnect();
242
243 return ERROR_IO;
244 }
245
Andreas Huber9d202762010-11-18 11:03:48 -0800246 mHasChunkedTransferEncoding = false;
247
Andreas Huber2381a8a2010-11-15 08:59:23 -0800248 {
Andreas Huber61c79b62010-11-19 09:36:13 -0800249 AString value;
250 if (mHTTP.find_header_value("Transfer-Encoding", &value)) {
Andreas Huber9d202762010-11-18 11:03:48 -0800251 // We don't currently support any transfer encodings but
252 // chunked.
Andreas Huber2381a8a2010-11-15 08:59:23 -0800253
Andreas Huber9d202762010-11-18 11:03:48 -0800254 if (!strcasecmp(value.c_str(), "chunked")) {
255 LOGI("Chunked transfer encoding applied.");
256 mHasChunkedTransferEncoding = true;
257 mChunkDataBytesLeft = 0;
258 } else {
259 mState = DISCONNECTED;
260 mHTTP.disconnect();
Andreas Huber2381a8a2010-11-15 08:59:23 -0800261
Andreas Huber9d202762010-11-18 11:03:48 -0800262 LOGE("We don't support '%s' transfer encoding.", value.c_str());
Andreas Huber2381a8a2010-11-15 08:59:23 -0800263
Andreas Huber9d202762010-11-18 11:03:48 -0800264 return ERROR_UNSUPPORTED;
265 }
Andreas Huber2381a8a2010-11-15 08:59:23 -0800266 }
267 }
268
Andreas Hubera2e57ca2011-03-30 11:15:27 -0700269 {
270 AString value;
271 if (mHTTP.find_header_value("Content-Type", &value)) {
272 mContentType = String8(value.c_str());
273 } else {
274 mContentType = String8("application/octet-stream");
275 }
276 }
277
Andreas Huber3a53dc52010-06-11 09:57:46 -0700278 applyTimeoutResponse();
Andreas Huber163c4932010-06-10 11:08:43 -0700279
280 if (offset == 0) {
Andreas Huber61c79b62010-11-19 09:36:13 -0800281 AString value;
Andreas Huber163c4932010-06-10 11:08:43 -0700282 unsigned long x;
Andreas Huber61c79b62010-11-19 09:36:13 -0800283 if (mHTTP.find_header_value(AString("Content-Length"), &value)
Andreas Huber163c4932010-06-10 11:08:43 -0700284 && ParseSingleUnsignedLong(value.c_str(), &x)) {
James Dongb1262a82010-11-16 14:04:54 -0800285 mContentLength = (off64_t)x;
Andreas Huber163c4932010-06-10 11:08:43 -0700286 mContentLengthValid = true;
Andreas Huber9d202762010-11-18 11:03:48 -0800287 } else {
288 LOGW("Server did not give us the content length!");
Andreas Huber163c4932010-06-10 11:08:43 -0700289 }
290 } else {
Andreas Huber9d202762010-11-18 11:03:48 -0800291 if (httpStatus != 206 /* Partial Content */) {
292 // We requested a range but the server didn't support that.
293 LOGE("We requested a range but the server didn't "
294 "support that.");
295 return ERROR_UNSUPPORTED;
296 }
297
Andreas Huber61c79b62010-11-19 09:36:13 -0800298 AString value;
Andreas Huber163c4932010-06-10 11:08:43 -0700299 unsigned long x;
Andreas Huber61c79b62010-11-19 09:36:13 -0800300 if (mHTTP.find_header_value(AString("Content-Range"), &value)) {
Andreas Huber163c4932010-06-10 11:08:43 -0700301 const char *slashPos = strchr(value.c_str(), '/');
302 if (slashPos != NULL
303 && ParseSingleUnsignedLong(slashPos + 1, &x)) {
304 mContentLength = x;
305 mContentLengthValid = true;
306 }
307 }
308 }
309 }
310
311 return err;
312}
313
314void NuHTTPDataSource::disconnect() {
315 if (mState == CONNECTING || mState == CONNECTED) {
316 mHTTP.disconnect();
317 }
318 mState = DISCONNECTED;
319}
320
321status_t NuHTTPDataSource::initCheck() const {
322 return mState == CONNECTED ? OK : NO_INIT;
323}
324
Andreas Huber9d202762010-11-18 11:03:48 -0800325ssize_t NuHTTPDataSource::internalRead(void *data, size_t size) {
326 if (!mHasChunkedTransferEncoding) {
327 return mHTTP.receive(data, size);
328 }
329
330 if (mChunkDataBytesLeft < 0) {
331 return 0;
332 } else if (mChunkDataBytesLeft == 0) {
333 char line[1024];
334 status_t err = mHTTP.receive_line(line, sizeof(line));
335
336 if (err != OK) {
337 return err;
338 }
339
340 LOGV("line = '%s'", line);
341
342 char *end;
343 unsigned long n = strtoul(line, &end, 16);
344
345 if (end == line || (*end != ';' && *end != '\0')) {
346 LOGE("malformed HTTP chunk '%s'", line);
347 return ERROR_MALFORMED;
348 }
349
350 mChunkDataBytesLeft = n;
351 LOGV("chunk data size = %lu", n);
352
353 if (mChunkDataBytesLeft == 0) {
354 mChunkDataBytesLeft = -1;
355 return 0;
356 }
357
358 // fall through
359 }
360
361 if (size > (size_t)mChunkDataBytesLeft) {
362 size = mChunkDataBytesLeft;
363 }
364
365 ssize_t n = mHTTP.receive(data, size);
366
367 if (n < 0) {
368 return n;
369 }
370
371 mChunkDataBytesLeft -= (size_t)n;
372
373 if (mChunkDataBytesLeft == 0) {
374 char line[1024];
375 status_t err = mHTTP.receive_line(line, sizeof(line));
376
377 if (err != OK) {
378 return err;
379 }
380
381 if (line[0] != '\0') {
382 LOGE("missing HTTP chunk terminator.");
383 return ERROR_MALFORMED;
384 }
385 }
386
387 return n;
388}
389
James Dongb1262a82010-11-16 14:04:54 -0800390ssize_t NuHTTPDataSource::readAt(off64_t offset, void *data, size_t size) {
Andreas Huber163c4932010-06-10 11:08:43 -0700391 LOGV("readAt offset %ld, size %d", offset, size);
392
393 Mutex::Autolock autoLock(mLock);
394
395 if (offset != mOffset) {
396 String8 host = mHost;
397 String8 path = mPath;
Andreas Huber3a53dc52010-06-11 09:57:46 -0700398 String8 headers = mHeaders;
Andreas Huber118a1502011-02-17 13:35:08 -0800399 status_t err = connect(host, mPort, path, mHTTPS, headers, offset);
Andreas Huber163c4932010-06-10 11:08:43 -0700400
401 if (err != OK) {
402 return err;
403 }
404 }
405
406 if (mContentLengthValid) {
407 size_t avail =
408 (offset >= mContentLength) ? 0 : mContentLength - offset;
409
410 if (size > avail) {
411 size = avail;
412 }
413 }
414
415 size_t numBytesRead = 0;
416 while (numBytesRead < size) {
Andreas Huber2381a8a2010-11-15 08:59:23 -0800417 int64_t startTimeUs = ALooper::GetNowUs();
418
Andreas Huber163c4932010-06-10 11:08:43 -0700419 ssize_t n =
Andreas Huber9d202762010-11-18 11:03:48 -0800420 internalRead((uint8_t *)data + numBytesRead, size - numBytesRead);
Andreas Huber163c4932010-06-10 11:08:43 -0700421
422 if (n < 0) {
Andreas Huber45ad57a2011-03-30 14:45:22 -0700423 if (numBytesRead == 0 || mContentLengthValid) {
424 return n;
425 }
426
427 // If there was an error we want to at least return the data
428 // we've already successfully read. The next call to read will
429 // then return the error.
430 n = 0;
Andreas Huber163c4932010-06-10 11:08:43 -0700431 }
432
Andreas Huber2381a8a2010-11-15 08:59:23 -0800433 int64_t delayUs = ALooper::GetNowUs() - startTimeUs;
434 addBandwidthMeasurement_l(n, delayUs);
435
Andreas Huber163c4932010-06-10 11:08:43 -0700436 numBytesRead += (size_t)n;
437
438 if (n == 0) {
439 if (mContentLengthValid) {
440 // We know the content length and made sure not to read beyond
441 // it and yet the server closed the connection on us.
442 return ERROR_IO;
443 }
444
445 break;
446 }
447 }
448
449 mOffset += numBytesRead;
450
451 return numBytesRead;
452}
453
James Dongb1262a82010-11-16 14:04:54 -0800454status_t NuHTTPDataSource::getSize(off64_t *size) {
Andreas Huber163c4932010-06-10 11:08:43 -0700455 *size = 0;
456
457 if (mState != CONNECTED) {
458 return ERROR_IO;
459 }
460
461 if (mContentLengthValid) {
462 *size = mContentLength;
463 return OK;
464 }
465
466 return ERROR_UNSUPPORTED;
467}
468
469uint32_t NuHTTPDataSource::flags() {
470 return kWantsPrefetching;
471}
472
Andreas Huber3a53dc52010-06-11 09:57:46 -0700473// static
474void NuHTTPDataSource::MakeFullHeaders(
475 const KeyedVector<String8, String8> *overrides, String8 *headers) {
476 headers->setTo("");
477
478 headers->append("User-Agent: stagefright/1.1 (Linux;Android ");
479
480#if (PROPERTY_VALUE_MAX < 8)
481#error "PROPERTY_VALUE_MAX must be at least 8"
482#endif
483
484 char value[PROPERTY_VALUE_MAX];
485 property_get("ro.build.version.release", value, "Unknown");
486 headers->append(value);
487 headers->append(")\r\n");
488
489 if (overrides == NULL) {
490 return;
491 }
492
493 for (size_t i = 0; i < overrides->size(); ++i) {
494 String8 line;
495 line.append(overrides->keyAt(i));
496 line.append(": ");
497 line.append(overrides->valueAt(i));
498 line.append("\r\n");
499
500 headers->append(line);
501 }
502}
503
504void NuHTTPDataSource::applyTimeoutResponse() {
Andreas Huber61c79b62010-11-19 09:36:13 -0800505 AString timeout;
Andreas Huber3a53dc52010-06-11 09:57:46 -0700506 if (mHTTP.find_header_value("X-SocketTimeout", &timeout)) {
507 const char *s = timeout.c_str();
508 char *end;
509 long tmp = strtol(s, &end, 10);
510 if (end == s || *end != '\0') {
511 LOGW("Illegal X-SocketTimeout value given.");
512 return;
513 }
514
515 LOGI("overriding default timeout, new timeout is %ld seconds", tmp);
516 mHTTP.setReceiveTimeout(tmp);
517 }
518}
519
Andreas Huber2381a8a2010-11-15 08:59:23 -0800520bool NuHTTPDataSource::estimateBandwidth(int32_t *bandwidth_bps) {
521 Mutex::Autolock autoLock(mLock);
522
Andreas Huberb5590842010-12-03 16:12:25 -0800523 if (mNumBandwidthHistoryItems < 2) {
Andreas Huber2381a8a2010-11-15 08:59:23 -0800524 return false;
525 }
526
527 *bandwidth_bps = ((double)mTotalTransferBytes * 8E6 / mTotalTransferTimeUs);
528
529 return true;
530}
531
532void NuHTTPDataSource::addBandwidthMeasurement_l(
533 size_t numBytes, int64_t delayUs) {
534 BandwidthEntry entry;
535 entry.mDelayUs = delayUs;
536 entry.mNumBytes = numBytes;
537 mTotalTransferTimeUs += delayUs;
538 mTotalTransferBytes += numBytes;
539
540 mBandwidthHistory.push_back(entry);
541 if (++mNumBandwidthHistoryItems > 100) {
542 BandwidthEntry *entry = &*mBandwidthHistory.begin();
543 mTotalTransferTimeUs -= entry->mDelayUs;
544 mTotalTransferBytes -= entry->mNumBytes;
545 mBandwidthHistory.erase(mBandwidthHistory.begin());
546 --mNumBandwidthHistoryItems;
547 }
548}
549
Gloria Wangae775272011-02-24 16:40:57 -0800550sp<DecryptHandle> NuHTTPDataSource::DrmInitialization() {
Gloria Wangc2dc4722011-02-07 11:41:11 -0800551 if (mDrmManagerClient == NULL) {
552 mDrmManagerClient = new DrmManagerClient();
553 }
554
555 if (mDrmManagerClient == NULL) {
Gloria Wangc2c22e72010-11-01 15:53:16 -0700556 return NULL;
557 }
Gloria Wangc2c22e72010-11-01 15:53:16 -0700558
559 if (mDecryptHandle == NULL) {
560 /* Note if redirect occurs, mUri is the redirect uri instead of the
561 * original one
562 */
563 mDecryptHandle = mDrmManagerClient->openDecryptSession(mUri);
564 }
565
566 if (mDecryptHandle == NULL) {
Gloria Wangc2dc4722011-02-07 11:41:11 -0800567 delete mDrmManagerClient;
Gloria Wangc2c22e72010-11-01 15:53:16 -0700568 mDrmManagerClient = NULL;
569 }
570
571 return mDecryptHandle;
572}
573
Gloria Wangae775272011-02-24 16:40:57 -0800574void NuHTTPDataSource::getDrmInfo(sp<DecryptHandle> &handle, DrmManagerClient **client) {
575 handle = mDecryptHandle;
Gloria Wangc2c22e72010-11-01 15:53:16 -0700576
577 *client = mDrmManagerClient;
578}
579
Gloria Wang43cd12d2010-11-09 15:06:51 -0800580String8 NuHTTPDataSource::getUri() {
581 return mUri;
582}
583
Andreas Hubera2e57ca2011-03-30 11:15:27 -0700584String8 NuHTTPDataSource::getMIMEType() const {
585 return mContentType;
586}
587
Andreas Huber163c4932010-06-10 11:08:43 -0700588} // namespace android