// 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 "chrome/browser/metrics/extensions_metrics_provider.h" #include #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/strings/stringprintf.h" #include "chrome/browser/browser_process.h" #include "chrome/browser/profiles/profile_manager.h" #include "components/metrics/metrics_log.h" #include "components/metrics/metrics_state_manager.h" #include "components/metrics/proto/system_profile.pb.h" #include "extensions/browser/extension_registry.h" #include "extensions/common/extension_set.h" #include "third_party/smhasher/src/City.h" namespace { // The number of possible hash keys that a client may use. The UMA client_id // value is reduced modulo this value to produce the key used by that // particular client. const size_t kExtensionListClientKeys = 4096; // The number of hash buckets into which extension IDs are mapped. This sets // the possible output range of the HashExtension function. const size_t kExtensionListBuckets = 1024; } // namespace ExtensionsMetricsProvider::ExtensionsMetricsProvider( metrics::MetricsStateManager* metrics_state_manager) : metrics_state_manager_(metrics_state_manager), cached_profile_(NULL) { DCHECK(metrics_state_manager_); } ExtensionsMetricsProvider::~ExtensionsMetricsProvider() { } // static int ExtensionsMetricsProvider::HashExtension(const std::string& extension_id, uint32 client_key) { DCHECK_LE(client_key, kExtensionListClientKeys); std::string message = base::StringPrintf("%u:%s", client_key, extension_id.c_str()); uint64 output = CityHash64(message.data(), message.size()); return output % kExtensionListBuckets; } Profile* ExtensionsMetricsProvider::GetMetricsProfile() { ProfileManager* profile_manager = g_browser_process->profile_manager(); if (!profile_manager) return NULL; // If there is a cached profile, reuse that. However, check that it is still // valid first. if (cached_profile_ && profile_manager->IsValidProfile(cached_profile_)) return cached_profile_; // Find a suitable profile to use, and cache it so that we continue to report // statistics on the same profile. We would simply use // ProfileManager::GetLastUsedProfile(), except that that has the side effect // of creating a profile if it does not yet exist. cached_profile_ = profile_manager->GetProfileByPath( profile_manager->GetLastUsedProfileDir(profile_manager->user_data_dir())); if (cached_profile_) { // Ensure that the returned profile is not an incognito profile. cached_profile_ = cached_profile_->GetOriginalProfile(); } return cached_profile_; } scoped_ptr ExtensionsMetricsProvider::GetInstalledExtensions() { #if defined(ENABLE_EXTENSIONS) // UMA reports do not support multiple profiles, but extensions are installed // per-profile. We return the extensions installed in the primary profile. // In the future, we might consider reporting data about extensions in all // profiles. Profile* profile = GetMetricsProfile(); if (profile) { return extensions::ExtensionRegistry::Get(profile) ->GenerateInstalledExtensionsSet(); } #endif // defined(ENABLE_EXTENSIONS) return scoped_ptr(); } uint64 ExtensionsMetricsProvider::GetClientID() { // TODO(blundell): Create a MetricsLog::ClientIDAsInt() API and call it // here as well as in MetricsLog's population of the client_id field of // the uma_proto. return MetricsLog::Hash(metrics_state_manager_->client_id()); } void ExtensionsMetricsProvider::ProvideSystemProfileMetrics( metrics::SystemProfileProto* system_profile) { scoped_ptr extensions(GetInstalledExtensions()); if (!extensions) return; const int client_key = GetClientID() % kExtensionListClientKeys; std::set buckets; for (extensions::ExtensionSet::const_iterator it = extensions->begin(); it != extensions->end(); ++it) { buckets.insert(HashExtension((*it)->id(), client_key)); } for (std::set::const_iterator it = buckets.begin(); it != buckets.end(); ++it) { system_profile->add_occupied_extension_bucket(*it); } }