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

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: Sync'd to ToT again (previous upload failed halfway). 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 "
Sorin Jianu 2014/06/19 19:08:49 we could drop component from sw_reporter_component
MAD 2014/06/19 21:31:50 Done.
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/browser/component_updater/default_component_installer.h"
31 #include "chrome/common/chrome_paths.h"
32 #include "chrome/common/pref_names.h"
33 #include "content/public/browser/browser_thread.h"
34
35 using content::BrowserThread;
36
37 namespace component_updater {
38
39 namespace {
40
41 // These values are used to send UMA information and are replicated in the
42 // histograms.xml file, so the order MUST NOT CHANGE.
43 enum SwReporterUmaValue {
44 SW_REPORTER_START_DOWNLOAD = 0,
45 SW_REPORTER_RETRYING_DOWNLOAD = 1,
46 SW_REPORTER_RETRIED_TOO_MANY_DOWNLOADS = 2,
47 SW_REPORTER_START_EXECUTION = 3,
48 SW_REPORTER_RETRYING_EXECUTION = 4,
49 SW_REPORTER_RETRIED_TOO_MANY_EXECUTIONS = 5,
50 SW_REPORTER_FAILED_TO_LAUNCH_REPORTER = 6,
51 SW_REPORTER_MAX,
52 };
53
54 // The maximum number of times to retry a download on startup.
55 const int kMaxRetry = 7;
56
57 // CRX hash. The extension id is: gkmgaooipdjhmangpemjhigmamcehddo. The hash was
58 // generated in Python with something like this:
59 // hashlib.sha256().update(open("<file>.crx").read()[16:16+294]).digest().
60 const uint8 kSha256Hash[] = {0x6a, 0xc6, 0x0e, 0xe8, 0xf3, 0x97, 0xc0, 0xd6,
61 0xf4, 0xc9, 0x78, 0x6c, 0x0c, 0x24, 0x73, 0x3e,
62 0x05, 0xa5, 0x62, 0x4b, 0x2e, 0xc7, 0xb7, 0x1c,
63 0x5f, 0xea, 0xf0, 0x88, 0xf6, 0x97, 0x9b, 0xc7};
64
65 const base::FilePath::CharType kSwReporterExeName[] =
66 FILE_PATH_LITERAL("software_reporter_tool.exe");
67
68 // Where to fetch the reporter exit code in the registry.
69 const wchar_t kSoftwareRemovalToolRegistryKey[] =
70 L"Software\\Google\\Software Removal Tool";
71 const wchar_t kExitCodeRegistryValueName[] = L"ExitCode";
72
73 void ReportSwReporterStep(SwReporterUmaValue value) {
74 UMA_HISTOGRAM_ENUMERATION(
75 "ComponentUpdater.SwReporterStep", value, SW_REPORTER_MAX);
76 }
77
78 // This function is called on the UI thread to report the SwReporter exit code
79 // and then to clear it from the registry as well as clear the execution state
80 // from the local state. This could be called from an interruptible worker
81 // thread so should be resilient to unexpected shutdown.
82 void ReportAndClearSwReporterExitCode(int exit_code) {
83 UMA_HISTOGRAM_SPARSE_SLOWLY("ComponentUpdater.SwReporterExitCode", exit_code);
84
85 base::win::RegKey srt_key(
86 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_WRITE);
87 srt_key.DeleteValue(kExitCodeRegistryValueName);
88
89 // Now that we are done we can reset the try count.
90 g_browser_process->local_state()->SetInteger(
91 prefs::kSwReporterComponentExecuteTryCount, 0);
92 }
93
94 // This function is called from a worker thread to wait for the SwReporter to
95 // complete and then collect its exit code. This task could be interrupted by
96 // a shutdown at anytime, so it shouldn't depend on anything external that could
97 // be shutdown beforehand.
98 void LaunchProcessAndWaitForTermination(const base::FilePath& exe_path) {
99 base::CommandLine reporter_command_line(exe_path);
Sorin Jianu 2014/06/19 19:08:49 const?
MAD 2014/06/19 21:31:50 Done.
100 base::ProcessHandle scan_reporter_process = NULL;
Sorin Jianu 2014/06/19 19:08:49 Even if we are in Windows code, would it make sens
MAD 2014/06/19 21:31:50 Done.
101 if (!base::LaunchProcess(reporter_command_line,
102 base::LaunchOptions(),
103 &scan_reporter_process)) {
104 ReportSwReporterStep(SW_REPORTER_FAILED_TO_LAUNCH_REPORTER);
105 return;
106 }
107
108 int exit_code = -1;
109 bool success = base::WaitForExitCode(scan_reporter_process, &exit_code);
110 DCHECK(success);
111 ::CloseHandle(scan_reporter_process);
112 scan_reporter_process = NULL;
113
114 BrowserThread::PostTask(
115 BrowserThread::UI,
116 FROM_HERE,
117 base::Bind(&ReportAndClearSwReporterExitCode, exit_code));
118 }
119
120 void SwReporterExecutionUITask(const base::FilePath& executable_path,
121 PrefService* prefs) {
122 // If we get here without a register or an execute try count, this means that
123 // the component have been silently updated to a new version, so we don't want
124 // to execute.
125 int execute_try_count =
126 prefs->GetInteger(prefs::kSwReporterComponentExecuteTryCount);
127 if (execute_try_count == 0 &&
128 prefs->GetInteger(prefs::kSwReporterComponentRegisterTryCount) == 0) {
129 return;
130 }
131
132 // If we get here, we're done with the registration so clear the number of
133 // retries, and add one more try at execution.
Sorin Jianu 2014/06/19 19:08:49 Might be better for readability to do: ++execute_t
MAD 2014/06/19 21:31:50 Done.
134 prefs->SetInteger(prefs::kSwReporterComponentRegisterTryCount, 0);
135 ReportSwReporterStep(execute_try_count ? SW_REPORTER_RETRYING_EXECUTION
136 : SW_REPORTER_START_EXECUTION);
137 prefs->SetInteger(prefs::kSwReporterComponentExecuteTryCount,
138 execute_try_count + 1);
139
140 // Now execute the reporter from a worker thread.
141 base::WorkerPool::PostTask(
142 FROM_HERE,
143 base::Bind(&LaunchProcessAndWaitForTermination,
144 executable_path.Append(kSwReporterExeName)),
145 true);
146 }
147
148 // The base directory on windows looks like:
149 // <profile>\AppData\Local\Google\Chrome\User Data\SwReporter\.
150 base::FilePath GetSwReporterBaseDirectory() {
151 base::FilePath result;
152 PathService::Get(chrome::DIR_USER_DATA, &result);
153 return result.Append(FILE_PATH_LITERAL("SwReporter"));
154 }
155
156 // Enumerate the version directories to find the latest one. On success,
157 // latest_version_path is set to something like:
158 // [user-data-dir]\SwReporter\<ver>.
159 bool GetLatestSwReporterDirectory(base::FilePath* latest_version_path) {
Sorin Jianu 2014/06/19 19:08:49 I recall waffles@ said this function is not needed
MAD 2014/06/19 21:31:50 Done.
160 base::FilePath base_dir = GetSwReporterBaseDirectory();
Sorin Jianu 2014/06/19 19:08:49 const?
MAD 2014/06/19 21:31:50 Done.
161 bool found = false;
162 base::FileEnumerator file_enumerator(
163 base_dir, false, base::FileEnumerator::DIRECTORIES);
164 Version latest_version("0.0.0.0");
165 for (base::FilePath path = file_enumerator.Next(); !path.value().empty();
166 path = file_enumerator.Next()) {
167 Version version(path.BaseName().MaybeAsASCII());
168 if (!version.IsValid())
169 continue;
170 if (version.CompareTo(latest_version) > 0 &&
171 base::PathExists(path.Append(kSwReporterExeName))) {
172 *latest_version_path = path;
173 latest_version = version;
174 found = true;
175 }
176 }
177 return found;
178 }
179
180 class SwReporterComponentInstallerTraits : public ComponentInstallerTraits {
181 public:
182 explicit SwReporterComponentInstallerTraits(PrefService* prefs)
183 : prefs_(prefs) {}
184
185 virtual ~SwReporterComponentInstallerTraits() {}
186
187 virtual bool VerifyInstallation(const base::FilePath& dir) const {
188 return base::PathExists(dir.Append(kSwReporterExeName));
189 }
190
191 virtual bool CanAutoUpdate() const { return true; }
192
193 virtual bool OnCustomInstall(const base::DictionaryValue& manifest,
194 const base::FilePath& install_dir) {
195 return true;
196 }
197
198 virtual void ComponentReady(const base::Version& version,
199 const base::FilePath& install_dir,
200 scoped_ptr<base::DictionaryValue> manifest) {
201 // Installation is over, now check if we have a pending execution.
202 SwReporterExecutionUITask(install_dir, prefs_);
203 }
204
205 virtual base::FilePath GetBaseDirectory() const {
206 return GetSwReporterBaseDirectory();
207 }
208
209 virtual void GetHash(std::vector<uint8>* hash) const {
210 DCHECK(hash);
211 hash->assign(kSha256Hash, &kSha256Hash[sizeof(kSha256Hash)]);
212 }
213
214 virtual std::string GetName() const { return "Software Reporter Tool"; }
215
216 private:
217 PrefService* prefs_;
218 };
219
220 void FindSwReporterComponentOnFileThread(ComponentUpdateService* cus,
Sorin Jianu 2014/06/19 19:08:49 We want to avoid trying to find our files and vers
MAD 2014/06/19 21:31:50 Yeah, I think Joshua was suggesting I use the pres
221 PrefService* prefs) {
222 base::FilePath latest_version_path;
223 if (!GetLatestSwReporterDirectory(&latest_version_path)) {
224 scoped_ptr<ComponentInstallerTraits> traits(
225 new SwReporterComponentInstallerTraits(prefs));
226 // |cus| will take ownership of |installer| during installer->Register(cus).
227 DefaultComponentInstaller* installer =
228 new DefaultComponentInstaller(traits.Pass());
229 installer->Register(cus);
230 } else {
231 BrowserThread::PostTask(
232 BrowserThread::UI,
233 FROM_HERE,
234 base::Bind(&SwReporterExecutionUITask, latest_version_path, prefs));
235 }
236 }
237
238 void ExecuteSwReporterComponentImpl(ComponentUpdateService* cus,
239 PrefService* prefs,
240 int register_try_count) {
241 if (register_try_count > kMaxRetry) {
242 ReportSwReporterStep(SW_REPORTER_RETRIED_TOO_MANY_DOWNLOADS);
243 // Let's forget about this attempt.
244 prefs->SetInteger(prefs::kSwReporterComponentRegisterTryCount, 0);
245 return;
246 }
247
248 ReportSwReporterStep(register_try_count ? SW_REPORTER_RETRYING_DOWNLOAD
249 : SW_REPORTER_START_DOWNLOAD);
250 prefs->SetInteger(prefs::kSwReporterComponentRegisterTryCount,
251 register_try_count + 1);
252 BrowserThread::PostTask(
253 BrowserThread::FILE,
254 FROM_HERE,
255 base::Bind(&FindSwReporterComponentOnFileThread, cus, prefs));
256 }
257
258 } // namespace
259
260 void ExecuteSwReporterComponent(ComponentUpdateService* cus,
261 PrefService* prefs) {
262 // This is an explicit call, so let's forget about previous incomplete
263 // attempts and start from scratch.
264 prefs->SetInteger(prefs::kSwReporterComponentExecuteTryCount, 0);
265 prefs->SetInteger(prefs::kSwReporterComponentRegisterTryCount, 0);
266 ExecuteSwReporterComponentImpl(cus, prefs, 0);
267 }
268
269 void ExecutePendingSwReporterComponent(ComponentUpdateService* cus,
270 PrefService* prefs) {
271 // Only register / execute if we have a pending registration / execution.
272 int register_try_count =
273 prefs->GetInteger(prefs::kSwReporterComponentRegisterTryCount);
274 int execute_try_count =
275 prefs->GetInteger(prefs::kSwReporterComponentExecuteTryCount);
276 if (register_try_count == 0) {
277 if (execute_try_count == 0)
278 return;
279 // We have a pending execution, let's check if it completed or not.
280 base::win::RegKey srt_key(
281 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_READ);
282 DWORD exit_code = -1;
283 if (srt_key.Valid() &&
284 srt_key.ReadValueDW(kExitCodeRegistryValueName, &exit_code) ==
285 ERROR_SUCCESS) {
286 ReportAndClearSwReporterExitCode(exit_code);
287 return;
288 }
289
290 // If it didn't complete, let's make sure we didn't go beyond the maximum
291 // retry count yet.
292 if (execute_try_count > kMaxRetry) {
293 ReportSwReporterStep(SW_REPORTER_RETRIED_TOO_MANY_EXECUTIONS);
294 // Let's forget about this attempt.
295 prefs->SetInteger(prefs::kSwReporterComponentExecuteTryCount, 0);
296 return;
297 }
298 }
299
300 ExecuteSwReporterComponentImpl(cus, prefs, register_try_count);
301 }
302
303 void RegisterPrefsForSwReporterComponent(PrefRegistrySimple* registry) {
304 registry->RegisterIntegerPref(prefs::kSwReporterComponentRegisterTryCount, 0);
305 registry->RegisterIntegerPref(prefs::kSwReporterComponentExecuteTryCount, 0);
306 }
307
308 } // namespace component_updater
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698