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