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"
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/rand_util.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_split.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/extensions/api/module/module.h"
21 #include "chrome/browser/extensions/crx_installer.h"
22 #include "chrome/browser/extensions/extension_service.h"
23 #include "chrome/browser/extensions/pending_extension_manager.h"
24 #include "chrome/browser/extensions/updater/extension_downloader.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/common/pref_names.h"
27 #include "content/public/browser/browser_thread.h"
28 #include "content/public/browser/notification_details.h"
29 #include "content/public/browser/notification_service.h"
30 #include "content/public/browser/notification_source.h"
31 #include "crypto/sha2.h"
32 #include "extensions/browser/extension_prefs.h"
33 #include "extensions/browser/extension_registry.h"
34 #include "extensions/browser/pref_names.h"
35 #include "extensions/common/constants.h"
36 #include "extensions/common/extension.h"
37 #include "extensions/common/extension_set.h"
38 #include "extensions/common/manifest.h"
40 using base::RandDouble
;
43 using base::TimeDelta
;
44 using content::BrowserThread
;
46 typedef extensions::ExtensionDownloaderDelegate::Error Error
;
47 typedef extensions::ExtensionDownloaderDelegate::PingResult PingResult
;
51 // Wait at least 5 minutes after browser startup before we do any checks. If you
52 // change this value, make sure to update comments where it is used.
53 const int kStartupWaitSeconds
= 60 * 5;
55 // For sanity checking on update frequency - enforced in release mode only.
57 const int kMinUpdateFrequencySeconds
= 30;
59 const int kMaxUpdateFrequencySeconds
= 60 * 60 * 24 * 7; // 7 days
61 // Require at least 5 seconds between consecutive non-succesful extension update
63 const int kMinUpdateThrottleTime
= 5;
65 // When we've computed a days value, we want to make sure we don't send a
66 // negative value (due to the system clock being set backwards, etc.), since -1
67 // is a special sentinel value that means "never pinged", and other negative
68 // values don't make sense.
69 int SanitizeDays(int days
) {
75 // Calculates the value to use for the ping days parameter.
76 int CalculatePingDays(const Time
& last_ping_day
) {
77 int days
= extensions::ManifestFetchData::kNeverPinged
;
78 if (!last_ping_day
.is_null()) {
79 days
= SanitizeDays((Time::Now() - last_ping_day
).InDays());
84 int CalculateActivePingDays(const Time
& last_active_ping_day
,
88 if (last_active_ping_day
.is_null())
89 return extensions::ManifestFetchData::kNeverPinged
;
90 return SanitizeDays((Time::Now() - last_active_ping_day
).InDays());
95 namespace extensions
{
97 ExtensionUpdater::CheckParams::CheckParams()
98 : install_immediately(false) {}
100 ExtensionUpdater::CheckParams::~CheckParams() {}
102 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile(
103 const std::string
& i
,
104 const base::FilePath
& p
,
105 bool file_ownership_passed
,
106 const std::set
<int>& request_ids
)
109 file_ownership_passed(file_ownership_passed
),
110 request_ids(request_ids
) {}
112 ExtensionUpdater::FetchedCRXFile::FetchedCRXFile()
113 : path(), file_ownership_passed(true) {}
115 ExtensionUpdater::FetchedCRXFile::~FetchedCRXFile() {}
117 ExtensionUpdater::InProgressCheck::InProgressCheck()
118 : install_immediately(false) {}
120 ExtensionUpdater::InProgressCheck::~InProgressCheck() {}
122 struct ExtensionUpdater::ThrottleInfo
{
125 throttle_delay(kMinUpdateThrottleTime
),
126 check_start(Time::Now()) {}
133 ExtensionUpdater::ExtensionUpdater(ExtensionServiceInterface
* service
,
134 ExtensionPrefs
* extension_prefs
,
137 int frequency_seconds
,
138 ExtensionCache
* cache
)
140 weak_ptr_factory_(this),
141 service_(service
), frequency_seconds_(frequency_seconds
),
142 will_check_soon_(false), extension_prefs_(extension_prefs
),
143 prefs_(prefs
), profile_(profile
),
145 crx_install_is_running_(false),
146 extension_cache_(cache
) {
147 DCHECK_GE(frequency_seconds_
, 5);
148 DCHECK_LE(frequency_seconds_
, kMaxUpdateFrequencySeconds
);
150 // In Release mode we enforce that update checks don't happen too often.
151 frequency_seconds_
= std::max(frequency_seconds_
, kMinUpdateFrequencySeconds
);
153 frequency_seconds_
= std::min(frequency_seconds_
, kMaxUpdateFrequencySeconds
);
156 chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED
,
157 content::NotificationService::AllBrowserContextsAndSources());
160 ExtensionUpdater::~ExtensionUpdater() {
164 // The overall goal here is to balance keeping clients up to date while
165 // avoiding a thundering herd against update servers.
166 TimeDelta
ExtensionUpdater::DetermineFirstCheckDelay() {
168 // If someone's testing with a quick frequency, just allow it.
169 if (frequency_seconds_
< kStartupWaitSeconds
)
170 return TimeDelta::FromSeconds(frequency_seconds_
);
172 // If we've never scheduled a check before, start at frequency_seconds_.
173 if (!prefs_
->HasPrefPath(pref_names::kNextUpdateCheck
))
174 return TimeDelta::FromSeconds(frequency_seconds_
);
176 // If it's been a long time since our last actual check, we want to do one
178 Time now
= Time::Now();
179 Time last
= Time::FromInternalValue(prefs_
->GetInt64(
180 pref_names::kLastUpdateCheck
));
181 int days
= (now
- last
).InDays();
183 // Wait 5-10 minutes.
184 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
,
185 kStartupWaitSeconds
* 2));
186 } else if (days
>= 14) {
187 // Wait 10-20 minutes.
188 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
* 2,
189 kStartupWaitSeconds
* 4));
190 } else if (days
>= 3) {
191 // Wait 20-40 minutes.
192 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
* 4,
193 kStartupWaitSeconds
* 8));
196 // Read the persisted next check time, and use that if it isn't too soon
197 // or too late. Otherwise pick something random.
198 Time saved_next
= Time::FromInternalValue(prefs_
->GetInt64(
199 pref_names::kNextUpdateCheck
));
200 Time earliest
= now
+ TimeDelta::FromSeconds(kStartupWaitSeconds
);
201 Time latest
= now
+ TimeDelta::FromSeconds(frequency_seconds_
);
202 if (saved_next
>= earliest
&& saved_next
<= latest
) {
203 return saved_next
- now
;
205 return TimeDelta::FromSeconds(RandInt(kStartupWaitSeconds
,
206 frequency_seconds_
));
210 void ExtensionUpdater::Start() {
212 // If these are NULL, then that means we've been called after Stop()
215 DCHECK(extension_prefs_
);
218 DCHECK(!weak_ptr_factory_
.HasWeakPtrs());
220 // Make sure our prefs are registered, then schedule the first check.
221 ScheduleNextCheck(DetermineFirstCheckDelay());
224 void ExtensionUpdater::Stop() {
225 weak_ptr_factory_
.InvalidateWeakPtrs();
228 extension_prefs_
= NULL
;
232 will_check_soon_
= false;
236 void ExtensionUpdater::ScheduleNextCheck(const TimeDelta
& target_delay
) {
238 DCHECK(!timer_
.IsRunning());
239 DCHECK(target_delay
>= TimeDelta::FromSeconds(1));
241 // Add +/- 10% random jitter.
242 double delay_ms
= target_delay
.InMillisecondsF();
243 double jitter_factor
= (RandDouble() * .2) - 0.1;
244 delay_ms
+= delay_ms
* jitter_factor
;
245 TimeDelta actual_delay
= TimeDelta::FromMilliseconds(
246 static_cast<int64
>(delay_ms
));
248 // Save the time of next check.
249 Time next
= Time::Now() + actual_delay
;
250 prefs_
->SetInt64(pref_names::kNextUpdateCheck
, next
.ToInternalValue());
252 timer_
.Start(FROM_HERE
, actual_delay
, this, &ExtensionUpdater::TimerFired
);
255 void ExtensionUpdater::TimerFired() {
257 CheckNow(default_params_
);
259 // If the user has overridden the update frequency, don't bother reporting
261 if (frequency_seconds_
== extensions::kDefaultUpdateFrequencySeconds
) {
262 Time last
= Time::FromInternalValue(prefs_
->GetInt64(
263 pref_names::kLastUpdateCheck
));
264 if (last
.ToInternalValue() != 0) {
265 // Use counts rather than time so we can use minutes rather than millis.
266 UMA_HISTOGRAM_CUSTOM_COUNTS("Extensions.UpdateCheckGap",
267 (Time::Now() - last
).InMinutes(),
268 TimeDelta::FromSeconds(kStartupWaitSeconds
).InMinutes(),
269 TimeDelta::FromDays(40).InMinutes(),
270 50); // 50 buckets seems to be the default.
274 // Save the last check time, and schedule the next check.
275 int64 now
= Time::Now().ToInternalValue();
276 prefs_
->SetInt64(pref_names::kLastUpdateCheck
, now
);
277 ScheduleNextCheck(TimeDelta::FromSeconds(frequency_seconds_
));
280 void ExtensionUpdater::CheckSoon() {
282 if (will_check_soon_
)
284 if (BrowserThread::PostTask(
285 BrowserThread::UI
, FROM_HERE
,
286 base::Bind(&ExtensionUpdater::DoCheckSoon
,
287 weak_ptr_factory_
.GetWeakPtr()))) {
288 will_check_soon_
= true;
294 bool ExtensionUpdater::WillCheckSoon() const {
295 return will_check_soon_
;
298 void ExtensionUpdater::DoCheckSoon() {
299 DCHECK(will_check_soon_
);
300 CheckNow(default_params_
);
301 will_check_soon_
= false;
304 void ExtensionUpdater::AddToDownloader(
305 const ExtensionSet
* extensions
,
306 const std::list
<std::string
>& pending_ids
,
308 InProgressCheck
& request
= requests_in_progress_
[request_id
];
309 for (ExtensionSet::const_iterator extension_iter
= extensions
->begin();
310 extension_iter
!= extensions
->end(); ++extension_iter
) {
311 const Extension
& extension
= *extension_iter
->get();
312 if (!Manifest::IsAutoUpdateableLocation(extension
.location())) {
313 VLOG(2) << "Extension " << extension
.id() << " is not auto updateable";
316 // An extension might be overwritten by policy, and have its update url
317 // changed. Make sure existing extensions aren't fetched again, if a
318 // pending fetch for an extension with the same id already exists.
319 std::list
<std::string
>::const_iterator pending_id_iter
= std::find(
320 pending_ids
.begin(), pending_ids
.end(), extension
.id());
321 if (pending_id_iter
== pending_ids
.end()) {
322 if (downloader_
->AddExtension(extension
, request_id
))
323 request
.in_progress_ids_
.push_back(extension
.id());
328 void ExtensionUpdater::CheckNow(const CheckParams
& params
) {
329 int request_id
= next_request_id_
++;
331 VLOG(2) << "Starting update check " << request_id
;
332 if (params
.ids
.empty())
337 InProgressCheck
& request
= requests_in_progress_
[request_id
];
338 request
.callback
= params
.callback
;
339 request
.install_immediately
= params
.install_immediately
;
341 if (!downloader_
.get()) {
343 new ExtensionDownloader(this, profile_
->GetRequestContext()));
346 // Add fetch records for extensions that should be fetched by an update URL.
347 // These extensions are not yet installed. They come from group policy
348 // and external install sources.
349 const PendingExtensionManager
* pending_extension_manager
=
350 service_
->pending_extension_manager();
352 std::list
<std::string
> pending_ids
;
354 if (params
.ids
.empty()) {
355 // If no extension ids are specified, check for updates for all extensions.
356 pending_extension_manager
->GetPendingIdsForUpdateCheck(&pending_ids
);
358 std::list
<std::string
>::const_iterator iter
;
359 for (iter
= pending_ids
.begin(); iter
!= pending_ids
.end(); ++iter
) {
360 const PendingExtensionInfo
* info
= pending_extension_manager
->GetById(
362 if (!Manifest::IsAutoUpdateableLocation(info
->install_source())) {
363 VLOG(2) << "Extension " << *iter
<< " is not auto updateable";
366 if (downloader_
->AddPendingExtension(*iter
, info
->update_url(),
368 request
.in_progress_ids_
.push_back(*iter
);
371 ExtensionRegistry
* registry
= ExtensionRegistry::Get(profile_
);
372 AddToDownloader(®istry
->enabled_extensions(), pending_ids
, request_id
);
373 AddToDownloader(®istry
->disabled_extensions(), pending_ids
, request_id
);
375 for (std::list
<std::string
>::const_iterator it
= params
.ids
.begin();
376 it
!= params
.ids
.end(); ++it
) {
377 const Extension
* extension
= service_
->GetExtensionById(*it
, true);
379 if (downloader_
->AddExtension(*extension
, request_id
))
380 request
.in_progress_ids_
.push_back(extension
->id());
384 // StartAllPending() might call OnExtensionDownloadFailed/Finished before
385 // it returns, which would cause NotifyIfFinished to incorrectly try to
386 // send out a notification. So check before we call StartAllPending if any
387 // extensions are going to be updated, and use that to figure out if
388 // NotifyIfFinished should be called.
389 bool noChecks
= request
.in_progress_ids_
.empty();
391 // StartAllPending() will call OnExtensionDownloadFailed or
392 // OnExtensionDownloadFinished for each extension that was checked.
393 downloader_
->StartAllPending(extension_cache_
);
396 NotifyIfFinished(request_id
);
399 bool ExtensionUpdater::CheckExtensionSoon(const std::string
& extension_id
,
400 const FinishedCallback
& callback
) {
401 bool have_throttle_info
= ContainsKey(throttle_info_
, extension_id
);
402 ThrottleInfo
& info
= throttle_info_
[extension_id
];
403 if (have_throttle_info
) {
404 // We already had a ThrottleInfo object for this extension, check if the
405 // update check request should be allowed.
407 // If another check is in progress, don't start a new check.
408 if (info
.in_progress
)
411 Time now
= Time::Now();
412 Time last
= info
.check_start
;
413 // If somehow time moved back, we don't want to infinitely keep throttling.
416 info
.check_start
= now
;
418 Time earliest
= last
+ TimeDelta::FromSeconds(info
.throttle_delay
);
419 // If check is too soon, throttle.
423 // TODO(mek): Somehow increase time between allowing checks when checks
424 // are repeatedly throttled and don't result in updates being installed.
426 // It's okay to start a check, update values.
427 info
.check_start
= now
;
428 info
.in_progress
= true;
432 params
.ids
.push_back(extension_id
);
433 params
.callback
= base::Bind(&ExtensionUpdater::ExtensionCheckFinished
,
434 weak_ptr_factory_
.GetWeakPtr(),
435 extension_id
, callback
);
440 void ExtensionUpdater::ExtensionCheckFinished(
441 const std::string
& extension_id
,
442 const FinishedCallback
& callback
) {
443 std::map
<std::string
, ThrottleInfo
>::iterator it
=
444 throttle_info_
.find(extension_id
);
445 if (it
!= throttle_info_
.end()) {
446 it
->second
.in_progress
= false;
451 void ExtensionUpdater::OnExtensionDownloadFailed(
452 const std::string
& id
,
454 const PingResult
& ping
,
455 const std::set
<int>& request_ids
) {
457 UpdatePingData(id
, ping
);
458 bool install_immediately
= false;
459 for (std::set
<int>::const_iterator it
= request_ids
.begin();
460 it
!= request_ids
.end(); ++it
) {
461 InProgressCheck
& request
= requests_in_progress_
[*it
];
462 install_immediately
|= request
.install_immediately
;
463 request
.in_progress_ids_
.remove(id
);
464 NotifyIfFinished(*it
);
467 // This method is called if no updates were found. However a previous update
468 // check might have queued an update for this extension already. If a
469 // current update check has |install_immediately| set the previously
470 // queued update should be installed now.
471 if (install_immediately
&& service_
->GetPendingExtensionUpdate(id
))
472 service_
->FinishDelayedInstallation(id
);
475 void ExtensionUpdater::OnExtensionDownloadFinished(
476 const std::string
& id
,
477 const base::FilePath
& path
,
478 bool file_ownership_passed
,
479 const GURL
& download_url
,
480 const std::string
& version
,
481 const PingResult
& ping
,
482 const std::set
<int>& request_ids
) {
484 UpdatePingData(id
, ping
);
486 VLOG(2) << download_url
<< " written to " << path
.value();
488 FetchedCRXFile
fetched(id
, path
, file_ownership_passed
, request_ids
);
489 fetched_crx_files_
.push(fetched
);
491 // MaybeInstallCRXFile() removes extensions from |in_progress_ids_| after
492 // starting the crx installer.
493 MaybeInstallCRXFile();
496 bool ExtensionUpdater::GetPingDataForExtension(
497 const std::string
& id
,
498 ManifestFetchData::PingData
* ping_data
) {
500 ping_data
->rollcall_days
= CalculatePingDays(
501 extension_prefs_
->LastPingDay(id
));
502 ping_data
->is_enabled
= service_
->IsExtensionEnabled(id
);
503 ping_data
->active_days
=
504 CalculateActivePingDays(extension_prefs_
->LastActivePingDay(id
),
505 extension_prefs_
->GetActiveBit(id
));
509 std::string
ExtensionUpdater::GetUpdateUrlData(const std::string
& id
) {
511 return extension::GetUpdateURLData(extension_prefs_
, id
);
514 bool ExtensionUpdater::IsExtensionPending(const std::string
& id
) {
516 return service_
->pending_extension_manager()->IsIdPending(id
);
519 bool ExtensionUpdater::GetExtensionExistingVersion(const std::string
& id
,
520 std::string
* version
) {
522 const Extension
* extension
= service_
->GetExtensionById(id
, true);
525 const Extension
* update
= service_
->GetPendingExtensionUpdate(id
);
527 *version
= update
->VersionString();
529 *version
= extension
->VersionString();
533 void ExtensionUpdater::UpdatePingData(const std::string
& id
,
534 const PingResult
& ping_result
) {
536 if (ping_result
.did_ping
)
537 extension_prefs_
->SetLastPingDay(id
, ping_result
.day_start
);
538 if (extension_prefs_
->GetActiveBit(id
)) {
539 extension_prefs_
->SetActiveBit(id
, false);
540 extension_prefs_
->SetLastActivePingDay(id
, ping_result
.day_start
);
544 void ExtensionUpdater::MaybeInstallCRXFile() {
545 if (crx_install_is_running_
|| fetched_crx_files_
.empty())
548 std::set
<int> request_ids
;
550 while (!fetched_crx_files_
.empty() && !crx_install_is_running_
) {
551 const FetchedCRXFile
& crx_file
= fetched_crx_files_
.top();
553 VLOG(2) << "updating " << crx_file
.extension_id
554 << " with " << crx_file
.path
.value();
556 // The ExtensionService is now responsible for cleaning up the temp file
557 // at |crx_file.path|.
558 CrxInstaller
* installer
= NULL
;
559 if (service_
->UpdateExtension(crx_file
.extension_id
,
561 crx_file
.file_ownership_passed
,
563 crx_install_is_running_
= true;
564 current_crx_file_
= crx_file
;
566 for (std::set
<int>::const_iterator it
= crx_file
.request_ids
.begin();
567 it
!= crx_file
.request_ids
.end(); ++it
) {
568 InProgressCheck
& request
= requests_in_progress_
[*it
];
569 if (request
.install_immediately
) {
570 installer
->set_install_wait_for_idle(false);
575 // Source parameter ensures that we only see the completion event for the
576 // the installer we started.
578 chrome::NOTIFICATION_CRX_INSTALLER_DONE
,
579 content::Source
<CrxInstaller
>(installer
));
581 for (std::set
<int>::const_iterator it
= crx_file
.request_ids
.begin();
582 it
!= crx_file
.request_ids
.end(); ++it
) {
583 InProgressCheck
& request
= requests_in_progress_
[*it
];
584 request
.in_progress_ids_
.remove(crx_file
.extension_id
);
586 request_ids
.insert(crx_file
.request_ids
.begin(),
587 crx_file
.request_ids
.end());
589 fetched_crx_files_
.pop();
592 for (std::set
<int>::const_iterator it
= request_ids
.begin();
593 it
!= request_ids
.end(); ++it
) {
594 NotifyIfFinished(*it
);
598 void ExtensionUpdater::Observe(int type
,
599 const content::NotificationSource
& source
,
600 const content::NotificationDetails
& details
) {
602 case chrome::NOTIFICATION_CRX_INSTALLER_DONE
: {
603 // No need to listen for CRX_INSTALLER_DONE anymore.
604 registrar_
.Remove(this,
605 chrome::NOTIFICATION_CRX_INSTALLER_DONE
,
607 crx_install_is_running_
= false;
609 const FetchedCRXFile
& crx_file
= current_crx_file_
;
610 for (std::set
<int>::const_iterator it
= crx_file
.request_ids
.begin();
611 it
!= crx_file
.request_ids
.end(); ++it
) {
612 InProgressCheck
& request
= requests_in_progress_
[*it
];
613 request
.in_progress_ids_
.remove(crx_file
.extension_id
);
614 NotifyIfFinished(*it
);
617 // If any files are available to update, start one.
618 MaybeInstallCRXFile();
621 case chrome::NOTIFICATION_EXTENSION_INSTALLED_DEPRECATED
: {
622 const Extension
* extension
=
623 content::Details
<const InstalledExtensionInfo
>(details
)->extension
;
625 throttle_info_
.erase(extension
->id());
633 void ExtensionUpdater::NotifyStarted() {
634 content::NotificationService::current()->Notify(
635 chrome::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