1 // Copyright 2014 The Chromium Authors. All rights reserved. 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 "chrome/browser/metrics/extensions_metrics_provider.h" 6 7 #include <algorithm> 8 #include <set> 9 #include <vector> 10 11 #include "base/logging.h" 12 #include "base/memory/scoped_ptr.h" 13 #include "base/strings/stringprintf.h" 14 #include "chrome/browser/browser_process.h" 15 #include "chrome/browser/extensions/install_verifier.h" 16 #include "chrome/browser/profiles/profile_manager.h" 17 #include "components/metrics/metrics_log.h" 18 #include "components/metrics/metrics_state_manager.h" 19 #include "components/metrics/proto/system_profile.pb.h" 20 #include "extensions/browser/extension_registry.h" 21 #include "extensions/browser/extension_system.h" 22 #include "extensions/common/extension_set.h" 23 #include "third_party/smhasher/src/City.h" 24 25 namespace { 26 27 // The number of possible hash keys that a client may use. The UMA client_id 28 // value is reduced modulo this value to produce the key used by that 29 // particular client. 30 const size_t kExtensionListClientKeys = 4096; 31 32 // The number of hash buckets into which extension IDs are mapped. This sets 33 // the possible output range of the HashExtension function. 34 const size_t kExtensionListBuckets = 1024; 35 36 // Possible states for extensions. The order of these enum values is important, 37 // and is used when combining the state of multiple extensions and multiple 38 // profiles. Combining two states should always result in the higher state. 39 // Ex: One profile is in state FROM_STORE_VERIFIED, and another is in 40 // FROM_STORE_UNVERIFIED. The state of the two profiles together will be 41 // FROM_STORE_UNVERIFIED. 42 // This enum should be kept in sync with the corresponding enum in 43 // components/metrics/proto/system_profile.proto 44 enum ExtensionState { 45 NO_EXTENSIONS, 46 FROM_STORE_VERIFIED, 47 FROM_STORE_UNVERIFIED, 48 OFF_STORE 49 }; 50 51 metrics::SystemProfileProto::ExtensionsState ExtensionStateAsProto( 52 ExtensionState value) { 53 switch (value) { 54 case NO_EXTENSIONS: 55 return metrics::SystemProfileProto::NO_EXTENSIONS; 56 case FROM_STORE_VERIFIED: 57 return metrics::SystemProfileProto::NO_OFFSTORE_VERIFIED; 58 case FROM_STORE_UNVERIFIED: 59 return metrics::SystemProfileProto::NO_OFFSTORE_UNVERIFIED; 60 case OFF_STORE: 61 return metrics::SystemProfileProto::HAS_OFFSTORE; 62 } 63 NOTREACHED(); 64 return metrics::SystemProfileProto::NO_EXTENSIONS; 65 } 66 67 // Determines if the |extension| is an extension (can use extension APIs) and is 68 // not from the webstore. If local information claims the extension is from the 69 // webstore, we attempt to verify with |verifier| by checking if it has been 70 // explicitly deemed invalid. If |verifier| is inactive or if the extension is 71 // unknown to |verifier|, the local information is trusted. 72 ExtensionState IsOffStoreExtension( 73 const extensions::Extension& extension, 74 const extensions::InstallVerifier& verifier) { 75 if (!extension.is_extension() && !extension.is_legacy_packaged_app()) 76 return NO_EXTENSIONS; 77 78 // Component extensions are considered safe. 79 if (extensions::Manifest::IsComponentLocation(extension.location())) 80 return NO_EXTENSIONS; 81 82 if (verifier.AllowedByEnterprisePolicy(extension.id())) 83 return NO_EXTENSIONS; 84 85 if (!extensions::InstallVerifier::IsFromStore(extension)) 86 return OFF_STORE; 87 88 // Local information about the extension implies it is from the store. We try 89 // to use the install verifier to verify this. 90 if (!verifier.IsKnownId(extension.id())) 91 return FROM_STORE_UNVERIFIED; 92 93 if (verifier.IsInvalid(extension.id())) 94 return OFF_STORE; 95 96 return FROM_STORE_VERIFIED; 97 } 98 99 // Finds the ExtensionState of |extensions|. The return value will be the 100 // highest (as defined by the order of ExtensionState) value of each extension 101 // in |extensions|. 102 ExtensionState CheckForOffStore(const extensions::ExtensionSet& extensions, 103 const extensions::InstallVerifier& verifier) { 104 ExtensionState state = NO_EXTENSIONS; 105 for (extensions::ExtensionSet::const_iterator it = extensions.begin(); 106 it != extensions.end() && state < OFF_STORE; 107 ++it) { 108 // Combine the state of each extension, always favoring the higher state as 109 // defined by the order of ExtensionState. 110 state = std::max(state, IsOffStoreExtension(**it, verifier)); 111 } 112 return state; 113 } 114 115 } // namespace 116 117 ExtensionsMetricsProvider::ExtensionsMetricsProvider( 118 metrics::MetricsStateManager* metrics_state_manager) 119 : metrics_state_manager_(metrics_state_manager), cached_profile_(NULL) { 120 DCHECK(metrics_state_manager_); 121 } 122 123 ExtensionsMetricsProvider::~ExtensionsMetricsProvider() { 124 } 125 126 // static 127 int ExtensionsMetricsProvider::HashExtension(const std::string& extension_id, 128 uint32 client_key) { 129 DCHECK_LE(client_key, kExtensionListClientKeys); 130 std::string message = 131 base::StringPrintf("%u:%s", client_key, extension_id.c_str()); 132 uint64 output = CityHash64(message.data(), message.size()); 133 return output % kExtensionListBuckets; 134 } 135 136 Profile* ExtensionsMetricsProvider::GetMetricsProfile() { 137 ProfileManager* profile_manager = g_browser_process->profile_manager(); 138 if (!profile_manager) 139 return NULL; 140 141 // If there is a cached profile, reuse that. However, check that it is still 142 // valid first. 143 if (cached_profile_ && profile_manager->IsValidProfile(cached_profile_)) 144 return cached_profile_; 145 146 // Find a suitable profile to use, and cache it so that we continue to report 147 // statistics on the same profile. We would simply use 148 // ProfileManager::GetLastUsedProfile(), except that that has the side effect 149 // of creating a profile if it does not yet exist. 150 cached_profile_ = profile_manager->GetProfileByPath( 151 profile_manager->GetLastUsedProfileDir(profile_manager->user_data_dir())); 152 if (cached_profile_) { 153 // Ensure that the returned profile is not an incognito profile. 154 cached_profile_ = cached_profile_->GetOriginalProfile(); 155 } 156 return cached_profile_; 157 } 158 159 scoped_ptr<extensions::ExtensionSet> 160 ExtensionsMetricsProvider::GetInstalledExtensions(Profile* profile) { 161 if (profile) { 162 return extensions::ExtensionRegistry::Get(profile) 163 ->GenerateInstalledExtensionsSet(); 164 } 165 return scoped_ptr<extensions::ExtensionSet>(); 166 } 167 168 uint64 ExtensionsMetricsProvider::GetClientID() { 169 // TODO(blundell): Create a MetricsLog::ClientIDAsInt() API and call it 170 // here as well as in MetricsLog's population of the client_id field of 171 // the uma_proto. 172 return metrics::MetricsLog::Hash(metrics_state_manager_->client_id()); 173 } 174 175 void ExtensionsMetricsProvider::ProvideSystemProfileMetrics( 176 metrics::SystemProfileProto* system_profile) { 177 ProvideOffStoreMetric(system_profile); 178 ProvideOccupiedBucketMetric(system_profile); 179 } 180 181 void ExtensionsMetricsProvider::ProvideOffStoreMetric( 182 metrics::SystemProfileProto* system_profile) { 183 ProfileManager* profile_manager = g_browser_process->profile_manager(); 184 if (!profile_manager) 185 return; 186 187 ExtensionState state = NO_EXTENSIONS; 188 189 // The off-store metric includes information from all loaded profiles at the 190 // time when this metric is generated. 191 std::vector<Profile*> profiles = profile_manager->GetLoadedProfiles(); 192 for (size_t i = 0u; i < profiles.size() && state < OFF_STORE; ++i) { 193 extensions::InstallVerifier* verifier = 194 extensions::ExtensionSystem::Get(profiles[i])->install_verifier(); 195 196 scoped_ptr<extensions::ExtensionSet> extensions( 197 GetInstalledExtensions(profiles[i])); 198 if (!extensions) 199 continue; 200 201 // Combine the state from each profile, always favoring the higher state as 202 // defined by the order of ExtensionState. 203 state = std::max(state, CheckForOffStore(*extensions.get(), *verifier)); 204 } 205 206 system_profile->set_offstore_extensions_state(ExtensionStateAsProto(state)); 207 } 208 209 void ExtensionsMetricsProvider::ProvideOccupiedBucketMetric( 210 metrics::SystemProfileProto* system_profile) { 211 // UMA reports do not support multiple profiles, but extensions are installed 212 // per-profile. We return the extensions installed in the primary profile. 213 // In the future, we might consider reporting data about extensions in all 214 // profiles. 215 Profile* profile = GetMetricsProfile(); 216 217 scoped_ptr<extensions::ExtensionSet> extensions( 218 GetInstalledExtensions(profile)); 219 if (!extensions) 220 return; 221 222 const int client_key = GetClientID() % kExtensionListClientKeys; 223 224 std::set<int> buckets; 225 for (extensions::ExtensionSet::const_iterator it = extensions->begin(); 226 it != extensions->end(); 227 ++it) { 228 buckets.insert(HashExtension((*it)->id(), client_key)); 229 } 230 231 for (std::set<int>::const_iterator it = buckets.begin(); it != buckets.end(); 232 ++it) { 233 system_profile->add_occupied_extension_bucket(*it); 234 } 235 } 236