root/chrome/installer/util/google_update_settings.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. ReadGoogleUpdateStrKey
  2. WriteGoogleUpdateStrKeyInternal
  3. WriteGoogleUpdateStrKey
  4. WriteGoogleUpdateStrKeyMultiInstall
  5. ClearGoogleUpdateStrKey
  6. RemoveGoogleUpdateStrKey
  7. GetChromeChannelInternal
  8. GetUpdatePolicyFromDword
  9. IsSystemInstall
  10. GetCollectStatsConsent
  11. GetCollectStatsConsentAtLevel
  12. SetCollectStatsConsent
  13. SetCollectStatsConsentAtLevel
  14. GetMetricsId
  15. SetMetricsId
  16. SetEULAConsent
  17. GetLastRunTime
  18. SetLastRunTime
  19. RemoveLastRunTime
  20. GetBrowser
  21. GetLanguage
  22. GetBrand
  23. GetReactivationBrand
  24. GetClient
  25. SetClient
  26. GetReferral
  27. ClearReferral
  28. UpdateDidRunState
  29. UpdateDidRunStateForDistribution
  30. GetChromeChannel
  31. GetChromeChannelAndModifiers
  32. UpdateInstallStatus
  33. UpdateGoogleUpdateApKey
  34. UpdateProfileCounts
  35. DuplicateGoogleUpdateSystemClientKey
  36. WriteGoogleUpdateSystemClientKey
  37. GetAppUpdatePolicy
  38. AreAutoupdatesEnabled
  39. ReenableAutoupdatesForApp
  40. RecordChromeUpdatePolicyHistograms
  41. GetUninstallCommandLine
  42. GetGoogleUpdateVersion
  43. GetGoogleUpdateLastStartedAU
  44. GetGoogleUpdateLastChecked
  45. GetUpdateDetailForApp
  46. GetUpdateDetailForGoogleUpdate
  47. GetUpdateDetail
  48. SetExperimentLabels
  49. ReadExperimentLabels

// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/installer/util/google_update_settings.h"

#include <algorithm>
#include <string>

#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/path_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread_restrictions.h"
#include "base/time/time.h"
#include "base/win/registry.h"
#include "base/win/win_util.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
#include "chrome/installer/util/google_update_constants.h"
#include "chrome/installer/util/google_update_experiment_util.h"
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/installation_state.h"
#include "chrome/installer/util/product.h"

using base::win::RegKey;
using installer::InstallationState;

const wchar_t GoogleUpdateSettings::kPoliciesKey[] =
    L"SOFTWARE\\Policies\\Google\\Update";
const wchar_t GoogleUpdateSettings::kUpdatePolicyValue[] = L"UpdateDefault";
const wchar_t GoogleUpdateSettings::kUpdateOverrideValuePrefix[] = L"Update";
const wchar_t GoogleUpdateSettings::kCheckPeriodOverrideMinutes[] =
    L"AutoUpdateCheckPeriodMinutes";

// Don't allow update periods longer than six weeks.
const int GoogleUpdateSettings::kCheckPeriodOverrideMinutesMax =
    60 * 24 * 7 * 6;

const GoogleUpdateSettings::UpdatePolicy
GoogleUpdateSettings::kDefaultUpdatePolicy =
#if defined(GOOGLE_CHROME_BUILD)
    GoogleUpdateSettings::AUTOMATIC_UPDATES;
#else
    GoogleUpdateSettings::UPDATES_DISABLED;
#endif

namespace {

bool ReadGoogleUpdateStrKey(const wchar_t* const name, std::wstring* value) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  std::wstring reg_path = dist->GetStateKey();
  RegKey key(HKEY_CURRENT_USER, reg_path.c_str(), KEY_READ);
  if (key.ReadValue(name, value) != ERROR_SUCCESS) {
    RegKey hklm_key(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_READ);
    return (hklm_key.ReadValue(name, value) == ERROR_SUCCESS);
  }
  return true;
}

