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> |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
63 // The key for a specific instance of an incident. | 63 // The key for a specific instance of an incident. |
64 std::string key; | 64 std::string key; |
65 | 65 |
66 // A hash digest representing a specific instance of an incident. | 66 // A hash digest representing a specific instance of an incident. |
67 uint32_t digest; | 67 uint32_t digest; |
68 }; | 68 }; |
69 | 69 |
70 // The amount of time the service will wait to collate incidents. | 70 // The amount of time the service will wait to collate incidents. |
71 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute | 71 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute |
72 | 72 |
| 73 // The amount of time between running delayed analysis callbacks. |
| 74 const int64 kDefaultCallbackMs = 1000 * 20; |
| 75 |
73 // Returns the number of incidents contained in |incident|. The result is | 76 // Returns the number of incidents contained in |incident|. The result is |
74 // expected to be 1. Used in DCHECKs. | 77 // expected to be 1. Used in DCHECKs. |
75 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { | 78 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { |
76 size_t result = 0; | 79 size_t result = 0; |
77 if (incident.has_tracked_preference()) | 80 if (incident.has_tracked_preference()) |
78 ++result; | 81 ++result; |
79 // Add detection for new incident types here. | 82 // Add detection for new incident types here. |
80 return result; | 83 return result; |
81 } | 84 } |
82 | 85 |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
218 environment_collection_task_runner_( | 221 environment_collection_task_runner_( |
219 content::BrowserThread::GetBlockingPool() | 222 content::BrowserThread::GetBlockingPool() |
220 ->GetTaskRunnerWithShutdownBehavior( | 223 ->GetTaskRunnerWithShutdownBehavior( |
221 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), | 224 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), |
222 environment_collection_pending_(), | 225 environment_collection_pending_(), |
223 collection_timeout_pending_(), | 226 collection_timeout_pending_(), |
224 upload_timer_(FROM_HERE, | 227 upload_timer_(FROM_HERE, |
225 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), | 228 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), |
226 this, | 229 this, |
227 &IncidentReportingService::OnCollectionTimeout), | 230 &IncidentReportingService::OnCollectionTimeout), |
| 231 delayed_analysis_callbacks_( |
| 232 base::TimeDelta::FromMilliseconds(kDefaultCallbackMs), |
| 233 content::BrowserThread::GetBlockingPool()), |
228 receiver_weak_ptr_factory_(this), | 234 receiver_weak_ptr_factory_(this), |
229 weak_ptr_factory_(this) { | 235 weak_ptr_factory_(this) { |
230 notification_registrar_.Add(this, | 236 notification_registrar_.Add(this, |
231 chrome::NOTIFICATION_PROFILE_ADDED, | 237 chrome::NOTIFICATION_PROFILE_ADDED, |
232 content::NotificationService::AllSources()); | 238 content::NotificationService::AllSources()); |
233 notification_registrar_.Add(this, | 239 notification_registrar_.Add(this, |
234 chrome::NOTIFICATION_PROFILE_DESTROYED, | 240 chrome::NOTIFICATION_PROFILE_DESTROYED, |
235 content::NotificationService::AllSources()); | 241 content::NotificationService::AllSources()); |
236 } | 242 } |
237 | 243 |
(...skipping 25 matching lines...) Expand all Loading... |
263 scoped_ptr<TrackedPreferenceValidationDelegate> | 269 scoped_ptr<TrackedPreferenceValidationDelegate> |
264 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { | 270 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { |
265 DCHECK(thread_checker_.CalledOnValidThread()); | 271 DCHECK(thread_checker_.CalledOnValidThread()); |
266 | 272 |
267 if (profile->IsOffTheRecord()) | 273 if (profile->IsOffTheRecord()) |
268 return scoped_ptr<TrackedPreferenceValidationDelegate>(); | 274 return scoped_ptr<TrackedPreferenceValidationDelegate>(); |
269 return scoped_ptr<TrackedPreferenceValidationDelegate>( | 275 return scoped_ptr<TrackedPreferenceValidationDelegate>( |
270 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); | 276 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); |
271 } | 277 } |
272 | 278 |
| 279 void IncidentReportingService::RegisterDelayedAnalysisCallback( |
| 280 const DelayedAnalysisCallback& callback) { |
| 281 delayed_analysis_callbacks_.RegisterCallback( |
| 282 base::Bind(callback, GetAddIncidentCallback(NULL))); |
| 283 |
| 284 // Start running the callbacks if any profiles are participating in safe |
| 285 // browsing. |
| 286 if (FindEligibleProfile()) |
| 287 delayed_analysis_callbacks_.Start(); |
| 288 } |
| 289 |
273 void IncidentReportingService::SetCollectEnvironmentHook( | 290 void IncidentReportingService::SetCollectEnvironmentHook( |
274 CollectEnvironmentDataFn collect_environment_data_hook, | 291 CollectEnvironmentDataFn collect_environment_data_hook, |
275 const scoped_refptr<base::TaskRunner>& task_runner) { | 292 const scoped_refptr<base::TaskRunner>& task_runner) { |
276 if (collect_environment_data_hook) { | 293 if (collect_environment_data_hook) { |
277 collect_environment_data_fn_ = collect_environment_data_hook; | 294 collect_environment_data_fn_ = collect_environment_data_hook; |
278 environment_collection_task_runner_ = task_runner; | 295 environment_collection_task_runner_ = task_runner; |
279 } else { | 296 } else { |
280 collect_environment_data_fn_ = &CollectEnvironmentData; | 297 collect_environment_data_fn_ = &CollectEnvironmentData; |
281 environment_collection_task_runner_ = | 298 environment_collection_task_runner_ = |
282 content::BrowserThread::GetBlockingPool() | 299 content::BrowserThread::GetBlockingPool() |
283 ->GetTaskRunnerWithShutdownBehavior( | 300 ->GetTaskRunnerWithShutdownBehavior( |
284 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); | 301 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); |
285 } | 302 } |
286 } | 303 } |
287 | 304 |
288 void IncidentReportingService::OnProfileAdded(Profile* profile) { | 305 void IncidentReportingService::OnProfileAdded(Profile* profile) { |
289 DCHECK(thread_checker_.CalledOnValidThread()); | 306 DCHECK(thread_checker_.CalledOnValidThread()); |
290 | 307 |
291 // Track the addition of all profiles even when no report is being assembled | 308 // 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 | 309 // so that the service can determine whether or not it can evaluate a |
293 // profile's preferences at the time of incident addition. | 310 // profile's preferences at the time of incident addition. |
294 ProfileContext* context = GetOrCreateProfileContext(profile); | 311 ProfileContext* context = GetOrCreateProfileContext(profile); |
295 context->added = true; | 312 context->added = true; |
296 | 313 |
| 314 const bool safe_browsing_enabled = |
| 315 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled); |
| 316 |
| 317 // Start processing delayed analysis callbacks if this new profile |
| 318 // participates in safe browsing. |
| 319 if (safe_browsing_enabled) |
| 320 delayed_analysis_callbacks_.Start(); |
| 321 |
297 // Nothing else to do if a report is not being assembled. | 322 // Nothing else to do if a report is not being assembled. |
298 if (!report_) | 323 if (!report_) |
299 return; | 324 return; |
300 | 325 |
301 // Drop all incidents received prior to creation if the profile is not | 326 // Drop all incidents associated with this profile that were received prior to |
302 // participating in safe browsing. | 327 // its addition if the profile is not participating in safe browsing. |
303 if (!context->incidents.empty() && | 328 if (!context->incidents.empty() && !safe_browsing_enabled) { |
304 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 329 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]); | 330 LogIncidentDataType(DROPPED, *context->incidents[i]); |
307 } | |
308 context->incidents.clear(); | 331 context->incidents.clear(); |
309 } | 332 } |
310 | 333 |
311 // Take another stab at finding the most recent download if a report is being | 334 // 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 | 335 // assembled and one hasn't been found yet (the LastDownloadFinder operates |
313 // only on profiles that have been added to the ProfileManager). | 336 // only on profiles that have been added to the ProfileManager). |
314 BeginDownloadCollection(); | 337 BeginDownloadCollection(); |
315 } | 338 } |
316 | 339 |
317 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder( | 340 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. | 378 // Forget about this profile. Incidents not yet sent for upload are lost. |
356 // No new incidents will be accepted for it. | 379 // No new incidents will be accepted for it. |
357 delete it->second; | 380 delete it->second; |
358 profiles_.erase(it); | 381 profiles_.erase(it); |
359 | 382 |
360 // Remove the association with this profile from all pending uploads. | 383 // Remove the association with this profile from all pending uploads. |
361 for (size_t i = 0; i < uploads_.size(); ++i) | 384 for (size_t i = 0; i < uploads_.size(); ++i) |
362 uploads_[i]->profiles_to_state.erase(profile); | 385 uploads_[i]->profiles_to_state.erase(profile); |
363 } | 386 } |
364 | 387 |
| 388 Profile* IncidentReportingService::FindEligibleProfile() const { |
| 389 Profile* candidate = NULL; |
| 390 for (ProfileContextCollection::const_iterator scan = profiles_.begin(); |
| 391 scan != profiles_.end(); |
| 392 ++scan) { |
| 393 // Skip over profiles that have yet to be added to the profile manager. |
| 394 // This will also skip over the NULL-profile context used to hold |
| 395 // process-wide incidents. |
| 396 if (!scan->second->added) |
| 397 continue; |
| 398 PrefService* prefs = scan->first->GetPrefs(); |
| 399 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { |
| 400 candidate = scan->first; |
| 401 break; |
| 402 } |
| 403 if (!candidate && prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) |
| 404 candidate = scan->first; |
| 405 } |
| 406 return candidate; |
| 407 } |
| 408 |
365 void IncidentReportingService::AddIncident( | 409 void IncidentReportingService::AddIncident( |
366 Profile* profile, | 410 Profile* profile, |
367 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { | 411 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { |
368 DCHECK(thread_checker_.CalledOnValidThread()); | 412 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)); | 413 DCHECK_EQ(1U, CountIncidents(*incident_data)); |
372 | 414 |
373 ProfileContext* context = GetProfileContext(profile); | 415 ProfileContext* context = GetProfileContext(profile); |
374 // It is forbidden to call this function with a destroyed profile. | 416 // It is forbidden to call this function with a destroyed profile. |
375 DCHECK(context); | 417 DCHECK(context); |
| 418 // If this is a process-wide incident, the context must not indicate that the |
| 419 // profile (which is NULL) has been added to the profile manager. |
| 420 DCHECK(profile || !context->added); |
376 | 421 |
377 // Drop the incident immediately if profile creation has completed and the | 422 // Drop the incident immediately if profile creation has completed and the |
378 // profile is not participating in safe browsing. Preference evaluation is | 423 // profile is not participating in safe browsing. Preference evaluation is |
379 // deferred until OnProfileAdded() if profile creation has not yet | 424 // deferred until OnProfileAdded() if profile creation has not yet |
380 // completed. | 425 // completed. |
381 if (context->added && | 426 if (context->added && |
382 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 427 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
383 LogIncidentDataType(DROPPED, *incident_data); | 428 LogIncidentDataType(DROPPED, *incident_data); |
384 return; | 429 return; |
385 } | 430 } |
(...skipping 97 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
483 report_.reset(); | 528 report_.reset(); |
484 } | 529 } |
485 | 530 |
486 void IncidentReportingService::OnCollectionTimeout() { | 531 void IncidentReportingService::OnCollectionTimeout() { |
487 DCHECK(thread_checker_.CalledOnValidThread()); | 532 DCHECK(thread_checker_.CalledOnValidThread()); |
488 | 533 |
489 // Exit early if collection was cancelled. | 534 // Exit early if collection was cancelled. |
490 if (!collection_timeout_pending_) | 535 if (!collection_timeout_pending_) |
491 return; | 536 return; |
492 | 537 |
493 // Wait another round if incidents have come in from a profile that has yet to | 538 // Wait another round if profile-bound incidents have come in from a profile |
494 // complete creation. | 539 // that has yet to complete creation. |
495 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 540 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
496 scan != profiles_.end(); | 541 scan != profiles_.end(); |
497 ++scan) { | 542 ++scan) { |
498 if (!scan->second->added && !scan->second->incidents.empty()) { | 543 if (scan->first && |
| 544 !scan->second->added && |
| 545 !scan->second->incidents.empty()) { |
499 upload_timer_.Reset(); | 546 upload_timer_.Reset(); |
500 return; | 547 return; |
501 } | 548 } |
502 } | 549 } |
503 | 550 |
504 collection_timeout_pending_ = false; | 551 collection_timeout_pending_ = false; |
505 | 552 |
506 UploadIfCollectionComplete(); | 553 UploadIfCollectionComplete(); |
507 } | 554 } |
508 | 555 |
(...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
594 ClientIncidentReport_EnvironmentData_Process* process = | 641 ClientIncidentReport_EnvironmentData_Process* process = |
595 report->mutable_environment()->mutable_process(); | 642 report->mutable_environment()->mutable_process(); |
596 | 643 |
597 // Not all platforms have a metrics reporting preference. | 644 // Not all platforms have a metrics reporting preference. |
598 if (g_browser_process->local_state()->FindPreference( | 645 if (g_browser_process->local_state()->FindPreference( |
599 prefs::kMetricsReportingEnabled)) { | 646 prefs::kMetricsReportingEnabled)) { |
600 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( | 647 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( |
601 prefs::kMetricsReportingEnabled)); | 648 prefs::kMetricsReportingEnabled)); |
602 } | 649 } |
603 | 650 |
604 // Check for extended consent in any profile while collecting incidents. | 651 // Find a profile suitable for tracking process-wide incidents. |
605 process->set_extended_consent(false); | 652 Profile* analyses_profile = FindEligibleProfile(); |
| 653 process->set_extended_consent( |
| 654 analyses_profile ? analyses_profile->GetPrefs()->GetBoolean( |
| 655 prefs::kSafeBrowsingExtendedReportingEnabled) : |
| 656 false); |
| 657 |
| 658 // Associate process-wide incidents with the analyses profile. |
| 659 ProfileContext* null_context = GetProfileContext(NULL); |
| 660 if (null_context && analyses_profile) { |
| 661 DCHECK(!null_context->incidents.empty()); |
| 662 ProfileContext* analyses_context = GetProfileContext(analyses_profile); |
| 663 // Move the incidents to the target context. |
| 664 analyses_context->incidents.insert(analyses_context->incidents.end(), |
| 665 null_context->incidents.begin(), |
| 666 null_context->incidents.end()); |
| 667 null_context->incidents.weak_clear(); |
| 668 // Delete the process-wide context. |
| 669 delete null_context; |
| 670 profiles_.erase(NULL); |
| 671 } |
| 672 |
606 // Collect incidents across all profiles participating in safe browsing. Drop | 673 // Collect incidents across all profiles participating in safe browsing. Drop |
607 // incidents if the profile stopped participating before collection completed. | 674 // incidents if the profile stopped participating before collection completed. |
608 // Prune previously submitted incidents. | 675 // Prune previously submitted incidents. |
609 // Associate the profiles and their incident data with the upload. | 676 // Associate the profiles and their incident data with the upload. |
610 size_t prune_count = 0; | 677 size_t prune_count = 0; |
611 UploadContext::PersistentIncidentStateCollection profiles_to_state; | 678 UploadContext::PersistentIncidentStateCollection profiles_to_state; |
612 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 679 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
613 scan != profiles_.end(); | 680 scan != profiles_.end(); |
614 ++scan) { | 681 ++scan) { |
| 682 // Bypass process-wide incidents that have not yet been associated with a |
| 683 // profile. |
| 684 if (!scan->first) |
| 685 continue; |
615 PrefService* prefs = scan->first->GetPrefs(); | 686 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; | 687 ProfileContext* context = scan->second; |
622 if (context->incidents.empty()) | 688 if (context->incidents.empty()) |
623 continue; | 689 continue; |
624 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 690 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
625 for (size_t i = 0; i < context->incidents.size(); ++i) { | 691 for (size_t i = 0; i < context->incidents.size(); ++i) { |
626 LogIncidentDataType(DROPPED, *context->incidents[i]); | 692 LogIncidentDataType(DROPPED, *context->incidents[i]); |
627 } | 693 } |
628 context->incidents.clear(); | 694 context->incidents.clear(); |
629 continue; | 695 continue; |
630 } | 696 } |
(...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
793 if (!profile->IsOffTheRecord()) | 859 if (!profile->IsOffTheRecord()) |
794 OnProfileDestroyed(profile); | 860 OnProfileDestroyed(profile); |
795 break; | 861 break; |
796 } | 862 } |
797 default: | 863 default: |
798 break; | 864 break; |
799 } | 865 } |
800 } | 866 } |
801 | 867 |
802 } // namespace safe_browsing | 868 } // namespace safe_browsing |
OLD | NEW |