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