diff options
author | blundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-22 13:49:39 +0000 |
---|---|---|
committer | blundell@chromium.org <blundell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2014-05-22 13:49:39 +0000 |
commit | 66d176ad38d72eabc969893b2b09ad6759109836 (patch) | |
tree | b49f61dcb41744b0fab09f0a7893dd84e940d1f8 /components | |
parent | 5a23d6986317ab9018521c3eaa87957a214d189d (diff) | |
download | chromium_src-66d176ad38d72eabc969893b2b09ad6759109836.zip chromium_src-66d176ad38d72eabc969893b2b09ad6759109836.tar.gz chromium_src-66d176ad38d72eabc969893b2b09ad6759109836.tar.bz2 |
Move ClonedInstallDetector and MachineIdProvider into the metrics component
cloned_install_detector_unittest is left in //chrome at the current time as it
still has //chrome-level dependencies.
BUG=374212
R=isherman@chromium.org
TBR=thakis
Review URL: https://codereview.chromium.org/290033003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272172 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'components')
-rw-r--r-- | components/components_tests.gyp | 1 | ||||
-rw-r--r-- | components/metrics.gypi | 10 | ||||
-rw-r--r-- | components/metrics/cloned_install_detector.cc | 98 | ||||
-rw-r--r-- | components/metrics/cloned_install_detector.h | 59 | ||||
-rw-r--r-- | components/metrics/machine_id_provider.h | 46 | ||||
-rw-r--r-- | components/metrics/machine_id_provider_stub.cc | 24 | ||||
-rw-r--r-- | components/metrics/machine_id_provider_win.cc | 117 | ||||
-rw-r--r-- | components/metrics/machine_id_provider_win_unittest.cc | 23 | ||||
-rw-r--r-- | components/metrics/metrics_pref_names.cc | 8 | ||||
-rw-r--r-- | components/metrics/metrics_pref_names.h | 2 |
10 files changed, 388 insertions, 0 deletions
diff --git a/components/components_tests.gyp b/components/components_tests.gyp index 64cf12b..c6928eb 100644 --- a/components/components_tests.gyp +++ b/components/components_tests.gyp @@ -104,6 +104,7 @@ 'keyed_service/content/browser_context_dependency_manager_unittest.cc', 'keyed_service/core/dependency_graph_unittest.cc', 'language_usage_metrics/language_usage_metrics_unittest.cc', + 'metrics/machine_id_provider_win_unittest.cc', 'metrics/metrics_hashes_unittest.cc', 'metrics/metrics_log_base_unittest.cc', 'metrics/metrics_log_manager_unittest.cc', diff --git a/components/metrics.gypi b/components/metrics.gypi index 850ecd09..fd79d50 100644 --- a/components/metrics.gypi +++ b/components/metrics.gypi @@ -16,6 +16,11 @@ ], 'sources': [ 'metrics/metrics_provider.h', + 'metrics/cloned_install_detector.cc', + 'metrics/cloned_install_detector.h', + 'metrics/machine_id_provider.h', + 'metrics/machine_id_provider_stub.cc', + 'metrics/machine_id_provider_win.cc', 'metrics/metrics_hashes.cc', 'metrics/metrics_hashes.h', 'metrics/metrics_log_base.cc', @@ -38,6 +43,11 @@ 'metrics_chromeos', ], }], + ['OS=="win"', { + 'sources!': [ + 'metrics/machine_id_provider_stub.cc', + ], + }], ], }, { diff --git a/components/metrics/cloned_install_detector.cc b/components/metrics/cloned_install_detector.cc new file mode 100644 index 0000000..b93e6e6 --- /dev/null +++ b/components/metrics/cloned_install_detector.cc @@ -0,0 +1,98 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// 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 "base/bind.h" +#include "base/location.h" +#include "base/metrics/histogram.h" +#include "base/prefs/pref_registry_simple.h" +#include "base/prefs/pref_service.h" +#include "base/single_thread_task_runner.h" +#include "base/task_runner_util.h" +#include "components/metrics/cloned_install_detector.h" +#include "components/metrics/machine_id_provider.h" +#include "components/metrics/metrics_hashes.h" +#include "components/metrics/metrics_pref_names.h" + +namespace metrics { + +namespace { + +uint32 HashRawId(const std::string& value) { + uint64 hash = metrics::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(MachineIdProvider* raw_id_provider) + : raw_id_provider_(raw_id_provider), weak_ptr_factory_(this) { +} + +ClonedInstallDetector::~ClonedInstallDetector() { +} + +void ClonedInstallDetector::CheckForClonedInstall( + PrefService* local_state, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) { + base::PostTaskAndReplyWithResult( + task_runner.get(), + FROM_HERE, + base::Bind(&metrics::MachineIdProvider::GetMachineId, raw_id_provider_), + base::Bind(&metrics::ClonedInstallDetector::SaveMachineId, + weak_ptr_factory_.GetWeakPtr(), + local_state)); +} + +void ClonedInstallDetector::SaveMachineId(PrefService* local_state, + 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) { + id_state = ID_CHANGED; + // TODO(jwd): Use a callback to set the reset pref. That way + // ClonedInstallDetector doesn't need to know about this pref. + local_state->SetBoolean(prefs::kMetricsResetIds, true); + } else { + id_state = ID_UNCHANGED; + } + } + + LogMachineIdState(id_state); + + local_state->SetInteger(prefs::kMetricsMachineId, hashed_id); +} + +// static +void ClonedInstallDetector::RegisterPrefs(PrefRegistrySimple* registry) { + registry->RegisterIntegerPref(prefs::kMetricsMachineId, 0); +} + +} // namespace metrics diff --git a/components/metrics/cloned_install_detector.h b/components/metrics/cloned_install_detector.h new file mode 100644 index 0000000..065cb49 --- /dev/null +++ b/components/metrics/cloned_install_detector.h @@ -0,0 +1,59 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_METRICS_CLONED_INSTALL_DETECTOR_H_ +#define COMPONENTS_METRICS_CLONED_INSTALL_DETECTOR_H_ + +#include "base/gtest_prod_util.h" +#include "base/memory/ref_counted.h" +#include "base/memory/weak_ptr.h" + +class PrefRegistrySimple; +class PrefService; + +namespace base { +class SingleThreadTaskRunner; +} + +namespace metrics { + +class MachineIdProvider; + +// A class for detecting if an install is cloned. It does this by detecting +// when the hardware running Chrome changes. +class ClonedInstallDetector { + public: + explicit ClonedInstallDetector(MachineIdProvider* raw_id_provider); + virtual ~ClonedInstallDetector(); + + // Posts a task to |task_runner| to generate a machine ID and store it to a + // local state pref. If the newly generated ID is different than the + // previously stored one, then the install is considered cloned. The ID is a + // 24-bit value based off of machine characteristics. This value should never + // be sent over the network. + // TODO(jwd): Implement change detection. + void CheckForClonedInstall( + PrefService* local_state, + scoped_refptr<base::SingleThreadTaskRunner> task_runner); + + static void RegisterPrefs(PrefRegistrySimple* registry); + + private: + FRIEND_TEST_ALL_PREFIXES(ClonedInstallDetectorTest, SaveId); + FRIEND_TEST_ALL_PREFIXES(ClonedInstallDetectorTest, DetectClone); + + // Converts raw_id into a 24-bit hash and stores the hash in |local_state|. + // |raw_id| is not a const ref because it's passed from a cross-thread post + // task. + void SaveMachineId(PrefService* local_state, std::string raw_id); + + scoped_refptr<MachineIdProvider> raw_id_provider_; + base::WeakPtrFactory<ClonedInstallDetector> weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(ClonedInstallDetector); +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_CLONED_INSTALL_DETECTOR_H_ diff --git a/components/metrics/machine_id_provider.h b/components/metrics/machine_id_provider.h new file mode 100644 index 0000000..d7fcc44 --- /dev/null +++ b/components/metrics/machine_id_provider.h @@ -0,0 +1,46 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef COMPONENTS_METRICS_MACHINE_ID_PROVIDER_H_ +#define COMPONENTS_METRICS_MACHINE_ID_PROVIDER_H_ + +#include <string> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" + +namespace metrics { + +// Provides machine characteristics used as a machine id. The implementation is +// platform specific with a default implementation that gives an empty id. The +// class is ref-counted thread safe so it can be used to post to the FILE thread +// and communicate back to the UI thread. +// This raw machine id should not be stored or transmitted over the network. +// TODO(jwd): Simplify implementation to get rid of the need for +// RefCountedThreadSafe (crbug.com/354882). +class MachineIdProvider : public base::RefCountedThreadSafe<MachineIdProvider> { + public: + // Get a string containing machine characteristics, to be used as a machine + // id. The implementation is platform specific, with a default implementation + // returning an empty string. + // The return value should not be stored to disk or transmitted. + std::string GetMachineId(); + + // Returns a pointer to a new MachineIdProvider or NULL if there is no + // provider implemented on a given platform. This is done to avoid posting a + // task to the FILE thread on platforms with no implementation. + static MachineIdProvider* CreateInstance(); + + private: + friend class base::RefCountedThreadSafe<MachineIdProvider>; + + MachineIdProvider(); + virtual ~MachineIdProvider(); + + DISALLOW_COPY_AND_ASSIGN(MachineIdProvider); +}; + +} // namespace metrics + +#endif // COMPONENTS_METRICS_MACHINE_ID_PROVIDER_H_ diff --git a/components/metrics/machine_id_provider_stub.cc b/components/metrics/machine_id_provider_stub.cc new file mode 100644 index 0000000..626f2b7 --- /dev/null +++ b/components/metrics/machine_id_provider_stub.cc @@ -0,0 +1,24 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/machine_id_provider.h" + +namespace metrics { + +MachineIdProvider::MachineIdProvider() { +} + +MachineIdProvider::~MachineIdProvider() { +} + +// static +MachineIdProvider* MachineIdProvider::CreateInstance() { + return NULL; +} + +std::string MachineIdProvider::GetMachineId() { + return std::string(); +} + +} // namespace metrics diff --git a/components/metrics/machine_id_provider_win.cc b/components/metrics/machine_id_provider_win.cc new file mode 100644 index 0000000..777151a --- /dev/null +++ b/components/metrics/machine_id_provider_win.cc @@ -0,0 +1,117 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/machine_id_provider.h" + +#include <windows.h> +#include <winioctl.h> + +#include "base/base_paths.h" +#include "base/files/file_path.h" +#include "base/path_service.h" +#include "base/threading/thread_restrictions.h" +#include "base/win/scoped_handle.h" + +namespace metrics { + +MachineIdProvider::MachineIdProvider() { +} + +MachineIdProvider::~MachineIdProvider() { +} + +// On windows, the machine id is based on the serial number of the drive Chrome +// is running from. +std::string MachineIdProvider::GetMachineId() { + base::ThreadRestrictions::AssertIOAllowed(); + + // Use the program's path to get the drive used for the machine id. This means + // that whenever the underlying drive changes, it's considered a new machine. + // This is fine as we do not support migrating Chrome installs to new drives. + base::FilePath executable_path; + + if (!PathService::Get(base::FILE_EXE, &executable_path)) { + NOTREACHED(); + return std::string(); + } + + std::vector<base::FilePath::StringType> path_components; + executable_path.GetComponents(&path_components); + if (path_components.empty()) { + NOTREACHED(); + return std::string(); + } + base::FilePath::StringType drive_name = L"\\\\.\\" + path_components[0]; + + base::win::ScopedHandle drive_handle( + CreateFile(drive_name.c_str(), + 0, + FILE_SHARE_READ | FILE_SHARE_WRITE, + NULL, + OPEN_EXISTING, + 0, + NULL)); + + STORAGE_PROPERTY_QUERY query = {}; + query.PropertyId = StorageDeviceProperty; + query.QueryType = PropertyStandardQuery; + + // Perform an initial query to get the number of bytes being returned. + DWORD bytes_returned; + STORAGE_DESCRIPTOR_HEADER header = {}; + BOOL status = DeviceIoControl(drive_handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &query, + sizeof(STORAGE_PROPERTY_QUERY), + &header, + sizeof(STORAGE_DESCRIPTOR_HEADER), + &bytes_returned, + NULL); + + if (!status) + return std::string(); + + // Query for the actual serial number. + std::vector<int8> output_buf(header.Size); + status = DeviceIoControl(drive_handle, + IOCTL_STORAGE_QUERY_PROPERTY, + &query, + sizeof(STORAGE_PROPERTY_QUERY), + &output_buf[0], + output_buf.size(), + &bytes_returned, + NULL); + + if (!status) + return std::string(); + + const STORAGE_DEVICE_DESCRIPTOR* device_descriptor = + reinterpret_cast<STORAGE_DEVICE_DESCRIPTOR*>(&output_buf[0]); + + // The serial number is stored in the |output_buf| as a null-terminated + // string starting at the specified offset. + const DWORD offset = device_descriptor->SerialNumberOffset; + if (offset >= output_buf.size()) + return std::string(); + + // Make sure that the null-terminator exists. + const std::vector<int8>::iterator serial_number_begin = + output_buf.begin() + offset; + const std::vector<int8>::iterator null_location = + std::find(serial_number_begin, output_buf.end(), '\0'); + if (null_location == output_buf.end()) + return std::string(); + + const char* serial_number = + reinterpret_cast<const char*>(&output_buf[offset]); + + return std::string(serial_number); +} + +// static +MachineIdProvider* MachineIdProvider::CreateInstance() { + return new MachineIdProvider(); +} + +} // namespace metrics diff --git a/components/metrics/machine_id_provider_win_unittest.cc b/components/metrics/machine_id_provider_win_unittest.cc new file mode 100644 index 0000000..44917d0 --- /dev/null +++ b/components/metrics/machine_id_provider_win_unittest.cc @@ -0,0 +1,23 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "components/metrics/machine_id_provider.h" + +#include "base/memory/ref_counted.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace metrics { + +TEST(MachineIdProviderTest, GetId) { + scoped_refptr<MachineIdProvider> provider( + MachineIdProvider::CreateInstance()); + std::string id1 = provider->GetMachineId(); + + EXPECT_NE(std::string(), id1); + + std::string id2 = provider->GetMachineId(); + EXPECT_EQ(id1, id2); +} + +} // namespace metrics diff --git a/components/metrics/metrics_pref_names.cc b/components/metrics/metrics_pref_names.cc index 92a1890..e0b358a1 100644 --- a/components/metrics/metrics_pref_names.cc +++ b/components/metrics/metrics_pref_names.cc @@ -13,6 +13,10 @@ namespace prefs { const char kMetricsInitialLogs[] = "user_experience_metrics.initial_logs_as_protobufs"; +// A machine ID used to detect when underlying hardware changes. It is only +// stored locally and never transmitted in metrics reports. +const char kMetricsMachineId[] = "user_experience_metrics.machine_id"; + // Array of strings that are each UMA logs that were not sent because the // browser terminated before these accumulated metrics could be sent. These // logs typically include histograms and memory reports, as well as ongoing @@ -20,5 +24,9 @@ const char kMetricsInitialLogs[] = const char kMetricsOngoingLogs[] = "user_experience_metrics.ongoing_logs_as_protobufs"; +// Boolean that indicates a cloned install has been detected and the metrics +// client id and low entropy source should be reset. +const char kMetricsResetIds[] = "user_experience_metrics.reset_metrics_ids"; + } // namespace prefs } // namespace metrics diff --git a/components/metrics/metrics_pref_names.h b/components/metrics/metrics_pref_names.h index 750d58d..4c799c7 100644 --- a/components/metrics/metrics_pref_names.h +++ b/components/metrics/metrics_pref_names.h @@ -11,7 +11,9 @@ namespace prefs { // Alphabetical list of preference names specific to the metrics // component. Keep alphabetized, and document each in the .cc file. extern const char kMetricsInitialLogs[]; +extern const char kMetricsMachineId[]; extern const char kMetricsOngoingLogs[]; +extern const char kMetricsResetIds[]; } // namespace prefs } // namespace metrics |