Home | History | Annotate | Download | only in browser
      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