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