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(×); 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