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/chrome_notification_types.h"
20 #include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/safe_browsing/database_manager.h"
23 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h"
24 #include "chrome/browser/safe_browsing/incident_reporting/incident.h"
25 #include "chrome/browser/safe_browsing/incident_reporting/incident_receiver.h"
26 #include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h"
27 #include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h"
28 #include "chrome/browser/safe_browsing/incident_reporting/state_store.h"
29 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
30 #include "chrome/common/pref_names.h"
31 #include "chrome/common/safe_browsing/csd.pb.h"
32 #include "components/user_prefs/tracked/tracked_preference_validation_delegate.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "net/url_request/url_request_context_getter.h"
37 namespace safe_browsing
{
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 process
->set_metrics_consent(
738 ChromeMetricsServiceAccessor::IsMetricsAndCrashReportingEnabled());
740 // Find the profile that benefits from the strongest protections.
741 Profile
* eligible_profile
= FindEligibleProfile();
742 process
->set_extended_consent(
743 eligible_profile
? eligible_profile
->GetPrefs()->GetBoolean(
744 prefs::kSafeBrowsingExtendedReportingEnabled
) :
747 // Associate process-wide incidents with the profile that benefits from the
748 // strongest safe browsing protections.
749 ProfileContext
* null_context
= GetProfileContext(NULL
);
750 if (null_context
&& !null_context
->incidents
.empty() && eligible_profile
) {
751 ProfileContext
* eligible_context
= GetProfileContext(eligible_profile
);
752 // Move the incidents to the target context.
753 eligible_context
->incidents
.insert(eligible_context
->incidents
.end(),
754 null_context
->incidents
.begin(),
755 null_context
->incidents
.end());
756 null_context
->incidents
.weak_clear();
759 // Collect incidents across all profiles participating in safe browsing. Drop
760 // incidents if the profile stopped participating before collection completed.
761 // Prune previously submitted incidents.
762 // Associate the profile contexts and their incident data with the upload.
763 UploadContext::PersistentIncidentStateCollection profiles_to_state
;
764 for (ProfileContextCollection::iterator scan
= profiles_
.begin();
765 scan
!= profiles_
.end();
767 // Bypass process-wide incidents that have not yet been associated with a
771 PrefService
* prefs
= scan
->first
->GetPrefs();
772 ProfileContext
* context
= scan
->second
;
773 if (context
->incidents
.empty())
775 if (!prefs
->GetBoolean(prefs::kSafeBrowsingEnabled
)) {
776 for (Incident
* incident
: context
->incidents
)
777 LogIncidentDataType(DROPPED
, *incident
);
778 context
->incidents
.clear();
781 StateStore::Transaction
transaction(context
->state_store
.get());
782 std::vector
<PersistentIncidentState
> states
;
783 // Prep persistent data and prune any incidents already sent.
784 for (Incident
* incident
: context
->incidents
) {
785 const PersistentIncidentState state
= ComputeIncidentState(*incident
);
786 if (context
->state_store
->HasBeenReported(state
.type
, state
.key
,
788 LogIncidentDataType(PRUNED
, *incident
);
789 } else if (!report
->has_download()) {
790 LogIncidentDataType(NO_DOWNLOAD
, *incident
);
791 // Drop the incident and mark for future pruning since no executable
792 // download was found.
793 transaction
.MarkAsReported(state
.type
, state
.key
, state
.digest
);
795 LogIncidentDataType(ACCEPTED
, *incident
);
796 // Ownership of the payload is passed to the report.
797 ClientIncidentReport_IncidentData
* data
=
798 incident
->TakePayload().release();
799 DCHECK(data
->has_incident_time_msec());
800 report
->mutable_incident()->AddAllocated(data
);
802 states
.push_back(state
);
805 context
->incidents
.clear();
806 profiles_to_state
[context
].swap(states
);
809 const int count
= report
->incident_size();
811 // Abandon the request if all incidents were pruned or otherwise dropped.
813 if (!report
->has_download()) {
814 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
815 IncidentReportUploader::UPLOAD_NO_DOWNLOAD
,
816 IncidentReportUploader::NUM_UPLOAD_RESULTS
);
821 UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count
);
823 scoped_ptr
<UploadContext
> context(new UploadContext(report
.Pass()));
824 context
->profiles_to_state
.swap(profiles_to_state
);
825 if (!database_manager_
.get()) {
826 // No database manager during testing. Take ownership of the context and
827 // continue processing.
828 UploadContext
* temp_context
= context
.get();
829 uploads_
.push_back(context
.release());
830 IncidentReportingService::OnKillSwitchResult(temp_context
, false);
832 if (content::BrowserThread::PostTaskAndReplyWithResult(
833 content::BrowserThread::IO
,
835 base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn
,
837 base::Bind(&IncidentReportingService::OnKillSwitchResult
,
838 weak_ptr_factory_
.GetWeakPtr(),
840 uploads_
.push_back(context
.release());
841 } // else should not happen. Let the context be deleted automatically.
845 void IncidentReportingService::CancelAllReportUploads() {
846 for (size_t i
= 0; i
< uploads_
.size(); ++i
) {
847 UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
848 IncidentReportUploader::UPLOAD_CANCELLED
,
849 IncidentReportUploader::NUM_UPLOAD_RESULTS
);
854 void IncidentReportingService::OnKillSwitchResult(UploadContext
* context
,
855 bool is_killswitch_on
) {
856 DCHECK(thread_checker_
.CalledOnValidThread());
857 if (!is_killswitch_on
) {
858 // Initiate the upload.
861 base::Bind(&IncidentReportingService::OnReportUploadResult
,
862 weak_ptr_factory_
.GetWeakPtr(),
864 url_request_context_getter_
,
865 *context
->report
).Pass();
866 if (!context
->uploader
) {
867 OnReportUploadResult(context
,
868 IncidentReportUploader::UPLOAD_INVALID_REQUEST
,
869 scoped_ptr
<ClientIncidentResponse
>());
872 OnReportUploadResult(context
,
873 IncidentReportUploader::UPLOAD_SUPPRESSED
,
874 scoped_ptr
<ClientIncidentResponse
>());
878 void IncidentReportingService::HandleResponse(const UploadContext
& context
) {
879 // Mark each incident as reported in its corresponding profile's state store.
880 for (const auto& context_and_states
: context
.profiles_to_state
) {
881 StateStore::Transaction
transaction(
882 context_and_states
.first
->state_store
.get());
883 for (const auto& state
: context_and_states
.second
)
884 transaction
.MarkAsReported(state
.type
, state
.key
, state
.digest
);
888 void IncidentReportingService::OnReportUploadResult(
889 UploadContext
* context
,
890 IncidentReportUploader::Result result
,
891 scoped_ptr
<ClientIncidentResponse
> response
) {
892 DCHECK(thread_checker_
.CalledOnValidThread());
894 UMA_HISTOGRAM_ENUMERATION(
895 "SBIRS.UploadResult", result
, IncidentReportUploader::NUM_UPLOAD_RESULTS
);
897 // The upload is no longer outstanding, so take ownership of the context (from
898 // the collection of outstanding uploads) in this scope.
899 ScopedVector
<UploadContext
>::iterator
it(
900 std::find(uploads_
.begin(), uploads_
.end(), context
));
901 DCHECK(it
!= uploads_
.end());
902 scoped_ptr
<UploadContext
> upload(context
); // == *it
903 *it
= uploads_
.back();
904 uploads_
.weak_erase(uploads_
.end() - 1);
906 if (result
== IncidentReportUploader::UPLOAD_SUCCESS
)
907 HandleResponse(*upload
);
911 void IncidentReportingService::OnClientDownloadRequest(
912 content::DownloadItem
* download
,
913 const ClientDownloadRequest
* request
) {
914 if (!download
->GetBrowserContext()->IsOffTheRecord())
915 download_metadata_manager_
.SetRequest(download
, request
);
918 void IncidentReportingService::Observe(
920 const content::NotificationSource
& source
,
921 const content::NotificationDetails
& details
) {
923 case chrome::NOTIFICATION_PROFILE_ADDED
: {
924 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
925 if (!profile
->IsOffTheRecord())
926 OnProfileAdded(profile
);
929 case chrome::NOTIFICATION_PROFILE_DESTROYED
: {
930 Profile
* profile
= content::Source
<Profile
>(source
).ptr();
931 if (!profile
->IsOffTheRecord())
932 OnProfileDestroyed(profile
);
940 } // namespace safe_browsing