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"
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
{
41 // The action taken for an incident; used for user metrics (see
42 // LogIncidentDataType).
43 enum IncidentDisposition
{
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.
59 // The key for a specific instance of an incident.
62 // A hash digest representing a specific instance of an incident.
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",
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
],
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
= {
101 incident
.ComputeDigest(),
108 struct IncidentReportingService::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
118 scoped_ptr
<StateStore
> state_store
;
120 // False until PROFILE_ADDED notification is received.
124 DISALLOW_COPY_AND_ASSIGN(ProfileContext
);
127 class IncidentReportingService::UploadContext
{
129 typedef std::map
<ProfileContext
*, std::vector
<PersistentIncidentState
>>
130 PersistentIncidentStateCollection
;
132 explicit UploadContext(scoped_ptr
<ClientIncidentReport
> report
);
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
143 PersistentIncidentStateCollection profiles_to_state
;
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
{
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
;
162 static void AddIncidentOnMainThread(
163 const base::WeakPtr
<IncidentReportingService
>& service
,
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
)
176 thread_runner_(base::ThreadTaskRunnerHandle::Get()) {
179 IncidentReportingService::Receiver::~Receiver() {
182 void IncidentReportingService::Receiver::AddIncidentForProfile(
184 scoped_ptr
<Incident
> incident
) {
185 DCHECK(thread_runner_
->BelongsToCurrentThread());
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(
196 base::Bind(&IncidentReportingService::Receiver::AddIncidentOnMainThread
,
197 service_
, nullptr, base::Passed(&incident
)))) {
198 LogIncidentDataType(DISCARDED
, *incident
);
203 void IncidentReportingService::Receiver::AddIncidentOnMainThread(
204 const base::WeakPtr
<IncidentReportingService
>& service
,
206 scoped_ptr
<Incident
> incident
) {
208 service
->AddIncident(profile
, incident
.Pass());
210 LogIncidentDataType(DISCARDED
, *incident
);
213 IncidentReportingService::ProfileContext::ProfileContext() : added(false) {
216 IncidentReportingService::ProfileContext::~ProfileContext() {
217 for (Incident
* incident
: incidents
) {
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()
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
),
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() :
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
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()
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
),
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
;
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
402 // Nothing else to do if a report is not being assembled.
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_
)),
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
))
446 it
->second
= new ProfileContext();
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())
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.
478 Profile
* IncidentReportingService::FindEligibleProfile() const {
479 Profile
* candidate
= NULL
;
480 for (ProfileContextCollection::const_iterator scan
= profiles_
.begin();
481 scan
!= profiles_
.end();
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
)
488 PrefService
* prefs
= scan
->first
->GetPrefs();
489 if (prefs
->GetBoolean(prefs::kSafeBrowsingEnabled
)) {
491 candidate
= scan
->first
;
492 if (prefs
->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled
)) {
493 candidate
= scan
->first
;
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())
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
);
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.
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());
567 // Nothing to do if environment collection is pending or has already
569 if (environment_collection_pending_
|| report_
->has_environment())
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(
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;
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());
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();
632 void IncidentReportingService::OnCollationTimeout() {
633 DCHECK(thread_checker_
.CalledOnValidThread());
635 // Exit early if collection was cancelled.
636 if (!collation_timeout_pending_
)
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();
644 if (scan
->first
&& !scan
->second
->added
&&
645 !scan
->second
->incidents
.empty()) {
646 collation_timer_
.Reset();
651 collation_timeout_pending_
= false;
653 UploadIfCollectionComplete();
656 void IncidentReportingService::BeginDownloadCollection() {
657 DCHECK(thread_checker_
.CalledOnValidThread());
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())
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
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())
680 // The next easy case: waiting if the finder is operating.
681 if (last_download_finder_
)
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();
687 if (scan
->first
&& !scan
->second
->added
)
690 // There is no most recent download and there's nothing more to wait for.
694 void IncidentReportingService::CancelDownloadCollection() {
695 last_download_finder_
.reset();
696 last_download_begin_
= base::TimeTicks();
698 report_
->clear_download();
701 void IncidentReportingService::OnLastDownloadFound(
702 scoped_ptr
<ClientIncidentReport_DownloadDetails
> last_download
) {
703 DCHECK(thread_checker_
.CalledOnValidThread());
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();
714 report_
->set_allocated_download(last_download
.release());
716 UploadIfCollectionComplete();
719 void IncidentReportingService::UploadIfCollectionComplete() {
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()) {
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
) :
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();
771 // Bypass process-wide incidents that have not yet been associated with a
775 PrefService
* prefs
= scan
->first
->GetPrefs();
776 ProfileContext
* context
= scan
->second
;
777 if (context
->incidents
.empty())
779 if (!prefs
->GetBoolean(prefs::kSafeBrowsingEnabled
)) {
780 for (Incident
* incident
: context
->incidents
)
781 LogIncidentDataType(DROPPED
, *incident
);
782 context
->incidents
.clear();
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
,
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
);
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
);
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.
817 if (!report
->has_download()) {
818 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
819 IncidentReportUploader::UPLOAD_NO_DOWNLOAD
,
820 IncidentReportUploader::NUM_UPLOAD_RESULTS
);
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);
836 if (content::BrowserThread::PostTaskAndReplyWithResult(
837 content::BrowserThread::IO
,
839 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn
,
841 base::Bind(&IncidentReportingService::OnKillSwitchResult
,
842 weak_ptr_factory_
.GetWeakPtr(),
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
);
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.
865 base::Bind(&IncidentReportingService::OnReportUploadResult
,
866 weak_ptr_factory_
.GetWeakPtr(),
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
>());
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
);
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(
924 const content::NotificationSource
& source
,
925 const content::NotificationDetails
& details
) {
927 case chrome::NOTIFICATION_PROFILE_ADDED
: {
928 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
929 if (!profile
->IsOffTheRecord())
930 OnProfileAdded(profile
);
933 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
934 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
935 if (!profile
->IsOffTheRecord())
936 OnProfileDestroyed(profile
);
944 } // namespace safe_browsing