Home | History | Annotate | Download | only in android
      1 // Copyright (c) 2012 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/android/download_controller_android_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/logging.h"
     11 #include "base/memory/scoped_ptr.h"
     12 #include "base/time/time.h"
     13 #include "content/browser/android/content_view_core_impl.h"
     14 #include "content/browser/download/download_item_impl.h"
     15 #include "content/browser/download/download_manager_impl.h"
     16 #include "content/browser/loader/resource_dispatcher_host_impl.h"
     17 #include "content/browser/renderer_host/render_process_host_impl.h"
     18 #include "content/browser/renderer_host/render_view_host_delegate.h"
     19 #include "content/browser/renderer_host/render_view_host_impl.h"
     20 #include "content/browser/web_contents/web_contents_impl.h"
     21 #include "content/public/browser/browser_context.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "content/public/browser/download_url_parameters.h"
     24 #include "content/public/browser/global_request_id.h"
     25 #include "content/public/common/referrer.h"
     26 #include "jni/DownloadController_jni.h"
     27 #include "net/cookies/cookie_options.h"
     28 #include "net/cookies/cookie_store.h"
     29 #include "net/http/http_content_disposition.h"
     30 #include "net/http/http_request_headers.h"
     31 #include "net/http/http_response_headers.h"
     32 #include "net/url_request/url_request.h"
     33 #include "net/url_request/url_request_context.h"
     34 
     35 using base::android::ConvertUTF8ToJavaString;
     36 using base::android::ScopedJavaLocalRef;
     37 
     38 namespace content {
     39 
     40 // JNI methods
     41 static void Init(JNIEnv* env, jobject obj) {
     42   DownloadControllerAndroidImpl::GetInstance()->Init(env, obj);
     43 }
     44 
     45 struct DownloadControllerAndroidImpl::JavaObject {
     46   ScopedJavaLocalRef<jobject> Controller(JNIEnv* env) {
     47     return GetRealObject(env, obj);
     48   }
     49   jweak obj;
     50 };
     51 
     52 // static
     53 bool DownloadControllerAndroidImpl::RegisterDownloadController(JNIEnv* env) {
     54   return RegisterNativesImpl(env);
     55 }
     56 
     57 // static
     58 DownloadControllerAndroid* DownloadControllerAndroid::Get() {
     59   return DownloadControllerAndroidImpl::GetInstance();
     60 }
     61 
     62 // static
     63 DownloadControllerAndroidImpl* DownloadControllerAndroidImpl::GetInstance() {
     64   return Singleton<DownloadControllerAndroidImpl>::get();
     65 }
     66 
     67 DownloadControllerAndroidImpl::DownloadControllerAndroidImpl()
     68     : java_object_(NULL) {
     69 }
     70 
     71 DownloadControllerAndroidImpl::~DownloadControllerAndroidImpl() {
     72   if (java_object_) {
     73     JNIEnv* env = base::android::AttachCurrentThread();
     74     env->DeleteWeakGlobalRef(java_object_->obj);
     75     delete java_object_;
     76     base::android::CheckException(env);
     77   }
     78 }
     79 
     80 // Initialize references to Java object.
     81 void DownloadControllerAndroidImpl::Init(JNIEnv* env, jobject obj) {
     82   java_object_ = new JavaObject;
     83   java_object_->obj = env->NewWeakGlobalRef(obj);
     84 }
     85 
     86 void DownloadControllerAndroidImpl::CreateGETDownload(
     87     int render_process_id, int render_view_id, int request_id) {
     88   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
     89   GlobalRequestID global_id(render_process_id, request_id);
     90 
     91   // We are yielding the UI thread and render_view_host may go away by
     92   // the time we come back. Pass along render_process_id and render_view_id
     93   // to retrieve it later (if it still exists).
     94   GetDownloadInfoCB cb = base::Bind(
     95         &DownloadControllerAndroidImpl::StartAndroidDownload,
     96         base::Unretained(this), render_process_id,
     97         render_view_id);
     98 
     99   PrepareDownloadInfo(
    100       global_id,
    101       base::Bind(&DownloadControllerAndroidImpl::StartDownloadOnUIThread,
    102                  base::Unretained(this), cb));
    103 }
    104 
    105 void DownloadControllerAndroidImpl::PrepareDownloadInfo(
    106     const GlobalRequestID& global_id,
    107     const GetDownloadInfoCB& callback) {
    108   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    109 
    110   net::URLRequest* request =
    111       ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
    112   if (!request) {
    113     LOG(ERROR) << "Request to download not found.";
    114     return;
    115   }
    116 
    117   DownloadInfoAndroid info_android(request);
    118 
    119   net::CookieStore* cookie_store = request->context()->cookie_store();
    120   if (cookie_store) {
    121     net::CookieMonster* cookie_monster = cookie_store->GetCookieMonster();
    122     if (cookie_monster) {
    123       cookie_monster->GetAllCookiesForURLAsync(
    124           request->url(),
    125           base::Bind(&DownloadControllerAndroidImpl::CheckPolicyAndLoadCookies,
    126                      base::Unretained(this), info_android, callback,
    127                      global_id));
    128     } else {
    129       DoLoadCookies(info_android, callback, global_id);
    130     }
    131   } else {
    132     // Can't get any cookies, start android download.
    133     callback.Run(info_android);
    134   }
    135 }
    136 
    137 void DownloadControllerAndroidImpl::CheckPolicyAndLoadCookies(
    138     const DownloadInfoAndroid& info, const GetDownloadInfoCB& callback,
    139     const GlobalRequestID& global_id, const net::CookieList& cookie_list) {
    140   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    141 
    142   net::URLRequest* request =
    143       ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
    144   if (!request) {
    145     LOG(ERROR) << "Request to download not found.";
    146     return;
    147   }
    148 
    149   if (request->context()->network_delegate()->CanGetCookies(
    150       *request, cookie_list)) {
    151     DoLoadCookies(info, callback, global_id);
    152   } else {
    153     callback.Run(info);
    154   }
    155 }
    156 
    157 void DownloadControllerAndroidImpl::DoLoadCookies(
    158     const DownloadInfoAndroid& info, const GetDownloadInfoCB& callback,
    159     const GlobalRequestID& global_id) {
    160   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    161 
    162   net::CookieOptions options;
    163   options.set_include_httponly();
    164 
    165   net::URLRequest* request =
    166       ResourceDispatcherHostImpl::Get()->GetURLRequest(global_id);
    167   if (!request) {
    168     LOG(ERROR) << "Request to download not found.";
    169     return;
    170   }
    171 
    172   request->context()->cookie_store()->GetCookiesWithOptionsAsync(
    173       info.url, options,
    174       base::Bind(&DownloadControllerAndroidImpl::OnCookieResponse,
    175                  base::Unretained(this), info, callback));
    176 }
    177 
    178 void DownloadControllerAndroidImpl::OnCookieResponse(
    179     DownloadInfoAndroid download_info,
    180     const GetDownloadInfoCB& callback,
    181     const std::string& cookie) {
    182   download_info.cookie = cookie;
    183 
    184   // We have everything we need, start Android download.
    185   callback.Run(download_info);
    186 }
    187 
    188 void DownloadControllerAndroidImpl::StartDownloadOnUIThread(
    189     const GetDownloadInfoCB& callback,
    190     const DownloadInfoAndroid& info) {
    191   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::IO));
    192   BrowserThread::PostTask(
    193       BrowserThread::UI, FROM_HERE, base::Bind(callback, info));
    194 }
    195 
    196 void DownloadControllerAndroidImpl::StartAndroidDownload(
    197     int render_process_id, int render_view_id,
    198     const DownloadInfoAndroid& info) {
    199   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    200   JNIEnv* env = base::android::AttachCurrentThread();
    201 
    202   // Call newHttpGetDownload
    203   ScopedJavaLocalRef<jobject> view = GetContentView(render_process_id,
    204                                                     render_view_id);
    205   if (view.is_null()) {
    206     // The view went away. Can't proceed.
    207     LOG(ERROR) << "Download failed on URL:" << info.url.spec();
    208     return;
    209   }
    210 
    211   ScopedJavaLocalRef<jstring> jurl =
    212       ConvertUTF8ToJavaString(env, info.url.spec());
    213   ScopedJavaLocalRef<jstring> juser_agent =
    214       ConvertUTF8ToJavaString(env, info.user_agent);
    215   ScopedJavaLocalRef<jstring> jcontent_disposition =
    216       ConvertUTF8ToJavaString(env, info.content_disposition);
    217   ScopedJavaLocalRef<jstring> jmime_type =
    218       ConvertUTF8ToJavaString(env, info.original_mime_type);
    219   ScopedJavaLocalRef<jstring> jcookie =
    220       ConvertUTF8ToJavaString(env, info.cookie);
    221   ScopedJavaLocalRef<jstring> jreferer =
    222       ConvertUTF8ToJavaString(env, info.referer);
    223 
    224   // Try parsing the content disposition header to get a
    225   // explicitly specified filename if available.
    226   net::HttpContentDisposition header(info.content_disposition, "");
    227   ScopedJavaLocalRef<jstring> jfilename =
    228       ConvertUTF8ToJavaString(env, header.filename());
    229 
    230   Java_DownloadController_newHttpGetDownload(
    231       env, GetJavaObject()->Controller(env).obj(), view.obj(), jurl.obj(),
    232       juser_agent.obj(), jcontent_disposition.obj(), jmime_type.obj(),
    233       jcookie.obj(), jreferer.obj(), jfilename.obj(), info.total_bytes);
    234 }
    235 
    236 void DownloadControllerAndroidImpl::OnDownloadStarted(
    237     DownloadItem* download_item) {
    238   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    239   if (!download_item->GetWebContents())
    240     return;
    241 
    242   JNIEnv* env = base::android::AttachCurrentThread();
    243 
    244   // Register for updates to the DownloadItem.
    245   download_item->AddObserver(this);
    246 
    247   ScopedJavaLocalRef<jobject> view =
    248       GetContentViewCoreFromWebContents(download_item->GetWebContents());
    249   // The view went away. Can't proceed.
    250   if (view.is_null())
    251     return;
    252 
    253   ScopedJavaLocalRef<jstring> jmime_type =
    254       ConvertUTF8ToJavaString(env, download_item->GetMimeType());
    255   ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
    256       env, download_item->GetTargetFilePath().BaseName().value());
    257   Java_DownloadController_onDownloadStarted(
    258       env, GetJavaObject()->Controller(env).obj(), view.obj(), jfilename.obj(),
    259       jmime_type.obj());
    260 }
    261 
    262 void DownloadControllerAndroidImpl::OnDownloadUpdated(DownloadItem* item) {
    263   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    264   if (item->IsDangerous() && (item->GetState() != DownloadItem::CANCELLED))
    265     OnDangerousDownload(item);
    266 
    267   JNIEnv* env = base::android::AttachCurrentThread();
    268   ScopedJavaLocalRef<jstring> jurl =
    269       ConvertUTF8ToJavaString(env, item->GetURL().spec());
    270   ScopedJavaLocalRef<jstring> jmime_type =
    271       ConvertUTF8ToJavaString(env, item->GetMimeType());
    272   ScopedJavaLocalRef<jstring> jpath =
    273       ConvertUTF8ToJavaString(env, item->GetTargetFilePath().value());
    274   ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
    275       env, item->GetTargetFilePath().BaseName().value());
    276 
    277   switch (item->GetState()) {
    278     case DownloadItem::IN_PROGRESS: {
    279       base::TimeDelta time_delta;
    280       item->TimeRemaining(&time_delta);
    281       Java_DownloadController_onDownloadUpdated(
    282           env, GetJavaObject()->Controller(env).obj(),
    283           base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(),
    284           jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), true,
    285           item->GetId(), item->PercentComplete(), time_delta.InMilliseconds());
    286       break;
    287     }
    288     case DownloadItem::COMPLETE:
    289       // Multiple OnDownloadUpdated() notifications may be issued while the
    290       // download is in the COMPLETE state. Only handle one.
    291       item->RemoveObserver(this);
    292 
    293       // Call onDownloadCompleted
    294       Java_DownloadController_onDownloadCompleted(
    295           env, GetJavaObject()->Controller(env).obj(),
    296           base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(),
    297           jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), true,
    298           item->GetId());
    299       break;
    300     case DownloadItem::CANCELLED:
    301     // TODO(shashishekhar): An interrupted download can be resumed. Android
    302     // currently does not support resumable downloads. Add handling for
    303     // interrupted case based on item->CanResume().
    304     case DownloadItem::INTERRUPTED:
    305       // Call onDownloadCompleted with success = false.
    306       Java_DownloadController_onDownloadCompleted(
    307           env, GetJavaObject()->Controller(env).obj(),
    308           base::android::GetApplicationContext(), jurl.obj(), jmime_type.obj(),
    309           jfilename.obj(), jpath.obj(), item->GetReceivedBytes(), false,
    310           item->GetId());
    311       break;
    312     case DownloadItem::MAX_DOWNLOAD_STATE:
    313       NOTREACHED();
    314   }
    315 }
    316 
    317 void DownloadControllerAndroidImpl::OnDangerousDownload(DownloadItem* item) {
    318   JNIEnv* env = base::android::AttachCurrentThread();
    319   ScopedJavaLocalRef<jstring> jfilename = ConvertUTF8ToJavaString(
    320       env, item->GetTargetFilePath().BaseName().value());
    321   ScopedJavaLocalRef<jobject> view_core = GetContentViewCoreFromWebContents(
    322       item->GetWebContents());
    323   if (!view_core.is_null()) {
    324     Java_DownloadController_onDangerousDownload(
    325         env, GetJavaObject()->Controller(env).obj(), view_core.obj(),
    326         jfilename.obj(), item->GetId());
    327   }
    328 }
    329 
    330 ScopedJavaLocalRef<jobject> DownloadControllerAndroidImpl::GetContentView(
    331     int render_process_id, int render_view_id) {
    332   RenderViewHost* render_view_host =
    333       RenderViewHost::FromID(render_process_id, render_view_id);
    334 
    335   if (!render_view_host)
    336     return ScopedJavaLocalRef<jobject>();
    337 
    338   WebContents* web_contents =
    339       render_view_host->GetDelegate()->GetAsWebContents();
    340 
    341   return GetContentViewCoreFromWebContents(web_contents);
    342 }
    343 
    344 ScopedJavaLocalRef<jobject>
    345     DownloadControllerAndroidImpl::GetContentViewCoreFromWebContents(
    346     WebContents* web_contents) {
    347   if (!web_contents)
    348     return ScopedJavaLocalRef<jobject>();
    349 
    350   ContentViewCore* view_core = ContentViewCore::FromWebContents(web_contents);
    351   return view_core ? view_core->GetJavaObject() :
    352       ScopedJavaLocalRef<jobject>();
    353 }
    354 
    355 DownloadControllerAndroidImpl::JavaObject*
    356     DownloadControllerAndroidImpl::GetJavaObject() {
    357   if (!java_object_) {
    358     // Initialize Java DownloadController by calling
    359     // DownloadController.getInstance(), which will call Init()
    360     // if Java DownloadController is not instantiated already.
    361     JNIEnv* env = base::android::AttachCurrentThread();
    362     Java_DownloadController_getInstance(env);
    363   }
    364 
    365   DCHECK(java_object_);
    366   return java_object_;
    367 }
    368 
    369 void DownloadControllerAndroidImpl::StartContextMenuDownload(
    370     const ContextMenuParams& params, WebContents* web_contents, bool is_link) {
    371   const GURL& url = is_link ? params.link_url : params.src_url;
    372   const GURL& referrer = params.frame_url.is_empty() ?
    373       params.page_url : params.frame_url;
    374   DownloadManagerImpl* dlm = static_cast<DownloadManagerImpl*>(
    375       BrowserContext::GetDownloadManager(web_contents->GetBrowserContext()));
    376   scoped_ptr<DownloadUrlParameters> dl_params(
    377       DownloadUrlParameters::FromWebContents(web_contents, url));
    378   dl_params->set_referrer(
    379       Referrer(referrer, params.referrer_policy));
    380   if (is_link)
    381     dl_params->set_referrer_encoding(params.frame_charset);
    382   else
    383     dl_params->set_prefer_cache(true);
    384   dl_params->set_prompt(false);
    385   dlm->DownloadUrl(dl_params.Pass());
    386 }
    387 
    388 void DownloadControllerAndroidImpl::DangerousDownloadValidated(
    389     WebContents* web_contents, int download_id, bool accept) {
    390   if (!web_contents)
    391     return;
    392   DownloadManagerImpl* dlm = static_cast<DownloadManagerImpl*>(
    393       BrowserContext::GetDownloadManager(web_contents->GetBrowserContext()));
    394   DownloadItem* item = dlm->GetDownload(download_id);
    395   if (!item)
    396     return;
    397   if (accept)
    398     item->ValidateDangerousDownload();
    399   else
    400     item->Remove();
    401 }
    402 
    403 DownloadControllerAndroidImpl::DownloadInfoAndroid::DownloadInfoAndroid(
    404     net::URLRequest* request) {
    405   request->GetResponseHeaderByName("content-disposition", &content_disposition);
    406 
    407   if (request->response_headers())
    408     request->response_headers()->GetMimeType(&original_mime_type);
    409 
    410   request->extra_request_headers().GetHeader(
    411       net::HttpRequestHeaders::kUserAgent, &user_agent);
    412   GURL referer_url(request->referrer());
    413   if (referer_url.is_valid())
    414     referer = referer_url.spec();
    415   if (!request->url_chain().empty()) {
    416     original_url = request->url_chain().front();
    417     url = request->url_chain().back();
    418   }
    419 }
    420 
    421 DownloadControllerAndroidImpl::DownloadInfoAndroid::~DownloadInfoAndroid() {}
    422 
    423 }  // namespace content
    424