background_downloader_win.cc 29 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898
  1. // Copyright 2014 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. #include "components/update_client/background_downloader_win.h"
  5. #include <objbase.h>
  6. #include <winerror.h>
  7. #include <stddef.h>
  8. #include <stdint.h>
  9. #include <functional>
  10. #include <iomanip>
  11. #include <limits>
  12. #include <memory>
  13. #include <utility>
  14. #include <vector>
  15. #include "base/bind.h"
  16. #include "base/files/file_util.h"
  17. #include "base/location.h"
  18. #include "base/logging.h"
  19. #include "base/metrics/histogram_macros.h"
  20. #include "base/strings/string_piece.h"
  21. #include "base/strings/sys_string_conversions.h"
  22. #include "base/task/sequenced_task_runner.h"
  23. #include "base/task/thread_pool.h"
  24. #include "base/threading/scoped_thread_priority.h"
  25. #include "base/win/atl.h"
  26. #include "base/win/scoped_co_mem.h"
  27. #include "components/update_client/task_traits.h"
  28. #include "components/update_client/update_client_errors.h"
  29. #include "components/update_client/utils.h"
  30. #include "url/gurl.h"
  31. using Microsoft::WRL::ComPtr;
  32. using base::win::ScopedCoMem;
  33. // The class BackgroundDownloader in this module is an adapter between
  34. // the CrxDownloader interface and the BITS service interfaces.
  35. // The interface exposed on the CrxDownloader code runs on the main sequence,
  36. // while the BITS specific code runs in a separate sequence bound to a
  37. // COM apartment. For every url to download, a BITS job is created, unless
  38. // there is already an existing job for that url, in which case, the downloader
  39. // connects to it. Once a job is associated with the url, the code looks for
  40. // changes in the BITS job state. The checks are triggered by a timer.
  41. // The BITS job contains just one file to download. There could only be one
  42. // download in progress at a time. If Chrome closes down before the download is
  43. // complete, the BITS job remains active and finishes in the background, without
  44. // any intervention. The job can be completed next time the code runs, if the
  45. // file is still needed, otherwise it will be cleaned up on a periodic basis.
  46. //
  47. // To list the BITS jobs for a user, use the |bitsadmin| tool. The command line
  48. // to do that is: "bitsadmin /list /verbose". Another useful command is
  49. // "bitsadmin /info" and provide the job id returned by the previous /list
  50. // command.
  51. //
  52. // Ignoring the suspend/resume issues since this code is not using them, the
  53. // job state machine implemented by BITS is something like this:
  54. //
  55. // Suspended--->Queued--->Connecting---->Transferring--->Transferred
  56. // | ^ | | |
  57. // | | V V | (complete)
  58. // +----------|---------+-----------------+-----+ V
  59. // | | | | Acknowledged
  60. // | V V |
  61. // | Transient Error------->Error |
  62. // | | | |(cancel)
  63. // | +-------+---------+--->-+
  64. // | V |
  65. // | (resume) | |
  66. // +------<----------+ +---->Cancelled
  67. //
  68. // The job is created in the "suspended" state. Once |Resume| is called,
  69. // BITS queues up the job, then tries to connect, begins transferring the
  70. // job bytes, and moves the job to the "transferred state, after the job files
  71. // have been transferred. When calling |Complete| for a job, the job files are
  72. // made available to the caller, and the job is moved to the "acknowledged"
  73. // state.
  74. // At any point, the job can be cancelled, in which case, the job is moved
  75. // to the "cancelled state" and the job object is removed from the BITS queue.
  76. // Along the way, the job can encounter recoverable and non-recoverable errors.
  77. // BITS moves the job to "transient error" or "error", depending on which kind
  78. // of error has occured.
  79. // If the job has reached the "transient error" state, BITS retries the
  80. // job after a certain programmable delay. If the job can't be completed in a
  81. // certain time interval, BITS stops retrying and errors the job out. This time
  82. // interval is also programmable.
  83. // If the job is in either of the error states, the job parameters can be
  84. // adjusted to handle the error, after which the job can be resumed, and the
  85. // whole cycle starts again.
  86. // Jobs that are not touched in 90 days (or a value set by group policy) are
  87. // automatically disposed off by BITS. This concludes the brief description of
  88. // a job lifetime, according to BITS.
  89. //
  90. // In addition to how BITS is managing the life time of the job, there are a
  91. // couple of special cases defined by the BackgroundDownloader.
  92. // First, if the job encounters any of the 5xx HTTP responses, the job is
  93. // not retried, in order to avoid DDOS-ing the servers.
  94. // Second, there is a simple mechanism to detect stuck jobs, and allow the rest
  95. // of the code to move on to trying other urls or trying other components.
  96. // Last, after completing a job, irrespective of the outcome, the jobs older
  97. // than a week are proactively cleaned up.
  98. namespace update_client {
  99. namespace {
  100. // All jobs created by this module have a specific description so they can
  101. // be found at run-time or by using system administration tools.
  102. const wchar_t kJobName[] = L"Chrome Component Updater";
  103. // How often the code looks for changes in the BITS job state.
  104. const int kJobPollingIntervalSec = 4;
  105. // How long BITS waits before retrying a job after the job encountered
  106. // a transient error. If this value is not set, the BITS default is 10 minutes.
  107. const int kMinimumRetryDelayMin = 1;
  108. // How long to wait for stuck jobs. Stuck jobs could be queued for too long,
  109. // have trouble connecting, could be suspended for any reason, or they have
  110. // encountered some transient error.
  111. const int kJobStuckTimeoutMin = 15;
  112. // How long BITS waits before giving up on a job that could not be completed
  113. // since the job has encountered its first transient error. If this value is
  114. // not set, the BITS default is 14 days.
  115. const int kSetNoProgressTimeoutDays = 1;
  116. // How often the jobs which were started but not completed for any reason
  117. // are cleaned up. Reasons for jobs to be left behind include browser restarts,
  118. // system restarts, etc. Also, the check to purge stale jobs only happens
  119. // at most once a day. If the job clean up code is not running, the BITS
  120. // default policy is to cancel jobs after 90 days of inactivity.
  121. const int kPurgeStaleJobsAfterDays = 3;
  122. const int kPurgeStaleJobsIntervalBetweenChecksDays = 1;
  123. // Number of maximum BITS jobs this downloader can create and queue up.
  124. const int kMaxQueuedJobs = 10;
  125. // Retrieves the singleton instance of GIT for this process.
  126. HRESULT GetGit(ComPtr<IGlobalInterfaceTable>* git) {
  127. // Mitigate the issues caused by loading DLLs on a background thread
  128. // (http://crbug/973868).
  129. SCOPED_MAY_LOAD_LIBRARY_AT_BACKGROUND_PRIORITY();
  130. return ::CoCreateInstance(CLSID_StdGlobalInterfaceTable, nullptr,
  131. CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&(*git)));
  132. }
  133. // Retrieves an interface pointer from the process GIT for a given |cookie|.
  134. HRESULT GetInterfaceFromGit(const ComPtr<IGlobalInterfaceTable>& git,
  135. DWORD cookie,
  136. REFIID riid,
  137. void** ppv) {
  138. return git->GetInterfaceFromGlobal(cookie, riid, ppv);
  139. }
  140. // Registers an interface pointer in GIT and returns its corresponding |cookie|.
  141. template <typename T>
  142. HRESULT RegisterInterfaceInGit(const ComPtr<IGlobalInterfaceTable>& git,
  143. const ComPtr<T>& p,
  144. DWORD* cookie) {
  145. return git->RegisterInterfaceInGlobal(p.Get(), __uuidof(T), cookie);
  146. }
  147. // Returns the status code from a given BITS error.
  148. int GetHttpStatusFromBitsError(HRESULT error) {
  149. // BITS errors are defined in bitsmsg.h. Although not documented, it is
  150. // clear that all errors corresponding to http status code have the high
  151. // word equal to 0x8019 and the low word equal to the http status code.
  152. const int kHttpStatusFirst = 100; // Continue.
  153. const int kHttpStatusLast = 505; // Version not supported.
  154. bool is_valid = HIWORD(error) == 0x8019 &&
  155. LOWORD(error) >= kHttpStatusFirst &&
  156. LOWORD(error) <= kHttpStatusLast;
  157. return is_valid ? LOWORD(error) : 0;
  158. }
  159. // Returns the files in a BITS job.
  160. HRESULT GetFilesInJob(const ComPtr<IBackgroundCopyJob>& job,
  161. std::vector<ComPtr<IBackgroundCopyFile>>* files) {
  162. ComPtr<IEnumBackgroundCopyFiles> enum_files;
  163. HRESULT hr = job->EnumFiles(&enum_files);
  164. if (FAILED(hr))
  165. return hr;
  166. ULONG num_files = 0;
  167. hr = enum_files->GetCount(&num_files);
  168. if (FAILED(hr))
  169. return hr;
  170. for (ULONG i = 0; i != num_files; ++i) {
  171. ComPtr<IBackgroundCopyFile> file;
  172. if (enum_files->Next(1, &file, nullptr) == S_OK && file.Get())
  173. files->push_back(file);
  174. }
  175. return S_OK;
  176. }
  177. // Returns the file name, the url, and some per-file progress information.
  178. // The function out parameters can be NULL if that data is not requested.
  179. HRESULT GetJobFileProperties(const ComPtr<IBackgroundCopyFile>& file,
  180. std::wstring* local_name,
  181. std::wstring* remote_name,
  182. BG_FILE_PROGRESS* progress) {
  183. if (!file)
  184. return E_FAIL;
  185. HRESULT hr = S_OK;
  186. if (local_name) {
  187. ScopedCoMem<wchar_t> name;
  188. hr = file->GetLocalName(&name);
  189. if (FAILED(hr))
  190. return hr;
  191. local_name->assign(name);
  192. }
  193. if (remote_name) {
  194. ScopedCoMem<wchar_t> name;
  195. hr = file->GetRemoteName(&name);
  196. if (FAILED(hr))
  197. return hr;
  198. remote_name->assign(name);
  199. }
  200. if (progress) {
  201. BG_FILE_PROGRESS bg_file_progress = {};
  202. hr = file->GetProgress(&bg_file_progress);
  203. if (FAILED(hr))
  204. return hr;
  205. *progress = bg_file_progress;
  206. }
  207. return hr;
  208. }
  209. // Returns the number of bytes downloaded and bytes to download for all files
  210. // in the job. If the values are not known or if an error has occurred,
  211. // a value of -1 is reported.
  212. HRESULT GetJobByteCount(const ComPtr<IBackgroundCopyJob>& job,
  213. int64_t* downloaded_bytes,
  214. int64_t* total_bytes) {
  215. *downloaded_bytes = -1;
  216. *total_bytes = -1;
  217. if (!job)
  218. return E_FAIL;
  219. BG_JOB_PROGRESS job_progress = {};
  220. HRESULT hr = job->GetProgress(&job_progress);
  221. if (FAILED(hr))
  222. return hr;
  223. const uint64_t kMaxNumBytes =
  224. static_cast<uint64_t>(std::numeric_limits<int64_t>::max());
  225. if (job_progress.BytesTransferred <= kMaxNumBytes)
  226. *downloaded_bytes = job_progress.BytesTransferred;
  227. if (job_progress.BytesTotal <= kMaxNumBytes &&
  228. job_progress.BytesTotal != BG_SIZE_UNKNOWN)
  229. *total_bytes = job_progress.BytesTotal;
  230. return S_OK;
  231. }
  232. HRESULT GetJobDisplayName(const ComPtr<IBackgroundCopyJob>& job,
  233. std::wstring* name) {
  234. ScopedCoMem<wchar_t> local_name;
  235. const HRESULT hr = job->GetDisplayName(&local_name);
  236. if (FAILED(hr))
  237. return hr;
  238. *name = local_name.get();
  239. return S_OK;
  240. }
  241. // Returns the job error code in |error_code| if the job is in the transient
  242. // or the final error state. Otherwise, the job error is not available and
  243. // the function fails.
  244. HRESULT GetJobError(const ComPtr<IBackgroundCopyJob>& job,
  245. HRESULT* error_code_out) {
  246. *error_code_out = S_OK;
  247. ComPtr<IBackgroundCopyError> copy_error;
  248. HRESULT hr = job->GetError(&copy_error);
  249. if (FAILED(hr))
  250. return hr;
  251. BG_ERROR_CONTEXT error_context = BG_ERROR_CONTEXT_NONE;
  252. HRESULT error_code = S_OK;
  253. hr = copy_error->GetError(&error_context, &error_code);
  254. if (FAILED(hr))
  255. return hr;
  256. *error_code_out = FAILED(error_code) ? error_code : E_FAIL;
  257. return S_OK;
  258. }
  259. // Finds the component updater jobs matching the given predicate.
  260. // Returns S_OK if the function has found at least one job, returns S_FALSE if
  261. // no job was found, and it returns an error otherwise.
  262. template <class Predicate>
  263. HRESULT FindBitsJobIf(Predicate pred,
  264. const ComPtr<IBackgroundCopyManager>& bits_manager,
  265. std::vector<ComPtr<IBackgroundCopyJob>>* jobs) {
  266. ComPtr<IEnumBackgroundCopyJobs> enum_jobs;
  267. HRESULT hr = bits_manager->EnumJobs(0, &enum_jobs);
  268. if (FAILED(hr))
  269. return hr;
  270. ULONG job_count = 0;
  271. hr = enum_jobs->GetCount(&job_count);
  272. if (FAILED(hr))
  273. return hr;
  274. // Iterate over jobs, run the predicate, and select the job only if
  275. // the job description matches the component updater jobs.
  276. for (ULONG i = 0; i != job_count; ++i) {
  277. ComPtr<IBackgroundCopyJob> current_job;
  278. if (enum_jobs->Next(1, &current_job, nullptr) == S_OK &&
  279. pred(current_job)) {
  280. std::wstring job_name;
  281. hr = GetJobDisplayName(current_job, &job_name);
  282. if (job_name.compare(kJobName) == 0)
  283. jobs->push_back(current_job);
  284. }
  285. }
  286. return jobs->empty() ? S_FALSE : S_OK;
  287. }
  288. bool JobCreationOlderThanDaysPredicate(ComPtr<IBackgroundCopyJob> job,
  289. int num_days) {
  290. BG_JOB_TIMES times = {};
  291. HRESULT hr = job->GetTimes(&times);
  292. if (FAILED(hr))
  293. return false;
  294. const base::TimeDelta time_delta(base::Days(num_days));
  295. const base::Time creation_time(base::Time::FromFileTime(times.CreationTime));
  296. return creation_time + time_delta < base::Time::Now();
  297. }
  298. bool JobFileUrlEqualPredicate(ComPtr<IBackgroundCopyJob> job, const GURL& url) {
  299. std::vector<ComPtr<IBackgroundCopyFile>> files;
  300. HRESULT hr = GetFilesInJob(job, &files);
  301. if (FAILED(hr))
  302. return false;
  303. for (size_t i = 0; i != files.size(); ++i) {
  304. ScopedCoMem<wchar_t> remote_name;
  305. if (SUCCEEDED(files[i]->GetRemoteName(&remote_name)) &&
  306. url == GURL(base::SysWideToUTF8(remote_name.get())))
  307. return true;
  308. }
  309. return false;
  310. }
  311. // Creates an instance of the BITS manager.
  312. HRESULT CreateBitsManager(ComPtr<IBackgroundCopyManager>* bits_manager) {
  313. ComPtr<IBackgroundCopyManager> local_bits_manager;
  314. HRESULT hr =
  315. ::CoCreateInstance(__uuidof(BackgroundCopyManager), nullptr, CLSCTX_ALL,
  316. IID_PPV_ARGS(&local_bits_manager));
  317. if (FAILED(hr)) {
  318. return hr;
  319. }
  320. *bits_manager = local_bits_manager;
  321. return S_OK;
  322. }
  323. void CleanupJob(const ComPtr<IBackgroundCopyJob>& job) {
  324. if (!job)
  325. return;
  326. // Get the file paths associated with this job before canceling the job.
  327. // Canceling the job removes it from the BITS queue right away. It appears
  328. // that it is still possible to query for the properties of the job after
  329. // the job has been canceled. It seems safer though to get the paths first.
  330. std::vector<ComPtr<IBackgroundCopyFile>> files;
  331. GetFilesInJob(job, &files);
  332. std::vector<base::FilePath> paths;
  333. for (const auto& file : files) {
  334. std::wstring local_name;
  335. HRESULT hr = GetJobFileProperties(file, &local_name, nullptr, nullptr);
  336. if (SUCCEEDED(hr))
  337. paths.push_back(base::FilePath(local_name));
  338. }
  339. job->Cancel();
  340. for (const auto& path : paths)
  341. DeleteFileAndEmptyParentDirectory(path);
  342. }
  343. } // namespace
  344. BackgroundDownloader::BackgroundDownloader(
  345. scoped_refptr<CrxDownloader> successor)
  346. : CrxDownloader(std::move(successor)),
  347. com_task_runner_(base::ThreadPool::CreateCOMSTATaskRunner(
  348. kTaskTraitsBackgroundDownloader)),
  349. git_cookie_bits_manager_(0),
  350. git_cookie_job_(0) {}
  351. BackgroundDownloader::~BackgroundDownloader() = default;
  352. void BackgroundDownloader::StartTimer() {
  353. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  354. timer_ = std::make_unique<base::OneShotTimer>();
  355. timer_->Start(FROM_HERE, base::Seconds(kJobPollingIntervalSec), this,
  356. &BackgroundDownloader::OnTimer);
  357. }
  358. void BackgroundDownloader::OnTimer() {
  359. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  360. timer_ = nullptr;
  361. com_task_runner_->PostTask(
  362. FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloading, this));
  363. }
  364. void BackgroundDownloader::DoStartDownload(const GURL& url) {
  365. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  366. com_task_runner_->PostTask(
  367. FROM_HERE,
  368. base::BindOnce(&BackgroundDownloader::BeginDownload, this, url));
  369. }
  370. // Called one time when this class is asked to do a download.
  371. void BackgroundDownloader::BeginDownload(const GURL& url) {
  372. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  373. download_start_time_ = base::TimeTicks::Now();
  374. job_stuck_begin_time_ = download_start_time_;
  375. HRESULT hr = BeginDownloadHelper(url);
  376. if (FAILED(hr)) {
  377. EndDownload(hr);
  378. return;
  379. }
  380. VLOG(1) << "Starting BITS download for: " << url.spec();
  381. ResetInterfacePointers();
  382. main_task_runner()->PostTask(
  383. FROM_HERE, base::BindOnce(&BackgroundDownloader::StartTimer, this));
  384. }
  385. // Creates or opens an existing BITS job to download the |url|, and handles
  386. // the marshalling of the interfaces in GIT.
  387. HRESULT BackgroundDownloader::BeginDownloadHelper(const GURL& url) {
  388. ComPtr<IGlobalInterfaceTable> git;
  389. HRESULT hr = GetGit(&git);
  390. if (FAILED(hr))
  391. return hr;
  392. hr = CreateBitsManager(&bits_manager_);
  393. if (FAILED(hr))
  394. return hr;
  395. hr = QueueBitsJob(url, &job_);
  396. if (FAILED(hr))
  397. return hr;
  398. hr = RegisterInterfaceInGit(git, bits_manager_, &git_cookie_bits_manager_);
  399. if (FAILED(hr))
  400. return hr;
  401. hr = RegisterInterfaceInGit(git, job_, &git_cookie_job_);
  402. if (FAILED(hr))
  403. return hr;
  404. return S_OK;
  405. }
  406. // Called any time the timer fires.
  407. void BackgroundDownloader::OnDownloading() {
  408. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  409. HRESULT hr = UpdateInterfacePointers();
  410. if (FAILED(hr)) {
  411. EndDownload(hr);
  412. return;
  413. }
  414. BG_JOB_STATE job_state = BG_JOB_STATE_ERROR;
  415. hr = job_->GetState(&job_state);
  416. if (FAILED(hr)) {
  417. EndDownload(hr);
  418. return;
  419. }
  420. bool is_handled = false;
  421. switch (job_state) {
  422. case BG_JOB_STATE_TRANSFERRED:
  423. is_handled = OnStateTransferred();
  424. break;
  425. case BG_JOB_STATE_ERROR:
  426. is_handled = OnStateError();
  427. break;
  428. case BG_JOB_STATE_CANCELLED:
  429. is_handled = OnStateCancelled();
  430. break;
  431. case BG_JOB_STATE_ACKNOWLEDGED:
  432. is_handled = OnStateAcknowledged();
  433. break;
  434. case BG_JOB_STATE_QUEUED:
  435. // Fall through.
  436. case BG_JOB_STATE_CONNECTING:
  437. // Fall through.
  438. case BG_JOB_STATE_SUSPENDED:
  439. is_handled = OnStateQueued();
  440. break;
  441. case BG_JOB_STATE_TRANSIENT_ERROR:
  442. is_handled = OnStateTransientError();
  443. break;
  444. case BG_JOB_STATE_TRANSFERRING:
  445. is_handled = OnStateTransferring();
  446. break;
  447. default:
  448. break;
  449. }
  450. if (is_handled)
  451. return;
  452. ResetInterfacePointers();
  453. main_task_runner()->PostTask(
  454. FROM_HERE, base::BindOnce(&BackgroundDownloader::StartTimer, this));
  455. }
  456. // Completes the BITS download, picks up the file path of the response, and
  457. // notifies the CrxDownloader. The function should be called only once.
  458. void BackgroundDownloader::EndDownload(HRESULT error) {
  459. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  460. const base::TimeTicks download_end_time(base::TimeTicks::Now());
  461. const base::TimeDelta download_time =
  462. download_end_time >= download_start_time_
  463. ? download_end_time - download_start_time_
  464. : base::TimeDelta();
  465. int64_t downloaded_bytes = -1;
  466. int64_t total_bytes = -1;
  467. GetJobByteCount(job_, &downloaded_bytes, &total_bytes);
  468. if (FAILED(error))
  469. CleanupJob(job_);
  470. ClearGit();
  471. // Consider the url handled if it has been successfully downloaded or a
  472. // 5xx has been received.
  473. const bool is_handled =
  474. SUCCEEDED(error) || IsHttpServerError(GetHttpStatusFromBitsError(error));
  475. const int error_to_report = SUCCEEDED(error) ? 0 : error;
  476. DCHECK(static_cast<bool>(error_to_report) == !base::PathExists(response_));
  477. DownloadMetrics download_metrics;
  478. download_metrics.url = url();
  479. download_metrics.downloader = DownloadMetrics::kBits;
  480. download_metrics.error = error_to_report;
  481. download_metrics.downloaded_bytes = downloaded_bytes;
  482. download_metrics.total_bytes = total_bytes;
  483. download_metrics.download_time_ms = download_time.InMilliseconds();
  484. Result result;
  485. result.error = error_to_report;
  486. if (!result.error)
  487. result.response = response_;
  488. main_task_runner()->PostTask(
  489. FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadComplete, this,
  490. is_handled, result, download_metrics));
  491. }
  492. // Called when the BITS job has been transferred successfully. Completes the
  493. // BITS job by removing it from the BITS queue and making the download
  494. // available to the caller.
  495. bool BackgroundDownloader::OnStateTransferred() {
  496. EndDownload(CompleteJob());
  497. return true;
  498. }
  499. // Called when the job has encountered an error and no further progress can
  500. // be made. Cancels this job and removes it from the BITS queue.
  501. bool BackgroundDownloader::OnStateError() {
  502. HRESULT error_code = S_OK;
  503. HRESULT hr = GetJobError(job_, &error_code);
  504. if (FAILED(hr))
  505. error_code = hr;
  506. DCHECK(FAILED(error_code));
  507. EndDownload(error_code);
  508. return true;
  509. }
  510. // Called when the download was completed. This notification is not seen
  511. // in the current implementation but provided here as a defensive programming
  512. // measure.
  513. bool BackgroundDownloader::OnStateAcknowledged() {
  514. EndDownload(E_UNEXPECTED);
  515. return true;
  516. }
  517. // Called when the download was cancelled. Same as above.
  518. bool BackgroundDownloader::OnStateCancelled() {
  519. EndDownload(E_UNEXPECTED);
  520. return true;
  521. }
  522. // Called when the job has encountered a transient error, such as a
  523. // network disconnect, a server error, or some other recoverable error.
  524. bool BackgroundDownloader::OnStateTransientError() {
  525. // If the job appears to be stuck, handle the transient error as if
  526. // it were a final error. This causes the job to be cancelled and a specific
  527. // error be returned, if the error was available.
  528. if (IsStuck()) {
  529. return OnStateError();
  530. }
  531. // Don't retry at all if the transient error was a 5xx.
  532. HRESULT error_code = S_OK;
  533. HRESULT hr = GetJobError(job_, &error_code);
  534. if (SUCCEEDED(hr) &&
  535. IsHttpServerError(GetHttpStatusFromBitsError(error_code))) {
  536. return OnStateError();
  537. }
  538. return false;
  539. }
  540. bool BackgroundDownloader::OnStateQueued() {
  541. if (!IsStuck())
  542. return false;
  543. // Terminate the download if the job has not made progress in a while.
  544. EndDownload(E_ABORT);
  545. return true;
  546. }
  547. bool BackgroundDownloader::OnStateTransferring() {
  548. // Resets the baseline for detecting a stuck job since the job is transferring
  549. // data and it is making progress.
  550. job_stuck_begin_time_ = base::TimeTicks::Now();
  551. int64_t downloaded_bytes = -1;
  552. int64_t total_bytes = -1;
  553. GetJobByteCount(job_, &downloaded_bytes, &total_bytes);
  554. main_task_runner()->PostTask(
  555. FROM_HERE, base::BindOnce(&BackgroundDownloader::OnDownloadProgress, this,
  556. downloaded_bytes, total_bytes));
  557. return false;
  558. }
  559. HRESULT BackgroundDownloader::QueueBitsJob(const GURL& url,
  560. ComPtr<IBackgroundCopyJob>* job) {
  561. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  562. size_t num_jobs = std::numeric_limits<size_t>::max();
  563. HRESULT hr = GetBackgroundDownloaderJobCount(&num_jobs);
  564. // The metric records a large number if the job count can't be read.
  565. UMA_HISTOGRAM_COUNTS_100("UpdateClient.BackgroundDownloaderJobs", num_jobs);
  566. // Remove some old jobs from the BITS queue before creating new jobs.
  567. CleanupStaleJobs();
  568. if (FAILED(hr) || num_jobs >= kMaxQueuedJobs)
  569. return MAKE_HRESULT(SEVERITY_ERROR, FACILITY_ITF,
  570. CrxDownloaderError::BITS_TOO_MANY_JOBS);
  571. ComPtr<IBackgroundCopyJob> local_job;
  572. hr = CreateOrOpenJob(url, &local_job);
  573. if (FAILED(hr)) {
  574. CleanupJob(local_job);
  575. return hr;
  576. }
  577. const bool is_new_job = hr == S_OK;
  578. UMA_HISTOGRAM_BOOLEAN("UpdateClient.BackgroundDownloaderNewJob", is_new_job);
  579. hr = local_job->Resume();
  580. if (FAILED(hr)) {
  581. CleanupJob(local_job);
  582. return hr;
  583. }
  584. *job = local_job;
  585. return S_OK;
  586. }
  587. HRESULT BackgroundDownloader::CreateOrOpenJob(const GURL& url,
  588. ComPtr<IBackgroundCopyJob>* job) {
  589. std::vector<ComPtr<IBackgroundCopyJob>> jobs;
  590. HRESULT hr = FindBitsJobIf(
  591. [&url](ComPtr<IBackgroundCopyJob> job) {
  592. return JobFileUrlEqualPredicate(job, url);
  593. },
  594. bits_manager_, &jobs);
  595. if (SUCCEEDED(hr) && !jobs.empty()) {
  596. *job = jobs.front();
  597. return S_FALSE;
  598. }
  599. ComPtr<IBackgroundCopyJob> local_job;
  600. GUID guid = {0};
  601. hr = bits_manager_->CreateJob(kJobName, BG_JOB_TYPE_DOWNLOAD, &guid,
  602. &local_job);
  603. if (FAILED(hr)) {
  604. CleanupJob(local_job);
  605. return hr;
  606. }
  607. hr = InitializeNewJob(local_job, url);
  608. if (FAILED(hr)) {
  609. CleanupJob(local_job);
  610. return hr;
  611. }
  612. *job = local_job;
  613. return S_OK;
  614. }
  615. HRESULT BackgroundDownloader::InitializeNewJob(
  616. const ComPtr<IBackgroundCopyJob>& job,
  617. const GURL& url) {
  618. base::FilePath tempdir;
  619. if (!base::CreateNewTempDirectory(FILE_PATH_LITERAL("chrome_BITS_"),
  620. &tempdir))
  621. return E_FAIL;
  622. const std::wstring filename(base::SysUTF8ToWide(url.ExtractFileName()));
  623. HRESULT hr = job->AddFile(base::SysUTF8ToWide(url.spec()).c_str(),
  624. tempdir.Append(filename).value().c_str());
  625. if (FAILED(hr))
  626. return hr;
  627. hr = job->SetDescription(filename.c_str());
  628. if (FAILED(hr))
  629. return hr;
  630. hr = job->SetPriority(BG_JOB_PRIORITY_NORMAL);
  631. if (FAILED(hr))
  632. return hr;
  633. hr = job->SetMinimumRetryDelay(60 * kMinimumRetryDelayMin);
  634. if (FAILED(hr))
  635. return hr;
  636. const int kSecondsDay = 60 * 60 * 24;
  637. hr = job->SetNoProgressTimeout(kSecondsDay * kSetNoProgressTimeoutDays);
  638. if (FAILED(hr))
  639. return hr;
  640. return S_OK;
  641. }
  642. bool BackgroundDownloader::IsStuck() {
  643. const base::TimeDelta job_stuck_timeout(base::Minutes(kJobStuckTimeoutMin));
  644. return job_stuck_begin_time_ + job_stuck_timeout < base::TimeTicks::Now();
  645. }
  646. HRESULT BackgroundDownloader::CompleteJob() {
  647. HRESULT hr = job_->Complete();
  648. if (FAILED(hr) && hr != BG_S_UNABLE_TO_DELETE_FILES)
  649. return hr;
  650. std::vector<ComPtr<IBackgroundCopyFile>> files;
  651. hr = GetFilesInJob(job_, &files);
  652. if (FAILED(hr))
  653. return hr;
  654. if (files.empty())
  655. return E_UNEXPECTED;
  656. std::wstring local_name;
  657. BG_FILE_PROGRESS progress = {0};
  658. hr = GetJobFileProperties(files.front(), &local_name, nullptr, &progress);
  659. if (FAILED(hr))
  660. return hr;
  661. // Check the post-conditions of a successful download, including the file and
  662. // job invariants. The byte counts for a job and its file must match as a job
  663. // only contains one file.
  664. DCHECK(progress.Completed);
  665. DCHECK_EQ(progress.BytesTotal, progress.BytesTransferred);
  666. response_ = base::FilePath(local_name);
  667. return S_OK;
  668. }
  669. HRESULT BackgroundDownloader::UpdateInterfacePointers() {
  670. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  671. bits_manager_ = nullptr;
  672. job_ = nullptr;
  673. ComPtr<IGlobalInterfaceTable> git;
  674. HRESULT hr = GetGit(&git);
  675. if (FAILED(hr))
  676. return hr;
  677. hr = GetInterfaceFromGit(git, git_cookie_bits_manager_,
  678. IID_PPV_ARGS(&bits_manager_));
  679. if (FAILED(hr))
  680. return hr;
  681. hr = GetInterfaceFromGit(git, git_cookie_job_, IID_PPV_ARGS(&job_));
  682. if (FAILED(hr))
  683. return hr;
  684. return S_OK;
  685. }
  686. void BackgroundDownloader::ResetInterfacePointers() {
  687. job_ = nullptr;
  688. bits_manager_ = nullptr;
  689. }
  690. HRESULT BackgroundDownloader::ClearGit() {
  691. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  692. ResetInterfacePointers();
  693. ComPtr<IGlobalInterfaceTable> git;
  694. HRESULT hr = GetGit(&git);
  695. if (FAILED(hr))
  696. return hr;
  697. const DWORD cookies[] = {
  698. git_cookie_job_, git_cookie_bits_manager_
  699. };
  700. for (auto cookie : cookies) {
  701. // TODO(sorin): check the result of the call, see crbug.com/644857.
  702. git->RevokeInterfaceFromGlobal(cookie);
  703. }
  704. return S_OK;
  705. }
  706. HRESULT BackgroundDownloader::GetBackgroundDownloaderJobCount(
  707. size_t* num_jobs) {
  708. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  709. DCHECK(bits_manager_);
  710. std::vector<ComPtr<IBackgroundCopyJob>> jobs;
  711. const HRESULT hr =
  712. FindBitsJobIf([](const ComPtr<IBackgroundCopyJob>&) { return true; },
  713. bits_manager_, &jobs);
  714. if (FAILED(hr))
  715. return hr;
  716. *num_jobs = jobs.size();
  717. return S_OK;
  718. }
  719. void BackgroundDownloader::CleanupStaleJobs() {
  720. DCHECK(com_task_runner_->RunsTasksInCurrentSequence());
  721. DCHECK(bits_manager_);
  722. static base::Time last_sweep;
  723. const base::TimeDelta time_delta(
  724. base::Days(kPurgeStaleJobsIntervalBetweenChecksDays));
  725. const base::Time current_time(base::Time::Now());
  726. if (last_sweep + time_delta > current_time)
  727. return;
  728. last_sweep = current_time;
  729. std::vector<ComPtr<IBackgroundCopyJob>> jobs;
  730. FindBitsJobIf(
  731. [](ComPtr<IBackgroundCopyJob> job) {
  732. return JobCreationOlderThanDaysPredicate(job, kPurgeStaleJobsAfterDays);
  733. },
  734. bits_manager_, &jobs);
  735. for (const auto& job : jobs)
  736. CleanupJob(job);
  737. }
  738. } // namespace update_client