| OLD | NEW |
| (Empty) |
| 1 // Copyright 2015 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/safe_browsing/srt_fetcher_win.h" | |
| 6 | |
| 7 #include <stdint.h> | |
| 8 | |
| 9 #include <algorithm> | |
| 10 #include <memory> | |
| 11 #include <utility> | |
| 12 #include <vector> | |
| 13 | |
| 14 #include "base/bind.h" | |
| 15 #include "base/bind_helpers.h" | |
| 16 #include "base/callback_helpers.h" | |
| 17 #include "base/command_line.h" | |
| 18 #include "base/debug/leak_annotations.h" | |
| 19 #include "base/files/file_path.h" | |
| 20 #include "base/macros.h" | |
| 21 #include "base/memory/ptr_util.h" | |
| 22 #include "base/metrics/field_trial.h" | |
| 23 #include "base/metrics/histogram_macros.h" | |
| 24 #include "base/metrics/sparse_histogram.h" | |
| 25 #include "base/strings/string_number_conversions.h" | |
| 26 #include "base/strings/stringprintf.h" | |
| 27 #include "base/strings/utf_string_conversions.h" | |
| 28 #include "base/task_runner_util.h" | |
| 29 #include "base/task_scheduler/post_task.h" | |
| 30 #include "base/task_scheduler/task_traits.h" | |
| 31 #include "base/time/time.h" | |
| 32 #include "base/version.h" | |
| 33 #include "base/win/registry.h" | |
| 34 #include "chrome/browser/browser_process.h" | |
| 35 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h" | |
| 36 #include "chrome/browser/profiles/profile.h" | |
| 37 #include "chrome/browser/profiles/profile_io_data.h" | |
| 38 #include "chrome/browser/safe_browsing/srt_chrome_prompt_impl.h" | |
| 39 #include "chrome/browser/safe_browsing/srt_client_info_win.h" | |
| 40 #include "chrome/browser/safe_browsing/srt_global_error_win.h" | |
| 41 #include "chrome/browser/ui/browser_finder.h" | |
| 42 #include "chrome/browser/ui/browser_list.h" | |
| 43 #include "chrome/browser/ui/browser_list_observer.h" | |
| 44 #include "chrome/browser/ui/global_error/global_error_service.h" | |
| 45 #include "chrome/browser/ui/global_error/global_error_service_factory.h" | |
| 46 #include "chrome/common/pref_names.h" | |
| 47 #include "components/chrome_cleaner/public/constants/constants.h" | |
| 48 #include "components/component_updater/pref_names.h" | |
| 49 #include "components/data_use_measurement/core/data_use_user_data.h" | |
| 50 #include "components/prefs/pref_service.h" | |
| 51 #include "components/variations/net/variations_http_headers.h" | |
| 52 #include "components/version_info/version_info.h" | |
| 53 #include "content/public/browser/browser_thread.h" | |
| 54 #include "mojo/edk/embedder/connection_params.h" | |
| 55 #include "mojo/edk/embedder/pending_process_connection.h" | |
| 56 #include "mojo/edk/embedder/platform_channel_pair.h" | |
| 57 #include "mojo/public/cpp/system/message_pipe.h" | |
| 58 #include "net/base/load_flags.h" | |
| 59 #include "net/http/http_status_code.h" | |
| 60 #include "net/url_request/url_fetcher.h" | |
| 61 #include "net/url_request/url_fetcher_delegate.h" | |
| 62 #include "net/url_request/url_request_context_getter.h" | |
| 63 | |
| 64 using content::BrowserThread; | |
| 65 | |
| 66 namespace safe_browsing { | |
| 67 | |
| 68 const base::Feature kInBrowserCleanerUIFeature{ | |
| 69 "InBrowserCleanerUI", base::FEATURE_DISABLED_BY_DEFAULT}; | |
| 70 | |
| 71 namespace { | |
| 72 | |
| 73 // Used to send UMA information about missing start and end time registry | |
| 74 // values for the reporter. Replicated in the histograms.xml file, so the order | |
| 75 // MUST NOT CHANGE. | |
| 76 enum SwReporterRunningTimeRegistryError { | |
| 77 REPORTER_RUNNING_TIME_ERROR_NO_ERROR = 0, | |
| 78 REPORTER_RUNNING_TIME_ERROR_REGISTRY_KEY_INVALID = 1, | |
| 79 REPORTER_RUNNING_TIME_ERROR_MISSING_START_TIME = 2, | |
| 80 REPORTER_RUNNING_TIME_ERROR_MISSING_END_TIME = 3, | |
| 81 REPORTER_RUNNING_TIME_ERROR_MISSING_BOTH_TIMES = 4, | |
| 82 REPORTER_RUNNING_TIME_ERROR_MAX, | |
| 83 }; | |
| 84 | |
| 85 // Used to send UMA information about the progress of the SwReporter launch and | |
| 86 // prompt sequence. Replicated in the histograms.xml file, so the order MUST | |
| 87 // NOT CHANGE. | |
| 88 enum SwReporterUmaValue { | |
| 89 // Deprecated. | |
| 90 SW_REPORTER_EXPLICIT_REQUEST = 0, | |
| 91 // Deprecated. | |
| 92 SW_REPORTER_STARTUP_RETRY = 1, | |
| 93 // Deprecated. | |
| 94 SW_REPORTER_RETRIED_TOO_MANY_TIMES = 2, | |
| 95 SW_REPORTER_START_EXECUTION = 3, | |
| 96 SW_REPORTER_FAILED_TO_START = 4, | |
| 97 // Deprecated. | |
| 98 SW_REPORTER_REGISTRY_EXIT_CODE = 5, | |
| 99 // Deprecated. | |
| 100 SW_REPORTER_RESET_RETRIES = 6, | |
| 101 SW_REPORTER_DOWNLOAD_START = 7, | |
| 102 SW_REPORTER_NO_BROWSER = 8, | |
| 103 SW_REPORTER_NO_LOCAL_STATE = 9, | |
| 104 SW_REPORTER_NO_PROMPT_NEEDED = 10, | |
| 105 SW_REPORTER_NO_PROMPT_FIELD_TRIAL = 11, | |
| 106 SW_REPORTER_ALREADY_PROMPTED = 12, | |
| 107 SW_REPORTER_RAN_DAILY = 13, | |
| 108 SW_REPORTER_ADDED_TO_MENU = 14, | |
| 109 | |
| 110 SW_REPORTER_MAX, | |
| 111 }; | |
| 112 | |
| 113 // Used to send UMA information showing whether uploading of Software Reporter | |
| 114 // logs is enabled, or the reason why not. | |
| 115 // Replicated in the histograms.xml file, so the order MUST NOT CHANGE. | |
| 116 enum SwReporterLogsUploadsEnabled { | |
| 117 REPORTER_LOGS_UPLOADS_ENABLED = 0, | |
| 118 REPORTER_LOGS_UPLOADS_SBER_DISABLED = 1, | |
| 119 REPORTER_LOGS_UPLOADS_RECENTLY_SENT_LOGS = 2, | |
| 120 REPORTER_LOGS_UPLOADS_MAX, | |
| 121 }; | |
| 122 | |
| 123 // Used to send UMA information about missing logs upload result in the registry | |
| 124 // for the reporter. Replicated in the histograms.xml file, so the order | |
| 125 // MUST NOT CHANGE. | |
| 126 enum SwReporterLogsUploadResultRegistryError { | |
| 127 REPORTER_LOGS_UPLOAD_RESULT_ERROR_NO_ERROR = 0, | |
| 128 REPORTER_LOGS_UPLOAD_RESULT_ERROR_REGISTRY_KEY_INVALID = 1, | |
| 129 REPORTER_LOGS_UPLOAD_RESULT_ERROR_VALUE_NOT_FOUND = 2, | |
| 130 REPORTER_LOGS_UPLOAD_RESULT_ERROR_VALUE_OUT_OF_BOUNDS = 3, | |
| 131 REPORTER_LOGS_UPLOAD_RESULT_ERROR_MAX, | |
| 132 }; | |
| 133 | |
| 134 const char kRunningTimeErrorMetricName[] = | |
| 135 "SoftwareReporter.RunningTimeRegistryError"; | |
| 136 | |
| 137 SwReporterTestingDelegate* g_testing_delegate_ = nullptr; | |
| 138 | |
| 139 const char kFoundUwsMetricName[] = "SoftwareReporter.FoundUwS"; | |
| 140 const char kFoundUwsReadErrorMetricName[] = | |
| 141 "SoftwareReporter.FoundUwSReadError"; | |
| 142 const char kScanTimesMetricName[] = "SoftwareReporter.UwSScanTimes"; | |
| 143 const char kMemoryUsedMetricName[] = "SoftwareReporter.MemoryUsed"; | |
| 144 const char kStepMetricName[] = "SoftwareReporter.Step"; | |
| 145 const char kLogsUploadEnabledMetricName[] = | |
| 146 "SoftwareReporter.LogsUploadEnabled"; | |
| 147 const char kLogsUploadResultMetricName[] = "SoftwareReporter.LogsUploadResult"; | |
| 148 const char kLogsUploadResultRegistryErrorMetricName[] = | |
| 149 "SoftwareReporter.LogsUploadResultRegistryError"; | |
| 150 const char kExitCodeMetricName[] = "SoftwareReporter.ExitCodeFromRegistry"; | |
| 151 const char kEngineErrorCodeMetricName[] = "SoftwareReporter.EngineErrorCode"; | |
| 152 | |
| 153 // The max value for histogram SoftwareReporter.LogsUploadResult, which is used | |
| 154 // to send UMA information about the result of Software Reporter's attempt to | |
| 155 // upload logs, when logs are enabled. This value must be consistent with the | |
| 156 // SoftwareReporterLogsUploadResult enum defined in the histograms.xml file. | |
| 157 const int kSwReporterLogsUploadResultMax = 30; | |
| 158 | |
| 159 // Reports metrics about the software reporter via UMA (and sometimes Rappor). | |
| 160 class UMAHistogramReporter { | |
| 161 public: | |
| 162 UMAHistogramReporter() : UMAHistogramReporter(std::string()) {} | |
| 163 | |
| 164 explicit UMAHistogramReporter(const std::string& suffix) | |
| 165 : suffix_(suffix), | |
| 166 registry_key_(suffix.empty() | |
| 167 ? chrome_cleaner::kSoftwareRemovalToolRegistryKey | |
| 168 : base::StringPrintf( | |
| 169 L"%ls\\%ls", | |
| 170 chrome_cleaner::kSoftwareRemovalToolRegistryKey, | |
| 171 base::UTF8ToUTF16(suffix).c_str())) {} | |
| 172 | |
| 173 // Reports the software reporter tool's version via UMA. | |
| 174 void ReportVersion(const base::Version& version) const { | |
| 175 DCHECK(!version.components().empty()); | |
| 176 // The minor version is the 2nd last component of the version, | |
| 177 // or just the first component if there is only 1. | |
| 178 uint32_t minor_version = 0; | |
| 179 if (version.components().size() > 1) | |
| 180 minor_version = version.components()[version.components().size() - 2]; | |
| 181 else | |
| 182 minor_version = version.components()[0]; | |
| 183 RecordSparseHistogram("SoftwareReporter.MinorVersion", minor_version); | |
| 184 | |
| 185 // The major version for X.Y.Z is X*256^3+Y*256+Z. If there are additional | |
| 186 // components, only the first three count, and if there are less than 3, the | |
| 187 // missing values are just replaced by zero. So 1 is equivalent 1.0.0. | |
| 188 DCHECK_LT(version.components()[0], 0x100U); | |
| 189 uint32_t major_version = 0x1000000 * version.components()[0]; | |
| 190 if (version.components().size() >= 2) { | |
| 191 DCHECK_LT(version.components()[1], 0x10000U); | |
| 192 major_version += 0x100 * version.components()[1]; | |
| 193 } | |
| 194 if (version.components().size() >= 3) { | |
| 195 DCHECK_LT(version.components()[2], 0x100U); | |
| 196 major_version += version.components()[2]; | |
| 197 } | |
| 198 RecordSparseHistogram("SoftwareReporter.MajorVersion", major_version); | |
| 199 } | |
| 200 | |
| 201 void ReportExitCode(int exit_code) const { | |
| 202 RecordSparseHistogram("SoftwareReporter.ExitCode", exit_code); | |
| 203 | |
| 204 // Also report the exit code that the reporter writes to the registry. | |
| 205 base::win::RegKey reporter_key; | |
| 206 DWORD exit_code_in_registry; | |
| 207 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 208 KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS || | |
| 209 reporter_key.ReadValueDW(chrome_cleaner::kExitCodeValueName, | |
| 210 &exit_code_in_registry) != ERROR_SUCCESS) { | |
| 211 return; | |
| 212 } | |
| 213 | |
| 214 RecordSparseHistogram(kExitCodeMetricName, exit_code_in_registry); | |
| 215 reporter_key.DeleteValue(chrome_cleaner::kExitCodeValueName); | |
| 216 } | |
| 217 | |
| 218 void ReportEngineErrorCode() const { | |
| 219 base::win::RegKey reporter_key; | |
| 220 DWORD engine_error_code; | |
| 221 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 222 KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS || | |
| 223 reporter_key.ReadValueDW(chrome_cleaner::kEngineErrorCodeValueName, | |
| 224 &engine_error_code) != ERROR_SUCCESS) { | |
| 225 return; | |
| 226 } | |
| 227 | |
| 228 RecordSparseHistogram(kEngineErrorCodeMetricName, engine_error_code); | |
| 229 reporter_key.DeleteValue(chrome_cleaner::kEngineErrorCodeValueName); | |
| 230 } | |
| 231 | |
| 232 // Reports UwS found by the software reporter tool via UMA and RAPPOR. | |
| 233 void ReportFoundUwS() const { | |
| 234 base::win::RegKey reporter_key; | |
| 235 std::vector<base::string16> found_uws_strings; | |
| 236 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 237 KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS || | |
| 238 reporter_key.ReadValues(chrome_cleaner::kFoundUwsValueName, | |
| 239 &found_uws_strings) != ERROR_SUCCESS) { | |
| 240 return; | |
| 241 } | |
| 242 | |
| 243 bool parse_error = false; | |
| 244 for (const base::string16& uws_string : found_uws_strings) { | |
| 245 // All UwS ids are expected to be integers. | |
| 246 uint32_t uws_id = 0; | |
| 247 if (base::StringToUint(uws_string, &uws_id)) { | |
| 248 RecordSparseHistogram(kFoundUwsMetricName, uws_id); | |
| 249 } else { | |
| 250 parse_error = true; | |
| 251 } | |
| 252 } | |
| 253 | |
| 254 // Clean up the old value. | |
| 255 reporter_key.DeleteValue(chrome_cleaner::kFoundUwsValueName); | |
| 256 RecordBooleanHistogram(kFoundUwsReadErrorMetricName, parse_error); | |
| 257 } | |
| 258 | |
| 259 // Reports to UMA the memory usage of the software reporter tool as reported | |
| 260 // by the tool itself in the Windows registry. | |
| 261 void ReportMemoryUsage() const { | |
| 262 base::win::RegKey reporter_key; | |
| 263 DWORD memory_used = 0; | |
| 264 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 265 KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS || | |
| 266 reporter_key.ReadValueDW(chrome_cleaner::kMemoryUsedValueName, | |
| 267 &memory_used) != ERROR_SUCCESS) { | |
| 268 return; | |
| 269 } | |
| 270 RecordMemoryKBHistogram(kMemoryUsedMetricName, memory_used); | |
| 271 reporter_key.DeleteValue(chrome_cleaner::kMemoryUsedValueName); | |
| 272 } | |
| 273 | |
| 274 // Reports the SwReporter run time with UMA both as reported by the tool via | |
| 275 // the registry and as measured by |ReporterRunner|. | |
| 276 void ReportRuntime(const base::TimeDelta& reporter_running_time) const { | |
| 277 RecordLongTimesHistogram("SoftwareReporter.RunningTimeAccordingToChrome", | |
| 278 reporter_running_time); | |
| 279 | |
| 280 // TODO(b/641081): This should only have KEY_QUERY_VALUE and KEY_SET_VALUE. | |
| 281 base::win::RegKey reporter_key; | |
| 282 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 283 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | |
| 284 RecordEnumerationHistogram( | |
| 285 kRunningTimeErrorMetricName, | |
| 286 REPORTER_RUNNING_TIME_ERROR_REGISTRY_KEY_INVALID, | |
| 287 REPORTER_RUNNING_TIME_ERROR_MAX); | |
| 288 return; | |
| 289 } | |
| 290 | |
| 291 bool has_start_time = false; | |
| 292 int64_t start_time_value = 0; | |
| 293 if (reporter_key.HasValue(chrome_cleaner::kStartTimeValueName) && | |
| 294 reporter_key.ReadInt64(chrome_cleaner::kStartTimeValueName, | |
| 295 &start_time_value) == ERROR_SUCCESS) { | |
| 296 has_start_time = true; | |
| 297 reporter_key.DeleteValue(chrome_cleaner::kStartTimeValueName); | |
| 298 } | |
| 299 | |
| 300 bool has_end_time = false; | |
| 301 int64_t end_time_value = 0; | |
| 302 if (reporter_key.HasValue(chrome_cleaner::kEndTimeValueName) && | |
| 303 reporter_key.ReadInt64(chrome_cleaner::kEndTimeValueName, | |
| 304 &end_time_value) == ERROR_SUCCESS) { | |
| 305 has_end_time = true; | |
| 306 reporter_key.DeleteValue(chrome_cleaner::kEndTimeValueName); | |
| 307 } | |
| 308 | |
| 309 if (has_start_time && has_end_time) { | |
| 310 base::TimeDelta registry_run_time = | |
| 311 base::Time::FromInternalValue(end_time_value) - | |
| 312 base::Time::FromInternalValue(start_time_value); | |
| 313 RecordLongTimesHistogram("SoftwareReporter.RunningTime", | |
| 314 registry_run_time); | |
| 315 RecordEnumerationHistogram(kRunningTimeErrorMetricName, | |
| 316 REPORTER_RUNNING_TIME_ERROR_NO_ERROR, | |
| 317 REPORTER_RUNNING_TIME_ERROR_MAX); | |
| 318 } else if (!has_start_time && !has_end_time) { | |
| 319 RecordEnumerationHistogram(kRunningTimeErrorMetricName, | |
| 320 REPORTER_RUNNING_TIME_ERROR_MISSING_BOTH_TIMES, | |
| 321 REPORTER_RUNNING_TIME_ERROR_MAX); | |
| 322 } else if (!has_start_time) { | |
| 323 RecordEnumerationHistogram(kRunningTimeErrorMetricName, | |
| 324 REPORTER_RUNNING_TIME_ERROR_MISSING_START_TIME, | |
| 325 REPORTER_RUNNING_TIME_ERROR_MAX); | |
| 326 } else { | |
| 327 DCHECK(!has_end_time); | |
| 328 RecordEnumerationHistogram(kRunningTimeErrorMetricName, | |
| 329 REPORTER_RUNNING_TIME_ERROR_MISSING_END_TIME, | |
| 330 REPORTER_RUNNING_TIME_ERROR_MAX); | |
| 331 } | |
| 332 } | |
| 333 | |
| 334 // Reports the UwS scan times of the software reporter tool via UMA. | |
| 335 void ReportScanTimes() const { | |
| 336 base::string16 scan_times_key_path = base::StringPrintf( | |
| 337 L"%ls\\%ls", registry_key_.c_str(), chrome_cleaner::kScanTimesSubKey); | |
| 338 // TODO(b/641081): This should only have KEY_QUERY_VALUE and KEY_SET_VALUE. | |
| 339 base::win::RegKey scan_times_key; | |
| 340 if (scan_times_key.Open(HKEY_CURRENT_USER, scan_times_key_path.c_str(), | |
| 341 KEY_ALL_ACCESS) != ERROR_SUCCESS) { | |
| 342 return; | |
| 343 } | |
| 344 | |
| 345 base::string16 value_name; | |
| 346 int uws_id = 0; | |
| 347 int64_t raw_scan_time = 0; | |
| 348 int num_scan_times = scan_times_key.GetValueCount(); | |
| 349 for (int i = 0; i < num_scan_times; ++i) { | |
| 350 if (scan_times_key.GetValueNameAt(i, &value_name) == ERROR_SUCCESS && | |
| 351 base::StringToInt(value_name, &uws_id) && | |
| 352 scan_times_key.ReadInt64(value_name.c_str(), &raw_scan_time) == | |
| 353 ERROR_SUCCESS) { | |
| 354 base::TimeDelta scan_time = | |
| 355 base::TimeDelta::FromInternalValue(raw_scan_time); | |
| 356 // We report the number of seconds plus one because it can take less | |
| 357 // than one second to scan some UwS and the count passed to |AddCount| | |
| 358 // must be at least one. | |
| 359 RecordSparseHistogramCount(kScanTimesMetricName, uws_id, | |
| 360 scan_time.InSeconds() + 1); | |
| 361 } | |
| 362 } | |
| 363 // Clean up by deleting the scan times key, which is a subkey of the main | |
| 364 // reporter key. | |
| 365 scan_times_key.Close(); | |
| 366 base::win::RegKey reporter_key; | |
| 367 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 368 KEY_ENUMERATE_SUB_KEYS) == ERROR_SUCCESS) { | |
| 369 reporter_key.DeleteKey(chrome_cleaner::kScanTimesSubKey); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 void RecordReporterStep(SwReporterUmaValue value) { | |
| 374 RecordEnumerationHistogram(kStepMetricName, value, SW_REPORTER_MAX); | |
| 375 } | |
| 376 | |
| 377 void RecordLogsUploadEnabled(SwReporterLogsUploadsEnabled value) { | |
| 378 RecordEnumerationHistogram(kLogsUploadEnabledMetricName, value, | |
| 379 REPORTER_LOGS_UPLOADS_MAX); | |
| 380 } | |
| 381 | |
| 382 void RecordLogsUploadResult() { | |
| 383 base::win::RegKey reporter_key; | |
| 384 DWORD logs_upload_result = 0; | |
| 385 if (reporter_key.Open(HKEY_CURRENT_USER, registry_key_.c_str(), | |
| 386 KEY_QUERY_VALUE | KEY_SET_VALUE) != ERROR_SUCCESS) { | |
| 387 RecordEnumerationHistogram( | |
| 388 kLogsUploadResultRegistryErrorMetricName, | |
| 389 REPORTER_LOGS_UPLOAD_RESULT_ERROR_REGISTRY_KEY_INVALID, | |
| 390 REPORTER_LOGS_UPLOAD_RESULT_ERROR_MAX); | |
| 391 return; | |
| 392 } | |
| 393 | |
| 394 if (reporter_key.ReadValueDW(chrome_cleaner::kLogsUploadResultValueName, | |
| 395 &logs_upload_result) != ERROR_SUCCESS) { | |
| 396 RecordEnumerationHistogram( | |
| 397 kLogsUploadResultRegistryErrorMetricName, | |
| 398 REPORTER_LOGS_UPLOAD_RESULT_ERROR_VALUE_NOT_FOUND, | |
| 399 REPORTER_LOGS_UPLOAD_RESULT_ERROR_MAX); | |
| 400 return; | |
| 401 } | |
| 402 | |
| 403 if (logs_upload_result >= kSwReporterLogsUploadResultMax) { | |
| 404 RecordEnumerationHistogram( | |
| 405 kLogsUploadResultRegistryErrorMetricName, | |
| 406 REPORTER_LOGS_UPLOAD_RESULT_ERROR_VALUE_OUT_OF_BOUNDS, | |
| 407 REPORTER_LOGS_UPLOAD_RESULT_ERROR_MAX); | |
| 408 return; | |
| 409 } | |
| 410 | |
| 411 RecordEnumerationHistogram(kLogsUploadResultMetricName, | |
| 412 static_cast<Sample>(logs_upload_result), | |
| 413 kSwReporterLogsUploadResultMax); | |
| 414 reporter_key.DeleteValue(chrome_cleaner::kLogsUploadResultValueName); | |
| 415 RecordEnumerationHistogram(kLogsUploadResultRegistryErrorMetricName, | |
| 416 REPORTER_LOGS_UPLOAD_RESULT_ERROR_NO_ERROR, | |
| 417 REPORTER_LOGS_UPLOAD_RESULT_ERROR_MAX); | |
| 418 } | |
| 419 | |
| 420 private: | |
| 421 using Sample = base::HistogramBase::Sample; | |
| 422 | |
| 423 static constexpr base::HistogramBase::Flags kUmaHistogramFlag = | |
| 424 base::HistogramBase::kUmaTargetedHistogramFlag; | |
| 425 | |
| 426 // Helper functions to record histograms with an optional suffix added to the | |
| 427 // histogram name. The UMA_HISTOGRAM macros can't be used because they | |
| 428 // require a constant string. | |
| 429 | |
| 430 std::string FullName(const std::string& name) const { | |
| 431 if (suffix_.empty()) | |
| 432 return name; | |
| 433 return base::StringPrintf("%s_%s", name.c_str(), suffix_.c_str()); | |
| 434 } | |
| 435 | |
| 436 void RecordBooleanHistogram(const std::string& name, bool sample) const { | |
| 437 auto* histogram = | |
| 438 base::BooleanHistogram::FactoryGet(FullName(name), kUmaHistogramFlag); | |
| 439 if (histogram) | |
| 440 histogram->AddBoolean(sample); | |
| 441 } | |
| 442 | |
| 443 void RecordEnumerationHistogram(const std::string& name, | |
| 444 Sample sample, | |
| 445 Sample boundary) const { | |
| 446 // See HISTOGRAM_ENUMERATION_WITH_FLAG for the parameters to |FactoryGet|. | |
| 447 auto* histogram = base::LinearHistogram::FactoryGet( | |
| 448 FullName(name), 1, boundary, boundary + 1, kUmaHistogramFlag); | |
| 449 if (histogram) | |
| 450 histogram->Add(sample); | |
| 451 } | |
| 452 | |
| 453 void RecordLongTimesHistogram(const std::string& name, | |
| 454 const base::TimeDelta& sample) const { | |
| 455 // See UMA_HISTOGRAM_LONG_TIMES for the parameters to |FactoryTimeGet|. | |
| 456 auto* histogram = base::Histogram::FactoryTimeGet( | |
| 457 FullName(name), base::TimeDelta::FromMilliseconds(1), | |
| 458 base::TimeDelta::FromHours(1), 100, kUmaHistogramFlag); | |
| 459 if (histogram) | |
| 460 histogram->AddTime(sample); | |
| 461 } | |
| 462 | |
| 463 void RecordMemoryKBHistogram(const std::string& name, Sample sample) const { | |
| 464 // See UMA_HISTOGRAM_MEMORY_KB for the parameters to |FactoryGet|. | |
| 465 auto* histogram = base::Histogram::FactoryGet(FullName(name), 1000, 500000, | |
| 466 50, kUmaHistogramFlag); | |
| 467 if (histogram) | |
| 468 histogram->Add(sample); | |
| 469 } | |
| 470 | |
| 471 void RecordSparseHistogram(const std::string& name, Sample sample) const { | |
| 472 auto* histogram = | |
| 473 base::SparseHistogram::FactoryGet(FullName(name), kUmaHistogramFlag); | |
| 474 if (histogram) | |
| 475 histogram->Add(sample); | |
| 476 } | |
| 477 | |
| 478 void RecordSparseHistogramCount(const std::string& name, | |
| 479 Sample sample, | |
| 480 int count) const { | |
| 481 auto* histogram = | |
| 482 base::SparseHistogram::FactoryGet(FullName(name), kUmaHistogramFlag); | |
| 483 if (histogram) | |
| 484 histogram->AddCount(sample, count); | |
| 485 } | |
| 486 | |
| 487 const std::string suffix_; | |
| 488 const std::wstring registry_key_; | |
| 489 }; | |
| 490 | |
| 491 // Records the reporter step without a suffix. (For steps that are never run by | |
| 492 // the experimental reporter.) | |
| 493 void RecordReporterStepHistogram(SwReporterUmaValue value) { | |
| 494 UMAHistogramReporter uma; | |
| 495 uma.RecordReporterStep(value); | |
| 496 } | |
| 497 | |
| 498 void DisplaySRTPrompt(const base::FilePath& download_path) { | |
| 499 // Find the last active browser, which may be NULL, in which case we won't | |
| 500 // show the prompt this time and will wait until the next run of the | |
| 501 // reporter. We can't use other ways of finding a browser because we don't | |
| 502 // have a profile. | |
| 503 Browser* browser = chrome::FindLastActive(); | |
| 504 if (!browser) | |
| 505 return; | |
| 506 | |
| 507 Profile* profile = browser->profile(); | |
| 508 DCHECK(profile); | |
| 509 | |
| 510 // Make sure we have a tabbed browser since we need to anchor the bubble to | |
| 511 // the toolbar's wrench menu. Create one if none exist already. | |
| 512 if (browser->type() != Browser::TYPE_TABBED) { | |
| 513 browser = chrome::FindTabbedBrowser(profile, false); | |
| 514 if (!browser) | |
| 515 browser = new Browser(Browser::CreateParams(profile, false)); | |
| 516 } | |
| 517 GlobalErrorService* global_error_service = | |
| 518 GlobalErrorServiceFactory::GetForProfile(profile); | |
| 519 SRTGlobalError* global_error = | |
| 520 new SRTGlobalError(global_error_service, download_path); | |
| 521 | |
| 522 // Ownership of |global_error| is passed to the service. The error removes | |
| 523 // itself from the service and self-destructs when done. | |
| 524 global_error_service->AddGlobalError(base::WrapUnique(global_error)); | |
| 525 | |
| 526 bool show_bubble = true; | |
| 527 PrefService* local_state = g_browser_process->local_state(); | |
| 528 if (local_state && local_state->GetBoolean(prefs::kSwReporterPendingPrompt)) { | |
| 529 // Don't show the bubble if there's already a pending prompt to only be | |
| 530 // sown in the Chrome menu. | |
| 531 RecordReporterStepHistogram(SW_REPORTER_ADDED_TO_MENU); | |
| 532 show_bubble = false; | |
| 533 } else { | |
| 534 // Do not try to show bubble if another GlobalError is already showing | |
| 535 // one. The bubble will be shown once the others have been dismissed. | |
| 536 for (GlobalError* error : global_error_service->errors()) { | |
| 537 if (error->GetBubbleView()) { | |
| 538 show_bubble = false; | |
| 539 break; | |
| 540 } | |
| 541 } | |
| 542 } | |
| 543 if (show_bubble) | |
| 544 global_error->ShowBubbleView(browser); | |
| 545 } | |
| 546 | |
| 547 // Handles the case when the remote end has been closed, by performing the | |
| 548 // necessary cleanups if the prompt dialog is being shown to the user. | |
| 549 void OnConnectionClosed() { | |
| 550 // Placeholder. This should handle cases when the reporter process is | |
| 551 // disconnected (e.g. due to a crash) and the prompt dialog is being shown | |
| 552 // to the user. | |
| 553 } | |
| 554 | |
| 555 // Handles the case when a mojo::ReportBadMessage has been explicitly reported. | |
| 556 void OnConnectionError(const std::string& message) { | |
| 557 // Placeholder. This should handle cases when the reporter process sends | |
| 558 // a bad message and the prompt dialog is being shown to the user. | |
| 559 } | |
| 560 | |
| 561 // Class responsible for launching the reporter process and waiting for its | |
| 562 // completion. If feature InBrowserCleanerUI is enabled, this object will also | |
| 563 // be responsible for starting the ChromePromptImpl object on the IO thread and | |
| 564 // controlling its lifetime. | |
| 565 // | |
| 566 // Expected lifecycle of a SwReporterProcess: | |
| 567 // - created on the UI thread before the reporter process launch is posted | |
| 568 // (method ScheduleNextInvocation); | |
| 569 // - deleted on the UI thread once ReporterDone() finishes (the method is | |
| 570 // called after the reporter process exits). | |
| 571 // | |
| 572 // If feature InBrowserCleanerUI feature is enabled, the following tasks will | |
| 573 // be posted in sequence to the IO Thread and will retain the SwReporterProcess | |
| 574 // object: | |
| 575 // - creation of a ChromePromptImpl object right after the reporter process is | |
| 576 // launched (that object will be responsible for handling IPC requests from | |
| 577 // the reporter process); | |
| 578 // - deletion of the ChromePromptImpl object on ReporterDone(). | |
| 579 // As a consequence, the SwReporterProcess object can outlive ReporterDone() | |
| 580 // and will only be deleted after the ChromePromptImpl object is released on | |
| 581 // the IO thread. | |
| 582 class SwReporterProcess : public base::RefCountedThreadSafe<SwReporterProcess> { | |
| 583 public: | |
| 584 explicit SwReporterProcess(const SwReporterInvocation& invocation) | |
| 585 : invocation_(invocation) {} | |
| 586 | |
| 587 // This function is called from a worker thread to launch the SwReporter and | |
| 588 // wait for termination to collect its exit code. This task could be | |
| 589 // interrupted by a shutdown at any time, so it shouldn't depend on anything | |
| 590 // external that could be shut down beforehand. | |
| 591 int LaunchAndWaitForExitOnBackgroundThread(); | |
| 592 | |
| 593 // Schedules to release the instance of ChromePromptImpl on the IO thread. | |
| 594 void OnReporterDone(); | |
| 595 | |
| 596 const SwReporterInvocation& invocation() const { return invocation_; } | |
| 597 | |
| 598 private: | |
| 599 friend class base::RefCountedThreadSafe<SwReporterProcess>; | |
| 600 ~SwReporterProcess() = default; | |
| 601 | |
| 602 // Starts a new IPC service implementing the ChromePrompt interface and | |
| 603 // launches a new reporter process that can connect to the IPC. | |
| 604 base::Process LaunchConnectedReporterProcess(); | |
| 605 | |
| 606 // Starts a new instance of ChromePromptImpl to receive requests from the | |
| 607 // reporter and establishes the mojo connection to it. | |
| 608 // Must be run on the IO thread. | |
| 609 void CreateChromePromptImpl( | |
| 610 chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request); | |
| 611 | |
| 612 // Releases the instance of ChromePromptImpl. Must be run on the IO thread. | |
| 613 void ReleaseChromePromptImpl(); | |
| 614 | |
| 615 // Launches a new process with the command line in the invocation and | |
| 616 // provided launch options. Uses g_testing_delegate_ if not null. | |
| 617 base::Process LaunchReporterProcess( | |
| 618 const SwReporterInvocation& invocation, | |
| 619 const base::LaunchOptions& launch_options); | |
| 620 | |
| 621 // The invocation for the current reporter process. | |
| 622 SwReporterInvocation invocation_; | |
| 623 | |
| 624 // Implementation of the ChromePrompt service to be used by the current | |
| 625 // reporter process. Can only be accessed on the IO thread. | |
| 626 std::unique_ptr<ChromePromptImpl> chrome_prompt_impl_; | |
| 627 }; | |
| 628 | |
| 629 int SwReporterProcess::LaunchAndWaitForExitOnBackgroundThread() { | |
| 630 base::Process reporter_process = | |
| 631 base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature) | |
| 632 ? LaunchConnectedReporterProcess() | |
| 633 : LaunchReporterProcess(invocation_, base::LaunchOptions()); | |
| 634 | |
| 635 // This exit code is used to identify that a reporter run didn't happen, so | |
| 636 // the result should be ignored and a rerun scheduled for the usual delay. | |
| 637 int exit_code = kReporterNotLaunchedExitCode; | |
| 638 UMAHistogramReporter uma(invocation_.suffix); | |
| 639 if (reporter_process.IsValid()) { | |
| 640 uma.RecordReporterStep(SW_REPORTER_START_EXECUTION); | |
| 641 bool success = reporter_process.WaitForExit(&exit_code); | |
| 642 DCHECK(success); | |
| 643 } else { | |
| 644 uma.RecordReporterStep(SW_REPORTER_FAILED_TO_START); | |
| 645 } | |
| 646 return exit_code; | |
| 647 } | |
| 648 | |
| 649 void SwReporterProcess::OnReporterDone() { | |
| 650 if (base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature)) { | |
| 651 BrowserThread::GetTaskRunnerForThread(BrowserThread::IO) | |
| 652 ->PostTask(FROM_HERE, | |
| 653 base::Bind(&SwReporterProcess::ReleaseChromePromptImpl, | |
| 654 base::RetainedRef(this))); | |
| 655 } | |
| 656 } | |
| 657 | |
| 658 base::Process SwReporterProcess::LaunchConnectedReporterProcess() { | |
| 659 DCHECK(base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature)); | |
| 660 | |
| 661 mojo::edk::PendingProcessConnection pending_process_connection; | |
| 662 std::string mojo_pipe_token; | |
| 663 mojo::ScopedMessagePipeHandle mojo_pipe = | |
| 664 pending_process_connection.CreateMessagePipe(&mojo_pipe_token); | |
| 665 invocation_.command_line.AppendSwitchASCII( | |
| 666 chrome_cleaner::kChromeMojoPipeTokenSwitch, mojo_pipe_token); | |
| 667 invocation_.command_line.AppendSwitchASCII( | |
| 668 chrome_cleaner::kExecutionModeSwitch, | |
| 669 base::IntToString( | |
| 670 static_cast<int>(chrome_cleaner::ExecutionMode::kScanning))); | |
| 671 | |
| 672 mojo::edk::PlatformChannelPair channel; | |
| 673 base::HandlesToInheritVector handles_to_inherit; | |
| 674 channel.PrepareToPassClientHandleToChildProcess(&invocation_.command_line, | |
| 675 &handles_to_inherit); | |
| 676 | |
| 677 base::LaunchOptions launch_options; | |
| 678 launch_options.handles_to_inherit = &handles_to_inherit; | |
| 679 base::Process reporter_process = | |
| 680 LaunchReporterProcess(invocation_, launch_options); | |
| 681 | |
| 682 if (!reporter_process.IsValid()) | |
| 683 return reporter_process; | |
| 684 | |
| 685 chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request; | |
| 686 chrome_prompt_request.Bind(std::move(mojo_pipe)); | |
| 687 | |
| 688 // ChromePromptImpl tasks will need to run on the IO thread. There is no | |
| 689 // need to synchronize its creation, since the client end will wait for this | |
| 690 // initialization to be done before sending requests. | |
| 691 BrowserThread::GetTaskRunnerForThread(BrowserThread::IO) | |
| 692 ->PostTask(FROM_HERE, | |
| 693 base::BindOnce(&SwReporterProcess::CreateChromePromptImpl, | |
| 694 base::RetainedRef(this), | |
| 695 std::move(chrome_prompt_request))); | |
| 696 | |
| 697 mojo::edk::ProcessErrorCallback on_connection_error = | |
| 698 g_testing_delegate_ | |
| 699 ? base::Bind(&SwReporterTestingDelegate::OnConnectionError, | |
| 700 base::Unretained(g_testing_delegate_)) | |
| 701 : base::Bind(&OnConnectionError); | |
| 702 pending_process_connection.Connect( | |
| 703 reporter_process.Handle(), | |
| 704 mojo::edk::ConnectionParams(channel.PassServerHandle()), | |
| 705 on_connection_error); | |
| 706 | |
| 707 return reporter_process; | |
| 708 } | |
| 709 | |
| 710 base::Process SwReporterProcess::LaunchReporterProcess( | |
| 711 const SwReporterInvocation& invocation, | |
| 712 const base::LaunchOptions& launch_options) { | |
| 713 return g_testing_delegate_ | |
| 714 ? g_testing_delegate_->LaunchReporter(invocation, launch_options) | |
| 715 : base::LaunchProcess(invocation.command_line, launch_options); | |
| 716 } | |
| 717 | |
| 718 void SwReporterProcess::CreateChromePromptImpl( | |
| 719 chrome_cleaner::mojom::ChromePromptRequest chrome_prompt_request) { | |
| 720 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 721 DCHECK(base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature)); | |
| 722 | |
| 723 chrome_prompt_impl_ = | |
| 724 g_testing_delegate_ | |
| 725 ? g_testing_delegate_->CreateChromePromptImpl( | |
| 726 std::move(chrome_prompt_request)) | |
| 727 : base::MakeUnique<ChromePromptImpl>(std::move(chrome_prompt_request), | |
| 728 base::Bind(&OnConnectionClosed)); | |
| 729 } | |
| 730 | |
| 731 void SwReporterProcess::ReleaseChromePromptImpl() { | |
| 732 DCHECK_CURRENTLY_ON(BrowserThread::IO); | |
| 733 DCHECK(base::FeatureList::IsEnabled(kInBrowserCleanerUIFeature)); | |
| 734 | |
| 735 chrome_prompt_impl_.release(); | |
| 736 } | |
| 737 | |
| 738 } // namespace | |
| 739 | |
| 740 void DisplaySRTPromptForTesting(const base::FilePath& download_path) { | |
| 741 DisplaySRTPrompt(download_path); | |
| 742 } | |
| 743 | |
| 744 // Class that will attempt to download the SRT, showing the SRT notification | |
| 745 // bubble when the download operation is complete. Instances of SRTFetcher own | |
| 746 // themselves, they will self-delete on completion of the network request when | |
| 747 // OnURLFetchComplete is called. | |
| 748 class SRTFetcher : public net::URLFetcherDelegate { | |
| 749 public: | |
| 750 explicit SRTFetcher(Profile* profile) | |
| 751 : profile_(profile), | |
| 752 url_fetcher_(net::URLFetcher::Create(0, | |
| 753 GURL(GetSRTDownloadURL()), | |
| 754 net::URLFetcher::GET, | |
| 755 this)) { | |
| 756 data_use_measurement::DataUseUserData::AttachToFetcher( | |
| 757 url_fetcher_.get(), | |
| 758 data_use_measurement::DataUseUserData::SAFE_BROWSING); | |
| 759 url_fetcher_->SetLoadFlags(net::LOAD_DISABLE_CACHE); | |
| 760 url_fetcher_->SetMaxRetriesOn5xx(3); | |
| 761 url_fetcher_->SaveResponseToTemporaryFile( | |
| 762 BrowserThread::GetTaskRunnerForThread(BrowserThread::FILE)); | |
| 763 url_fetcher_->SetRequestContext( | |
| 764 g_browser_process->system_request_context()); | |
| 765 // Adds the UMA bit to the download request if the user is enrolled in UMA. | |
| 766 ProfileIOData* io_data = ProfileIOData::FromResourceContext( | |
| 767 profile_->GetResourceContext()); | |
| 768 net::HttpRequestHeaders headers; | |
| 769 // Note: It's OK to pass |is_signed_in| false if it's unknown, as it does | |
| 770 // not affect transmission of experiments coming from the variations server. | |
| 771 bool is_signed_in = false; | |
| 772 variations::AppendVariationHeaders( | |
| 773 url_fetcher_->GetOriginalURL(), io_data->IsOffTheRecord(), | |
| 774 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled(), | |
| 775 is_signed_in, &headers); | |
| 776 url_fetcher_->SetExtraRequestHeaders(headers.ToString()); | |
| 777 url_fetcher_->Start(); | |
| 778 } | |
| 779 | |
| 780 // net::URLFetcherDelegate: | |
| 781 void OnURLFetchComplete(const net::URLFetcher* source) override { | |
| 782 // Take ownership of the fetcher in this scope (source == url_fetcher_). | |
| 783 DCHECK_EQ(url_fetcher_.get(), source); | |
| 784 | |
| 785 base::FilePath download_path; | |
| 786 if (source->GetStatus().is_success() && | |
| 787 source->GetResponseCode() == net::HTTP_OK) { | |
| 788 if (source->GetResponseAsFilePath(true, &download_path)) { | |
| 789 DCHECK(!download_path.empty()); | |
| 790 } | |
| 791 } | |
| 792 | |
| 793 // As long as the fetch didn't fail due to HTTP_NOT_FOUND, show a prompt | |
| 794 // (either offering the tool directly or pointing to the download page). | |
| 795 // If the fetch failed to find the file, don't prompt the user since the | |
| 796 // tool is not currently available. | |
| 797 // TODO(mad): Consider implementing another layer of retries / alternate | |
| 798 // fetching mechanisms. http://crbug.com/460293 | |
| 799 // TODO(mad): In the event the browser is closed before the prompt displays, | |
| 800 // we will wait until the next scanner run to re-display it. | |
| 801 // Improve this. http://crbug.com/460295 | |
| 802 if (source->GetResponseCode() != net::HTTP_NOT_FOUND) | |
| 803 DisplaySRTPrompt(download_path); | |
| 804 else | |
| 805 RecordSRTPromptHistogram(SRT_PROMPT_DOWNLOAD_UNAVAILABLE); | |
| 806 | |
| 807 // Explicitly destroy the url_fetcher_ to avoid destruction races. | |
| 808 url_fetcher_.reset(); | |
| 809 | |
| 810 // At this point, the url_fetcher_ is gone and this SRTFetcher instance is | |
| 811 // no longer needed. | |
| 812 delete this; | |
| 813 } | |
| 814 | |
| 815 private: | |
| 816 ~SRTFetcher() override {} | |
| 817 | |
| 818 // The user profile. | |
| 819 Profile* profile_; | |
| 820 | |
| 821 // The underlying URL fetcher. The instance is alive from construction through | |
| 822 // OnURLFetchComplete. | |
| 823 std::unique_ptr<net::URLFetcher> url_fetcher_; | |
| 824 | |
| 825 DISALLOW_COPY_AND_ASSIGN(SRTFetcher); | |
| 826 }; | |
| 827 | |
| 828 namespace { | |
| 829 | |
| 830 // Try to fetch the SRT, and on success, show the prompt to run it. | |
| 831 void MaybeFetchSRT(Browser* browser, const base::Version& reporter_version) { | |
| 832 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 833 | |
| 834 if (g_testing_delegate_) { | |
| 835 g_testing_delegate_->TriggerPrompt(browser, reporter_version.GetString()); | |
| 836 return; | |
| 837 } | |
| 838 Profile* profile = browser->profile(); | |
| 839 DCHECK(profile); | |
| 840 PrefService* prefs = profile->GetPrefs(); | |
| 841 DCHECK(prefs); | |
| 842 | |
| 843 // Don't show the prompt again if it's been shown before for this profile | |
| 844 // and for the current variations seed, unless there's a pending prompt to | |
| 845 // show in the Chrome menu. | |
| 846 std::string incoming_seed = GetIncomingSRTSeed(); | |
| 847 std::string old_seed = prefs->GetString(prefs::kSwReporterPromptSeed); | |
| 848 PrefService* local_state = g_browser_process->local_state(); | |
| 849 bool pending_prompt = | |
| 850 local_state && local_state->GetBoolean(prefs::kSwReporterPendingPrompt); | |
| 851 if (!incoming_seed.empty() && incoming_seed == old_seed && !pending_prompt) { | |
| 852 RecordReporterStepHistogram(SW_REPORTER_ALREADY_PROMPTED); | |
| 853 return; | |
| 854 } | |
| 855 | |
| 856 if (!incoming_seed.empty() && incoming_seed != old_seed) { | |
| 857 prefs->SetString(prefs::kSwReporterPromptSeed, incoming_seed); | |
| 858 // Forget about pending prompts if prompt seed has changed. | |
| 859 if (local_state) | |
| 860 local_state->SetBoolean(prefs::kSwReporterPendingPrompt, false); | |
| 861 } | |
| 862 prefs->SetString(prefs::kSwReporterPromptVersion, | |
| 863 reporter_version.GetString()); | |
| 864 | |
| 865 // Download the SRT. | |
| 866 RecordReporterStepHistogram(SW_REPORTER_DOWNLOAD_START); | |
| 867 | |
| 868 // All the work happens in the self-deleting class below. | |
| 869 new SRTFetcher(profile); | |
| 870 } | |
| 871 | |
| 872 base::Time Now() { | |
| 873 return g_testing_delegate_ ? g_testing_delegate_->Now() : base::Time::Now(); | |
| 874 } | |
| 875 | |
| 876 } // namespace | |
| 877 | |
| 878 // This class tries to run a queue of reporters and react to their exit codes. | |
| 879 // It schedules subsequent runs of the queue as needed, or retries as soon as a | |
| 880 // browser is available when none is on first try. | |
| 881 class ReporterRunner : public chrome::BrowserListObserver { | |
| 882 public: | |
| 883 // Registers |invocations| to run next time |TryToRun| is scheduled. (And if | |
| 884 // it's not already scheduled, call it now.) | |
| 885 static void ScheduleInvocations(const SwReporterQueue& invocations, | |
| 886 const base::Version& version) { | |
| 887 if (!instance_) { | |
| 888 instance_ = new ReporterRunner; | |
| 889 ANNOTATE_LEAKING_OBJECT_PTR(instance_); | |
| 890 } | |
| 891 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 892 | |
| 893 // There's nothing to do if the invocation parameters and version of the | |
| 894 // reporter have not changed, we just keep running the tasks that are | |
| 895 // running now. | |
| 896 if (instance_->pending_invocations_ == invocations && | |
| 897 instance_->version_.IsValid() && instance_->version_ == version) | |
| 898 return; | |
| 899 | |
| 900 instance_->pending_invocations_ = invocations; | |
| 901 instance_->version_ = version; | |
| 902 if (instance_->first_run_) { | |
| 903 instance_->first_run_ = false; | |
| 904 instance_->TryToRun(); | |
| 905 } | |
| 906 } | |
| 907 | |
| 908 private: | |
| 909 ReporterRunner() {} | |
| 910 ~ReporterRunner() override {} | |
| 911 | |
| 912 // BrowserListObserver. | |
| 913 void OnBrowserSetLastActive(Browser* browser) override {} | |
| 914 void OnBrowserRemoved(Browser* browser) override {} | |
| 915 void OnBrowserAdded(Browser* browser) override { | |
| 916 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 917 DCHECK(browser); | |
| 918 MaybeFetchSRT(browser, version_); | |
| 919 BrowserList::RemoveObserver(this); | |
| 920 } | |
| 921 | |
| 922 // Launches the command line at the head of the queue. | |
| 923 void ScheduleNextInvocation() { | |
| 924 DCHECK(!current_invocations_.empty()); | |
| 925 auto next_invocation = current_invocations_.front(); | |
| 926 current_invocations_.pop(); | |
| 927 | |
| 928 AppendInvocationSpecificSwitches(&next_invocation); | |
| 929 | |
| 930 base::TaskRunner* task_runner = | |
| 931 g_testing_delegate_ ? g_testing_delegate_->BlockingTaskRunner() | |
| 932 : blocking_task_runner_.get(); | |
| 933 auto sw_reporter_process = | |
| 934 make_scoped_refptr(new SwReporterProcess(next_invocation)); | |
| 935 auto launch_and_wait = | |
| 936 base::Bind(&SwReporterProcess::LaunchAndWaitForExitOnBackgroundThread, | |
| 937 sw_reporter_process); | |
| 938 auto reporter_done = | |
| 939 base::Bind(&ReporterRunner::ReporterDone, base::Unretained(this), Now(), | |
| 940 version_, std::move(sw_reporter_process)); | |
| 941 base::PostTaskAndReplyWithResult(task_runner, FROM_HERE, | |
| 942 std::move(launch_and_wait), | |
| 943 std::move(reporter_done)); | |
| 944 } | |
| 945 | |
| 946 // This method is called on the UI thread when an invocation of the reporter | |
| 947 // has completed. This is run as a task posted from an interruptible worker | |
| 948 // thread so should be resilient to unexpected shutdown. | |
| 949 void ReporterDone(const base::Time& reporter_start_time, | |
| 950 const base::Version& version, | |
| 951 scoped_refptr<SwReporterProcess> sw_reporter_process, | |
| 952 int exit_code) { | |
| 953 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 954 | |
| 955 sw_reporter_process->OnReporterDone(); | |
| 956 | |
| 957 base::Time now = Now(); | |
| 958 base::TimeDelta reporter_running_time = now - reporter_start_time; | |
| 959 | |
| 960 // Don't continue the current queue of reporters if one failed to launch. | |
| 961 if (exit_code == kReporterNotLaunchedExitCode) | |
| 962 current_invocations_ = SwReporterQueue(); | |
| 963 | |
| 964 // As soon as we're not running this queue, schedule the next overall queue | |
| 965 // run after the regular delay. (If there was a failure it's not worth | |
| 966 // retrying earlier, risking running too often if it always fails, since | |
| 967 // not many users fail here.) | |
| 968 if (current_invocations_.empty()) { | |
| 969 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 970 FROM_HERE, | |
| 971 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), | |
| 972 base::TimeDelta::FromDays(days_between_reporter_runs_)); | |
| 973 } else { | |
| 974 ScheduleNextInvocation(); | |
| 975 } | |
| 976 | |
| 977 // If the reporter failed to launch, do not process the results. (The exit | |
| 978 // code itself doesn't need to be logged in this case because | |
| 979 // SW_REPORTER_FAILED_TO_START is logged in | |
| 980 // |LaunchAndWaitForExitOnBackgroundThread|.) | |
| 981 if (exit_code == kReporterNotLaunchedExitCode) | |
| 982 return; | |
| 983 | |
| 984 const auto& finished_invocation = sw_reporter_process->invocation(); | |
| 985 UMAHistogramReporter uma(finished_invocation.suffix); | |
| 986 uma.ReportVersion(version); | |
| 987 uma.ReportExitCode(exit_code); | |
| 988 uma.ReportEngineErrorCode(); | |
| 989 uma.ReportFoundUwS(); | |
| 990 | |
| 991 PrefService* local_state = g_browser_process->local_state(); | |
| 992 if (local_state) { | |
| 993 if (finished_invocation.BehaviourIsSupported( | |
| 994 SwReporterInvocation::BEHAVIOUR_LOG_EXIT_CODE_TO_PREFS)) { | |
| 995 local_state->SetInteger(prefs::kSwReporterLastExitCode, exit_code); | |
| 996 } | |
| 997 local_state->SetInt64(prefs::kSwReporterLastTimeTriggered, | |
| 998 now.ToInternalValue()); | |
| 999 } | |
| 1000 uma.ReportRuntime(reporter_running_time); | |
| 1001 uma.ReportScanTimes(); | |
| 1002 uma.ReportMemoryUsage(); | |
| 1003 if (finished_invocation.logs_upload_enabled) | |
| 1004 uma.RecordLogsUploadResult(); | |
| 1005 | |
| 1006 if (!finished_invocation.BehaviourIsSupported( | |
| 1007 SwReporterInvocation::BEHAVIOUR_TRIGGER_PROMPT)) { | |
| 1008 return; | |
| 1009 } | |
| 1010 | |
| 1011 if (!IsInSRTPromptFieldTrialGroups()) { | |
| 1012 // Knowing about disabled field trial is more important than reporter not | |
| 1013 // finding anything to remove, so check this case first. | |
| 1014 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_FIELD_TRIAL); | |
| 1015 return; | |
| 1016 } | |
| 1017 | |
| 1018 if (exit_code != chrome_cleaner::kSwReporterPostRebootCleanupNeeded && | |
| 1019 exit_code != chrome_cleaner::kSwReporterCleanupNeeded) { | |
| 1020 RecordReporterStepHistogram(SW_REPORTER_NO_PROMPT_NEEDED); | |
| 1021 return; | |
| 1022 } | |
| 1023 | |
| 1024 // Find the last active browser, which may be NULL, in which case we need | |
| 1025 // to wait for one to be available. We can't use other ways of finding a | |
| 1026 // browser because we don't have a profile. And we need a browser to get to | |
| 1027 // a profile, which we need, to tell whether we should prompt or not. | |
| 1028 // TODO(mad): crbug.com/503269, investigate whether we should change how we | |
| 1029 // decide when it's time to download the SRT and when to display the prompt. | |
| 1030 Browser* browser = chrome::FindLastActive(); | |
| 1031 if (!browser) { | |
| 1032 RecordReporterStepHistogram(SW_REPORTER_NO_BROWSER); | |
| 1033 BrowserList::AddObserver(this); | |
| 1034 } else { | |
| 1035 MaybeFetchSRT(browser, version_); | |
| 1036 } | |
| 1037 } | |
| 1038 | |
| 1039 void TryToRun() { | |
| 1040 DCHECK_CURRENTLY_ON(BrowserThread::UI); | |
| 1041 PrefService* local_state = g_browser_process->local_state(); | |
| 1042 | |
| 1043 if (!version_.IsValid() || !local_state) { | |
| 1044 // TODO(b/641081): This doesn't look right. Even on first run, |version_| | |
| 1045 // should be valid (and this is already checked in RunSwReporters). We | |
| 1046 // should abort if local_state is missing, but this has nothing to do | |
| 1047 // with |first_run_|. | |
| 1048 DCHECK(first_run_); | |
| 1049 return; | |
| 1050 } | |
| 1051 | |
| 1052 // Run a queue of reporters if none have been triggered in the last | |
| 1053 // |days_between_reporter_runs_| days, which depends if there is a pending | |
| 1054 // prompt to be added to Chrome's menu. | |
| 1055 if (local_state->GetBoolean(prefs::kSwReporterPendingPrompt)) { | |
| 1056 days_between_reporter_runs_ = kDaysBetweenSwReporterRunsForPendingPrompt; | |
| 1057 RecordReporterStepHistogram(SW_REPORTER_RAN_DAILY); | |
| 1058 } else { | |
| 1059 days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns; | |
| 1060 } | |
| 1061 const base::Time now = Now(); | |
| 1062 const base::Time last_time_triggered = base::Time::FromInternalValue( | |
| 1063 local_state->GetInt64(prefs::kSwReporterLastTimeTriggered)); | |
| 1064 const base::Time next_trigger( | |
| 1065 last_time_triggered + | |
| 1066 base::TimeDelta::FromDays(days_between_reporter_runs_)); | |
| 1067 if (!pending_invocations_.empty() && | |
| 1068 (next_trigger <= now || | |
| 1069 // Also make sure the kSwReporterLastTimeTriggered value is not set in | |
| 1070 // the future. | |
| 1071 last_time_triggered > now)) { | |
| 1072 const base::Time last_time_sent_logs = base::Time::FromInternalValue( | |
| 1073 local_state->GetInt64(prefs::kSwReporterLastTimeSentReport)); | |
| 1074 const base::Time next_time_send_logs = | |
| 1075 last_time_sent_logs + | |
| 1076 base::TimeDelta::FromDays(kDaysBetweenReporterLogsSent); | |
| 1077 // Send the logs for this whole queue of invocations if the last send is | |
| 1078 // in the future or if logs have been sent at least | |
| 1079 // |kSwReporterLastTimeSentReport| days ago. The former is intended as a | |
| 1080 // measure for failure recovery, in case the time in local state is | |
| 1081 // incorrectly set to the future. | |
| 1082 in_logs_upload_period_ = | |
| 1083 last_time_sent_logs > now || next_time_send_logs <= now; | |
| 1084 | |
| 1085 DCHECK(current_invocations_.empty()); | |
| 1086 current_invocations_ = pending_invocations_; | |
| 1087 ScheduleNextInvocation(); | |
| 1088 } else { | |
| 1089 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( | |
| 1090 FROM_HERE, | |
| 1091 base::Bind(&ReporterRunner::TryToRun, base::Unretained(this)), | |
| 1092 next_trigger - now); | |
| 1093 } | |
| 1094 } | |
| 1095 | |
| 1096 // Returns true if the experiment to send reporter logs is enabled, the user | |
| 1097 // opted into Safe Browsing extended reporting, and this queue of invocations | |
| 1098 // started during the logs upload interval. | |
| 1099 bool ShouldSendReporterLogs(const std::string& suffix, | |
| 1100 const PrefService& local_state) { | |
| 1101 UMAHistogramReporter uma(suffix); | |
| 1102 if (!SafeBrowsingExtendedReportingEnabled()) { | |
| 1103 uma.RecordLogsUploadEnabled(REPORTER_LOGS_UPLOADS_SBER_DISABLED); | |
| 1104 return false; | |
| 1105 } | |
| 1106 if (!in_logs_upload_period_) { | |
| 1107 uma.RecordLogsUploadEnabled(REPORTER_LOGS_UPLOADS_RECENTLY_SENT_LOGS); | |
| 1108 return false; | |
| 1109 } | |
| 1110 uma.RecordLogsUploadEnabled(REPORTER_LOGS_UPLOADS_ENABLED); | |
| 1111 return true; | |
| 1112 } | |
| 1113 | |
| 1114 // Appends switches to the next invocation that depend on the user current | |
| 1115 // state with respect to opting into extended Safe Browsing reporting and | |
| 1116 // metrics and crash reporting. The invocation object is changed locally right | |
| 1117 // before the actual process is launched because user status can change | |
| 1118 // between this and the next run for this ReporterRunner object. For example, | |
| 1119 // the ReporterDone() callback schedules the next run for a few days later, | |
| 1120 // and the user might have changed settings in the meantime. | |
| 1121 void AppendInvocationSpecificSwitches(SwReporterInvocation* next_invocation) { | |
| 1122 // Add switches for users who opted into extended Safe Browsing reporting. | |
| 1123 PrefService* local_state = g_browser_process->local_state(); | |
| 1124 if (next_invocation->BehaviourIsSupported( | |
| 1125 SwReporterInvocation::BEHAVIOUR_ALLOW_SEND_REPORTER_LOGS) && | |
| 1126 local_state && | |
| 1127 ShouldSendReporterLogs(next_invocation->suffix, *local_state)) { | |
| 1128 next_invocation->logs_upload_enabled = true; | |
| 1129 AddSwitchesForExtendedReportingUser(next_invocation); | |
| 1130 // Set the local state value before the first attempt to run the | |
| 1131 // reporter, because we only want to upload logs once in the window | |
| 1132 // defined by |kDaysBetweenReporterLogsSent|. If we set with other local | |
| 1133 // state values after the reporter runs, we could send logs again too | |
| 1134 // quickly (for example, if Chrome stops before the reporter finishes). | |
| 1135 local_state->SetInt64(prefs::kSwReporterLastTimeSentReport, | |
| 1136 Now().ToInternalValue()); | |
| 1137 } | |
| 1138 | |
| 1139 if (ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled()) | |
| 1140 next_invocation->command_line.AppendSwitch( | |
| 1141 chrome_cleaner::kEnableCrashReportingSwitch); | |
| 1142 } | |
| 1143 | |
| 1144 // Adds switches to be sent to the Software Reporter when the user opted into | |
| 1145 // extended Safe Browsing reporting and is not incognito. | |
| 1146 void AddSwitchesForExtendedReportingUser(SwReporterInvocation* invocation) { | |
| 1147 invocation->command_line.AppendSwitch( | |
| 1148 chrome_cleaner::kExtendedSafeBrowsingEnabledSwitch); | |
| 1149 invocation->command_line.AppendSwitchASCII( | |
| 1150 chrome_cleaner::kChromeVersionSwitch, version_info::GetVersionNumber()); | |
| 1151 invocation->command_line.AppendSwitchNative( | |
| 1152 chrome_cleaner::kChromeChannelSwitch, | |
| 1153 base::IntToString16(ChannelAsInt())); | |
| 1154 } | |
| 1155 | |
| 1156 bool first_run_ = true; | |
| 1157 | |
| 1158 // The queue of invocations that are currently running. | |
| 1159 SwReporterQueue current_invocations_; | |
| 1160 | |
| 1161 // The invocations to run next time the SwReporter is run. | |
| 1162 SwReporterQueue pending_invocations_; | |
| 1163 | |
| 1164 base::Version version_; | |
| 1165 | |
| 1166 scoped_refptr<base::TaskRunner> blocking_task_runner_ = | |
| 1167 base::CreateTaskRunnerWithTraits( | |
| 1168 // LaunchAndWaitForExitOnBackgroundThread() creates (MayBlock()) and | |
| 1169 // joins (WithBaseSyncPrimitives()) a process. | |
| 1170 base::TaskTraits() | |
| 1171 .WithShutdownBehavior( | |
| 1172 base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN) | |
| 1173 .WithPriority(base::TaskPriority::BACKGROUND) | |
| 1174 .MayBlock() | |
| 1175 .WithBaseSyncPrimitives()); | |
| 1176 | |
| 1177 // This value is used to identify how long to wait before starting a new run | |
| 1178 // of the reporter queue. It's initialized with the default value and may be | |
| 1179 // changed to a different value when a prompt is pending and the reporter | |
| 1180 // should be run before adding the global error to the Chrome menu. | |
| 1181 int days_between_reporter_runs_ = kDaysBetweenSuccessfulSwReporterRuns; | |
| 1182 | |
| 1183 // This will be true if the current queue of invocations started at a time | |
| 1184 // when logs should be uploaded. | |
| 1185 bool in_logs_upload_period_ = false; | |
| 1186 | |
| 1187 // A single leaky instance. | |
| 1188 static ReporterRunner* instance_; | |
| 1189 | |
| 1190 DISALLOW_COPY_AND_ASSIGN(ReporterRunner); | |
| 1191 }; | |
| 1192 | |
| 1193 ReporterRunner* ReporterRunner::instance_ = nullptr; | |
| 1194 | |
| 1195 SwReporterInvocation::SwReporterInvocation() | |
| 1196 : command_line(base::CommandLine::NO_PROGRAM) {} | |
| 1197 | |
| 1198 SwReporterInvocation SwReporterInvocation::FromFilePath( | |
| 1199 const base::FilePath& exe_path) { | |
| 1200 SwReporterInvocation invocation; | |
| 1201 invocation.command_line = base::CommandLine(exe_path); | |
| 1202 return invocation; | |
| 1203 } | |
| 1204 | |
| 1205 SwReporterInvocation SwReporterInvocation::FromCommandLine( | |
| 1206 const base::CommandLine& command_line) { | |
| 1207 SwReporterInvocation invocation; | |
| 1208 invocation.command_line = command_line; | |
| 1209 return invocation; | |
| 1210 } | |
| 1211 | |
| 1212 bool SwReporterInvocation::operator==(const SwReporterInvocation& other) const { | |
| 1213 return command_line.argv() == other.command_line.argv() && | |
| 1214 suffix == other.suffix && | |
| 1215 supported_behaviours == other.supported_behaviours && | |
| 1216 logs_upload_enabled == other.logs_upload_enabled; | |
| 1217 } | |
| 1218 | |
| 1219 bool SwReporterInvocation::BehaviourIsSupported( | |
| 1220 SwReporterInvocation::Behaviours intended_behaviour) const { | |
| 1221 return (supported_behaviours & intended_behaviour) != 0; | |
| 1222 } | |
| 1223 | |
| 1224 void RunSwReporters(const SwReporterQueue& invocations, | |
| 1225 const base::Version& version) { | |
| 1226 DCHECK(!invocations.empty()); | |
| 1227 DCHECK(version.IsValid()); | |
| 1228 ReporterRunner::ScheduleInvocations(invocations, version); | |
| 1229 } | |
| 1230 | |
| 1231 bool ReporterFoundUws() { | |
| 1232 PrefService* local_state = g_browser_process->local_state(); | |
| 1233 if (!local_state) | |
| 1234 return false; | |
| 1235 int exit_code = local_state->GetInteger(prefs::kSwReporterLastExitCode); | |
| 1236 return exit_code == chrome_cleaner::kSwReporterCleanupNeeded; | |
| 1237 } | |
| 1238 | |
| 1239 bool UserHasRunCleaner() { | |
| 1240 base::string16 cleaner_key_path( | |
| 1241 chrome_cleaner::kSoftwareRemovalToolRegistryKey); | |
| 1242 cleaner_key_path.append(L"\\").append(chrome_cleaner::kCleanerSubKey); | |
| 1243 | |
| 1244 base::win::RegKey srt_cleaner_key; | |
| 1245 return srt_cleaner_key.Open(HKEY_CURRENT_USER, cleaner_key_path.c_str(), | |
| 1246 KEY_QUERY_VALUE) == ERROR_SUCCESS && | |
| 1247 srt_cleaner_key.GetValueCount() > 0; | |
| 1248 } | |
| 1249 | |
| 1250 void SetSwReporterTestingDelegate(SwReporterTestingDelegate* delegate) { | |
| 1251 g_testing_delegate_ = delegate; | |
| 1252 } | |
| 1253 | |
| 1254 } // namespace safe_browsing | |
| OLD | NEW |