• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "components/metrics/cloned_install_detector.h"
6 
7 #include <stdint.h>
8 
9 #include <string>
10 
11 #include "base/callback_list.h"
12 #include "base/functional/bind.h"
13 #include "base/location.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/metrics/metrics_hashes.h"
16 #include "base/task/single_thread_task_runner.h"
17 #include "base/task/thread_pool.h"
18 #include "components/metrics/machine_id_provider.h"
19 #include "components/metrics/metrics_pref_names.h"
20 #include "components/prefs/pref_registry_simple.h"
21 #include "components/prefs/pref_service.h"
22 
23 namespace metrics {
24 
25 namespace {
26 
HashRawId(const std::string & value)27 uint32_t HashRawId(const std::string& value) {
28   uint64_t hash = base::HashMetricName(value);
29 
30   // Only use 24 bits from the 64-bit hash.
31   return hash & ((1 << 24) - 1);
32 }
33 
34 // State of the generated machine id in relation to the previously stored value.
35 // Note: UMA histogram enum - don't re-order or remove entries
36 enum MachineIdState {
37   ID_GENERATION_FAILED,
38   ID_NO_STORED_VALUE,
39   ID_CHANGED,
40   ID_UNCHANGED,
41   ID_ENUM_SIZE
42 };
43 
44 // Logs the state of generating a machine id and comparing it to a stored value.
LogMachineIdState(MachineIdState state)45 void LogMachineIdState(MachineIdState state) {
46   UMA_HISTOGRAM_ENUMERATION("UMA.MachineIdState", state, ID_ENUM_SIZE);
47 }
48 
49 }  // namespace
50 
ClonedInstallDetector()51 ClonedInstallDetector::ClonedInstallDetector() {}
52 
~ClonedInstallDetector()53 ClonedInstallDetector::~ClonedInstallDetector() {
54 }
55 
CheckForClonedInstall(PrefService * local_state)56 void ClonedInstallDetector::CheckForClonedInstall(PrefService* local_state) {
57   if (!MachineIdProvider::HasId())
58     return;
59 
60   base::ThreadPool::PostTaskAndReplyWithResult(
61       FROM_HERE,
62       {base::MayBlock(), base::TaskPriority::BEST_EFFORT,
63        base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN},
64       base::BindOnce(&MachineIdProvider::GetMachineId),
65       base::BindOnce(&ClonedInstallDetector::SaveMachineId,
66                      weak_ptr_factory_.GetWeakPtr(), local_state));
67 }
68 
SaveMachineId(PrefService * local_state,const std::string & raw_id)69 void ClonedInstallDetector::SaveMachineId(PrefService* local_state,
70                                           const std::string& raw_id) {
71   if (raw_id.empty()) {
72     LogMachineIdState(ID_GENERATION_FAILED);
73     local_state->ClearPref(prefs::kMetricsMachineId);
74     return;
75   }
76 
77   int hashed_id = HashRawId(raw_id);
78 
79   MachineIdState id_state = ID_NO_STORED_VALUE;
80   if (local_state->HasPrefPath(prefs::kMetricsMachineId)) {
81     if (local_state->GetInteger(prefs::kMetricsMachineId) != hashed_id) {
82       DCHECK(!detected_this_session_);
83       id_state = ID_CHANGED;
84       detected_this_session_ = true;
85       local_state->SetBoolean(prefs::kMetricsResetIds, true);
86       callback_list_.Notify();
87     } else {
88       id_state = ID_UNCHANGED;
89     }
90   }
91 
92   LogMachineIdState(id_state);
93 
94   local_state->SetInteger(prefs::kMetricsMachineId, hashed_id);
95 }
96 
ShouldResetClientIds(PrefService * local_state)97 bool ClonedInstallDetector::ShouldResetClientIds(PrefService* local_state) {
98   // The existence of the pref indicates that it has been set when we saved the
99   // MachineId and thus we need to update the member variable for this session
100   // and clear the pref for future runs. We shouldn't clear the pref multiple
101   // times because it may have been cloned again.
102   if (!should_reset_client_ids_ &&
103       local_state->HasPrefPath(prefs::kMetricsResetIds)) {
104     should_reset_client_ids_ = local_state->GetBoolean(prefs::kMetricsResetIds);
105     local_state->ClearPref(prefs::kMetricsResetIds);
106   }
107 
108   return should_reset_client_ids_;
109 }
110 
ClonedInstallDetectedInCurrentSession() const111 bool ClonedInstallDetector::ClonedInstallDetectedInCurrentSession() const {
112   return detected_this_session_;
113 }
114 
115 base::CallbackListSubscription
AddOnClonedInstallDetectedCallback(base::OnceClosure callback)116 ClonedInstallDetector::AddOnClonedInstallDetectedCallback(
117     base::OnceClosure callback) {
118   if (detected_this_session_) {
119     // If this install has already been detected as cloned, run the callback
120     // immediately.
121     std::move(callback).Run();
122     return base::CallbackListSubscription();
123   }
124   return callback_list_.Add(std::move(callback));
125 }
126 
SaveMachineIdForTesting(PrefService * local_state,const std::string & raw_id)127 void ClonedInstallDetector::SaveMachineIdForTesting(PrefService* local_state,
128                                                     const std::string& raw_id) {
129   SaveMachineId(local_state, raw_id);
130 }
131 
132 // static
RegisterPrefs(PrefRegistrySimple * registry)133 void ClonedInstallDetector::RegisterPrefs(PrefRegistrySimple* registry) {
134   registry->RegisterBooleanPref(prefs::kMetricsResetIds, false);
135   registry->RegisterIntegerPref(prefs::kMetricsMachineId, 0);
136   registry->RegisterIntegerPref(prefs::kClonedResetCount, 0);
137   registry->RegisterInt64Pref(prefs::kFirstClonedResetTimestamp, 0);
138   registry->RegisterInt64Pref(prefs::kLastClonedResetTimestamp, 0);
139 }
140 
ReadClonedInstallInfo(PrefService * local_state)141 ClonedInstallInfo ClonedInstallDetector::ReadClonedInstallInfo(
142     PrefService* local_state) {
143   return ClonedInstallInfo{
144       .last_reset_timestamp =
145           local_state->GetInt64(prefs::kLastClonedResetTimestamp),
146       .first_reset_timestamp =
147           local_state->GetInt64(prefs::kFirstClonedResetTimestamp),
148       .reset_count = local_state->GetInteger(prefs::kClonedResetCount)};
149 }
150 
ClearClonedInstallInfo(PrefService * local_state)151 void ClonedInstallDetector::ClearClonedInstallInfo(PrefService* local_state) {
152   local_state->ClearPref(prefs::kClonedResetCount);
153   local_state->ClearPref(prefs::kFirstClonedResetTimestamp);
154   local_state->ClearPref(prefs::kLastClonedResetTimestamp);
155 }
156 
RecordClonedInstallInfo(PrefService * local_state)157 void ClonedInstallDetector::RecordClonedInstallInfo(PrefService* local_state) {
158   ClonedInstallInfo cloned = ReadClonedInstallInfo(local_state);
159 
160   // Make sure that at the first time of reset, the first_timestamp matches with
161   // the last_timestamp.
162   int64_t time = base::Time::Now().ToTimeT();
163 
164   // Only set |prefs::kFirstClonedResetTimestamp| when the client needs to be
165   // reset due to cloned install for the first time.
166   if (cloned.reset_count == 0) {
167     local_state->SetInt64(prefs::kFirstClonedResetTimestamp, time);
168   }
169   local_state->SetInt64(prefs::kLastClonedResetTimestamp, time);
170   local_state->SetInteger(prefs::kClonedResetCount, cloned.reset_count + 1);
171 }
172 
173 }  // namespace metrics
174