Switch global error menu icon to vectorized MD asset
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / incident_reporting_service.cc
blob669d5c78cb03c51f04e5b0544438dc150e09657c
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/browser_process.h"
20 #include "chrome/browser/chrome_notification_types.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 // Not all platforms have a metrics reporting preference.
738 if (g_browser_process->local_state()->FindPreference(
739 prefs::kMetricsReportingEnabled)) {
740 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean(
741 prefs::kMetricsReportingEnabled));
744 // Find the profile that benefits from the strongest protections.
745 Profile* eligible_profile = FindEligibleProfile();
746 process->set_extended_consent(
747 eligible_profile ? eligible_profile->GetPrefs()->GetBoolean(
748 prefs::kSafeBrowsingExtendedReportingEnabled) :
749 false);
751 // Associate process-wide incidents with the profile that benefits from the
752 // strongest safe browsing protections.
753 ProfileContext* null_context = GetProfileContext(NULL);
754 if (null_context && !null_context->incidents.empty() && eligible_profile) {
755 ProfileContext* eligible_context = GetProfileContext(eligible_profile);
756 // Move the incidents to the target context.
757 eligible_context->incidents.insert(eligible_context->incidents.end(),
758 null_context->incidents.begin(),
759 null_context->incidents.end());
760 null_context->incidents.weak_clear();
763 // Collect incidents across all profiles participating in safe browsing. Drop
764 // incidents if the profile stopped participating before collection completed.
765 // Prune previously submitted incidents.
766 // Associate the profile contexts and their incident data with the upload.
767 UploadContext::PersistentIncidentStateCollection profiles_to_state;
768 for (ProfileContextCollection::iterator scan = profiles_.begin();
769 scan != profiles_.end();
770 ++scan) {
771 // Bypass process-wide incidents that have not yet been associated with a
772 // profile.
773 if (!scan->first)
774 continue;
775 PrefService* prefs = scan->first->GetPrefs();
776 ProfileContext* context = scan->second;
777 if (context->incidents.empty())
778 continue;
779 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
780 for (Incident* incident : context->incidents)
781 LogIncidentDataType(DROPPED, *incident);
782 context->incidents.clear();
783 continue;
785 StateStore::Transaction transaction(context->state_store.get());
786 std::vector<PersistentIncidentState> states;
787 // Prep persistent data and prune any incidents already sent.
788 for (Incident* incident : context->incidents) {
789 const PersistentIncidentState state = ComputeIncidentState(*incident);
790 if (context->state_store->HasBeenReported(state.type, state.key,
791 state.digest)) {
792 LogIncidentDataType(PRUNED, *incident);
793 } else if (!report->has_download()) {
794 LogIncidentDataType(NO_DOWNLOAD, *incident);
795 // Drop the incident and mark for future pruning since no executable
796 // download was found.
797 transaction.MarkAsReported(state.type, state.key, state.digest);
798 } else {
799 LogIncidentDataType(ACCEPTED, *incident);
800 // Ownership of the payload is passed to the report.
801 ClientIncidentReport_IncidentData* data =
802 incident->TakePayload().release();
803 DCHECK(data->has_incident_time_msec());
804 report->mutable_incident()->AddAllocated(data);
805 data = nullptr;
806 states.push_back(state);
809 context->incidents.clear();
810 profiles_to_state[context].swap(states);
813 const int count = report->incident_size();
815 // Abandon the request if all incidents were pruned or otherwise dropped.
816 if (!count) {
817 if (!report->has_download()) {
818 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
819 IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
820 IncidentReportUploader::NUM_UPLOAD_RESULTS);
822 return;
825 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count);
827 scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
828 context->profiles_to_state.swap(profiles_to_state);
829 if (!database_manager_.get()) {
830 // No database manager during testing. Take ownership of the context and
831 // continue processing.
832 UploadContext* temp_context = context.get();
833 uploads_.push_back(context.release());
834 IncidentReportingService::OnKillSwitchResult(temp_context, false);
835 } else {
836 if (content::BrowserThread::PostTaskAndReplyWithResult(
837 content::BrowserThread::IO,
838 FROM_HERE,
839 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn,
840 database_manager_),
841 base::Bind(&IncidentReportingService::OnKillSwitchResult,
842 weak_ptr_factory_.GetWeakPtr(),
843 context.get()))) {
844 uploads_.push_back(context.release());
845 } // else should not happen. Let the context be deleted automatically.
849 void IncidentReportingService::CancelAllReportUploads() {
850 for (size_t i = 0; i < uploads_.size(); ++i) {
851 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
852 IncidentReportUploader::UPLOAD_CANCELLED,
853 IncidentReportUploader::NUM_UPLOAD_RESULTS);
855 uploads_.clear();
858 void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
859 bool is_killswitch_on) {
860 DCHECK(thread_checker_.CalledOnValidThread());
861 if (!is_killswitch_on) {
862 // Initiate the upload.
863 context->uploader =
864 StartReportUpload(
865 base::Bind(&IncidentReportingService::OnReportUploadResult,
866 weak_ptr_factory_.GetWeakPtr(),
867 context),
868 url_request_context_getter_,
869 *context->report).Pass();
870 if (!context->uploader) {
871 OnReportUploadResult(context,
872 IncidentReportUploader::UPLOAD_INVALID_REQUEST,
873 scoped_ptr<ClientIncidentResponse>());
875 } else {
876 OnReportUploadResult(context,
877 IncidentReportUploader::UPLOAD_SUPPRESSED,
878 scoped_ptr<ClientIncidentResponse>());
882 void IncidentReportingService::HandleResponse(const UploadContext& context) {
883 // Mark each incident as reported in its corresponding profile's state store.
884 for (const auto& context_and_states : context.profiles_to_state) {
885 StateStore::Transaction transaction(
886 context_and_states.first->state_store.get());
887 for (const auto& state : context_and_states.second)
888 transaction.MarkAsReported(state.type, state.key, state.digest);
892 void IncidentReportingService::OnReportUploadResult(
893 UploadContext* context,
894 IncidentReportUploader::Result result,
895 scoped_ptr<ClientIncidentResponse> response) {
896 DCHECK(thread_checker_.CalledOnValidThread());
898 UMA_HISTOGRAM_ENUMERATION(
899 "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
901 // The upload is no longer outstanding, so take ownership of the context (from
902 // the collection of outstanding uploads) in this scope.
903 ScopedVector<UploadContext>::iterator it(
904 std::find(uploads_.begin(), uploads_.end(), context));
905 DCHECK(it != uploads_.end());
906 scoped_ptr<UploadContext> upload(context); // == *it
907 *it = uploads_.back();
908 uploads_.weak_erase(uploads_.end() - 1);
910 if (result == IncidentReportUploader::UPLOAD_SUCCESS)
911 HandleResponse(*upload);
912 // else retry?
915 void IncidentReportingService::OnClientDownloadRequest(
916 content::DownloadItem* download,
917 const ClientDownloadRequest* request) {
918 if (!download->GetBrowserContext()->IsOffTheRecord())
919 download_metadata_manager_.SetRequest(download, request);
922 void IncidentReportingService::Observe(
923 int type,
924 const content::NotificationSource& source,
925 const content::NotificationDetails& details) {
926 switch (type) {
927 case chrome::NOTIFICATION_PROFILE_ADDED: {
928 Profile* profile = content::Source<Profile>(source).ptr();
929 if (!profile->IsOffTheRecord())
930 OnProfileAdded(profile);
931 break;
933 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
934 Profile* profile = content::Source<Profile>(source).ptr();
935 if (!profile->IsOffTheRecord())
936 OnProfileDestroyed(profile);
937 break;
939 default:
940 break;
944 } // namespace safe_browsing