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