// Update a state registry key |name| to be |value| for the given browser
// |dist|.  If this is a |system_install|, then update the value under
// HKLM (istead of HKCU for user-installs) using a group of keys (one
// for each OS user) and also include the method to |aggregate| these
// values when reporting.
bool WriteGoogleUpdateStrKeyInternal(BrowserDistribution* dist,
                                     bool system_install,
                                     const wchar_t* const name,
                                     // presubmit: allow wstring
                                     const std::wstring& value,
                                     const wchar_t* const aggregate) {
  DCHECK(dist);

  if (system_install) {
    DCHECK(aggregate);
    // Machine installs require each OS user to write a unique key under a
    // named key in HKLM as well as an "aggregation" function that describes
    // how the values of multiple users are to be combined.
    std::wstring uniquename;  // presubmit: allow wstring
    if (!base::win::GetUserSidString(&uniquename)) {
      NOTREACHED();
      return false;
    }

    // presubmit: allow wstring
    std::wstring reg_path(dist->GetStateMediumKey());
    reg_path.append(L"\\");
    reg_path.append(name);
    RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_SET_VALUE);
    key.WriteValue(google_update::kRegAggregateMethod, aggregate);
    return (key.WriteValue(uniquename.c_str(), value.c_str()) == ERROR_SUCCESS);
  } else {
    // User installs are easy: just write the values to HKCU tree.
    RegKey key(HKEY_CURRENT_USER, dist->GetStateKey().c_str(), KEY_SET_VALUE);
    return (key.WriteValue(name, value.c_str()) == ERROR_SUCCESS);
  }
}

bool WriteGoogleUpdateStrKey(const wchar_t* const name,
                             const std::wstring& value) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  return WriteGoogleUpdateStrKeyInternal(dist, false, name, value, NULL);
}

bool WriteGoogleUpdateStrKeyMultiInstall(BrowserDistribution* dist,
                                         const wchar_t* const name,
                                         const std::wstring& value,
                                         bool system_level) {
  bool result = WriteGoogleUpdateStrKeyInternal(dist, false, name, value, NULL);
  if (!InstallUtil::IsMultiInstall(dist, system_level))
    return result;
  // It is a multi-install distro. Must write the reg value again.
  BrowserDistribution* multi_dist =
      BrowserDistribution::GetSpecificDistribution(
          BrowserDistribution::CHROME_BINARIES);
  return
      WriteGoogleUpdateStrKeyInternal(multi_dist, false, name, value, NULL) &&
      result;
}

bool ClearGoogleUpdateStrKey(const wchar_t* const name) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  std::wstring reg_path = dist->GetStateKey();
  RegKey key(HKEY_CURRENT_USER, reg_path.c_str(), KEY_READ | KEY_WRITE);
  std::wstring value;
  if (key.ReadValue(name, &value) != ERROR_SUCCESS)
    return false;
  return (key.WriteValue(name, L"") == ERROR_SUCCESS);
}

bool RemoveGoogleUpdateStrKey(const wchar_t* const name) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  std::wstring reg_path = dist->GetStateKey();
  RegKey key(HKEY_CURRENT_USER, reg_path.c_str(), KEY_READ | KEY_WRITE);
  if (!key.HasValue(name))
    return true;
  return (key.DeleteValue(name) == ERROR_SUCCESS);
}

bool GetChromeChannelInternal(bool system_install,
                              bool add_multi_modifier,
                              base::string16* channel) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (dist->GetChromeChannel(channel)) {
    return true;
  }

  HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  base::string16 reg_path = dist->GetStateKey();
  RegKey key(root_key, reg_path.c_str(), KEY_READ);

  installer::ChannelInfo channel_info;
  if (!channel_info.Initialize(key)) {
    channel->assign(installer::kChromeChannelUnknown);
    return false;
  }

  if (!channel_info.GetChannelName(channel)) {
    channel->assign(installer::kChromeChannelUnknown);
  }

  // Tag the channel name if this is a multi-install.
  if (add_multi_modifier && channel_info.IsMultiInstall()) {
    if (!channel->empty()) {
      channel->append(1, L'-');
    }
    channel->append(1, L'm');
  }

  return true;
}

// Populates |update_policy| with the UpdatePolicy enum value corresponding to a
// DWORD read from the registry and returns true if |value| is within range.
// If |value| is out of range, returns false without modifying |update_policy|.
bool GetUpdatePolicyFromDword(
    const DWORD value,
    GoogleUpdateSettings::UpdatePolicy* update_policy) {
  switch (value) {
    case GoogleUpdateSettings::UPDATES_DISABLED:
    case GoogleUpdateSettings::AUTOMATIC_UPDATES:
    case GoogleUpdateSettings::MANUAL_UPDATES_ONLY:
    case GoogleUpdateSettings::AUTO_UPDATES_ONLY:
      *update_policy = static_cast<GoogleUpdateSettings::UpdatePolicy>(value);
      return true;
    default:
      LOG(WARNING) << "Unexpected update policy override value: " << value;
  }
  return false;
}

}  // namespace

