Home | History | Annotate | Download | only in android
      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