1 // Copyright 2016 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 "chrome/browser/download/android/download_controller.h"
6 
7 #include <memory>
8 #include <utility>
9 #include <vector>
10 
11 #include "base/android/jni_android.h"
12 #include "base/android/jni_string.h"
13 #include "base/bind.h"
14 #include "base/check_op.h"
15 #include "base/feature_list.h"
16 #include "base/lazy_instance.h"
17 #include "base/metrics/field_trial_params.h"
18 #include "base/metrics/histogram_macros.h"
19 #include "base/notreached.h"
20 #include "base/synchronization/lock.h"
21 #include "chrome/android/chrome_jni_headers/DownloadController_jni.h"
22 #include "chrome/browser/android/profile_key_startup_accessor.h"
23 #include "chrome/browser/android/profile_key_util.h"
24 #include "chrome/browser/android/tab_android.h"
25 #include "chrome/browser/download/android/dangerous_download_infobar_delegate.h"
26 #include "chrome/browser/download/android/download_manager_service.h"
27 #include "chrome/browser/download/android/download_utils.h"
28 #include "chrome/browser/download/download_offline_content_provider.h"
29 #include "chrome/browser/download/download_offline_content_provider_factory.h"
30 #include "chrome/browser/download/download_stats.h"
31 #include "chrome/browser/flags/android/chrome_feature_list.h"
32 #include "chrome/browser/infobars/infobar_service.h"
33 #include "chrome/browser/offline_pages/android/offline_page_bridge.h"
34 #include "chrome/browser/permissions/permission_update_infobar_delegate_android.h"
35 #include "chrome/browser/vr/vr_tab_helper.h"
36 #include "chrome/grit/chromium_strings.h"
37 #include "components/download/content/public/context_menu_download.h"
38 #include "components/download/public/common/auto_resumption_handler.h"
39 #include "components/download/public/common/download_features.h"
40 #include "content/public/browser/browser_context.h"
41 #include "content/public/browser/browser_task_traits.h"
42 #include "content/public/browser/browser_thread.h"
43 #include "content/public/browser/download_item_utils.h"
44 #include "content/public/browser/download_manager.h"
45 #include "content/public/browser/render_process_host.h"
46 #include "content/public/browser/render_view_host.h"
47 #include "net/base/filename_util.h"
48 #include "ui/android/view_android.h"
49 #include "ui/android/window_android.h"
50 #include "ui/base/page_transition_types.h"
51 
52 using base::android::ConvertUTF8ToJavaString;
53 using base::android::JavaParamRef;
54 using base::android::ScopedJavaLocalRef;
55 using content::BrowserContext;
56 using content::BrowserThread;
57 using content::ContextMenuParams;
58 using content::DownloadManager;
59 using content::WebContents;
60 using download::DownloadItem;
61 
62 namespace {
63 // Guards download_controller_
64 base::LazyInstance<base::Lock>::DestructorAtExit g_download_controller_lock_;
65 
CreateContextMenuDownloadInternal(const content::WebContents::Getter & wc_getter,const content::ContextMenuParams & params,bool is_link,bool granted)66 void CreateContextMenuDownloadInternal(
67     const content::WebContents::Getter& wc_getter,
68     const content::ContextMenuParams& params,
69     bool is_link,
70     bool granted) {
71   content::WebContents* web_contents = wc_getter.Run();
72   if (!granted)
73     return;
74 
75   if (!web_contents) {
76     DownloadController::RecordStoragePermission(
77         DownloadController::StoragePermissionType::
78             STORAGE_PERMISSION_NO_WEB_CONTENTS);
79     return;
80   }
81 
82   RecordDownloadSource(DOWNLOAD_INITIATED_BY_CONTEXT_MENU);
83   auto origin = offline_pages::android::OfflinePageBridge::GetEncodedOriginApp(
84       web_contents);
85   download::CreateContextMenuDownload(web_contents, params, origin, is_link);
86 }
87 
88 // Helper class for retrieving a DownloadManager.
89 class DownloadManagerGetter : public DownloadManager::Observer {
90  public:
DownloadManagerGetter(DownloadManager * manager)91   explicit DownloadManagerGetter(DownloadManager* manager) : manager_(manager) {
92     manager_->AddObserver(this);
93   }
94 
~DownloadManagerGetter()95   ~DownloadManagerGetter() override {
96     if (manager_)
97       manager_->RemoveObserver(this);
98   }
99 
ManagerGoingDown(DownloadManager * manager)100   void ManagerGoingDown(DownloadManager* manager) override {
101     manager_ = nullptr;
102   }
103 
manager()104   DownloadManager* manager() { return manager_; }
105 
106  private:
107   DownloadManager* manager_;
108   DISALLOW_COPY_AND_ASSIGN(DownloadManagerGetter);
109 };
110 
RemoveDownloadItem(std::unique_ptr<DownloadManagerGetter> getter,const std::string & guid)111 void RemoveDownloadItem(std::unique_ptr<DownloadManagerGetter> getter,
112                         const std::string& guid) {
113   if (!getter->manager())
114     return;
115   DownloadItem* item = getter->manager()->GetDownloadByGuid(guid);
116   if (item)
117     item->Remove();
118 }
119 
OnRequestFileAccessResult(const content::WebContents::Getter & web_contents_getter,DownloadControllerBase::AcquireFileAccessPermissionCallback cb,bool granted,const std::string & permission_to_update)120 void OnRequestFileAccessResult(
121     const content::WebContents::Getter& web_contents_getter,
122     DownloadControllerBase::AcquireFileAccessPermissionCallback cb,
123     bool granted,
124     const std::string& permission_to_update) {
125   DCHECK_CURRENTLY_ON(BrowserThread::UI);
126 
127   if (!granted && !permission_to_update.empty() && web_contents_getter.Run()) {
128     WebContents* web_contents = web_contents_getter.Run();
129     std::vector<std::string> permissions;
130     permissions.push_back(permission_to_update);
131 
132     PermissionUpdateInfoBarDelegate::Create(
133         web_contents, permissions,
134         IDS_MISSING_STORAGE_PERMISSION_DOWNLOAD_EDUCATION_TEXT, std::move(cb));
135     return;
136   }
137 
138   std::move(cb).Run(granted);
139 }
140 
OnStoragePermissionDecided(DownloadControllerBase::AcquireFileAccessPermissionCallback cb,bool granted)141 void OnStoragePermissionDecided(
142     DownloadControllerBase::AcquireFileAccessPermissionCallback cb,
143     bool granted) {
144   DCHECK_CURRENTLY_ON(BrowserThread::UI);
145 
146   if (granted) {
147     DownloadController::RecordStoragePermission(
148         DownloadController::StoragePermissionType::STORAGE_PERMISSION_GRANTED);
149   } else {
150     DownloadController::RecordStoragePermission(
151         DownloadController::StoragePermissionType::STORAGE_PERMISSION_DENIED);
152   }
153 
154   std::move(cb).Run(granted);
155 }
156 
157 }  // namespace
158 
JNI_DownloadController_OnAcquirePermissionResult(JNIEnv * env,jlong callback_id,jboolean granted,const JavaParamRef<jstring> & jpermission_to_update)159 static void JNI_DownloadController_OnAcquirePermissionResult(
160     JNIEnv* env,
161     jlong callback_id,
162     jboolean granted,
163     const JavaParamRef<jstring>& jpermission_to_update) {
164   DCHECK_CURRENTLY_ON(BrowserThread::UI);
165   DCHECK(callback_id);
166 
167   std::string permission_to_update;
168   if (jpermission_to_update) {
169     permission_to_update =
170         base::android::ConvertJavaStringToUTF8(env, jpermission_to_update);
171   }
172   // Convert java long long int to c++ pointer, take ownership.
173   std::unique_ptr<DownloadController::AcquirePermissionCallback> cb(
174       reinterpret_cast<DownloadController::AcquirePermissionCallback*>(
175           callback_id));
176   std::move(*cb).Run(granted, permission_to_update);
177 }
178 
179 // static
Get()180 DownloadControllerBase* DownloadControllerBase::Get() {
181   base::AutoLock lock(g_download_controller_lock_.Get());
182   if (!DownloadControllerBase::download_controller_)
183     download_controller_ = DownloadController::GetInstance();
184   return DownloadControllerBase::download_controller_;
185 }
186 
187 // static
SetDownloadControllerBase(DownloadControllerBase * download_controller)188 void DownloadControllerBase::SetDownloadControllerBase(
189     DownloadControllerBase* download_controller) {
190   base::AutoLock lock(g_download_controller_lock_.Get());
191   DownloadControllerBase::download_controller_ = download_controller;
192 }
193 
194 // static
RecordStoragePermission(StoragePermissionType type)195 void DownloadController::RecordStoragePermission(StoragePermissionType type) {
196   UMA_HISTOGRAM_ENUMERATION("MobileDownload.StoragePermission", type,
197                             STORAGE_PERMISSION_MAX);
198 }
199 
200 // static
CloseTabIfEmpty(content::WebContents * web_contents)201 void DownloadController::CloseTabIfEmpty(content::WebContents* web_contents) {
202   if (!web_contents)
203     return;
204 
205   TabAndroid* tab = TabAndroid::FromWebContents(web_contents);
206   if (tab && !tab->GetJavaObject().is_null()) {
207     JNIEnv* env = base::android::AttachCurrentThread();
208     Java_DownloadController_closeTabIfBlank(env, tab->GetJavaObject());
209   }
210 }
211 
212 // static
GetInstance()213 DownloadController* DownloadController::GetInstance() {
214   return base::Singleton<DownloadController>::get();
215 }
216 
217 DownloadController::DownloadController() = default;
218 
219 DownloadController::~DownloadController() = default;
220 
AcquireFileAccessPermission(const content::WebContents::Getter & web_contents_getter,DownloadControllerBase::AcquireFileAccessPermissionCallback cb)221 void DownloadController::AcquireFileAccessPermission(
222     const content::WebContents::Getter& web_contents_getter,
223     DownloadControllerBase::AcquireFileAccessPermissionCallback cb) {
224   DCHECK_CURRENTLY_ON(BrowserThread::UI);
225 
226   WebContents* web_contents = web_contents_getter.Run();
227 
228   if (HasFileAccessPermission()) {
229     RecordStoragePermission(
230         StoragePermissionType::STORAGE_PERMISSION_REQUESTED);
231     RecordStoragePermission(
232         StoragePermissionType::STORAGE_PERMISSION_NO_ACTION_NEEDED);
233     content::GetUIThreadTaskRunner({})->PostTask(
234         FROM_HERE, base::BindOnce(std::move(cb), true));
235     return;
236   } else if (vr::VrTabHelper::IsUiSuppressedInVr(
237                  web_contents,
238                  vr::UiSuppressedElement::kFileAccessPermission)) {
239     content::GetUIThreadTaskRunner({})->PostTask(
240         FROM_HERE, base::BindOnce(std::move(cb), false));
241     return;
242   }
243 
244   RecordStoragePermission(StoragePermissionType::STORAGE_PERMISSION_REQUESTED);
245   AcquirePermissionCallback callback(base::BindOnce(
246       &OnRequestFileAccessResult, web_contents_getter,
247       base::BindOnce(&OnStoragePermissionDecided, std::move(cb))));
248   // Make copy on the heap so we can pass the pointer through JNI.
249   intptr_t callback_id = reinterpret_cast<intptr_t>(
250       new AcquirePermissionCallback(std::move(callback)));
251   JNIEnv* env = base::android::AttachCurrentThread();
252   Java_DownloadController_requestFileAccess(env, callback_id);
253 }
254 
CreateAndroidDownload(const content::WebContents::Getter & wc_getter,const DownloadInfo & info)255 void DownloadController::CreateAndroidDownload(
256     const content::WebContents::Getter& wc_getter,
257     const DownloadInfo& info) {
258   content::GetUIThreadTaskRunner({})->PostTask(
259       FROM_HERE, base::BindOnce(&DownloadController::StartAndroidDownload,
260                                 base::Unretained(this), wc_getter, info));
261 }
262 
AboutToResumeDownload(DownloadItem * download_item)263 void DownloadController::AboutToResumeDownload(DownloadItem* download_item) {
264   download_item->RemoveObserver(this);
265   download_item->AddObserver(this);
266 
267   // If a download is resumed from an interrupted state, record its strong
268   // validators so we know whether the resumption causes a restart.
269   if (download_item->GetState() == DownloadItem::IN_PROGRESS ||
270       download_item->GetLastReason() ==
271           download::DOWNLOAD_INTERRUPT_REASON_NONE) {
272     return;
273   }
274   if (download_item->GetETag().empty() &&
275       download_item->GetLastModifiedTime().empty()) {
276     return;
277   }
278   strong_validators_map_.emplace(
279       download_item->GetGuid(),
280       std::make_pair(download_item->GetETag(),
281                      download_item->GetLastModifiedTime()));
282 }
283 
StartAndroidDownload(const content::WebContents::Getter & wc_getter,const DownloadInfo & info)284 void DownloadController::StartAndroidDownload(
285     const content::WebContents::Getter& wc_getter,
286     const DownloadInfo& info) {
287   DCHECK_CURRENTLY_ON(BrowserThread::UI);
288 
289   AcquireFileAccessPermission(
290       wc_getter,
291       base::BindOnce(&DownloadController::StartAndroidDownloadInternal,
292                      base::Unretained(this), wc_getter, info));
293 }
294 
StartAndroidDownloadInternal(const content::WebContents::Getter & wc_getter,const DownloadInfo & info,bool allowed)295 void DownloadController::StartAndroidDownloadInternal(
296     const content::WebContents::Getter& wc_getter,
297     const DownloadInfo& info,
298     bool allowed) {
299   DCHECK_CURRENTLY_ON(BrowserThread::UI);
300   if (!allowed)
301     return;
302 
303   JNIEnv* env = base::android::AttachCurrentThread();
304   base::string16 file_name =
305       net::GetSuggestedFilename(info.url, info.content_disposition,
306                                 std::string(),  // referrer_charset
307                                 std::string(),  // suggested_name
308                                 info.original_mime_type, default_file_name_);
309   ScopedJavaLocalRef<jstring> jurl =
310       ConvertUTF8ToJavaString(env, info.url.spec());
311   ScopedJavaLocalRef<jstring> juser_agent =
312       ConvertUTF8ToJavaString(env, info.user_agent);
313   ScopedJavaLocalRef<jstring> jmime_type =
314       ConvertUTF8ToJavaString(env, info.original_mime_type);
315   ScopedJavaLocalRef<jstring> jcookie =
316       ConvertUTF8ToJavaString(env, info.cookie);
317   ScopedJavaLocalRef<jstring> jreferer =
318       ConvertUTF8ToJavaString(env, info.referer);
319   ScopedJavaLocalRef<jstring> jfile_name =
320       base::android::ConvertUTF16ToJavaString(env, file_name);
321   Java_DownloadController_enqueueAndroidDownloadManagerRequest(
322       env, jurl, juser_agent, jfile_name, jmime_type, jcookie, jreferer);
323 
324   WebContents* web_contents = wc_getter.Run();
325   CloseTabIfEmpty(web_contents);
326 }
327 
HasFileAccessPermission()328 bool DownloadController::HasFileAccessPermission() {
329   DCHECK_CURRENTLY_ON(BrowserThread::UI);
330 
331   JNIEnv* env = base::android::AttachCurrentThread();
332   return Java_DownloadController_hasFileAccess(env);
333 }
334 
OnDownloadStarted(DownloadItem * download_item)335 void DownloadController::OnDownloadStarted(DownloadItem* download_item) {
336   // For dangerous downloads, we need to show the dangerous infobar before the
337   // download can start.
338   JNIEnv* env = base::android::AttachCurrentThread();
339   if (!download_item->IsDangerous())
340     Java_DownloadController_onDownloadStarted(env);
341 
342   // Register for updates to the DownloadItem.
343   download_item->RemoveObserver(this);
344   download_item->AddObserver(this);
345 
346   if (download::AutoResumptionHandler::Get())
347     download::AutoResumptionHandler::Get()->OnDownloadStarted(download_item);
348 
349   ProfileKey* profile_key = GetProfileKey(download_item);
350   if (!profile_key)
351     return;
352 
353   DownloadOfflineContentProviderFactory::GetForKey(profile_key)
354       ->OnDownloadStarted(download_item);
355 
356   OnDownloadUpdated(download_item);
357 }
358 
OnDownloadUpdated(DownloadItem * item)359 void DownloadController::OnDownloadUpdated(DownloadItem* item) {
360   if (item->IsTemporary() || item->IsTransient())
361     return;
362 
363   if (item->IsDangerous() && (item->GetState() != DownloadItem::CANCELLED)) {
364     // Dont't show notification for a dangerous download, as user can resume
365     // the download after browser crash through notification.
366     OnDangerousDownload(item);
367     return;
368   }
369 
370   JNIEnv* env = base::android::AttachCurrentThread();
371   ScopedJavaLocalRef<jobject> j_item =
372       DownloadManagerService::CreateJavaDownloadInfo(env, item);
373   switch (item->GetState()) {
374     case DownloadItem::IN_PROGRESS: {
375       Java_DownloadController_onDownloadUpdated(env, j_item);
376       break;
377     }
378     case DownloadItem::COMPLETE:
379       strong_validators_map_.erase(item->GetGuid());
380       // Multiple OnDownloadUpdated() notifications may be issued while the
381       // download is in the COMPLETE state. Only handle one.
382       item->RemoveObserver(this);
383 
384       // Call onDownloadCompleted
385       Java_DownloadController_onDownloadCompleted(env, j_item);
386       break;
387     case DownloadItem::CANCELLED:
388       strong_validators_map_.erase(item->GetGuid());
389       Java_DownloadController_onDownloadCancelled(env, j_item);
390       break;
391     case DownloadItem::INTERRUPTED:
392       if (item->IsDone())
393         strong_validators_map_.erase(item->GetGuid());
394       // When device loses/changes network, we get a NETWORK_TIMEOUT,
395       // NETWORK_FAILED or NETWORK_DISCONNECTED error. Download should auto
396       // resume in this case.
397       Java_DownloadController_onDownloadInterrupted(
398           env, j_item, IsInterruptedDownloadAutoResumable(item));
399       break;
400     case DownloadItem::MAX_DOWNLOAD_STATE:
401       NOTREACHED();
402   }
403 }
404 
OnDangerousDownload(DownloadItem * item)405 void DownloadController::OnDangerousDownload(DownloadItem* item) {
406   WebContents* web_contents = content::DownloadItemUtils::GetWebContents(item);
407   if (!web_contents) {
408     auto download_manager_getter = std::make_unique<DownloadManagerGetter>(
409         BrowserContext::GetDownloadManager(
410             content::DownloadItemUtils::GetBrowserContext(item)));
411     content::GetUIThreadTaskRunner({})->PostTask(
412         FROM_HERE,
413         base::BindOnce(&RemoveDownloadItem, std::move(download_manager_getter),
414                        item->GetGuid()));
415     item->RemoveObserver(this);
416     return;
417   }
418 
419   DangerousDownloadInfoBarDelegate::Create(
420       InfoBarService::FromWebContents(web_contents), item);
421 }
422 
StartContextMenuDownload(const ContextMenuParams & params,WebContents * web_contents,bool is_link)423 void DownloadController::StartContextMenuDownload(
424     const ContextMenuParams& params,
425     WebContents* web_contents,
426     bool is_link) {
427   int process_id = web_contents->GetRenderViewHost()->GetProcess()->GetID();
428   int routing_id = web_contents->GetRenderViewHost()->GetRoutingID();
429 
430   const content::WebContents::Getter& wc_getter(
431       base::Bind(&GetWebContents, process_id, routing_id));
432 
433   AcquireFileAccessPermission(
434       wc_getter, base::BindOnce(&CreateContextMenuDownloadInternal, wc_getter,
435                                 params, is_link));
436 }
437 
IsInterruptedDownloadAutoResumable(download::DownloadItem * download_item)438 bool DownloadController::IsInterruptedDownloadAutoResumable(
439     download::DownloadItem* download_item) {
440   if (!download_item->GetURL().SchemeIsHTTPOrHTTPS())
441     return false;
442   static int size_limit = DownloadUtils::GetAutoResumptionSizeLimit();
443   bool exceeds_size_limit = download_item->GetReceivedBytes() > size_limit;
444   std::string etag = download_item->GetETag();
445   std::string last_modified = download_item->GetLastModifiedTime();
446 
447   if (exceeds_size_limit && etag.empty() && last_modified.empty() &&
448       !base::FeatureList::IsEnabled(
449           download::features::
450               kAllowDownloadResumptionWithoutStrongValidators)) {
451     return false;
452   }
453 
454   // If the download has strong validators, but it caused a restart, stop auto
455   // resumption as the server may always send new strong validators on
456   // resumption.
457   auto strong_validator = strong_validators_map_.find(download_item->GetGuid());
458   if (strong_validator != strong_validators_map_.end()) {
459     if (exceeds_size_limit &&
460         (strong_validator->second.first != etag ||
461          strong_validator->second.second != last_modified)) {
462       return false;
463     }
464   }
465 
466   int interrupt_reason = download_item->GetLastReason();
467   DCHECK_NE(interrupt_reason, download::DOWNLOAD_INTERRUPT_REASON_NONE);
468   return interrupt_reason ==
469              download::DOWNLOAD_INTERRUPT_REASON_NETWORK_TIMEOUT ||
470          interrupt_reason ==
471              download::DOWNLOAD_INTERRUPT_REASON_NETWORK_FAILED ||
472          interrupt_reason ==
473              download::DOWNLOAD_INTERRUPT_REASON_NETWORK_DISCONNECTED;
474 }
475 
GetProfileKey(DownloadItem * download_item)476 ProfileKey* DownloadController::GetProfileKey(DownloadItem* download_item) {
477   Profile* profile = Profile::FromBrowserContext(
478       content::DownloadItemUtils::GetBrowserContext(download_item));
479 
480   ProfileKey* profile_key;
481   if (profile)
482     profile_key = profile->GetProfileKey();
483   else
484     profile_key = ProfileKeyStartupAccessor::GetInstance()->profile_key();
485 
486   return profile_key;
487 }
488