bool GoogleUpdateSettings::IsSystemInstall() {
  bool system_install = false;
  base::FilePath module_dir;
  if (!PathService::Get(base::DIR_MODULE, &module_dir)) {
    LOG(WARNING)
        << "Failed to get directory of module; assuming per-user install.";
  } else {
    system_install = !InstallUtil::IsPerUserInstall(module_dir.value().c_str());
  }
  return system_install;
}

bool GoogleUpdateSettings::GetCollectStatsConsent() {
  return GetCollectStatsConsentAtLevel(IsSystemInstall());
}

// Older versions of Chrome unconditionally read from HKCU\...\ClientState\...
// and then HKLM\...\ClientState\....  This means that system-level Chrome
// never checked ClientStateMedium (which has priority according to Google
// Update) and gave preference to a value in HKCU (which was never checked by
// Google Update).  From now on, Chrome follows Google Update's policy.
bool GoogleUpdateSettings::GetCollectStatsConsentAtLevel(bool system_install) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();

  // Consent applies to all products in a multi-install package.
  if (InstallUtil::IsMultiInstall(dist, system_install)) {
    dist = BrowserDistribution::GetSpecificDistribution(
        BrowserDistribution::CHROME_BINARIES);
  }

  RegKey key;
  DWORD value = 0;
  bool have_value = false;

  // For system-level installs, try ClientStateMedium first.
  have_value =
      system_install &&
      key.Open(HKEY_LOCAL_MACHINE, dist->GetStateMediumKey().c_str(),
               KEY_QUERY_VALUE) == ERROR_SUCCESS &&
      key.ReadValueDW(google_update::kRegUsageStatsField,
                      &value) == ERROR_SUCCESS;

  // Otherwise, try ClientState.
  if (!have_value) {
    have_value =
        key.Open(system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER,
                 dist->GetStateKey().c_str(),
                 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
        key.ReadValueDW(google_update::kRegUsageStatsField,
                        &value) == ERROR_SUCCESS;
  }

  // Google Update specifically checks that the value is 1, so we do the same.
  return have_value && value == 1;
}

bool GoogleUpdateSettings::SetCollectStatsConsent(bool consented) {
  return SetCollectStatsConsentAtLevel(IsSystemInstall(), consented);
}

bool GoogleUpdateSettings::SetCollectStatsConsentAtLevel(bool system_install,
                                                         bool consented) {
  // Google Update writes and expects 1 for true, 0 for false.
  DWORD value = consented ? 1 : 0;

  BrowserDistribution* dist = BrowserDistribution::GetDistribution();

  // Consent applies to all products in a multi-install package.
  if (InstallUtil::IsMultiInstall(dist, system_install)) {
    dist = BrowserDistribution::GetSpecificDistribution(
        BrowserDistribution::CHROME_BINARIES);
  }

  // Write to ClientStateMedium for system-level; ClientState otherwise.
  HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  std::wstring reg_path =
      system_install ? dist->GetStateMediumKey() : dist->GetStateKey();
  RegKey key;
  LONG result = key.Create(root_key, reg_path.c_str(), KEY_SET_VALUE);
  if (result != ERROR_SUCCESS) {
    LOG(ERROR) << "Failed opening key " << reg_path << " to set "
               << google_update::kRegUsageStatsField << "; result: " << result;
  } else {
    result = key.WriteValue(google_update::kRegUsageStatsField, value);
    LOG_IF(ERROR, result != ERROR_SUCCESS) << "Failed setting "
        << google_update::kRegUsageStatsField << " in key " << reg_path
        << "; result: " << result;
  }
  return (result == ERROR_SUCCESS);
}

bool GoogleUpdateSettings::GetMetricsId(std::string* metrics_id) {
  std::wstring metrics_id_w;
  bool rv = ReadGoogleUpdateStrKey(google_update::kRegMetricsId, &metrics_id_w);
  *metrics_id = base::WideToUTF8(metrics_id_w);
  return rv;
}

bool GoogleUpdateSettings::SetMetricsId(const std::string& metrics_id) {
  std::wstring metrics_id_w = base::UTF8ToWide(metrics_id);
  return WriteGoogleUpdateStrKey(google_update::kRegMetricsId, metrics_id_w);
}

