1 // Copyright (c) 2012 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.
5 #include "chrome/browser/extensions/updater/extension_updater.h"
10 #include "base/bind.h"
11 #include "base/logging.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/rand_util.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/string_split.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/extensions/api/module/module.h"
20 #include "chrome/browser/extensions/crx_installer.h"
21 #include "chrome/browser/extensions/extension_service.h"
22 #include "chrome/browser/extensions/pending_extension_manager.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/common/pref_names.h"
25 #include "content/public/browser/browser_thread.h"
26 #include "content/public/browser/notification_details.h"
27 #include "content/public/browser/notification_service.h"
28 #include "content/public/browser/notification_source.h"
29 #include "crypto/sha2.h"
30 #include "extensions/browser/extension_prefs.h"
31 #include "extensions/browser/extension_registry.h"
32 #include "extensions/browser/pref_names.h"
33 #include "extensions/common/constants.h"
34 #include "extensions/common/extension.h"
35 #include "extensions/common/extension_set.h"
36 #include "extensions/common/manifest.h"
38 using base::RandDouble;
41 using base::TimeDelta;
42 using content::BrowserThread;
44 typedef extensions::ExtensionDownloaderDelegate::Error Error;
45 typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult;
49 // Wait at least 5 minutes after browser startup before we do any checks. If you
50 // change this value, make sure to update comments where it is used.
51 const int kStartupWaitSeconds = 60 * 5;
53 // For sanity checking on update frequency - enforced in release mode only.
55 const int kMinUpdateFrequencySeconds = 30;
57 const int kMaxUpdateFrequencySeconds = 60 * 60 * 24 * 7; // 7 days
59 // Require at least 5 seconds between consecutive non-succesful extension update
61 const int kMinUpdateThrottleTime = 5;
63 // When we've computed a days value, we want to make sure we don't send a
64 // negative value (due to the system clock being set backwards, etc.), since -1
65 // is a special sentinel value that means "never pinged", and other negative
66 // values don't make sense.
67 int SanitizeDays(int days) {
73 // Calculates the value to use for the ping days parameter.
74 int CalculatePingDays(const Time& last_ping_day) {
75 int days = extensions::ManifestFetchData::kNeverPinged;
76 if (!last_ping_day.is_null()) {
77 days = SanitizeDays((Time::Now() - last_ping_day).InDays());
82 int CalculateActivePingDays(const Time& last_active_ping_day,
86 if (last_active_ping_day.is_null())
87 return extensions::ManifestFetchData::kNeverPinged;
88 return SanitizeDays((Time::Now() - last_active_ping_day).InDays());
93 namespace extensions {
95 ExtensionUpdater::CheckParams::CheckParams()
96 : install_immediately(false) {}
98 ExtensionUpdater::CheckParams::~CheckParams() {}
100 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(
101 const std::string& i,
102 const base::FilePath& p,
103 bool file_ownership_passed,
104 const std::set<int>& request_ids)
107 file_ownership_passed(file_ownership_passed),
108 request_ids(request_ids) {}
110 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
111 : path(), file_ownership_passed(true) {}
113 ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
115 ExtensionUpdater::InProgressCheck::InProgressCheck()
116 : install_immediately(false) {}
118 ExtensionUpdater::InProgressCheck::~InProgressCheck() {}
120 struct ExtensionUpdater::ThrottleInfo {
123 throttle_delay(kMinUpdateThrottleTime),
124 check_start(Time::Now()) {}
131 ExtensionUpdater::ExtensionUpdater(
132 ExtensionServiceInterface* service,
133 ExtensionPrefs* extension_prefs,
136 int frequency_seconds,
137 ExtensionCache* cache,
138 const ExtensionDownloader::Factory& downloader_factory)
140 weak_ptr_factory_(this),
142 downloader_factory_(downloader_factory),
143 frequency_seconds_(frequency_seconds),
144 will_check_soon_(false),
145 extension_prefs_(extension_prefs),
149 extension_registry_observer_(this),
150 crx_install_is_running_(false),
151 extension_cache_(cache) {
152 DCHECK_GE(frequency_seconds_, 5);
153 DCHECK_LE(frequency_seconds_, kMaxUpdateFrequencySeconds);
155 // In Release mode we enforce that update checks don't happen too often.
156 frequency_seconds_ = std::max(frequency_seconds_, kMinUpdateFrequencySeconds);
158 frequency_seconds_ = std::min(frequency_seconds_, kMaxUpdateFrequencySeconds);
160 extension_registry_observer_.Add(ExtensionRegistry::Get(profile));
163 ExtensionUpdater::~ExtensionUpdater() {
167 void ExtensionUpdater::EnsureDownloaderCreated() {
168 if (!downloader_.get()) {
169 downloader_ = downloader_factory_.Run(this);
173 // The overall goal here is to balance keeping clients up to date while
174 // avoiding a thundering herd against update servers.
175 TimeDelta ExtensionUpdater::DetermineFirstCheckDelay() {
177 // If someone's testing with a quick frequency, just allow it.
178 if (frequency_seconds_ < kStartupWaitSeconds)
179 return TimeDelta::FromSeconds(frequency_seconds_);
181 // If we've never scheduled a check before, start at frequency_seconds_.
182 if (!prefs_->HasPrefPath(pref_names::kNextUpdateCheck))
183 return TimeDelta::FromSeconds(frequency_seconds_);
185 // If it's been a long time since our last actual check, we want to do one
187 Time now = Time::Now();
188 Time last = Time::FromInternalValue(prefs_->GetInt64(
189 pref_names::kLastUpdateCheck));
190 int days = (now - last).InDays();
192 // Wait 5-10 minutes.
193 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
194 kStartupWaitSeconds * 2));
195 } else if (days >= 14) {
196 // Wait 10-20 minutes.
197 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 2,
198 kStartupWaitSeconds * 4));
199 } else if (days >= 3) {
200 // Wait 20-40 minutes.
201 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds * 4,
202 kStartupWaitSeconds * 8));
205 // Read the persisted next check time, and use that if it isn't too soon
206 // or too late. Otherwise pick something random.
207 Time saved_next = Time::FromInternalValue(prefs_->GetInt64(
208 pref_names::kNextUpdateCheck));
209 Time earliest = now + TimeDelta::FromSeconds(kStartupWaitSeconds);
210 Time latest = now + TimeDelta::FromSeconds(frequency_seconds_);
211 if (saved_next >= earliest && saved_next <= latest) {
212 return saved_next - now;
214 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds,
215 frequency_seconds_));
219 void ExtensionUpdater::Start() {
221 // If these are NULL, then that means we've been called after Stop()
224 DCHECK(extension_prefs_);
227 DCHECK(!weak_ptr_factory_.HasWeakPtrs());
229 // Make sure our prefs are registered, then schedule the first check.
230 ScheduleNextCheck(DetermineFirstCheckDelay());
233 void ExtensionUpdater::Stop() {
234 weak_ptr_factory_.InvalidateWeakPtrs();
237 extension_prefs_ = NULL;
241 will_check_soon_ = false;
245 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta& target_delay) {
247 DCHECK(!timer_.IsRunning());
248 DCHECK(target_delay >= TimeDelta::FromSeconds(1));
250 // Add +/- 10% random jitter.
251 double delay_ms = target_delay.InMillisecondsF();
252 double jitter_factor = (RandDouble() * .2) - 0.1;
253 delay_ms += delay_ms * jitter_factor;
254 TimeDelta actual_delay = TimeDelta::FromMilliseconds(
255 static_cast<int64>(delay_ms));
257 // Save the time of next check.
258 Time next = Time::Now() + actual_delay;
259 prefs_->SetInt64(pref_names::kNextUpdateCheck, next.ToInternalValue());
261 timer_.Start(FROM_HERE, actual_delay, this, &ExtensionUpdater::TimerFired);
264 void ExtensionUpdater::TimerFired() {
266 CheckNow(default_params_);
268 // If the user has overridden the update frequency, don't bother reporting
270 if (frequency_seconds_ == extensions::kDefaultUpdateFrequencySeconds) {
271 Time last = Time::FromInternalValue(prefs_->GetInt64(
272 pref_names::kLastUpdateCheck));
273 if (last.ToInternalValue() != 0) {
274 // Use counts rather than time so we can use minutes rather than millis.
275 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
276 (Time::Now() - last).InMinutes(),
277 TimeDelta::FromSeconds(kStartupWaitSeconds).InMinutes(),
278 TimeDelta::FromDays(40).InMinutes(),
279 50); // 50 buckets seems to be the default.
283 // Save the last check time, and schedule the next check.
284 int64 now = Time::Now().ToInternalValue();
285 prefs_->SetInt64(pref_names::kLastUpdateCheck, now);
286 ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_));
289 void ExtensionUpdater::CheckSoon() {
291 if (will_check_soon_)
293 if (BrowserThread::PostTask(
294 BrowserThread::UI, FROM_HERE,
295 base::Bind(&ExtensionUpdater::DoCheckSoon,
296 weak_ptr_factory_.GetWeakPtr()))) {
297 will_check_soon_ = true;
303 bool ExtensionUpdater::WillCheckSoon() const {
304 return will_check_soon_;
307 void ExtensionUpdater::DoCheckSoon() {
308 DCHECK(will_check_soon_);
309 CheckNow(default_params_);
310 will_check_soon_ = false;
313 void ExtensionUpdater::AddToDownloader(
314 const ExtensionSet* extensions,
315 const std::list<std::string>& pending_ids,
317 InProgressCheck& request = requests_in_progress_[request_id];
318 for (ExtensionSet::const_iterator extension_iter = extensions->begin();
319 extension_iter != extensions->end(); ++extension_iter) {
320 const Extension& extension = *extension_iter->get();
321 if (!Manifest::IsAutoUpdateableLocation(extension.location())) {
322 VLOG(2) << "Extension " << extension.id() << " is not auto updateable";
325 // An extension might be overwritten by policy, and have its update url
326 // changed. Make sure existing extensions aren't fetched again, if a
327 // pending fetch for an extension with the same id already exists.
328 std::list<std::string>::const_iterator pending_id_iter = std::find(
329 pending_ids.begin(), pending_ids.end(), extension.id());
330 if (pending_id_iter == pending_ids.end()) {
331 if (downloader_->AddExtension(extension, request_id))
332 request.in_progress_ids_.push_back(extension.id());
337 void ExtensionUpdater::CheckNow(const CheckParams& params) {
338 int request_id = next_request_id_++;
340 VLOG(2) << "Starting update check " << request_id;
341 if (params.ids.empty())
346 InProgressCheck& request = requests_in_progress_[request_id];
347 request.callback = params.callback;
348 request.install_immediately = params.install_immediately;
350 EnsureDownloaderCreated();
352 // Add fetch records for extensions that should be fetched by an update URL.
353 // These extensions are not yet installed. They come from group policy
354 // and external install sources.
355 const PendingExtensionManager* pending_extension_manager =
356 service_->pending_extension_manager();
358 std::list<std::string> pending_ids;
360 if (params.ids.empty()) {
361 // If no extension ids are specified, check for updates for all extensions.
362 pending_extension_manager->GetPendingIdsForUpdateCheck(&pending_ids);
364 std::list<std::string>::const_iterator iter;
365 for (iter = pending_ids.begin(); iter != pending_ids.end(); ++iter) {
366 const PendingExtensionInfo* info = pending_extension_manager->GetById(
368 if (!Manifest::IsAutoUpdateableLocation(info->install_source())) {
369 VLOG(2) << "Extension " << *iter << " is not auto updateable";
372 if (downloader_->AddPendingExtension(*iter, info->update_url(),
374 request.in_progress_ids_.push_back(*iter);
377 ExtensionRegistry* registry = ExtensionRegistry::Get(profile_);
378 AddToDownloader(®istry->enabled_extensions(), pending_ids, request_id);
379 AddToDownloader(®istry->disabled_extensions(), pending_ids, request_id);
381 for (std::list<std::string>::const_iterator it = params.ids.begin();
382 it != params.ids.end(); ++it) {
383 const Extension* extension = service_->GetExtensionById(*it, true);
385 if (downloader_->AddExtension(*extension, request_id))
386 request.in_progress_ids_.push_back(extension->id());
390 // StartAllPending() might call OnExtensionDownloadFailed/Finished before
391 // it returns, which would cause NotifyIfFinished to incorrectly try to
392 // send out a notification. So check before we call StartAllPending if any
393 // extensions are going to be updated, and use that to figure out if
394 // NotifyIfFinished should be called.
395 bool noChecks = request.in_progress_ids_.empty();
397 // StartAllPending() will call OnExtensionDownloadFailed or
398 // OnExtensionDownloadFinished for each extension that was checked.
399 downloader_->StartAllPending(extension_cache_);
402 NotifyIfFinished(request_id);
405 bool ExtensionUpdater::CheckExtensionSoon(const std::string& extension_id,
406 const FinishedCallback& callback) {
407 bool have_throttle_info = ContainsKey(throttle_info_, extension_id);
408 ThrottleInfo& info = throttle_info_[extension_id];
409 if (have_throttle_info) {
410 // We already had a ThrottleInfo object for this extension, check if the
411 // update check request should be allowed.
413 // If another check is in progress, don't start a new check.
414 if (info.in_progress)
417 Time now = Time::Now();
418 Time last = info.check_start;
419 // If somehow time moved back, we don't want to infinitely keep throttling.
422 info.check_start = now;
424 Time earliest = last + TimeDelta::FromSeconds(info.throttle_delay);
425 // If check is too soon, throttle.
429 // TODO(mek): Somehow increase time between allowing checks when checks
430 // are repeatedly throttled and don't result in updates being installed.
432 // It's okay to start a check, update values.
433 info.check_start = now;
434 info.in_progress = true;
438 params.ids.push_back(extension_id);
439 params.callback = base::Bind(&ExtensionUpdater::ExtensionCheckFinished,
440 weak_ptr_factory_.GetWeakPtr(),
441 extension_id, callback);
446 void ExtensionUpdater::ExtensionCheckFinished(
447 const std::string& extension_id,
448 const FinishedCallback& callback) {
449 std::map<std::string, ThrottleInfo>::iterator it =
450 throttle_info_.find(extension_id);
451 if (it != throttle_info_.end()) {
452 it->second.in_progress = false;
457 void ExtensionUpdater::OnExtensionDownloadFailed(
458 const std::string& id,
460 const PingResult& ping,
461 const std::set<int>& request_ids) {
463 UpdatePingData(id, ping);
464 bool install_immediately = false;
465 for (std::set<int>::const_iterator it = request_ids.begin();
466 it != request_ids.end(); ++it) {
467 InProgressCheck& request = requests_in_progress_[*it];
468 install_immediately |= request.install_immediately;
469 request.in_progress_ids_.remove(id);
470 NotifyIfFinished(*it);
473 // This method is called if no updates were found. However a previous update
474 // check might have queued an update for this extension already. If a
475 // current update check has |install_immediately| set the previously
476 // queued update should be installed now.
477 if (install_immediately && service_->GetPendingExtensionUpdate(id))
478 service_->FinishDelayedInstallation(id);
481 void ExtensionUpdater::OnExtensionDownloadFinished(
482 const std::string& id,
483 const base::FilePath& path,
484 bool file_ownership_passed,
485 const GURL& download_url,
486 const std::string& version,
487 const PingResult& ping,
488 const std::set<int>& request_ids) {
490 UpdatePingData(id, ping);
492 VLOG(2) << download_url << " written to " << path.value();
494 FetchedCRXFile fetched(id, path, file_ownership_passed, request_ids);
495 fetched_crx_files_.push(fetched);
497 // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
498 // starting the crx installer.
499 MaybeInstallCRXFile();
502 bool ExtensionUpdater::GetPingDataForExtension(
503 const std::string& id,
504 ManifestFetchData::PingData* ping_data) {
506 ping_data->rollcall_days = CalculatePingDays(
507 extension_prefs_->LastPingDay(id));
508 ping_data->is_enabled = service_->IsExtensionEnabled(id);
509 ping_data->active_days =
510 CalculateActivePingDays(extension_prefs_->LastActivePingDay(id),
511 extension_prefs_->GetActiveBit(id));
515 std::string ExtensionUpdater::GetUpdateUrlData(const std::string& id) {
517 return extension::GetUpdateURLData(extension_prefs_, id);
520 bool ExtensionUpdater::IsExtensionPending(const std::string& id) {
522 return service_->pending_extension_manager()->IsIdPending(id);
525 bool ExtensionUpdater::GetExtensionExistingVersion(const std::string& id,
526 std::string* version) {
528 const Extension* extension = service_->GetExtensionById(id, true);
531 const Extension* update = service_->GetPendingExtensionUpdate(id);
533 *version = update->VersionString();
535 *version = extension->VersionString();
539 void ExtensionUpdater::UpdatePingData(const std::string& id,
540 const PingResult& ping_result) {
542 if (ping_result.did_ping)
543 extension_prefs_->SetLastPingDay(id, ping_result.day_start);
544 if (extension_prefs_->GetActiveBit(id)) {
545 extension_prefs_->SetActiveBit(id, false);
546 extension_prefs_->SetLastActivePingDay(id, ping_result.day_start);
550 void ExtensionUpdater::MaybeInstallCRXFile() {
551 if (crx_install_is_running_ || fetched_crx_files_.empty())
554 std::set<int> request_ids;
556 while (!fetched_crx_files_.empty() && !crx_install_is_running_) {
557 const FetchedCRXFile& crx_file = fetched_crx_files_.top();
559 VLOG(2) << "updating " << crx_file.extension_id
560 << " with " << crx_file.path.value();
562 // The ExtensionService is now responsible for cleaning up the temp file
563 // at |crx_file.path|.
564 CrxInstaller* installer = NULL;
565 if (service_->UpdateExtension(crx_file.extension_id,
567 crx_file.file_ownership_passed,
569 crx_install_is_running_ = true;
570 current_crx_file_ = crx_file;
572 for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
573 it != crx_file.request_ids.end(); ++it) {
574 InProgressCheck& request = requests_in_progress_[*it];
575 if (request.install_immediately) {
576 installer->set_install_immediately(true);
581 // Source parameter ensures that we only see the completion event for the
582 // the installer we started.
584 extensions::NOTIFICATION_CRX_INSTALLER_DONE,
585 content::Source<CrxInstaller>(installer));
587 for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
588 it != crx_file.request_ids.end(); ++it) {
589 InProgressCheck& request = requests_in_progress_[*it];
590 request.in_progress_ids_.remove(crx_file.extension_id);
592 request_ids.insert(crx_file.request_ids.begin(),
593 crx_file.request_ids.end());
595 fetched_crx_files_.pop();
598 for (std::set<int>::const_iterator it = request_ids.begin();
599 it != request_ids.end(); ++it) {
600 NotifyIfFinished(*it);
604 void ExtensionUpdater::Observe(int type,
605 const content::NotificationSource& source,
606 const content::NotificationDetails& details) {
607 DCHECK_EQ(type, extensions::NOTIFICATION_CRX_INSTALLER_DONE);
609 registrar_.Remove(this, extensions::NOTIFICATION_CRX_INSTALLER_DONE, source);
610 crx_install_is_running_ = false;
612 const FetchedCRXFile& crx_file = current_crx_file_;
613 for (std::set<int>::const_iterator it = crx_file.request_ids.begin();
614 it != crx_file.request_ids.end(); ++it) {
615 InProgressCheck& request = requests_in_progress_[*it];
616 request.in_progress_ids_.remove(crx_file.extension_id);
617 NotifyIfFinished(*it);
620 // If any files are available to update, start one.
621 MaybeInstallCRXFile();
624 void ExtensionUpdater::OnExtensionWillBeInstalled(
625 content::BrowserContext* browser_context,
626 const Extension* extension,
629 const std::string& old_name) {
630 throttle_info_.erase(extension->id());
633 void ExtensionUpdater::NotifyStarted() {
634 content::NotificationService::current()->Notify(
635 extensions::NOTIFICATION_EXTENSION_UPDATING_STARTED,
636 content::Source<Profile>(profile_),
637 content::NotificationService::NoDetails());
640 void ExtensionUpdater::NotifyIfFinished(int request_id) {
641 DCHECK(ContainsKey(requests_in_progress_, request_id));
642 const InProgressCheck& request = requests_in_progress_[request_id];
643 if (request.in_progress_ids_.empty()) {
644 VLOG(2) << "Finished update check " << request_id;
645 if (!request.callback.is_null())
646 request.callback.Run();
647 requests_in_progress_.erase(request_id);
651 } // namespace extensions