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