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