|  | // Copyright 2015 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "chrome/browser/download/android/download_manager_service.h" | 
|  |  | 
|  | #include <algorithm> | 
|  | #include <memory> | 
|  | #include <optional> | 
|  |  | 
|  | #include "base/android/callback_android.h" | 
|  | #include "base/android/jni_string.h" | 
|  | #include "base/android/path_utils.h" | 
|  | #include "base/containers/contains.h" | 
|  | #include "base/functional/bind.h" | 
|  | #include "base/location.h" | 
|  | #include "base/metrics/field_trial_params.h" | 
|  | #include "base/strings/string_number_conversions.h" | 
|  | #include "base/task/single_thread_task_runner.h" | 
|  | #include "base/time/default_clock.h" | 
|  | #include "base/time/time.h" | 
|  | #include "chrome/browser/android/flags/chrome_cached_flags.h" | 
|  | #include "chrome/browser/android/profile_key_startup_accessor.h" | 
|  | #include "chrome/browser/download/android/download_controller.h" | 
|  | #include "chrome/browser/download/android/download_startup_utils.h" | 
|  | #include "chrome/browser/download/android/download_utils.h" | 
|  | #include "chrome/browser/download/android/service/download_task_scheduler.h" | 
|  | #include "chrome/browser/download/offline_item_utils.h" | 
|  | #include "chrome/browser/download/simple_download_manager_coordinator_factory.h" | 
|  | #include "chrome/browser/flags/android/chrome_feature_list.h" | 
|  | #include "chrome/browser/profiles/profile.h" | 
|  | #include "chrome/browser/profiles/profile_key.h" | 
|  | #include "chrome/browser/profiles/profile_key_android.h" | 
|  | #include "chrome/browser/profiles/profile_manager.h" | 
|  | #include "chrome/common/chrome_constants.h" | 
|  | #include "components/download/network/android/network_status_listener_android.h" | 
|  | #include "components/download/public/common/android/auto_resumption_handler.h" | 
|  | #include "components/download/public/common/download_features.h" | 
|  | #include "components/download/public/common/download_item.h" | 
|  | #include "components/download/public/common/download_item_impl.h" | 
|  | #include "components/download/public/common/download_stats.h" | 
|  | #include "components/download/public/common/simple_download_manager_coordinator.h" | 
|  | #include "components/download/public/common/url_download_handler_factory.h" | 
|  | #include "components/download/public/task/task_manager_impl.h" | 
|  | #include "components/offline_items_collection/core/android/offline_item_bridge.h" | 
|  | #include "content/public/browser/browser_context.h" | 
|  | #include "content/public/browser/download_item_utils.h" | 
|  | #include "content/public/browser/download_request_utils.h" | 
|  | #include "net/url_request/referrer_policy.h" | 
|  | #include "third_party/blink/public/common/mime_util/mime_util.h" | 
|  | #include "url/android/gurl_android.h" | 
|  | #include "url/origin.h" | 
|  |  | 
|  | // Must come after all headers that specialize FromJniType() / ToJniType(). | 
|  | #include "chrome/android/chrome_jni_headers/DownloadItem_jni.h" | 
|  | #include "chrome/android/chrome_jni_headers/DownloadManagerService_jni.h" | 
|  | #include "chrome/browser/download/android/jni_headers/DownloadInfo_jni.h" | 
|  |  | 
|  | using base::android::JavaParamRef; | 
|  | using base::android::ScopedJavaLocalRef; | 
|  | using offline_items_collection::android::OfflineItemBridge; | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | // The remaining time for a download item if it cannot be calculated. | 
|  | constexpr int64_t kUnknownRemainingTime = -1; | 
|  |  | 
|  | bool ShouldShowDownloadItem(download::DownloadItem* item) { | 
|  | return !item->IsTemporary() && !item->IsTransient(); | 
|  | } | 
|  |  | 
|  | ScopedJavaLocalRef<jobject> JNI_DownloadManagerService_CreateJavaDownloadItem( | 
|  | JNIEnv* env, | 
|  | download::DownloadItem* item) { | 
|  | DCHECK(!item->IsTransient()); | 
|  | return Java_DownloadItem_createDownloadItem( | 
|  | env, DownloadManagerService::CreateJavaDownloadInfo(env, item), | 
|  | item->GetStartTime().InMillisecondsSinceUnixEpoch(), | 
|  | item->GetEndTime().InMillisecondsSinceUnixEpoch(), | 
|  | item->GetFileExternallyRemoved()); | 
|  | } | 
|  |  | 
|  | void RenameItemCallback( | 
|  | const base::android::ScopedJavaGlobalRef<jobject> j_callback, | 
|  | download::DownloadItem::DownloadRenameResult result) { | 
|  | base::android::RunIntCallbackAndroid( | 
|  | j_callback, | 
|  | static_cast<int32_t>( | 
|  | OfflineItemUtils::ConvertDownloadRenameResultToRenameResult(result))); | 
|  | } | 
|  |  | 
|  | bool IsReducedModeProfileKey(ProfileKey* profile_key) { | 
|  | return profile_key == ProfileKeyStartupAccessor::GetInstance()->profile_key(); | 
|  | } | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | // static | 
|  | void DownloadManagerService::CreateAutoResumptionHandler() { | 
|  | auto network_listener = | 
|  | std::make_unique<download::NetworkStatusListenerAndroid>(); | 
|  | auto task_scheduler = | 
|  | std::make_unique<download::android::DownloadTaskScheduler>(); | 
|  | auto task_manager = | 
|  | std::make_unique<download::TaskManagerImpl>(std::move(task_scheduler)); | 
|  | auto config = std::make_unique<download::AutoResumptionHandler::Config>(); | 
|  | config->auto_resumption_size_limit = | 
|  | DownloadUtils::GetAutoResumptionSizeLimit(); | 
|  | download::AutoResumptionHandler::Create( | 
|  | std::move(network_listener), std::move(task_manager), std::move(config), | 
|  | base::DefaultClock::GetInstance()); | 
|  | } | 
|  |  | 
|  | // static | 
|  | void DownloadManagerService::OnDownloadCanceled( | 
|  | download::DownloadItem* download, | 
|  | bool has_no_external_storage) { | 
|  | if (download->IsTransient()) { | 
|  | LOG(WARNING) << "Transient download should not have user interaction!"; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Inform the user in Java UI about file writing failures. | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  |  | 
|  | ScopedJavaLocalRef<jobject> j_item = | 
|  | JNI_DownloadManagerService_CreateJavaDownloadItem(env, download); | 
|  | Java_DownloadManagerService_onDownloadItemCanceled(env, j_item, | 
|  | has_no_external_storage); | 
|  | } | 
|  |  | 
|  | // static | 
|  | DownloadManagerService* DownloadManagerService::GetInstance() { | 
|  | return base::Singleton<DownloadManagerService>::get(); | 
|  | } | 
|  |  | 
|  | // static | 
|  | ScopedJavaLocalRef<jobject> DownloadManagerService::CreateJavaDownloadInfo( | 
|  | JNIEnv* env, | 
|  | download::DownloadItem* item) { | 
|  | base::TimeDelta time_delta; | 
|  | bool time_remaining_known = item->TimeRemaining(&time_delta); | 
|  | GURL original_url = item->GetOriginalUrl().SchemeIs(url::kDataScheme) | 
|  | ? GURL() | 
|  | : item->GetOriginalUrl(); | 
|  | content::BrowserContext* browser_context = | 
|  | content::DownloadItemUtils::GetBrowserContext(item); | 
|  |  | 
|  | base::android::ScopedJavaLocalRef<jobject> otr_profile_id; | 
|  | if (browser_context && browser_context->IsOffTheRecord()) { | 
|  | Profile* profile = Profile::FromBrowserContext(browser_context); | 
|  | otr_profile_id = profile->GetOTRProfileID().ConvertToJavaOTRProfileID(env); | 
|  | } | 
|  |  | 
|  | return Java_DownloadInfo_createDownloadInfo( | 
|  | env, item->GetGuid(), item->GetFileNameToReportUser().value(), | 
|  | item->GetTargetFilePath().value(), item->GetURL(), item->GetMimeType(), | 
|  | item->GetReceivedBytes(), item->GetTotalBytes(), otr_profile_id, | 
|  | item->GetState(), item->PercentComplete(), item->IsPaused(), | 
|  | DownloadUtils::IsDownloadUserInitiated(item), item->CanResume(), | 
|  | item->IsParallelDownload(), original_url, item->GetReferrerUrl(), | 
|  | time_remaining_known ? time_delta.InMilliseconds() | 
|  | : kUnknownRemainingTime, | 
|  | item->GetLastAccessTime().InMillisecondsSinceUnixEpoch(), | 
|  | item->GetDangerType(), item->IsDangerous(), | 
|  | static_cast<int>( | 
|  | OfflineItemUtils::ConvertDownloadInterruptReasonToFailState( | 
|  | item->GetLastReason())), | 
|  | item->IsTransient()); | 
|  | } | 
|  |  | 
|  | static jlong JNI_DownloadManagerService_Init(JNIEnv* env, | 
|  | const JavaParamRef<jobject>& jobj, | 
|  | jboolean is_full_browser_started) { | 
|  | DownloadManagerService* service = DownloadManagerService::GetInstance(); | 
|  | service->Init(env, jobj, is_full_browser_started); | 
|  | return reinterpret_cast<intptr_t>(service); | 
|  | } | 
|  |  | 
|  | DownloadManagerService::DownloadManagerService() | 
|  | : is_manager_initialized_(false), is_pending_downloads_loaded_(false) {} | 
|  |  | 
|  | DownloadManagerService::~DownloadManagerService() = default; | 
|  |  | 
|  | void DownloadManagerService::Init(JNIEnv* env, | 
|  | const base::android::JavaRef<jobject>& obj, | 
|  | bool is_profile_added) { | 
|  | java_ref_.Reset(env, obj); | 
|  | if (is_profile_added) { | 
|  | OnProfileAdded( | 
|  | ProfileManager::GetActiveUserProfile()->GetOriginalProfile()); | 
|  | } else { | 
|  | // In reduced mode, only non-incognito downloads should be loaded. | 
|  | ResetCoordinatorIfNeeded( | 
|  | DownloadStartupUtils::EnsureDownloadSystemInitialized(nullptr)); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnProfileAdded(JNIEnv* env, | 
|  | Profile* profile) { | 
|  | OnProfileAdded(profile); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnProfileAdded(Profile* profile) { | 
|  | InitializeForProfile(profile->GetProfileKey()); | 
|  | observed_profiles_.AddObservation(profile); | 
|  | for (Profile* otr : profile->GetAllOffTheRecordProfiles()) | 
|  | InitializeForProfile(otr->GetProfileKey()); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnOffTheRecordProfileCreated( | 
|  | Profile* off_the_record) { | 
|  | InitializeForProfile(off_the_record->GetProfileKey()); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OpenDownload(download::DownloadItem* download, | 
|  | int source) { | 
|  | if (java_ref_.is_null()) | 
|  | return; | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jobject> j_item = | 
|  | JNI_DownloadManagerService_CreateJavaDownloadItem(env, download); | 
|  |  | 
|  | Java_DownloadManagerService_openDownloadItem(env, java_ref_, j_item, source); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::HandleOMADownload(download::DownloadItem* download, | 
|  | int64_t system_download_id) { | 
|  | if (java_ref_.is_null()) | 
|  | return; | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jobject> j_item = | 
|  | JNI_DownloadManagerService_CreateJavaDownloadItem(env, download); | 
|  |  | 
|  | Java_DownloadManagerService_handleOMADownload(env, java_ref_, j_item, | 
|  | system_download_id); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OpenDownload( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | const JavaParamRef<jobject>& j_profile_key, | 
|  | jint source) { | 
|  | if (!is_manager_initialized_) | 
|  | return; | 
|  |  | 
|  | download::DownloadItem* item = GetDownload( | 
|  | download_guid, ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key)); | 
|  | if (!item) | 
|  | return; | 
|  |  | 
|  | OpenDownload(item, source); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OpenDownloadsPage( | 
|  | Profile* profile, | 
|  | DownloadOpenSource download_open_source) { | 
|  | if (java_ref_.is_null() || !profile) | 
|  | return; | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | if (profile->IsIncognitoProfile()) { | 
|  | profile->GetOTRProfileID().ConvertToJavaOTRProfileID(env); | 
|  | } | 
|  | Java_DownloadManagerService_openDownloadsPage( | 
|  | env, | 
|  | profile->IsIncognitoProfile() | 
|  | ? profile->GetOTRProfileID().ConvertToJavaOTRProfileID(env) | 
|  | : nullptr, | 
|  | static_cast<int>(download_open_source)); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::ResumeDownload( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | if (is_pending_downloads_loaded_ || profile_key->IsOffTheRecord()) { | 
|  | ResumeDownloadInternal(download_guid, profile_key); | 
|  | } else { | 
|  | EnqueueDownloadAction(download_guid, RESUME); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::PauseDownload( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | if (is_pending_downloads_loaded_ || profile_key->IsOffTheRecord()) | 
|  | PauseDownloadInternal(download_guid, profile_key); | 
|  | else | 
|  | EnqueueDownloadAction(download_guid, PAUSE); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::RemoveDownload( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | if (is_manager_initialized_ || profile_key->IsOffTheRecord()) | 
|  | RemoveDownloadInternal(download_guid, profile_key); | 
|  | else | 
|  | EnqueueDownloadAction(download_guid, REMOVE); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::GetAllDownloads( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | if (is_manager_initialized_) { | 
|  | GetAllDownloadsInternal(profile_key); | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Full download manager is required for this call. | 
|  | GetDownloadManager(profile_key); | 
|  | profiles_with_pending_get_downloads_actions_.push_back(profile_key); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::GetAllDownloadsInternal(ProfileKey* profile_key) { | 
|  | content::DownloadManager* manager = GetDownloadManager(profile_key); | 
|  | if (java_ref_.is_null() || !manager) | 
|  | return; | 
|  |  | 
|  | content::DownloadManager::DownloadVector all_items; | 
|  | manager->GetAllDownloads(&all_items); | 
|  |  | 
|  | // Create a Java array of all of the visible DownloadItems. | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jobject> j_download_item_list = | 
|  | Java_DownloadManagerService_createDownloadItemList(env, java_ref_); | 
|  |  | 
|  | for (size_t i = 0; i < all_items.size(); i++) { | 
|  | download::DownloadItem* item = all_items[i]; | 
|  | if (!ShouldShowDownloadItem(item)) | 
|  | continue; | 
|  |  | 
|  | ScopedJavaLocalRef<jobject> j_item = | 
|  | JNI_DownloadManagerService_CreateJavaDownloadItem(env, item); | 
|  | Java_DownloadManagerService_addDownloadItemToList( | 
|  | env, java_ref_, j_download_item_list, j_item); | 
|  | } | 
|  |  | 
|  | Java_DownloadManagerService_onAllDownloadsRetrieved( | 
|  | env, java_ref_, j_download_item_list, | 
|  | profile_key->GetProfileKeyAndroid()->GetJavaObject()); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::CheckForExternallyRemovedDownloads( | 
|  | JNIEnv* env, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | // Once the DownloadManager is initlaized, DownloadHistory will check for the | 
|  | // removal of history files. If the history query is not yet complete, ignore | 
|  | // requests to check for externally removed downloads. | 
|  | if (!is_manager_initialized_) | 
|  | return; | 
|  |  | 
|  | content::DownloadManager* manager = GetDownloadManager( | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key)); | 
|  | if (!manager) | 
|  | return; | 
|  | manager->CheckForHistoryFilesRemoval(); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::UpdateLastAccessTime( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | download::DownloadItem* item = GetDownload(download_guid, profile_key); | 
|  | if (item) | 
|  | item->SetLastAccessTime(base::Time::Now()); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::CancelDownload( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | if (is_pending_downloads_loaded_ || profile_key->IsOffTheRecord()) | 
|  | CancelDownloadInternal(download_guid, profile_key); | 
|  | else | 
|  | EnqueueDownloadAction(download_guid, CANCEL); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnDownloadsInitialized( | 
|  | download::SimpleDownloadManagerCoordinator* coordinator, | 
|  | bool active_downloads_only) { | 
|  | if (active_downloads_only) { | 
|  | OnPendingDownloadsLoaded(); | 
|  | return; | 
|  | } | 
|  | is_manager_initialized_ = true; | 
|  | OnPendingDownloadsLoaded(); | 
|  | while (!profiles_with_pending_get_downloads_actions_.empty()) { | 
|  | ProfileKey* profile_key = | 
|  | profiles_with_pending_get_downloads_actions_.back(); | 
|  | profiles_with_pending_get_downloads_actions_.pop_back(); | 
|  | GetAllDownloadsInternal(profile_key); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnManagerGoingDown( | 
|  | download::SimpleDownloadManagerCoordinator* coordinator) { | 
|  | for (auto it = coordinators_.begin(); it != coordinators_.end(); it++) { | 
|  | if (it->second == coordinator) { | 
|  | coordinators_.erase(it->first); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnDownloadCreated( | 
|  | download::SimpleDownloadManagerCoordinator* coordinator, | 
|  | download::DownloadItem* item) { | 
|  | if (item->IsTransient()) | 
|  | return; | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jobject> j_item = | 
|  | JNI_DownloadManagerService_CreateJavaDownloadItem(env, item); | 
|  | Java_DownloadManagerService_onDownloadItemCreated(env, java_ref_, j_item); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnDownloadUpdated( | 
|  | download::SimpleDownloadManagerCoordinator* coordinator, | 
|  | download::DownloadItem* item) { | 
|  | if (java_ref_.is_null()) | 
|  | return; | 
|  |  | 
|  | if (item->IsTemporary() || item->IsTransient()) | 
|  | return; | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | ScopedJavaLocalRef<jobject> j_item = | 
|  | JNI_DownloadManagerService_CreateJavaDownloadItem(env, item); | 
|  | Java_DownloadManagerService_onDownloadItemUpdated(env, java_ref_, j_item); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnDownloadRemoved( | 
|  | download::SimpleDownloadManagerCoordinator* coordinator, | 
|  | download::DownloadItem* item) { | 
|  | if (java_ref_.is_null() || item->IsTransient()) | 
|  | return; | 
|  |  | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | Java_DownloadManagerService_onDownloadItemRemoved(env, java_ref_, | 
|  | item->GetGuid()); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::ResumeDownloadInternal( | 
|  | const std::string& download_guid, | 
|  | ProfileKey* profile_key) { | 
|  | download::DownloadItem* item = GetDownload(download_guid, profile_key); | 
|  | if (!item) { | 
|  | OnResumptionFailed(download_guid); | 
|  | return; | 
|  | } | 
|  | if (!item->CanResume()) { | 
|  | OnResumptionFailed(download_guid); | 
|  | return; | 
|  | } | 
|  | item->Resume(true /* user_resume */); | 
|  | if (resume_callback_for_testing_) | 
|  | std::move(resume_callback_for_testing_).Run(true); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::CancelDownloadInternal( | 
|  | const std::string& download_guid, | 
|  | ProfileKey* profile_key) { | 
|  | download::DownloadItem* item = GetDownload(download_guid, profile_key); | 
|  | if (item) { | 
|  | // Remove the observer first to avoid item->Cancel() causing re-entrance | 
|  | // issue. | 
|  | item->RemoveObserver(DownloadControllerBase::Get()); | 
|  | item->Cancel(true); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::PauseDownloadInternal( | 
|  | const std::string& download_guid, | 
|  | ProfileKey* profile_key) { | 
|  | download::DownloadItem* item = GetDownload(download_guid, profile_key); | 
|  | if (item) | 
|  | item->Pause(); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::RemoveDownloadInternal( | 
|  | const std::string& download_guid, | 
|  | ProfileKey* profile_key) { | 
|  | download::DownloadItem* item = GetDownload(download_guid, profile_key); | 
|  | if (item) | 
|  | item->Remove(); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::EnqueueDownloadAction( | 
|  | const std::string& download_guid, | 
|  | DownloadAction download_action) { | 
|  | auto iter = pending_actions_.find(download_guid); | 
|  | if (iter == pending_actions_.end()) { | 
|  | pending_actions_.insert(std::make_pair(download_guid, download_action)); | 
|  | return; | 
|  | } | 
|  | switch (download_action) { | 
|  | case RESUME: | 
|  | if (iter->second == PAUSE) { | 
|  | iter->second = RESUME; | 
|  | } | 
|  | break; | 
|  | case PAUSE: | 
|  | if (iter->second == RESUME) { | 
|  | iter->second = PAUSE; | 
|  | } | 
|  | break; | 
|  | case CANCEL: | 
|  | case REMOVE: | 
|  | iter->second = download_action; | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnResumptionFailed( | 
|  | const std::string& download_guid) { | 
|  | base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce(&DownloadManagerService::OnResumptionFailedInternal, | 
|  | base::Unretained(this), download_guid)); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnResumptionFailedInternal( | 
|  | const std::string& download_guid) { | 
|  | if (!java_ref_.is_null()) { | 
|  | JNIEnv* env = base::android::AttachCurrentThread(); | 
|  | Java_DownloadManagerService_onResumptionFailed(env, java_ref_, | 
|  | download_guid); | 
|  | } | 
|  | if (resume_callback_for_testing_) | 
|  | std::move(resume_callback_for_testing_).Run(false); | 
|  | } | 
|  |  | 
|  | download::DownloadItem* DownloadManagerService::GetDownload( | 
|  | const std::string& download_guid, | 
|  | ProfileKey* profile_key) { | 
|  | download::SimpleDownloadManagerCoordinator* coordinator = | 
|  | GetCoordinator(profile_key); | 
|  | return coordinator ? coordinator->GetDownloadByGuid(download_guid) : nullptr; | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::OnPendingDownloadsLoaded() { | 
|  | is_pending_downloads_loaded_ = true; | 
|  |  | 
|  | auto result = | 
|  | std::ranges::find_if_not(coordinators_, &ProfileKey::IsOffTheRecord, | 
|  | &Coordinators::value_type::first); | 
|  | CHECK(result != coordinators_.end()) | 
|  | << "A non-OffTheRecord coordinator should exist when " | 
|  | "OnPendingDownloadsLoaded is triggered."; | 
|  | ProfileKey* profile_key = result->first; | 
|  |  | 
|  | // Kick-off the auto-resumption handler. | 
|  | content::DownloadManager::DownloadVector all_items; | 
|  | GetCoordinator(profile_key)->GetAllDownloads(&all_items); | 
|  |  | 
|  | if (!download::AutoResumptionHandler::Get()) | 
|  | CreateAutoResumptionHandler(); | 
|  |  | 
|  | download::AutoResumptionHandler::Get()->SetResumableDownloads(all_items); | 
|  |  | 
|  | for (auto iter = pending_actions_.begin(); iter != pending_actions_.end(); | 
|  | ++iter) { | 
|  | DownloadAction action = iter->second; | 
|  | std::string download_guid = iter->first; | 
|  | switch (action) { | 
|  | case RESUME: | 
|  | ResumeDownloadInternal(download_guid, profile_key); | 
|  | break; | 
|  | case PAUSE: | 
|  | PauseDownloadInternal(download_guid, profile_key); | 
|  | break; | 
|  | case CANCEL: | 
|  | CancelDownloadInternal(download_guid, profile_key); | 
|  | break; | 
|  | default: | 
|  | NOTREACHED(); | 
|  | } | 
|  | } | 
|  | pending_actions_.clear(); | 
|  | } | 
|  |  | 
|  | content::DownloadManager* DownloadManagerService::GetDownloadManager( | 
|  | ProfileKey* profile_key) { | 
|  | Profile* profile = | 
|  | IsReducedModeProfileKey(profile_key) | 
|  | ? ProfileManager::GetActiveUserProfile() | 
|  | : ProfileManager::GetProfileFromProfileKey(profile_key); | 
|  | content::DownloadManager* manager = profile->GetDownloadManager(); | 
|  | ResetCoordinatorIfNeeded(profile_key); | 
|  | return manager; | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::ResetCoordinatorIfNeeded(ProfileKey* profile_key) { | 
|  | download::SimpleDownloadManagerCoordinator* coordinator = | 
|  | SimpleDownloadManagerCoordinatorFactory::GetForKey(profile_key); | 
|  | UpdateCoordinator(coordinator, profile_key); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::UpdateCoordinator( | 
|  | download::SimpleDownloadManagerCoordinator* new_coordinator, | 
|  | ProfileKey* profile_key) { | 
|  | bool coordinator_exists = base::Contains(coordinators_, profile_key); | 
|  | if (!coordinator_exists || coordinators_[profile_key] != new_coordinator) { | 
|  | if (coordinator_exists) | 
|  | coordinators_[profile_key]->GetNotifier()->RemoveObserver(this); | 
|  | coordinators_[profile_key] = new_coordinator; | 
|  | new_coordinator->GetNotifier()->AddObserver(this); | 
|  | } | 
|  | } | 
|  |  | 
|  | download::SimpleDownloadManagerCoordinator* | 
|  | DownloadManagerService::GetCoordinator(ProfileKey* profile_key) { | 
|  | DCHECK(base::Contains(coordinators_, profile_key)); | 
|  | return coordinators_[profile_key]; | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::RenameDownload( | 
|  | JNIEnv* env, | 
|  | std::string& download_guid, | 
|  | std::string& target_name, | 
|  | const JavaParamRef<jobject>& j_callback, | 
|  | const JavaParamRef<jobject>& j_profile_key) { | 
|  | ProfileKey* profile_key = | 
|  | ProfileKeyAndroid::FromProfileKeyAndroid(j_profile_key); | 
|  | download::DownloadItem* item = GetDownload(download_guid, profile_key); | 
|  | if (!item) { | 
|  | base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask( | 
|  | FROM_HERE, | 
|  | base::BindOnce( | 
|  | &RenameItemCallback, | 
|  | base::android::ScopedJavaGlobalRef<jobject>(env, j_callback), | 
|  | download::DownloadItem::DownloadRenameResult::FAILURE_UNAVAILABLE)); | 
|  |  | 
|  | return; | 
|  | } | 
|  | base::OnceCallback<void(download::DownloadItem::DownloadRenameResult)> | 
|  | callback = base::BindOnce( | 
|  | &RenameItemCallback, | 
|  | base::android::ScopedJavaGlobalRef<jobject>(env, j_callback)); | 
|  | item->Rename(base::FilePath(target_name), std::move(callback)); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::CreateInterruptedDownloadForTest( | 
|  | JNIEnv* env, | 
|  | std::string& url, | 
|  | std::string& download_guid, | 
|  | std::string& target_path_str) { | 
|  | download::InProgressDownloadManager* in_progress_manager = | 
|  | DownloadManagerUtils::GetInProgressDownloadManager( | 
|  | ProfileKeyStartupAccessor::GetInstance()->profile_key()); | 
|  | std::vector<GURL> url_chain; | 
|  | url_chain.emplace_back(url); | 
|  | base::FilePath target_path(target_path_str); | 
|  | in_progress_manager->AddInProgressDownloadForTest( | 
|  | std::make_unique<download::DownloadItemImpl>( | 
|  | in_progress_manager, download_guid, 1, | 
|  | target_path.AddExtension("crdownload"), target_path, url_chain, | 
|  | GURL(), "", GURL(), GURL(), url::Origin(), "", "", base::Time(), | 
|  | base::Time(), "", "", 0, -1, 0, "", | 
|  | download::DownloadItem::INTERRUPTED, | 
|  | download::DOWNLOAD_DANGER_TYPE_NOT_DANGEROUS, | 
|  | download::DOWNLOAD_INTERRUPT_REASON_CRASH, false, false, false, | 
|  | base::Time(), false, | 
|  | std::vector<download::DownloadItem::ReceivedSlice>(), | 
|  | download::kInvalidRange, download::kInvalidRange, nullptr)); | 
|  | } | 
|  |  | 
|  | void DownloadManagerService::InitializeForProfile(ProfileKey* profile_key) { | 
|  | ResetCoordinatorIfNeeded( | 
|  | DownloadStartupUtils::EnsureDownloadSystemInitialized(profile_key)); | 
|  | } | 
|  |  | 
|  | // static | 
|  | jboolean JNI_DownloadManagerService_IsSupportedMimeType( | 
|  | JNIEnv* env, | 
|  | std::string& mime_type) { | 
|  | return blink::IsSupportedMimeType(mime_type); | 
|  | } |