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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h" 6 7 #include <math.h> 8 9 #include <algorithm> 10 #include <vector> 11 12 #include "base/metrics/histogram.h" 13 #include "base/prefs/pref_service.h" 14 #include "base/prefs/scoped_user_pref_update.h" 15 #include "base/process/process_info.h" 16 #include "base/single_thread_task_runner.h" 17 #include "base/stl_util.h" 18 #include "base/strings/string_number_conversions.h" 19 #include "base/thread_task_runner_handle.h" 20 #include "base/threading/sequenced_worker_pool.h" 21 #include "base/values.h" 22 #include "chrome/browser/browser_process.h" 23 #include "chrome/browser/chrome_notification_types.h" 24 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h" 25 #include "chrome/browser/profiles/profile.h" 26 #include "chrome/browser/safe_browsing/database_manager.h" 27 #include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_handlers.h" 28 #include "chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_handlers.h" 29 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h" 30 #include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h" 31 #include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h" 32 #include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_handlers.h" 33 #include "chrome/browser/safe_browsing/safe_browsing_service.h" 34 #include "chrome/common/pref_names.h" 35 #include "chrome/common/safe_browsing/csd.pb.h" 36 #include "content/public/browser/browser_thread.h" 37 #include "content/public/browser/notification_service.h" 38 #include "net/url_request/url_request_context_getter.h" 39 40 namespace safe_browsing { 41 42 namespace { 43 44 // The type of an incident. Used for user metrics and for pruning of 45 // previously-reported incidents. 46 enum IncidentType { 47 // Start with 1 rather than zero; otherwise there won't be enough buckets for 48 // the histogram. 49 TRACKED_PREFERENCE = 1, 50 BINARY_INTEGRITY = 2, 51 BLACKLIST_LOAD = 3, 52 // Values for new incident types go here. 53 NUM_INCIDENT_TYPES = 4 54 }; 55 56 // The action taken for an incident; used for user metrics (see 57 // LogIncidentDataType). 58 enum IncidentDisposition { 59 DROPPED, 60 ACCEPTED, 61 }; 62 63 // The state persisted for a specific instance of an incident to enable pruning 64 // of previously-reported incidents. 65 struct PersistentIncidentState { 66 // The type of the incident. 67 IncidentType type; 68 69 // The key for a specific instance of an incident. 70 std::string key; 71 72 // A hash digest representing a specific instance of an incident. 73 uint32_t digest; 74 }; 75 76 // The amount of time the service will wait to collate incidents. 77 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute 78 79 // The amount of time between running delayed analysis callbacks. 80 const int64 kDefaultCallbackIntervalMs = 1000 * 20; 81 82 // Returns the number of incidents contained in |incident|. The result is 83 // expected to be 1. Used in DCHECKs. 84 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) { 85 size_t result = 0; 86 if (incident.has_tracked_preference()) 87 ++result; 88 if (incident.has_binary_integrity()) 89 ++result; 90 if (incident.has_blacklist_load()) 91 ++result; 92 // Add detection for new incident types here. 93 return result; 94 } 95 96 // Returns the type of incident contained in |incident_data|. 97 IncidentType GetIncidentType( 98 const ClientIncidentReport_IncidentData& incident_data) { 99 if (incident_data.has_tracked_preference()) 100 return TRACKED_PREFERENCE; 101 if (incident_data.has_binary_integrity()) 102 return BINARY_INTEGRITY; 103 if (incident_data.has_blacklist_load()) 104 return BLACKLIST_LOAD; 105 106 // Add detection for new incident types here. 107 COMPILE_ASSERT(BLACKLIST_LOAD + 1 == NUM_INCIDENT_TYPES, 108 add_support_for_new_types); 109 NOTREACHED(); 110 return NUM_INCIDENT_TYPES; 111 } 112 113 // Logs the type of incident in |incident_data| to a user metrics histogram. 114 void LogIncidentDataType( 115 IncidentDisposition disposition, 116 const ClientIncidentReport_IncidentData& incident_data) { 117 IncidentType type = GetIncidentType(incident_data); 118 if (disposition == ACCEPTED) { 119 UMA_HISTOGRAM_ENUMERATION("SBIRS.Incident", type, NUM_INCIDENT_TYPES); 120 } else { 121 DCHECK_EQ(disposition, DROPPED); 122 UMA_HISTOGRAM_ENUMERATION("SBIRS.DroppedIncident", type, 123 NUM_INCIDENT_TYPES); 124 } 125 } 126 127 // Computes the persistent state for an incident. 128 PersistentIncidentState ComputeIncidentState( 129 const ClientIncidentReport_IncidentData& incident) { 130 PersistentIncidentState state = {GetIncidentType(incident)}; 131 switch (state.type) { 132 case TRACKED_PREFERENCE: 133 state.key = GetTrackedPreferenceIncidentKey(incident); 134 state.digest = GetTrackedPreferenceIncidentDigest(incident); 135 break; 136 case BINARY_INTEGRITY: 137 state.key = GetBinaryIntegrityIncidentKey(incident); 138 state.digest = GetBinaryIntegrityIncidentDigest(incident); 139 break; 140 case BLACKLIST_LOAD: 141 state.key = GetBlacklistLoadIncidentKey(incident); 142 state.digest = GetBlacklistLoadIncidentDigest(incident); 143 break; 144 // Add handling for new incident types here. 145 default: 146 COMPILE_ASSERT(BLACKLIST_LOAD + 1 == NUM_INCIDENT_TYPES, 147 add_support_for_new_types); 148 NOTREACHED(); 149 break; 150 } 151 return state; 152 } 153 154 // Returns true if the incident described by |state| has already been reported 155 // based on the bookkeeping in the |incidents_sent| preference dictionary. 156 bool IncidentHasBeenReported(const base::DictionaryValue* incidents_sent, 157 const PersistentIncidentState& state) { 158 const base::DictionaryValue* type_dict = NULL; 159 std::string digest_string; 160 return (incidents_sent && 161 incidents_sent->GetDictionaryWithoutPathExpansion( 162 base::IntToString(state.type), &type_dict) && 163 type_dict->GetStringWithoutPathExpansion(state.key, &digest_string) && 164 digest_string == base::UintToString(state.digest)); 165 } 166 167 // Marks the incidents described by |states| as having been reported 168 // in |incidents_set|. 169 void MarkIncidentsAsReported(const std::vector<PersistentIncidentState>& states, 170 base::DictionaryValue* incidents_sent) { 171 for (size_t i = 0; i < states.size(); ++i) { 172 const PersistentIncidentState& data = states[i]; 173 base::DictionaryValue* type_dict = NULL; 174 const std::string type_string(base::IntToString(data.type)); 175 if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string, 176 &type_dict)) { 177 type_dict = new base::DictionaryValue(); 178 incidents_sent->SetWithoutPathExpansion(type_string, type_dict); 179 } 180 type_dict->SetStringWithoutPathExpansion(data.key, 181 base::UintToString(data.digest)); 182 } 183 } 184 185 // Runs |callback| on the thread to which |thread_runner| belongs. The callback 186 // is run immediately if this function is called on |thread_runner|'s thread. 187 void AddIncidentOnOriginThread( 188 const AddIncidentCallback& callback, 189 scoped_refptr<base::SingleThreadTaskRunner> thread_runner, 190 scoped_ptr<ClientIncidentReport_IncidentData> incident) { 191 if (thread_runner->BelongsToCurrentThread()) 192 callback.Run(incident.Pass()); 193 else 194 thread_runner->PostTask(FROM_HERE, 195 base::Bind(callback, base::Passed(&incident))); 196 } 197 198 } // namespace 199 200 struct IncidentReportingService::ProfileContext { 201 ProfileContext(); 202 ~ProfileContext(); 203 204 // The incidents collected for this profile pending creation and/or upload. 205 ScopedVector<ClientIncidentReport_IncidentData> incidents; 206 207 // False until PROFILE_ADDED notification is received. 208 bool added; 209 210 private: 211 DISALLOW_COPY_AND_ASSIGN(ProfileContext); 212 }; 213 214 class IncidentReportingService::UploadContext { 215 public: 216 typedef std::map<Profile*, std::vector<PersistentIncidentState> > 217 PersistentIncidentStateCollection; 218 219 explicit UploadContext(scoped_ptr<ClientIncidentReport> report); 220 ~UploadContext(); 221 222 // The report being uploaded. 223 scoped_ptr<ClientIncidentReport> report; 224 225 // The uploader in use. This is NULL until the CSD killswitch is checked. 226 scoped_ptr<IncidentReportUploader> uploader; 227 228 // A mapping of profiles to the data to be persisted upon successful upload. 229 PersistentIncidentStateCollection profiles_to_state; 230 231 private: 232 DISALLOW_COPY_AND_ASSIGN(UploadContext); 233 }; 234 235 IncidentReportingService::ProfileContext::ProfileContext() : added() { 236 } 237 238 IncidentReportingService::ProfileContext::~ProfileContext() { 239 } 240 241 IncidentReportingService::UploadContext::UploadContext( 242 scoped_ptr<ClientIncidentReport> report) 243 : report(report.Pass()) { 244 } 245 246 IncidentReportingService::UploadContext::~UploadContext() { 247 } 248 249 IncidentReportingService::IncidentReportingService( 250 SafeBrowsingService* safe_browsing_service, 251 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter) 252 : database_manager_(safe_browsing_service ? 253 safe_browsing_service->database_manager() : NULL), 254 url_request_context_getter_(request_context_getter), 255 collect_environment_data_fn_(&CollectEnvironmentData), 256 environment_collection_task_runner_( 257 content::BrowserThread::GetBlockingPool() 258 ->GetTaskRunnerWithShutdownBehavior( 259 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), 260 environment_collection_pending_(), 261 collation_timeout_pending_(), 262 collation_timer_(FROM_HERE, 263 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), 264 this, 265 &IncidentReportingService::OnCollationTimeout), 266 delayed_analysis_callbacks_( 267 base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs), 268 content::BrowserThread::GetBlockingPool() 269 ->GetTaskRunnerWithShutdownBehavior( 270 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), 271 receiver_weak_ptr_factory_(this), 272 weak_ptr_factory_(this) { 273 notification_registrar_.Add(this, 274 chrome::NOTIFICATION_PROFILE_ADDED, 275 content::NotificationService::AllSources()); 276 notification_registrar_.Add(this, 277 chrome::NOTIFICATION_PROFILE_DESTROYED, 278 content::NotificationService::AllSources()); 279 } 280 281 IncidentReportingService::~IncidentReportingService() { 282 CancelIncidentCollection(); 283 284 // Cancel all internal asynchronous tasks. 285 weak_ptr_factory_.InvalidateWeakPtrs(); 286 287 CancelEnvironmentCollection(); 288 CancelDownloadCollection(); 289 CancelAllReportUploads(); 290 291 STLDeleteValues(&profiles_); 292 } 293 294 AddIncidentCallback IncidentReportingService::GetAddIncidentCallback( 295 Profile* profile) { 296 // Force the context to be created so that incidents added before 297 // OnProfileAdded is called are held until the profile's preferences can be 298 // queried. 299 ignore_result(GetOrCreateProfileContext(profile)); 300 301 return base::Bind(&IncidentReportingService::AddIncident, 302 receiver_weak_ptr_factory_.GetWeakPtr(), 303 profile); 304 } 305 306 scoped_ptr<TrackedPreferenceValidationDelegate> 307 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) { 308 DCHECK(thread_checker_.CalledOnValidThread()); 309 310 if (profile->IsOffTheRecord()) 311 return scoped_ptr<TrackedPreferenceValidationDelegate>(); 312 return scoped_ptr<TrackedPreferenceValidationDelegate>( 313 new PreferenceValidationDelegate(GetAddIncidentCallback(profile))); 314 } 315 316 void IncidentReportingService::RegisterDelayedAnalysisCallback( 317 const DelayedAnalysisCallback& callback) { 318 DCHECK(thread_checker_.CalledOnValidThread()); 319 320 // |callback| will be run on the blocking pool, so it will likely run the 321 // AddIncidentCallback there as well. Bounce the run of that callback back to 322 // the current thread via AddIncidentOnOriginThread. 323 delayed_analysis_callbacks_.RegisterCallback( 324 base::Bind(callback, 325 base::Bind(&AddIncidentOnOriginThread, 326 GetAddIncidentCallback(NULL), 327 base::ThreadTaskRunnerHandle::Get()))); 328 329 // Start running the callbacks if any profiles are participating in safe 330 // browsing. If none are now, running will commence if/when a participaing 331 // profile is added. 332 if (FindEligibleProfile()) 333 delayed_analysis_callbacks_.Start(); 334 } 335 336 IncidentReportingService::IncidentReportingService( 337 SafeBrowsingService* safe_browsing_service, 338 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, 339 base::TimeDelta delayed_task_interval, 340 const scoped_refptr<base::TaskRunner>& delayed_task_runner) 341 : database_manager_(safe_browsing_service ? 342 safe_browsing_service->database_manager() : NULL), 343 url_request_context_getter_(request_context_getter), 344 collect_environment_data_fn_(&CollectEnvironmentData), 345 environment_collection_task_runner_( 346 content::BrowserThread::GetBlockingPool() 347 ->GetTaskRunnerWithShutdownBehavior( 348 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)), 349 environment_collection_pending_(), 350 collation_timeout_pending_(), 351 collation_timer_(FROM_HERE, 352 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs), 353 this, 354 &IncidentReportingService::OnCollationTimeout), 355 delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner), 356 receiver_weak_ptr_factory_(this), 357 weak_ptr_factory_(this) { 358 notification_registrar_.Add(this, 359 chrome::NOTIFICATION_PROFILE_ADDED, 360 content::NotificationService::AllSources()); 361 notification_registrar_.Add(this, 362 chrome::NOTIFICATION_PROFILE_DESTROYED, 363 content::NotificationService::AllSources()); 364 } 365 366 void IncidentReportingService::SetCollectEnvironmentHook( 367 CollectEnvironmentDataFn collect_environment_data_hook, 368 const scoped_refptr<base::TaskRunner>& task_runner) { 369 if (collect_environment_data_hook) { 370 collect_environment_data_fn_ = collect_environment_data_hook; 371 environment_collection_task_runner_ = task_runner; 372 } else { 373 collect_environment_data_fn_ = &CollectEnvironmentData; 374 environment_collection_task_runner_ = 375 content::BrowserThread::GetBlockingPool() 376 ->GetTaskRunnerWithShutdownBehavior( 377 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN); 378 } 379 } 380 381 void IncidentReportingService::OnProfileAdded(Profile* profile) { 382 DCHECK(thread_checker_.CalledOnValidThread()); 383 384 // Track the addition of all profiles even when no report is being assembled 385 // so that the service can determine whether or not it can evaluate a 386 // profile's preferences at the time of incident addition. 387 ProfileContext* context = GetOrCreateProfileContext(profile); 388 context->added = true; 389 390 const bool safe_browsing_enabled = 391 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled); 392 393 // Start processing delayed analysis callbacks if this new profile 394 // participates in safe browsing. Start is idempotent, so this is safe even if 395 // they're already running. 396 if (safe_browsing_enabled) 397 delayed_analysis_callbacks_.Start(); 398 399 // Start a new report if this profile participates in safe browsing and there 400 // are process-wide incidents. 401 if (safe_browsing_enabled && GetProfileContext(NULL) && 402 GetProfileContext(NULL)->incidents.size()) { 403 BeginReportProcessing(); 404 } 405 406 // TODO(grt): register for pref change notifications to start delayed analysis 407 // and/or report processing if sb is currently disabled but subsequently 408 // enabled. 409 410 // Nothing else to do if a report is not being assembled. 411 if (!report_) 412 return; 413 414 // Drop all incidents associated with this profile that were received prior to 415 // its addition if the profile is not participating in safe browsing. 416 if (!context->incidents.empty() && !safe_browsing_enabled) { 417 for (size_t i = 0; i < context->incidents.size(); ++i) 418 LogIncidentDataType(DROPPED, *context->incidents[i]); 419 context->incidents.clear(); 420 } 421 422 // Take another stab at finding the most recent download if a report is being 423 // assembled and one hasn't been found yet (the LastDownloadFinder operates 424 // only on profiles that have been added to the ProfileManager). 425 BeginDownloadCollection(); 426 } 427 428 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder( 429 const LastDownloadFinder::LastDownloadCallback& callback) { 430 return LastDownloadFinder::Create(callback).Pass(); 431 } 432 433 scoped_ptr<IncidentReportUploader> IncidentReportingService::StartReportUpload( 434 const IncidentReportUploader::OnResultCallback& callback, 435 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter, 436 const ClientIncidentReport& report) { 437 return IncidentReportUploaderImpl::UploadReport( 438 callback, request_context_getter, report).Pass(); 439 } 440 441 bool IncidentReportingService::IsProcessingReport() const { 442 return report_ != NULL; 443 } 444 445 IncidentReportingService::ProfileContext* 446 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) { 447 ProfileContextCollection::iterator it = 448 profiles_.insert(ProfileContextCollection::value_type(profile, NULL)) 449 .first; 450 if (!it->second) 451 it->second = new ProfileContext(); 452 return it->second; 453 } 454 455 IncidentReportingService::ProfileContext* 456 IncidentReportingService::GetProfileContext(Profile* profile) { 457 ProfileContextCollection::iterator it = profiles_.find(profile); 458 return it == profiles_.end() ? NULL : it->second; 459 } 460 461 void IncidentReportingService::OnProfileDestroyed(Profile* profile) { 462 DCHECK(thread_checker_.CalledOnValidThread()); 463 464 ProfileContextCollection::iterator it = profiles_.find(profile); 465 if (it == profiles_.end()) 466 return; 467 468 // TODO(grt): Persist incidents for upload on future profile load. 469 470 // Forget about this profile. Incidents not yet sent for upload are lost. 471 // No new incidents will be accepted for it. 472 delete it->second; 473 profiles_.erase(it); 474 475 // Remove the association with this profile from all pending uploads. 476 for (size_t i = 0; i < uploads_.size(); ++i) 477 uploads_[i]->profiles_to_state.erase(profile); 478 } 479 480 Profile* IncidentReportingService::FindEligibleProfile() const { 481 Profile* candidate = NULL; 482 for (ProfileContextCollection::const_iterator scan = profiles_.begin(); 483 scan != profiles_.end(); 484 ++scan) { 485 // Skip over profiles that have yet to be added to the profile manager. 486 // This will also skip over the NULL-profile context used to hold 487 // process-wide incidents. 488 if (!scan->second->added) 489 continue; 490 PrefService* prefs = scan->first->GetPrefs(); 491 if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { 492 if (!candidate) 493 candidate = scan->first; 494 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) { 495 candidate = scan->first; 496 break; 497 } 498 } 499 } 500 return candidate; 501 } 502 503 void IncidentReportingService::AddIncident( 504 Profile* profile, 505 scoped_ptr<ClientIncidentReport_IncidentData> incident_data) { 506 DCHECK(thread_checker_.CalledOnValidThread()); 507 DCHECK_EQ(1U, CountIncidents(*incident_data)); 508 509 ProfileContext* context = GetProfileContext(profile); 510 // It is forbidden to call this function with a destroyed profile. 511 DCHECK(context); 512 // If this is a process-wide incident, the context must not indicate that the 513 // profile (which is NULL) has been added to the profile manager. 514 DCHECK(profile || !context->added); 515 516 // Drop the incident immediately if the profile has already been added to the 517 // manager and is not participating in safe browsing. Preference evaluation is 518 // deferred until OnProfileAdded() otherwise. 519 if (context->added && 520 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) { 521 LogIncidentDataType(DROPPED, *incident_data); 522 return; 523 } 524 525 // Provide time to the new incident if the caller didn't do so. 526 if (!incident_data->has_incident_time_msec()) 527 incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime()); 528 529 // Take ownership of the incident. 530 context->incidents.push_back(incident_data.release()); 531 532 // Remember when the first incident for this report arrived. 533 if (first_incident_time_.is_null()) 534 first_incident_time_ = base::Time::Now(); 535 // Log the time between the previous incident and this one. 536 if (!last_incident_time_.is_null()) { 537 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime", 538 base::TimeTicks::Now() - last_incident_time_); 539 } 540 last_incident_time_ = base::TimeTicks::Now(); 541 542 // Persist the incident data. 543 544 // Start assembling a new report if this is the first incident ever or the 545 // first since the last upload. 546 BeginReportProcessing(); 547 } 548 549 void IncidentReportingService::BeginReportProcessing() { 550 DCHECK(thread_checker_.CalledOnValidThread()); 551 552 // Creates a new report if needed. 553 if (!report_) 554 report_.reset(new ClientIncidentReport()); 555 556 // Ensure that collection tasks are running (calls are idempotent). 557 BeginIncidentCollation(); 558 BeginEnvironmentCollection(); 559 BeginDownloadCollection(); 560 } 561 562 void IncidentReportingService::BeginIncidentCollation() { 563 // Restart the delay timer to send the report upon expiration. 564 collation_timeout_pending_ = true; 565 collation_timer_.Reset(); 566 } 567 568 void IncidentReportingService::BeginEnvironmentCollection() { 569 DCHECK(thread_checker_.CalledOnValidThread()); 570 DCHECK(report_); 571 // Nothing to do if environment collection is pending or has already 572 // completed. 573 if (environment_collection_pending_ || report_->has_environment()) 574 return; 575 576 environment_collection_begin_ = base::TimeTicks::Now(); 577 ClientIncidentReport_EnvironmentData* environment_data = 578 new ClientIncidentReport_EnvironmentData(); 579 environment_collection_pending_ = 580 environment_collection_task_runner_->PostTaskAndReply( 581 FROM_HERE, 582 base::Bind(collect_environment_data_fn_, environment_data), 583 base::Bind(&IncidentReportingService::OnEnvironmentDataCollected, 584 weak_ptr_factory_.GetWeakPtr(), 585 base::Passed(make_scoped_ptr(environment_data)))); 586 587 // Posting the task will fail if the runner has been shut down. This should 588 // never happen since the blocking pool is shut down after this service. 589 DCHECK(environment_collection_pending_); 590 } 591 592 bool IncidentReportingService::WaitingForEnvironmentCollection() { 593 return environment_collection_pending_; 594 } 595 596 void IncidentReportingService::CancelEnvironmentCollection() { 597 environment_collection_begin_ = base::TimeTicks(); 598 environment_collection_pending_ = false; 599 if (report_) 600 report_->clear_environment(); 601 } 602 603 void IncidentReportingService::OnEnvironmentDataCollected( 604 scoped_ptr<ClientIncidentReport_EnvironmentData> environment_data) { 605 DCHECK(thread_checker_.CalledOnValidThread()); 606 DCHECK(environment_collection_pending_); 607 DCHECK(report_ && !report_->has_environment()); 608 environment_collection_pending_ = false; 609 610 // CurrentProcessInfo::CreationTime() is missing on some platforms. 611 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX) 612 base::TimeDelta uptime = 613 first_incident_time_ - base::CurrentProcessInfo::CreationTime(); 614 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds()); 615 #endif 616 617 report_->set_allocated_environment(environment_data.release()); 618 619 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime", 620 base::TimeTicks::Now() - environment_collection_begin_); 621 environment_collection_begin_ = base::TimeTicks(); 622 623 UploadIfCollectionComplete(); 624 } 625 626 bool IncidentReportingService::WaitingToCollateIncidents() { 627 return collation_timeout_pending_; 628 } 629 630 void IncidentReportingService::CancelIncidentCollection() { 631 collation_timeout_pending_ = false; 632 last_incident_time_ = base::TimeTicks(); 633 report_.reset(); 634 } 635 636 void IncidentReportingService::OnCollationTimeout() { 637 DCHECK(thread_checker_.CalledOnValidThread()); 638 639 // Exit early if collection was cancelled. 640 if (!collation_timeout_pending_) 641 return; 642 643 // Wait another round if profile-bound incidents have come in from a profile 644 // that has yet to complete creation. 645 for (ProfileContextCollection::iterator scan = profiles_.begin(); 646 scan != profiles_.end(); 647 ++scan) { 648 if (scan->first && !scan->second->added && 649 !scan->second->incidents.empty()) { 650 collation_timer_.Reset(); 651 return; 652 } 653 } 654 655 collation_timeout_pending_ = false; 656 657 UploadIfCollectionComplete(); 658 } 659 660 void IncidentReportingService::BeginDownloadCollection() { 661 DCHECK(thread_checker_.CalledOnValidThread()); 662 DCHECK(report_); 663 // Nothing to do if a search for the most recent download is already pending 664 // or if one has already been found. 665 if (last_download_finder_ || report_->has_download()) 666 return; 667 668 last_download_begin_ = base::TimeTicks::Now(); 669 last_download_finder_ = CreateDownloadFinder( 670 base::Bind(&IncidentReportingService::OnLastDownloadFound, 671 weak_ptr_factory_.GetWeakPtr())); 672 // No instance is returned if there are no eligible loaded profiles. Another 673 // search will be attempted in OnProfileAdded() if another profile appears on 674 // the scene. 675 if (!last_download_finder_) 676 last_download_begin_ = base::TimeTicks(); 677 } 678 679 bool IncidentReportingService::WaitingForMostRecentDownload() { 680 DCHECK(report_); // Only call this when a report is being assembled. 681 // The easy case: not waiting if a download has already been found. 682 if (report_->has_download()) 683 return false; 684 // The next easy case: waiting if the finder is operating. 685 if (last_download_finder_) 686 return true; 687 // The harder case: waiting if a non-NULL profile has not yet been added. 688 for (ProfileContextCollection::const_iterator scan = profiles_.begin(); 689 scan != profiles_.end(); 690 ++scan) { 691 if (scan->first && !scan->second->added) 692 return true; 693 } 694 // There is no most recent download and there's nothing more to wait for. 695 return false; 696 } 697 698 void IncidentReportingService::CancelDownloadCollection() { 699 last_download_finder_.reset(); 700 last_download_begin_ = base::TimeTicks(); 701 if (report_) 702 report_->clear_download(); 703 } 704 705 void IncidentReportingService::OnLastDownloadFound( 706 scoped_ptr<ClientIncidentReport_DownloadDetails> last_download) { 707 DCHECK(thread_checker_.CalledOnValidThread()); 708 DCHECK(report_); 709 710 UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime", 711 base::TimeTicks::Now() - last_download_begin_); 712 last_download_begin_ = base::TimeTicks(); 713 714 // Harvest the finder. 715 last_download_finder_.reset(); 716 717 if (last_download) 718 report_->set_allocated_download(last_download.release()); 719 720 UploadIfCollectionComplete(); 721 } 722 723 void IncidentReportingService::UploadIfCollectionComplete() { 724 DCHECK(report_); 725 // Bail out if there are still outstanding collection tasks. Completion of any 726 // of these will start another upload attempt. 727 if (WaitingForEnvironmentCollection() || 728 WaitingToCollateIncidents() || 729 WaitingForMostRecentDownload()) { 730 return; 731 } 732 733 // Take ownership of the report and clear things for future reports. 734 scoped_ptr<ClientIncidentReport> report(report_.Pass()); 735 first_incident_time_ = base::Time(); 736 last_incident_time_ = base::TimeTicks(); 737 738 // Drop the report if no executable download was found. 739 if (!report->has_download()) { 740 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult", 741 IncidentReportUploader::UPLOAD_NO_DOWNLOAD, 742 IncidentReportUploader::NUM_UPLOAD_RESULTS); 743 return; 744 } 745 746 ClientIncidentReport_EnvironmentData_Process* process = 747 report->mutable_environment()->mutable_process(); 748 749 // Not all platforms have a metrics reporting preference. 750 if (g_browser_process->local_state()->FindPreference( 751 prefs::kMetricsReportingEnabled)) { 752 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean( 753 prefs::kMetricsReportingEnabled)); 754 } 755 756 // Find the profile that benefits from the strongest protections. 757 Profile* eligible_profile = FindEligibleProfile(); 758 process->set_extended_consent( 759 eligible_profile ? eligible_profile->GetPrefs()->GetBoolean( 760 prefs::kSafeBrowsingExtendedReportingEnabled) : 761 false); 762 763 // Associate process-wide incidents with the profile that benefits from the 764 // strongest safe browsing protections. 765 ProfileContext* null_context = GetProfileContext(NULL); 766 if (null_context && !null_context->incidents.empty() && eligible_profile) { 767 ProfileContext* eligible_context = GetProfileContext(eligible_profile); 768 // Move the incidents to the target context. 769 eligible_context->incidents.insert(eligible_context->incidents.end(), 770 null_context->incidents.begin(), 771 null_context->incidents.end()); 772 null_context->incidents.weak_clear(); 773 } 774 775 // Collect incidents across all profiles participating in safe browsing. Drop 776 // incidents if the profile stopped participating before collection completed. 777 // Prune previously submitted incidents. 778 // Associate the profiles and their incident data with the upload. 779 size_t prune_count = 0; 780 UploadContext::PersistentIncidentStateCollection profiles_to_state; 781 for (ProfileContextCollection::iterator scan = profiles_.begin(); 782 scan != profiles_.end(); 783 ++scan) { 784 // Bypass process-wide incidents that have not yet been associated with a 785 // profile. 786 if (!scan->first) 787 continue; 788 PrefService* prefs = scan->first->GetPrefs(); 789 ProfileContext* context = scan->second; 790 if (context->incidents.empty()) 791 continue; 792 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) { 793 for (size_t i = 0; i < context->incidents.size(); ++i) { 794 LogIncidentDataType(DROPPED, *context->incidents[i]); 795 } 796 context->incidents.clear(); 797 continue; 798 } 799 std::vector<PersistentIncidentState> states; 800 const base::DictionaryValue* incidents_sent = 801 prefs->GetDictionary(prefs::kSafeBrowsingIncidentsSent); 802 // Prep persistent data and prune any incidents already sent. 803 for (size_t i = 0; i < context->incidents.size(); ++i) { 804 ClientIncidentReport_IncidentData* incident = context->incidents[i]; 805 const PersistentIncidentState state = ComputeIncidentState(*incident); 806 if (IncidentHasBeenReported(incidents_sent, state)) { 807 ++prune_count; 808 delete context->incidents[i]; 809 context->incidents[i] = NULL; 810 } else { 811 states.push_back(state); 812 } 813 } 814 if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) { 815 // Prune all incidents as if they had been reported, migrating to the new 816 // technique. TODO(grt): remove this branch after it has shipped. 817 for (size_t i = 0; i < context->incidents.size(); ++i) { 818 if (context->incidents[i]) 819 ++prune_count; 820 } 821 context->incidents.clear(); 822 prefs->ClearPref(prefs::kSafeBrowsingIncidentReportSent); 823 DictionaryPrefUpdate pref_update(prefs, 824 prefs::kSafeBrowsingIncidentsSent); 825 MarkIncidentsAsReported(states, pref_update.Get()); 826 } else { 827 for (size_t i = 0; i < context->incidents.size(); ++i) { 828 ClientIncidentReport_IncidentData* incident = context->incidents[i]; 829 if (incident) { 830 LogIncidentDataType(ACCEPTED, *incident); 831 // Ownership of the incident is passed to the report. 832 report->mutable_incident()->AddAllocated(incident); 833 } 834 } 835 context->incidents.weak_clear(); 836 std::vector<PersistentIncidentState>& profile_states = 837 profiles_to_state[scan->first]; 838 profile_states.swap(states); 839 } 840 } 841 842 const int count = report->incident_size(); 843 // Abandon the request if all incidents were dropped with none pruned. 844 if (!count && !prune_count) 845 return; 846 847 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count); 848 849 { 850 double prune_pct = static_cast<double>(prune_count); 851 prune_pct = prune_pct * 100.0 / (count + prune_count); 852 prune_pct = round(prune_pct); 853 UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct)); 854 } 855 // Abandon the report if all incidents were pruned. 856 if (!count) 857 return; 858 859 scoped_ptr<UploadContext> context(new UploadContext(report.Pass())); 860 context->profiles_to_state.swap(profiles_to_state); 861 if (!database_manager_.get()) { 862 // No database manager during testing. Take ownership of the context and 863 // continue processing. 864 UploadContext* temp_context = context.get(); 865 uploads_.push_back(context.release()); 866 IncidentReportingService::OnKillSwitchResult(temp_context, false); 867 } else { 868 if (content::BrowserThread::PostTaskAndReplyWithResult( 869 content::BrowserThread::IO, 870 FROM_HERE, 871 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn, 872 database_manager_), 873 base::Bind(&IncidentReportingService::OnKillSwitchResult, 874 weak_ptr_factory_.GetWeakPtr(), 875 context.get()))) { 876 uploads_.push_back(context.release()); 877 } // else should not happen. Let the context be deleted automatically. 878 } 879 } 880 881 void IncidentReportingService::CancelAllReportUploads() { 882 for (size_t i = 0; i < uploads_.size(); ++i) { 883 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult", 884 IncidentReportUploader::UPLOAD_CANCELLED, 885 IncidentReportUploader::NUM_UPLOAD_RESULTS); 886 } 887 uploads_.clear(); 888 } 889 890 void IncidentReportingService::OnKillSwitchResult(UploadContext* context, 891 bool is_killswitch_on) { 892 DCHECK(thread_checker_.CalledOnValidThread()); 893 if (!is_killswitch_on) { 894 // Initiate the upload. 895 context->uploader = 896 StartReportUpload( 897 base::Bind(&IncidentReportingService::OnReportUploadResult, 898 weak_ptr_factory_.GetWeakPtr(), 899 context), 900 url_request_context_getter_, 901 *context->report).Pass(); 902 if (!context->uploader) { 903 OnReportUploadResult(context, 904 IncidentReportUploader::UPLOAD_INVALID_REQUEST, 905 scoped_ptr<ClientIncidentResponse>()); 906 } 907 } else { 908 OnReportUploadResult(context, 909 IncidentReportUploader::UPLOAD_SUPPRESSED, 910 scoped_ptr<ClientIncidentResponse>()); 911 } 912 } 913 914 void IncidentReportingService::HandleResponse(const UploadContext& context) { 915 for (UploadContext::PersistentIncidentStateCollection::const_iterator scan = 916 context.profiles_to_state.begin(); 917 scan != context.profiles_to_state.end(); 918 ++scan) { 919 DictionaryPrefUpdate pref_update(scan->first->GetPrefs(), 920 prefs::kSafeBrowsingIncidentsSent); 921 MarkIncidentsAsReported(scan->second, pref_update.Get()); 922 } 923 } 924 925 void IncidentReportingService::OnReportUploadResult( 926 UploadContext* context, 927 IncidentReportUploader::Result result, 928 scoped_ptr<ClientIncidentResponse> response) { 929 DCHECK(thread_checker_.CalledOnValidThread()); 930 931 UMA_HISTOGRAM_ENUMERATION( 932 "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS); 933 934 // The upload is no longer outstanding, so take ownership of the context (from 935 // the collection of outstanding uploads) in this scope. 936 ScopedVector<UploadContext>::iterator it( 937 std::find(uploads_.begin(), uploads_.end(), context)); 938 DCHECK(it != uploads_.end()); 939 scoped_ptr<UploadContext> upload(context); // == *it 940 *it = uploads_.back(); 941 uploads_.weak_erase(uploads_.end() - 1); 942 943 if (result == IncidentReportUploader::UPLOAD_SUCCESS) 944 HandleResponse(*upload); 945 // else retry? 946 } 947 948 void IncidentReportingService::Observe( 949 int type, 950 const content::NotificationSource& source, 951 const content::NotificationDetails& details) { 952 switch (type) { 953 case chrome::NOTIFICATION_PROFILE_ADDED: { 954 Profile* profile = content::Source<Profile>(source).ptr(); 955 if (!profile->IsOffTheRecord()) 956 OnProfileAdded(profile); 957 break; 958 } 959 case chrome::NOTIFICATION_PROFILE_DESTROYED: { 960 Profile* profile = content::Source<Profile>(source).ptr(); 961 if (!profile->IsOffTheRecord()) 962 OnProfileDestroyed(profile); 963 break; 964 } 965 default: 966 break; 967 } 968 } 969 970 } // namespace safe_browsing 971