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