[Extensions] Make extension message bubble factory platform-abstract
[chromium-blink-merge.git] / chrome / browser / safe_browsing / incident_reporting / incident_reporting_service.cc
blob44ab0b18c6c46796087c20656dcd31520b059f3d
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/prefs/scoped_user_pref_update.h"
15 #include "base/process/process_info.h"
16 #include "base/single_thread_task_runner.h"
17 #include "base/stl_util.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/thread_task_runner_handle.h"
20 #include "base/threading/sequenced_worker_pool.h"
21 #include "base/values.h"
22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/chrome_notification_types.h"
24 #include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h"
25 #include "chrome/browser/profiles/profile.h"
26 #include "chrome/browser/safe_browsing/database_manager.h"
27 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h"
28 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
29 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
30 #include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h"
31 #include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h"
32 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
33 #include "chrome/common/pref_names.h"
34 #include "chrome/common/safe_browsing/csd.pb.h"
35 #include "content/public/browser/browser_thread.h"
36 #include "content/public/browser/notification_service.h"
37 #include "net/url_request/url_request_context_getter.h"
39 namespace safe_browsing {
41 namespace {
43 // The action taken for an incident; used for user metrics (see
44 // LogIncidentDataType).
45 enum IncidentDisposition {
46 RECEIVED,
47 DROPPED,
48 ACCEPTED,
49 PRUNED,
50 DISCARDED,
51 NUM_DISPOSITIONS
54 // The state persisted for a specific instance of an incident to enable pruning
55 // of previously-reported incidents.
56 struct PersistentIncidentState {
57 // The type of the incident.
58 std::string type;
60 // The key for a specific instance of an incident.
61 std::string key;
63 // A hash digest representing a specific instance of an incident.
64 uint32_t digest;
67 // The amount of time the service will wait to collate incidents.
68 const int64 kDefaultUploadDelayMs = 1000 * 60; // one minute
70 // The amount of time between running delayed analysis callbacks.
71 const int64 kDefaultCallbackIntervalMs = 1000 * 20;
73 // Logs the type of incident in |incident_data| to a user metrics histogram.
74 void LogIncidentDataType(IncidentDisposition disposition,
75 const Incident& incident) {
76 static const char* const kHistogramNames[] = {
77 "SBIRS.ReceivedIncident",
78 "SBIRS.DroppedIncident",
79 "SBIRS.Incident",
80 "SBIRS.PrunedIncident",
81 "SBIRS.DiscardedIncident",
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 base::IntToString(static_cast<int32_t>(incident.GetType())),
100 incident.GetKey(),
101 incident.ComputeDigest(),
103 return state;
106 // Returns true if the incident described by |state| has already been reported
107 // based on the bookkeeping in the |incidents_sent| preference dictionary.
108 bool IncidentHasBeenReported(const base::DictionaryValue* incidents_sent,
109 const PersistentIncidentState& state) {
110 const base::DictionaryValue* type_dict = NULL;
111 std::string digest_string;
112 return (incidents_sent &&
113 incidents_sent->GetDictionaryWithoutPathExpansion(state.type,
114 &type_dict) &&
115 type_dict->GetStringWithoutPathExpansion(state.key, &digest_string) &&
116 digest_string == base::UintToString(state.digest));
119 // Marks the incidents described by |states| as having been reported
120 // in |incidents_set|.
121 void MarkIncidentsAsReported(const std::vector<PersistentIncidentState>& states,
122 base::DictionaryValue* incidents_sent) {
123 for (size_t i = 0; i < states.size(); ++i) {
124 const PersistentIncidentState& data = states[i];
125 base::DictionaryValue* type_dict = NULL;
126 if (!incidents_sent->GetDictionaryWithoutPathExpansion(data.type,
127 &type_dict)) {
128 type_dict = new base::DictionaryValue();
129 incidents_sent->SetWithoutPathExpansion(data.type, type_dict);
131 type_dict->SetStringWithoutPathExpansion(data.key,
132 base::UintToString(data.digest));
136 // Removes a profile's prune state for legacy incident types.
137 void CleanLegacyPruneState(Profile* profile) {
138 static const IncidentType kLegacyTypes[] = {
139 // TODO(grt): remove in M44 (crbug.com/451173).
140 IncidentType::OMNIBOX_INTERACTION,
143 // Figure out if there are any values to remove before committing to making
144 // any changes since any use of DictionaryPrefUpdate will result in a full
145 // serialize-and-write operation on the preferences store.
146 const base::Value* value =
147 profile->GetPrefs()->GetUserPrefValue(prefs::kSafeBrowsingIncidentsSent);
148 const base::DictionaryValue* incidents_sent = NULL;
149 if (!value || !value->GetAsDictionary(&incidents_sent))
150 return;
151 std::vector<std::string> types_to_remove;
152 for (size_t i = 0; i < arraysize(kLegacyTypes); ++i) {
153 const std::string incident_type(
154 base::IntToString(static_cast<int32_t>(kLegacyTypes[i])));
155 const base::DictionaryValue* type_dict = NULL;
156 if (incidents_sent->GetDictionaryWithoutPathExpansion(incident_type,
157 &type_dict)) {
158 types_to_remove.push_back(incident_type);
161 if (types_to_remove.empty())
162 return;
164 DictionaryPrefUpdate pref_update(profile->GetPrefs(),
165 prefs::kSafeBrowsingIncidentsSent);
166 for (const auto& incident_type : types_to_remove)
167 pref_update.Get()->RemoveWithoutPathExpansion(incident_type, NULL);
170 } // namespace
172 struct IncidentReportingService::ProfileContext {
173 ProfileContext();
174 ~ProfileContext();
176 // The incidents collected for this profile pending creation and/or upload.
177 // Will contain null values for pruned incidents.
178 ScopedVector<Incident> incidents;
180 // False until PROFILE_ADDED notification is received.
181 bool added;
183 private:
184 DISALLOW_COPY_AND_ASSIGN(ProfileContext);
187 class IncidentReportingService::UploadContext {
188 public:
189 typedef std::map<Profile*, std::vector<PersistentIncidentState> >
190 PersistentIncidentStateCollection;
192 explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
193 ~UploadContext();
195 // The report being uploaded.
196 scoped_ptr<ClientIncidentReport> report;
198 // The uploader in use. This is NULL until the CSD killswitch is checked.
199 scoped_ptr<IncidentReportUploader> uploader;
201 // A mapping of profiles to the data to be persisted upon successful upload.
202 PersistentIncidentStateCollection profiles_to_state;
204 private:
205 DISALLOW_COPY_AND_ASSIGN(UploadContext);
208 // An IncidentReceiver that is weakly-bound to the service and transparently
209 // bounces process-wide incidents back to the main thread for handling.
210 class IncidentReportingService::Receiver : public IncidentReceiver {
211 public:
212 explicit Receiver(const base::WeakPtr<IncidentReportingService>& service);
213 ~Receiver() override;
215 // IncidentReceiver methods:
216 void AddIncidentForProfile(Profile* profile,
217 scoped_ptr<Incident> incident) override;
218 void AddIncidentForProcess(scoped_ptr<Incident> incident) override;
220 private:
221 static void AddIncidentOnMainThread(
222 const base::WeakPtr<IncidentReportingService>& service,
223 Profile* profile,
224 scoped_ptr<Incident> incident);
226 base::WeakPtr<IncidentReportingService> service_;
227 scoped_refptr<base::SingleThreadTaskRunner> thread_runner_;
229 DISALLOW_COPY_AND_ASSIGN(Receiver);
232 IncidentReportingService::Receiver::Receiver(
233 const base::WeakPtr<IncidentReportingService>& service)
234 : service_(service),
235 thread_runner_(base::ThreadTaskRunnerHandle::Get()) {
238 IncidentReportingService::Receiver::~Receiver() {
241 void IncidentReportingService::Receiver::AddIncidentForProfile(
242 Profile* profile,
243 scoped_ptr<Incident> incident) {
244 DCHECK(thread_runner_->BelongsToCurrentThread());
245 DCHECK(profile);
246 AddIncidentOnMainThread(service_, profile, incident.Pass());
249 void IncidentReportingService::Receiver::AddIncidentForProcess(
250 scoped_ptr<Incident> incident) {
251 if (thread_runner_->BelongsToCurrentThread()) {
252 AddIncidentOnMainThread(service_, nullptr, incident.Pass());
253 } else if (!thread_runner_->PostTask(
254 FROM_HERE,
255 base::Bind(&IncidentReportingService::Receiver::AddIncidentOnMainThread,
256 service_, nullptr, base::Passed(&incident)))) {
257 LogIncidentDataType(DISCARDED, *incident);
261 // static
262 void IncidentReportingService::Receiver::AddIncidentOnMainThread(
263 const base::WeakPtr<IncidentReportingService>& service,
264 Profile* profile,
265 scoped_ptr<Incident> incident) {
266 if (service)
267 service->AddIncident(profile, incident.Pass());
268 else
269 LogIncidentDataType(DISCARDED, *incident);
272 IncidentReportingService::ProfileContext::ProfileContext() : added() {
275 IncidentReportingService::ProfileContext::~ProfileContext() {
276 for (Incident* incident : incidents) {
277 if (incident)
278 LogIncidentDataType(DISCARDED, *incident);
282 IncidentReportingService::UploadContext::UploadContext(
283 scoped_ptr<ClientIncidentReport> report)
284 : report(report.Pass()) {
287 IncidentReportingService::UploadContext::~UploadContext() {
290 IncidentReportingService::IncidentReportingService(
291 SafeBrowsingService* safe_browsing_service,
292 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter)
293 : database_manager_(safe_browsing_service
294 ? safe_browsing_service->database_manager()
295 : NULL),
296 url_request_context_getter_(request_context_getter),
297 collect_environment_data_fn_(&CollectEnvironmentData),
298 environment_collection_task_runner_(
299 content::BrowserThread::GetBlockingPool()
300 ->GetTaskRunnerWithShutdownBehavior(
301 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
302 environment_collection_pending_(),
303 collation_timeout_pending_(),
304 collation_timer_(FROM_HERE,
305 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
306 this,
307 &IncidentReportingService::OnCollationTimeout),
308 delayed_analysis_callbacks_(
309 base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs),
310 content::BrowserThread::GetBlockingPool()
311 ->GetTaskRunnerWithShutdownBehavior(
312 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
313 download_metadata_manager_(content::BrowserThread::GetBlockingPool()),
314 receiver_weak_ptr_factory_(this),
315 weak_ptr_factory_(this) {
316 notification_registrar_.Add(this,
317 chrome::NOTIFICATION_PROFILE_ADDED,
318 content::NotificationService::AllSources());
319 notification_registrar_.Add(this,
320 chrome::NOTIFICATION_PROFILE_DESTROYED,
321 content::NotificationService::AllSources());
322 DownloadProtectionService* download_protection_service =
323 (safe_browsing_service ?
324 safe_browsing_service->download_protection_service() :
325 NULL);
326 if (download_protection_service) {
327 client_download_request_subscription_ =
328 download_protection_service->RegisterClientDownloadRequestCallback(
329 base::Bind(&IncidentReportingService::OnClientDownloadRequest,
330 base::Unretained(this)));
334 IncidentReportingService::~IncidentReportingService() {
335 DCHECK(thread_checker_.CalledOnValidThread());
336 CancelIncidentCollection();
338 // Cancel all internal asynchronous tasks.
339 weak_ptr_factory_.InvalidateWeakPtrs();
341 CancelEnvironmentCollection();
342 CancelDownloadCollection();
343 CancelAllReportUploads();
345 STLDeleteValues(&profiles_);
348 scoped_ptr<IncidentReceiver> IncidentReportingService::GetIncidentReceiver() {
349 return make_scoped_ptr(new Receiver(receiver_weak_ptr_factory_.GetWeakPtr()));
352 scoped_ptr<TrackedPreferenceValidationDelegate>
353 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) {
354 DCHECK(thread_checker_.CalledOnValidThread());
356 if (profile->IsOffTheRecord())
357 return scoped_ptr<TrackedPreferenceValidationDelegate>();
358 return scoped_ptr<TrackedPreferenceValidationDelegate>(
359 new PreferenceValidationDelegate(profile, GetIncidentReceiver()));
362 void IncidentReportingService::RegisterDelayedAnalysisCallback(
363 const DelayedAnalysisCallback& callback) {
364 DCHECK(thread_checker_.CalledOnValidThread());
366 // |callback| will be run on the blocking pool. The receiver will bounce back
367 // to the origin thread if needed.
368 delayed_analysis_callbacks_.RegisterCallback(
369 base::Bind(callback, base::Passed(GetIncidentReceiver())));
371 // Start running the callbacks if any profiles are participating in safe
372 // browsing. If none are now, running will commence if/when a participaing
373 // profile is added.
374 if (FindEligibleProfile())
375 delayed_analysis_callbacks_.Start();
378 void IncidentReportingService::AddDownloadManager(
379 content::DownloadManager* download_manager) {
380 download_metadata_manager_.AddDownloadManager(download_manager);
383 IncidentReportingService::IncidentReportingService(
384 SafeBrowsingService* safe_browsing_service,
385 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
386 base::TimeDelta delayed_task_interval,
387 const scoped_refptr<base::TaskRunner>& delayed_task_runner)
388 : database_manager_(safe_browsing_service
389 ? safe_browsing_service->database_manager()
390 : NULL),
391 url_request_context_getter_(request_context_getter),
392 collect_environment_data_fn_(&CollectEnvironmentData),
393 environment_collection_task_runner_(
394 content::BrowserThread::GetBlockingPool()
395 ->GetTaskRunnerWithShutdownBehavior(
396 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
397 environment_collection_pending_(),
398 collation_timeout_pending_(),
399 collation_timer_(FROM_HERE,
400 base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
401 this,
402 &IncidentReportingService::OnCollationTimeout),
403 delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner),
404 download_metadata_manager_(content::BrowserThread::GetBlockingPool()),
405 receiver_weak_ptr_factory_(this),
406 weak_ptr_factory_(this) {
407 notification_registrar_.Add(this,
408 chrome::NOTIFICATION_PROFILE_ADDED,
409 content::NotificationService::AllSources());
410 notification_registrar_.Add(this,
411 chrome::NOTIFICATION_PROFILE_DESTROYED,
412 content::NotificationService::AllSources());
415 void IncidentReportingService::SetCollectEnvironmentHook(
416 CollectEnvironmentDataFn collect_environment_data_hook,
417 const scoped_refptr<base::TaskRunner>& task_runner) {
418 if (collect_environment_data_hook) {
419 collect_environment_data_fn_ = collect_environment_data_hook;
420 environment_collection_task_runner_ = task_runner;
421 } else {
422 collect_environment_data_fn_ = &CollectEnvironmentData;
423 environment_collection_task_runner_ =
424 content::BrowserThread::GetBlockingPool()
425 ->GetTaskRunnerWithShutdownBehavior(
426 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
430 void IncidentReportingService::OnProfileAdded(Profile* profile) {
431 DCHECK(thread_checker_.CalledOnValidThread());
433 // Track the addition of all profiles even when no report is being assembled
434 // so that the service can determine whether or not it can evaluate a
435 // profile's preferences at the time of incident addition.
436 ProfileContext* context = GetOrCreateProfileContext(profile);
437 context->added = true;
439 const bool safe_browsing_enabled =
440 profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
442 // Start processing delayed analysis callbacks if this new profile
443 // participates in safe browsing. Start is idempotent, so this is safe even if
444 // they're already running.
445 if (safe_browsing_enabled)
446 delayed_analysis_callbacks_.Start();
448 // Start a new report if this profile participates in safe browsing and there
449 // are process-wide incidents.
450 if (safe_browsing_enabled && GetProfileContext(NULL) &&
451 GetProfileContext(NULL)->incidents.size()) {
452 BeginReportProcessing();
455 CleanLegacyPruneState(profile);
457 // TODO(grt): register for pref change notifications to start delayed analysis
458 // and/or report processing if sb is currently disabled but subsequently
459 // enabled.
461 // Nothing else to do if a report is not being assembled.
462 if (!report_)
463 return;
465 // Drop all incidents associated with this profile that were received prior to
466 // its addition if the profile is not participating in safe browsing.
467 if (!context->incidents.empty() && !safe_browsing_enabled) {
468 for (Incident* incident : context->incidents)
469 LogIncidentDataType(DROPPED, *incident);
470 context->incidents.clear();
473 // Take another stab at finding the most recent download if a report is being
474 // assembled and one hasn't been found yet (the LastDownloadFinder operates
475 // only on profiles that have been added to the ProfileManager).
476 BeginDownloadCollection();
479 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder(
480 const LastDownloadFinder::LastDownloadCallback& callback) {
481 return LastDownloadFinder::Create(
482 base::Bind(&DownloadMetadataManager::GetDownloadDetails,
483 base::Unretained(&download_metadata_manager_)),
484 callback).Pass();
487 scoped_ptr<IncidentReportUploader> IncidentReportingService::StartReportUpload(
488 const IncidentReportUploader::OnResultCallback& callback,
489 const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
490 const ClientIncidentReport& report) {
491 return IncidentReportUploaderImpl::UploadReport(
492 callback, request_context_getter, report).Pass();
495 bool IncidentReportingService::IsProcessingReport() const {
496 return report_ != NULL;
499 IncidentReportingService::ProfileContext*
500 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
501 ProfileContextCollection::iterator it =
502 profiles_.insert(ProfileContextCollection::value_type(profile, NULL))
503 .first;
504 if (!it->second)
505 it->second = new ProfileContext();
506 return it->second;
509 IncidentReportingService::ProfileContext*
510 IncidentReportingService::GetProfileContext(Profile* profile) {
511 ProfileContextCollection::iterator it = profiles_.find(profile);
512 return it == profiles_.end() ? NULL : it->second;
515 void IncidentReportingService::OnProfileDestroyed(Profile* profile) {
516 DCHECK(thread_checker_.CalledOnValidThread());
518 ProfileContextCollection::iterator it = profiles_.find(profile);
519 if (it == profiles_.end())
520 return;
522 // TODO(grt): Persist incidents for upload on future profile load.
524 // Forget about this profile. Incidents not yet sent for upload are lost.
525 // No new incidents will be accepted for it.
526 delete it->second;
527 profiles_.erase(it);
529 // Remove the association with this profile from all pending uploads.
530 for (size_t i = 0; i < uploads_.size(); ++i)
531 uploads_[i]->profiles_to_state.erase(profile);
534 Profile* IncidentReportingService::FindEligibleProfile() const {
535 Profile* candidate = NULL;
536 for (ProfileContextCollection::const_iterator scan = profiles_.begin();
537 scan != profiles_.end();
538 ++scan) {
539 // Skip over profiles that have yet to be added to the profile manager.
540 // This will also skip over the NULL-profile context used to hold
541 // process-wide incidents.
542 if (!scan->second->added)
543 continue;
544 PrefService* prefs = scan->first->GetPrefs();
545 if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
546 if (!candidate)
547 candidate = scan->first;
548 if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
549 candidate = scan->first;
550 break;
554 return candidate;
557 void IncidentReportingService::AddIncident(Profile* profile,
558 scoped_ptr<Incident> incident) {
559 DCHECK(thread_checker_.CalledOnValidThread());
561 // Ignore incidents from off-the-record profiles.
562 if (profile && profile->IsOffTheRecord())
563 return;
565 ProfileContext* context = GetOrCreateProfileContext(profile);
566 // If this is a process-wide incident, the context must not indicate that the
567 // profile (which is NULL) has been added to the profile manager.
568 DCHECK(profile || !context->added);
570 LogIncidentDataType(RECEIVED, *incident);
572 // Drop the incident immediately if the profile has already been added to the
573 // manager and is not participating in safe browsing. Preference evaluation is
574 // deferred until OnProfileAdded() otherwise.
575 if (context->added &&
576 !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
577 LogIncidentDataType(DROPPED, *incident);
578 return;
581 // Take ownership of the incident.
582 context->incidents.push_back(incident.release());
584 // Remember when the first incident for this report arrived.
585 if (first_incident_time_.is_null())
586 first_incident_time_ = base::Time::Now();
587 // Log the time between the previous incident and this one.
588 if (!last_incident_time_.is_null()) {
589 UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime",
590 base::TimeTicks::Now() - last_incident_time_);
592 last_incident_time_ = base::TimeTicks::Now();
594 // Persist the incident data.
596 // Start assembling a new report if this is the first incident ever or the
597 // first since the last upload.
598 BeginReportProcessing();
601 void IncidentReportingService::BeginReportProcessing() {
602 DCHECK(thread_checker_.CalledOnValidThread());
604 // Creates a new report if needed.
605 if (!report_)
606 report_.reset(new ClientIncidentReport());
608 // Ensure that collection tasks are running (calls are idempotent).
609 BeginIncidentCollation();
610 BeginEnvironmentCollection();
611 BeginDownloadCollection();
614 void IncidentReportingService::BeginIncidentCollation() {
615 // Restart the delay timer to send the report upon expiration.
616 collation_timeout_pending_ = true;
617 collation_timer_.Reset();
620 void IncidentReportingService::BeginEnvironmentCollection() {
621 DCHECK(thread_checker_.CalledOnValidThread());
622 DCHECK(report_);
623 // Nothing to do if environment collection is pending or has already
624 // completed.
625 if (environment_collection_pending_ || report_->has_environment())
626 return;
628 environment_collection_begin_ = base::TimeTicks::Now();
629 ClientIncidentReport_EnvironmentData* environment_data =
630 new ClientIncidentReport_EnvironmentData();
631 environment_collection_pending_ =
632 environment_collection_task_runner_->PostTaskAndReply(
633 FROM_HERE,
634 base::Bind(collect_environment_data_fn_, environment_data),
635 base::Bind(&IncidentReportingService::OnEnvironmentDataCollected,
636 weak_ptr_factory_.GetWeakPtr(),
637 base::Passed(make_scoped_ptr(environment_data))));
639 // Posting the task will fail if the runner has been shut down. This should
640 // never happen since the blocking pool is shut down after this service.
641 DCHECK(environment_collection_pending_);
644 bool IncidentReportingService::WaitingForEnvironmentCollection() {
645 return environment_collection_pending_;
648 void IncidentReportingService::CancelEnvironmentCollection() {
649 environment_collection_begin_ = base::TimeTicks();
650 environment_collection_pending_ = false;
651 if (report_)
652 report_->clear_environment();
655 void IncidentReportingService::OnEnvironmentDataCollected(
656 scoped_ptr<ClientIncidentReport_EnvironmentData> environment_data) {
657 DCHECK(thread_checker_.CalledOnValidThread());
658 DCHECK(environment_collection_pending_);
659 DCHECK(report_ && !report_->has_environment());
660 environment_collection_pending_ = false;
662 // CurrentProcessInfo::CreationTime() is missing on some platforms.
663 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
664 base::TimeDelta uptime =
665 first_incident_time_ - base::CurrentProcessInfo::CreationTime();
666 environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds());
667 #endif
669 report_->set_allocated_environment(environment_data.release());
671 UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime",
672 base::TimeTicks::Now() - environment_collection_begin_);
673 environment_collection_begin_ = base::TimeTicks();
675 UploadIfCollectionComplete();
678 bool IncidentReportingService::WaitingToCollateIncidents() {
679 return collation_timeout_pending_;
682 void IncidentReportingService::CancelIncidentCollection() {
683 collation_timeout_pending_ = false;
684 last_incident_time_ = base::TimeTicks();
685 report_.reset();
688 void IncidentReportingService::OnCollationTimeout() {
689 DCHECK(thread_checker_.CalledOnValidThread());
691 // Exit early if collection was cancelled.
692 if (!collation_timeout_pending_)
693 return;
695 // Wait another round if profile-bound incidents have come in from a profile
696 // that has yet to complete creation.
697 for (ProfileContextCollection::iterator scan = profiles_.begin();
698 scan != profiles_.end();
699 ++scan) {
700 if (scan->first && !scan->second->added &&
701 !scan->second->incidents.empty()) {
702 collation_timer_.Reset();
703 return;
707 collation_timeout_pending_ = false;
709 UploadIfCollectionComplete();
712 void IncidentReportingService::BeginDownloadCollection() {
713 DCHECK(thread_checker_.CalledOnValidThread());
714 DCHECK(report_);
715 // Nothing to do if a search for the most recent download is already pending
716 // or if one has already been found.
717 if (last_download_finder_ || report_->has_download())
718 return;
720 last_download_begin_ = base::TimeTicks::Now();
721 last_download_finder_ = CreateDownloadFinder(
722 base::Bind(&IncidentReportingService::OnLastDownloadFound,
723 weak_ptr_factory_.GetWeakPtr()));
724 // No instance is returned if there are no eligible loaded profiles. Another
725 // search will be attempted in OnProfileAdded() if another profile appears on
726 // the scene.
727 if (!last_download_finder_)
728 last_download_begin_ = base::TimeTicks();
731 bool IncidentReportingService::WaitingForMostRecentDownload() {
732 DCHECK(report_); // Only call this when a report is being assembled.
733 // The easy case: not waiting if a download has already been found.
734 if (report_->has_download())
735 return false;
736 // The next easy case: waiting if the finder is operating.
737 if (last_download_finder_)
738 return true;
739 // The harder case: waiting if a non-NULL profile has not yet been added.
740 for (ProfileContextCollection::const_iterator scan = profiles_.begin();
741 scan != profiles_.end();
742 ++scan) {
743 if (scan->first && !scan->second->added)
744 return true;
746 // There is no most recent download and there's nothing more to wait for.
747 return false;
750 void IncidentReportingService::CancelDownloadCollection() {
751 last_download_finder_.reset();
752 last_download_begin_ = base::TimeTicks();
753 if (report_)
754 report_->clear_download();
757 void IncidentReportingService::OnLastDownloadFound(
758 scoped_ptr<ClientIncidentReport_DownloadDetails> last_download) {
759 DCHECK(thread_checker_.CalledOnValidThread());
760 DCHECK(report_);
762 UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime",
763 base::TimeTicks::Now() - last_download_begin_);
764 last_download_begin_ = base::TimeTicks();
766 // Harvest the finder.
767 last_download_finder_.reset();
769 if (last_download)
770 report_->set_allocated_download(last_download.release());
772 UploadIfCollectionComplete();
775 void IncidentReportingService::UploadIfCollectionComplete() {
776 DCHECK(report_);
777 // Bail out if there are still outstanding collection tasks. Completion of any
778 // of these will start another upload attempt.
779 if (WaitingForEnvironmentCollection() ||
780 WaitingToCollateIncidents() ||
781 WaitingForMostRecentDownload()) {
782 return;
785 // Take ownership of the report and clear things for future reports.
786 scoped_ptr<ClientIncidentReport> report(report_.Pass());
787 first_incident_time_ = base::Time();
788 last_incident_time_ = base::TimeTicks();
790 // Drop the report if no executable download was found.
791 if (!report->has_download()) {
792 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
793 IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
794 IncidentReportUploader::NUM_UPLOAD_RESULTS);
795 return;
798 ClientIncidentReport_EnvironmentData_Process* process =
799 report->mutable_environment()->mutable_process();
801 // Not all platforms have a metrics reporting preference.
802 if (g_browser_process->local_state()->FindPreference(
803 prefs::kMetricsReportingEnabled)) {
804 process->set_metrics_consent(g_browser_process->local_state()->GetBoolean(
805 prefs::kMetricsReportingEnabled));
808 // Find the profile that benefits from the strongest protections.
809 Profile* eligible_profile = FindEligibleProfile();
810 process->set_extended_consent(
811 eligible_profile ? eligible_profile->GetPrefs()->GetBoolean(
812 prefs::kSafeBrowsingExtendedReportingEnabled) :
813 false);
815 // Associate process-wide incidents with the profile that benefits from the
816 // strongest safe browsing protections.
817 ProfileContext* null_context = GetProfileContext(NULL);
818 if (null_context && !null_context->incidents.empty() && eligible_profile) {
819 ProfileContext* eligible_context = GetProfileContext(eligible_profile);
820 // Move the incidents to the target context.
821 eligible_context->incidents.insert(eligible_context->incidents.end(),
822 null_context->incidents.begin(),
823 null_context->incidents.end());
824 null_context->incidents.weak_clear();
827 // Collect incidents across all profiles participating in safe browsing. Drop
828 // incidents if the profile stopped participating before collection completed.
829 // Prune previously submitted incidents.
830 // Associate the profiles and their incident data with the upload.
831 size_t prune_count = 0;
832 UploadContext::PersistentIncidentStateCollection profiles_to_state;
833 for (ProfileContextCollection::iterator scan = profiles_.begin();
834 scan != profiles_.end();
835 ++scan) {
836 // Bypass process-wide incidents that have not yet been associated with a
837 // profile.
838 if (!scan->first)
839 continue;
840 PrefService* prefs = scan->first->GetPrefs();
841 ProfileContext* context = scan->second;
842 if (context->incidents.empty())
843 continue;
844 if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
845 for (Incident* incident : context->incidents)
846 LogIncidentDataType(DROPPED, *incident);
847 context->incidents.clear();
848 continue;
850 std::vector<PersistentIncidentState> states;
851 const base::DictionaryValue* incidents_sent =
852 prefs->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
853 // Prep persistent data and prune any incidents already sent.
854 for (Incident* incident : context->incidents) {
855 const PersistentIncidentState state = ComputeIncidentState(*incident);
856 if (IncidentHasBeenReported(incidents_sent, state)) {
857 LogIncidentDataType(PRUNED, *incident);
858 ++prune_count;
859 } else {
860 LogIncidentDataType(ACCEPTED, *incident);
861 // Ownership of the payload is passed to the report.
862 ClientIncidentReport_IncidentData* data =
863 incident->TakePayload().release();
864 DCHECK(data->has_incident_time_msec());
865 report->mutable_incident()->AddAllocated(data);
866 data = nullptr;
867 states.push_back(state);
870 context->incidents.clear();
871 profiles_to_state[scan->first].swap(states);
874 const int count = report->incident_size();
875 // Abandon the request if all incidents were dropped with none pruned.
876 if (!count && !prune_count)
877 return;
879 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count);
882 double prune_pct = static_cast<double>(prune_count);
883 prune_pct = prune_pct * 100.0 / (count + prune_count);
884 prune_pct = round(prune_pct);
885 UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct));
887 // Abandon the report if all incidents were pruned.
888 if (!count)
889 return;
891 scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
892 context->profiles_to_state.swap(profiles_to_state);
893 if (!database_manager_.get()) {
894 // No database manager during testing. Take ownership of the context and
895 // continue processing.
896 UploadContext* temp_context = context.get();
897 uploads_.push_back(context.release());
898 IncidentReportingService::OnKillSwitchResult(temp_context, false);
899 } else {
900 if (content::BrowserThread::PostTaskAndReplyWithResult(
901 content::BrowserThread::IO,
902 FROM_HERE,
903 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn,
904 database_manager_),
905 base::Bind(&IncidentReportingService::OnKillSwitchResult,
906 weak_ptr_factory_.GetWeakPtr(),
907 context.get()))) {
908 uploads_.push_back(context.release());
909 } // else should not happen. Let the context be deleted automatically.
913 void IncidentReportingService::CancelAllReportUploads() {
914 for (size_t i = 0; i < uploads_.size(); ++i) {
915 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
916 IncidentReportUploader::UPLOAD_CANCELLED,
917 IncidentReportUploader::NUM_UPLOAD_RESULTS);
919 uploads_.clear();
922 void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
923 bool is_killswitch_on) {
924 DCHECK(thread_checker_.CalledOnValidThread());
925 if (!is_killswitch_on) {
926 // Initiate the upload.
927 context->uploader =
928 StartReportUpload(
929 base::Bind(&IncidentReportingService::OnReportUploadResult,
930 weak_ptr_factory_.GetWeakPtr(),
931 context),
932 url_request_context_getter_,
933 *context->report).Pass();
934 if (!context->uploader) {
935 OnReportUploadResult(context,
936 IncidentReportUploader::UPLOAD_INVALID_REQUEST,
937 scoped_ptr<ClientIncidentResponse>());
939 } else {
940 OnReportUploadResult(context,
941 IncidentReportUploader::UPLOAD_SUPPRESSED,
942 scoped_ptr<ClientIncidentResponse>());
946 void IncidentReportingService::HandleResponse(const UploadContext& context) {
947 for (UploadContext::PersistentIncidentStateCollection::const_iterator scan =
948 context.profiles_to_state.begin();
949 scan != context.profiles_to_state.end();
950 ++scan) {
951 DictionaryPrefUpdate pref_update(scan->first->GetPrefs(),
952 prefs::kSafeBrowsingIncidentsSent);
953 MarkIncidentsAsReported(scan->second, pref_update.Get());
957 void IncidentReportingService::OnReportUploadResult(
958 UploadContext* context,
959 IncidentReportUploader::Result result,
960 scoped_ptr<ClientIncidentResponse> response) {
961 DCHECK(thread_checker_.CalledOnValidThread());
963 UMA_HISTOGRAM_ENUMERATION(
964 "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
966 // The upload is no longer outstanding, so take ownership of the context (from
967 // the collection of outstanding uploads) in this scope.
968 ScopedVector<UploadContext>::iterator it(
969 std::find(uploads_.begin(), uploads_.end(), context));
970 DCHECK(it != uploads_.end());
971 scoped_ptr<UploadContext> upload(context); // == *it
972 *it = uploads_.back();
973 uploads_.weak_erase(uploads_.end() - 1);
975 if (result == IncidentReportUploader::UPLOAD_SUCCESS)
976 HandleResponse(*upload);
977 // else retry?
980 void IncidentReportingService::OnClientDownloadRequest(
981 content::DownloadItem* download,
982 const ClientDownloadRequest* request) {
983 if (!download->GetBrowserContext()->IsOffTheRecord())
984 download_metadata_manager_.SetRequest(download, request);
987 void IncidentReportingService::Observe(
988 int type,
989 const content::NotificationSource& source,
990 const content::NotificationDetails& details) {
991 switch (type) {
992 case chrome::NOTIFICATION_PROFILE_ADDED: {
993 Profile* profile = content::Source<Profile>(source).ptr();
994 if (!profile->IsOffTheRecord())
995 OnProfileAdded(profile);
996 break;
998 case chrome::NOTIFICATION_PROFILE_DESTROYED: {
999 Profile* profile = content::Source<Profile>(source).ptr();
1000 if (!profile->IsOffTheRecord())
1001 OnProfileDestroyed(profile);
1002 break;
1004 default:
1005 break;
1009 } // namespace safe_browsing