OLD | NEW |
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 #include <string> | 8 #include <string> |
9 #include <vector> | 9 #include <vector> |
10 | 10 |
(...skipping 10 matching lines...) Expand all Loading... |
21 #include "base/prefs/pref_registry_simple.h" | 21 #include "base/prefs/pref_registry_simple.h" |
22 #include "base/prefs/pref_service.h" | 22 #include "base/prefs/pref_service.h" |
23 #include "base/process/kill.h" | 23 #include "base/process/kill.h" |
24 #include "base/process/launch.h" | 24 #include "base/process/launch.h" |
25 #include "base/task_runner_util.h" | 25 #include "base/task_runner_util.h" |
26 #include "base/threading/worker_pool.h" | 26 #include "base/threading/worker_pool.h" |
27 #include "base/time/time.h" | 27 #include "base/time/time.h" |
28 #include "base/win/registry.h" | 28 #include "base/win/registry.h" |
29 #include "chrome/browser/browser_process.h" | 29 #include "chrome/browser/browser_process.h" |
30 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" | 30 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" |
| 31 #include "chrome/browser/profiles/profile.h" |
| 32 #include "chrome/browser/safe_browsing/srt_global_error_win.h" |
| 33 #include "chrome/browser/ui/browser_finder.h" |
| 34 #include "chrome/browser/ui/global_error/global_error_service.h" |
| 35 #include "chrome/browser/ui/global_error/global_error_service_factory.h" |
31 #include "chrome/common/pref_names.h" | 36 #include "chrome/common/pref_names.h" |
32 #include "components/component_updater/component_updater_paths.h" | 37 #include "components/component_updater/component_updater_paths.h" |
33 #include "components/component_updater/component_updater_service.h" | 38 #include "components/component_updater/component_updater_service.h" |
34 #include "components/component_updater/component_updater_utils.h" | 39 #include "components/component_updater/component_updater_utils.h" |
35 #include "components/component_updater/default_component_installer.h" | 40 #include "components/component_updater/default_component_installer.h" |
36 #include "components/component_updater/pref_names.h" | 41 #include "components/component_updater/pref_names.h" |
| 42 #include "components/pref_registry/pref_registry_syncable.h" |
37 #include "content/public/browser/browser_thread.h" | 43 #include "content/public/browser/browser_thread.h" |
38 | 44 |
39 using content::BrowserThread; | 45 using content::BrowserThread; |
40 | 46 |
41 namespace component_updater { | 47 namespace component_updater { |
42 | 48 |
43 namespace { | 49 namespace { |
44 | 50 |
45 // These values are used to send UMA information and are replicated in the | 51 // These values are used to send UMA information and are replicated in the |
46 // histograms.xml file, so the order MUST NOT CHANGE. | 52 // histograms.xml file, so the order MUST NOT CHANGE. |
(...skipping 23 matching lines...) Expand all Loading... |
70 0x5f, 0xea, 0xf0, 0x88, 0xf6, 0x97, 0x9b, 0xc7}; | 76 0x5f, 0xea, 0xf0, 0x88, 0xf6, 0x97, 0x9b, 0xc7}; |
71 | 77 |
72 const base::FilePath::CharType kSwReporterExeName[] = | 78 const base::FilePath::CharType kSwReporterExeName[] = |
73 FILE_PATH_LITERAL("software_reporter_tool.exe"); | 79 FILE_PATH_LITERAL("software_reporter_tool.exe"); |
74 | 80 |
75 // Where to fetch the reporter exit code in the registry. | 81 // Where to fetch the reporter exit code in the registry. |
76 const wchar_t kSoftwareRemovalToolRegistryKey[] = | 82 const wchar_t kSoftwareRemovalToolRegistryKey[] = |
77 L"Software\\Google\\Software Removal Tool"; | 83 L"Software\\Google\\Software Removal Tool"; |
78 const wchar_t kExitCodeRegistryValueName[] = L"ExitCode"; | 84 const wchar_t kExitCodeRegistryValueName[] = L"ExitCode"; |
79 | 85 |
| 86 // Exit codes that identify that a cleanup is needed. |
| 87 const int kCleanupNeeded = 0; |
| 88 const int kPostRebootCleanupNeeded = 4; |
| 89 |
80 void ReportUmaStep(SwReporterUmaValue value) { | 90 void ReportUmaStep(SwReporterUmaValue value) { |
81 UMA_HISTOGRAM_ENUMERATION("SoftwareReporter.Step", value, SW_REPORTER_MAX); | 91 UMA_HISTOGRAM_ENUMERATION("SoftwareReporter.Step", value, SW_REPORTER_MAX); |
82 } | 92 } |
83 | 93 |
| 94 void ReportUmaVersion(const base::Version& version) { |
| 95 DCHECK(!version.components().empty()); |
| 96 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.MinorVersion", |
| 97 version.components().back()); |
| 98 // The major version uses the 1st component value (when there is more than |
| 99 // one, since the last one is always the minor version) as a hi word in a |
| 100 // double word. The low word is either the second component (when there are |
| 101 // only three) or the 3rd one if there are at least 4. E.g., for W.X.Y.Z, we |
| 102 // ignore X, and Z is the minor version. We compute the major version with W |
| 103 // as the hi word, and Y as the low word. For X.Y.Z, we use X and Y as hi and |
| 104 // low words, and if we would have Y.Z we would use Y as the hi word and 0 as |
| 105 // the low word. major version is 0 if the version only has one component. |
| 106 uint32_t major_version = 0; |
| 107 if (version.components().size() > 1) |
| 108 major_version = 0x10000 * version.components()[0]; |
| 109 if (version.components().size() < 4 && version.components().size() > 2) |
| 110 major_version += version.components()[1]; |
| 111 else if (version.components().size() > 3) |
| 112 major_version += version.components()[2]; |
| 113 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.MajorVersion", major_version); |
| 114 } |
| 115 |
84 // This function is called on the UI thread to report the SwReporter exit code | 116 // This function is called on the UI thread to report the SwReporter exit code |
85 // and then clear it from the registry as well as clear the execution state | 117 // and then clear it from the registry as well as clear the execution state |
86 // from the local state. This could be called from an interruptible worker | 118 // from the local state. This could be called from an interruptible worker |
87 // thread so should be resilient to unexpected shutdown. | 119 // thread so should be resilient to unexpected shutdown. |version| is provided |
88 void ReportAndClearExitCode(int exit_code) { | 120 // so the kSwReporterPromptVersion prefs can be set. |
| 121 void ReportAndClearExitCode(int exit_code, const std::string& version) { |
89 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code); | 122 UMA_HISTOGRAM_SPARSE_SLOWLY("SoftwareReporter.ExitCode", exit_code); |
| 123 if (g_browser_process && g_browser_process->local_state()) { |
| 124 g_browser_process->local_state()->SetInteger(prefs::kSwReporterLastExitCode, |
| 125 exit_code); |
| 126 } |
| 127 |
| 128 if (exit_code == kPostRebootCleanupNeeded || exit_code == kCleanupNeeded) { |
| 129 // Find the last active browser, which may be NULL, in which case we won't |
| 130 // show the prompt this time and will wait until the next run of the |
| 131 // reporter. We can't use other ways of finding a browser because we don't |
| 132 // have a profile. |
| 133 chrome::HostDesktopType desktop_type = chrome::GetActiveDesktop(); |
| 134 Browser* browser = chrome::FindLastActiveWithHostDesktopType(desktop_type); |
| 135 if (browser) { |
| 136 Profile* profile = browser->profile(); |
| 137 DCHECK(profile); |
| 138 // Now that we have a profile, make sure we have a tabbed browser since we |
| 139 // need to anchor the bubble to the toolbar's wrench menu. Create one if |
| 140 // none exist already. |
| 141 if (browser->type() != Browser::TYPE_TABBED) { |
| 142 browser = chrome::FindTabbedBrowser(profile, false, desktop_type); |
| 143 if (!browser) |
| 144 browser = new Browser(Browser::CreateParams(profile, desktop_type)); |
| 145 } |
| 146 const std::string prompt_version = |
| 147 profile->GetPrefs()->GetString(prefs::kSwReporterPromptVersion); |
| 148 // Don't show the prompt again if it's been shown before. |
| 149 if (prompt_version.empty()) { |
| 150 profile->GetPrefs()->SetString(prefs::kSwReporterPromptVersion, |
| 151 version); |
| 152 profile->GetPrefs()->SetInteger(prefs::kSwReporterPromptReason, |
| 153 exit_code); |
| 154 GlobalErrorService* global_error_service = |
| 155 GlobalErrorServiceFactory::GetForProfile(profile); |
| 156 SRTGlobalError* global_error = new SRTGlobalError(global_error_service); |
| 157 // |global_error_service| takes ownership of |global_error| and keeps it |
| 158 // alive until RemoveGlobalError() is called, and even then, the object |
| 159 // is not destroyed, the caller of RemoveGlobalError is responsible to |
| 160 // destroy it, and in the case of the SRTGlobalError, it deletes itself |
| 161 // but only after the bubble has been interacted with. |
| 162 global_error_service->AddGlobalError(global_error); |
| 163 |
| 164 // Do not try to show bubble if another GlobalError is already showing |
| 165 // one. The bubble will be shown once the others have been dismissed. |
| 166 const GlobalErrorService::GlobalErrorList& global_errors( |
| 167 global_error_service->errors()); |
| 168 GlobalErrorService::GlobalErrorList::const_iterator it; |
| 169 for (it = global_errors.begin(); it != global_errors.end(); ++it) { |
| 170 if ((*it)->GetBubbleView()) |
| 171 break; |
| 172 } |
| 173 if (it == global_errors.end()) |
| 174 global_error->ShowBubbleView(browser); |
| 175 } |
| 176 } |
| 177 } |
90 | 178 |
91 base::win::RegKey srt_key( | 179 base::win::RegKey srt_key( |
92 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_WRITE); | 180 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_WRITE); |
93 srt_key.DeleteValue(kExitCodeRegistryValueName); | 181 srt_key.DeleteValue(kExitCodeRegistryValueName); |
94 } | 182 } |
95 | 183 |
96 // This function is called from a worker thread to launch the SwReporter and | 184 // This function is called from a worker thread to launch the SwReporter and |
97 // wait for termination to collect its exit code. This task could be interrupted | 185 // wait for termination to collect its exit code. This task could be interrupted |
98 // by a shutdown at anytime, so it shouldn't depend on anything external that | 186 // by a shutdown at anytime, so it shouldn't depend on anything external that |
99 // could be shutdown beforehand. | 187 // could be shutdown beforehand. |
100 void LaunchAndWaitForExit(const base::FilePath& exe_path) { | 188 void LaunchAndWaitForExit(const base::FilePath& exe_path, |
| 189 const std::string& version) { |
101 const base::CommandLine reporter_command_line(exe_path); | 190 const base::CommandLine reporter_command_line(exe_path); |
102 base::ProcessHandle scan_reporter_process = base::kNullProcessHandle; | 191 base::ProcessHandle scan_reporter_process = base::kNullProcessHandle; |
103 if (!base::LaunchProcess(reporter_command_line, | 192 if (!base::LaunchProcess(reporter_command_line, |
104 base::LaunchOptions(), | 193 base::LaunchOptions(), |
105 &scan_reporter_process)) { | 194 &scan_reporter_process)) { |
106 ReportUmaStep(SW_REPORTER_FAILED_TO_START); | 195 ReportUmaStep(SW_REPORTER_FAILED_TO_START); |
107 return; | 196 return; |
108 } | 197 } |
109 ReportUmaStep(SW_REPORTER_START_EXECUTION); | 198 ReportUmaStep(SW_REPORTER_START_EXECUTION); |
110 | 199 |
111 int exit_code = -1; | 200 int exit_code = -1; |
112 bool success = base::WaitForExitCode(scan_reporter_process, &exit_code); | 201 bool success = base::WaitForExitCode(scan_reporter_process, &exit_code); |
113 DCHECK(success); | 202 DCHECK(success); |
114 base::CloseProcessHandle(scan_reporter_process); | 203 base::CloseProcessHandle(scan_reporter_process); |
115 scan_reporter_process = base::kNullProcessHandle; | 204 scan_reporter_process = base::kNullProcessHandle; |
116 // It's OK if this doesn't complete, the work will continue on next startup. | 205 // It's OK if this doesn't complete, the work will continue on next startup. |
117 BrowserThread::PostTask(BrowserThread::UI, | 206 BrowserThread::PostTask( |
118 FROM_HERE, | 207 BrowserThread::UI, |
119 base::Bind(&ReportAndClearExitCode, exit_code)); | |
120 } | |
121 | |
122 void ExecuteReporter(const base::FilePath& install_dir) { | |
123 base::WorkerPool::PostTask( | |
124 FROM_HERE, | 208 FROM_HERE, |
125 base::Bind(&LaunchAndWaitForExit, install_dir.Append(kSwReporterExeName)), | 209 base::Bind(&ReportAndClearExitCode, exit_code, version)); |
126 true); | |
127 } | 210 } |
128 | 211 |
129 class SwReporterInstallerTraits : public ComponentInstallerTraits { | 212 class SwReporterInstallerTraits : public ComponentInstallerTraits { |
130 public: | 213 public: |
131 explicit SwReporterInstallerTraits(PrefService* prefs) : prefs_(prefs) {} | 214 explicit SwReporterInstallerTraits(PrefService* prefs) : prefs_(prefs) {} |
132 | 215 |
133 virtual ~SwReporterInstallerTraits() {} | 216 virtual ~SwReporterInstallerTraits() {} |
134 | 217 |
135 virtual bool VerifyInstallation(const base::FilePath& dir) const { | 218 virtual bool VerifyInstallation(const base::FilePath& dir) const { |
136 return base::PathExists(dir.Append(kSwReporterExeName)); | 219 return base::PathExists(dir.Append(kSwReporterExeName)); |
137 } | 220 } |
138 | 221 |
139 virtual bool CanAutoUpdate() const { return true; } | 222 virtual bool CanAutoUpdate() const { return true; } |
140 | 223 |
141 virtual bool OnCustomInstall(const base::DictionaryValue& manifest, | 224 virtual bool OnCustomInstall(const base::DictionaryValue& manifest, |
142 const base::FilePath& install_dir) { | 225 const base::FilePath& install_dir) { |
143 return true; | 226 return true; |
144 } | 227 } |
145 | 228 |
146 virtual void ComponentReady(const base::Version& version, | 229 virtual void ComponentReady(const base::Version& version, |
147 const base::FilePath& install_dir, | 230 const base::FilePath& install_dir, |
148 scoped_ptr<base::DictionaryValue> manifest) { | 231 scoped_ptr<base::DictionaryValue> manifest) { |
149 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); | 232 DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| 233 ReportUmaVersion(version); |
150 | 234 |
151 wcsncpy_s(version_dir_, | 235 wcsncpy_s(version_dir_, |
152 _MAX_PATH, | 236 _MAX_PATH, |
153 install_dir.value().c_str(), | 237 install_dir.value().c_str(), |
154 install_dir.value().size()); | 238 install_dir.value().size()); |
155 | 239 |
156 // A previous run may have results in the registry, so check and report | 240 // A previous run may have results in the registry, so check and report |
157 // them if present. | 241 // them if present. |
| 242 std::string version_string(version.GetString()); |
158 base::win::RegKey srt_key( | 243 base::win::RegKey srt_key( |
159 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_READ); | 244 HKEY_CURRENT_USER, kSoftwareRemovalToolRegistryKey, KEY_READ); |
160 DWORD exit_code; | 245 DWORD exit_code; |
161 if (srt_key.Valid() && | 246 if (srt_key.Valid() && |
162 srt_key.ReadValueDW(kExitCodeRegistryValueName, &exit_code) == | 247 srt_key.ReadValueDW(kExitCodeRegistryValueName, &exit_code) == |
163 ERROR_SUCCESS) { | 248 ERROR_SUCCESS) { |
164 ReportUmaStep(SW_REPORTER_REGISTRY_EXIT_CODE); | 249 ReportUmaStep(SW_REPORTER_REGISTRY_EXIT_CODE); |
165 ReportAndClearExitCode(exit_code); | 250 ReportAndClearExitCode(exit_code, version_string); |
166 } | 251 } |
167 | 252 |
168 // If we can't access local state, we can't see when we last ran, so | 253 // If we can't access local state, we can't see when we last ran, so |
169 // just exit without running. | 254 // just exit without running. |
170 if (!g_browser_process || !g_browser_process->local_state()) | 255 if (!g_browser_process || !g_browser_process->local_state()) |
171 return; | 256 return; |
172 | 257 |
173 // Run the reporter if it hasn't been triggered in the | 258 // Run the reporter if it hasn't been triggered in the |
174 // kDaysBetweenSwReporterRuns days. | 259 // kDaysBetweenSwReporterRuns days. |
175 const base::Time last_time_triggered = base::Time::FromInternalValue( | 260 const base::Time last_time_triggered = base::Time::FromInternalValue( |
176 g_browser_process->local_state()->GetInt64( | 261 g_browser_process->local_state()->GetInt64( |
177 prefs::kSwReporterLastTimeTriggered)); | 262 prefs::kSwReporterLastTimeTriggered)); |
178 if ((base::Time::Now() - last_time_triggered).InDays() >= | 263 if ((base::Time::Now() - last_time_triggered).InDays() >= |
179 kDaysBetweenSwReporterRuns) { | 264 kDaysBetweenSwReporterRuns) { |
180 g_browser_process->local_state()->SetInt64( | 265 g_browser_process->local_state()->SetInt64( |
181 prefs::kSwReporterLastTimeTriggered, | 266 prefs::kSwReporterLastTimeTriggered, |
182 base::Time::Now().ToInternalValue()); | 267 base::Time::Now().ToInternalValue()); |
183 | 268 |
184 ExecuteReporter(install_dir); | 269 base::WorkerPool::PostTask( |
| 270 FROM_HERE, |
| 271 base::Bind(&LaunchAndWaitForExit, |
| 272 install_dir.Append(kSwReporterExeName), |
| 273 version_string), |
| 274 true); |
185 } | 275 } |
186 } | 276 } |
187 | 277 |
188 virtual base::FilePath GetBaseDirectory() const { return install_dir(); } | 278 virtual base::FilePath GetBaseDirectory() const { return install_dir(); } |
189 | 279 |
190 virtual void GetHash(std::vector<uint8_t>* hash) const { GetPkHash(hash); } | 280 virtual void GetHash(std::vector<uint8_t>* hash) const { GetPkHash(hash); } |
191 | 281 |
192 virtual std::string GetName() const { return "Software Reporter Tool"; } | 282 virtual std::string GetName() const { return "Software Reporter Tool"; } |
193 | 283 |
194 static base::FilePath install_dir() { | 284 static base::FilePath install_dir() { |
(...skipping 22 matching lines...) Expand all Loading... |
217 PrefService* prefs_; | 307 PrefService* prefs_; |
218 static wchar_t version_dir_[_MAX_PATH]; | 308 static wchar_t version_dir_[_MAX_PATH]; |
219 }; | 309 }; |
220 | 310 |
221 wchar_t SwReporterInstallerTraits::version_dir_[] = {}; | 311 wchar_t SwReporterInstallerTraits::version_dir_[] = {}; |
222 | 312 |
223 } // namespace | 313 } // namespace |
224 | 314 |
225 void RegisterSwReporterComponent(ComponentUpdateService* cus, | 315 void RegisterSwReporterComponent(ComponentUpdateService* cus, |
226 PrefService* prefs) { | 316 PrefService* prefs) { |
227 // The Sw reporter shouldn't run if the user isn't reporting metrics. | |
228 if (!ChromeMetricsServiceAccessor::IsMetricsReportingEnabled()) | |
229 return; | |
230 | |
231 // Install the component. | 317 // Install the component. |
232 scoped_ptr<ComponentInstallerTraits> traits( | 318 scoped_ptr<ComponentInstallerTraits> traits( |
233 new SwReporterInstallerTraits(prefs)); | 319 new SwReporterInstallerTraits(prefs)); |
234 // |cus| will take ownership of |installer| during installer->Register(cus). | 320 // |cus| will take ownership of |installer| during installer->Register(cus). |
235 DefaultComponentInstaller* installer = | 321 DefaultComponentInstaller* installer = |
236 new DefaultComponentInstaller(traits.Pass()); | 322 new DefaultComponentInstaller(traits.Pass()); |
237 installer->Register(cus); | 323 installer->Register(cus); |
238 } | 324 } |
239 | 325 |
240 void RegisterPrefsForSwReporter(PrefRegistrySimple* registry) { | 326 void RegisterPrefsForSwReporter(PrefRegistrySimple* registry) { |
241 registry->RegisterInt64Pref(prefs::kSwReporterLastTimeTriggered, 0); | 327 registry->RegisterInt64Pref(prefs::kSwReporterLastTimeTriggered, 0); |
| 328 registry->RegisterIntegerPref(prefs::kSwReporterLastExitCode, -1); |
| 329 } |
| 330 |
| 331 void RegisterProfilePrefsForSwReporter( |
| 332 user_prefs::PrefRegistrySyncable* registry) { |
| 333 registry->RegisterIntegerPref( |
| 334 prefs::kSwReporterPromptReason, |
| 335 -1, |
| 336 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
| 337 |
| 338 registry->RegisterStringPref( |
| 339 prefs::kSwReporterPromptVersion, |
| 340 "", |
| 341 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); |
242 } | 342 } |
243 | 343 |
244 } // namespace component_updater | 344 } // namespace component_updater |
OLD | NEW |