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