Home | History | Annotate | Download | only in incident_reporting
      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.
      4 
      5 #include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
      6 
      7 #include <math.h>
      8 
      9 #include <algorithm>
     10 #include <vector>
     11 
     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/binary_integrity_incident_handlers.h"
     28 #include "chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_handlers.h"
     29 #include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.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/incident_reporting/tracked_preference_incident_handlers.h"
     33 #include "chrome/browser/safe_browsing/safe_browsing_service.h"
     34 #include "chrome/common/pref_names.h"
     35 #include "chrome/common/safe_browsing/csd.pb.h"
     36 #include "content/public/browser/browser_thread.h"
     37 #include "content/public/browser/notification_service.h"
     38 #include "net/url_request/url_request_context_getter.h"
     39 
     40 namespace safe_browsing {
     41 
     42 namespace {
     43 
     44 // The type of an incident. Used for user metrics and for pruning of
     45 // previously-reported incidents.
     46 enum IncidentType {
     47   // Start with 1 rather than zero; otherwise there won't be enough buckets for
     48   // the histogram.
     49   TRACKED_PREFERENCE = 1,
     50   BINARY_INTEGRITY = 2,
     51   BLACKLIST_LOAD = 3,
     52   // Values for new incident types go here.
     53   NUM_INCIDENT_TYPES = 4
     54 };
     55 
     56 // The action taken for an incident; used for user metrics (see
     57 // LogIncidentDataType).
     58 enum IncidentDisposition {
     59   DROPPED,
     60   ACCEPTED,
     61 };
     62 
     63 // The state persisted for a specific instance of an incident to enable pruning
     64 // of previously-reported incidents.
     65 struct PersistentIncidentState {
     66   // The type of the incident.
     67   IncidentType type;
     68 
     69   // The key for a specific instance of an incident.
     70   std::string key;
     71 
     72   // A hash digest representing a specific instance of an incident.
     73   uint32_t digest;
     74 };
     75 
     76 // The amount of time the service will wait to collate incidents.
     77 const int64 kDefaultUploadDelayMs = 1000 * 60;  // one minute
     78 
     79 // The amount of time between running delayed analysis callbacks.
     80 const int64 kDefaultCallbackIntervalMs = 1000 * 20;
     81 
     82 // Returns the number of incidents contained in |incident|. The result is
     83 // expected to be 1. Used in DCHECKs.
     84 size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) {
     85   size_t result = 0;
     86   if (incident.has_tracked_preference())
     87     ++result;
     88   if (incident.has_binary_integrity())
     89     ++result;
     90   if (incident.has_blacklist_load())
     91     ++result;
     92   // Add detection for new incident types here.
     93   return result;
     94 }
     95 
     96 // Returns the type of incident contained in |incident_data|.
     97 IncidentType GetIncidentType(
     98     const ClientIncidentReport_IncidentData& incident_data) {
     99   if (incident_data.has_tracked_preference())
    100     return TRACKED_PREFERENCE;
    101   if (incident_data.has_binary_integrity())
    102     return BINARY_INTEGRITY;
    103   if (incident_data.has_blacklist_load())
    104     return BLACKLIST_LOAD;
    105 
    106   // Add detection for new incident types here.
    107   COMPILE_ASSERT(BLACKLIST_LOAD + 1 == NUM_INCIDENT_TYPES,
    108                  add_support_for_new_types);
    109   NOTREACHED();
    110   return NUM_INCIDENT_TYPES;
    111 }
    112 
    113 // Logs the type of incident in |incident_data| to a user metrics histogram.
    114 void LogIncidentDataType(
    115     IncidentDisposition disposition,
    116     const ClientIncidentReport_IncidentData& incident_data) {
    117   IncidentType type = GetIncidentType(incident_data);
    118   if (disposition == ACCEPTED) {
    119     UMA_HISTOGRAM_ENUMERATION("SBIRS.Incident", type, NUM_INCIDENT_TYPES);
    120   } else {
    121     DCHECK_EQ(disposition, DROPPED);
    122     UMA_HISTOGRAM_ENUMERATION("SBIRS.DroppedIncident", type,
    123                               NUM_INCIDENT_TYPES);
    124   }
    125 }
    126 
    127 // Computes the persistent state for an incident.
    128 PersistentIncidentState ComputeIncidentState(
    129     const ClientIncidentReport_IncidentData& incident) {
    130   PersistentIncidentState state = {GetIncidentType(incident)};
    131   switch (state.type) {
    132     case TRACKED_PREFERENCE:
    133       state.key = GetTrackedPreferenceIncidentKey(incident);
    134       state.digest = GetTrackedPreferenceIncidentDigest(incident);
    135       break;
    136     case BINARY_INTEGRITY:
    137       state.key = GetBinaryIntegrityIncidentKey(incident);
    138       state.digest = GetBinaryIntegrityIncidentDigest(incident);
    139       break;
    140     case BLACKLIST_LOAD:
    141       state.key = GetBlacklistLoadIncidentKey(incident);
    142       state.digest = GetBlacklistLoadIncidentDigest(incident);
    143       break;
    144     // Add handling for new incident types here.
    145     default:
    146       COMPILE_ASSERT(BLACKLIST_LOAD + 1 == NUM_INCIDENT_TYPES,
    147                      add_support_for_new_types);
    148       NOTREACHED();
    149       break;
    150   }
    151   return state;
    152 }
    153 
    154 // Returns true if the incident described by |state| has already been reported
    155 // based on the bookkeeping in the |incidents_sent| preference dictionary.
    156 bool IncidentHasBeenReported(const base::DictionaryValue* incidents_sent,
    157                              const PersistentIncidentState& state) {
    158   const base::DictionaryValue* type_dict = NULL;
    159   std::string digest_string;
    160   return (incidents_sent &&
    161           incidents_sent->GetDictionaryWithoutPathExpansion(
    162               base::IntToString(state.type), &type_dict) &&
    163           type_dict->GetStringWithoutPathExpansion(state.key, &digest_string) &&
    164           digest_string == base::UintToString(state.digest));
    165 }
    166 
    167 // Marks the incidents described by |states| as having been reported
    168 // in |incidents_set|.
    169 void MarkIncidentsAsReported(const std::vector<PersistentIncidentState>& states,
    170                              base::DictionaryValue* incidents_sent) {
    171   for (size_t i = 0; i < states.size(); ++i) {
    172     const PersistentIncidentState& data = states[i];
    173     base::DictionaryValue* type_dict = NULL;
    174     const std::string type_string(base::IntToString(data.type));
    175     if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string,
    176                                                            &type_dict)) {
    177       type_dict = new base::DictionaryValue();
    178       incidents_sent->SetWithoutPathExpansion(type_string, type_dict);
    179     }
    180     type_dict->SetStringWithoutPathExpansion(data.key,
    181                                              base::UintToString(data.digest));
    182   }
    183 }
    184 
    185 // Runs |callback| on the thread to which |thread_runner| belongs. The callback
    186 // is run immediately if this function is called on |thread_runner|'s thread.
    187 void AddIncidentOnOriginThread(
    188     const AddIncidentCallback& callback,
    189     scoped_refptr<base::SingleThreadTaskRunner> thread_runner,
    190     scoped_ptr<ClientIncidentReport_IncidentData> incident) {
    191   if (thread_runner->BelongsToCurrentThread())
    192     callback.Run(incident.Pass());
    193   else
    194     thread_runner->PostTask(FROM_HERE,
    195                             base::Bind(callback, base::Passed(&incident)));
    196 }
    197 
    198 }  // namespace
    199 
    200 struct IncidentReportingService::ProfileContext {
    201   ProfileContext();
    202   ~ProfileContext();
    203 
    204   // The incidents collected for this profile pending creation and/or upload.
    205   ScopedVector<ClientIncidentReport_IncidentData> incidents;
    206 
    207   // False until PROFILE_ADDED notification is received.
    208   bool added;
    209 
    210  private:
    211   DISALLOW_COPY_AND_ASSIGN(ProfileContext);
    212 };
    213 
    214 class IncidentReportingService::UploadContext {
    215  public:
    216   typedef std::map<Profile*, std::vector<PersistentIncidentState> >
    217       PersistentIncidentStateCollection;
    218 
    219   explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
    220   ~UploadContext();
    221 
    222   // The report being uploaded.
    223   scoped_ptr<ClientIncidentReport> report;
    224 
    225   // The uploader in use. This is NULL until the CSD killswitch is checked.
    226   scoped_ptr<IncidentReportUploader> uploader;
    227 
    228   // A mapping of profiles to the data to be persisted upon successful upload.
    229   PersistentIncidentStateCollection profiles_to_state;
    230 
    231  private:
    232   DISALLOW_COPY_AND_ASSIGN(UploadContext);
    233 };
    234 
    235 IncidentReportingService::ProfileContext::ProfileContext() : added() {
    236 }
    237 
    238 IncidentReportingService::ProfileContext::~ProfileContext() {
    239 }
    240 
    241 IncidentReportingService::UploadContext::UploadContext(
    242     scoped_ptr<ClientIncidentReport> report)
    243     : report(report.Pass()) {
    244 }
    245 
    246 IncidentReportingService::UploadContext::~UploadContext() {
    247 }
    248 
    249 IncidentReportingService::IncidentReportingService(
    250     SafeBrowsingService* safe_browsing_service,
    251     const scoped_refptr<net::URLRequestContextGetter>& request_context_getter)
    252     : database_manager_(safe_browsing_service ?
    253                         safe_browsing_service->database_manager() : NULL),
    254       url_request_context_getter_(request_context_getter),
    255       collect_environment_data_fn_(&CollectEnvironmentData),
    256       environment_collection_task_runner_(
    257           content::BrowserThread::GetBlockingPool()
    258               ->GetTaskRunnerWithShutdownBehavior(
    259                   base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
    260       environment_collection_pending_(),
    261       collation_timeout_pending_(),
    262       collation_timer_(FROM_HERE,
    263                        base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
    264                        this,
    265                        &IncidentReportingService::OnCollationTimeout),
    266       delayed_analysis_callbacks_(
    267           base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs),
    268           content::BrowserThread::GetBlockingPool()
    269               ->GetTaskRunnerWithShutdownBehavior(
    270                   base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
    271       receiver_weak_ptr_factory_(this),
    272       weak_ptr_factory_(this) {
    273   notification_registrar_.Add(this,
    274                               chrome::NOTIFICATION_PROFILE_ADDED,
    275                               content::NotificationService::AllSources());
    276   notification_registrar_.Add(this,
    277                               chrome::NOTIFICATION_PROFILE_DESTROYED,
    278                               content::NotificationService::AllSources());
    279 }
    280 
    281 IncidentReportingService::~IncidentReportingService() {
    282   CancelIncidentCollection();
    283 
    284   // Cancel all internal asynchronous tasks.
    285   weak_ptr_factory_.InvalidateWeakPtrs();
    286 
    287   CancelEnvironmentCollection();
    288   CancelDownloadCollection();
    289   CancelAllReportUploads();
    290 
    291   STLDeleteValues(&profiles_);
    292 }
    293 
    294 AddIncidentCallback IncidentReportingService::GetAddIncidentCallback(
    295     Profile* profile) {
    296   // Force the context to be created so that incidents added before
    297   // OnProfileAdded is called are held until the profile's preferences can be
    298   // queried.
    299   ignore_result(GetOrCreateProfileContext(profile));
    300 
    301   return base::Bind(&IncidentReportingService::AddIncident,
    302                     receiver_weak_ptr_factory_.GetWeakPtr(),
    303                     profile);
    304 }
    305 
    306 scoped_ptr<TrackedPreferenceValidationDelegate>
    307 IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) {
    308   DCHECK(thread_checker_.CalledOnValidThread());
    309 
    310   if (profile->IsOffTheRecord())
    311     return scoped_ptr<TrackedPreferenceValidationDelegate>();
    312   return scoped_ptr<TrackedPreferenceValidationDelegate>(
    313       new PreferenceValidationDelegate(GetAddIncidentCallback(profile)));
    314 }
    315 
    316 void IncidentReportingService::RegisterDelayedAnalysisCallback(
    317     const DelayedAnalysisCallback& callback) {
    318   DCHECK(thread_checker_.CalledOnValidThread());
    319 
    320   // |callback| will be run on the blocking pool, so it will likely run the
    321   // AddIncidentCallback there as well. Bounce the run of that callback back to
    322   // the current thread via AddIncidentOnOriginThread.
    323   delayed_analysis_callbacks_.RegisterCallback(
    324       base::Bind(callback,
    325                  base::Bind(&AddIncidentOnOriginThread,
    326                             GetAddIncidentCallback(NULL),
    327                             base::ThreadTaskRunnerHandle::Get())));
    328 
    329   // Start running the callbacks if any profiles are participating in safe
    330   // browsing. If none are now, running will commence if/when a participaing
    331   // profile is added.
    332   if (FindEligibleProfile())
    333     delayed_analysis_callbacks_.Start();
    334 }
    335 
    336 IncidentReportingService::IncidentReportingService(
    337     SafeBrowsingService* safe_browsing_service,
    338     const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
    339     base::TimeDelta delayed_task_interval,
    340     const scoped_refptr<base::TaskRunner>& delayed_task_runner)
    341     : database_manager_(safe_browsing_service ?
    342                         safe_browsing_service->database_manager() : NULL),
    343       url_request_context_getter_(request_context_getter),
    344       collect_environment_data_fn_(&CollectEnvironmentData),
    345       environment_collection_task_runner_(
    346           content::BrowserThread::GetBlockingPool()
    347               ->GetTaskRunnerWithShutdownBehavior(
    348                   base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
    349       environment_collection_pending_(),
    350       collation_timeout_pending_(),
    351       collation_timer_(FROM_HERE,
    352                        base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
    353                        this,
    354                        &IncidentReportingService::OnCollationTimeout),
    355       delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner),
    356       receiver_weak_ptr_factory_(this),
    357       weak_ptr_factory_(this) {
    358   notification_registrar_.Add(this,
    359                               chrome::NOTIFICATION_PROFILE_ADDED,
    360                               content::NotificationService::AllSources());
    361   notification_registrar_.Add(this,
    362                               chrome::NOTIFICATION_PROFILE_DESTROYED,
    363                               content::NotificationService::AllSources());
    364 }
    365 
    366 void IncidentReportingService::SetCollectEnvironmentHook(
    367     CollectEnvironmentDataFn collect_environment_data_hook,
    368     const scoped_refptr<base::TaskRunner>& task_runner) {
    369   if (collect_environment_data_hook) {
    370     collect_environment_data_fn_ = collect_environment_data_hook;
    371     environment_collection_task_runner_ = task_runner;
    372   } else {
    373     collect_environment_data_fn_ = &CollectEnvironmentData;
    374     environment_collection_task_runner_ =
    375         content::BrowserThread::GetBlockingPool()
    376             ->GetTaskRunnerWithShutdownBehavior(
    377                 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
    378   }
    379 }
    380 
    381 void IncidentReportingService::OnProfileAdded(Profile* profile) {
    382   DCHECK(thread_checker_.CalledOnValidThread());
    383 
    384   // Track the addition of all profiles even when no report is being assembled
    385   // so that the service can determine whether or not it can evaluate a
    386   // profile's preferences at the time of incident addition.
    387   ProfileContext* context = GetOrCreateProfileContext(profile);
    388   context->added = true;
    389 
    390   const bool safe_browsing_enabled =
    391       profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
    392 
    393   // Start processing delayed analysis callbacks if this new profile
    394   // participates in safe browsing. Start is idempotent, so this is safe even if
    395   // they're already running.
    396   if (safe_browsing_enabled)
    397     delayed_analysis_callbacks_.Start();
    398 
    399   // Start a new report if this profile participates in safe browsing and there
    400   // are process-wide incidents.
    401   if (safe_browsing_enabled && GetProfileContext(NULL) &&
    402       GetProfileContext(NULL)->incidents.size()) {
    403     BeginReportProcessing();
    404   }
    405 
    406   // TODO(grt): register for pref change notifications to start delayed analysis
    407   // and/or report processing if sb is currently disabled but subsequently
    408   // enabled.
    409 
    410   // Nothing else to do if a report is not being assembled.
    411   if (!report_)
    412     return;
    413 
    414   // Drop all incidents associated with this profile that were received prior to
    415   // its addition if the profile is not participating in safe browsing.
    416   if (!context->incidents.empty() && !safe_browsing_enabled) {
    417     for (size_t i = 0; i < context->incidents.size(); ++i)
    418       LogIncidentDataType(DROPPED, *context->incidents[i]);
    419     context->incidents.clear();
    420   }
    421 
    422   // Take another stab at finding the most recent download if a report is being
    423   // assembled and one hasn't been found yet (the LastDownloadFinder operates
    424   // only on profiles that have been added to the ProfileManager).
    425   BeginDownloadCollection();
    426 }
    427 
    428 scoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder(
    429     const LastDownloadFinder::LastDownloadCallback& callback) {
    430   return LastDownloadFinder::Create(callback).Pass();
    431 }
    432 
    433 scoped_ptr<IncidentReportUploader> IncidentReportingService::StartReportUpload(
    434     const IncidentReportUploader::OnResultCallback& callback,
    435     const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
    436     const ClientIncidentReport& report) {
    437   return IncidentReportUploaderImpl::UploadReport(
    438              callback, request_context_getter, report).Pass();
    439 }
    440 
    441 bool IncidentReportingService::IsProcessingReport() const {
    442   return report_ != NULL;
    443 }
    444 
    445 IncidentReportingService::ProfileContext*
    446 IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
    447   ProfileContextCollection::iterator it =
    448       profiles_.insert(ProfileContextCollection::value_type(profile, NULL))
    449           .first;
    450   if (!it->second)
    451     it->second = new ProfileContext();
    452   return it->second;
    453 }
    454 
    455 IncidentReportingService::ProfileContext*
    456 IncidentReportingService::GetProfileContext(Profile* profile) {
    457   ProfileContextCollection::iterator it = profiles_.find(profile);
    458   return it == profiles_.end() ? NULL : it->second;
    459 }
    460 
    461 void IncidentReportingService::OnProfileDestroyed(Profile* profile) {
    462   DCHECK(thread_checker_.CalledOnValidThread());
    463 
    464   ProfileContextCollection::iterator it = profiles_.find(profile);
    465   if (it == profiles_.end())
    466     return;
    467 
    468   // TODO(grt): Persist incidents for upload on future profile load.
    469 
    470   // Forget about this profile. Incidents not yet sent for upload are lost.
    471   // No new incidents will be accepted for it.
    472   delete it->second;
    473   profiles_.erase(it);
    474 
    475   // Remove the association with this profile from all pending uploads.
    476   for (size_t i = 0; i < uploads_.size(); ++i)
    477     uploads_[i]->profiles_to_state.erase(profile);
    478 }
    479 
    480 Profile* IncidentReportingService::FindEligibleProfile() const {
    481   Profile* candidate = NULL;
    482   for (ProfileContextCollection::const_iterator scan = profiles_.begin();
    483        scan != profiles_.end();
    484        ++scan) {
    485     // Skip over profiles that have yet to be added to the profile manager.
    486     // This will also skip over the NULL-profile context used to hold
    487     // process-wide incidents.
    488     if (!scan->second->added)
    489       continue;
    490     PrefService* prefs = scan->first->GetPrefs();
    491     if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
    492       if (!candidate)
    493         candidate = scan->first;
    494       if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
    495         candidate = scan->first;
    496         break;
    497       }
    498     }
    499   }
    500   return candidate;
    501 }
    502 
    503 void IncidentReportingService::AddIncident(
    504     Profile* profile,
    505     scoped_ptr<ClientIncidentReport_IncidentData> incident_data) {
    506   DCHECK(thread_checker_.CalledOnValidThread());
    507   DCHECK_EQ(1U, CountIncidents(*incident_data));
    508 
    509   ProfileContext* context = GetProfileContext(profile);
    510   // It is forbidden to call this function with a destroyed profile.
    511   DCHECK(context);
    512   // If this is a process-wide incident, the context must not indicate that the
    513   // profile (which is NULL) has been added to the profile manager.
    514   DCHECK(profile || !context->added);
    515 
    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_data);
    522     return;
    523   }
    524 
    525   // Provide time to the new incident if the caller didn't do so.
    526   if (!incident_data->has_incident_time_msec())
    527     incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime());
    528 
    529   // Take ownership of the incident.
    530   context->incidents.push_back(incident_data.release());
    531 
    532   // Remember when the first incident for this report arrived.
    533   if (first_incident_time_.is_null())
    534     first_incident_time_ = base::Time::Now();
    535   // Log the time between the previous incident and this one.
    536   if (!last_incident_time_.is_null()) {
    537     UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime",
    538                         base::TimeTicks::Now() - last_incident_time_);
    539   }
    540   last_incident_time_ = base::TimeTicks::Now();
    541 
    542   // Persist the incident data.
    543 
    544   // Start assembling a new report if this is the first incident ever or the
    545   // first since the last upload.
    546   BeginReportProcessing();
    547 }
    548 
    549 void IncidentReportingService::BeginReportProcessing() {
    550   DCHECK(thread_checker_.CalledOnValidThread());
    551 
    552   // Creates a new report if needed.
    553   if (!report_)
    554     report_.reset(new ClientIncidentReport());
    555 
    556   // Ensure that collection tasks are running (calls are idempotent).
    557   BeginIncidentCollation();
    558   BeginEnvironmentCollection();
    559   BeginDownloadCollection();
    560 }
    561 
    562 void IncidentReportingService::BeginIncidentCollation() {
    563   // Restart the delay timer to send the report upon expiration.
    564   collation_timeout_pending_ = true;
    565   collation_timer_.Reset();
    566 }
    567 
    568 void IncidentReportingService::BeginEnvironmentCollection() {
    569   DCHECK(thread_checker_.CalledOnValidThread());
    570   DCHECK(report_);
    571   // Nothing to do if environment collection is pending or has already
    572   // completed.
    573   if (environment_collection_pending_ || report_->has_environment())
    574     return;
    575 
    576   environment_collection_begin_ = base::TimeTicks::Now();
    577   ClientIncidentReport_EnvironmentData* environment_data =
    578       new ClientIncidentReport_EnvironmentData();
    579   environment_collection_pending_ =
    580       environment_collection_task_runner_->PostTaskAndReply(
    581           FROM_HERE,
    582           base::Bind(collect_environment_data_fn_, environment_data),
    583           base::Bind(&IncidentReportingService::OnEnvironmentDataCollected,
    584                      weak_ptr_factory_.GetWeakPtr(),
    585                      base::Passed(make_scoped_ptr(environment_data))));
    586 
    587   // Posting the task will fail if the runner has been shut down. This should
    588   // never happen since the blocking pool is shut down after this service.
    589   DCHECK(environment_collection_pending_);
    590 }
    591 
    592 bool IncidentReportingService::WaitingForEnvironmentCollection() {
    593   return environment_collection_pending_;
    594 }
    595 
    596 void IncidentReportingService::CancelEnvironmentCollection() {
    597   environment_collection_begin_ = base::TimeTicks();
    598   environment_collection_pending_ = false;
    599   if (report_)
    600     report_->clear_environment();
    601 }
    602 
    603 void IncidentReportingService::OnEnvironmentDataCollected(
    604     scoped_ptr<ClientIncidentReport_EnvironmentData> environment_data) {
    605   DCHECK(thread_checker_.CalledOnValidThread());
    606   DCHECK(environment_collection_pending_);
    607   DCHECK(report_ && !report_->has_environment());
    608   environment_collection_pending_ = false;
    609 
    610 // CurrentProcessInfo::CreationTime() is missing on some platforms.
    611 #if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
    612   base::TimeDelta uptime =
    613       first_incident_time_ - base::CurrentProcessInfo::CreationTime();
    614   environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds());
    615 #endif
    616 
    617   report_->set_allocated_environment(environment_data.release());
    618 
    619   UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime",
    620                       base::TimeTicks::Now() - environment_collection_begin_);
    621   environment_collection_begin_ = base::TimeTicks();
    622 
    623   UploadIfCollectionComplete();
    624 }
    625 
    626 bool IncidentReportingService::WaitingToCollateIncidents() {
    627   return collation_timeout_pending_;
    628 }
    629 
    630 void IncidentReportingService::CancelIncidentCollection() {
    631   collation_timeout_pending_ = false;
    632   last_incident_time_ = base::TimeTicks();
    633   report_.reset();
    634 }
    635 
    636 void IncidentReportingService::OnCollationTimeout() {
    637   DCHECK(thread_checker_.CalledOnValidThread());
    638 
    639   // Exit early if collection was cancelled.
    640   if (!collation_timeout_pending_)
    641     return;
    642 
    643   // Wait another round if profile-bound incidents have come in from a profile
    644   // that has yet to complete creation.
    645   for (ProfileContextCollection::iterator scan = profiles_.begin();
    646        scan != profiles_.end();
    647        ++scan) {
    648     if (scan->first && !scan->second->added &&
    649         !scan->second->incidents.empty()) {
    650       collation_timer_.Reset();
    651       return;
    652     }
    653   }
    654 
    655   collation_timeout_pending_ = false;
    656 
    657   UploadIfCollectionComplete();
    658 }
    659 
    660 void IncidentReportingService::BeginDownloadCollection() {
    661   DCHECK(thread_checker_.CalledOnValidThread());
    662   DCHECK(report_);
    663   // Nothing to do if a search for the most recent download is already pending
    664   // or if one has already been found.
    665   if (last_download_finder_ || report_->has_download())
    666     return;
    667 
    668   last_download_begin_ = base::TimeTicks::Now();
    669   last_download_finder_ = CreateDownloadFinder(
    670       base::Bind(&IncidentReportingService::OnLastDownloadFound,
    671                  weak_ptr_factory_.GetWeakPtr()));
    672   // No instance is returned if there are no eligible loaded profiles. Another
    673   // search will be attempted in OnProfileAdded() if another profile appears on
    674   // the scene.
    675   if (!last_download_finder_)
    676     last_download_begin_ = base::TimeTicks();
    677 }
    678 
    679 bool IncidentReportingService::WaitingForMostRecentDownload() {
    680   DCHECK(report_);  // Only call this when a report is being assembled.
    681   // The easy case: not waiting if a download has already been found.
    682   if (report_->has_download())
    683     return false;
    684   // The next easy case: waiting if the finder is operating.
    685   if (last_download_finder_)
    686     return true;
    687   // The harder case: waiting if a non-NULL profile has not yet been added.
    688   for (ProfileContextCollection::const_iterator scan = profiles_.begin();
    689        scan != profiles_.end();
    690        ++scan) {
    691     if (scan->first && !scan->second->added)
    692       return true;
    693   }
    694   // There is no most recent download and there's nothing more to wait for.
    695   return false;
    696 }
    697 
    698 void IncidentReportingService::CancelDownloadCollection() {
    699   last_download_finder_.reset();
    700   last_download_begin_ = base::TimeTicks();
    701   if (report_)
    702     report_->clear_download();
    703 }
    704 
    705 void IncidentReportingService::OnLastDownloadFound(
    706     scoped_ptr<ClientIncidentReport_DownloadDetails> last_download) {
    707   DCHECK(thread_checker_.CalledOnValidThread());
    708   DCHECK(report_);
    709 
    710   UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime",
    711                       base::TimeTicks::Now() - last_download_begin_);
    712   last_download_begin_ = base::TimeTicks();
    713 
    714   // Harvest the finder.
    715   last_download_finder_.reset();
    716 
    717   if (last_download)
    718     report_->set_allocated_download(last_download.release());
    719 
    720   UploadIfCollectionComplete();
    721 }
    722 
    723 void IncidentReportingService::UploadIfCollectionComplete() {
    724   DCHECK(report_);
    725   // Bail out if there are still outstanding collection tasks. Completion of any
    726   // of these will start another upload attempt.
    727   if (WaitingForEnvironmentCollection() ||
    728       WaitingToCollateIncidents() ||
    729       WaitingForMostRecentDownload()) {
    730     return;
    731   }
    732 
    733   // Take ownership of the report and clear things for future reports.
    734   scoped_ptr<ClientIncidentReport> report(report_.Pass());
    735   first_incident_time_ = base::Time();
    736   last_incident_time_ = base::TimeTicks();
    737 
    738   // Drop the report if no executable download was found.
    739   if (!report->has_download()) {
    740     UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
    741                               IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
    742                               IncidentReportUploader::NUM_UPLOAD_RESULTS);
    743     return;
    744   }
    745 
    746   ClientIncidentReport_EnvironmentData_Process* process =
    747       report->mutable_environment()->mutable_process();
    748 
    749   // Not all platforms have a metrics reporting preference.
    750   if (g_browser_process->local_state()->FindPreference(
    751           prefs::kMetricsReportingEnabled)) {
    752     process->set_metrics_consent(g_browser_process->local_state()->GetBoolean(
    753         prefs::kMetricsReportingEnabled));
    754   }
    755 
    756   // Find the profile that benefits from the strongest protections.
    757   Profile* eligible_profile = FindEligibleProfile();
    758   process->set_extended_consent(
    759       eligible_profile ? eligible_profile->GetPrefs()->GetBoolean(
    760                              prefs::kSafeBrowsingExtendedReportingEnabled) :
    761                        false);
    762 
    763   // Associate process-wide incidents with the profile that benefits from the
    764   // strongest safe browsing protections.
    765   ProfileContext* null_context = GetProfileContext(NULL);
    766   if (null_context && !null_context->incidents.empty() && eligible_profile) {
    767     ProfileContext* eligible_context = GetProfileContext(eligible_profile);
    768     // Move the incidents to the target context.
    769     eligible_context->incidents.insert(eligible_context->incidents.end(),
    770                                        null_context->incidents.begin(),
    771                                        null_context->incidents.end());
    772     null_context->incidents.weak_clear();
    773   }
    774 
    775   // Collect incidents across all profiles participating in safe browsing. Drop
    776   // incidents if the profile stopped participating before collection completed.
    777   // Prune previously submitted incidents.
    778   // Associate the profiles and their incident data with the upload.
    779   size_t prune_count = 0;
    780   UploadContext::PersistentIncidentStateCollection profiles_to_state;
    781   for (ProfileContextCollection::iterator scan = profiles_.begin();
    782        scan != profiles_.end();
    783        ++scan) {
    784     // Bypass process-wide incidents that have not yet been associated with a
    785     // profile.
    786     if (!scan->first)
    787       continue;
    788     PrefService* prefs = scan->first->GetPrefs();
    789     ProfileContext* context = scan->second;
    790     if (context->incidents.empty())
    791       continue;
    792     if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
    793       for (size_t i = 0; i < context->incidents.size(); ++i) {
    794         LogIncidentDataType(DROPPED, *context->incidents[i]);
    795       }
    796       context->incidents.clear();
    797       continue;
    798     }
    799     std::vector<PersistentIncidentState> states;
    800     const base::DictionaryValue* incidents_sent =
    801         prefs->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
    802     // Prep persistent data and prune any incidents already sent.
    803     for (size_t i = 0; i < context->incidents.size(); ++i) {
    804       ClientIncidentReport_IncidentData* incident = context->incidents[i];
    805       const PersistentIncidentState state = ComputeIncidentState(*incident);
    806       if (IncidentHasBeenReported(incidents_sent, state)) {
    807         ++prune_count;
    808         delete context->incidents[i];
    809         context->incidents[i] = NULL;
    810       } else {
    811         states.push_back(state);
    812       }
    813     }
    814     if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) {
    815       // Prune all incidents as if they had been reported, migrating to the new
    816       // technique. TODO(grt): remove this branch after it has shipped.
    817       for (size_t i = 0; i < context->incidents.size(); ++i) {
    818         if (context->incidents[i])
    819           ++prune_count;
    820       }
    821       context->incidents.clear();
    822       prefs->ClearPref(prefs::kSafeBrowsingIncidentReportSent);
    823       DictionaryPrefUpdate pref_update(prefs,
    824                                        prefs::kSafeBrowsingIncidentsSent);
    825       MarkIncidentsAsReported(states, pref_update.Get());
    826     } else {
    827       for (size_t i = 0; i < context->incidents.size(); ++i) {
    828         ClientIncidentReport_IncidentData* incident = context->incidents[i];
    829         if (incident) {
    830           LogIncidentDataType(ACCEPTED, *incident);
    831           // Ownership of the incident is passed to the report.
    832           report->mutable_incident()->AddAllocated(incident);
    833         }
    834       }
    835       context->incidents.weak_clear();
    836       std::vector<PersistentIncidentState>& profile_states =
    837           profiles_to_state[scan->first];
    838       profile_states.swap(states);
    839     }
    840   }
    841 
    842   const int count = report->incident_size();
    843   // Abandon the request if all incidents were dropped with none pruned.
    844   if (!count && !prune_count)
    845     return;
    846 
    847   UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count);
    848 
    849   {
    850     double prune_pct = static_cast<double>(prune_count);
    851     prune_pct = prune_pct * 100.0 / (count + prune_count);
    852     prune_pct = round(prune_pct);
    853     UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct));
    854   }
    855   // Abandon the report if all incidents were pruned.
    856   if (!count)
    857     return;
    858 
    859   scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
    860   context->profiles_to_state.swap(profiles_to_state);
    861   if (!database_manager_.get()) {
    862     // No database manager during testing. Take ownership of the context and
    863     // continue processing.
    864     UploadContext* temp_context = context.get();
    865     uploads_.push_back(context.release());
    866     IncidentReportingService::OnKillSwitchResult(temp_context, false);
    867   } else {
    868     if (content::BrowserThread::PostTaskAndReplyWithResult(
    869             content::BrowserThread::IO,
    870             FROM_HERE,
    871             base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn,
    872                        database_manager_),
    873             base::Bind(&IncidentReportingService::OnKillSwitchResult,
    874                        weak_ptr_factory_.GetWeakPtr(),
    875                        context.get()))) {
    876       uploads_.push_back(context.release());
    877     }  // else should not happen. Let the context be deleted automatically.
    878   }
    879 }
    880 
    881 void IncidentReportingService::CancelAllReportUploads() {
    882   for (size_t i = 0; i < uploads_.size(); ++i) {
    883     UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
    884                               IncidentReportUploader::UPLOAD_CANCELLED,
    885                               IncidentReportUploader::NUM_UPLOAD_RESULTS);
    886   }
    887   uploads_.clear();
    888 }
    889 
    890 void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
    891                                                   bool is_killswitch_on) {
    892   DCHECK(thread_checker_.CalledOnValidThread());
    893   if (!is_killswitch_on) {
    894     // Initiate the upload.
    895     context->uploader =
    896         StartReportUpload(
    897             base::Bind(&IncidentReportingService::OnReportUploadResult,
    898                        weak_ptr_factory_.GetWeakPtr(),
    899                        context),
    900             url_request_context_getter_,
    901             *context->report).Pass();
    902     if (!context->uploader) {
    903       OnReportUploadResult(context,
    904                            IncidentReportUploader::UPLOAD_INVALID_REQUEST,
    905                            scoped_ptr<ClientIncidentResponse>());
    906     }
    907   } else {
    908     OnReportUploadResult(context,
    909                          IncidentReportUploader::UPLOAD_SUPPRESSED,
    910                          scoped_ptr<ClientIncidentResponse>());
    911   }
    912 }
    913 
    914 void IncidentReportingService::HandleResponse(const UploadContext& context) {
    915   for (UploadContext::PersistentIncidentStateCollection::const_iterator scan =
    916            context.profiles_to_state.begin();
    917        scan != context.profiles_to_state.end();
    918        ++scan) {
    919     DictionaryPrefUpdate pref_update(scan->first->GetPrefs(),
    920                                      prefs::kSafeBrowsingIncidentsSent);
    921     MarkIncidentsAsReported(scan->second, pref_update.Get());
    922   }
    923 }
    924 
    925 void IncidentReportingService::OnReportUploadResult(
    926     UploadContext* context,
    927     IncidentReportUploader::Result result,
    928     scoped_ptr<ClientIncidentResponse> response) {
    929   DCHECK(thread_checker_.CalledOnValidThread());
    930 
    931   UMA_HISTOGRAM_ENUMERATION(
    932       "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
    933 
    934   // The upload is no longer outstanding, so take ownership of the context (from
    935   // the collection of outstanding uploads) in this scope.
    936   ScopedVector<UploadContext>::iterator it(
    937       std::find(uploads_.begin(), uploads_.end(), context));
    938   DCHECK(it != uploads_.end());
    939   scoped_ptr<UploadContext> upload(context);  // == *it
    940   *it = uploads_.back();
    941   uploads_.weak_erase(uploads_.end() - 1);
    942 
    943   if (result == IncidentReportUploader::UPLOAD_SUCCESS)
    944     HandleResponse(*upload);
    945   // else retry?
    946 }
    947 
    948 void IncidentReportingService::Observe(
    949     int type,
    950     const content::NotificationSource& source,
    951     const content::NotificationDetails& details) {
    952   switch (type) {
    953     case chrome::NOTIFICATION_PROFILE_ADDED: {
    954       Profile* profile = content::Source<Profile>(source).ptr();
    955       if (!profile->IsOffTheRecord())
    956         OnProfileAdded(profile);
    957       break;
    958     }
    959     case chrome::NOTIFICATION_PROFILE_DESTROYED: {
    960       Profile* profile = content::Source<Profile>(source).ptr();
    961       if (!profile->IsOffTheRecord())
    962         OnProfileDestroyed(profile);
    963       break;
    964     }
    965     default:
    966       break;
    967   }
    968 }
    969 
    970 }  // namespace safe_browsing
    971