Update mojo surfaces bindings and mojo/cc/ glue
[chromium-blink-merge.git] / chrome / browser / upgrade_detector_impl.cc
blobf0e854c88f15c6742dd613a0ac50f5f437b1b44c
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/upgrade_detector_impl.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/build_time.h"
11 #include "base/command_line.h"
12 #include "base/cpu.h"
13 #include "base/files/file_path.h"
14 #include "base/memory/scoped_ptr.h"
15 #include "base/memory/singleton.h"
16 #include "base/path_service.h"
17 #include "base/prefs/pref_service.h"
18 #include "base/process/launch.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/time/time.h"
23 #include "chrome/browser/browser_process.h"
24 #include "chrome/browser/google/google_brand.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/chrome_version_info.h"
27 #include "chrome/common/pref_names.h"
28 #include "components/network_time/network_time_tracker.h"
29 #include "content/public/browser/browser_thread.h"
31 #if defined(OS_WIN)
32 #include "base/win/win_util.h"
33 #include "chrome/installer/util/browser_distribution.h"
34 #include "chrome/installer/util/google_update_settings.h"
35 #include "chrome/installer/util/helper.h"
36 #include "chrome/installer/util/install_util.h"
37 #elif defined(OS_MACOSX)
38 #include "chrome/browser/mac/keystone_glue.h"
39 #endif
41 using content::BrowserThread;
43 namespace {
45 // How long (in milliseconds) to wait (each cycle) before checking whether
46 // Chrome's been upgraded behind our back.
47 const int kCheckForUpgradeMs = 2 * 60 * 60 * 1000; // 2 hours.
49 // How long to wait (each cycle) before checking which severity level we should
50 // be at. Once we reach the highest severity, the timer will stop.
51 const int kNotifyCycleTimeMs = 20 * 60 * 1000; // 20 minutes.
53 // Same as kNotifyCycleTimeMs but only used during testing.
54 const int kNotifyCycleTimeForTestingMs = 500; // Half a second.
56 // The number of days after which we identify a build/install as outdated.
57 const uint64 kOutdatedBuildAgeInDays = 12 * 7;
59 // Return the string that was passed as a value for the
60 // kCheckForUpdateIntervalSec switch.
61 std::string CmdLineInterval() {
62 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
63 return cmd_line.GetSwitchValueASCII(switches::kCheckForUpdateIntervalSec);
66 // Check if one of the outdated simulation switches was present on the command
67 // line.
68 bool SimulatingOutdated() {
69 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
70 return cmd_line.HasSwitch(switches::kSimulateOutdated) ||
71 cmd_line.HasSwitch(switches::kSimulateOutdatedNoAU);
74 // Check if any of the testing switches was present on the command line.
75 bool IsTesting() {
76 const CommandLine& cmd_line = *CommandLine::ForCurrentProcess();
77 return cmd_line.HasSwitch(switches::kSimulateUpgrade) ||
78 cmd_line.HasSwitch(switches::kCheckForUpdateIntervalSec) ||
79 cmd_line.HasSwitch(switches::kSimulateCriticalUpdate) ||
80 SimulatingOutdated();
83 // How often to check for an upgrade.
84 int GetCheckForUpgradeEveryMs() {
85 // Check for a value passed via the command line.
86 int interval_ms;
87 std::string interval = CmdLineInterval();
88 if (!interval.empty() && base::StringToInt(interval, &interval_ms))
89 return interval_ms * 1000; // Command line value is in seconds.
91 return kCheckForUpgradeMs;
94 // Return true if the current build is one of the unstable channels.
95 bool IsUnstableChannel() {
96 // TODO(mad): Investigate whether we still need to be on the file thread for
97 // this. On Windows, the file thread used to be required for registry access
98 // but no anymore. But other platform may still need the file thread.
99 // crbug.com/366647.
100 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
101 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
102 return channel == chrome::VersionInfo::CHANNEL_DEV ||
103 channel == chrome::VersionInfo::CHANNEL_CANARY;
106 // This task identifies whether we are running an unstable version. And then it
107 // unconditionally calls back the provided task.
108 void CheckForUnstableChannel(const base::Closure& callback_task,
109 bool* is_unstable_channel) {
110 *is_unstable_channel = IsUnstableChannel();
111 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback_task);
114 #if defined(OS_WIN)
115 // Return true if the currently running Chrome is a system install.
116 bool IsSystemInstall() {
117 // Get the version of the currently *installed* instance of Chrome,
118 // which might be newer than the *running* instance if we have been
119 // upgraded in the background.
120 base::FilePath exe_path;
121 if (!PathService::Get(base::DIR_EXE, &exe_path)) {
122 NOTREACHED() << "Failed to find executable path";
123 return false;
126 return !InstallUtil::IsPerUserInstall(exe_path.value().c_str());
129 // Sets |is_unstable_channel| to true if the current chrome is on the dev or
130 // canary channels. Sets |is_auto_update_enabled| to true if Google Update will
131 // update the current chrome. Unconditionally posts |callback_task| to the UI
132 // thread to continue processing.
133 void DetectUpdatability(const base::Closure& callback_task,
134 bool* is_unstable_channel,
135 bool* is_auto_update_enabled) {
136 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
138 base::string16 app_guid = installer::GetAppGuidForUpdates(IsSystemInstall());
139 DCHECK(!app_guid.empty());
140 // Don't try to turn on autoupdate when we failed previously.
141 if (is_auto_update_enabled) {
142 *is_auto_update_enabled =
143 GoogleUpdateSettings::AreAutoupdatesEnabled(app_guid);
145 *is_unstable_channel = IsUnstableChannel();
146 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, callback_task);
148 #endif // defined(OS_WIN)
150 // Gets the currently installed version. On Windows, if |critical_update| is not
151 // NULL, also retrieves the critical update version info if available.
152 base::Version GetCurrentlyInstalledVersionImpl(Version* critical_update) {
153 base::ThreadRestrictions::AssertIOAllowed();
155 Version installed_version;
156 #if defined(OS_WIN)
157 // Get the version of the currently *installed* instance of Chrome,
158 // which might be newer than the *running* instance if we have been
159 // upgraded in the background.
160 bool system_install = IsSystemInstall();
162 // TODO(tommi): Check if using the default distribution is always the right
163 // thing to do.
164 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
165 InstallUtil::GetChromeVersion(dist, system_install, &installed_version);
166 if (critical_update && installed_version.IsValid()) {
167 InstallUtil::GetCriticalUpdateVersion(dist, system_install,
168 critical_update);
170 #elif defined(OS_MACOSX)
171 installed_version =
172 Version(base::UTF16ToASCII(keystone_glue::CurrentlyInstalledVersion()));
173 #elif defined(OS_POSIX)
174 // POSIX but not Mac OS X: Linux, etc.
175 CommandLine command_line(*CommandLine::ForCurrentProcess());
176 command_line.AppendSwitch(switches::kProductVersion);
177 std::string reply;
178 if (!base::GetAppOutput(command_line, &reply)) {
179 DLOG(ERROR) << "Failed to get current file version";
180 return installed_version;
183 installed_version = Version(reply);
184 #endif
185 return installed_version;
188 } // namespace
190 UpgradeDetectorImpl::UpgradeDetectorImpl()
191 : weak_factory_(this),
192 is_unstable_channel_(false),
193 is_auto_update_enabled_(true),
194 build_date_(base::GetBuildTime()) {
195 CommandLine command_line(*CommandLine::ForCurrentProcess());
196 // The different command line switches that affect testing can't be used
197 // simultaneously, if they do, here's the precedence order, based on the order
198 // of the if statements below:
199 // - kDisableBackgroundNetworking prevents any of the other command line
200 // switch from being taken into account.
201 // - kSimulateUpgrade supersedes critical or outdated upgrade switches.
202 // - kSimulateCriticalUpdate has precedence over kSimulateOutdated.
203 // - kSimulateOutdatedNoAU has precedence over kSimulateOutdated.
204 // - kSimulateOutdated[NoAu] can work on its own, or with a specified date.
205 if (command_line.HasSwitch(switches::kDisableBackgroundNetworking))
206 return;
207 if (command_line.HasSwitch(switches::kSimulateUpgrade)) {
208 UpgradeDetected(UPGRADE_AVAILABLE_REGULAR);
209 return;
211 if (command_line.HasSwitch(switches::kSimulateCriticalUpdate)) {
212 UpgradeDetected(UPGRADE_AVAILABLE_CRITICAL);
213 return;
215 if (SimulatingOutdated()) {
216 // The outdated simulation can work without a value, which means outdated
217 // now, or with a value that must be a well formed date/time string that
218 // overrides the build date.
219 // Also note that to test with a given time/date, until the network time
220 // tracking moves off of the VariationsService, the "variations-server-url"
221 // command line switch must also be specified for the service to be
222 // available on non GOOGLE_CHROME_BUILD.
223 std::string switch_name;
224 if (command_line.HasSwitch(switches::kSimulateOutdatedNoAU)) {
225 is_auto_update_enabled_ = false;
226 switch_name = switches::kSimulateOutdatedNoAU;
227 } else {
228 switch_name = switches::kSimulateOutdated;
230 std::string build_date = command_line.GetSwitchValueASCII(switch_name);
231 base::Time maybe_build_time;
232 bool result = base::Time::FromString(build_date.c_str(), &maybe_build_time);
233 if (result && !maybe_build_time.is_null()) {
234 // We got a valid build date simulation so use it and check for upgrades.
235 build_date_ = maybe_build_time;
236 StartTimerForUpgradeCheck();
237 } else {
238 // Without a valid date, we simulate that we are already outdated...
239 UpgradeDetected(
240 is_auto_update_enabled_ ? UPGRADE_NEEDED_OUTDATED_INSTALL
241 : UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU);
243 return;
246 // Register for experiment notifications. Note that since this class is a
247 // singleton, it does not need to unregister for notifications when destroyed,
248 // since it outlives the VariationsService.
249 chrome_variations::VariationsService* variations_service =
250 g_browser_process->variations_service();
251 if (variations_service)
252 variations_service->AddObserver(this);
254 base::Closure start_upgrade_check_timer_task =
255 base::Bind(&UpgradeDetectorImpl::StartTimerForUpgradeCheck,
256 weak_factory_.GetWeakPtr());
258 #if defined(OS_WIN)
259 // Only enable upgrade notifications for official builds. Chromium has no
260 // upgrade channel.
261 #if defined(GOOGLE_CHROME_BUILD)
262 // On Windows, there might be a policy/enterprise environment preventing
263 // updates, so validate updatability, and then call StartTimerForUpgradeCheck
264 // appropriately. And don't check for autoupdate if we already attempted to
265 // enable it in the past.
266 bool attempted_enabling_autoupdate = g_browser_process->local_state() &&
267 g_browser_process->local_state()->GetBoolean(
268 prefs::kAttemptedToEnableAutoupdate);
269 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
270 base::Bind(&DetectUpdatability,
271 start_upgrade_check_timer_task,
272 &is_unstable_channel_,
273 attempted_enabling_autoupdate ?
274 NULL : &is_auto_update_enabled_));
275 #endif
276 #else
277 #if defined(OS_MACOSX)
278 // Only enable upgrade notifications if the updater (Keystone) is present.
279 if (!keystone_glue::KeystoneEnabled()) {
280 is_auto_update_enabled_ = false;
281 return;
283 #elif defined(OS_POSIX)
284 // Always enable upgrade notifications regardless of branding.
285 #else
286 return;
287 #endif
288 // Check whether the build is an unstable channel before starting the timer.
289 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
290 base::Bind(&CheckForUnstableChannel,
291 start_upgrade_check_timer_task,
292 &is_unstable_channel_));
293 #endif
296 UpgradeDetectorImpl::~UpgradeDetectorImpl() {
299 // static
300 base::Version UpgradeDetectorImpl::GetCurrentlyInstalledVersion() {
301 return GetCurrentlyInstalledVersionImpl(NULL);
304 // static
305 // This task checks the currently running version of Chrome against the
306 // installed version. If the installed version is newer, it calls back
307 // UpgradeDetectorImpl::UpgradeDetected using a weak pointer so that it can
308 // be interrupted from the UI thread.
309 void UpgradeDetectorImpl::DetectUpgradeTask(
310 base::WeakPtr<UpgradeDetectorImpl> upgrade_detector) {
311 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
313 Version critical_update;
314 Version installed_version =
315 GetCurrentlyInstalledVersionImpl(&critical_update);
317 // Get the version of the currently *running* instance of Chrome.
318 chrome::VersionInfo version_info;
319 if (!version_info.is_valid()) {
320 NOTREACHED() << "Failed to get current file version";
321 return;
323 Version running_version(version_info.Version());
324 if (!running_version.IsValid()) {
325 NOTREACHED();
326 return;
329 // |installed_version| may be NULL when the user downgrades on Linux (by
330 // switching from dev to beta channel, for example). The user needs a
331 // restart in this case as well. See http://crbug.com/46547
332 if (!installed_version.IsValid() ||
333 (installed_version.CompareTo(running_version) > 0)) {
334 // If a more recent version is available, it might be that we are lacking
335 // a critical update, such as a zero-day fix.
336 UpgradeAvailable upgrade_available = UPGRADE_AVAILABLE_REGULAR;
337 if (critical_update.IsValid() &&
338 critical_update.CompareTo(running_version) > 0) {
339 upgrade_available = UPGRADE_AVAILABLE_CRITICAL;
342 // Fire off the upgrade detected task.
343 BrowserThread::PostTask(BrowserThread::UI, FROM_HERE,
344 base::Bind(&UpgradeDetectorImpl::UpgradeDetected,
345 upgrade_detector,
346 upgrade_available));
350 void UpgradeDetectorImpl::StartTimerForUpgradeCheck() {
351 detect_upgrade_timer_.Start(FROM_HERE,
352 base::TimeDelta::FromMilliseconds(GetCheckForUpgradeEveryMs()),
353 this, &UpgradeDetectorImpl::CheckForUpgrade);
356 void UpgradeDetectorImpl::StartUpgradeNotificationTimer() {
357 // The timer may already be running (e.g. due to both a software upgrade and
358 // experiment updates being available).
359 if (upgrade_notification_timer_.IsRunning())
360 return;
362 upgrade_detected_time_ = base::Time::Now();
364 // Start the repeating timer for notifying the user after a certain period.
365 // The called function will eventually figure out that enough time has passed
366 // and stop the timer.
367 const int cycle_time_ms = IsTesting() ?
368 kNotifyCycleTimeForTestingMs : kNotifyCycleTimeMs;
369 upgrade_notification_timer_.Start(FROM_HERE,
370 base::TimeDelta::FromMilliseconds(cycle_time_ms),
371 this, &UpgradeDetectorImpl::NotifyOnUpgrade);
374 void UpgradeDetectorImpl::CheckForUpgrade() {
375 // Interrupt any (unlikely) unfinished execution of DetectUpgradeTask, or at
376 // least prevent the callback from being executed, because we will potentially
377 // call it from within DetectOutdatedInstall() or will post
378 // DetectUpgradeTask again below anyway.
379 weak_factory_.InvalidateWeakPtrs();
381 // No need to look for upgrades if the install is outdated.
382 if (DetectOutdatedInstall())
383 return;
385 // We use FILE as the thread to run the upgrade detection code on all
386 // platforms. For Linux, this is because we don't want to block the UI thread
387 // while launching a background process and reading its output; on the Mac and
388 // on Windows checking for an upgrade requires reading a file.
389 BrowserThread::PostTask(BrowserThread::FILE, FROM_HERE,
390 base::Bind(&UpgradeDetectorImpl::DetectUpgradeTask,
391 weak_factory_.GetWeakPtr()));
394 bool UpgradeDetectorImpl::DetectOutdatedInstall() {
395 // Don't show the bubble if we have a brand code that is NOT organic, unless
396 // an outdated build is being simulated by command line switches.
397 static bool simulate_outdated = SimulatingOutdated();
398 if (!simulate_outdated) {
399 std::string brand;
400 if (google_brand::GetBrand(&brand) && !google_brand::IsOrganic(brand))
401 return false;
403 #if defined(OS_WIN)
404 // Don't show the update bubbles to enterprise users (i.e., on a domain).
405 if (base::win::IsEnrolledToDomain())
406 return false;
408 // On Windows, we don't want to warn about outdated installs when the
409 // machine doesn't support SSE2, it's been deprecated starting with M35.
410 if (!base::CPU().has_sse2())
411 return false;
412 #endif
415 base::Time network_time;
416 base::TimeDelta uncertainty;
417 if (!g_browser_process->network_time_tracker()->GetNetworkTime(
418 base::TimeTicks::Now(), &network_time, &uncertainty)) {
419 // When network time has not been initialized yet, simply rely on the
420 // machine's current time.
421 network_time = base::Time::Now();
424 if (network_time.is_null() || build_date_.is_null() ||
425 build_date_ > network_time) {
426 NOTREACHED();
427 return false;
430 if (network_time - build_date_ >
431 base::TimeDelta::FromDays(kOutdatedBuildAgeInDays)) {
432 UpgradeDetected(is_auto_update_enabled_ ?
433 UPGRADE_NEEDED_OUTDATED_INSTALL :
434 UPGRADE_NEEDED_OUTDATED_INSTALL_NO_AU);
435 return true;
437 // If we simlated an outdated install with a date, we don't want to keep
438 // checking for version upgrades, which happens on non-official builds.
439 return simulate_outdated;
442 void UpgradeDetectorImpl::OnExperimentChangesDetected(Severity severity) {
443 set_best_effort_experiment_updates_available(severity == BEST_EFFORT);
444 set_critical_experiment_updates_available(severity == CRITICAL);
445 StartUpgradeNotificationTimer();
448 void UpgradeDetectorImpl::UpgradeDetected(UpgradeAvailable upgrade_available) {
449 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
450 set_upgrade_available(upgrade_available);
452 // Stop the recurring timer (that is checking for changes).
453 detect_upgrade_timer_.Stop();
454 set_critical_update_acknowledged(false);
456 StartUpgradeNotificationTimer();
459 void UpgradeDetectorImpl::NotifyOnUpgradeWithTimePassed(
460 base::TimeDelta time_passed) {
461 const bool is_critical_or_outdated =
462 upgrade_available() > UPGRADE_AVAILABLE_REGULAR ||
463 critical_experiment_updates_available();
464 if (is_unstable_channel_) {
465 // There's only one threat level for unstable channels like dev and
466 // canary, and it hits after one hour. During testing, it hits after one
467 // second.
468 const base::TimeDelta unstable_threshold = IsTesting() ?
469 base::TimeDelta::FromSeconds(1) : base::TimeDelta::FromHours(1);
471 if (is_critical_or_outdated) {
472 set_upgrade_notification_stage(UPGRADE_ANNOYANCE_CRITICAL);
473 } else if (time_passed >= unstable_threshold) {
474 set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
476 // That's as high as it goes.
477 upgrade_notification_timer_.Stop();
478 } else {
479 return; // Not ready to recommend upgrade.
481 } else {
482 const base::TimeDelta multiplier = IsTesting() ?
483 base::TimeDelta::FromSeconds(10) : base::TimeDelta::FromDays(1);
485 // 14 days when not testing, otherwise 140 seconds.
486 const base::TimeDelta severe_threshold = 14 * multiplier;
487 const base::TimeDelta high_threshold = 7 * multiplier;
488 const base::TimeDelta elevated_threshold = 4 * multiplier;
489 const base::TimeDelta low_threshold = 2 * multiplier;
491 // These if statements must be sorted (highest interval first).
492 if (time_passed >= severe_threshold || is_critical_or_outdated) {
493 set_upgrade_notification_stage(
494 is_critical_or_outdated ? UPGRADE_ANNOYANCE_CRITICAL :
495 UPGRADE_ANNOYANCE_SEVERE);
497 // We can't get any higher, baby.
498 upgrade_notification_timer_.Stop();
499 } else if (time_passed >= high_threshold) {
500 set_upgrade_notification_stage(UPGRADE_ANNOYANCE_HIGH);
501 } else if (time_passed >= elevated_threshold) {
502 set_upgrade_notification_stage(UPGRADE_ANNOYANCE_ELEVATED);
503 } else if (time_passed >= low_threshold) {
504 set_upgrade_notification_stage(UPGRADE_ANNOYANCE_LOW);
505 } else {
506 return; // Not ready to recommend upgrade.
510 NotifyUpgradeRecommended();
513 void UpgradeDetectorImpl::NotifyOnUpgrade() {
514 const base::TimeDelta time_passed =
515 base::Time::Now() - upgrade_detected_time_;
516 NotifyOnUpgradeWithTimePassed(time_passed);
519 // static
520 UpgradeDetectorImpl* UpgradeDetectorImpl::GetInstance() {
521 return Singleton<UpgradeDetectorImpl>::get();
524 // static
525 UpgradeDetector* UpgradeDetector::GetInstance() {
526 return UpgradeDetectorImpl::GetInstance();