Commit 70cab0ca authored by Yann Dago's avatar Yann Dago Committed by Commit Bot

Handle "--channel" switch in chrome setup to allow channel switching

In order to support channel switching on the main installation of
the browser, A switch flag "--channel" that specifies which channel of
the browser is about to be installed is added to the setup's command
line. This flag will be used to store the right channel value in the
registry so the browser knows which channel is running.
The supported values are {stable|dev|beta}. This new switch will be
ignored for if any of "--chrome-beta", "--chrome-dev" and "--chrome-sxs"
is found in the command line.

Bug: 1102927
Change-Id: Ib7bd622e906f8f4af067307c90d0caffc73afc24
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2172210
Commit-Queue: Yann Dago <ydago@chromium.org>
Reviewed-by: default avatarGreg Thompson <grt@chromium.org>
Cr-Commit-Position: refs/heads/master@{#797688}
parent 756572bf
......@@ -12,12 +12,20 @@
#include "chrome/install_static/install_constants.h"
#include "chrome/install_static/install_modes.h"
#include "chrome/install_static/install_util.h"
namespace install_static {
class PrimaryInstallDetails;
class ScopedInstallDetails;
// The origin of the active channel.
enum class ChannelOrigin {
kInstallMode, // The channel dictated by the install mode.
kAdditionalParameters, // The legacy "ap" value.
kPolicy, // The updater's "TargetChannel" policy.
};
// Details relating to how Chrome is installed. This class and
// PrimaryInstallDetails (below) are used in tandem so that one instance of the
// latter may be initialized early during process startup and then shared with
......@@ -51,6 +59,13 @@ class InstallDetails {
// The string length of |channel| (not including the string terminator).
size_t channel_length;
// The origin of the |channel| value. Install modes that use the
// ADDITIONAL_PARAMETERS channel strategy may determine the channel by
// either the "ap" value (kAdditionalParameters) or by an administrative
// policy override (kPolicy). For all other install modes, the channel is
// dictated by the mode itself (kInstallMode).
ChannelOrigin channel_origin;
// The "ap" (additional parameters) value read from Chrome's ClientState key
// during process startup.
const wchar_t* update_ap;
......@@ -143,6 +158,10 @@ class InstallDetails {
return std::wstring(payload_->channel, payload_->channel_length);
}
// The origin of a ChannelStrategy::ADDITIONAL_PARAMETERS install mode's
// channel, or kInstallMode.
ChannelOrigin channel_origin() const { return payload_->channel_origin; }
// Returns the "ap" (additional parameters) value read from Chrome's
// ClientState key during process startup.
std::wstring update_ap() const {
......@@ -221,6 +240,9 @@ class PrimaryInstallDetails : public InstallDetails {
payload_.channel = channel_.c_str();
payload_.channel_length = channel_.size();
}
void set_channel_origin(ChannelOrigin origin) {
payload_.channel_origin = origin;
}
void set_update_ap(const std::wstring& update_ap) {
update_ap_ = update_ap;
payload_.update_ap = update_ap_.c_str();
......
......@@ -311,7 +311,23 @@ std::wstring ChannelFromAdditionalParameters(const InstallConstants& mode,
// rules in the update configs.
return std::wstring();
}
#endif
bool GetChromeChannelNameFromString(const wchar_t* channel_test,
std::wstring& channel) {
if (!channel_test)
return false;
if (!*channel_test || !lstrcmpiW(channel_test, kChromeChannelStableExplicit))
channel = std::wstring();
else if (!lstrcmpiW(channel_test, kChromeChannelBeta))
channel = kChromeChannelBeta;
else if (!lstrcmpiW(channel_test, kChromeChannelDev))
channel = kChromeChannelDev;
else
return false;
return true;
}
#endif // BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
// Converts a process type specified as a string to the ProcessType enum.
ProcessType GetProcessType(const std::wstring& process_type) {
......@@ -918,12 +934,13 @@ bool RecursiveDirectoryCreate(const std::wstring& full_path) {
// This function takes these inputs rather than accessing the module's
// InstallDetails instance since it is used to bootstrap InstallDetails.
std::wstring DetermineChannel(const InstallConstants& mode,
bool system_level,
std::wstring* update_ap,
std::wstring* update_cohort_name) {
DetermineChannelResult DetermineChannel(const InstallConstants& mode,
bool system_level,
const wchar_t* channel_override,
std::wstring* update_ap,
std::wstring* update_cohort_name) {
#if !BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
return std::wstring();
return {std::wstring(), ChannelOrigin::kInstallMode};
#else
// Read the "ap" value and cache it if requested.
std::wstring client_state(GetClientStateKeyPath(mode.app_guid));
......@@ -945,13 +962,19 @@ std::wstring DetermineChannel(const InstallConstants& mode,
case ChannelStrategy::UNSUPPORTED:
assert(false);
break;
case ChannelStrategy::ADDITIONAL_PARAMETERS:
return ChannelFromAdditionalParameters(mode, ap_value);
case ChannelStrategy::ADDITIONAL_PARAMETERS: {
std::wstring channel_override_value;
if (channel_override && GetChromeChannelNameFromString(
channel_override, channel_override_value)) {
return {std::move(channel_override_value), ChannelOrigin::kPolicy};
}
return {ChannelFromAdditionalParameters(mode, ap_value),
ChannelOrigin::kAdditionalParameters};
}
case ChannelStrategy::FIXED:
return mode.default_channel_name;
return {mode.default_channel_name, ChannelOrigin::kInstallMode};
}
return std::wstring();
return {std::wstring(), ChannelOrigin::kInstallMode};
#endif
}
......
......@@ -21,6 +21,7 @@ enum class Channel;
namespace install_static {
enum class ChannelOrigin;
struct InstallConstants;
// Registry key to store the stats/crash sampling state of Chrome. If set to 1,
......@@ -287,15 +288,22 @@ std::wstring GetSwitchValueFromCommandLine(const std::wstring& command_line,
// failure to create a directory.
bool RecursiveDirectoryCreate(const std::wstring& full_path);
// Returns the unadorned channel name based on the channel strategy for the
// install mode. |update_ap|, if not null, is set to the raw "ap" value read
// from Chrome's ClientState key in the registry. |update_cohort_name|, if not
// null, is set to the raw "cohort\name" value read from Chrome's ClientState
// key in the registry.
std::wstring DetermineChannel(const InstallConstants& mode,
bool system_level,
std::wstring* update_ap,
std::wstring* update_cohort_name);
struct DetermineChannelResult {
std::wstring channel_name;
ChannelOrigin origin;
};
// Returns the unadorned channel name and its origin based on the channel
// strategy for the install mode. |channel_override|, if not empty is the
// channel to return if |mode| supports non-fixed channels. |update_ap|, if not
// null, is set to the raw "ap" value read from Chrome's ClientState key in the
// registry. |update_cohort_name|, if not null, is set to the raw "cohort\name"
// value read from Chrome's ClientState key in the registry.
DetermineChannelResult DetermineChannel(const InstallConstants& mode,
bool system_level,
const wchar_t* channel_override,
std::wstring* update_ap,
std::wstring* update_cohort_name);
} // namespace install_static
......
......@@ -129,12 +129,28 @@ std::unique_ptr<PrimaryInstallDetails> MakeProductDetails(
details->set_mode(mode);
details->set_system_level(system_level);
const wchar_t* channel_override = nullptr;
// |kRegValueChannel| must match the value of google_update::kRegChannelField.
static constexpr wchar_t kRegValueChannel[] = L"channel";
// |kRegValueChannel| is written by the installer to convey a policy-driven
// channel value.
std::wstring client(GetClientsKeyPath(mode->app_guid));
std::wstring channel_from_registry;
if (nt::QueryRegValueSZ(system_level ? nt::HKLM : nt::HKCU, nt::WOW6432,
client.c_str(), kRegValueChannel,
&channel_from_registry)) {
channel_override = channel_from_registry.c_str();
}
// Cache the ap and cohort name values found in the registry for use in crash
// keys and in about:version.
std::wstring update_ap;
std::wstring update_cohort_name;
details->set_channel(
DetermineChannel(*mode, system_level, &update_ap, &update_cohort_name));
auto channel = DetermineChannel(*mode, system_level, channel_override,
&update_ap, &update_cohort_name);
details->set_channel(channel.channel_name);
details->set_channel_origin(channel.origin);
details->set_update_ap(update_ap);
details->set_update_cohort_name(update_cohort_name);
......
......@@ -224,6 +224,13 @@ class MakeProductDetailsTest : public testing::TestWithParam<TestData> {
const TestData& test_data() const { return test_data_; }
void SetChannelOverride(const wchar_t* value) {
ASSERT_THAT(base::win::RegKey(root_key_, GetClientsKeyPath().c_str(),
KEY_WOW64_32KEY | KEY_SET_VALUE)
.WriteValue(L"channel", value),
Eq(ERROR_SUCCESS));
}
void SetAp(const wchar_t* value) {
ASSERT_THAT(base::win::RegKey(root_key_, GetClientStateKeyPath().c_str(),
KEY_WOW64_32KEY | KEY_SET_VALUE)
......@@ -241,6 +248,18 @@ class MakeProductDetailsTest : public testing::TestWithParam<TestData> {
}
private:
// Returns the registry path for the product's Clients key.
std::wstring GetClientsKeyPath() {
std::wstring result(L"Software\\");
#if BUILDFLAG(USE_GOOGLE_UPDATE_INTEGRATION)
result.append(L"Google\\Update\\Clients\\");
result.append(kInstallModes[test_data().index].app_guid);
#else
result.append(kProductPathName);
#endif
return result;
}
// Returns the registry path for the product's ClientState key.
std::wstring GetClientStateKeyPath() {
std::wstring result(L"Software\\");
......@@ -282,6 +301,40 @@ TEST_P(MakeProductDetailsTest, DefaultChannel) {
EXPECT_THAT(details->channel(), StrEq(test_data().channel));
}
// Test that the default channel is sniffed properly based on the channel
// override.
TEST_P(MakeProductDetailsTest, PolicyOverrideChannel) {
static constexpr std::tuple<const wchar_t*, const wchar_t*, const wchar_t*>
kChannelOverrides[] = {
{nullptr, L"", L""}, {nullptr, L"1.1-beta", L"beta"},
{L"", L"", L""}, {L"", L"1.1-beta", L""},
{L"stable", L"", L""}, {L"stable", L"1.1-beta", L""},
{L"dev", L"", L"dev"}, {L"dev", L"1.1-beta", L"dev"},
{L"beta", L"", L"beta"}, {L"beta", L"2.0-dev", L"beta"},
};
for (const auto& override_ap_channel : kChannelOverrides) {
const wchar_t* channel_override;
const wchar_t* ap;
const wchar_t* expected_channel;
std::tie(channel_override, ap, expected_channel) = override_ap_channel;
if (ap)
SetAp(ap);
if (channel_override)
SetChannelOverride(channel_override);
std::unique_ptr<PrimaryInstallDetails> details(
MakeProductDetails(test_data().path));
if (kInstallModes[test_data().index].channel_strategy ==
ChannelStrategy::ADDITIONAL_PARAMETERS) {
EXPECT_THAT(details->channel(), StrEq(expected_channel));
} else {
// "ap" and override are ignored for this mode.
EXPECT_THAT(details->channel(), StrEq(test_data().channel));
}
}
}
// Test that the channel name is properly parsed out of additional parameters.
TEST_P(MakeProductDetailsTest, AdditionalParametersChannels) {
const std::pair<const wchar_t*, const wchar_t*> kApChannels[] = {
......
......@@ -548,7 +548,7 @@ InstallStatus InstallOrUpdateProduct(const InstallParams& install_params,
void LaunchDeleteOldVersionsProcess(const base::FilePath& setup_path,
const InstallerState& installer_state) {
base::CommandLine command_line(setup_path);
InstallUtil::AppendModeSwitch(&command_line);
InstallUtil::AppendModeAndChannelSwitches(&command_line);
command_line.AppendSwitch(switches::kDeleteOldVersions);
if (installer_state.system_install())
......
......@@ -428,7 +428,7 @@ void AddEnterpriseEnrollmentWorkItems(const InstallerState& installer_state,
cmd_line.AppendSwitchASCII(switches::kStoreDMToken, "%1");
cmd_line.AppendSwitch(switches::kSystemLevel);
cmd_line.AppendSwitch(switches::kVerboseLogging);
InstallUtil::AppendModeSwitch(&cmd_line);
InstallUtil::AppendModeAndChannelSwitches(&cmd_line);
AppCommand cmd(cmd_line.GetCommandLineString());
// TODO(alito): For now setting this command as web accessible is required
......@@ -644,8 +644,9 @@ bool AppendPostInstallTasks(const InstallParams& install_params,
HKEY root = installer_state.root_key();
const base::FilePath& target_path = installer_state.target_path();
base::FilePath new_chrome_exe(target_path.Append(kChromeNewExe));
const base::string16 clients_key(install_static::GetClientsKeyPath());
// Append work items that will only be executed if this was an update.
// Append work items that will only be executed if this was an in-use update.
// We update the 'opv' value with the current version that is active,
// the 'cpv' value with the critical update version (if present), and the
// 'cmd' value with the rename command to run.
......@@ -663,8 +664,6 @@ bool AppendPostInstallTasks(const InstallParams& install_params,
installer_state.GetInstallerDirectory(new_version)
.Append(setup_path.BaseName()));
const base::string16 clients_key(install_static::GetClientsKeyPath());
if (current_version.IsValid()) {
in_use_update_work_items->AddSetRegValueWorkItem(
root, clients_key, KEY_WOW64_32KEY,
......@@ -689,7 +688,7 @@ bool AppendPostInstallTasks(const InstallParams& install_params,
product_rename_cmd.AppendSwitch(switches::kSystemLevel);
if (installer_state.verbose_logging())
product_rename_cmd.AppendSwitch(switches::kVerboseLogging);
InstallUtil::AppendModeSwitch(&product_rename_cmd);
InstallUtil::AppendModeAndChannelSwitches(&product_rename_cmd);
in_use_update_work_items->AddSetRegValueWorkItem(
root, clients_key, KEY_WOW64_32KEY, google_update::kRegRenameCmdField,
product_rename_cmd.GetCommandLineString(), true);
......@@ -709,8 +708,21 @@ bool AppendPostInstallTasks(const InstallParams& install_params,
new Not(new ConditionRunIfFileExists(new_chrome_exe))));
regular_update_work_items->set_log_message("RegularUpdateWorkItemList");
// Since this was not an in-use-update, delete 'opv', 'cpv', and 'cmd' keys.
const base::string16 clients_key(install_static::GetClientsKeyPath());
// Convey the channel name to the browser if this installer instance's
// channel is enforced by policy. Otherwise, delete the registry value.
const auto& install_details = install_static::InstallDetails::Get();
if (install_details.channel_origin() ==
install_static::ChannelOrigin::kPolicy) {
post_install_task_list->AddSetRegValueWorkItem(
root, clients_key, KEY_WOW64_32KEY, google_update::kRegChannelField,
install_details.channel(), true);
} else {
regular_update_work_items->AddDeleteRegValueWorkItem(
root, clients_key, KEY_WOW64_32KEY, google_update::kRegChannelField);
}
// Since this was not an in-use-update, delete 'opv', 'cpv',
// and 'cmd' keys.
regular_update_work_items->AddDeleteRegValueWorkItem(
root, clients_key, KEY_WOW64_32KEY, google_update::kRegOldVersionField);
regular_update_work_items->AddDeleteRegValueWorkItem(
......@@ -968,7 +980,7 @@ void AddActiveSetupWorkItems(const InstallerState& installer_state,
cmd.AppendSwitch(installer::switches::kConfigureUserSettings);
cmd.AppendSwitch(installer::switches::kVerboseLogging);
cmd.AppendSwitch(installer::switches::kSystemLevel);
InstallUtil::AppendModeSwitch(&cmd);
InstallUtil::AppendModeAndChannelSwitches(&cmd);
list->AddSetRegValueWorkItem(root, active_setup_path, WorkItem::kWow64Default,
L"StubPath", cmd.GetCommandLineString(), true);
......@@ -991,7 +1003,7 @@ void AppendUninstallCommandLineFlags(const InstallerState& installer_state,
uninstall_cmd->AppendSwitch(installer::switches::kUninstall);
InstallUtil::AppendModeSwitch(uninstall_cmd);
InstallUtil::AppendModeAndChannelSwitches(uninstall_cmd);
if (installer_state.is_msi())
uninstall_cmd->AppendSwitch(installer::switches::kMsi);
if (installer_state.system_install())
......@@ -1018,7 +1030,7 @@ void AddOsUpgradeWorkItems(const InstallerState& installer_state,
.Append(setup_path.BaseName()));
// Add the main option to indicate OS upgrade flow.
cmd_line.AppendSwitch(installer::switches::kOnOsUpgrade);
InstallUtil::AppendModeSwitch(&cmd_line);
InstallUtil::AppendModeAndChannelSwitches(&cmd_line);
if (installer_state.system_install())
cmd_line.AppendSwitch(installer::switches::kSystemLevel);
// Log everything for now.
......
......@@ -4,6 +4,8 @@
#include "chrome/installer/setup/setup_install_details.h"
#include <string>
#include "base/command_line.h"
#include "base/strings/string16.h"
#include "base/win/registry.h"
......@@ -73,8 +75,18 @@ std::unique_ptr<install_static::PrimaryInstallDetails> MakeInstallDetails(
// keys.
base::string16 update_ap;
base::string16 update_cohort_name;
details->set_channel(install_static::DetermineChannel(
*mode, system_level, &update_ap, &update_cohort_name));
auto channel_from_cmd_line =
command_line.GetSwitchValueNative(installer::switches::kChannel);
auto channel = install_static::DetermineChannel(
*mode, system_level,
command_line.HasSwitch(installer::switches::kChannel)
? channel_from_cmd_line.c_str()
: nullptr,
&update_ap, &update_cohort_name);
details->set_channel(channel.channel_name);
details->set_channel_origin(channel.origin);
details->set_update_ap(update_ap);
details->set_update_cohort_name(update_cohort_name);
......
......@@ -457,6 +457,7 @@ installer::InstallStatus RenameChromeExecutables(
// TODO(grt): Clean this up; https://crbug.com/577816.
HKEY reg_root = installer_state->root_key();
const base::string16 clients_key = install_static::GetClientsKeyPath();
install_list->AddDeleteRegValueWorkItem(reg_root, clients_key,
KEY_WOW64_32KEY,
google_update::kRegOldVersionField);
......@@ -466,6 +467,22 @@ installer::InstallStatus RenameChromeExecutables(
install_list->AddDeleteRegValueWorkItem(reg_root, clients_key,
KEY_WOW64_32KEY,
google_update::kRegRenameCmdField);
// If a channel was specified by policy, update the "channel" registry value
// with it so that the browser knows which channel to use, otherwise delete
// whatever value that key holds.
const auto& install_details = install_static::InstallDetails::Get();
if (install_details.channel_origin() ==
install_static::ChannelOrigin::kPolicy) {
install_list->AddSetRegValueWorkItem(reg_root, clients_key, KEY_WOW64_32KEY,
google_update::kRegChannelField,
install_details.channel(), true);
} else {
install_list->AddDeleteRegValueWorkItem(reg_root, clients_key,
KEY_WOW64_32KEY,
google_update::kRegChannelField);
}
// old_chrome.exe is still in use in most cases, so ignore failures here.
install_list->AddDeleteTreeWorkItem(chrome_old_exe, temp_path.path())
->set_best_effort(true);
......
......@@ -269,7 +269,7 @@ void BeginUserExperiment(const InstallerState& installer_state,
// Prepare a command line to relaunch the installed setup.exe for the
// experiment.
base::CommandLine setup_command(setup_path);
InstallUtil::AppendModeSwitch(&setup_command);
InstallUtil::AppendModeAndChannelSwitches(&setup_command);
if (installer_state.system_install())
setup_command.AppendSwitch(switches::kSystemLevel);
if (installer_state.verbose_logging())
......
......@@ -30,6 +30,7 @@ const wchar_t kRegCFEndTempOptOutCmdField[] = L"CFEndTempOptOutCmd";
const wchar_t kRegCFOptInCmdField[] = L"CFOptInCmd";
const wchar_t kRegCFOptOutCmdField[] = L"CFOptOutCmd";
const wchar_t kRegCFTempOptOutCmdField[] = L"CFTempOptOutCmd";
const wchar_t kRegChannelField[] = L"channel";
const wchar_t kRegClientField[] = L"client";
const wchar_t kRegCommandLineField[] = L"CommandLine";
const wchar_t kRegCriticalVersionField[] = L"cpv";
......
......@@ -46,6 +46,7 @@ extern const wchar_t kRegCFEndTempOptOutCmdField[];
extern const wchar_t kRegCFOptInCmdField[];
extern const wchar_t kRegCFOptOutCmdField[];
extern const wchar_t kRegCFTempOptOutCmdField[];
extern const wchar_t kRegChannelField[];
extern const wchar_t kRegClientField[];
extern const wchar_t kRegCommandLineField[];
extern const wchar_t kRegCriticalVersionField[];
......
......@@ -77,7 +77,7 @@ void ElevateIfNeededToReenableUpdates() {
base::CommandLine cmd(exe_path);
cmd.AppendSwitch(installer::switches::kReenableAutoupdates);
InstallUtil::AppendModeSwitch(&cmd);
InstallUtil::AppendModeAndChannelSwitches(&cmd);
if (system_install)
cmd.AppendSwitch(installer::switches::kSystemLevel);
if (product_state.uninstall_command().HasSwitch(
......
......@@ -480,11 +480,17 @@ void InstallUtil::ComposeCommandLine(const base::string16& program,
base::CommandLine::FromString(L"\"" + program + L"\" " + arguments);
}
void InstallUtil::AppendModeSwitch(base::CommandLine* command_line) {
void InstallUtil::AppendModeAndChannelSwitches(
base::CommandLine* command_line) {
const install_static::InstallDetails& install_details =
install_static::InstallDetails::Get();
if (*install_details.install_switch())
command_line->AppendSwitch(install_details.install_switch());
if (install_details.channel_origin() ==
install_static::ChannelOrigin::kPolicy) {
command_line->AppendSwitchNative(installer::switches::kChannel,
install_details.channel());
}
}
// static
......
......@@ -163,9 +163,9 @@ class InstallUtil {
const base::string16& arguments,
base::CommandLine* command_line);
// Appends the installer switch that selects the current install mode (see
// install_static::InstallDetails).
static void AppendModeSwitch(base::CommandLine* command_line);
// Appends the installer switch that selects the current install mode and
// policy-specified channel (see install_static::InstallDetails).
static void AppendModeAndChannelSwitches(base::CommandLine* command_line);
// Returns a string in the form YYYYMMDD of the current date.
static base::string16 GetCurrentDate();
......
......@@ -713,7 +713,7 @@ bool ElevateAndRegisterChrome(const base::FilePath& chrome_exe,
if (base::PathExists(exe_path)) {
base::CommandLine cmd(exe_path);
InstallUtil::AppendModeSwitch(&cmd);
InstallUtil::AppendModeAndChannelSwitches(&cmd);
if (!is_per_user)
cmd.AppendSwitch(installer::switches::kSystemLevel);
cmd.AppendSwitchPath(installer::switches::kRegisterChromeBrowser,
......
......@@ -14,6 +14,12 @@ namespace switches {
// permissible.
const char kAllowDowngrade[] = "allow-downgrade";
// A channel name specified via administrative policy. This switch sets the
// channel both of the installer and of the version of Chrome being installed.
// This switch has no effect for secondary install modes (i.e., installs that
// use --chrome-sxs or another mode switch).
const char kChannel[] = "channel";
// Create shortcuts for this user to point to a system-level install (which
// must already be installed on the machine). The shortcuts created will
// match the preferences of the already present system-level install as such
......
......@@ -149,6 +149,7 @@ enum InstallerStage {
namespace switches {
extern const char kAllowDowngrade[];
extern const char kChannel[];
extern const char kConfigureUserSettings[];
extern const char kCriticalUpdateVersion[];
extern const char kDeleteOldVersions[];
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment