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