1f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Copyright 2014 The Chromium Authors. All rights reserved.
2f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
3f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// found in the LICENSE file.
4f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
56e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/safe_browsing/incident_reporting/incident_reporting_service.h"
6f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
7f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include <math.h>
8f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
96d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include <algorithm>
106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include <vector>
116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
12f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/metrics/histogram.h"
136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "base/prefs/pref_service.h"
145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/prefs/scoped_user_pref_update.h"
15f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/process/process_info.h"
165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/single_thread_task_runner.h"
176d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "base/stl_util.h"
185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/strings/string_number_conversions.h"
195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/thread_task_runner_handle.h"
20f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "base/threading/sequenced_worker_pool.h"
215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)#include "base/values.h"
226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/browser_process.h"
236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/chrome_notification_types.h"
24f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/browser/prefs/tracked/tracked_preference_validation_delegate.h"
256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/browser/profiles/profile.h"
26f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/browser/safe_browsing/database_manager.h"
276e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/safe_browsing/incident_reporting/binary_integrity_incident_handlers.h"
281320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci#include "chrome/browser/safe_browsing/incident_reporting/blacklist_load_incident_handlers.h"
296e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/safe_browsing/incident_reporting/environment_data_collection.h"
306e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/safe_browsing/incident_reporting/incident_report_uploader_impl.h"
316e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/safe_browsing/incident_reporting/preference_validation_delegate.h"
326e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)#include "chrome/browser/safe_browsing/incident_reporting/tracked_preference_incident_handlers.h"
33f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/browser/safe_browsing/safe_browsing_service.h"
346d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "chrome/common/pref_names.h"
35f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "chrome/common/safe_browsing/csd.pb.h"
36f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "content/public/browser/browser_thread.h"
376d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)#include "content/public/browser/notification_service.h"
38f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
39f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
40f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)namespace safe_browsing {
41f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
42f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)namespace {
43f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The type of an incident. Used for user metrics and for pruning of
455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// previously-reported incidents.
46f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)enum IncidentType {
47f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Start with 1 rather than zero; otherwise there won't be enough buckets for
48f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // the histogram.
49f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  TRACKED_PREFERENCE = 1,
506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  BINARY_INTEGRITY = 2,
511320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  BLACKLIST_LOAD = 3,
525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Values for new incident types go here.
531320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  NUM_INCIDENT_TYPES = 4
54f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
55f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The action taken for an incident; used for user metrics (see
575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// LogIncidentDataType).
586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)enum IncidentDisposition {
596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  DROPPED,
606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ACCEPTED,
616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)};
626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The state persisted for a specific instance of an incident to enable pruning
645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// of previously-reported incidents.
655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)struct PersistentIncidentState {
665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // The type of the incident.
675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  IncidentType type;
685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // The key for a specific instance of an incident.
705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  std::string key;
715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // A hash digest representing a specific instance of an incident.
735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  uint32_t digest;
745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)};
755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The amount of time the service will wait to collate incidents.
77f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)const int64 kDefaultUploadDelayMs = 1000 * 60;  // one minute
78f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// The amount of time between running delayed analysis callbacks.
805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)const int64 kDefaultCallbackIntervalMs = 1000 * 20;
815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Returns the number of incidents contained in |incident|. The result is
835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// expected to be 1. Used in DCHECKs.
845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)size_t CountIncidents(const ClientIncidentReport_IncidentData& incident) {
855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  size_t result = 0;
865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (incident.has_tracked_preference())
875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ++result;
886e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (incident.has_binary_integrity())
896e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    ++result;
901320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (incident.has_blacklist_load())
911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    ++result;
925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Add detection for new incident types here.
935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return result;
945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Returns the type of incident contained in |incident_data|.
975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)IncidentType GetIncidentType(
98f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const ClientIncidentReport_IncidentData& incident_data) {
995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (incident_data.has_tracked_preference())
1005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    return TRACKED_PREFERENCE;
1016e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  if (incident_data.has_binary_integrity())
1026e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    return BINARY_INTEGRITY;
1031320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (incident_data.has_blacklist_load())
1041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    return BLACKLIST_LOAD;
1056d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Add detection for new incident types here.
1071320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  COMPILE_ASSERT(BLACKLIST_LOAD + 1 == NUM_INCIDENT_TYPES,
1085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 add_support_for_new_types);
1095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  NOTREACHED();
1105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return NUM_INCIDENT_TYPES;
1115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1126d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
1135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Logs the type of incident in |incident_data| to a user metrics histogram.
1145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void LogIncidentDataType(
1155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    IncidentDisposition disposition,
1165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const ClientIncidentReport_IncidentData& incident_data) {
1175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  IncidentType type = GetIncidentType(incident_data);
1186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (disposition == ACCEPTED) {
1196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("SBIRS.Incident", type, NUM_INCIDENT_TYPES);
1206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  } else {
1216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    DCHECK_EQ(disposition, DROPPED);
1226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("SBIRS.DroppedIncident", type,
1236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                              NUM_INCIDENT_TYPES);
124f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
125f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
126f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
1275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Computes the persistent state for an incident.
1285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)PersistentIncidentState ComputeIncidentState(
1295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const ClientIncidentReport_IncidentData& incident) {
1305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  PersistentIncidentState state = {GetIncidentType(incident)};
1315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  switch (state.type) {
1325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    case TRACKED_PREFERENCE:
1335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      state.key = GetTrackedPreferenceIncidentKey(incident);
1345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      state.digest = GetTrackedPreferenceIncidentDigest(incident);
1355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      break;
1366e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case BINARY_INTEGRITY:
1376e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      state.key = GetBinaryIntegrityIncidentKey(incident);
1386e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      state.digest = GetBinaryIntegrityIncidentDigest(incident);
1396e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      break;
1401320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    case BLACKLIST_LOAD:
1411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      state.key = GetBlacklistLoadIncidentKey(incident);
1421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      state.digest = GetBlacklistLoadIncidentDigest(incident);
1431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      break;
1445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Add handling for new incident types here.
1455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    default:
1461320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      COMPILE_ASSERT(BLACKLIST_LOAD + 1 == NUM_INCIDENT_TYPES,
1475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                     add_support_for_new_types);
1485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      NOTREACHED();
1495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      break;
1505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return state;
1525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Returns true if the incident described by |state| has already been reported
1555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// based on the bookkeeping in the |incidents_sent| preference dictionary.
1565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)bool IncidentHasBeenReported(const base::DictionaryValue* incidents_sent,
1575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                             const PersistentIncidentState& state) {
1585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const base::DictionaryValue* type_dict = NULL;
1595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  std::string digest_string;
1605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return (incidents_sent &&
1615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          incidents_sent->GetDictionaryWithoutPathExpansion(
1625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              base::IntToString(state.type), &type_dict) &&
1635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          type_dict->GetStringWithoutPathExpansion(state.key, &digest_string) &&
1645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          digest_string == base::UintToString(state.digest));
1655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Marks the incidents described by |states| as having been reported
1685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// in |incidents_set|.
1695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void MarkIncidentsAsReported(const std::vector<PersistentIncidentState>& states,
1705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                             base::DictionaryValue* incidents_sent) {
1715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (size_t i = 0; i < states.size(); ++i) {
1725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const PersistentIncidentState& data = states[i];
1735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    base::DictionaryValue* type_dict = NULL;
1745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const std::string type_string(base::IntToString(data.type));
1755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (!incidents_sent->GetDictionaryWithoutPathExpansion(type_string,
1765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                                           &type_dict)) {
1775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      type_dict = new base::DictionaryValue();
1785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      incidents_sent->SetWithoutPathExpansion(type_string, type_dict);
1795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
1805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    type_dict->SetStringWithoutPathExpansion(data.key,
1815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                             base::UintToString(data.digest));
1825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
1835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
1855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// Runs |callback| on the thread to which |thread_runner| belongs. The callback
1865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)// is run immediately if this function is called on |thread_runner|'s thread.
1875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void AddIncidentOnOriginThread(
1885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const AddIncidentCallback& callback,
1895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    scoped_refptr<base::SingleThreadTaskRunner> thread_runner,
1905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    scoped_ptr<ClientIncidentReport_IncidentData> incident) {
1915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (thread_runner->BelongsToCurrentThread())
1925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    callback.Run(incident.Pass());
1935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  else
1945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    thread_runner->PostTask(FROM_HERE,
1955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            base::Bind(callback, base::Passed(&incident)));
1965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
1975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
198f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace
199f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2006d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)struct IncidentReportingService::ProfileContext {
2016d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ProfileContext();
2026d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ~ProfileContext();
2036d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2046d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // The incidents collected for this profile pending creation and/or upload.
2056d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ScopedVector<ClientIncidentReport_IncidentData> incidents;
2066d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
207116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // False until PROFILE_ADDED notification is received.
208116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  bool added;
2096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles) private:
2116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(ProfileContext);
2126d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)};
2136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
214f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)class IncidentReportingService::UploadContext {
215f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) public:
2165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  typedef std::map<Profile*, std::vector<PersistentIncidentState> >
2175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      PersistentIncidentStateCollection;
2185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
219f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  explicit UploadContext(scoped_ptr<ClientIncidentReport> report);
220f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ~UploadContext();
221f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // The report being uploaded.
223f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  scoped_ptr<ClientIncidentReport> report;
2246d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2256d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // The uploader in use. This is NULL until the CSD killswitch is checked.
226f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  scoped_ptr<IncidentReportUploader> uploader;
227f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // A mapping of profiles to the data to be persisted upon successful upload.
2295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  PersistentIncidentStateCollection profiles_to_state;
2306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
231f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles) private:
232f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DISALLOW_COPY_AND_ASSIGN(UploadContext);
233f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)};
234f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
235116680a4aac90f2aa7413d9095a592090648e557Ben MurdochIncidentReportingService::ProfileContext::ProfileContext() : added() {
2366d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2376d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
2386d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)IncidentReportingService::ProfileContext::~ProfileContext() {
2396d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
2406d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)IncidentReportingService::UploadContext::UploadContext(
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    scoped_ptr<ClientIncidentReport> report)
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    : report(report.Pass()) {
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)IncidentReportingService::UploadContext::~UploadContext() {
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)IncidentReportingService::IncidentReportingService(
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    SafeBrowsingService* safe_browsing_service,
251f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const scoped_refptr<net::URLRequestContextGetter>& request_context_getter)
252f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    : database_manager_(safe_browsing_service ?
253f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                        safe_browsing_service->database_manager() : NULL),
254f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      url_request_context_getter_(request_context_getter),
255f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      collect_environment_data_fn_(&CollectEnvironmentData),
256f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      environment_collection_task_runner_(
257f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          content::BrowserThread::GetBlockingPool()
258f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)              ->GetTaskRunnerWithShutdownBehavior(
259f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                  base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
260f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      environment_collection_pending_(),
2615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      collation_timeout_pending_(),
2625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      collation_timer_(FROM_HERE,
2635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
2645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       this,
2655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       &IncidentReportingService::OnCollationTimeout),
2665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      delayed_analysis_callbacks_(
2675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          base::TimeDelta::FromMilliseconds(kDefaultCallbackIntervalMs),
2685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          content::BrowserThread::GetBlockingPool()
2695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              ->GetTaskRunnerWithShutdownBehavior(
2705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
271f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      receiver_weak_ptr_factory_(this),
272f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      weak_ptr_factory_(this) {
2736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  notification_registrar_.Add(this,
274116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                              chrome::NOTIFICATION_PROFILE_ADDED,
2756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                              content::NotificationService::AllSources());
2766d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  notification_registrar_.Add(this,
2776d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                              chrome::NOTIFICATION_PROFILE_DESTROYED,
2786d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                              content::NotificationService::AllSources());
279f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
280f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
281f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)IncidentReportingService::~IncidentReportingService() {
2826d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CancelIncidentCollection();
283f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2846d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Cancel all internal asynchronous tasks.
2856d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  weak_ptr_factory_.InvalidateWeakPtrs();
286f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2876d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CancelEnvironmentCollection();
288116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  CancelDownloadCollection();
2896d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  CancelAllReportUploads();
290f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  STLDeleteValues(&profiles_);
292f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
293f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
2946d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)AddIncidentCallback IncidentReportingService::GetAddIncidentCallback(
2956d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    Profile* profile) {
2966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Force the context to be created so that incidents added before
297116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // OnProfileAdded is called are held until the profile's preferences can be
2986d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // queried.
2996d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ignore_result(GetOrCreateProfileContext(profile));
3006d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
301f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return base::Bind(&IncidentReportingService::AddIncident,
3026d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                    receiver_weak_ptr_factory_.GetWeakPtr(),
3036d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)                    profile);
304f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
305f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
306f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)scoped_ptr<TrackedPreferenceValidationDelegate>
3076d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)IncidentReportingService::CreatePreferenceValidationDelegate(Profile* profile) {
308f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
3096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (profile->IsOffTheRecord())
3116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return scoped_ptr<TrackedPreferenceValidationDelegate>();
312f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return scoped_ptr<TrackedPreferenceValidationDelegate>(
3136d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      new PreferenceValidationDelegate(GetAddIncidentCallback(profile)));
314f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
315f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
3165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void IncidentReportingService::RegisterDelayedAnalysisCallback(
3175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const DelayedAnalysisCallback& callback) {
3185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
3195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // |callback| will be run on the blocking pool, so it will likely run the
3215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // AddIncidentCallback there as well. Bounce the run of that callback back to
3225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // the current thread via AddIncidentOnOriginThread.
3235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  delayed_analysis_callbacks_.RegisterCallback(
3245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      base::Bind(callback,
3255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                 base::Bind(&AddIncidentOnOriginThread,
3265f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            GetAddIncidentCallback(NULL),
3275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                            base::ThreadTaskRunnerHandle::Get())));
3285f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Start running the callbacks if any profiles are participating in safe
3305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // browsing. If none are now, running will commence if/when a participaing
3315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // profile is added.
3325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (FindEligibleProfile())
3335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    delayed_analysis_callbacks_.Start();
3345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
3355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)IncidentReportingService::IncidentReportingService(
3375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    SafeBrowsingService* safe_browsing_service,
3385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
3395f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    base::TimeDelta delayed_task_interval,
3405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const scoped_refptr<base::TaskRunner>& delayed_task_runner)
3415f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    : database_manager_(safe_browsing_service ?
3425f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                        safe_browsing_service->database_manager() : NULL),
3435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      url_request_context_getter_(request_context_getter),
3445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      collect_environment_data_fn_(&CollectEnvironmentData),
3455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      environment_collection_task_runner_(
3465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          content::BrowserThread::GetBlockingPool()
3475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)              ->GetTaskRunnerWithShutdownBehavior(
3485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                  base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)),
3495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      environment_collection_pending_(),
3505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      collation_timeout_pending_(),
3515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      collation_timer_(FROM_HERE,
3525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       base::TimeDelta::FromMilliseconds(kDefaultUploadDelayMs),
3535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       this,
3545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       &IncidentReportingService::OnCollationTimeout),
3555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      delayed_analysis_callbacks_(delayed_task_interval, delayed_task_runner),
3565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      receiver_weak_ptr_factory_(this),
3575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      weak_ptr_factory_(this) {
3585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  notification_registrar_.Add(this,
3595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              chrome::NOTIFICATION_PROFILE_ADDED,
3605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              content::NotificationService::AllSources());
3615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  notification_registrar_.Add(this,
3625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              chrome::NOTIFICATION_PROFILE_DESTROYED,
3635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                              content::NotificationService::AllSources());
3645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
3655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
366f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::SetCollectEnvironmentHook(
367f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    CollectEnvironmentDataFn collect_environment_data_hook,
368f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const scoped_refptr<base::TaskRunner>& task_runner) {
369f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (collect_environment_data_hook) {
370f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    collect_environment_data_fn_ = collect_environment_data_hook;
371f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    environment_collection_task_runner_ = task_runner;
372f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
373f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    collect_environment_data_fn_ = &CollectEnvironmentData;
374f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    environment_collection_task_runner_ =
375f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        content::BrowserThread::GetBlockingPool()
376f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            ->GetTaskRunnerWithShutdownBehavior(
377f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
378f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
379f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
380f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
381116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid IncidentReportingService::OnProfileAdded(Profile* profile) {
3826d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
3836d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
384116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Track the addition of all profiles even when no report is being assembled
385116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // so that the service can determine whether or not it can evaluate a
386116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // profile's preferences at the time of incident addition.
3876d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ProfileContext* context = GetOrCreateProfileContext(profile);
388116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  context->added = true;
3896d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
3905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  const bool safe_browsing_enabled =
3915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled);
3925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Start processing delayed analysis callbacks if this new profile
3945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // participates in safe browsing. Start is idempotent, so this is safe even if
3955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // they're already running.
3965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (safe_browsing_enabled)
3975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    delayed_analysis_callbacks_.Start();
3985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
3995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Start a new report if this profile participates in safe browsing and there
4005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // are process-wide incidents.
4011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (safe_browsing_enabled && GetProfileContext(NULL) &&
4021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci      GetProfileContext(NULL)->incidents.size()) {
4035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    BeginReportProcessing();
4041320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  }
4055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // TODO(grt): register for pref change notifications to start delayed analysis
4075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // and/or report processing if sb is currently disabled but subsequently
4085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // enabled.
4095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
410116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Nothing else to do if a report is not being assembled.
411116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!report_)
412116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
413116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
4145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Drop all incidents associated with this profile that were received prior to
4155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // its addition if the profile is not participating in safe browsing.
4165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!context->incidents.empty() && !safe_browsing_enabled) {
4175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for (size_t i = 0; i < context->incidents.size(); ++i)
4186d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      LogIncidentDataType(DROPPED, *context->incidents[i]);
4196d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    context->incidents.clear();
4206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
421116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
422116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Take another stab at finding the most recent download if a report is being
423116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // assembled and one hasn't been found yet (the LastDownloadFinder operates
424116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // only on profiles that have been added to the ProfileManager).
425116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  BeginDownloadCollection();
426116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
427116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
428116680a4aac90f2aa7413d9095a592090648e557Ben Murdochscoped_ptr<LastDownloadFinder> IncidentReportingService::CreateDownloadFinder(
429116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    const LastDownloadFinder::LastDownloadCallback& callback) {
430116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return LastDownloadFinder::Create(callback).Pass();
4316d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
4326d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
433f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)scoped_ptr<IncidentReportUploader> IncidentReportingService::StartReportUpload(
434f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const IncidentReportUploader::OnResultCallback& callback,
435f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const scoped_refptr<net::URLRequestContextGetter>& request_context_getter,
436f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    const ClientIncidentReport& report) {
437f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  return IncidentReportUploaderImpl::UploadReport(
438f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)             callback, request_context_getter, report).Pass();
439f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
440f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
4411320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tuccibool IncidentReportingService::IsProcessingReport() const {
4421320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  return report_ != NULL;
4431320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci}
4441320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci
4456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)IncidentReportingService::ProfileContext*
4466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)IncidentReportingService::GetOrCreateProfileContext(Profile* profile) {
4476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ProfileContextCollection::iterator it =
4486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      profiles_.insert(ProfileContextCollection::value_type(profile, NULL))
4496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          .first;
4506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!it->second)
4516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    it->second = new ProfileContext();
4526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return it->second;
4536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
4546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)IncidentReportingService::ProfileContext*
4566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)IncidentReportingService::GetProfileContext(Profile* profile) {
4576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ProfileContextCollection::iterator it = profiles_.find(profile);
4586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  return it == profiles_.end() ? NULL : it->second;
4596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
4606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void IncidentReportingService::OnProfileDestroyed(Profile* profile) {
4626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
4636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4646d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ProfileContextCollection::iterator it = profiles_.find(profile);
4656d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (it == profiles_.end())
4666d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
4676d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4686d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // TODO(grt): Persist incidents for upload on future profile load.
4696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4706d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Forget about this profile. Incidents not yet sent for upload are lost.
4716d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // No new incidents will be accepted for it.
4726d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  delete it->second;
4736d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  profiles_.erase(it);
4746d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
4755f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Remove the association with this profile from all pending uploads.
4765f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (size_t i = 0; i < uploads_.size(); ++i)
4775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    uploads_[i]->profiles_to_state.erase(profile);
4785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
4795f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
4805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)Profile* IncidentReportingService::FindEligibleProfile() const {
4815f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  Profile* candidate = NULL;
4825f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (ProfileContextCollection::const_iterator scan = profiles_.begin();
4835f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       scan != profiles_.end();
4845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       ++scan) {
4855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Skip over profiles that have yet to be added to the profile manager.
4865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // This will also skip over the NULL-profile context used to hold
4875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // process-wide incidents.
4885f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (!scan->second->added)
4895f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      continue;
4905f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    PrefService* prefs = scan->first->GetPrefs();
4915f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
4925f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (!candidate)
4935f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        candidate = scan->first;
4945f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (prefs->GetBoolean(prefs::kSafeBrowsingExtendedReportingEnabled)) {
4955f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        candidate = scan->first;
4965f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        break;
4975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
4986d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    }
4996d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
5005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return candidate;
5016d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
5026d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
503f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::AddIncident(
5046d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    Profile* profile,
505f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    scoped_ptr<ClientIncidentReport_IncidentData> incident_data) {
506f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
5075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK_EQ(1U, CountIncidents(*incident_data));
5086d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5096d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ProfileContext* context = GetProfileContext(profile);
5106d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // It is forbidden to call this function with a destroyed profile.
5116d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  DCHECK(context);
5125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // If this is a process-wide incident, the context must not indicate that the
5135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // profile (which is NULL) has been added to the profile manager.
5145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(profile || !context->added);
5156d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Drop the incident immediately if the profile has already been added to the
5175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // manager and is not participating in safe browsing. Preference evaluation is
5185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // deferred until OnProfileAdded() otherwise.
519116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (context->added &&
5206d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      !profile->GetPrefs()->GetBoolean(prefs::kSafeBrowsingEnabled)) {
5216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    LogIncidentDataType(DROPPED, *incident_data);
522f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
5236d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
524f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
525116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Provide time to the new incident if the caller didn't do so.
526f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!incident_data->has_incident_time_msec())
527f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    incident_data->set_incident_time_msec(base::Time::Now().ToJavaTime());
5286d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
5296d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Take ownership of the incident.
5306d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  context->incidents.push_back(incident_data.release());
531f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Remember when the first incident for this report arrived.
5335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (first_incident_time_.is_null())
5345f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    first_incident_time_ = base::Time::Now();
5355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Log the time between the previous incident and this one.
536f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!last_incident_time_.is_null()) {
537f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    UMA_HISTOGRAM_TIMES("SBIRS.InterIncidentTime",
538f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                        base::TimeTicks::Now() - last_incident_time_);
539f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
540f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  last_incident_time_ = base::TimeTicks::Now();
541f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
542f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Persist the incident data.
543f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Start assembling a new report if this is the first incident ever or the
5455f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // first since the last upload.
5465f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  BeginReportProcessing();
5475f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
548f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void IncidentReportingService::BeginReportProcessing() {
5505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
5515f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5525f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Creates a new report if needed.
5535f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!report_)
5545f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    report_.reset(new ClientIncidentReport());
5555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
5565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Ensure that collection tasks are running (calls are idempotent).
5575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  BeginIncidentCollation();
558f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  BeginEnvironmentCollection();
559116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  BeginDownloadCollection();
560f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
561f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
5625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void IncidentReportingService::BeginIncidentCollation() {
5635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Restart the delay timer to send the report upon expiration.
5645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  collation_timeout_pending_ = true;
5655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  collation_timer_.Reset();
5665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)}
5675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
568f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::BeginEnvironmentCollection() {
569f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
570f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(report_);
571116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Nothing to do if environment collection is pending or has already
572116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // completed.
573f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (environment_collection_pending_ || report_->has_environment())
574f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
575f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
576f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_collection_begin_ = base::TimeTicks::Now();
577f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ClientIncidentReport_EnvironmentData* environment_data =
578f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      new ClientIncidentReport_EnvironmentData();
579f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_collection_pending_ =
580f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      environment_collection_task_runner_->PostTaskAndReply(
581f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          FROM_HERE,
582f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          base::Bind(collect_environment_data_fn_, environment_data),
583f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)          base::Bind(&IncidentReportingService::OnEnvironmentDataCollected,
584f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                     weak_ptr_factory_.GetWeakPtr(),
585f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                     base::Passed(make_scoped_ptr(environment_data))));
586f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
587f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Posting the task will fail if the runner has been shut down. This should
588f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // never happen since the blocking pool is shut down after this service.
589f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(environment_collection_pending_);
590f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
591f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
592116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool IncidentReportingService::WaitingForEnvironmentCollection() {
593116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return environment_collection_pending_;
594116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
595116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
596f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::CancelEnvironmentCollection() {
597f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_collection_begin_ = base::TimeTicks();
598f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_collection_pending_ = false;
599f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (report_)
600f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    report_->clear_environment();
601f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
602f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
603f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::OnEnvironmentDataCollected(
604f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    scoped_ptr<ClientIncidentReport_EnvironmentData> environment_data) {
605f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
606f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(environment_collection_pending_);
607f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(report_ && !report_->has_environment());
608f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_collection_pending_ = false;
609f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
610f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)// CurrentProcessInfo::CreationTime() is missing on some platforms.
611f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#if defined(OS_MACOSX) || defined(OS_WIN) || defined(OS_LINUX)
612f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  base::TimeDelta uptime =
613f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      first_incident_time_ - base::CurrentProcessInfo::CreationTime();
614f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_data->mutable_process()->set_uptime_msec(uptime.InMilliseconds());
615f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)#endif
616f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
617f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  report_->set_allocated_environment(environment_data.release());
618f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
619f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  UMA_HISTOGRAM_TIMES("SBIRS.EnvCollectionTime",
620f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                      base::TimeTicks::Now() - environment_collection_begin_);
621f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  environment_collection_begin_ = base::TimeTicks();
622f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
623f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  UploadIfCollectionComplete();
624f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
625f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
626116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool IncidentReportingService::WaitingToCollateIncidents() {
6275f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  return collation_timeout_pending_;
628116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
629116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
630f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::CancelIncidentCollection() {
6315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  collation_timeout_pending_ = false;
632f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  last_incident_time_ = base::TimeTicks();
633f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  report_.reset();
634f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
635f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
6365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)void IncidentReportingService::OnCollationTimeout() {
637f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
638f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
639f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Exit early if collection was cancelled.
6405f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (!collation_timeout_pending_)
641f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
642f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
6435f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Wait another round if profile-bound incidents have come in from a profile
6445f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // that has yet to complete creation.
6456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  for (ProfileContextCollection::iterator scan = profiles_.begin();
6466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)       scan != profiles_.end();
6476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)       ++scan) {
6485f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (scan->first && !scan->second->added &&
6495f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        !scan->second->incidents.empty()) {
6505f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      collation_timer_.Reset();
6516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      return;
6526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    }
6536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
6546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
6555f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  collation_timeout_pending_ = false;
656f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
657f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  UploadIfCollectionComplete();
658f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
659f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
660116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid IncidentReportingService::BeginDownloadCollection() {
661116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(thread_checker_.CalledOnValidThread());
662116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(report_);
663116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Nothing to do if a search for the most recent download is already pending
664116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // or if one has already been found.
665116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (last_download_finder_ || report_->has_download())
666116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
667116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
668116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  last_download_begin_ = base::TimeTicks::Now();
669116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  last_download_finder_ = CreateDownloadFinder(
670116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      base::Bind(&IncidentReportingService::OnLastDownloadFound,
671116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                 weak_ptr_factory_.GetWeakPtr()));
672116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // No instance is returned if there are no eligible loaded profiles. Another
673116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // search will be attempted in OnProfileAdded() if another profile appears on
674116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // the scene.
675116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!last_download_finder_)
676116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    last_download_begin_ = base::TimeTicks();
677116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
678116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
679116680a4aac90f2aa7413d9095a592090648e557Ben Murdochbool IncidentReportingService::WaitingForMostRecentDownload() {
680116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(report_);  // Only call this when a report is being assembled.
681116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // The easy case: not waiting if a download has already been found.
682116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (report_->has_download())
683116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return false;
684116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // The next easy case: waiting if the finder is operating.
685116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (last_download_finder_)
686116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return true;
6871320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  // The harder case: waiting if a non-NULL profile has not yet been added.
688116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  for (ProfileContextCollection::const_iterator scan = profiles_.begin();
689116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch       scan != profiles_.end();
690116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch       ++scan) {
6911320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci    if (scan->first && !scan->second->added)
692116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      return true;
693116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
694116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // There is no most recent download and there's nothing more to wait for.
695116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  return false;
696116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
697116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
698116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid IncidentReportingService::CancelDownloadCollection() {
699116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  last_download_finder_.reset();
700116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  last_download_begin_ = base::TimeTicks();
701116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (report_)
702116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    report_->clear_download();
703116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch}
704116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
705116680a4aac90f2aa7413d9095a592090648e557Ben Murdochvoid IncidentReportingService::OnLastDownloadFound(
706116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    scoped_ptr<ClientIncidentReport_DownloadDetails> last_download) {
707f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
708116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  DCHECK(report_);
709116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
710116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UMA_HISTOGRAM_TIMES("SBIRS.FindDownloadedBinaryTime",
711116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                      base::TimeTicks::Now() - last_download_begin_);
712116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  last_download_begin_ = base::TimeTicks();
713116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
714116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Harvest the finder.
715116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  last_download_finder_.reset();
716116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
717116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (last_download)
718116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    report_->set_allocated_download(last_download.release());
719116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
720116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  UploadIfCollectionComplete();
721f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
722f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
723f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::UploadIfCollectionComplete() {
724f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(report_);
725116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Bail out if there are still outstanding collection tasks. Completion of any
726116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // of these will start another upload attempt.
727116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (WaitingForEnvironmentCollection() ||
728116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      WaitingToCollateIncidents() ||
729116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      WaitingForMostRecentDownload()) {
730f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
731116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
732f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
733f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Take ownership of the report and clear things for future reports.
734f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  scoped_ptr<ClientIncidentReport> report(report_.Pass());
7355f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  first_incident_time_ = base::Time();
736f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  last_incident_time_ = base::TimeTicks();
737f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
738116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  // Drop the report if no executable download was found.
739116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  if (!report->has_download()) {
740116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
741116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                              IncidentReportUploader::UPLOAD_NO_DOWNLOAD,
742116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                              IncidentReportUploader::NUM_UPLOAD_RESULTS);
743116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    return;
744116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  }
745116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
7466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  ClientIncidentReport_EnvironmentData_Process* process =
7476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      report->mutable_environment()->mutable_process();
7486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
7496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Not all platforms have a metrics reporting preference.
7506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (g_browser_process->local_state()->FindPreference(
7516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)          prefs::kMetricsReportingEnabled)) {
7526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    process->set_metrics_consent(g_browser_process->local_state()->GetBoolean(
7536d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        prefs::kMetricsReportingEnabled));
7546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
7556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
7565f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Find the profile that benefits from the strongest protections.
7575f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  Profile* eligible_profile = FindEligibleProfile();
7585f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  process->set_extended_consent(
7595f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      eligible_profile ? eligible_profile->GetPrefs()->GetBoolean(
7605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                             prefs::kSafeBrowsingExtendedReportingEnabled) :
7615f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                       false);
7625f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7635f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Associate process-wide incidents with the profile that benefits from the
7645f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // strongest safe browsing protections.
7655f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  ProfileContext* null_context = GetProfileContext(NULL);
7665f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  if (null_context && !null_context->incidents.empty() && eligible_profile) {
7675f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    ProfileContext* eligible_context = GetProfileContext(eligible_profile);
7685f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Move the incidents to the target context.
7695f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    eligible_context->incidents.insert(eligible_context->incidents.end(),
7705f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       null_context->incidents.begin(),
7715f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       null_context->incidents.end());
7725f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    null_context->incidents.weak_clear();
7735f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  }
7745f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)
7756d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Collect incidents across all profiles participating in safe browsing. Drop
7766d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // incidents if the profile stopped participating before collection completed.
7775f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Prune previously submitted incidents.
7785f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  // Associate the profiles and their incident data with the upload.
7796d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  size_t prune_count = 0;
7805f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  UploadContext::PersistentIncidentStateCollection profiles_to_state;
7816d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  for (ProfileContextCollection::iterator scan = profiles_.begin();
7826d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)       scan != profiles_.end();
7836d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)       ++scan) {
7845f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Bypass process-wide incidents that have not yet been associated with a
7855f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // profile.
7865f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (!scan->first)
7875f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      continue;
7886d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    PrefService* prefs = scan->first->GetPrefs();
7896d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    ProfileContext* context = scan->second;
7906d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    if (context->incidents.empty())
7916d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      continue;
7926d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    if (!prefs->GetBoolean(prefs::kSafeBrowsingEnabled)) {
7936d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      for (size_t i = 0; i < context->incidents.size(); ++i) {
7946d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        LogIncidentDataType(DROPPED, *context->incidents[i]);
7956d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      }
7966d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      context->incidents.clear();
7975f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      continue;
7985f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
7995f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    std::vector<PersistentIncidentState> states;
8005f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    const base::DictionaryValue* incidents_sent =
8015f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        prefs->GetDictionary(prefs::kSafeBrowsingIncidentsSent);
8025f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    // Prep persistent data and prune any incidents already sent.
8035f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    for (size_t i = 0; i < context->incidents.size(); ++i) {
8045f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      ClientIncidentReport_IncidentData* incident = context->incidents[i];
8055f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      const PersistentIncidentState state = ComputeIncidentState(*incident);
8065f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      if (IncidentHasBeenReported(incidents_sent, state)) {
8075f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        ++prune_count;
8085f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        delete context->incidents[i];
8095f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        context->incidents[i] = NULL;
8105f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      } else {
8115f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        states.push_back(state);
8125f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
8135f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    }
8145f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    if (prefs->GetBoolean(prefs::kSafeBrowsingIncidentReportSent)) {
8155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // Prune all incidents as if they had been reported, migrating to the new
8165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      // technique. TODO(grt): remove this branch after it has shipped.
8175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      for (size_t i = 0; i < context->incidents.size(); ++i) {
8185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (context->incidents[i])
8195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          ++prune_count;
8205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      }
8216d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      context->incidents.clear();
8225f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      prefs->ClearPref(prefs::kSafeBrowsingIncidentReportSent);
8235f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      DictionaryPrefUpdate pref_update(prefs,
8245f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                       prefs::kSafeBrowsingIncidentsSent);
8255f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      MarkIncidentsAsReported(states, pref_update.Get());
8266d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    } else {
8276d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      for (size_t i = 0; i < context->incidents.size(); ++i) {
8286d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        ClientIncidentReport_IncidentData* incident = context->incidents[i];
8295f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        if (incident) {
8305f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          LogIncidentDataType(ACCEPTED, *incident);
8315f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          // Ownership of the incident is passed to the report.
8325f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          report->mutable_incident()->AddAllocated(incident);
8335f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)        }
8346d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      }
8356d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      context->incidents.weak_clear();
8365f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      std::vector<PersistentIncidentState>& profile_states =
8375f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)          profiles_to_state[scan->first];
8385f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)      profile_states.swap(states);
8396d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    }
840f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
841f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
8426d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  const int count = report->incident_size();
8436d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  // Abandon the request if all incidents were dropped with none pruned.
8446d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!count && !prune_count)
8456d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    return;
8466d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
8476d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  UMA_HISTOGRAM_COUNTS_100("SBIRS.IncidentCount", count + prune_count);
848f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
849f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  {
8506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    double prune_pct = static_cast<double>(prune_count);
8516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    prune_pct = prune_pct * 100.0 / (count + prune_count);
852f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    prune_pct = round(prune_pct);
853f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    UMA_HISTOGRAM_PERCENTAGE("SBIRS.PruneRatio", static_cast<int>(prune_pct));
854f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
855f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // Abandon the report if all incidents were pruned.
8566d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  if (!count)
857f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    return;
858f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
859f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  scoped_ptr<UploadContext> context(new UploadContext(report.Pass()));
8605f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  context->profiles_to_state.swap(profiles_to_state);
8611320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci  if (!database_manager_.get()) {
862f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // No database manager during testing. Take ownership of the context and
863f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // continue processing.
864f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    UploadContext* temp_context = context.get();
865f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    uploads_.push_back(context.release());
866f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    IncidentReportingService::OnKillSwitchResult(temp_context, false);
867f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
868f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (content::BrowserThread::PostTaskAndReplyWithResult(
869f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            content::BrowserThread::IO,
870f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            FROM_HERE,
871f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            base::Bind(&SafeBrowsingDatabaseManager::IsCsdWhitelistKillSwitchOn,
872f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       database_manager_),
873f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            base::Bind(&IncidentReportingService::OnKillSwitchResult,
874f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       weak_ptr_factory_.GetWeakPtr(),
875f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       context.get()))) {
876f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      uploads_.push_back(context.release());
877f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }  // else should not happen. Let the context be deleted automatically.
878f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
879f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
880f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
881f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::CancelAllReportUploads() {
882f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  for (size_t i = 0; i < uploads_.size(); ++i) {
883f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    UMA_HISTOGRAM_ENUMERATION("SBIRS.UploadResult",
884f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              IncidentReportUploader::UPLOAD_CANCELLED,
885f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                              IncidentReportUploader::NUM_UPLOAD_RESULTS);
886f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
887f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  uploads_.clear();
888f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
889f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
890f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::OnKillSwitchResult(UploadContext* context,
891f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                                  bool is_killswitch_on) {
892f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
893f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (!is_killswitch_on) {
894f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // Initiate the upload.
895f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    context->uploader =
896f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)        StartReportUpload(
897f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            base::Bind(&IncidentReportingService::OnReportUploadResult,
898f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       weak_ptr_factory_.GetWeakPtr(),
899f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                       context),
900f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            url_request_context_getter_,
901f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)            *context->report).Pass();
902f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (!context->uploader) {
903f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      OnReportUploadResult(context,
904f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                           IncidentReportUploader::UPLOAD_INVALID_REQUEST,
905f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                           scoped_ptr<ClientIncidentResponse>());
906f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
907f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  } else {
908f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    OnReportUploadResult(context,
909f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                         IncidentReportUploader::UPLOAD_SUPPRESSED,
910f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                         scoped_ptr<ClientIncidentResponse>());
911f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
912f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
913f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
9146d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void IncidentReportingService::HandleResponse(const UploadContext& context) {
9155f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)  for (UploadContext::PersistentIncidentStateCollection::const_iterator scan =
9165f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)           context.profiles_to_state.begin();
9175f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       scan != context.profiles_to_state.end();
9185f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)       ++scan) {
9195f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    DictionaryPrefUpdate pref_update(scan->first->GetPrefs(),
9205f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)                                     prefs::kSafeBrowsingIncidentsSent);
9215f1c94371a64b3196d4be9466099bb892df9b88eTorne (Richard Coles)    MarkIncidentsAsReported(scan->second, pref_update.Get());
9226d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
923f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
924f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
925f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)void IncidentReportingService::OnReportUploadResult(
926f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    UploadContext* context,
927f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    IncidentReportUploader::Result result,
928f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    scoped_ptr<ClientIncidentResponse> response) {
929f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(thread_checker_.CalledOnValidThread());
930f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
931f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  UMA_HISTOGRAM_ENUMERATION(
932f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      "SBIRS.UploadResult", result, IncidentReportUploader::NUM_UPLOAD_RESULTS);
933f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
934f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // The upload is no longer outstanding, so take ownership of the context (from
935f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // the collection of outstanding uploads) in this scope.
936f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  ScopedVector<UploadContext>::iterator it(
937f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      std::find(uploads_.begin(), uploads_.end(), context));
938f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  DCHECK(it != uploads_.end());
939f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  scoped_ptr<UploadContext> upload(context);  // == *it
940f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  *it = uploads_.back();
941f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  uploads_.weak_erase(uploads_.end() - 1);
942f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
943f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (result == IncidentReportUploader::UPLOAD_SUCCESS)
9446d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    HandleResponse(*upload);
945f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  // else retry?
946f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}
947f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)
9486d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)void IncidentReportingService::Observe(
9496d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    int type,
9506d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    const content::NotificationSource& source,
9516d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    const content::NotificationDetails& details) {
9526d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  switch (type) {
953116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    case chrome::NOTIFICATION_PROFILE_ADDED: {
9546d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      Profile* profile = content::Source<Profile>(source).ptr();
9556d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      if (!profile->IsOffTheRecord())
956116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch        OnProfileAdded(profile);
9576d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      break;
9586d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    }
9596d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    case chrome::NOTIFICATION_PROFILE_DESTROYED: {
9606d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      Profile* profile = content::Source<Profile>(source).ptr();
9616d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      if (!profile->IsOffTheRecord())
9626d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)        OnProfileDestroyed(profile);
9636d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      break;
9646d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    }
9656d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)    default:
9666d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)      break;
9676d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)  }
9686d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)}
9696d86b77056ed63eb6871182f42a9fd5f07550f90Torne (Richard Coles)
970f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)}  // namespace safe_browsing
971