| // Copyright 2014 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "components/metrics/cloned_install_detector.h" |
| |
| #include <stdint.h> |
| |
| #include <string> |
| |
| #include "base/callback_list.h" |
| #include "base/functional/bind.h" |
| #include "base/location.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/metrics_hashes.h" |
| #include "base/task/single_thread_task_runner.h" |
| #include "base/task/thread_pool.h" |
| #include "components/metrics/machine_id_provider.h" |
| #include "components/metrics/metrics_pref_names.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| |
| namespace metrics { |
| |
| namespace { |
| |
| uint32_t HashRawId(const std::string& value) { |
| uint64_t hash = base::HashMetricName(value); |
| |
| // Only use 24 bits from the 64-bit hash. |
| return hash & ((1 << 24) - 1); |
| } |
| |
| // State of the generated machine id in relation to the previously stored value. |
| // Note: UMA histogram enum - don't re-order or remove entries |
| enum MachineIdState { |
| ID_GENERATION_FAILED, |
| ID_NO_STORED_VALUE, |
| ID_CHANGED, |
| ID_UNCHANGED, |
| ID_ENUM_SIZE |
| }; |
| |
| // Logs the state of generating a machine id and comparing it to a stored value. |
| void LogMachineIdState(MachineIdState state) { |
| UMA_HISTOGRAM_ENUMERATION("UMA.MachineIdState", state, ID_ENUM_SIZE); |
| } |
| |
| } // namespace |
| |
| ClonedInstallDetector::ClonedInstallDetector() {} |
| |
| ClonedInstallDetector::~ClonedInstallDetector() { |
| } |
| |
| void ClonedInstallDetector::CheckForClonedInstall(PrefService* local_state) { |
| if (!MachineIdProvider::HasId()) |
| return; |
| |
| base::ThreadPool::PostTaskAndReplyWithResult( |
| FROM_HERE, |
| {base::MayBlock(), base::TaskPriority::BEST_EFFORT, |
| base::TaskShutdownBehavior::CONTINUE_ON_SHUTDOWN}, |
| base::BindOnce(&MachineIdProvider::GetMachineId), |
| base::BindOnce(&ClonedInstallDetector::SaveMachineId, |
| weak_ptr_factory_.GetWeakPtr(), local_state)); |
| } |
| |
| void ClonedInstallDetector::SaveMachineId(PrefService* local_state, |
| const std::string& raw_id) { |
| if (raw_id.empty()) { |
| LogMachineIdState(ID_GENERATION_FAILED); |
| local_state->ClearPref(prefs::kMetricsMachineId); |
| return; |
| } |
| |
| int hashed_id = HashRawId(raw_id); |
| |
| MachineIdState id_state = ID_NO_STORED_VALUE; |
| if (local_state->HasPrefPath(prefs::kMetricsMachineId)) { |
| if (local_state->GetInteger(prefs::kMetricsMachineId) != hashed_id) { |
| DCHECK(!detected_this_session_); |
| id_state = ID_CHANGED; |
| detected_this_session_ = true; |
| local_state->SetBoolean(prefs::kMetricsResetIds, true); |
| callback_list_.Notify(); |
| } else { |
| id_state = ID_UNCHANGED; |
| } |
| } |
| |
| LogMachineIdState(id_state); |
| |
| local_state->SetInteger(prefs::kMetricsMachineId, hashed_id); |
| } |
| |
| bool ClonedInstallDetector::ShouldResetClientIds(PrefService* local_state) { |
| // The existence of the pref indicates that it has been set when we saved the |
| // MachineId and thus we need to update the member variable for this session |
| // and clear the pref for future runs. We shouldn't clear the pref multiple |
| // times because it may have been cloned again. |
| if (!should_reset_client_ids_ && |
| local_state->HasPrefPath(prefs::kMetricsResetIds)) { |
| should_reset_client_ids_ = local_state->GetBoolean(prefs::kMetricsResetIds); |
| local_state->ClearPref(prefs::kMetricsResetIds); |
| } |
| |
| return should_reset_client_ids_; |
| } |
| |
| bool ClonedInstallDetector::ClonedInstallDetectedInCurrentSession() const { |
| return detected_this_session_; |
| } |
| |
| base::CallbackListSubscription |
| ClonedInstallDetector::AddOnClonedInstallDetectedCallback( |
| base::OnceClosure callback) { |
| if (detected_this_session_) { |
| // If this install has already been detected as cloned, run the callback |
| // immediately. |
| std::move(callback).Run(); |
| return base::CallbackListSubscription(); |
| } |
| return callback_list_.Add(std::move(callback)); |
| } |
| |
| void ClonedInstallDetector::SaveMachineIdForTesting(PrefService* local_state, |
| const std::string& raw_id) { |
| SaveMachineId(local_state, raw_id); |
| } |
| |
| // static |
| void ClonedInstallDetector::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterBooleanPref(prefs::kMetricsResetIds, false); |
| registry->RegisterIntegerPref(prefs::kMetricsMachineId, 0); |
| registry->RegisterIntegerPref(prefs::kClonedResetCount, 0); |
| registry->RegisterInt64Pref(prefs::kFirstClonedResetTimestamp, 0); |
| registry->RegisterInt64Pref(prefs::kLastClonedResetTimestamp, 0); |
| } |
| |
| ClonedInstallInfo ClonedInstallDetector::ReadClonedInstallInfo( |
| PrefService* local_state) { |
| return ClonedInstallInfo{ |
| .last_reset_timestamp = |
| local_state->GetInt64(prefs::kLastClonedResetTimestamp), |
| .first_reset_timestamp = |
| local_state->GetInt64(prefs::kFirstClonedResetTimestamp), |
| .reset_count = local_state->GetInteger(prefs::kClonedResetCount)}; |
| } |
| |
| void ClonedInstallDetector::ClearClonedInstallInfo(PrefService* local_state) { |
| local_state->ClearPref(prefs::kClonedResetCount); |
| local_state->ClearPref(prefs::kFirstClonedResetTimestamp); |
| local_state->ClearPref(prefs::kLastClonedResetTimestamp); |
| } |
| |
| void ClonedInstallDetector::RecordClonedInstallInfo(PrefService* local_state) { |
| ClonedInstallInfo cloned = ReadClonedInstallInfo(local_state); |
| |
| // Make sure that at the first time of reset, the first_timestamp matches with |
| // the last_timestamp. |
| int64_t time = base::Time::Now().ToTimeT(); |
| |
| // Only set |prefs::kFirstClonedResetTimestamp| when the client needs to be |
| // reset due to cloned install for the first time. |
| if (cloned.reset_count == 0) { |
| local_state->SetInt64(prefs::kFirstClonedResetTimestamp, time); |
| } |
| local_state->SetInt64(prefs::kLastClonedResetTimestamp, time); |
| local_state->SetInteger(prefs::kClonedResetCount, cloned.reset_count + 1); |
| } |
| |
| } // namespace metrics |