Elim cr-checkbox
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / incident_reporting_service.cc
blob1c691b272621969be2bba5da55b66da91420f2d3
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.
5 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
7 #include <math.h>
9 #include <algorithm>
10 #include <vector>
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/process/process_info.h"
15 #include "base/single_thread_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/thread_task_runner_handle.h"
18 #include "base/threading/sequenced_worker_pool.h"
19 #include "chrome/browser/chrome_notification_types.h"
20 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/safe_browsing/database_manager.h"
23 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h"
24 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
25 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
26 #include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h"
27 #include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h"
28 #include "chrome/browser/safe_browsing/incident_reporting/state_store.h"
29 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/safe_browsing/csd.pb.h"
32 #include "components/user_prefs/tracked/tracked_preference_validation_delegate.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "net/url_request/url_request_context_getter.h"
37 namespace safe_browsing {
39 namespace {
41 // The action taken for an incident; used for user metrics (see
42 // LogIncidentDataType).
43 enum IncidentDisposition {
44 RECEIVED,
45 DROPPED,
46 ACCEPTED,
47 PRUNED,
48 DISCARDED,
49 NO_DOWNLOAD,
50 NUM_DISPOSITIONS
53 // The state persisted for a specific instance of an incident to enable pruning
54 // of previously-reported incidents.
55 struct PersistentIncidentState {
56 // The type of the incident.
57 IncidentType type;
59 // The key for a specific instance of an incident.
60 std::string key;
62 // A hash digest representing a specific instance of an incident.
63 uint32_t digest;
66 // The amount of time the service will wait to collate incidents.
67 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute
69 // The amount of time between running delayed analysis callbacks.
70 const int64 kDefaultCallbackIntervalMs = 1000 * 20;
72 // Logs the type of incident in |incident_data| to a user metrics histogram.
73 void LogIncidentDataType(IncidentDisposition disposition,
74 const Incident& incident) {
75 static const char* const kHistogramNames[] = {
76 "SBIRS.ReceivedIncident",
77 "SBIRS.DroppedIncident",
78 "SBIRS.Incident",
79 "SBIRS.PrunedIncident",
80 "SBIRS.DiscardedIncident",
81 "SBIRS.NoDownloadIncident",
83 static_assert(arraysize(kHistogramNames) == NUM_DISPOSITIONS,
84 "Keep kHistogramNames in sync with enum IncidentDisposition.");
85 DCHECK_GE(disposition, 0);
86 DCHECK_LT(disposition, NUM_DISPOSITIONS);
87 base::LinearHistogram::FactoryGet(
88 kHistogramNames[disposition],
89 1, // minimum
90 static_cast<int32_t>(IncidentType::NUM_TYPES), // maximum
91 static_cast<size_t>(IncidentType::NUM_TYPES) + 1, // bucket_count
92 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(
93 static_cast<int32_t>(incident.GetType()));
96 // Computes the persistent state for an incident.
97 PersistentIncidentState ComputeIncidentState(const Incident& incident) {
98 PersistentIncidentState state = {
99 incident.GetType(),
100 incident.GetKey(),
101 incident.ComputeDigest(),
103 return state;
106 } // namespace
108 struct IncidentReportingService::ProfileContext {
109 ProfileContext();
110 ~ProfileContext();
112 // The incidents collected for this profile pending creation and/or upload.
113 // Will contain null values for pruned incidents.
114 ScopedVector<Incident> incidents;
116 // State storage for this profile; null until PROFILE_ADDED notification is
117 // received.
118 scoped_ptr<StateStore> state_store;
120 // False until PROFILE_ADDED notification is received.
121 bool added;
123 private:
124 DISALLOW_COPY_AND_ASSIGN(ProfileContext);
127 class IncidentReportingService::UploadContext {
128 public:
129 typedef std::map<ProfileContext*, std::vector<PersistentIncidentState>>
130 PersistentIncidentStateCollection;
132 explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
133 ~UploadContext();
135 // The report being uploaded.
136 scoped_ptr<ClientIncidentReport> report;
138 // The uploader in use. This is NULL until the CSD killswitch is checked.
139 scoped_ptr<IncidentReportUploader> uploader;
141 // A mapping of profile contexts to the data to be persisted upon successful
142 // upload.
143 PersistentIncidentStateCollection profiles_to_state;
145 private:
146 DISALLOW_COPY_AND_ASSIGN(UploadContext);
149 // An IncidentReceiver that is weakly-bound to the service and transparently
150 // bounces process-wide incidents back to the main thread for handling.
151 class IncidentReportingService::Receiver : public IncidentReceiver {
152 public:
153 explicit Receiver(const base::WeakPtr<IncidentReportingService>& service);
154 ~Receiver() override;
156 // IncidentReceiver methods:
157 void AddIncidentForProfile(Profile* profile,
158 scoped_ptr<Incident> incident) override;
159 void AddIncidentForProcess(scoped_ptr<Incident> incident) override;
161 private:
162 static void AddIncidentOnMainThread(
163 const base::WeakPtr<IncidentReportingService>& service,
164 Profile* profile,
165 scoped_ptr<Incident> incident);
167 base::WeakPtr<IncidentReportingService> service_;
168 scoped_refptr<base::SingleThreadTaskRunner> thread_runner_;
170 DISALLOW_COPY_AND_ASSIGN(Receiver);
173 IncidentReportingService::Receiver::Receiver(
174 const base::WeakPtr<IncidentReportingService>& service)
175 : service_(service),
176 thread_runner_(base::ThreadTaskRunnerHandle::Get()) {
179 IncidentReportingService::Receiver::~Receiver() {
182 void IncidentReportingService::Receiver::AddIncidentForProfile(
183 Profile* profile,
184 scoped_ptr<Incident> incident) {
185 DCHECK(thread_runner_->BelongsToCurrentThread());
186 DCHECK(profile);
187 AddIncidentOnMainThread(service_, profile, incident.Pass());
190 void IncidentReportingService::Receiver::AddIncidentForProcess(
191 scoped_ptr<Incident> incident) {
192 if (thread_runner_->BelongsToCurrentThread()) {
193 AddIncidentOnMainThread(service_, nullptr, incident.Pass());
194 } else if (!thread_runner_->PostTask(
195 FROM_HERE,
196 base::Bind(&IncidentReportingService::Receiver::AddIncidentOnMainThread,
197 service_, nullptr, base::Passed(&incident)))) {
198 LogIncidentDataType(DISCARDED, *incident);
202 // static
203 void IncidentReportingService::Receiver::AddIncidentOnMainThread(
204 const base::WeakPtr<IncidentReportingService>& service,
205 Profile* profile,
206 scoped_ptr<Incident> incident) {
207 if (service)
208 service->AddIncident(profile, incident.Pass());
209 else
210 LogIncidentDataType(DISCARDED, *incident);
213 IncidentReportingService::ProfileContext::ProfileContext() : added(false) {
216 IncidentReportingService::ProfileContext::~ProfileContext() {
217 for (Incident* incident : incidents) {
218 if (incident)
219 LogIncidentDataType(DISCARDED, *incident);
223 IncidentReportingService::UploadContext::UploadContext(
224 scoped_ptr<ClientIncidentReport> report)
225 : report(report.Pass()) {
228 IncidentReportingService::UploadContext::~UploadContext() {
231 IncidentReportingService::IncidentReportingService(
232 SafeBrowsingService* safe_browsing_service,
233 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter)
234 : database_manager_(safe_browsing_service
235 ? safe_browsing_service->database_manager()
236 : NULL),
237 url_request_context_getter_(request_context_getter),
238 collect_environment_data_fn_(&CollectEnvironmentData),
239 environment_collection_task_runner_(
240 content::BrowserThread::GetBlockingPool()
241 ->GetTaskRunnerWithShutdownBehavior(
242 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
243 environment_collection_pending_(),
244 collation_timeout_pending_(),
245 collation_timer_(FROM_HERE,
246 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
247 this,
248 &IncidentReportingService::OnCollationTimeout),
249 delayed_analysis_callbacks_(
250 base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs),
251 content::BrowserThread::GetBlockingPool()
252 ->GetTaskRunnerWithShutdownBehavior(
253 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
254 download_metadata_manager_(content::BrowserThread::GetBlockingPool()),
255 receiver_weak_ptr_factory_(this),
256 weak_ptr_factory_(this) {
257 notification_registrar_.Add(this,
258 chrome::NOTIFICATION_PROFILE_ADDED,
259 content::NotificationService::AllSources());
260 notification_registrar_.Add(this,
261 chrome::NOTIFICATION_PROFILE_DESTROYED,
262 content::NotificationService::AllSources());
263 DownloadProtectionService* download_protection_service =
264 (safe_browsing_service ?
265 safe_browsing_service->download_protection_service() :
266 NULL);
267 if (download_protection_service) {
268 client_download_request_subscription_ =
269 download_protection_service->RegisterClientDownloadRequestCallback(
270 base::Bind(&IncidentReportingService::OnClientDownloadRequest,
271 base::Unretained(this)));
275 IncidentReportingService::~IncidentReportingService() {
276 DCHECK(thread_checker_.CalledOnValidThread());
277 CancelIncidentCollection();
279 // Cancel all internal asynchronous tasks.
280 weak_ptr_factory_.InvalidateWeakPtrs();
282 CancelEnvironmentCollection();
283 CancelDownloadCollection();
284 CancelAllReportUploads();
286 STLDeleteValues(&profiles_);
289 scoped_ptr<IncidentReceiver> IncidentReportingService::GetIncidentReceiver() {
290 return make_scoped_ptr(new Receiver(receiver_weak_ptr_factory_.GetWeakPtr()));
293 scoped_ptr<TrackedPreferenceValidationDelegate>
294 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) {
295 DCHECK(thread_checker_.CalledOnValidThread());
297 if (profile->IsOffTheRecord())
298 return scoped_ptr<TrackedPreferenceValidationDelegate>();
299 return scoped_ptr<TrackedPreferenceValidationDelegate>(
300 new PreferenceValidationDelegate(profile, GetIncidentReceiver()));
303 void IncidentReportingService::RegisterDelayedAnalysisCallback(
304 const DelayedAnalysisCallback& callback) {
305 DCHECK(thread_checker_.CalledOnValidThread());
307 // |callback| will be run on the blocking pool. The receiver will bounce back
308 // to the origin thread if needed.
309 delayed_analysis_callbacks_.RegisterCallback(
310 base::Bind(callback, base::Passed(GetIncidentReceiver())));
312 // Start running the callbacks if any profiles are participating in safe
313 // browsing. If none are now, running will commence if/when a participaing
314 // profile is added.
315 if (FindEligibleProfile())
316 delayed_analysis_callbacks_.Start();
319 void IncidentReportingService::AddDownloadManager(
320 content::DownloadManager* download_manager) {
321 download_metadata_manager_.AddDownloadManager(download_manager);
324 IncidentReportingService::IncidentReportingService(
325 SafeBrowsingService* safe_browsing_service,
326 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
327 base::TimeDelta delayed_task_interval,
328 const scoped_refptr<base::TaskRunner>& delayed_task_runner)
329 : database_manager_(safe_browsing_service
330 ? safe_browsing_service->database_manager()
331 : NULL),
332 url_request_context_getter_(request_context_getter),
333 collect_environment_data_fn_(&CollectEnvironmentData),
334 environment_collection_task_runner_(
335 content::BrowserThread::GetBlockingPool()
336 ->GetTaskRunnerWithShutdownBehavior(
337 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
338 environment_collection_pending_(),
339 collation_timeout_pending_(),
340 collation_timer_(FROM_HERE,
341 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
342 this,
343 &IncidentReportingService::OnCollationTimeout),
344 delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner),
345 download_metadata_manager_(content::BrowserThread::GetBlockingPool()),
346 receiver_weak_ptr_factory_(this),
347 weak_ptr_factory_(this) {
348 notification_registrar_.Add(this,
349 chrome::NOTIFICATION_PROFILE_ADDED,
350 content::NotificationService::AllSources());
351 notification_registrar_.Add(this,
352 chrome::NOTIFICATION_PROFILE_DESTROYED,
353 content::NotificationService::AllSources());
356 void IncidentReportingService::SetCollectEnvironmentHook(
357 CollectEnvironmentDataFn collect_environment_data_hook,
358 const scoped_refptr<base::TaskRunner>& task_runner) {
359 if (collect_environment_data_hook) {
360 collect_environment_data_fn_ = collect_environment_data_hook;
361 environment_collection_task_runner_ = task_runner;
362 } else {
363 collect_environment_data_fn_ = &CollectEnvironmentData;
364 environment_collection_task_runner_ =
365 content::BrowserThread::GetBlockingPool()
366 ->GetTaskRunnerWithShutdownBehavior(
367 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
371 void IncidentReportingService::OnProfileAdded(Profile* profile) {
372 DCHECK(thread_checker_.CalledOnValidThread());
374 // Track the addition of all profiles even when no report is being assembled
375 // so that the service can determine whether or not it can evaluate a
376 // profile's preferences at the time of incident addition.
377 ProfileContext* context = GetOrCreateProfileContext(profile);
378 DCHECK(!context->added);
379 context->added = true;
380 context->state_store.reset(new StateStore(profile));
382 const bool safe_browsing_enabled =
383 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
385 // Start processing delayed analysis callbacks if this new profile
386 // participates in safe browsing. Start is idempotent, so this is safe even if
387 // they're already running.
388 if (safe_browsing_enabled)
389 delayed_analysis_callbacks_.Start();
391 // Start a new report if this profile participates in safe browsing and there
392 // are process-wide incidents.
393 if (safe_browsing_enabled && GetProfileContext(NULL) &&
394 GetProfileContext(NULL)->incidents.size()) {
395 BeginReportProcessing();
398 // TODO(grt): register for pref change notifications to start delayed analysis
399 // and/or report processing if sb is currently disabled but subsequently
400 // enabled.
402 // Nothing else to do if a report is not being assembled.
403 if (!report_)
404 return;
406 // Drop all incidents associated with this profile that were received prior to
407 // its addition if the profile is not participating in safe browsing.
408 if (!context->incidents.empty() && !safe_browsing_enabled) {
409 for (Incident* incident : context->incidents)
410 LogIncidentDataType(DROPPED, *incident);
411 context->incidents.clear();
414 // Take another stab at finding the most recent download if a report is being
415 // assembled and one hasn't been found yet (the LastDownloadFinder operates
416 // only on profiles that have been added to the ProfileManager).
417 BeginDownloadCollection();
420 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder(
421 const LastDownloadFinder::LastDownloadCallback& callback) {
422 return LastDownloadFinder::Create(
423 base::Bind(&DownloadMetadataManager::GetDownloadDetails,
424 base::Unretained(&download_metadata_manager_)),
425 callback).Pass();
428 scoped_ptr<IncidentReportUploader> IncidentReportingService::StartReportUpload(
429 const IncidentReportUploader::OnResultCallback& callback,
430 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
431 const ClientIncidentReport& report) {
432 return IncidentReportUploaderImpl::UploadReport(
433 callback, request_context_getter, report).Pass();
436 bool IncidentReportingService::IsProcessingReport() const {
437 return report_ != NULL;
440 IncidentReportingService::ProfileContext*
441 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
442 ProfileContextCollection::iterator it =
443 profiles_.insert(ProfileContextCollection::value_type(profile, NULL))
444 .first;
445 if (!it->second)
446 it->second = new ProfileContext();
447 return it->second;
450 IncidentReportingService::ProfileContext*
451 IncidentReportingService::GetProfileContext(Profile* profile) {
452 ProfileContextCollection::iterator it = profiles_.find(profile);
453 return it == profiles_.end() ? NULL : it->second;
456 void IncidentReportingService::OnProfileDestroyed(Profile* profile) {
457 DCHECK(thread_checker_.CalledOnValidThread());
459 ProfileContextCollection::iterator it = profiles_.find(profile);
460 if (it == profiles_.end())
461 return;
463 // Take ownership of the context.
464 scoped_ptr<ProfileContext> context(it->second);
465 it->second = nullptr;
467 // TODO(grt): Persist incidents for upload on future profile load.
469 // Remove the association with this profile context from all pending uploads.
470 for (UploadContext* upload : uploads_)
471 upload->profiles_to_state.erase(context.get());
473 // Forget about this profile. Incidents not yet sent for upload are lost.
474 // No new incidents will be accepted for it.
475 profiles_.erase(it);
478 Profile* IncidentReportingService::FindEligibleProfile() const {
479 Profile* candidate = NULL;
480 for (ProfileContextCollection::const_iterator scan = profiles_.begin();
481 scan != profiles_.end();
482 ++scan) {
483 // Skip over profiles that have yet to be added to the profile manager.
484 // This will also skip over the NULL-profile context used to hold
485 // process-wide incidents.
486 if (!scan->second->added)
487 continue;
488 PrefService* prefs = scan->first->GetPrefs();
489 if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
490 if (!candidate)
491 candidate = scan->first;
492 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
493 candidate = scan->first;
494 break;
498 return candidate;
501 void IncidentReportingService::AddIncident(Profile* profile,
502 scoped_ptr<Incident> incident) {
503 DCHECK(thread_checker_.CalledOnValidThread());
505 // Ignore incidents from off-the-record profiles.
506 if (profile && profile->IsOffTheRecord())
507 return;
509 ProfileContext* context = GetOrCreateProfileContext(profile);
510 // If this is a process-wide incident, the context must not indicate that the
511 // profile (which is NULL) has been added to the profile manager.
512 DCHECK(profile || !context->added);
514 LogIncidentDataType(RECEIVED, *incident);
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);
522 return;
525 // Take ownership of the incident.
526 context->incidents.push_back(incident.release());
528 // Remember when the first incident for this report arrived.
529 if (first_incident_time_.is_null())
530 first_incident_time_ = base::Time::Now();
531 // Log the time between the previous incident and this one.
532 if (!last_incident_time_.is_null()) {
533 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime",
534 base::TimeTicks::Now() - last_incident_time_);
536 last_incident_time_ = base::TimeTicks::Now();
538 // Persist the incident data.
540 // Start assembling a new report if this is the first incident ever or the
541 // first since the last upload.
542 BeginReportProcessing();
545 void IncidentReportingService::BeginReportProcessing() {
546 DCHECK(thread_checker_.CalledOnValidThread());
548 // Creates a new report if needed.
549 if (!report_)
550 report_.reset(new ClientIncidentReport());
552 // Ensure that collection tasks are running (calls are idempotent).
553 BeginIncidentCollation();
554 BeginEnvironmentCollection();
555 BeginDownloadCollection();
558 void IncidentReportingService::BeginIncidentCollation() {
559 // Restart the delay timer to send the report upon expiration.
560 collation_timeout_pending_ = true;
561 collation_timer_.Reset();
564 void IncidentReportingService::BeginEnvironmentCollection() {
565 DCHECK(thread_checker_.CalledOnValidThread());
566 DCHECK(report_);
567 // Nothing to do if environment collection is pending or has already
568 // completed.
569 if (environment_collection_pending_ || report_->has_environment())
570 return;
572 environment_collection_begin_ = base::TimeTicks::Now();
573 ClientIncidentReport_EnvironmentData* environment_data =
574 new ClientIncidentReport_EnvironmentData();
575 environment_collection_pending_ =
576 environment_collection_task_runner_->PostTaskAndReply(
577 FROM_HERE,
578 base::Bind(collect_environment_data_fn_, environment_data),
579 base::Bind(&IncidentReportingService::OnEnvironmentDataCollected,
580 weak_ptr_factory_.GetWeakPtr(),
581 base::Passed(make_scoped_ptr(environment_data))));
583 // Posting the task will fail if the runner has been shut down. This should
584 // never happen since the blocking pool is shut down after this service.
585 DCHECK(environment_collection_pending_);
588 bool IncidentReportingService::WaitingForEnvironmentCollection() {
589 return environment_collection_pending_;
592 void IncidentReportingService::CancelEnvironmentCollection() {
593 environment_collection_begin_ = base::TimeTicks();
594 environment_collection_pending_ = false;
595 if (report_)
596 report_->clear_environment();
599 void IncidentReportingService::OnEnvironmentDataCollected(
600 scoped_ptr<ClientIncidentReport_EnvironmentData> environment_data) {
601 DCHECK(thread_checker_.CalledOnValidThread());
602 DCHECK(environment_collection_pending_);
603 DCHECK(report_ && !report_->has_environment());
604 environment_collection_pending_ = false;
606 // CurrentProcessInfo::CreationTime() is missing on some platforms.
607 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
608 base::TimeDelta uptime =
609 first_incident_time_ - base::CurrentProcessInfo::CreationTime();
610 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds());
611 #endif
613 report_->set_allocated_environment(environment_data.release());
615 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime",
616 base::TimeTicks::Now() - environment_collection_begin_);
617 environment_collection_begin_ = base::TimeTicks();
619 UploadIfCollectionComplete();
622 bool IncidentReportingService::WaitingToCollateIncidents() {
623 return collation_timeout_pending_;
626 void IncidentReportingService::CancelIncidentCollection() {
627 collation_timeout_pending_ = false;
628 last_incident_time_ = base::TimeTicks();
629 report_.reset();
632 void IncidentReportingService::OnCollationTimeout() {
633 DCHECK(thread_checker_.CalledOnValidThread());
635 // Exit early if collection was cancelled.
636 if (!collation_timeout_pending_)
637 return;
639 // Wait another round if profile-bound incidents have come in from a profile
640 // that has yet to complete creation.
641 for (ProfileContextCollection::iterator scan = profiles_.begin();
642 scan != profiles_.end();
643 ++scan) {
644 if (scan->first && !scan->second->added &&
645 !scan->second->incidents.empty()) {
646 collation_timer_.Reset();
647 return;
651 collation_timeout_pending_ = false;
653 UploadIfCollectionComplete();
656 void IncidentReportingService::BeginDownloadCollection() {
657 DCHECK(thread_checker_.CalledOnValidThread());
658 DCHECK(report_);
659 // Nothing to do if a search for the most recent download is already pending
660 // or if one has already been found.
661 if (last_download_finder_ || report_->has_download())
662 return;
664 last_download_begin_ = base::TimeTicks::Now();
665 last_download_finder_ = CreateDownloadFinder(
666 base::Bind(&IncidentReportingService::OnLastDownloadFound,
667 weak_ptr_factory_.GetWeakPtr()));
668 // No instance is returned if there are no eligible loaded profiles. Another
669 // search will be attempted in OnProfileAdded() if another profile appears on
670 // the scene.
671 if (!last_download_finder_)
672 last_download_begin_ = base::TimeTicks();
675 bool IncidentReportingService::WaitingForMostRecentDownload() {
676 DCHECK(report_); // Only call this when a report is being assembled.
677 // The easy case: not waiting if a download has already been found.
678 if (report_->has_download())
679 return false;
680 // The next easy case: waiting if the finder is operating.
681 if (last_download_finder_)
682 return true;
683 // The harder case: waiting if a non-NULL profile has not yet been added.
684 for (ProfileContextCollection::const_iterator scan = profiles_.begin();
685 scan != profiles_.end();
686 ++scan) {
687 if (scan->first && !scan->second->added)
688 return true;
690 // There is no most recent download and there's nothing more to wait for.
691 return false;
694 void IncidentReportingService::CancelDownloadCollection() {
695 last_download_finder_.reset();
696 last_download_begin_ = base::TimeTicks();
697 if (report_)
698 report_->clear_download();
701 void IncidentReportingService::OnLastDownloadFound(
702 scoped_ptr<ClientIncidentReport_DownloadDetails> last_download) {
703 DCHECK(thread_checker_.CalledOnValidThread());
704 DCHECK(report_);
706 UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime",
707 base::TimeTicks::Now() - last_download_begin_);
708 last_download_begin_ = base::TimeTicks();
710 // Harvest the finder.
711 last_download_finder_.reset();
713 if (last_download)
714 report_->set_allocated_download(last_download.release());
716 UploadIfCollectionComplete();
719 void IncidentReportingService::UploadIfCollectionComplete() {
720 DCHECK(report_);
721 // Bail out if there are still outstanding collection tasks. Completion of any
722 // of these will start another upload attempt.
723 if (WaitingForEnvironmentCollection() ||
724 WaitingToCollateIncidents() ||
725 WaitingForMostRecentDownload()) {
726 return;
729 // Take ownership of the report and clear things for future reports.
730 scoped_ptr<ClientIncidentReport> report(report_.Pass());
731 first_incident_time_ = base::Time();
732 last_incident_time_ = base::TimeTicks();
734 ClientIncidentReport_EnvironmentData_Process* process =
735 report->mutable_environment()->mutable_process();
737 process->set_metrics_consent(
738 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
740 // Find the profile that benefits from the strongest protections.
741 Profile* eligible_profile = FindEligibleProfile();
742 process->set_extended_consent(
743 eligible_profile ? eligible_profile->GetPrefs()->GetBoolean(
744 prefs::kSafeBrowsingExtendedReportingEnabled) :
745 false);
747 // Associate process-wide incidents with the profile that benefits from the
748 // strongest safe browsing protections.
749 ProfileContext* null_context = GetProfileContext(NULL);
750 if (null_context && !null_context->incidents.empty() && eligible_profile) {
751 ProfileContext* eligible_context = GetProfileContext(eligible_profile);
752 // Move the incidents to the target context.
753 eligible_context->incidents.insert(eligible_context->incidents.end(),
754 null_context->incidents.begin(),
755 null_context->incidents.end());
756 null_context->incidents.weak_clear();
759 // Collect incidents across all profiles participating in safe browsing. Drop
760 // incidents if the profile stopped participating before collection completed.
761 // Prune previously submitted incidents.
762 // Associate the profile contexts and their incident data with the upload.
763 UploadContext::PersistentIncidentStateCollection profiles_to_state;
764 for (ProfileContextCollection::iterator scan = profiles_.begin();
765 scan != profiles_.end();
766 ++scan) {
767 // Bypass process-wide incidents that have not yet been associated with a
768 // profile.
769 if (!scan->first)
770 continue;
771 PrefService* prefs = scan->first->GetPrefs();
772 ProfileContext* context = scan->second;
773 if (context->incidents.empty())
774 continue;
775 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
776 for (Incident* incident : context->incidents)
777 LogIncidentDataType(DROPPED, *incident);
778 context->incidents.clear();
779 continue;
781 StateStore::Transaction transaction(context->state_store.get());
782 std::vector<PersistentIncidentState> states;
783 // Prep persistent data and prune any incidents already sent.
784 for (Incident* incident : context->incidents) {
785 const PersistentIncidentState state = ComputeIncidentState(*incident);
786 if (context->state_store->HasBeenReported(state.type, state.key,
787 state.digest)) {
788 LogIncidentDataType(PRUNED, *incident);
789 } else if (!report->has_download()) {
790 LogIncidentDataType(NO_DOWNLOAD, *incident);
791 // Drop the incident and mark for future pruning since no executable
792 // download was found.
793 transaction.MarkAsReported(state.type, state.key, state.digest);
794 } else {
795 LogIncidentDataType(ACCEPTED, *incident);
796 // Ownership of the payload is passed to the report.
797 ClientIncidentReport_IncidentData* data =
798 incident->TakePayload().release();
799 DCHECK(data->has_incident_time_msec());
800 report->mutable_incident()->AddAllocated(data);
801 data = nullptr;
802 states.push_back(state);
805 context->incidents.clear();
806 profiles_to_state[context].swap(states);
809 const int count = report->incident_size();
811 // Abandon the request if all incidents were pruned or otherwise dropped.
812 if (!count) {
813 if (!report->has_download()) {
814 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
815 IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
816 IncidentReportUploader::NUM_UPLOAD_RESULTS);
818 return;
821 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count);
823 scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
824 context->profiles_to_state.swap(profiles_to_state);
825 if (!database_manager_.get()) {
826 // No database manager during testing. Take ownership of the context and
827 // continue processing.
828 UploadContext* temp_context = context.get();
829 uploads_.push_back(context.release());
830 IncidentReportingService::OnKillSwitchResult(temp_context, false);
831 } else {
832 if (content::BrowserThread::PostTaskAndReplyWithResult(
833 content::BrowserThread::IO,
834 FROM_HERE,
835 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn,
836 database_manager_),
837 base::Bind(&IncidentReportingService::OnKillSwitchResult,
838 weak_ptr_factory_.GetWeakPtr(),
839 context.get()))) {
840 uploads_.push_back(context.release());
841 } // else should not happen. Let the context be deleted automatically.
845 void IncidentReportingService::CancelAllReportUploads() {
846 for (size_t i = 0; i < uploads_.size(); ++i) {
847 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
848 IncidentReportUploader::UPLOAD_CANCELLED,
849 IncidentReportUploader::NUM_UPLOAD_RESULTS);
851 uploads_.clear();
854 void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
855 bool is_killswitch_on) {
856 DCHECK(thread_checker_.CalledOnValidThread());
857 if (!is_killswitch_on) {
858 // Initiate the upload.
859 context->uploader =
860 StartReportUpload(
861 base::Bind(&IncidentReportingService::OnReportUploadResult,
862 weak_ptr_factory_.GetWeakPtr(),
863 context),
864 url_request_context_getter_,
865 *context->report).Pass();
866 if (!context->uploader) {
867 OnReportUploadResult(context,
868 IncidentReportUploader::UPLOAD_INVALID_REQUEST,
869 scoped_ptr<ClientIncidentResponse>());
871 } else {
872 OnReportUploadResult(context,
873 IncidentReportUploader::UPLOAD_SUPPRESSED,
874 scoped_ptr<ClientIncidentResponse>());
878 void IncidentReportingService::HandleResponse(const UploadContext& context) {
879 // Mark each incident as reported in its corresponding profile's state store.
880 for (const auto& context_and_states : context.profiles_to_state) {
881 StateStore::Transaction transaction(
882 context_and_states.first->state_store.get());
883 for (const auto& state : context_and_states.second)
884 transaction.MarkAsReported(state.type, state.key, state.digest);
888 void IncidentReportingService::OnReportUploadResult(
889 UploadContext* context,
890 IncidentReportUploader::Result result,
891 scoped_ptr<ClientIncidentResponse> response) {
892 DCHECK(thread_checker_.CalledOnValidThread());
894 UMA_HISTOGRAM_ENUMERATION(
895 "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
897 // The upload is no longer outstanding, so take ownership of the context (from
898 // the collection of outstanding uploads) in this scope.
899 ScopedVector<UploadContext>::iterator it(
900 std::find(uploads_.begin(), uploads_.end(), context));
901 DCHECK(it != uploads_.end());
902 scoped_ptr<UploadContext> upload(context); // == *it
903 *it = uploads_.back();
904 uploads_.weak_erase(uploads_.end() - 1);
906 if (result == IncidentReportUploader::UPLOAD_SUCCESS)
907 HandleResponse(*upload);
908 // else retry?
911 void IncidentReportingService::OnClientDownloadRequest(
912 content::DownloadItem* download,
913 const ClientDownloadRequest* request) {
914 if (!download->GetBrowserContext()->IsOffTheRecord())
915 download_metadata_manager_.SetRequest(download, request);
918 void IncidentReportingService::Observe(
919 int type,
920 const content::NotificationSource& source,
921 const content::NotificationDetails& details) {
922 switch (type) {
923 case chrome::NOTIFICATION_PROFILE_ADDED: {
924 Profile* profile = content::Source<Profile>(source).ptr();
925 if (!profile->IsOffTheRecord())
926 OnProfileAdded(profile);
927 break;
929 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
930 Profile* profile = content::Source<Profile>(source).ptr();
931 if (!profile->IsOffTheRecord())
932 OnProfileDestroyed(profile);
933 break;
935 default:
936 break;
940 } // namespace safe_browsing