1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "content/browser/media/android/media_resource_getter_impl.h" 6 7 #include "base/android/jni_android.h" 8 #include "base/android/jni_string.h" 9 #include "base/bind.h" 10 #include "base/path_service.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "content/browser/child_process_security_policy_impl.h" 13 #include "content/browser/fileapi/browser_file_system_helper.h" 14 #include "content/browser/fileapi/chrome_blob_storage_context.h" 15 #include "content/public/browser/browser_context.h" 16 #include "content/public/browser/browser_thread.h" 17 #include "content/public/browser/content_browser_client.h" 18 #include "content/public/common/content_client.h" 19 #include "content/public/common/url_constants.h" 20 #include "jni/MediaResourceGetter_jni.h" 21 #include "media/base/android/media_url_interceptor.h" 22 #include "net/base/auth.h" 23 #include "net/cookies/cookie_monster.h" 24 #include "net/cookies/cookie_store.h" 25 #include "net/http/http_auth.h" 26 #include "net/http/http_transaction_factory.h" 27 #include "net/url_request/url_request_context.h" 28 #include "net/url_request/url_request_context_getter.h" 29 #include "storage/browser/blob/blob_data_handle.h" 30 #include "storage/browser/blob/blob_storage_context.h" 31 #include "url/gurl.h" 32 33 using base::android::ConvertUTF8ToJavaString; 34 using base::android::ScopedJavaLocalRef; 35 36 namespace content { 37 38 static void ReturnResultOnUIThread( 39 const base::Callback<void(const std::string&)>& callback, 40 const std::string& result) { 41 BrowserThread::PostTask( 42 BrowserThread::UI, FROM_HERE, base::Bind(callback, result)); 43 } 44 45 static void RequestPlatformPathFromBlobURL( 46 const GURL& url, 47 BrowserContext* browser_context, 48 const base::Callback<void(const std::string&)>& callback) { 49 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 50 ChromeBlobStorageContext* context = 51 ChromeBlobStorageContext::GetFor(browser_context); 52 scoped_ptr<storage::BlobDataHandle> handle = 53 context->context()->GetBlobDataFromPublicURL(url); 54 storage::BlobData* data = handle->data(); 55 if (!data) { 56 ReturnResultOnUIThread(callback, ""); 57 NOTREACHED(); 58 return; 59 } 60 const std::vector<storage::BlobData::Item> items = data->items(); 61 62 // TODO(qinmin): handle the case when the blob data is not a single file. 63 DLOG_IF(WARNING, items.size() != 1u) 64 << "More than one blob data are present: " << items.size(); 65 ReturnResultOnUIThread(callback, items[0].path().value()); 66 } 67 68 static void RequestPlaformPathFromFileSystemURL( 69 const GURL& url, 70 int render_process_id, 71 scoped_refptr<storage::FileSystemContext> file_system_context, 72 const base::Callback<void(const std::string&)>& callback) { 73 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE)); 74 base::FilePath platform_path; 75 SyncGetPlatformPath(file_system_context.get(), 76 render_process_id, 77 url, 78 &platform_path); 79 base::FilePath data_storage_path; 80 PathService::Get(base::DIR_ANDROID_APP_DATA, &data_storage_path); 81 if (data_storage_path.IsParent(platform_path)) 82 ReturnResultOnUIThread(callback, platform_path.value()); 83 else 84 ReturnResultOnUIThread(callback, std::string()); 85 } 86 87 // Posts a task to the UI thread to run the callback function. 88 static void PostMediaMetadataCallbackTask( 89 const media::MediaResourceGetter::ExtractMediaMetadataCB& callback, 90 JNIEnv* env, ScopedJavaLocalRef<jobject>& j_metadata) { 91 BrowserThread::PostTask( 92 BrowserThread::UI, FROM_HERE, 93 base::Bind(callback, base::TimeDelta::FromMilliseconds( 94 Java_MediaMetadata_getDurationInMilliseconds( 95 env, j_metadata.obj())), 96 Java_MediaMetadata_getWidth(env, j_metadata.obj()), 97 Java_MediaMetadata_getHeight(env, j_metadata.obj()), 98 Java_MediaMetadata_isSuccess(env, j_metadata.obj()))); 99 } 100 101 // Gets the metadata from a media URL. When finished, a task is posted to the UI 102 // thread to run the callback function. 103 static void GetMediaMetadata( 104 const std::string& url, const std::string& cookies, 105 const std::string& user_agent, 106 const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) { 107 JNIEnv* env = base::android::AttachCurrentThread(); 108 109 ScopedJavaLocalRef<jstring> j_url_string = ConvertUTF8ToJavaString(env, url); 110 ScopedJavaLocalRef<jstring> j_cookies = ConvertUTF8ToJavaString(env, cookies); 111 jobject j_context = base::android::GetApplicationContext(); 112 ScopedJavaLocalRef<jstring> j_user_agent = ConvertUTF8ToJavaString( 113 env, user_agent); 114 ScopedJavaLocalRef<jobject> j_metadata = 115 Java_MediaResourceGetter_extractMediaMetadata(env, 116 j_context, 117 j_url_string.obj(), 118 j_cookies.obj(), 119 j_user_agent.obj()); 120 121 PostMediaMetadataCallbackTask(callback, env, j_metadata); 122 } 123 124 // Gets the metadata from a file descriptor. When finished, a task is posted to 125 // the UI thread to run the callback function. 126 static void GetMediaMetadataFromFd( 127 const int fd, const int64 offset, const int64 size, 128 const media::MediaResourceGetter::ExtractMediaMetadataCB& callback) { 129 JNIEnv* env = base::android::AttachCurrentThread(); 130 131 ScopedJavaLocalRef<jobject> j_metadata = 132 Java_MediaResourceGetter_extractMediaMetadataFromFd( 133 env, fd, offset, size); 134 135 PostMediaMetadataCallbackTask(callback, env, j_metadata); 136 } 137 138 // The task object that retrieves media resources on the IO thread. 139 // TODO(qinmin): refactor this class to make the code reusable by others as 140 // there are lots of duplicated functionalities elsewhere. 141 // http://crbug.com/395762. 142 class MediaResourceGetterTask 143 : public base::RefCountedThreadSafe<MediaResourceGetterTask> { 144 public: 145 MediaResourceGetterTask(BrowserContext* browser_context, 146 int render_process_id, int render_frame_id); 147 148 // Called by MediaResourceGetterImpl to start getting auth credentials. 149 net::AuthCredentials RequestAuthCredentials(const GURL& url) const; 150 151 // Called by MediaResourceGetterImpl to start getting cookies for a URL. 152 void RequestCookies( 153 const GURL& url, const GURL& first_party_for_cookies, 154 const media::MediaResourceGetter::GetCookieCB& callback); 155 156 private: 157 friend class base::RefCountedThreadSafe<MediaResourceGetterTask>; 158 virtual ~MediaResourceGetterTask(); 159 160 void CheckPolicyForCookies( 161 const GURL& url, const GURL& first_party_for_cookies, 162 const media::MediaResourceGetter::GetCookieCB& callback, 163 const net::CookieList& cookie_list); 164 165 // Context getter used to get the CookieStore and auth cache. 166 net::URLRequestContextGetter* context_getter_; 167 168 // Resource context for checking cookie policies. 169 ResourceContext* resource_context_; 170 171 // Render process id, used to check whether the process can access cookies. 172 int render_process_id_; 173 174 // Render frame id, used to check tab specific cookie policy. 175 int render_frame_id_; 176 177 DISALLOW_COPY_AND_ASSIGN(MediaResourceGetterTask); 178 }; 179 180 MediaResourceGetterTask::MediaResourceGetterTask( 181 BrowserContext* browser_context, int render_process_id, int render_frame_id) 182 : context_getter_(browser_context->GetRequestContext()), 183 resource_context_(browser_context->GetResourceContext()), 184 render_process_id_(render_process_id), 185 render_frame_id_(render_frame_id) { 186 } 187 188 MediaResourceGetterTask::~MediaResourceGetterTask() {} 189 190 net::AuthCredentials MediaResourceGetterTask::RequestAuthCredentials( 191 const GURL& url) const { 192 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 193 net::HttpTransactionFactory* factory = 194 context_getter_->GetURLRequestContext()->http_transaction_factory(); 195 if (!factory) 196 return net::AuthCredentials(); 197 198 net::HttpAuthCache* auth_cache = 199 factory->GetSession()->http_auth_cache(); 200 if (!auth_cache) 201 return net::AuthCredentials(); 202 203 net::HttpAuthCache::Entry* entry = 204 auth_cache->LookupByPath(url.GetOrigin(), url.path()); 205 206 // TODO(qinmin): handle other auth schemes. See http://crbug.com/395219. 207 if (entry && entry->scheme() == net::HttpAuth::AUTH_SCHEME_BASIC) 208 return entry->credentials(); 209 else 210 return net::AuthCredentials(); 211 } 212 213 void MediaResourceGetterTask::RequestCookies( 214 const GURL& url, const GURL& first_party_for_cookies, 215 const media::MediaResourceGetter::GetCookieCB& callback) { 216 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 217 ChildProcessSecurityPolicyImpl* policy = 218 ChildProcessSecurityPolicyImpl::GetInstance(); 219 if (!policy->CanAccessCookiesForOrigin(render_process_id_, url)) { 220 callback.Run(std::string()); 221 return; 222 } 223 224 net::CookieStore* cookie_store = 225 context_getter_->GetURLRequestContext()->cookie_store(); 226 if (!cookie_store) { 227 callback.Run(std::string()); 228 return; 229 } 230 231 net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster(); 232 if (cookie_monster) { 233 cookie_monster->GetAllCookiesForURLAsync(url, base::Bind( 234 &MediaResourceGetterTask::CheckPolicyForCookies, this, 235 url, first_party_for_cookies, callback)); 236 } else { 237 callback.Run(std::string()); 238 } 239 } 240 241 void MediaResourceGetterTask::CheckPolicyForCookies( 242 const GURL& url, const GURL& first_party_for_cookies, 243 const media::MediaResourceGetter::GetCookieCB& callback, 244 const net::CookieList& cookie_list) { 245 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO)); 246 if (GetContentClient()->browser()->AllowGetCookie( 247 url, first_party_for_cookies, cookie_list, 248 resource_context_, render_process_id_, render_frame_id_)) { 249 net::CookieStore* cookie_store = 250 context_getter_->GetURLRequestContext()->cookie_store(); 251 net::CookieOptions options; 252 options.set_include_httponly(); 253 cookie_store->GetCookiesWithOptionsAsync(url, options, callback); 254 } else { 255 callback.Run(std::string()); 256 } 257 } 258 259 MediaResourceGetterImpl::MediaResourceGetterImpl( 260 BrowserContext* browser_context, 261 storage::FileSystemContext* file_system_context, 262 int render_process_id, 263 int render_frame_id) 264 : browser_context_(browser_context), 265 file_system_context_(file_system_context), 266 render_process_id_(render_process_id), 267 render_frame_id_(render_frame_id), 268 weak_factory_(this) { 269 } 270 271 MediaResourceGetterImpl::~MediaResourceGetterImpl() {} 272 273 void MediaResourceGetterImpl::GetAuthCredentials( 274 const GURL& url, const GetAuthCredentialsCB& callback) { 275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 276 scoped_refptr<MediaResourceGetterTask> task = new MediaResourceGetterTask( 277 browser_context_, 0, 0); 278 279 BrowserThread::PostTaskAndReplyWithResult( 280 BrowserThread::IO, 281 FROM_HERE, 282 base::Bind(&MediaResourceGetterTask::RequestAuthCredentials, task, url), 283 base::Bind(&MediaResourceGetterImpl::GetAuthCredentialsCallback, 284 weak_factory_.GetWeakPtr(), callback)); 285 } 286 287 void MediaResourceGetterImpl::GetCookies( 288 const GURL& url, const GURL& first_party_for_cookies, 289 const GetCookieCB& callback) { 290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 291 scoped_refptr<MediaResourceGetterTask> task = new MediaResourceGetterTask( 292 browser_context_, render_process_id_, render_frame_id_); 293 294 GetCookieCB cb = base::Bind(&MediaResourceGetterImpl::GetCookiesCallback, 295 weak_factory_.GetWeakPtr(), 296 callback); 297 BrowserThread::PostTask( 298 BrowserThread::IO, 299 FROM_HERE, 300 base::Bind(&MediaResourceGetterTask::RequestCookies, 301 task, url, first_party_for_cookies, 302 base::Bind(&ReturnResultOnUIThread, cb))); 303 } 304 305 void MediaResourceGetterImpl::GetAuthCredentialsCallback( 306 const GetAuthCredentialsCB& callback, 307 const net::AuthCredentials& credentials) { 308 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 309 callback.Run(credentials.username(), credentials.password()); 310 } 311 312 void MediaResourceGetterImpl::GetCookiesCallback( 313 const GetCookieCB& callback, const std::string& cookies) { 314 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 315 callback.Run(cookies); 316 } 317 318 void MediaResourceGetterImpl::GetPlatformPathFromURL( 319 const GURL& url, const GetPlatformPathCB& callback) { 320 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 321 DCHECK(url.SchemeIsFileSystem() || url.SchemeIs(url::kBlobScheme)); 322 323 GetPlatformPathCB cb = 324 base::Bind(&MediaResourceGetterImpl::GetPlatformPathCallback, 325 weak_factory_.GetWeakPtr(), 326 callback); 327 328 if (url.SchemeIs(url::kBlobScheme)) { 329 BrowserThread::PostTask( 330 BrowserThread::IO, 331 FROM_HERE, 332 base::Bind(&RequestPlatformPathFromBlobURL, url, browser_context_, cb)); 333 return; 334 } 335 336 scoped_refptr<storage::FileSystemContext> context(file_system_context_); 337 BrowserThread::PostTask( 338 BrowserThread::FILE, 339 FROM_HERE, 340 base::Bind(&RequestPlaformPathFromFileSystemURL, url, render_process_id_, 341 context, cb)); 342 } 343 344 void MediaResourceGetterImpl::GetPlatformPathCallback( 345 const GetPlatformPathCB& callback, const std::string& platform_path) { 346 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 347 callback.Run(platform_path); 348 } 349 350 void MediaResourceGetterImpl::ExtractMediaMetadata( 351 const std::string& url, const std::string& cookies, 352 const std::string& user_agent, const ExtractMediaMetadataCB& callback) { 353 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 354 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 355 pool->PostWorkerTask( 356 FROM_HERE, 357 base::Bind(&GetMediaMetadata, url, cookies, user_agent, callback)); 358 } 359 360 void MediaResourceGetterImpl::ExtractMediaMetadata( 361 const int fd, const int64 offset, const int64 size, 362 const ExtractMediaMetadataCB& callback) { 363 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); 364 base::SequencedWorkerPool* pool = content::BrowserThread::GetBlockingPool(); 365 pool->PostWorkerTask( 366 FROM_HERE, 367 base::Bind(&GetMediaMetadataFromFd, fd, offset, size, callback)); 368 } 369 370 // static 371 bool MediaResourceGetterImpl::RegisterMediaResourceGetter(JNIEnv* env) { 372 return RegisterNativesImpl(env); 373 } 374 375 } // namespace content 376