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

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

Issue 333193002: Adding a SW reporter component updater (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: OWNER Nit fix in profile resetter. (and a small change to process handle close in component) Created 6 years, 6 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 | Annotate | Revision Log
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/component_updater/sw_reporter_component_installer_win.h "
6
7 #include <string>
8 #include <vector>
9
10 #include "base/base_paths.h"
11 #include "base/bind.h"
12 #include "base/bind_helpers.h"
13 #include "base/command_line.h"
14 #include "base/file_util.h"
15 #include "base/files/file_enumerator.h"
16 #include "base/files/file_path.h"
17 #include "base/logging.h"
18 #include "base/metrics/histogram.h"
19 #include "base/metrics/sparse_histogram.h"
20 #include "base/path_service.h"
21 #include "base/prefs/pref_registry_simple.h"
22 #include "base/prefs/pref_service.h"
23 #include "base/process/kill.h"
24 #include "base/process/launch.h"
25 #include "base/threading/worker_pool.h"
26 #include "base/values.h"
27 #include "base/win/registry.h"
28 #include "chrome/browser/browser_process.h"
29 #include "chrome/browser/component_updater/component_updater_service.h"
30 #include "chrome/common/chrome_paths.h"
31 #include "chrome/common/pref_names.h"
32 #include "content/public/browser/browser_thread.h"
33
34 using content::BrowserThread;
35
36 namespace component_updater {
37
38 namespace {
39
40 // These values are used to send UMA information and are replicated in the
41 // histograms.xml file, so the order MUST NOT CHANGE.
42 enum SwReporterUmaValue {
43 SW_REPORTER_START_DOWNLOAD = 0,
44 SW_REPORTER_RETRYING_DOWNLOAD,
45 SW_REPORTER_RETRIED_TOO_MANY_DOWNLOADS,
46 SW_REPORTER_START_EXECUTION,
47 SW_REPORTER_RETRYING_EXECUTION,
48 SW_REPORTER_RETRIED_TOO_MANY_EXECUTIONS,
49 SW_REPORTER_COMPONENT_UPDATE_ERROR,
50 SW_REPORTER_MAX,
51 };
52
53 // The maximum number of times to retry a download on startup.
54 const int kMaxRetry = 7;
55
56 // CRX hash. The extension id is: gkmgaooipdjhmangpemjhigmamcehddo.
57 // hashlib.sha256().update(open(".crx").read()[16:16+294]).digest().
58 const uint8 kSha2Hash[] = {0x6a, 0xc6, 0x0e, 0xe8, 0xf3, 0x97, 0xc0, 0xd6,
59 0xf4, 0xc9, 0x78, 0x6c, 0x0c, 0x24, 0x73, 0x3e,
60 0x05, 0xa5, 0x62, 0x4b, 0x2e, 0xc7, 0xb7, 0x1c,
61 0x5f, 0xea, 0xf0, 0x88, 0xf6, 0x97, 0x9b, 0xc7};
62
63 const char kSwReporterManifestName[] = "Software Reporter Tool";
64
65 const base::FilePath::CharType kSwReporterExeName[] =
66 FILE_PATH_LITERAL("software_reporter_tool.exe");
67 const base::FilePath::CharType kSwReporterBaseDirectory[] =
68 FILE_PATH_LITERAL("SwReporter");
69
70 // If we don't have a SwReporter component, this is the version we claim.
71 const char kNullVersion[] = "0.0.0.0";
72
73 // Where to fetch the reporter exit code in the registry.
74 const wchar_t kSoftwareRemovalToolRegistryKey[] =
75 L"Software\\Google\\Software Removal Tool";
76 const wchar_t kExitCodeRegistryValueName[] = L"ExitCode";
77
78 void ReportSwReporterStep(SwReporterUmaValue value) {
79 UMA_HISTOGRAM_ENUMERATION(
80 "ComponentUpdater.SwReporterStep", value, SW_REPORTER_MAX);
81 }
82
83 // This function is called on the UI thread to report the SwReporter exit code
84 // and then to clear it from the registry as well as clear the execution state
85 // from the local state.
86 void ReportAndClearSwReporterExitCode(int exit_code) {
87 UMA_HISTOGRAM_SPARSE_SLOWLY("ComponentUpdater.SwReporterExitCode", exit_code);
88
89 base::win::RegKey srt_key(
90 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_WRITE);
91 srt_key.DeleteValue(kExitCodeRegistryValueName);
92
93 // Now that we are done we can reset the try count.
94 g_browser_process->local_state()->SetInteger(
95 prefs::kSwReporterComponentExecuteTryCount, 0);
96 }
97
98 // This function is called from a worker thread to wait for the SwReporter to
99 // complete and then collect it's exit code to be sent to
100 // ReportAndClearSwReporterExitCode on the UI thread.
101 void WaitForProcessTermination(base::ProcessHandle process_handle,
102 const base::FilePath& reporter_exe_full_path) {
103 int exit_code = -1;
104 bool success = base::WaitForExitCode(process_handle, &exit_code);
105 DCHECK(success);
106 ::CloseHandle(process_handle);
Sorin Jianu 2014/06/19 19:08:48 Consider using base::CloseProcessHandle instead si
107 process_handle = NULL;
Sorin Jianu 2014/06/19 19:08:48 base::kNullProcessHandle ?
108
109 BrowserThread::PostTask(
110 BrowserThread::UI,
111 FROM_HERE,
112 base::Bind(&ReportAndClearSwReporterExitCode, exit_code));
113
114 // The software reporter must be deleted so that the component updater will
115 // fetch it again the next time we need it. This is because the updater
116 // doesn't let us know when no new version is available and so we can't tell
117 // if we should run the currently installed version or wait for the update.
118 success = base::DeleteFile(base::FilePath(reporter_exe_full_path), false);
119 DCHECK(success);
120 }
121
122 // The base directory on windows looks like:
123 // <profile>\AppData\Local\Google\Chrome\User Data\SwReporter\.
124 base::FilePath GetSwReporterBaseDirectory() {
125 base::FilePath result;
126 PathService::Get(chrome::DIR_USER_DATA, &result);
127 return result.Append(kSwReporterBaseDirectory);
128 }
129
130 // SwReporter has version encoded in the path itself so we need to enumerate the
131 // directories to find the full path. On success it returns something like:
132 // <profile>\AppData\Local\Google\Chrome\User Data\SwReporter\1.0.1.5\.
133 void GetLatestSwReporterDirectory(base::FilePath* result,
134 Version* latest,
135 std::vector<base::FilePath>* older_dirs) {
136 base::FilePath base_dir = GetSwReporterBaseDirectory();
137 bool found = false;
138 base::FileEnumerator file_enumerator(
139 base_dir, false, base::FileEnumerator::DIRECTORIES);
140 for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
141 path = file_enumerator.Next()) {
142 Version version(path.BaseName().MaybeAsASCII());
143 if (!version.IsValid())
144 continue;
145 if (version.CompareTo(*latest) > 0 &&
146 base::PathExists(path.Append(kSwReporterExeName))) {
147 if (found && older_dirs)
148 older_dirs->push_back(*result);
149 *latest = version;
150 *result = path;
151 found = true;
152 } else {
153 if (older_dirs)
154 older_dirs->push_back(path);
155 }
156 }
157 }
158
159 class SwReporterComponentInstaller : public ComponentInstaller {
waffles 2014/06/17 23:30:11 I think you might be able to reduce the amount of
MAD 2014/06/18 01:38:49 Done.
160 public:
161 explicit SwReporterComponentInstaller(const Version& version,
Sorin Jianu 2014/06/19 19:08:48 explicit makes sense with one-param ctor unless yo
162 PrefService* prefs);
163 virtual ~SwReporterComponentInstaller() {}
164
165 // ComponentInstaller overrides.
166 virtual void OnUpdateError(int error) OVERRIDE;
167 virtual bool Install(const base::DictionaryValue& manifest,
168 const base::FilePath& unpack_path) OVERRIDE;
169 virtual bool GetInstalledFile(const std::string& file,
170 base::FilePath* installed_file) OVERRIDE;
171
172 private:
173 void UpdateInstallPrefsOnUIThread();
174
175 Version current_version_;
176 PrefService* prefs_;
177 };
178
179 SwReporterComponentInstaller::SwReporterComponentInstaller(
180 const Version& version,
181 PrefService* prefs)
182 : current_version_(version), prefs_(prefs) {
183 DCHECK(version.IsValid());
184 DCHECK(prefs_);
185 }
186
187 void SwReporterComponentInstaller::OnUpdateError(int error) {
188 ReportSwReporterStep(SW_REPORTER_COMPONENT_UPDATE_ERROR);
189 NOTREACHED() << "SwReporter update error: " << error;
190 }
191
192 bool SwReporterComponentInstaller::Install(
193 const base::DictionaryValue& manifest,
194 const base::FilePath& unpack_path) {
195 std::string name;
196 manifest.GetStringASCII("name", &name);
197 if (name != kSwReporterManifestName)
198 return false;
199
200 std::string proposed_version;
201 manifest.GetStringASCII("version", &proposed_version);
202 Version new_version(proposed_version.c_str());
203 if (!new_version.IsValid())
204 return false;
205
206 base::FilePath sw_reporter_path(GetSwReporterBaseDirectory());
207 base::FilePath new_version_path(
208 sw_reporter_path.AppendASCII(proposed_version));
209 if (current_version_.CompareTo(new_version) < 0) {
210 // If the latest version identified is current_version_, then
211 // new_version_path should not exist.
212 DCHECK(!base::PathExists(new_version_path));
213 if (!base::PathExists(unpack_path.Append(kSwReporterExeName)) ||
214 !base::Move(unpack_path, new_version_path)) {
215 PLOG(ERROR) << "Couldn't move files to new_version_path.\n"
216 << unpack_path.value() << " -> " << new_version_path.value();
217 return false;
218 }
219 } else {
220 // We should never get an older version than the current one.
221 DCHECK(current_version_.CompareTo(new_version) == 0);
222 if (!base::PathExists(new_version_path.Append(kSwReporterExeName))) {
223 PLOG(INFO) << "existing version_path doesn't contain file.";
224 return false;
225 }
226 }
227
228 // Passed the basic tests, update the prefs on the UI thread.
229 BrowserThread::PostTask(
230 BrowserThread::UI,
231 FROM_HERE,
232 base::Bind(&SwReporterComponentInstaller::UpdateInstallPrefsOnUIThread,
233 base::Unretained(this)));
234
235 // Installation is done. Now execute the reporter.
236 base::FilePath reporter_exe_full_path(
237 new_version_path.Append(kSwReporterExeName));
238 base::CommandLine reporter_command_line(reporter_exe_full_path);
239 base::ProcessHandle scan_reporter_process = NULL;
240 bool success = base::LaunchProcess(
241 reporter_command_line, base::LaunchOptions(), &scan_reporter_process);
242 if (success) {
243 base::WorkerPool::PostTask(FROM_HERE,
244 base::Bind(&WaitForProcessTermination,
245 scan_reporter_process,
246 reporter_exe_full_path),
247 true);
248 // WaitForProcessTermination will close the handle.
249 scan_reporter_process = NULL;
250 return true;
251 }
252 return false;
253 }
254
255 bool SwReporterComponentInstaller::GetInstalledFile(
256 const std::string& file,
257 base::FilePath* installed_file) {
258 return false;
259 }
260
261 void SwReporterComponentInstaller::UpdateInstallPrefsOnUIThread() {
262 // We're done with the registration so clean the number of retries,
263 prefs_->SetInteger(prefs::kSwReporterComponentRegisterTryCount, 0);
264
265 int tries = prefs_->GetInteger(prefs::kSwReporterComponentExecuteTryCount);
266 ReportSwReporterStep(tries ? SW_REPORTER_RETRYING_EXECUTION
267 : SW_REPORTER_START_EXECUTION);
268 prefs_->SetInteger(prefs::kSwReporterComponentExecuteTryCount, tries + 1);
269 }
270
271 // Complete the registration of the component on the UI thread...
272 void FinishSwReporterUpdateRegistration(ComponentUpdateService* cus,
273 const Version& version,
274 PrefService* prefs) {
275 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
276
277 CrxComponent SwReporter;
278 SwReporter.name = "SwReporter";
279 SwReporter.installer = new SwReporterComponentInstaller(version, prefs);
280 SwReporter.version = version;
281 SwReporter.pk_hash.assign(kSha2Hash, &kSha2Hash[sizeof(kSha2Hash)]);
282 if (cus->RegisterComponent(SwReporter) == ComponentUpdateService::kError)
283 NOTREACHED() << "SwReporter component registration fail";
284 }
285
286 // Check if there already is a version of SwReporter installed, and if so
287 // register it.
288 void RegisterSwReporterComponentOnFileThread(ComponentUpdateService* cus,
289 PrefService* prefs) {
290 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::FILE));
291 base::FilePath path = GetSwReporterBaseDirectory();
292 if (!base::PathExists(path)) {
293 if (!base::CreateDirectory(path)) {
294 NOTREACHED() << "Could not create SwReporter directory.";
295 return;
296 }
297 }
298
299 Version version(kNullVersion);
300 std::vector<base::FilePath> older_dirs;
301 GetLatestSwReporterDirectory(&path, &version, &older_dirs);
302
303 BrowserThread::PostTask(
304 BrowserThread::UI,
305 FROM_HERE,
306 base::Bind(&FinishSwReporterUpdateRegistration, cus, version, prefs));
307
308 // Remove older versions of SwReporter.
309 for (std::vector<base::FilePath>::iterator iter = older_dirs.begin();
310 iter != older_dirs.end();
311 ++iter) {
312 base::DeleteFile(*iter, true);
313 }
314 }
315
316 } // namespace
317
318 void RegisterSwReporterComponent(ComponentUpdateService* cus,
319 PrefService* prefs) {
320 int tries = prefs->GetInteger(prefs::kSwReporterComponentRegisterTryCount);
321 if (tries > kMaxRetry) {
322 ReportSwReporterStep(SW_REPORTER_RETRIED_TOO_MANY_DOWNLOADS);
323 // Let's forget about this attempt.
324 prefs->SetInteger(prefs::kSwReporterComponentRegisterTryCount, 0);
325 return;
326 }
327
328 ReportSwReporterStep(tries ? SW_REPORTER_RETRYING_DOWNLOAD
329 : SW_REPORTER_START_DOWNLOAD);
330 prefs->SetInteger(prefs::kSwReporterComponentRegisterTryCount, tries + 1);
331 BrowserThread::PostTask(
332 BrowserThread::FILE,
333 FROM_HERE,
334 base::Bind(&RegisterSwReporterComponentOnFileThread, cus, prefs));
335 }
336
337 void MaybeRegisterSwReporterComponent(ComponentUpdateService* cus,
338 PrefService* prefs) {
339 // Only register if we have a pending registration / execution.
340 if (prefs->GetInteger(prefs::kSwReporterComponentRegisterTryCount) == 0) {
341 if (prefs->GetInteger(prefs::kSwReporterComponentExecuteTryCount) == 0)
342 return;
343 // We have a pending execution, let's check if it completed or not.
344 base::win::RegKey srt_key(
345 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_READ);
346 DWORD exit_code = -1;
347 if (srt_key.Valid() &&
348 srt_key.ReadValueDW(kExitCodeRegistryValueName, &exit_code) ==
349 ERROR_SUCCESS) {
350 ReportAndClearSwReporterExitCode(exit_code);
351 return;
352 }
353
354 // If it didn't complete, let's make sure we didn't go beyond the maximum
355 // retry count yet.
356 if (prefs->GetInteger(prefs::kSwReporterComponentExecuteTryCount) >
357 kMaxRetry) {
358 ReportSwReporterStep(SW_REPORTER_RETRIED_TOO_MANY_EXECUTIONS);
359 // Let's forget about this attempt.
360 prefs->SetInteger(prefs::kSwReporterComponentExecuteTryCount, 0);
361 return;
362 }
363 }
364
365 RegisterSwReporterComponent(cus, prefs);
366 }
367
368 void RegisterPrefsForSwReporterComponent(PrefRegistrySimple* registry) {
369 registry->RegisterIntegerPref(prefs::kSwReporterComponentRegisterTryCount, 0);
370 registry->RegisterIntegerPref(prefs::kSwReporterComponentExecuteTryCount, 0);
371 }
372
373 } // namespace component_updater
OLDNEW
« no previous file with comments | « chrome/browser/component_updater/sw_reporter_component_installer_win.h ('k') | chrome/browser/prefs/browser_prefs.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698