// EULA consent is only relevant for system-level installs.
bool GoogleUpdateSettings::SetEULAConsent(
    const InstallationState& machine_state,
    BrowserDistribution* dist,
    bool consented) {
  DCHECK(dist);
  const DWORD eula_accepted = consented ? 1 : 0;
  std::wstring reg_path = dist->GetStateMediumKey();
  bool succeeded = true;
  RegKey key;

  // Write the consent value into the product's ClientStateMedium key.
  if (key.Create(HKEY_LOCAL_MACHINE, reg_path.c_str(),
                 KEY_SET_VALUE) != ERROR_SUCCESS ||
      key.WriteValue(google_update::kRegEULAAceptedField,
                     eula_accepted) != ERROR_SUCCESS) {
    succeeded = false;
  }

  // If this is a multi-install, also write it into the binaries' key.
  // --mutli-install is not provided on the command-line, so deduce it from
  // the product's state.
  const installer::ProductState* product_state =
      machine_state.GetProductState(true, dist->GetType());
  if (product_state != NULL && product_state->is_multi_install()) {
    dist = BrowserDistribution::GetSpecificDistribution(
        BrowserDistribution::CHROME_BINARIES);
    reg_path = dist->GetStateMediumKey();
    if (key.Create(HKEY_LOCAL_MACHINE, reg_path.c_str(),
                   KEY_SET_VALUE) != ERROR_SUCCESS ||
        key.WriteValue(google_update::kRegEULAAceptedField,
                       eula_accepted) != ERROR_SUCCESS) {
        succeeded = false;
    }
  }

  return succeeded;
}

int GoogleUpdateSettings::GetLastRunTime() {
  std::wstring time_s;
  if (!ReadGoogleUpdateStrKey(google_update::kRegLastRunTimeField, &time_s))
    return -1;
  int64 time_i;
  if (!base::StringToInt64(time_s, &time_i))
    return -1;
  base::TimeDelta td =
      base::Time::NowFromSystemTime() - base::Time::FromInternalValue(time_i);
  return td.InDays();
}

bool GoogleUpdateSettings::SetLastRunTime() {
  int64 time = base::Time::NowFromSystemTime().ToInternalValue();
  return WriteGoogleUpdateStrKey(google_update::kRegLastRunTimeField,
                                 base::Int64ToString16(time));
}

bool GoogleUpdateSettings::RemoveLastRunTime() {
  return RemoveGoogleUpdateStrKey(google_update::kRegLastRunTimeField);
}

bool GoogleUpdateSettings::GetBrowser(std::wstring* browser) {
  return ReadGoogleUpdateStrKey(google_update::kRegBrowserField, browser);
}

bool GoogleUpdateSettings::GetLanguage(std::wstring* language) {
  return ReadGoogleUpdateStrKey(google_update::kRegLangField, language);
}

bool GoogleUpdateSettings::GetBrand(std::wstring* brand) {
  return ReadGoogleUpdateStrKey(google_update::kRegRLZBrandField, brand);
}

bool GoogleUpdateSettings::GetReactivationBrand(std::wstring* brand) {
  return ReadGoogleUpdateStrKey(google_update::kRegRLZReactivationBrandField,
                                brand);
}

bool GoogleUpdateSettings::GetClient(std::wstring* client) {
  return ReadGoogleUpdateStrKey(google_update::kRegClientField, client);
}

bool GoogleUpdateSettings::SetClient(const std::wstring& client) {
  return WriteGoogleUpdateStrKey(google_update::kRegClientField, client);
}

bool GoogleUpdateSettings::GetReferral(std::wstring* referral) {
  return ReadGoogleUpdateStrKey(google_update::kRegReferralField, referral);
}

bool GoogleUpdateSettings::ClearReferral() {
  return ClearGoogleUpdateStrKey(google_update::kRegReferralField);
}

bool GoogleUpdateSettings::UpdateDidRunState(bool did_run,
                                             bool system_level) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  return UpdateDidRunStateForDistribution(dist, did_run, system_level);
}

bool GoogleUpdateSettings::UpdateDidRunStateForDistribution(
    BrowserDistribution* dist,
    bool did_run,
    bool system_level) {
  return WriteGoogleUpdateStrKeyMultiInstall(dist,
                                             google_update::kRegDidRunField,
                                             did_run ? L"1" : L"0",
                                             system_level);
}

std::wstring GoogleUpdateSettings::GetChromeChannel(bool system_install) {
  std::wstring channel;
  GetChromeChannelInternal(system_install, false, &channel);
  return channel;
}

bool GoogleUpdateSettings::GetChromeChannelAndModifiers(
    bool system_install,
    base::string16* channel) {
  return GetChromeChannelInternal(system_install, true, channel);
}

void GoogleUpdateSettings::UpdateInstallStatus(bool system_install,
    installer::ArchiveType archive_type, int install_return_code,
    const std::wstring& product_guid) {
  DCHECK(archive_type != installer::UNKNOWN_ARCHIVE_TYPE ||
         install_return_code != 0);
  HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;

  RegKey key;
  installer::ChannelInfo channel_info;
  std::wstring reg_key(google_update::kRegPathClientState);
  reg_key.append(L"\\");
  reg_key.append(product_guid);
  LONG result = key.Open(reg_root, reg_key.c_str(),
                         KEY_QUERY_VALUE | KEY_SET_VALUE);
  if (result == ERROR_SUCCESS)
    channel_info.Initialize(key);
  else if (result != ERROR_FILE_NOT_FOUND)
    LOG(ERROR) << "Failed to open " << reg_key << "; Error: " << result;

  if (UpdateGoogleUpdateApKey(archive_type, install_return_code,
                              &channel_info)) {
    // We have a modified channel_info value to write.
    // Create the app's ClientState key if it doesn't already exist.
    if (!key.Valid()) {
      result = key.Open(reg_root, google_update::kRegPathClientState,
                        KEY_CREATE_SUB_KEY);
      if (result == ERROR_SUCCESS)
        result = key.CreateKey(product_guid.c_str(), KEY_SET_VALUE);

      if (result != ERROR_SUCCESS) {
        LOG(ERROR) << "Failed to create " << reg_key << "; Error: " << result;
        return;
      }
    }
    if (!channel_info.Write(&key)) {
      LOG(ERROR) << "Failed to write to application's ClientState key "
                 << google_update::kRegApField << " = " << channel_info.value();
    }
  }
}

bool GoogleUpdateSettings::UpdateGoogleUpdateApKey(
    installer::ArchiveType archive_type, int install_return_code,
    installer::ChannelInfo* value) {
  DCHECK(archive_type != installer::UNKNOWN_ARCHIVE_TYPE ||
         install_return_code != 0);
  bool modified = false;

  if (archive_type == installer::FULL_ARCHIVE_TYPE || !install_return_code) {
    if (value->SetFullSuffix(false)) {
      VLOG(1) << "Removed incremental installer failure key; "
                 "switching to channel: "
              << value->value();
      modified = true;
    }
  } else if (archive_type == installer::INCREMENTAL_ARCHIVE_TYPE) {
    if (value->SetFullSuffix(true)) {
      VLOG(1) << "Incremental installer failed; switching to channel: "
              << value->value();
      modified = true;
    } else {
      VLOG(1) << "Incremental installer failure; already on channel: "
              << value->value();
    }
  } else {
    // It's okay if we don't know the archive type.  In this case, leave the
    // "-full" suffix as we found it.
    DCHECK_EQ(installer::UNKNOWN_ARCHIVE_TYPE, archive_type);
  }

  if (value->SetMultiFailSuffix(false)) {
    VLOG(1) << "Removed multi-install failure key; switching to channel: "
            << value->value();
    modified = true;
  }

  return modified;
}

void GoogleUpdateSettings::UpdateProfileCounts(int profiles_active,
                                               int profiles_signedin) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  bool system_install = IsSystemInstall();
  WriteGoogleUpdateStrKeyInternal(dist, system_install,
                                  google_update::kRegProfilesActive,
                                  base::Int64ToString16(profiles_active),
                                  L"sum()");
  WriteGoogleUpdateStrKeyInternal(dist, system_install,
                                  google_update::kRegProfilesSignedIn,
                                  base::Int64ToString16(profiles_signedin),
                                  L"sum()");
}

int GoogleUpdateSettings::DuplicateGoogleUpdateSystemClientKey() {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  std::wstring reg_path = dist->GetStateKey();

  // Minimum access needed is to be able to write to this key.
  RegKey reg_key(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_SET_VALUE);
  if (!reg_key.Valid())
    return 0;

  HANDLE target_handle = 0;
  if (!DuplicateHandle(GetCurrentProcess(), reg_key.Handle(),
                       GetCurrentProcess(), &target_handle, KEY_SET_VALUE,
                       TRUE, DUPLICATE_SAME_ACCESS)) {
    return 0;
  }
  return reinterpret_cast<int>(target_handle);
}

