Remove aura enum from DesktopMediaID to fix desktop mirroring audio (CrOS).
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / incident_reporting_service.cc
blob5f40cbf296af456a79234c9344e2f6b3c501b44f
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/prefs/tracked/tracked_preference_validation_delegate.h"
22 #include "chrome/browser/profiles/profile.h"
23 #include "chrome/browser/safe_browsing/database_manager.h"
24 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h"
25 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
26 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
27 #include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h"
28 #include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h"
29 #include "chrome/browser/safe_browsing/incident_reporting/state_store.h"
30 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
31 #include "chrome/common/pref_names.h"
32 #include "chrome/common/safe_browsing/csd.pb.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 NUM_DISPOSITIONS
52 // The state persisted for a specific instance of an incident to enable pruning
53 // of previously-reported incidents.
54 struct PersistentIncidentState {
55 // The type of the incident.
56 IncidentType type;
58 // The key for a specific instance of an incident.
59 std::string key;
61 // A hash digest representing a specific instance of an incident.
62 uint32_t digest;
65 // The amount of time the service will wait to collate incidents.
66 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute
68 // The amount of time between running delayed analysis callbacks.
69 const int64 kDefaultCallbackIntervalMs = 1000 * 20;
71 // Logs the type of incident in |incident_data| to a user metrics histogram.
72 void LogIncidentDataType(IncidentDisposition disposition,
73 const Incident& incident) {
74 static const char* const kHistogramNames[] = {
75 "SBIRS.ReceivedIncident",
76 "SBIRS.DroppedIncident",
77 "SBIRS.Incident",
78 "SBIRS.PrunedIncident",
79 "SBIRS.DiscardedIncident",
81 static_assert(arraysize(kHistogramNames) == NUM_DISPOSITIONS,
82 "Keep kHistogramNames in sync with enum IncidentDisposition.");
83 DCHECK_GE(disposition, 0);
84 DCHECK_LT(disposition, NUM_DISPOSITIONS);
85 base::LinearHistogram::FactoryGet(
86 kHistogramNames[disposition],
87 1, // minimum
88 static_cast<int32_t>(IncidentType::NUM_TYPES), // maximum
89 static_cast<size_t>(IncidentType::NUM_TYPES) + 1, // bucket_count
90 base::HistogramBase::kUmaTargetedHistogramFlag)->Add(
91 static_cast<int32_t>(incident.GetType()));
94 // Computes the persistent state for an incident.
95 PersistentIncidentState ComputeIncidentState(const Incident& incident) {
96 PersistentIncidentState state = {
97 incident.GetType(),
98 incident.GetKey(),
99 incident.ComputeDigest(),
101 return state;
104 } // namespace
106 struct IncidentReportingService::ProfileContext {
107 ProfileContext();
108 ~ProfileContext();
110 // The incidents collected for this profile pending creation and/or upload.
111 // Will contain null values for pruned incidents.
112 ScopedVector<Incident> incidents;
114 // State storage for this profile; null until PROFILE_ADDED notification is
115 // received.
116 scoped_ptr<StateStore> state_store;
118 // False until PROFILE_ADDED notification is received.
119 bool added;
121 private:
122 DISALLOW_COPY_AND_ASSIGN(ProfileContext);
125 class IncidentReportingService::UploadContext {
126 public:
127 typedef std::map<ProfileContext*, std::vector<PersistentIncidentState>>
128 PersistentIncidentStateCollection;
130 explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
131 ~UploadContext();
133 // The report being uploaded.
134 scoped_ptr<ClientIncidentReport> report;
136 // The uploader in use. This is NULL until the CSD killswitch is checked.
137 scoped_ptr<IncidentReportUploader> uploader;
139 // A mapping of profile contexts to the data to be persisted upon successful
140 // upload.
141 PersistentIncidentStateCollection profiles_to_state;
143 private:
144 DISALLOW_COPY_AND_ASSIGN(UploadContext);
147 // An IncidentReceiver that is weakly-bound to the service and transparently
148 // bounces process-wide incidents back to the main thread for handling.
149 class IncidentReportingService::Receiver : public IncidentReceiver {
150 public:
151 explicit Receiver(const base::WeakPtr<IncidentReportingService>& service);
152 ~Receiver() override;
154 // IncidentReceiver methods:
155 void AddIncidentForProfile(Profile* profile,
156 scoped_ptr<Incident> incident) override;
157 void AddIncidentForProcess(scoped_ptr<Incident> incident) override;
159 private:
160 static void AddIncidentOnMainThread(
161 const base::WeakPtr<IncidentReportingService>& service,
162 Profile* profile,
163 scoped_ptr<Incident> incident);
165 base::WeakPtr<IncidentReportingService> service_;
166 scoped_refptr<base::SingleThreadTaskRunner> thread_runner_;
168 DISALLOW_COPY_AND_ASSIGN(Receiver);
171 IncidentReportingService::Receiver::Receiver(
172 const base::WeakPtr<IncidentReportingService>& service)
173 : service_(service),
174 thread_runner_(base::ThreadTaskRunnerHandle::Get()) {
177 IncidentReportingService::Receiver::~Receiver() {
180 void IncidentReportingService::Receiver::AddIncidentForProfile(
181 Profile* profile,
182 scoped_ptr<Incident> incident) {
183 DCHECK(thread_runner_->BelongsToCurrentThread());
184 DCHECK(profile);
185 AddIncidentOnMainThread(service_, profile, incident.Pass());
188 void IncidentReportingService::Receiver::AddIncidentForProcess(
189 scoped_ptr<Incident> incident) {
190 if (thread_runner_->BelongsToCurrentThread()) {
191 AddIncidentOnMainThread(service_, nullptr, incident.Pass());
192 } else if (!thread_runner_->PostTask(
193 FROM_HERE,
194 base::Bind(&IncidentReportingService::Receiver::AddIncidentOnMainThread,
195 service_, nullptr, base::Passed(&incident)))) {
196 LogIncidentDataType(DISCARDED, *incident);
200 // static
201 void IncidentReportingService::Receiver::AddIncidentOnMainThread(
202 const base::WeakPtr<IncidentReportingService>& service,
203 Profile* profile,
204 scoped_ptr<Incident> incident) {
205 if (service)
206 service->AddIncident(profile, incident.Pass());
207 else
208 LogIncidentDataType(DISCARDED, *incident);
211 IncidentReportingService::ProfileContext::ProfileContext() : added(false) {
214 IncidentReportingService::ProfileContext::~ProfileContext() {
215 for (Incident* incident : incidents) {
216 if (incident)
217 LogIncidentDataType(DISCARDED, *incident);
221 IncidentReportingService::UploadContext::UploadContext(
222 scoped_ptr<ClientIncidentReport> report)
223 : report(report.Pass()) {
226 IncidentReportingService::UploadContext::~UploadContext() {
229 IncidentReportingService::IncidentReportingService(
230 SafeBrowsingService* safe_browsing_service,
231 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter)
232 : database_manager_(safe_browsing_service
233 ? safe_browsing_service->database_manager()
234 : NULL),
235 url_request_context_getter_(request_context_getter),
236 collect_environment_data_fn_(&CollectEnvironmentData),
237 environment_collection_task_runner_(
238 content::BrowserThread::GetBlockingPool()
239 ->GetTaskRunnerWithShutdownBehavior(
240 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
241 environment_collection_pending_(),
242 collation_timeout_pending_(),
243 collation_timer_(FROM_HERE,
244 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
245 this,
246 &IncidentReportingService::OnCollationTimeout),
247 delayed_analysis_callbacks_(
248 base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs),
249 content::BrowserThread::GetBlockingPool()
250 ->GetTaskRunnerWithShutdownBehavior(
251 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
252 download_metadata_manager_(content::BrowserThread::GetBlockingPool()),
253 receiver_weak_ptr_factory_(this),
254 weak_ptr_factory_(this) {
255 notification_registrar_.Add(this,
256 chrome::NOTIFICATION_PROFILE_ADDED,
257 content::NotificationService::AllSources());
258 notification_registrar_.Add(this,
259 chrome::NOTIFICATION_PROFILE_DESTROYED,
260 content::NotificationService::AllSources());
261 DownloadProtectionService* download_protection_service =
262 (safe_browsing_service ?
263 safe_browsing_service->download_protection_service() :
264 NULL);
265 if (download_protection_service) {
266 client_download_request_subscription_ =
267 download_protection_service->RegisterClientDownloadRequestCallback(
268 base::Bind(&IncidentReportingService::OnClientDownloadRequest,
269 base::Unretained(this)));
273 IncidentReportingService::~IncidentReportingService() {
274 DCHECK(thread_checker_.CalledOnValidThread());
275 CancelIncidentCollection();
277 // Cancel all internal asynchronous tasks.
278 weak_ptr_factory_.InvalidateWeakPtrs();
280 CancelEnvironmentCollection();
281 CancelDownloadCollection();
282 CancelAllReportUploads();
284 STLDeleteValues(&profiles_);
287 scoped_ptr<IncidentReceiver> IncidentReportingService::GetIncidentReceiver() {
288 return make_scoped_ptr(new Receiver(receiver_weak_ptr_factory_.GetWeakPtr()));
291 scoped_ptr<TrackedPreferenceValidationDelegate>
292 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) {
293 DCHECK(thread_checker_.CalledOnValidThread());
295 if (profile->IsOffTheRecord())
296 return scoped_ptr<TrackedPreferenceValidationDelegate>();
297 return scoped_ptr<TrackedPreferenceValidationDelegate>(
298 new PreferenceValidationDelegate(profile, GetIncidentReceiver()));
301 void IncidentReportingService::RegisterDelayedAnalysisCallback(
302 const DelayedAnalysisCallback& callback) {
303 DCHECK(thread_checker_.CalledOnValidThread());
305 // |callback| will be run on the blocking pool. The receiver will bounce back
306 // to the origin thread if needed.
307 delayed_analysis_callbacks_.RegisterCallback(
308 base::Bind(callback, base::Passed(GetIncidentReceiver())));
310 // Start running the callbacks if any profiles are participating in safe
311 // browsing. If none are now, running will commence if/when a participaing
312 // profile is added.
313 if (FindEligibleProfile())
314 delayed_analysis_callbacks_.Start();
317 void IncidentReportingService::AddDownloadManager(
318 content::DownloadManager* download_manager) {
319 download_metadata_manager_.AddDownloadManager(download_manager);
322 IncidentReportingService::IncidentReportingService(
323 SafeBrowsingService* safe_browsing_service,
324 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
325 base::TimeDelta delayed_task_interval,
326 const scoped_refptr<base::TaskRunner>& delayed_task_runner)
327 : database_manager_(safe_browsing_service
328 ? safe_browsing_service->database_manager()
329 : NULL),
330 url_request_context_getter_(request_context_getter),
331 collect_environment_data_fn_(&CollectEnvironmentData),
332 environment_collection_task_runner_(
333 content::BrowserThread::GetBlockingPool()
334 ->GetTaskRunnerWithShutdownBehavior(
335 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
336 environment_collection_pending_(),
337 collation_timeout_pending_(),
338 collation_timer_(FROM_HERE,
339 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
340 this,
341 &IncidentReportingService::OnCollationTimeout),
342 delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner),
343 download_metadata_manager_(content::BrowserThread::GetBlockingPool()),
344 receiver_weak_ptr_factory_(this),
345 weak_ptr_factory_(this) {
346 notification_registrar_.Add(this,
347 chrome::NOTIFICATION_PROFILE_ADDED,
348 content::NotificationService::AllSources());
349 notification_registrar_.Add(this,
350 chrome::NOTIFICATION_PROFILE_DESTROYED,
351 content::NotificationService::AllSources());
354 void IncidentReportingService::SetCollectEnvironmentHook(
355 CollectEnvironmentDataFn collect_environment_data_hook,
356 const scoped_refptr<base::TaskRunner>& task_runner) {
357 if (collect_environment_data_hook) {
358 collect_environment_data_fn_ = collect_environment_data_hook;
359 environment_collection_task_runner_ = task_runner;
360 } else {
361 collect_environment_data_fn_ = &CollectEnvironmentData;
362 environment_collection_task_runner_ =
363 content::BrowserThread::GetBlockingPool()
364 ->GetTaskRunnerWithShutdownBehavior(
365 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
369 void IncidentReportingService::OnProfileAdded(Profile* profile) {
370 DCHECK(thread_checker_.CalledOnValidThread());
372 // Track the addition of all profiles even when no report is being assembled
373 // so that the service can determine whether or not it can evaluate a
374 // profile's preferences at the time of incident addition.
375 ProfileContext* context = GetOrCreateProfileContext(profile);
376 DCHECK(!context->added);
377 context->added = true;
378 context->state_store.reset(new StateStore(profile));
380 const bool safe_browsing_enabled =
381 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
383 // Start processing delayed analysis callbacks if this new profile
384 // participates in safe browsing. Start is idempotent, so this is safe even if
385 // they're already running.
386 if (safe_browsing_enabled)
387 delayed_analysis_callbacks_.Start();
389 // Start a new report if this profile participates in safe browsing and there
390 // are process-wide incidents.
391 if (safe_browsing_enabled && GetProfileContext(NULL) &&
392 GetProfileContext(NULL)->incidents.size()) {
393 BeginReportProcessing();
396 // TODO(grt): register for pref change notifications to start delayed analysis
397 // and/or report processing if sb is currently disabled but subsequently
398 // enabled.
400 // Nothing else to do if a report is not being assembled.
401 if (!report_)
402 return;
404 // Drop all incidents associated with this profile that were received prior to
405 // its addition if the profile is not participating in safe browsing.
406 if (!context->incidents.empty() && !safe_browsing_enabled) {
407 for (Incident* incident : context->incidents)
408 LogIncidentDataType(DROPPED, *incident);
409 context->incidents.clear();
412 // Take another stab at finding the most recent download if a report is being
413 // assembled and one hasn't been found yet (the LastDownloadFinder operates
414 // only on profiles that have been added to the ProfileManager).
415 BeginDownloadCollection();
418 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder(
419 const LastDownloadFinder::LastDownloadCallback& callback) {
420 return LastDownloadFinder::Create(
421 base::Bind(&DownloadMetadataManager::GetDownloadDetails,
422 base::Unretained(&download_metadata_manager_)),
423 callback).Pass();
426 scoped_ptr<IncidentReportUploader> IncidentReportingService::StartReportUpload(
427 const IncidentReportUploader::OnResultCallback& callback,
428 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
429 const ClientIncidentReport& report) {
430 return IncidentReportUploaderImpl::UploadReport(
431 callback, request_context_getter, report).Pass();
434 bool IncidentReportingService::IsProcessingReport() const {
435 return report_ != NULL;
438 IncidentReportingService::ProfileContext*
439 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
440 ProfileContextCollection::iterator it =
441 profiles_.insert(ProfileContextCollection::value_type(profile, NULL))
442 .first;
443 if (!it->second)
444 it->second = new ProfileContext();
445 return it->second;
448 IncidentReportingService::ProfileContext*
449 IncidentReportingService::GetProfileContext(Profile* profile) {
450 ProfileContextCollection::iterator it = profiles_.find(profile);
451 return it == profiles_.end() ? NULL : it->second;
454 void IncidentReportingService::OnProfileDestroyed(Profile* profile) {
455 DCHECK(thread_checker_.CalledOnValidThread());
457 ProfileContextCollection::iterator it = profiles_.find(profile);
458 if (it == profiles_.end())
459 return;
461 // Take ownership of the context.
462 scoped_ptr<ProfileContext> context(it->second);
463 it->second = nullptr;
465 // TODO(grt): Persist incidents for upload on future profile load.
467 // Remove the association with this profile context from all pending uploads.
468 for (UploadContext* upload : uploads_)
469 upload->profiles_to_state.erase(context.get());
471 // Forget about this profile. Incidents not yet sent for upload are lost.
472 // No new incidents will be accepted for it.
473 profiles_.erase(it);
476 Profile* IncidentReportingService::FindEligibleProfile() const {
477 Profile* candidate = NULL;
478 for (ProfileContextCollection::const_iterator scan = profiles_.begin();
479 scan != profiles_.end();
480 ++scan) {
481 // Skip over profiles that have yet to be added to the profile manager.
482 // This will also skip over the NULL-profile context used to hold
483 // process-wide incidents.
484 if (!scan->second->added)
485 continue;
486 PrefService* prefs = scan->first->GetPrefs();
487 if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
488 if (!candidate)
489 candidate = scan->first;
490 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
491 candidate = scan->first;
492 break;
496 return candidate;
499 void IncidentReportingService::AddIncident(Profile* profile,
500 scoped_ptr<Incident> incident) {
501 DCHECK(thread_checker_.CalledOnValidThread());
503 // Ignore incidents from off-the-record profiles.
504 if (profile && profile->IsOffTheRecord())
505 return;
507 ProfileContext* context = GetOrCreateProfileContext(profile);
508 // If this is a process-wide incident, the context must not indicate that the
509 // profile (which is NULL) has been added to the profile manager.
510 DCHECK(profile || !context->added);
512 LogIncidentDataType(RECEIVED, *incident);
514 // Drop the incident immediately if the profile has already been added to the
515 // manager and is not participating in safe browsing. Preference evaluation is
516 // deferred until OnProfileAdded() otherwise.
517 if (context->added &&
518 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
519 LogIncidentDataType(DROPPED, *incident);
520 return;
523 // Take ownership of the incident.
524 context->incidents.push_back(incident.release());
526 // Remember when the first incident for this report arrived.
527 if (first_incident_time_.is_null())
528 first_incident_time_ = base::Time::Now();
529 // Log the time between the previous incident and this one.
530 if (!last_incident_time_.is_null()) {
531 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime",
532 base::TimeTicks::Now() - last_incident_time_);
534 last_incident_time_ = base::TimeTicks::Now();
536 // Persist the incident data.
538 // Start assembling a new report if this is the first incident ever or the
539 // first since the last upload.
540 BeginReportProcessing();
543 void IncidentReportingService::BeginReportProcessing() {
544 DCHECK(thread_checker_.CalledOnValidThread());
546 // Creates a new report if needed.
547 if (!report_)
548 report_.reset(new ClientIncidentReport());
550 // Ensure that collection tasks are running (calls are idempotent).
551 BeginIncidentCollation();
552 BeginEnvironmentCollection();
553 BeginDownloadCollection();
556 void IncidentReportingService::BeginIncidentCollation() {
557 // Restart the delay timer to send the report upon expiration.
558 collation_timeout_pending_ = true;
559 collation_timer_.Reset();
562 void IncidentReportingService::BeginEnvironmentCollection() {
563 DCHECK(thread_checker_.CalledOnValidThread());
564 DCHECK(report_);
565 // Nothing to do if environment collection is pending or has already
566 // completed.
567 if (environment_collection_pending_ || report_->has_environment())
568 return;
570 environment_collection_begin_ = base::TimeTicks::Now();
571 ClientIncidentReport_EnvironmentData* environment_data =
572 new ClientIncidentReport_EnvironmentData();
573 environment_collection_pending_ =
574 environment_collection_task_runner_->PostTaskAndReply(
575 FROM_HERE,
576 base::Bind(collect_environment_data_fn_, environment_data),
577 base::Bind(&IncidentReportingService::OnEnvironmentDataCollected,
578 weak_ptr_factory_.GetWeakPtr(),
579 base::Passed(make_scoped_ptr(environment_data))));
581 // Posting the task will fail if the runner has been shut down. This should
582 // never happen since the blocking pool is shut down after this service.
583 DCHECK(environment_collection_pending_);
586 bool IncidentReportingService::WaitingForEnvironmentCollection() {
587 return environment_collection_pending_;
590 void IncidentReportingService::CancelEnvironmentCollection() {
591 environment_collection_begin_ = base::TimeTicks();
592 environment_collection_pending_ = false;
593 if (report_)
594 report_->clear_environment();
597 void IncidentReportingService::OnEnvironmentDataCollected(
598 scoped_ptr<ClientIncidentReport_EnvironmentData> environment_data) {
599 DCHECK(thread_checker_.CalledOnValidThread());
600 DCHECK(environment_collection_pending_);
601 DCHECK(report_ && !report_->has_environment());
602 environment_collection_pending_ = false;
604 // CurrentProcessInfo::CreationTime() is missing on some platforms.
605 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
606 base::TimeDelta uptime =
607 first_incident_time_ - base::CurrentProcessInfo::CreationTime();
608 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds());
609 #endif
611 report_->set_allocated_environment(environment_data.release());
613 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime",
614 base::TimeTicks::Now() - environment_collection_begin_);
615 environment_collection_begin_ = base::TimeTicks();
617 UploadIfCollectionComplete();
620 bool IncidentReportingService::WaitingToCollateIncidents() {
621 return collation_timeout_pending_;
624 void IncidentReportingService::CancelIncidentCollection() {
625 collation_timeout_pending_ = false;
626 last_incident_time_ = base::TimeTicks();
627 report_.reset();
630 void IncidentReportingService::OnCollationTimeout() {
631 DCHECK(thread_checker_.CalledOnValidThread());
633 // Exit early if collection was cancelled.
634 if (!collation_timeout_pending_)
635 return;
637 // Wait another round if profile-bound incidents have come in from a profile
638 // that has yet to complete creation.
639 for (ProfileContextCollection::iterator scan = profiles_.begin();
640 scan != profiles_.end();
641 ++scan) {
642 if (scan->first && !scan->second->added &&
643 !scan->second->incidents.empty()) {
644 collation_timer_.Reset();
645 return;
649 collation_timeout_pending_ = false;
651 UploadIfCollectionComplete();
654 void IncidentReportingService::BeginDownloadCollection() {
655 DCHECK(thread_checker_.CalledOnValidThread());
656 DCHECK(report_);
657 // Nothing to do if a search for the most recent download is already pending
658 // or if one has already been found.
659 if (last_download_finder_ || report_->has_download())
660 return;
662 last_download_begin_ = base::TimeTicks::Now();
663 last_download_finder_ = CreateDownloadFinder(
664 base::Bind(&IncidentReportingService::OnLastDownloadFound,
665 weak_ptr_factory_.GetWeakPtr()));
666 // No instance is returned if there are no eligible loaded profiles. Another
667 // search will be attempted in OnProfileAdded() if another profile appears on
668 // the scene.
669 if (!last_download_finder_)
670 last_download_begin_ = base::TimeTicks();
673 bool IncidentReportingService::WaitingForMostRecentDownload() {
674 DCHECK(report_); // Only call this when a report is being assembled.
675 // The easy case: not waiting if a download has already been found.
676 if (report_->has_download())
677 return false;
678 // The next easy case: waiting if the finder is operating.
679 if (last_download_finder_)
680 return true;
681 // The harder case: waiting if a non-NULL profile has not yet been added.
682 for (ProfileContextCollection::const_iterator scan = profiles_.begin();
683 scan != profiles_.end();
684 ++scan) {
685 if (scan->first && !scan->second->added)
686 return true;
688 // There is no most recent download and there's nothing more to wait for.
689 return false;
692 void IncidentReportingService::CancelDownloadCollection() {
693 last_download_finder_.reset();
694 last_download_begin_ = base::TimeTicks();
695 if (report_)
696 report_->clear_download();
699 void IncidentReportingService::OnLastDownloadFound(
700 scoped_ptr<ClientIncidentReport_DownloadDetails> last_download) {
701 DCHECK(thread_checker_.CalledOnValidThread());
702 DCHECK(report_);
704 UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime",
705 base::TimeTicks::Now() - last_download_begin_);
706 last_download_begin_ = base::TimeTicks();
708 // Harvest the finder.
709 last_download_finder_.reset();
711 if (last_download)
712 report_->set_allocated_download(last_download.release());
714 UploadIfCollectionComplete();
717 void IncidentReportingService::UploadIfCollectionComplete() {
718 DCHECK(report_);
719 // Bail out if there are still outstanding collection tasks. Completion of any
720 // of these will start another upload attempt.
721 if (WaitingForEnvironmentCollection() ||
722 WaitingToCollateIncidents() ||
723 WaitingForMostRecentDownload()) {
724 return;
727 // Take ownership of the report and clear things for future reports.
728 scoped_ptr<ClientIncidentReport> report(report_.Pass());
729 first_incident_time_ = base::Time();
730 last_incident_time_ = base::TimeTicks();
732 // Drop the report if no executable download was found.
733 if (!report->has_download()) {
734 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
735 IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
736 IncidentReportUploader::NUM_UPLOAD_RESULTS);
737 return;
740 ClientIncidentReport_EnvironmentData_Process* process =
741 report->mutable_environment()->mutable_process();
743 // Not all platforms have a metrics reporting preference.
744 if (g_browser_process->local_state()->FindPreference(
745 prefs::kMetricsReportingEnabled)) {
746 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean(
747 prefs::kMetricsReportingEnabled));
750 // Find the profile that benefits from the strongest protections.
751 Profile* eligible_profile = FindEligibleProfile();
752 process->set_extended_consent(
753 eligible_profile ? eligible_profile->GetPrefs()->GetBoolean(
754 prefs::kSafeBrowsingExtendedReportingEnabled) :
755 false);
757 // Associate process-wide incidents with the profile that benefits from the
758 // strongest safe browsing protections.
759 ProfileContext* null_context = GetProfileContext(NULL);
760 if (null_context && !null_context->incidents.empty() && eligible_profile) {
761 ProfileContext* eligible_context = GetProfileContext(eligible_profile);
762 // Move the incidents to the target context.
763 eligible_context->incidents.insert(eligible_context->incidents.end(),
764 null_context->incidents.begin(),
765 null_context->incidents.end());
766 null_context->incidents.weak_clear();
769 // Collect incidents across all profiles participating in safe browsing. Drop
770 // incidents if the profile stopped participating before collection completed.
771 // Prune previously submitted incidents.
772 // Associate the profile contexts and their incident data with the upload.
773 size_t prune_count = 0;
774 UploadContext::PersistentIncidentStateCollection profiles_to_state;
775 for (ProfileContextCollection::iterator scan = profiles_.begin();
776 scan != profiles_.end();
777 ++scan) {
778 // Bypass process-wide incidents that have not yet been associated with a
779 // profile.
780 if (!scan->first)
781 continue;
782 PrefService* prefs = scan->first->GetPrefs();
783 ProfileContext* context = scan->second;
784 if (context->incidents.empty())
785 continue;
786 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
787 for (Incident* incident : context->incidents)
788 LogIncidentDataType(DROPPED, *incident);
789 context->incidents.clear();
790 continue;
792 std::vector<PersistentIncidentState> states;
793 // Prep persistent data and prune any incidents already sent.
794 for (Incident* incident : context->incidents) {
795 const PersistentIncidentState state = ComputeIncidentState(*incident);
796 if (context->state_store->HasBeenReported(state.type, state.key,
797 state.digest)) {
798 LogIncidentDataType(PRUNED, *incident);
799 ++prune_count;
800 } else {
801 LogIncidentDataType(ACCEPTED, *incident);
802 // Ownership of the payload is passed to the report.
803 ClientIncidentReport_IncidentData* data =
804 incident->TakePayload().release();
805 DCHECK(data->has_incident_time_msec());
806 report->mutable_incident()->AddAllocated(data);
807 data = nullptr;
808 states.push_back(state);
811 context->incidents.clear();
812 profiles_to_state[context].swap(states);
815 const int count = report->incident_size();
816 // Abandon the request if all incidents were dropped with none pruned.
817 if (!count && !prune_count)
818 return;
820 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count);
823 double prune_pct = static_cast<double>(prune_count);
824 prune_pct = prune_pct * 100.0 / (count + prune_count);
825 prune_pct = round(prune_pct);
826 UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct));
828 // Abandon the report if all incidents were pruned.
829 if (!count)
830 return;
832 scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
833 context->profiles_to_state.swap(profiles_to_state);
834 if (!database_manager_.get()) {
835 // No database manager during testing. Take ownership of the context and
836 // continue processing.
837 UploadContext* temp_context = context.get();
838 uploads_.push_back(context.release());
839 IncidentReportingService::OnKillSwitchResult(temp_context, false);
840 } else {
841 if (content::BrowserThread::PostTaskAndReplyWithResult(
842 content::BrowserThread::IO,
843 FROM_HERE,
844 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn,
845 database_manager_),
846 base::Bind(&IncidentReportingService::OnKillSwitchResult,
847 weak_ptr_factory_.GetWeakPtr(),
848 context.get()))) {
849 uploads_.push_back(context.release());
850 } // else should not happen. Let the context be deleted automatically.
854 void IncidentReportingService::CancelAllReportUploads() {
855 for (size_t i = 0; i < uploads_.size(); ++i) {
856 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
857 IncidentReportUploader::UPLOAD_CANCELLED,
858 IncidentReportUploader::NUM_UPLOAD_RESULTS);
860 uploads_.clear();
863 void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
864 bool is_killswitch_on) {
865 DCHECK(thread_checker_.CalledOnValidThread());
866 if (!is_killswitch_on) {
867 // Initiate the upload.
868 context->uploader =
869 StartReportUpload(
870 base::Bind(&IncidentReportingService::OnReportUploadResult,
871 weak_ptr_factory_.GetWeakPtr(),
872 context),
873 url_request_context_getter_,
874 *context->report).Pass();
875 if (!context->uploader) {
876 OnReportUploadResult(context,
877 IncidentReportUploader::UPLOAD_INVALID_REQUEST,
878 scoped_ptr<ClientIncidentResponse>());
880 } else {
881 OnReportUploadResult(context,
882 IncidentReportUploader::UPLOAD_SUPPRESSED,
883 scoped_ptr<ClientIncidentResponse>());
887 void IncidentReportingService::HandleResponse(const UploadContext& context) {
888 // Mark each incident as reported in its corresponding profile's state store.
889 for (const auto& context_and_states : context.profiles_to_state) {
890 StateStore::Transaction transaction(
891 context_and_states.first->state_store.get());
892 for (const auto& state : context_and_states.second)
893 transaction.MarkAsReported(state.type, state.key, state.digest);
897 void IncidentReportingService::OnReportUploadResult(
898 UploadContext* context,
899 IncidentReportUploader::Result result,
900 scoped_ptr<ClientIncidentResponse> response) {
901 DCHECK(thread_checker_.CalledOnValidThread());
903 UMA_HISTOGRAM_ENUMERATION(
904 "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
906 // The upload is no longer outstanding, so take ownership of the context (from
907 // the collection of outstanding uploads) in this scope.
908 ScopedVector<UploadContext>::iterator it(
909 std::find(uploads_.begin(), uploads_.end(), context));
910 DCHECK(it != uploads_.end());
911 scoped_ptr<UploadContext> upload(context); // == *it
912 *it = uploads_.back();
913 uploads_.weak_erase(uploads_.end() - 1);
915 if (result == IncidentReportUploader::UPLOAD_SUCCESS)
916 HandleResponse(*upload);
917 // else retry?
920 void IncidentReportingService::OnClientDownloadRequest(
921 content::DownloadItem* download,
922 const ClientDownloadRequest* request) {
923 if (!download->GetBrowserContext()->IsOffTheRecord())
924 download_metadata_manager_.SetRequest(download, request);
927 void IncidentReportingService::Observe(
928 int type,
929 const content::NotificationSource& source,
930 const content::NotificationDetails& details) {
931 switch (type) {
932 case chrome::NOTIFICATION_PROFILE_ADDED: {
933 Profile* profile = content::Source<Profile>(source).ptr();
934 if (!profile->IsOffTheRecord())
935 OnProfileAdded(profile);
936 break;
938 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
939 Profile* profile = content::Source<Profile>(source).ptr();
940 if (!profile->IsOffTheRecord())
941 OnProfileDestroyed(profile);
942 break;
944 default:
945 break;
949 } // namespace safe_browsing