1 // Copyright 2013 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 "components/variations/variations_associated_data.h" 6 7 #include <map> 8 #include <utility> 9 #include <vector> 10 11 #include "base/memory/singleton.h" 12 #include "components/variations/metrics_util.h" 13 14 namespace chrome_variations { 15 16 namespace { 17 18 // The internal singleton accessor for the map, used to keep it thread-safe. 19 class GroupMapAccessor { 20 public: 21 typedef std::map<ActiveGroupId, VariationID, ActiveGroupIdCompare> 22 GroupToIDMap; 23 24 // Retrieve the singleton. 25 static GroupMapAccessor* GetInstance() { 26 return Singleton<GroupMapAccessor>::get(); 27 } 28 29 // Note that this normally only sets the ID for a group the first time, unless 30 // |force| is set to true, in which case it will always override it. 31 void AssociateID(IDCollectionKey key, 32 const ActiveGroupId& group_identifier, 33 const VariationID id, 34 const bool force) { 35 #if !defined(NDEBUG) 36 // Validate that all collections with this |group_identifier| have the same 37 // associated ID. 38 DCHECK_EQ(2, ID_COLLECTION_COUNT); 39 IDCollectionKey other_key = GOOGLE_WEB_PROPERTIES; 40 if (key == GOOGLE_WEB_PROPERTIES) 41 other_key = GOOGLE_UPDATE_SERVICE; 42 VariationID other_id = GetID(other_key, group_identifier); 43 DCHECK(other_id == EMPTY_ID || other_id == id); 44 #endif 45 46 base::AutoLock scoped_lock(lock_); 47 48 GroupToIDMap* group_to_id_map = GetGroupToIDMap(key); 49 if (force || 50 group_to_id_map->find(group_identifier) == group_to_id_map->end()) 51 (*group_to_id_map)[group_identifier] = id; 52 } 53 54 VariationID GetID(IDCollectionKey key, 55 const ActiveGroupId& group_identifier) { 56 base::AutoLock scoped_lock(lock_); 57 GroupToIDMap* group_to_id_map = GetGroupToIDMap(key); 58 GroupToIDMap::const_iterator it = group_to_id_map->find(group_identifier); 59 if (it == group_to_id_map->end()) 60 return EMPTY_ID; 61 return it->second; 62 } 63 64 void ClearAllMapsForTesting() { 65 base::AutoLock scoped_lock(lock_); 66 67 for (int i = 0; i < ID_COLLECTION_COUNT; ++i) { 68 GroupToIDMap* map = GetGroupToIDMap(static_cast<IDCollectionKey>(i)); 69 DCHECK(map); 70 map->clear(); 71 } 72 } 73 74 private: 75 friend struct DefaultSingletonTraits<GroupMapAccessor>; 76 77 // Retrieves the GroupToIDMap for |key|. 78 GroupToIDMap* GetGroupToIDMap(IDCollectionKey key) { 79 return &group_to_id_maps_[key]; 80 } 81 82 GroupMapAccessor() { 83 group_to_id_maps_.resize(ID_COLLECTION_COUNT); 84 } 85 ~GroupMapAccessor() {} 86 87 base::Lock lock_; 88 std::vector<GroupToIDMap> group_to_id_maps_; 89 90 DISALLOW_COPY_AND_ASSIGN(GroupMapAccessor); 91 }; 92 93 // Singleton helper class that keeps track of the parameters of all variations 94 // and ensures access to these is thread-safe. 95 class VariationsParamAssociator { 96 public: 97 typedef std::pair<std::string, std::string> VariationKey; 98 typedef std::map<std::string, std::string> VariationParams; 99 100 // Retrieve the singleton. 101 static VariationsParamAssociator* GetInstance() { 102 return Singleton<VariationsParamAssociator>::get(); 103 } 104 105 bool AssociateVariationParams(const std::string& trial_name, 106 const std::string& group_name, 107 const VariationParams& params) { 108 base::AutoLock scoped_lock(lock_); 109 110 if (IsFieldTrialActive(trial_name)) 111 return false; 112 113 const VariationKey key(trial_name, group_name); 114 if (ContainsKey(variation_params_, key)) 115 return false; 116 117 variation_params_[key] = params; 118 return true; 119 } 120 121 bool GetVariationParams(const std::string& trial_name, 122 VariationParams* params) { 123 base::AutoLock scoped_lock(lock_); 124 125 const std::string group_name = 126 base::FieldTrialList::FindFullName(trial_name); 127 const VariationKey key(trial_name, group_name); 128 if (!ContainsKey(variation_params_, key)) 129 return false; 130 131 *params = variation_params_[key]; 132 return true; 133 } 134 135 void ClearAllParamsForTesting() { 136 base::AutoLock scoped_lock(lock_); 137 variation_params_.clear(); 138 } 139 140 private: 141 friend struct DefaultSingletonTraits<VariationsParamAssociator>; 142 143 VariationsParamAssociator() {} 144 ~VariationsParamAssociator() {} 145 146 // Tests whether a field trial is active (i.e. group() has been called on it). 147 // TODO(asvitkine): Expose this as an API on base::FieldTrial. 148 bool IsFieldTrialActive(const std::string& trial_name) { 149 base::FieldTrial::ActiveGroups active_groups; 150 base::FieldTrialList::GetActiveFieldTrialGroups(&active_groups); 151 for (size_t i = 0; i < active_groups.size(); ++i) { 152 if (active_groups[i].trial_name == trial_name) 153 return true; 154 } 155 return false; 156 } 157 158 base::Lock lock_; 159 std::map<VariationKey, VariationParams> variation_params_; 160 161 DISALLOW_COPY_AND_ASSIGN(VariationsParamAssociator); 162 }; 163 164 } // namespace 165 166 ActiveGroupId MakeActiveGroupId(const std::string& trial_name, 167 const std::string& group_name) { 168 ActiveGroupId id; 169 id.name = metrics::HashName(trial_name); 170 id.group = metrics::HashName(group_name); 171 return id; 172 } 173 174 void AssociateGoogleVariationID(IDCollectionKey key, 175 const std::string& trial_name, 176 const std::string& group_name, 177 VariationID id) { 178 GroupMapAccessor::GetInstance()->AssociateID( 179 key, MakeActiveGroupId(trial_name, group_name), id, false); 180 } 181 182 void AssociateGoogleVariationIDForce(IDCollectionKey key, 183 const std::string& trial_name, 184 const std::string& group_name, 185 VariationID id) { 186 GroupMapAccessor::GetInstance()->AssociateID( 187 key, MakeActiveGroupId(trial_name, group_name), id, true); 188 } 189 190 VariationID GetGoogleVariationID(IDCollectionKey key, 191 const std::string& trial_name, 192 const std::string& group_name) { 193 return GroupMapAccessor::GetInstance()->GetID( 194 key, MakeActiveGroupId(trial_name, group_name)); 195 } 196 197 bool AssociateVariationParams( 198 const std::string& trial_name, 199 const std::string& group_name, 200 const std::map<std::string, std::string>& params) { 201 return VariationsParamAssociator::GetInstance()->AssociateVariationParams( 202 trial_name, group_name, params); 203 } 204 205 bool GetVariationParams(const std::string& trial_name, 206 std::map<std::string, std::string>* params) { 207 return VariationsParamAssociator::GetInstance()->GetVariationParams( 208 trial_name, params); 209 } 210 211 std::string GetVariationParamValue(const std::string& trial_name, 212 const std::string& param_name) { 213 std::map<std::string, std::string> params; 214 if (GetVariationParams(trial_name, ¶ms)) { 215 std::map<std::string, std::string>::iterator it = params.find(param_name); 216 if (it != params.end()) 217 return it->second; 218 } 219 return std::string(); 220 } 221 222 // Functions below are exposed for testing explicitly behind this namespace. 223 // They simply wrap existing functions in this file. 224 namespace testing { 225 226 void ClearAllVariationIDs() { 227 GroupMapAccessor::GetInstance()->ClearAllMapsForTesting(); 228 } 229 230 void ClearAllVariationParams() { 231 VariationsParamAssociator::GetInstance()->ClearAllParamsForTesting(); 232 } 233 234 } // namespace testing 235 236 } // namespace chrome_variations 237