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