bool GoogleUpdateSettings::WriteGoogleUpdateSystemClientKey(
    int handle, const std::wstring& key, const std::wstring& value) {
  HKEY reg_key = reinterpret_cast<HKEY>(reinterpret_cast<void*>(handle));
  DWORD size = static_cast<DWORD>(value.size()) * sizeof(wchar_t);
  LSTATUS status = RegSetValueEx(reg_key, key.c_str(), 0, REG_SZ,
      reinterpret_cast<const BYTE*>(value.c_str()), size);
  return status == ERROR_SUCCESS;
}

GoogleUpdateSettings::UpdatePolicy GoogleUpdateSettings::GetAppUpdatePolicy(
    const std::wstring& app_guid,
    bool* is_overridden) {
  bool found_override = false;
  UpdatePolicy update_policy = kDefaultUpdatePolicy;

#if defined(GOOGLE_CHROME_BUILD)
  DCHECK(!app_guid.empty());
  RegKey policy_key;

  // Google Update Group Policy settings are always in HKLM.
  if (policy_key.Open(HKEY_LOCAL_MACHINE, kPoliciesKey, KEY_QUERY_VALUE) ==
          ERROR_SUCCESS) {
    DWORD value = 0;
    base::string16 app_update_override(kUpdateOverrideValuePrefix);
    app_update_override.append(app_guid);
    // First try to read and comprehend the app-specific override.
    found_override = (policy_key.ReadValueDW(app_update_override.c_str(),
                                             &value) == ERROR_SUCCESS &&
                      GetUpdatePolicyFromDword(value, &update_policy));

    // Failing that, try to read and comprehend the default override.
    if (!found_override &&
        policy_key.ReadValueDW(kUpdatePolicyValue, &value) == ERROR_SUCCESS) {
      GetUpdatePolicyFromDword(value, &update_policy);
    }
  }
#endif  // defined(GOOGLE_CHROME_BUILD)

  if (is_overridden != NULL)
    *is_overridden = found_override;

  return update_policy;
}

// static
bool GoogleUpdateSettings::AreAutoupdatesEnabled(
    const base::string16& app_guid) {
  // Check the auto-update check period override. If it is 0 or exceeds the
  // maximum timeout, then for all intents and purposes auto updates are
  // disabled.
  RegKey policy_key;
  DWORD value = 0;
  if (policy_key.Open(HKEY_LOCAL_MACHINE, kPoliciesKey,
                      KEY_QUERY_VALUE) == ERROR_SUCCESS &&
      policy_key.ReadValueDW(kCheckPeriodOverrideMinutes,
                             &value) == ERROR_SUCCESS &&
      (value == 0 || value > kCheckPeriodOverrideMinutesMax)) {
    return false;
  }

  UpdatePolicy policy = GetAppUpdatePolicy(app_guid, NULL);
  return (policy == AUTOMATIC_UPDATES || policy == AUTO_UPDATES_ONLY);
}

// static
bool GoogleUpdateSettings::ReenableAutoupdatesForApp(
    const base::string16& app_guid) {
#if defined(GOOGLE_CHROME_BUILD)
  int needs_reset_count = 0;
  int did_reset_count = 0;

  UpdatePolicy update_policy = kDefaultUpdatePolicy;
  RegKey policy_key;
  if (policy_key.Open(HKEY_LOCAL_MACHINE, kPoliciesKey,
                      KEY_SET_VALUE | KEY_QUERY_VALUE) == ERROR_SUCCESS) {
    // First check the app-specific override value and reset that if needed.
    // Note that this intentionally sets the override to AUTOMATIC_UPDATES
    // even if it was previously AUTO_UPDATES_ONLY. The thinking is that
    // AUTOMATIC_UPDATES is marginally more likely to let a user update and this
    // code is only called when a stuck user asks for updates.
    base::string16 app_update_override(kUpdateOverrideValuePrefix);
    app_update_override.append(app_guid);
    DWORD value = 0;
    bool has_app_update_override =
        policy_key.ReadValueDW(app_update_override.c_str(),
                               &value) == ERROR_SUCCESS;
    if (has_app_update_override &&
        (!GetUpdatePolicyFromDword(value, &update_policy) ||
         update_policy != GoogleUpdateSettings::AUTOMATIC_UPDATES)) {
      ++needs_reset_count;
      if (policy_key.WriteValue(
              app_update_override.c_str(),
              static_cast<DWORD>(GoogleUpdateSettings::AUTOMATIC_UPDATES)) ==
                  ERROR_SUCCESS) {
        ++did_reset_count;
      }
    }

    // If there was no app-specific override policy see if there's a global
    // policy preventing updates and delete it if so.
    if (!has_app_update_override &&
        policy_key.ReadValueDW(kUpdatePolicyValue, &value) == ERROR_SUCCESS &&
        (!GetUpdatePolicyFromDword(value, &update_policy) ||
         update_policy != GoogleUpdateSettings::AUTOMATIC_UPDATES)) {
      ++needs_reset_count;
      if (policy_key.DeleteValue(kUpdatePolicyValue) == ERROR_SUCCESS)
        ++did_reset_count;
    }

    // Check the auto-update check period override. If it is 0 or exceeds
    // the maximum timeout, delete the override value.
    if (policy_key.ReadValueDW(kCheckPeriodOverrideMinutes,
                               &value) == ERROR_SUCCESS &&
        (value == 0 || value > kCheckPeriodOverrideMinutesMax)) {
      ++needs_reset_count;
      if (policy_key.DeleteValue(kCheckPeriodOverrideMinutes) == ERROR_SUCCESS)
        ++did_reset_count;
    }

    // Return whether the number of successful resets is the same as the
    // number of things that appeared to need resetting.
    return (needs_reset_count == did_reset_count);
  } else {
    // For some reason we couldn't open the policy key with the desired
    // permissions to make changes (the most likely reason is that there is no
    // policy set). Simply return whether or not we think updates are enabled.
    return AreAutoupdatesEnabled(app_guid);
  }

#endif
  // Non Google Chrome isn't going to autoupdate.
  return true;
}

