OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 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/safe_browsing/incident_reporting_service.h" | 5 #include "chrome/browser/safe_browsing/incident_reporting_service.h" |
6 | 6 |
7 #include <math.h> | 7 #include <math.h> |
8 | 8 |
9 #include <algorithm> | 9 #include <algorithm> |
10 #include <vector> | 10 #include <vector> |
11 | 11 |
12 #include "base/metrics/histogram.h" | 12 #include "base/metrics/histogram.h" |
13 #include "base/prefs/pref_service.h" | 13 #include "base/prefs/pref_service.h" |
14 #include "base/prefs/scoped_user_pref_update.h" | 14 #include "base/prefs/scoped_user_pref_update.h" |
15 #include "base/process/process_info.h" | 15 #include "base/process/process_info.h" |
| 16 #include "base/single_thread_task_runner.h" |
16 #include "base/stl_util.h" | 17 #include "base/stl_util.h" |
17 #include "base/strings/string_number_conversions.h" | 18 #include "base/strings/string_number_conversions.h" |
| 19 #include "base/thread_task_runner_handle.h" |
18 #include "base/threading/sequenced_worker_pool.h" | 20 #include "base/threading/sequenced_worker_pool.h" |
19 #include "base/values.h" | 21 #include "base/values.h" |
20 #include "chrome/browser/browser_process.h" | 22 #include "chrome/browser/browser_process.h" |
21 #include "chrome/browser/chrome_notification_types.h" | 23 #include "chrome/browser/chrome_notification_types.h" |
22 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h" | 24 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h" |
23 #include "chrome/browser/profiles/profile.h" | 25 #include "chrome/browser/profiles/profile.h" |
24 #include "chrome/browser/safe_browsing/database_manager.h" | 26 #include "chrome/browser/safe_browsing/database_manager.h" |
25 #include "chrome/browser/safe_browsing/environment_data_collection.h" | 27 #include "chrome/browser/safe_browsing/environment_data_collection.h" |
26 #include "chrome/browser/safe_browsing/incident_report_uploader_impl.h" | 28 #include "chrome/browser/safe_browsing/incident_report_uploader_impl.h" |
27 #include "chrome/browser/safe_browsing/preference_validation_delegate.h" | 29 #include "chrome/browser/safe_browsing/preference_validation_delegate.h" |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 // The key for a specific instance of an incident. | 65 // The key for a specific instance of an incident. |
64 std::string key; | 66 std::string key; |
65 | 67 |
66 // A hash digest representing a specific instance of an incident. | 68 // A hash digest representing a specific instance of an incident. |
67 uint32_t digest; | 69 uint32_t digest; |
68 }; | 70 }; |
69 | 71 |
70 // The amount of time the service will wait to collate incidents. | 72 // The amount of time the service will wait to collate incidents. |
71 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute | 73 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute |
72 | 74 |
| 75 // The amount of time between running delayed analysis callbacks. |
| 76 const int64 kDefaultCallbackIntervalMs = 1000 * 20; |
| 77 |
73 // Returns the number of incidents contained in |incident|. The result is | 78 // Returns the number of incidents contained in |incident|. The result is |
74 // expected to be 1. Used in DCHECKs. | 79 // expected to be 1. Used in DCHECKs. |
75 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { | 80 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { |
76 size_t result = 0; | 81 size_t result = 0; |
77 if (incident.has_tracked_preference()) | 82 if (incident.has_tracked_preference()) |
78 ++result; | 83 ++result; |
79 // Add detection for new incident types here. | 84 // Add detection for new incident types here. |
80 return result; | 85 return result; |
81 } | 86 } |
82 | 87 |
(...skipping 67 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
150 if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string, | 155 if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string, |
151 &type_dict)) { | 156 &type_dict)) { |
152 type_dict = new base::DictionaryValue(); | 157 type_dict = new base::DictionaryValue(); |
153 incidents_sent->SetWithoutPathExpansion(type_string, type_dict); | 158 incidents_sent->SetWithoutPathExpansion(type_string, type_dict); |
154 } | 159 } |
155 type_dict->SetStringWithoutPathExpansion(data.key, | 160 type_dict->SetStringWithoutPathExpansion(data.key, |
156 base::UintToString(data.digest)); | 161 base::UintToString(data.digest)); |
157 } | 162 } |
158 } | 163 } |
159 | 164 |
| 165 // Runs |callback| on the thread to which |thread_runner| belongs. The callback |
| 166 // is run immediately if this function is called on |thread_runner|'s thread. |
| 167 void AddIncidentOnOriginThread( |
| 168 const AddIncidentCallback& callback, |
| 169 scoped_refptr<base::SingleThreadTaskRunner> thread_runner, |
| 170 scoped_ptr<ClientIncidentReport_IncidentData> incident) { |
| 171 if (thread_runner->BelongsToCurrentThread()) |
| 172 callback.Run(incident.Pass()); |
| 173 else |
| 174 thread_runner->PostTask(FROM_HERE, |
| 175 base::Bind(callback, base::Passed(&incident))); |
| 176 } |
| 177 |
160 } // namespace | 178 } // namespace |
161 | 179 |
162 struct IncidentReportingService::ProfileContext { | 180 struct IncidentReportingService::ProfileContext { |
163 ProfileContext(); | 181 ProfileContext(); |
164 ~ProfileContext(); | 182 ~ProfileContext(); |
165 | 183 |
166 // The incidents collected for this profile pending creation and/or upload. | 184 // The incidents collected for this profile pending creation and/or upload. |
167 ScopedVector<ClientIncidentReport_IncidentData> incidents; | 185 ScopedVector<ClientIncidentReport_IncidentData> incidents; |
168 | 186 |
169 // False until PROFILE_ADDED notification is received. | 187 // False until PROFILE_ADDED notification is received. |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
213 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) | 231 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) |
214 : database_manager_(safe_browsing_service ? | 232 : database_manager_(safe_browsing_service ? |
215 safe_browsing_service->database_manager() : NULL), | 233 safe_browsing_service->database_manager() : NULL), |
216 url_request_context_getter_(request_context_getter), | 234 url_request_context_getter_(request_context_getter), |
217 collect_environment_data_fn_(&CollectEnvironmentData), | 235 collect_environment_data_fn_(&CollectEnvironmentData), |
218 environment_collection_task_runner_( | 236 environment_collection_task_runner_( |
219 content::BrowserThread::GetBlockingPool() | 237 content::BrowserThread::GetBlockingPool() |
220 ->GetTaskRunnerWithShutdownBehavior( | 238 ->GetTaskRunnerWithShutdownBehavior( |
221 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), | 239 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
222 environment_collection_pending_(), | 240 environment_collection_pending_(), |
223 collection_timeout_pending_(), | 241 collation_timeout_pending_(), |
224 upload_timer_(FROM_HERE, | 242 collation_timer_(FROM_HERE, |
225 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), | 243 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), |
226 this, | 244 this, |
227 &IncidentReportingService::OnCollectionTimeout), | 245 &IncidentReportingService::OnCollationTimeout), |
| 246 delayed_analysis_callbacks_( |
| 247 base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs), |
| 248 content::BrowserThread::GetBlockingPool() |
| 249 ->GetTaskRunnerWithShutdownBehavior( |
| 250 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
228 receiver_weak_ptr_factory_(this), | 251 receiver_weak_ptr_factory_(this), |
229 weak_ptr_factory_(this) { | 252 weak_ptr_factory_(this) { |
230 notification_registrar_.Add(this, | 253 notification_registrar_.Add(this, |
231 chrome::NOTIFICATION_PROFILE_ADDED, | 254 chrome::NOTIFICATION_PROFILE_ADDED, |
232 content::NotificationService::AllSources()); | 255 content::NotificationService::AllSources()); |
233 notification_registrar_.Add(this, | 256 notification_registrar_.Add(this, |
234 chrome::NOTIFICATION_PROFILE_DESTROYED, | 257 chrome::NOTIFICATION_PROFILE_DESTROYED, |
235 content::NotificationService::AllSources()); | 258 content::NotificationService::AllSources()); |
236 } | 259 } |
237 | 260 |
(...skipping 25 matching lines...) Expand all Loading... |
263 scoped_ptr<TrackedPreferenceValidationDelegate> | 286 scoped_ptr<TrackedPreferenceValidationDelegate> |
264 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { | 287 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { |
265 DCHECK(thread_checker_.CalledOnValidThread()); | 288 DCHECK(thread_checker_.CalledOnValidThread()); |
266 | 289 |
267 if (profile->IsOffTheRecord()) | 290 if (profile->IsOffTheRecord()) |
268 return scoped_ptr<TrackedPreferenceValidationDelegate>(); | 291 return scoped_ptr<TrackedPreferenceValidationDelegate>(); |
269 return scoped_ptr<TrackedPreferenceValidationDelegate>( | 292 return scoped_ptr<TrackedPreferenceValidationDelegate>( |
270 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); | 293 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); |
271 } | 294 } |
272 | 295 |
| 296 void IncidentReportingService::RegisterDelayedAnalysisCallback( |
| 297 const DelayedAnalysisCallback& callback) { |
| 298 DCHECK(thread_checker_.CalledOnValidThread()); |
| 299 |
| 300 // |callback| will be run on the blocking pool, so it will likely run the |
| 301 // AddIncidentCallback there as well. Bounce the run of that callback back to |
| 302 // the current thread via AddIncidentOnOriginThread. |
| 303 delayed_analysis_callbacks_.RegisterCallback( |
| 304 base::Bind(callback, |
| 305 base::Bind(&AddIncidentOnOriginThread, |
| 306 GetAddIncidentCallback(NULL), |
| 307 base::ThreadTaskRunnerHandle::Get()))); |
| 308 |
| 309 // Start running the callbacks if any profiles are participating in safe |
| 310 // browsing. If none are now, running will commence if/when a participaing |
| 311 // profile is added. |
| 312 if (FindEligibleProfile()) |
| 313 delayed_analysis_callbacks_.Start(); |
| 314 } |
| 315 |
| 316 IncidentReportingService::IncidentReportingService( |
| 317 SafeBrowsingService* safe_browsing_service, |
| 318 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, |
| 319 base::TimeDelta delayed_task_interval, |
| 320 const scoped_refptr<base::TaskRunner>& delayed_task_runner) |
| 321 : database_manager_(safe_browsing_service ? |
| 322 safe_browsing_service->database_manager() : NULL), |
| 323 url_request_context_getter_(request_context_getter), |
| 324 collect_environment_data_fn_(&CollectEnvironmentData), |
| 325 environment_collection_task_runner_( |
| 326 content::BrowserThread::GetBlockingPool() |
| 327 ->GetTaskRunnerWithShutdownBehavior( |
| 328 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
| 329 environment_collection_pending_(), |
| 330 collation_timeout_pending_(), |
| 331 collation_timer_(FROM_HERE, |
| 332 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), |
| 333 this, |
| 334 &IncidentReportingService::OnCollationTimeout), |
| 335 delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner), |
| 336 receiver_weak_ptr_factory_(this), |
| 337 weak_ptr_factory_(this) { |
| 338 notification_registrar_.Add(this, |
| 339 chrome::NOTIFICATION_PROFILE_ADDED, |
| 340 content::NotificationService::AllSources()); |
| 341 notification_registrar_.Add(this, |
| 342 chrome::NOTIFICATION_PROFILE_DESTROYED, |
| 343 content::NotificationService::AllSources()); |
| 344 } |
| 345 |
273 void IncidentReportingService::SetCollectEnvironmentHook( | 346 void IncidentReportingService::SetCollectEnvironmentHook( |
274 CollectEnvironmentDataFn collect_environment_data_hook, | 347 CollectEnvironmentDataFn collect_environment_data_hook, |
275 const scoped_refptr<base::TaskRunner>& task_runner) { | 348 const scoped_refptr<base::TaskRunner>& task_runner) { |
276 if (collect_environment_data_hook) { | 349 if (collect_environment_data_hook) { |
277 collect_environment_data_fn_ = collect_environment_data_hook; | 350 collect_environment_data_fn_ = collect_environment_data_hook; |
278 environment_collection_task_runner_ = task_runner; | 351 environment_collection_task_runner_ = task_runner; |
279 } else { | 352 } else { |
280 collect_environment_data_fn_ = &CollectEnvironmentData; | 353 collect_environment_data_fn_ = &CollectEnvironmentData; |
281 environment_collection_task_runner_ = | 354 environment_collection_task_runner_ = |
282 content::BrowserThread::GetBlockingPool() | 355 content::BrowserThread::GetBlockingPool() |
283 ->GetTaskRunnerWithShutdownBehavior( | 356 ->GetTaskRunnerWithShutdownBehavior( |
284 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | 357 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
285 } | 358 } |
286 } | 359 } |
287 | 360 |
288 void IncidentReportingService::OnProfileAdded(Profile* profile) { | 361 void IncidentReportingService::OnProfileAdded(Profile* profile) { |
289 DCHECK(thread_checker_.CalledOnValidThread()); | 362 DCHECK(thread_checker_.CalledOnValidThread()); |
290 | 363 |
291 // Track the addition of all profiles even when no report is being assembled | 364 // Track the addition of all profiles even when no report is being assembled |
292 // so that the service can determine whether or not it can evaluate a | 365 // so that the service can determine whether or not it can evaluate a |
293 // profile's preferences at the time of incident addition. | 366 // profile's preferences at the time of incident addition. |
294 ProfileContext* context = GetOrCreateProfileContext(profile); | 367 ProfileContext* context = GetOrCreateProfileContext(profile); |
295 context->added = true; | 368 context->added = true; |
296 | 369 |
| 370 const bool safe_browsing_enabled = |
| 371 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled); |
| 372 |
| 373 // Start processing delayed analysis callbacks if this new profile |
| 374 // participates in safe browsing. Start is idempotent, so this is safe even if |
| 375 // they're already running. |
| 376 if (safe_browsing_enabled) |
| 377 delayed_analysis_callbacks_.Start(); |
| 378 |
| 379 // Start a new report if this profile participates in safe browsing and there |
| 380 // are process-wide incidents. |
| 381 if (safe_browsing_enabled && GetProfileContext(NULL)) |
| 382 BeginReportProcessing(); |
| 383 |
| 384 // TODO(grt): register for pref change notifications to start delayed analysis |
| 385 // and/or report processing if sb is currently disabled but subsequently |
| 386 // enabled. |
| 387 |
297 // Nothing else to do if a report is not being assembled. | 388 // Nothing else to do if a report is not being assembled. |
298 if (!report_) | 389 if (!report_) |
299 return; | 390 return; |
300 | 391 |
301 // Drop all incidents received prior to creation if the profile is not | 392 // Drop all incidents associated with this profile that were received prior to |
302 // participating in safe browsing. | 393 // its addition if the profile is not participating in safe browsing. |
303 if (!context->incidents.empty() && | 394 if (!context->incidents.empty() && !safe_browsing_enabled) { |
304 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 395 for (size_t i = 0; i < context->incidents.size(); ++i) |
305 for (size_t i = 0; i < context->incidents.size(); ++i) { | |
306 LogIncidentDataType(DROPPED, *context->incidents[i]); | 396 LogIncidentDataType(DROPPED, *context->incidents[i]); |
307 } | |
308 context->incidents.clear(); | 397 context->incidents.clear(); |
309 } | 398 } |
310 | 399 |
311 // Take another stab at finding the most recent download if a report is being | 400 // Take another stab at finding the most recent download if a report is being |
312 // assembled and one hasn't been found yet (the LastDownloadFinder operates | 401 // assembled and one hasn't been found yet (the LastDownloadFinder operates |
313 // only on profiles that have been added to the ProfileManager). | 402 // only on profiles that have been added to the ProfileManager). |
314 BeginDownloadCollection(); | 403 BeginDownloadCollection(); |
315 } | 404 } |
316 | 405 |
317 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder( | 406 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder( |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
355 // Forget about this profile. Incidents not yet sent for upload are lost. | 444 // Forget about this profile. Incidents not yet sent for upload are lost. |
356 // No new incidents will be accepted for it. | 445 // No new incidents will be accepted for it. |
357 delete it->second; | 446 delete it->second; |
358 profiles_.erase(it); | 447 profiles_.erase(it); |
359 | 448 |
360 // Remove the association with this profile from all pending uploads. | 449 // Remove the association with this profile from all pending uploads. |
361 for (size_t i = 0; i < uploads_.size(); ++i) | 450 for (size_t i = 0; i < uploads_.size(); ++i) |
362 uploads_[i]->profiles_to_state.erase(profile); | 451 uploads_[i]->profiles_to_state.erase(profile); |
363 } | 452 } |
364 | 453 |
| 454 Profile* IncidentReportingService::FindEligibleProfile() const { |
| 455 Profile* candidate = NULL; |
| 456 for (ProfileContextCollection::const_iterator scan = profiles_.begin(); |
| 457 scan != profiles_.end(); |
| 458 ++scan) { |
| 459 // Skip over profiles that have yet to be added to the profile manager. |
| 460 // This will also skip over the NULL-profile context used to hold |
| 461 // process-wide incidents. |
| 462 if (!scan->second->added) |
| 463 continue; |
| 464 PrefService* prefs = scan->first->GetPrefs(); |
| 465 if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
| 466 if (!candidate) |
| 467 candidate = scan->first; |
| 468 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { |
| 469 candidate = scan->first; |
| 470 break; |
| 471 } |
| 472 } |
| 473 } |
| 474 return candidate; |
| 475 } |
| 476 |
365 void IncidentReportingService::AddIncident( | 477 void IncidentReportingService::AddIncident( |
366 Profile* profile, | 478 Profile* profile, |
367 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { | 479 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { |
368 DCHECK(thread_checker_.CalledOnValidThread()); | 480 DCHECK(thread_checker_.CalledOnValidThread()); |
369 // Incidents outside the context of a profile are not supported at the moment. | |
370 DCHECK(profile); | |
371 DCHECK_EQ(1U, CountIncidents(*incident_data)); | 481 DCHECK_EQ(1U, CountIncidents(*incident_data)); |
372 | 482 |
373 ProfileContext* context = GetProfileContext(profile); | 483 ProfileContext* context = GetProfileContext(profile); |
374 // It is forbidden to call this function with a destroyed profile. | 484 // It is forbidden to call this function with a destroyed profile. |
375 DCHECK(context); | 485 DCHECK(context); |
| 486 // If this is a process-wide incident, the context must not indicate that the |
| 487 // profile (which is NULL) has been added to the profile manager. |
| 488 DCHECK(profile || !context->added); |
376 | 489 |
377 // Drop the incident immediately if profile creation has completed and the | 490 // Drop the incident immediately if the profile has already been added to the |
378 // profile is not participating in safe browsing. Preference evaluation is | 491 // manager and is not participating in safe browsing. Preference evaluation is |
379 // deferred until OnProfileAdded() if profile creation has not yet | 492 // deferred until OnProfileAdded() otherwise. |
380 // completed. | |
381 if (context->added && | 493 if (context->added && |
382 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 494 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
383 LogIncidentDataType(DROPPED, *incident_data); | 495 LogIncidentDataType(DROPPED, *incident_data); |
384 return; | 496 return; |
385 } | 497 } |
386 | 498 |
387 // Start assembling a new report if this is the first incident ever or the | |
388 // first since the last upload. | |
389 if (!report_) { | |
390 report_.reset(new ClientIncidentReport()); | |
391 first_incident_time_ = base::Time::Now(); | |
392 } | |
393 | |
394 // Provide time to the new incident if the caller didn't do so. | 499 // Provide time to the new incident if the caller didn't do so. |
395 if (!incident_data->has_incident_time_msec()) | 500 if (!incident_data->has_incident_time_msec()) |
396 incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime()); | 501 incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime()); |
397 | 502 |
398 // Take ownership of the incident. | 503 // Take ownership of the incident. |
399 context->incidents.push_back(incident_data.release()); | 504 context->incidents.push_back(incident_data.release()); |
400 | 505 |
| 506 // Remember when the first incident for this report arrived. |
| 507 if (first_incident_time_.is_null()) |
| 508 first_incident_time_ = base::Time::Now(); |
| 509 // Log the time between the previous incident and this one. |
401 if (!last_incident_time_.is_null()) { | 510 if (!last_incident_time_.is_null()) { |
402 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime", | 511 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime", |
403 base::TimeTicks::Now() - last_incident_time_); | 512 base::TimeTicks::Now() - last_incident_time_); |
404 } | 513 } |
405 last_incident_time_ = base::TimeTicks::Now(); | 514 last_incident_time_ = base::TimeTicks::Now(); |
406 | 515 |
407 // Persist the incident data. | 516 // Persist the incident data. |
408 | 517 |
409 // Restart the delay timer to send the report upon expiration. | 518 // Start assembling a new report if this is the first incident ever or the |
410 collection_timeout_pending_ = true; | 519 // first since the last upload. |
411 upload_timer_.Reset(); | 520 BeginReportProcessing(); |
| 521 } |
412 | 522 |
| 523 void IncidentReportingService::BeginReportProcessing() { |
| 524 DCHECK(thread_checker_.CalledOnValidThread()); |
| 525 |
| 526 // Creates a new report if needed. |
| 527 if (!report_) |
| 528 report_.reset(new ClientIncidentReport()); |
| 529 |
| 530 // Ensure that collection tasks are running (calls are idempotent). |
| 531 BeginIncidentCollation(); |
413 BeginEnvironmentCollection(); | 532 BeginEnvironmentCollection(); |
414 BeginDownloadCollection(); | 533 BeginDownloadCollection(); |
415 } | 534 } |
416 | 535 |
| 536 void IncidentReportingService::BeginIncidentCollation() { |
| 537 // Restart the delay timer to send the report upon expiration. |
| 538 collation_timeout_pending_ = true; |
| 539 collation_timer_.Reset(); |
| 540 } |
| 541 |
417 void IncidentReportingService::BeginEnvironmentCollection() { | 542 void IncidentReportingService::BeginEnvironmentCollection() { |
418 DCHECK(thread_checker_.CalledOnValidThread()); | 543 DCHECK(thread_checker_.CalledOnValidThread()); |
419 DCHECK(report_); | 544 DCHECK(report_); |
420 // Nothing to do if environment collection is pending or has already | 545 // Nothing to do if environment collection is pending or has already |
421 // completed. | 546 // completed. |
422 if (environment_collection_pending_ || report_->has_environment()) | 547 if (environment_collection_pending_ || report_->has_environment()) |
423 return; | 548 return; |
424 | 549 |
425 environment_collection_begin_ = base::TimeTicks::Now(); | 550 environment_collection_begin_ = base::TimeTicks::Now(); |
426 ClientIncidentReport_EnvironmentData* environment_data = | 551 ClientIncidentReport_EnvironmentData* environment_data = |
(...skipping 28 matching lines...) Expand all Loading... |
455 DCHECK(environment_collection_pending_); | 580 DCHECK(environment_collection_pending_); |
456 DCHECK(report_ && !report_->has_environment()); | 581 DCHECK(report_ && !report_->has_environment()); |
457 environment_collection_pending_ = false; | 582 environment_collection_pending_ = false; |
458 | 583 |
459 // CurrentProcessInfo::CreationTime() is missing on some platforms. | 584 // CurrentProcessInfo::CreationTime() is missing on some platforms. |
460 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) | 585 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) |
461 base::TimeDelta uptime = | 586 base::TimeDelta uptime = |
462 first_incident_time_ - base::CurrentProcessInfo::CreationTime(); | 587 first_incident_time_ - base::CurrentProcessInfo::CreationTime(); |
463 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds()); | 588 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds()); |
464 #endif | 589 #endif |
465 first_incident_time_ = base::Time(); | |
466 | 590 |
467 report_->set_allocated_environment(environment_data.release()); | 591 report_->set_allocated_environment(environment_data.release()); |
468 | 592 |
469 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime", | 593 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime", |
470 base::TimeTicks::Now() - environment_collection_begin_); | 594 base::TimeTicks::Now() - environment_collection_begin_); |
471 environment_collection_begin_ = base::TimeTicks(); | 595 environment_collection_begin_ = base::TimeTicks(); |
472 | 596 |
473 UploadIfCollectionComplete(); | 597 UploadIfCollectionComplete(); |
474 } | 598 } |
475 | 599 |
476 bool IncidentReportingService::WaitingToCollateIncidents() { | 600 bool IncidentReportingService::WaitingToCollateIncidents() { |
477 return collection_timeout_pending_; | 601 return collation_timeout_pending_; |
478 } | 602 } |
479 | 603 |
480 void IncidentReportingService::CancelIncidentCollection() { | 604 void IncidentReportingService::CancelIncidentCollection() { |
481 collection_timeout_pending_ = false; | 605 collation_timeout_pending_ = false; |
482 last_incident_time_ = base::TimeTicks(); | 606 last_incident_time_ = base::TimeTicks(); |
483 report_.reset(); | 607 report_.reset(); |
484 } | 608 } |
485 | 609 |
486 void IncidentReportingService::OnCollectionTimeout() { | 610 void IncidentReportingService::OnCollationTimeout() { |
487 DCHECK(thread_checker_.CalledOnValidThread()); | 611 DCHECK(thread_checker_.CalledOnValidThread()); |
488 | 612 |
489 // Exit early if collection was cancelled. | 613 // Exit early if collection was cancelled. |
490 if (!collection_timeout_pending_) | 614 if (!collation_timeout_pending_) |
491 return; | 615 return; |
492 | 616 |
493 // Wait another round if incidents have come in from a profile that has yet to | 617 // Wait another round if profile-bound incidents have come in from a profile |
494 // complete creation. | 618 // that has yet to complete creation. |
495 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 619 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
496 scan != profiles_.end(); | 620 scan != profiles_.end(); |
497 ++scan) { | 621 ++scan) { |
498 if (!scan->second->added && !scan->second->incidents.empty()) { | 622 if (scan->first && !scan->second->added && |
499 upload_timer_.Reset(); | 623 !scan->second->incidents.empty()) { |
| 624 collation_timer_.Reset(); |
500 return; | 625 return; |
501 } | 626 } |
502 } | 627 } |
503 | 628 |
504 collection_timeout_pending_ = false; | 629 collation_timeout_pending_ = false; |
505 | 630 |
506 UploadIfCollectionComplete(); | 631 UploadIfCollectionComplete(); |
507 } | 632 } |
508 | 633 |
509 void IncidentReportingService::BeginDownloadCollection() { | 634 void IncidentReportingService::BeginDownloadCollection() { |
510 DCHECK(thread_checker_.CalledOnValidThread()); | 635 DCHECK(thread_checker_.CalledOnValidThread()); |
511 DCHECK(report_); | 636 DCHECK(report_); |
512 // Nothing to do if a search for the most recent download is already pending | 637 // Nothing to do if a search for the most recent download is already pending |
513 // or if one has already been found. | 638 // or if one has already been found. |
514 if (last_download_finder_ || report_->has_download()) | 639 if (last_download_finder_ || report_->has_download()) |
(...skipping 59 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
574 // Bail out if there are still outstanding collection tasks. Completion of any | 699 // Bail out if there are still outstanding collection tasks. Completion of any |
575 // of these will start another upload attempt. | 700 // of these will start another upload attempt. |
576 if (WaitingForEnvironmentCollection() || | 701 if (WaitingForEnvironmentCollection() || |
577 WaitingToCollateIncidents() || | 702 WaitingToCollateIncidents() || |
578 WaitingForMostRecentDownload()) { | 703 WaitingForMostRecentDownload()) { |
579 return; | 704 return; |
580 } | 705 } |
581 | 706 |
582 // Take ownership of the report and clear things for future reports. | 707 // Take ownership of the report and clear things for future reports. |
583 scoped_ptr<ClientIncidentReport> report(report_.Pass()); | 708 scoped_ptr<ClientIncidentReport> report(report_.Pass()); |
| 709 first_incident_time_ = base::Time(); |
584 last_incident_time_ = base::TimeTicks(); | 710 last_incident_time_ = base::TimeTicks(); |
585 | 711 |
586 // Drop the report if no executable download was found. | 712 // Drop the report if no executable download was found. |
587 if (!report->has_download()) { | 713 if (!report->has_download()) { |
588 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult", | 714 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult", |
589 IncidentReportUploader::UPLOAD_NO_DOWNLOAD, | 715 IncidentReportUploader::UPLOAD_NO_DOWNLOAD, |
590 IncidentReportUploader::NUM_UPLOAD_RESULTS); | 716 IncidentReportUploader::NUM_UPLOAD_RESULTS); |
591 return; | 717 return; |
592 } | 718 } |
593 | 719 |
594 ClientIncidentReport_EnvironmentData_Process* process = | 720 ClientIncidentReport_EnvironmentData_Process* process = |
595 report->mutable_environment()->mutable_process(); | 721 report->mutable_environment()->mutable_process(); |
596 | 722 |
597 // Not all platforms have a metrics reporting preference. | 723 // Not all platforms have a metrics reporting preference. |
598 if (g_browser_process->local_state()->FindPreference( | 724 if (g_browser_process->local_state()->FindPreference( |
599 prefs::kMetricsReportingEnabled)) { | 725 prefs::kMetricsReportingEnabled)) { |
600 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( | 726 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( |
601 prefs::kMetricsReportingEnabled)); | 727 prefs::kMetricsReportingEnabled)); |
602 } | 728 } |
603 | 729 |
604 // Check for extended consent in any profile while collecting incidents. | 730 // Find the profile that benefits from the strongest protections. |
605 process->set_extended_consent(false); | 731 Profile* eligible_profile = FindEligibleProfile(); |
| 732 process->set_extended_consent( |
| 733 eligible_profile ? eligible_profile->GetPrefs()->GetBoolean( |
| 734 prefs::kSafeBrowsingExtendedReportingEnabled) : |
| 735 false); |
| 736 |
| 737 // Associate process-wide incidents with the profile that benefits from the |
| 738 // strongest safe browsing protections. |
| 739 ProfileContext* null_context = GetProfileContext(NULL); |
| 740 if (null_context && !null_context->incidents.empty() && eligible_profile) { |
| 741 ProfileContext* eligible_context = GetProfileContext(eligible_profile); |
| 742 // Move the incidents to the target context. |
| 743 eligible_context->incidents.insert(eligible_context->incidents.end(), |
| 744 null_context->incidents.begin(), |
| 745 null_context->incidents.end()); |
| 746 null_context->incidents.weak_clear(); |
| 747 } |
| 748 |
606 // Collect incidents across all profiles participating in safe browsing. Drop | 749 // Collect incidents across all profiles participating in safe browsing. Drop |
607 // incidents if the profile stopped participating before collection completed. | 750 // incidents if the profile stopped participating before collection completed. |
608 // Prune previously submitted incidents. | 751 // Prune previously submitted incidents. |
609 // Associate the profiles and their incident data with the upload. | 752 // Associate the profiles and their incident data with the upload. |
610 size_t prune_count = 0; | 753 size_t prune_count = 0; |
611 UploadContext::PersistentIncidentStateCollection profiles_to_state; | 754 UploadContext::PersistentIncidentStateCollection profiles_to_state; |
612 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 755 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
613 scan != profiles_.end(); | 756 scan != profiles_.end(); |
614 ++scan) { | 757 ++scan) { |
| 758 // Bypass process-wide incidents that have not yet been associated with a |
| 759 // profile. |
| 760 if (!scan->first) |
| 761 continue; |
615 PrefService* prefs = scan->first->GetPrefs(); | 762 PrefService* prefs = scan->first->GetPrefs(); |
616 if (process && | |
617 prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { | |
618 process->set_extended_consent(true); | |
619 process = NULL; // Don't check any more once one is found. | |
620 } | |
621 ProfileContext* context = scan->second; | 763 ProfileContext* context = scan->second; |
622 if (context->incidents.empty()) | 764 if (context->incidents.empty()) |
623 continue; | 765 continue; |
624 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 766 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
625 for (size_t i = 0; i < context->incidents.size(); ++i) { | 767 for (size_t i = 0; i < context->incidents.size(); ++i) { |
626 LogIncidentDataType(DROPPED, *context->incidents[i]); | 768 LogIncidentDataType(DROPPED, *context->incidents[i]); |
627 } | 769 } |
628 context->incidents.clear(); | 770 context->incidents.clear(); |
629 continue; | 771 continue; |
630 } | 772 } |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
793 if (!profile->IsOffTheRecord()) | 935 if (!profile->IsOffTheRecord()) |
794 OnProfileDestroyed(profile); | 936 OnProfileDestroyed(profile); |
795 break; | 937 break; |
796 } | 938 } |
797 default: | 939 default: |
798 break; | 940 break; |
799 } | 941 } |
800 } | 942 } |
801 | 943 |
802 } // namespace safe_browsing | 944 } // namespace safe_browsing |
OLD | NEW |