Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(559)

Side by Side Diff: chrome/browser/component_updater/sw_reporter_installer_win.cc

Issue 2278013002: Add support for the ExperimentalSwReporterEngine field trial. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Address sorin's review comments and simplify empty suffix handling Created 4 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/component_updater/sw_reporter_installer_win.h" 5 #include "chrome/browser/component_updater/sw_reporter_installer_win.h"
6 6
7 #include <stdint.h> 7 #include <stdint.h>
8 8
9 #include <algorithm>
9 #include <map> 10 #include <map>
10 #include <memory> 11 #include <memory>
11 #include <string> 12 #include <string>
12 #include <utility> 13 #include <utility>
13 #include <vector> 14 #include <vector>
14 15
15 #include "base/base_paths.h" 16 #include "base/base_paths.h"
16 #include "base/bind.h" 17 #include "base/bind.h"
18 #include "base/command_line.h"
19 #include "base/feature_list.h"
17 #include "base/files/file_path.h" 20 #include "base/files/file_path.h"
18 #include "base/files/file_util.h" 21 #include "base/files/file_util.h"
19 #include "base/logging.h" 22 #include "base/logging.h"
20 #include "base/metrics/histogram.h" 23 #include "base/metrics/histogram.h"
21 #include "base/metrics/sparse_histogram.h" 24 #include "base/metrics/sparse_histogram.h"
22 #include "base/path_service.h" 25 #include "base/path_service.h"
23 #include "base/strings/string_tokenizer.h" 26 #include "base/strings/string_tokenizer.h"
27 #include "base/strings/string_util.h"
28 #include "base/sys_info.h"
24 #include "base/threading/thread_task_runner_handle.h" 29 #include "base/threading/thread_task_runner_handle.h"
25 #include "base/threading/worker_pool.h" 30 #include "base/threading/worker_pool.h"
26 #include "base/time/time.h" 31 #include "base/time/time.h"
27 #include "base/win/registry.h" 32 #include "base/win/registry.h"
28 #include "chrome/browser/browser_process.h" 33 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/safe_browsing/srt_fetcher_win.h" 34 #include "chrome/browser/safe_browsing/srt_fetcher_win.h"
30 #include "chrome/browser/safe_browsing/srt_field_trial_win.h" 35 #include "chrome/browser/safe_browsing/srt_field_trial_win.h"
31 #include "components/component_updater/component_updater_paths.h" 36 #include "components/component_updater/component_updater_paths.h"
32 #include "components/component_updater/component_updater_service.h" 37 #include "components/component_updater/component_updater_service.h"
33 #include "components/component_updater/pref_names.h" 38 #include "components/component_updater/pref_names.h"
34 #include "components/pref_registry/pref_registry_syncable.h" 39 #include "components/pref_registry/pref_registry_syncable.h"
35 #include "components/prefs/pref_registry_simple.h" 40 #include "components/prefs/pref_registry_simple.h"
36 #include "components/update_client/update_client.h" 41 #include "components/update_client/update_client.h"
37 #include "components/update_client/utils.h" 42 #include "components/update_client/utils.h"
43 #include "components/variations/variations_associated_data.h"
38 #include "content/public/browser/browser_thread.h" 44 #include "content/public/browser/browser_thread.h"
39 45
40 namespace component_updater { 46 namespace component_updater {
41 47
42 namespace { 48 namespace {
43 49
44 // These two sets of values are used to send UMA information and are replicated 50 // These values are used to send UMA information and are replicated in the
45 // in the histograms.xml file, so the order MUST NOT CHANGE. 51 // histograms.xml file, so the order MUST NOT CHANGE.
46 enum SRTCompleted { 52 enum SRTCompleted {
47 SRT_COMPLETED_NOT_YET = 0, 53 SRT_COMPLETED_NOT_YET = 0,
48 SRT_COMPLETED_YES = 1, 54 SRT_COMPLETED_YES = 1,
49 SRT_COMPLETED_LATER = 2, 55 SRT_COMPLETED_LATER = 2,
50 SRT_COMPLETED_MAX, 56 SRT_COMPLETED_MAX,
51 }; 57 };
52 58
53 // CRX hash. The extension id is: gkmgaooipdjhmangpemjhigmamcehddo. The hash was 59 // CRX hash. The extension id is: gkmgaooipdjhmangpemjhigmamcehddo. The hash was
54 // generated in Python with something like this: 60 // generated in Python with something like this:
55 // hashlib.sha256().update(open("<file>.crx").read()[16:16+294]).digest(). 61 // hashlib.sha256().update(open("<file>.crx").read()[16:16+294]).digest().
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after
110 void RunSwReporterAfterStartup( 116 void RunSwReporterAfterStartup(
111 const safe_browsing::SwReporterInvocation& invocation, 117 const safe_browsing::SwReporterInvocation& invocation,
112 const base::Version& version) { 118 const base::Version& version) {
113 content::BrowserThread::PostAfterStartupTask( 119 content::BrowserThread::PostAfterStartupTask(
114 FROM_HERE, base::ThreadTaskRunnerHandle::Get(), 120 FROM_HERE, base::ThreadTaskRunnerHandle::Get(),
115 base::Bind(&safe_browsing::RunSwReporter, invocation, version, 121 base::Bind(&safe_browsing::RunSwReporter, invocation, version,
116 base::ThreadTaskRunnerHandle::Get(), 122 base::ThreadTaskRunnerHandle::Get(),
117 base::WorkerPool::GetTaskRunner(true))); 123 base::WorkerPool::GetTaskRunner(true)));
118 } 124 }
119 125
126 constexpr base::Feature kExperimentalEngineFeature{
127 "ExperimentalSwReporterEngine", base::FEATURE_DISABLED_BY_DEFAULT};
128
129 // Max length of an installer attribute (taken from the regexp in
130 // ComponentInstallerTraits::InstallerAttributes).
131 constexpr size_t kMaxAttributeLength = 256;
132
133 // Max length of the registry and histogram suffix. Fairly arbitrary: the
134 // Windows registry accepts much longer keys, but we need to display this
135 // string in histograms as well.
136 constexpr size_t kMaxSuffixLength = 80;
137
138 // These MUST match the values for SwReporterExperimentError in histograms.xml.
139 enum ExperimentError {
140 EXPERIMENT_ERROR_BAD_TAG = 0,
141 EXPERIMENT_ERROR_BAD_PARAMS = 1,
142 EXPERIMENT_ERROR_MAX,
143 };
144
145 void ReportExperimentError(ExperimentError error) {
146 UMA_HISTOGRAM_ENUMERATION("SoftwareReporter.ExperimentErrors", error,
147 EXPERIMENT_ERROR_MAX);
148 }
149
150 // Ensure |str| contains only alphanumeric characters and characters from
151 // |extras|, and is not longer than |max_length|.
152 bool ValidateString(const std::string& str,
153 const std::string& extras,
154 size_t max_length) {
155 return str.size() <= max_length &&
156 std::all_of(str.cbegin(), str.cend(), [&extras](char c) {
157 return base::IsAsciiAlpha(c) || base::IsAsciiDigit(c) ||
158 extras.find(c) != std::string::npos;
159 });
160 }
161
162 // Read the command-line params and an UMA histogram suffix from the manifest,
163 // and launch the SwReporter with those parameters. If anything goes wrong the
164 // SwReporter should not be run at all - do not fall back to the default.
165 void RunExperimentalSwReporter(const base::FilePath& exe_path,
166 const base::Version& version,
167 std::unique_ptr<base::DictionaryValue> manifest,
168 const SwReporterRunner& reporter_runner) {
169 // Don't log an error if the launch_params section is entirely missing. This
170 // can happen when the user already has an older version of the software
171 // reporter component, so |ComponentReady| is called on startup before the
172 // experimental version is downloaded.
173 //
174 // Also don't run the old version, though. We want this user to run the
175 // experimental version once it's been downloaded.
176 if (!manifest->HasKey("launch_params"))
177 return;
178
179 const base::ListValue* parameter_list = nullptr;
180 if (!manifest->GetList("launch_params", &parameter_list)) {
181 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
182 return;
183 }
184 DCHECK(parameter_list);
185 if (parameter_list->empty()) {
186 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
187 return;
188 }
189
190 // For now we only support running the reporter once, so only look at the
191 // first set of arguments in the list.
192 const base::DictionaryValue* invocation_params = nullptr;
193 if (!parameter_list->GetDictionary(0, &invocation_params)) {
194 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
195 return;
196 }
197
198 // The suffix is optional. If present, it must be a short alphanumeric string.
199 std::string suffix;
200 DCHECK(invocation_params);
201 if (invocation_params->HasKey("suffix")) {
202 if (!invocation_params->GetString("suffix", &suffix)) {
203 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
204 return;
205 }
206 if (!suffix.empty() &&
207 !ValidateString(suffix, std::string(), kMaxSuffixLength)) {
208 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
209 return;
210 }
211 }
212
213 // Build a command line for the reporter out of the executable path and the
214 // (optional) arguments from the manifest.
215 std::vector<base::string16> argv{exe_path.value()};
216 if (invocation_params->HasKey("arguments")) {
217 const base::ListValue* arguments = nullptr;
218 if (!invocation_params->GetList("arguments", &arguments)) {
219 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
220 return;
221 }
222 for (const std::unique_ptr<base::Value>& value : *arguments) {
223 base::string16 argument;
224 if (!value->GetAsString(&argument)) {
225 ReportExperimentError(EXPERIMENT_ERROR_BAD_PARAMS);
226 return;
227 }
228 if (!argument.empty())
229 argv.push_back(argument);
230 }
231 }
232 base::CommandLine command_line(argv);
233
234 // Add the histogram suffix to the command-line as well, so that the
235 // reporter will add the same suffix to registry keys where it writes
236 // metrics.
237 if (!suffix.empty())
238 command_line.AppendSwitchASCII("registry-suffix", suffix);
239
240 auto invocation =
241 safe_browsing::SwReporterInvocation::FromCommandLine(command_line);
242 invocation.suffix = suffix;
243 invocation.is_experimental = true;
244
245 reporter_runner.Run(invocation, version);
246 }
247
120 } // namespace 248 } // namespace
121 249
122 SwReporterInstallerTraits::SwReporterInstallerTraits( 250 SwReporterInstallerTraits::SwReporterInstallerTraits(
123 const SwReporterRunner& reporter_runner) 251 const SwReporterRunner& reporter_runner,
124 : reporter_runner_(reporter_runner) {} 252 bool is_experimental_engine_supported)
253 : reporter_runner_(reporter_runner),
254 is_experimental_engine_supported_(is_experimental_engine_supported) {}
125 255
126 SwReporterInstallerTraits::~SwReporterInstallerTraits() {} 256 SwReporterInstallerTraits::~SwReporterInstallerTraits() {}
127 257
128 bool SwReporterInstallerTraits::VerifyInstallation( 258 bool SwReporterInstallerTraits::VerifyInstallation(
129 const base::DictionaryValue& manifest, 259 const base::DictionaryValue& manifest,
130 const base::FilePath& dir) const { 260 const base::FilePath& dir) const {
131 return base::PathExists(dir.Append(kSwReporterExeName)); 261 return base::PathExists(dir.Append(kSwReporterExeName));
132 } 262 }
133 263
134 bool SwReporterInstallerTraits::SupportsGroupPolicyEnabledComponentUpdates() 264 bool SwReporterInstallerTraits::SupportsGroupPolicyEnabledComponentUpdates()
(...skipping 10 matching lines...) Expand all
145 const base::FilePath& install_dir) { 275 const base::FilePath& install_dir) {
146 return true; 276 return true;
147 } 277 }
148 278
149 void SwReporterInstallerTraits::ComponentReady( 279 void SwReporterInstallerTraits::ComponentReady(
150 const base::Version& version, 280 const base::Version& version,
151 const base::FilePath& install_dir, 281 const base::FilePath& install_dir,
152 std::unique_ptr<base::DictionaryValue> manifest) { 282 std::unique_ptr<base::DictionaryValue> manifest) {
153 DCHECK_CURRENTLY_ON(content::BrowserThread::UI); 283 DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
154 const base::FilePath exe_path(install_dir.Append(kSwReporterExeName)); 284 const base::FilePath exe_path(install_dir.Append(kSwReporterExeName));
285 if (IsExperimentalEngineEnabled()) {
286 RunExperimentalSwReporter(exe_path, version, std::move(manifest),
287 reporter_runner_);
288 return;
289 }
155 reporter_runner_.Run( 290 reporter_runner_.Run(
156 safe_browsing::SwReporterInvocation::FromFilePath(exe_path), version); 291 safe_browsing::SwReporterInvocation::FromFilePath(exe_path), version);
157 } 292 }
158 293
159 base::FilePath SwReporterInstallerTraits::GetRelativeInstallDir() const { 294 base::FilePath SwReporterInstallerTraits::GetRelativeInstallDir() const {
160 return base::FilePath(FILE_PATH_LITERAL("SwReporter")); 295 return base::FilePath(FILE_PATH_LITERAL("SwReporter"));
161 } 296 }
162 297
163 void SwReporterInstallerTraits::GetHash(std::vector<uint8_t>* hash) const { 298 void SwReporterInstallerTraits::GetHash(std::vector<uint8_t>* hash) const {
164 DCHECK(hash); 299 DCHECK(hash);
165 hash->assign(kSha256Hash, kSha256Hash + sizeof(kSha256Hash)); 300 hash->assign(kSha256Hash, kSha256Hash + sizeof(kSha256Hash));
166 } 301 }
167 302
168 std::string SwReporterInstallerTraits::GetName() const { 303 std::string SwReporterInstallerTraits::GetName() const {
169 return "Software Reporter Tool"; 304 return "Software Reporter Tool";
170 } 305 }
171 306
172 update_client::InstallerAttributes 307 update_client::InstallerAttributes
173 SwReporterInstallerTraits::GetInstallerAttributes() const { 308 SwReporterInstallerTraits::GetInstallerAttributes() const {
174 return update_client::InstallerAttributes(); 309 update_client::InstallerAttributes attributes;
310 if (IsExperimentalEngineEnabled()) {
311 // Pass the "tag" parameter to the installer; it will be used to choose
312 // which binary is downloaded.
313 constexpr char kTagParam[] = "tag";
314 const std::string tag = variations::GetVariationParamValueByFeature(
315 kExperimentalEngineFeature, kTagParam);
316
317 // If the tag is not a valid attribute (see the regexp in
318 // ComponentInstallerTraits::InstallerAttributes), set it to a valid but
319 // unrecognized value so that nothing will be downloaded.
320 if (tag.empty() || !ValidateString(tag, "_.,;+_=", kMaxAttributeLength)) {
321 ReportExperimentError(EXPERIMENT_ERROR_BAD_TAG);
322 attributes[kTagParam] = "missing_tag";
323 } else {
324 attributes[kTagParam] = tag;
325 }
326 }
327 return attributes;
175 } 328 }
176 329
177 std::vector<std::string> SwReporterInstallerTraits::GetMimeTypes() const { 330 std::vector<std::string> SwReporterInstallerTraits::GetMimeTypes() const {
178 return std::vector<std::string>(); 331 return std::vector<std::string>();
179 } 332 }
180 333
334 bool SwReporterInstallerTraits::IsExperimentalEngineEnabled() const {
335 return is_experimental_engine_supported_ &&
336 base::FeatureList::IsEnabled(kExperimentalEngineFeature);
337 }
338
181 void RegisterSwReporterComponent(ComponentUpdateService* cus) { 339 void RegisterSwReporterComponent(ComponentUpdateService* cus) {
182 if (!safe_browsing::IsSwReporterEnabled()) 340 if (!safe_browsing::IsSwReporterEnabled())
183 return; 341 return;
184 342
185 // Check if we have information from Cleaner and record UMA statistics. 343 // Check if we have information from Cleaner and record UMA statistics.
186 base::string16 cleaner_key_name( 344 base::string16 cleaner_key_name(
187 safe_browsing::kSoftwareRemovalToolRegistryKey); 345 safe_browsing::kSoftwareRemovalToolRegistryKey);
188 cleaner_key_name.append(1, L'\\').append(kCleanerSuffixRegistryKey); 346 cleaner_key_name.append(1, L'\\').append(kCleanerSuffixRegistryKey);
189 base::win::RegKey cleaner_key( 347 base::win::RegKey cleaner_key(
190 HKEY_CURRENT_USER, cleaner_key_name.c_str(), KEY_ALL_ACCESS); 348 HKEY_CURRENT_USER, cleaner_key_name.c_str(), KEY_ALL_ACCESS);
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
247 ReportUploadsWithUma(upload_results); 405 ReportUploadsWithUma(upload_results);
248 } 406 }
249 } else { 407 } else {
250 if (cleaner_key.HasValue(safe_browsing::kEndTimeValueName)) { 408 if (cleaner_key.HasValue(safe_browsing::kEndTimeValueName)) {
251 SRTHasCompleted(SRT_COMPLETED_LATER); 409 SRTHasCompleted(SRT_COMPLETED_LATER);
252 cleaner_key.DeleteValue(safe_browsing::kEndTimeValueName); 410 cleaner_key.DeleteValue(safe_browsing::kEndTimeValueName);
253 } 411 }
254 } 412 }
255 } 413 }
256 414
415 // The experiment is only enabled on x86. There's no way to check this in the
416 // variations config so we'll hard-code it.
417 const bool is_experimental_engine_enabled =
jwd 2016/08/25 17:53:55 Can you change this to be _supported?
Joe Mason 2016/08/25 18:07:17 Done.
418 base::SysInfo::OperatingSystemArchitecture() == "x86";
419
257 // Install the component. 420 // Install the component.
258 std::unique_ptr<ComponentInstallerTraits> traits( 421 std::unique_ptr<ComponentInstallerTraits> traits(
259 new SwReporterInstallerTraits(base::Bind(&RunSwReporterAfterStartup))); 422 new SwReporterInstallerTraits(base::Bind(&RunSwReporterAfterStartup),
423 is_experimental_engine_enabled));
260 // |cus| will take ownership of |installer| during installer->Register(cus). 424 // |cus| will take ownership of |installer| during installer->Register(cus).
261 DefaultComponentInstaller* installer = 425 DefaultComponentInstaller* installer =
262 new DefaultComponentInstaller(std::move(traits)); 426 new DefaultComponentInstaller(std::move(traits));
263 installer->Register(cus, base::Closure()); 427 installer->Register(cus, base::Closure());
264 } 428 }
265 429
266 void RegisterPrefsForSwReporter(PrefRegistrySimple* registry) { 430 void RegisterPrefsForSwReporter(PrefRegistrySimple* registry) {
267 registry->RegisterInt64Pref(prefs::kSwReporterLastTimeTriggered, 0); 431 registry->RegisterInt64Pref(prefs::kSwReporterLastTimeTriggered, 0);
268 registry->RegisterIntegerPref(prefs::kSwReporterLastExitCode, -1); 432 registry->RegisterIntegerPref(prefs::kSwReporterLastExitCode, -1);
269 registry->RegisterBooleanPref(prefs::kSwReporterPendingPrompt, false); 433 registry->RegisterBooleanPref(prefs::kSwReporterPendingPrompt, false);
270 } 434 }
271 435
272 void RegisterProfilePrefsForSwReporter( 436 void RegisterProfilePrefsForSwReporter(
273 user_prefs::PrefRegistrySyncable* registry) { 437 user_prefs::PrefRegistrySyncable* registry) {
274 registry->RegisterStringPref(prefs::kSwReporterPromptVersion, ""); 438 registry->RegisterStringPref(prefs::kSwReporterPromptVersion, "");
275 439
276 registry->RegisterStringPref(prefs::kSwReporterPromptSeed, ""); 440 registry->RegisterStringPref(prefs::kSwReporterPromptSeed, "");
277 } 441 }
278 442
279 } // namespace component_updater 443 } // namespace component_updater
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698