void GoogleUpdateSettings::RecordChromeUpdatePolicyHistograms() {
  const bool is_multi_install = InstallUtil::IsMultiInstall(
      BrowserDistribution::GetDistribution(), IsSystemInstall());
  const base::string16 app_guid =
      BrowserDistribution::GetSpecificDistribution(
          is_multi_install ? BrowserDistribution::CHROME_BINARIES :
                             BrowserDistribution::CHROME_BROWSER)->GetAppGuid();

  bool is_overridden = false;
  const UpdatePolicy update_policy = GetAppUpdatePolicy(app_guid,
                                                        &is_overridden);
  UMA_HISTOGRAM_BOOLEAN("GoogleUpdate.UpdatePolicyIsOverridden", is_overridden);
  UMA_HISTOGRAM_ENUMERATION("GoogleUpdate.EffectivePolicy", update_policy,
                            UPDATE_POLICIES_COUNT);
}

base::string16 GoogleUpdateSettings::GetUninstallCommandLine(
    bool system_install) {
  const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  base::string16 cmd_line;
  RegKey update_key;

  if (update_key.Open(root_key, google_update::kRegPathGoogleUpdate,
                      KEY_QUERY_VALUE) == ERROR_SUCCESS) {
    update_key.ReadValue(google_update::kRegUninstallCmdLine, &cmd_line);
  }

  return cmd_line;
}

Version GoogleUpdateSettings::GetGoogleUpdateVersion(bool system_install) {
  const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  base::string16 version;
  RegKey key;

  if (key.Open(root_key,
               google_update::kRegPathGoogleUpdate,
               KEY_QUERY_VALUE) == ERROR_SUCCESS &&
      key.ReadValue(google_update::kRegGoogleUpdateVersion,
                    &version) == ERROR_SUCCESS) {
    return Version(base::UTF16ToUTF8(version));
  }

  return Version();
}

base::Time GoogleUpdateSettings::GetGoogleUpdateLastStartedAU(
    bool system_install) {
  const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  RegKey update_key;

  if (update_key.Open(root_key, google_update::kRegPathGoogleUpdate,
                      KEY_QUERY_VALUE) == ERROR_SUCCESS) {
    DWORD last_start;
    if (update_key.ReadValueDW(google_update::kRegLastStartedAUField,
                               &last_start) == ERROR_SUCCESS) {
      return base::Time::FromTimeT(last_start);
    }
  }

  return base::Time();
}

base::Time GoogleUpdateSettings::GetGoogleUpdateLastChecked(
    bool system_install) {
  const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  RegKey update_key;

  if (update_key.Open(root_key, google_update::kRegPathGoogleUpdate,
                      KEY_QUERY_VALUE) == ERROR_SUCCESS) {
    DWORD last_check;
    if (update_key.ReadValueDW(google_update::kRegLastCheckedField,
                               &last_check) == ERROR_SUCCESS) {
      return base::Time::FromTimeT(last_check);
    }
  }

  return base::Time();
}

bool GoogleUpdateSettings::GetUpdateDetailForApp(bool system_install,
                                                 const wchar_t* app_guid,
                                                 ProductData* data) {
  DCHECK(app_guid);
  DCHECK(data);

  bool product_found = false;

  const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;
  base::string16 clientstate_reg_path(google_update::kRegPathClientState);
  clientstate_reg_path.append(L"\\");
  clientstate_reg_path.append(app_guid);

  RegKey clientstate;
  if (clientstate.Open(root_key, clientstate_reg_path.c_str(),
                       KEY_QUERY_VALUE) == ERROR_SUCCESS) {
    base::string16 version;
    DWORD dword_value;
    if ((clientstate.ReadValueDW(google_update::kRegLastCheckSuccessField,
                                 &dword_value) == ERROR_SUCCESS) &&
        (clientstate.ReadValue(google_update::kRegVersionField,
                               &version) == ERROR_SUCCESS)) {
      product_found = true;
      data->version = base::UTF16ToASCII(version);
      data->last_success = base::Time::FromTimeT(dword_value);
      data->last_result = 0;
      data->last_error_code = 0;
      data->last_extra_code = 0;

      if (clientstate.ReadValueDW(google_update::kRegLastInstallerResultField,
                                  &dword_value) == ERROR_SUCCESS) {
        // Google Update convention is that if an installer writes an result
        // code that is invalid, it is clamped to an exit code result.
        const DWORD kMaxValidInstallResult = 4;  // INSTALLER_RESULT_EXIT_CODE
        data->last_result = std::min(dword_value, kMaxValidInstallResult);
      }
      if (clientstate.ReadValueDW(google_update::kRegLastInstallerErrorField,
                                  &dword_value) == ERROR_SUCCESS) {
        data->last_error_code = dword_value;
      }
      if (clientstate.ReadValueDW(google_update::kRegLastInstallerExtraField,
                                  &dword_value) == ERROR_SUCCESS) {
        data->last_extra_code = dword_value;
      }
    }
  }

  return product_found;
}

bool GoogleUpdateSettings::GetUpdateDetailForGoogleUpdate(bool system_install,
                                                          ProductData* data) {
  return GetUpdateDetailForApp(system_install,
                               google_update::kGoogleUpdateUpgradeCode,
                               data);
}

bool GoogleUpdateSettings::GetUpdateDetail(bool system_install,
                                           ProductData* data) {
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  return GetUpdateDetailForApp(system_install,
                               dist->GetAppGuid().c_str(),
                               data);
}

bool GoogleUpdateSettings::SetExperimentLabels(
    bool system_install,
    const base::string16& experiment_labels) {
  HKEY reg_root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;

  // Use the browser distribution and install level to write to the correct
  // client state/app guid key.
  bool success = false;
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (dist->ShouldSetExperimentLabels()) {
    base::string16 client_state_path(
        system_install ? dist->GetStateMediumKey() : dist->GetStateKey());
    RegKey client_state(
        reg_root, client_state_path.c_str(), KEY_SET_VALUE);
    if (experiment_labels.empty()) {
      success = client_state.DeleteValue(google_update::kExperimentLabels)
          == ERROR_SUCCESS;
    } else {
      success = client_state.WriteValue(google_update::kExperimentLabels,
          experiment_labels.c_str()) == ERROR_SUCCESS;
    }
  }

  return success;
}

bool GoogleUpdateSettings::ReadExperimentLabels(
    bool system_install,
    base::string16* experiment_labels) {
  HKEY reg_root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER;

  // If this distribution does not set the experiment labels, don't bother
  // reading.
  BrowserDistribution* dist = BrowserDistribution::GetDistribution();
  if (!dist->ShouldSetExperimentLabels())
    return false;

  base::string16 client_state_path(
      system_install ? dist->GetStateMediumKey() : dist->GetStateKey());

  RegKey client_state;
  LONG result =
      client_state.Open(reg_root, client_state_path.c_str(), KEY_QUERY_VALUE);
  if (result == ERROR_SUCCESS) {
    result = client_state.ReadValue(google_update::kExperimentLabels,
                                    experiment_labels);
  }

  // If the key or value was not present, return the empty string.
  if (result == ERROR_FILE_NOT_FOUND || result == ERROR_PATH_NOT_FOUND) {
    experiment_labels->clear();
    return true;
  }

  return result == ERROR_SUCCESS;
}

/* [<][>][^][v][top][bottom][index][help] */