| 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 && !scan->second->added && |
| 544 !scan->second->incidents.empty()) { |
| 499 upload_timer_.Reset(); | 545 upload_timer_.Reset(); |
| 500 return; | 546 return; |
| 501 } | 547 } |
| 502 } | 548 } |
| 503 | 549 |
| 504 collection_timeout_pending_ = false; | 550 collection_timeout_pending_ = false; |
| 505 | 551 |
| 506 UploadIfCollectionComplete(); | 552 UploadIfCollectionComplete(); |
| 507 } | 553 } |
| 508 | 554 |
| (...skipping 85 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 594 ClientIncidentReport_EnvironmentData_Process* process = | 640 ClientIncidentReport_EnvironmentData_Process* process = |
| 595 report->mutable_environment()->mutable_process(); | 641 report->mutable_environment()->mutable_process(); |
| 596 | 642 |
| 597 // Not all platforms have a metrics reporting preference. | 643 // Not all platforms have a metrics reporting preference. |
| 598 if (g_browser_process->local_state()->FindPreference( | 644 if (g_browser_process->local_state()->FindPreference( |
| 599 prefs::kMetricsReportingEnabled)) { | 645 prefs::kMetricsReportingEnabled)) { |
| 600 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( | 646 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( |
| 601 prefs::kMetricsReportingEnabled)); | 647 prefs::kMetricsReportingEnabled)); |
| 602 } | 648 } |
| 603 | 649 |
| 604 // Check for extended consent in any profile while collecting incidents. | 650 // Find a profile suitable for tracking process-wide incidents. |
| 605 process->set_extended_consent(false); | 651 Profile* analyses_profile = FindEligibleProfile(); |
| 652 process->set_extended_consent( |
| 653 analyses_profile ? analyses_profile->GetPrefs()->GetBoolean( |
| 654 prefs::kSafeBrowsingExtendedReportingEnabled) : |
| 655 false); |
| 656 |
| 657 // Associate process-wide incidents with the analyses profile. |
| 658 ProfileContext* null_context = GetProfileContext(NULL); |
| 659 if (null_context && analyses_profile) { |
| 660 DCHECK(!null_context->incidents.empty()); |
| 661 ProfileContext* analyses_context = GetProfileContext(analyses_profile); |
| 662 // Move the incidents to the target context. |
| 663 analyses_context->incidents.insert(analyses_context->incidents.end(), |
| 664 null_context->incidents.begin(), |
| 665 null_context->incidents.end()); |
| 666 null_context->incidents.weak_clear(); |
| 667 // Delete the process-wide context. |
| 668 delete null_context; |
| 669 profiles_.erase(NULL); |
| 670 } |
| 671 |
| 606 // Collect incidents across all profiles participating in safe browsing. Drop | 672 // Collect incidents across all profiles participating in safe browsing. Drop |
| 607 // incidents if the profile stopped participating before collection completed. | 673 // incidents if the profile stopped participating before collection completed. |
| 608 // Prune previously submitted incidents. | 674 // Prune previously submitted incidents. |
| 609 // Associate the profiles and their incident data with the upload. | 675 // Associate the profiles and their incident data with the upload. |
| 610 size_t prune_count = 0; | 676 size_t prune_count = 0; |
| 611 UploadContext::PersistentIncidentStateCollection profiles_to_state; | 677 UploadContext::PersistentIncidentStateCollection profiles_to_state; |
| 612 for (ProfileContextCollection::iterator scan = profiles_.begin(); | 678 for (ProfileContextCollection::iterator scan = profiles_.begin(); |
| 613 scan != profiles_.end(); | 679 scan != profiles_.end(); |
| 614 ++scan) { | 680 ++scan) { |
| 681 // Bypass process-wide incidents that have not yet been associated with a |
| 682 // profile. |
| 683 if (!scan->first) |
| 684 continue; |
| 615 PrefService* prefs = scan->first->GetPrefs(); | 685 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; | 686 ProfileContext* context = scan->second; |
| 622 if (context->incidents.empty()) | 687 if (context->incidents.empty()) |
| 623 continue; | 688 continue; |
| 624 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { | 689 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { |
| 625 for (size_t i = 0; i < context->incidents.size(); ++i) { | 690 for (size_t i = 0; i < context->incidents.size(); ++i) { |
| 626 LogIncidentDataType(DROPPED, *context->incidents[i]); | 691 LogIncidentDataType(DROPPED, *context->incidents[i]); |
| 627 } | 692 } |
| 628 context->incidents.clear(); | 693 context->incidents.clear(); |
| 629 continue; | 694 continue; |
| 630 } | 695 } |
| (...skipping 162 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 793 if (!profile->IsOffTheRecord()) | 858 if (!profile->IsOffTheRecord()) |
| 794 OnProfileDestroyed(profile); | 859 OnProfileDestroyed(profile); |
| 795 break; | 860 break; |
| 796 } | 861 } |
| 797 default: | 862 default: |
| 798 break; | 863 break; |
| 799 } | 864 } |
| 800 } | 865 } |
| 801 | 866 |
| 802 } // namespace safe_browsing | 867 } // namespace safe_browsing |
| OLD | NEW |