1 // Copyright (c) 2012 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 "rlz/chromeos/lib/rlz_value_store_chromeos.h" 6 7 #include "base/file_util.h" 8 #include "base/files/important_file_writer.h" 9 #include "base/json/json_file_value_serializer.h" 10 #include "base/json/json_string_value_serializer.h" 11 #include "base/logging.h" 12 #include "base/sequenced_task_runner.h" 13 #include "base/strings/string_number_conversions.h" 14 #include "base/values.h" 15 #include "rlz/lib/lib_values.h" 16 #include "rlz/lib/recursive_cross_process_lock_posix.h" 17 #include "rlz/lib/rlz_lib.h" 18 19 namespace rlz_lib { 20 21 namespace { 22 23 // Product names. 24 const char kProductChrome[] = "chrome"; 25 const char kProductOther[] = "other"; 26 27 // Key names. 28 const char kPingTimeKey[] = "ping_time"; 29 const char kAccessPointKey[] = "access_points"; 30 const char kProductEventKey[] = "product_events"; 31 const char kStatefulEventKey[] = "stateful_events"; 32 33 // Brand name used when there is no supplementary brand name. 34 const char kNoSupplementaryBrand[] = "_"; 35 36 // RLZ store filename. 37 const base::FilePath::CharType kRLZDataFileName[] = 38 FILE_PATH_LITERAL("RLZ Data"); 39 40 // RLZ store lock filename 41 const base::FilePath::CharType kRLZLockFileName[] = 42 FILE_PATH_LITERAL("RLZ Data.lock"); 43 44 // RLZ store path for testing. 45 base::FilePath g_testing_rlz_store_path_; 46 47 // Returns file path of the RLZ storage. 48 base::FilePath GetRlzStorePath() { 49 return g_testing_rlz_store_path_.empty() ? 50 file_util::GetHomeDir().Append(kRLZDataFileName) : 51 g_testing_rlz_store_path_.Append(kRLZDataFileName); 52 } 53 54 // Returns file path of the RLZ storage lock file. 55 base::FilePath GetRlzStoreLockPath() { 56 return g_testing_rlz_store_path_.empty() ? 57 file_util::GetHomeDir().Append(kRLZLockFileName) : 58 g_testing_rlz_store_path_.Append(kRLZLockFileName); 59 } 60 61 // Returns the dictionary key for storing access point-related prefs. 62 std::string GetKeyName(std::string key, AccessPoint access_point) { 63 std::string brand = SupplementaryBranding::GetBrand(); 64 if (brand.empty()) 65 brand = kNoSupplementaryBrand; 66 return key + "." + GetAccessPointName(access_point) + "." + brand; 67 } 68 69 // Returns the dictionary key for storing product-related prefs. 70 std::string GetKeyName(std::string key, Product product) { 71 std::string brand = SupplementaryBranding::GetBrand(); 72 if (brand.empty()) 73 brand = kNoSupplementaryBrand; 74 return key + "." + GetProductName(product) + "." + brand; 75 } 76 77 } // namespace 78 79 RlzValueStoreChromeOS::RlzValueStoreChromeOS(const base::FilePath& store_path) 80 : rlz_store_(new base::DictionaryValue), 81 store_path_(store_path), 82 read_only_(true) { 83 ReadStore(); 84 } 85 86 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() { 87 WriteStore(); 88 } 89 90 bool RlzValueStoreChromeOS::HasAccess(AccessType type) { 91 DCHECK(CalledOnValidThread()); 92 return type == kReadAccess || !read_only_; 93 } 94 95 bool RlzValueStoreChromeOS::WritePingTime(Product product, int64 time) { 96 DCHECK(CalledOnValidThread()); 97 rlz_store_->SetString(GetKeyName(kPingTimeKey, product), 98 base::Int64ToString(time)); 99 return true; 100 } 101 102 bool RlzValueStoreChromeOS::ReadPingTime(Product product, int64* time) { 103 DCHECK(CalledOnValidThread()); 104 std::string ping_time; 105 return rlz_store_->GetString(GetKeyName(kPingTimeKey, product), &ping_time) && 106 base::StringToInt64(ping_time, time); 107 } 108 109 bool RlzValueStoreChromeOS::ClearPingTime(Product product) { 110 DCHECK(CalledOnValidThread()); 111 rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL); 112 return true; 113 } 114 115 bool RlzValueStoreChromeOS::WriteAccessPointRlz(AccessPoint access_point, 116 const char* new_rlz) { 117 DCHECK(CalledOnValidThread()); 118 rlz_store_->SetString( 119 GetKeyName(kAccessPointKey, access_point), new_rlz); 120 return true; 121 } 122 123 bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point, 124 char* rlz, 125 size_t rlz_size) { 126 DCHECK(CalledOnValidThread()); 127 std::string rlz_value; 128 rlz_store_->GetString(GetKeyName(kAccessPointKey, access_point), &rlz_value); 129 if (rlz_value.size() < rlz_size) { 130 strncpy(rlz, rlz_value.c_str(), rlz_size); 131 return true; 132 } 133 if (rlz_size > 0) 134 *rlz = '\0'; 135 return false; 136 } 137 138 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) { 139 DCHECK(CalledOnValidThread()); 140 rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL); 141 return true; 142 } 143 144 bool RlzValueStoreChromeOS::AddProductEvent(Product product, 145 const char* event_rlz) { 146 DCHECK(CalledOnValidThread()); 147 return AddValueToList(GetKeyName(kProductEventKey, product), 148 base::Value::CreateStringValue(event_rlz)); 149 } 150 151 bool RlzValueStoreChromeOS::ReadProductEvents( 152 Product product, 153 std::vector<std::string>* events) { 154 DCHECK(CalledOnValidThread()); 155 base::ListValue* events_list = NULL; ; 156 if (!rlz_store_->GetList(GetKeyName(kProductEventKey, product), &events_list)) 157 return false; 158 events->clear(); 159 for (size_t i = 0; i < events_list->GetSize(); ++i) { 160 std::string event; 161 if (events_list->GetString(i, &event)) 162 events->push_back(event); 163 } 164 return true; 165 } 166 167 bool RlzValueStoreChromeOS::ClearProductEvent(Product product, 168 const char* event_rlz) { 169 DCHECK(CalledOnValidThread()); 170 base::StringValue event_value(event_rlz); 171 return RemoveValueFromList(GetKeyName(kProductEventKey, product), 172 event_value); 173 } 174 175 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) { 176 DCHECK(CalledOnValidThread()); 177 rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL); 178 return true; 179 } 180 181 bool RlzValueStoreChromeOS::AddStatefulEvent(Product product, 182 const char* event_rlz) { 183 DCHECK(CalledOnValidThread()); 184 return AddValueToList(GetKeyName(kStatefulEventKey, product), 185 base::Value::CreateStringValue(event_rlz)); 186 } 187 188 bool RlzValueStoreChromeOS::IsStatefulEvent(Product product, 189 const char* event_rlz) { 190 DCHECK(CalledOnValidThread()); 191 base::StringValue event_value(event_rlz); 192 base::ListValue* events_list = NULL; 193 return rlz_store_->GetList(GetKeyName(kStatefulEventKey, product), 194 &events_list) && 195 events_list->Find(event_value) != events_list->end(); 196 } 197 198 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) { 199 DCHECK(CalledOnValidThread()); 200 rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL); 201 return true; 202 } 203 204 void RlzValueStoreChromeOS::CollectGarbage() { 205 DCHECK(CalledOnValidThread()); 206 NOTIMPLEMENTED(); 207 } 208 209 void RlzValueStoreChromeOS::ReadStore() { 210 int error_code = 0; 211 std::string error_msg; 212 JSONFileValueSerializer serializer(store_path_); 213 scoped_ptr<base::Value> value( 214 serializer.Deserialize(&error_code, &error_msg)); 215 switch (error_code) { 216 case JSONFileValueSerializer::JSON_NO_SUCH_FILE: 217 read_only_ = false; 218 break; 219 case JSONFileValueSerializer::JSON_NO_ERROR: 220 read_only_ = false; 221 rlz_store_.reset(static_cast<base::DictionaryValue*>(value.release())); 222 break; 223 default: 224 LOG(ERROR) << "Error reading RLZ store: " << error_msg; 225 } 226 } 227 228 void RlzValueStoreChromeOS::WriteStore() { 229 std::string json_data; 230 JSONStringValueSerializer serializer(&json_data); 231 serializer.set_pretty_print(true); 232 scoped_ptr<DictionaryValue> copy(rlz_store_->DeepCopyWithoutEmptyChildren()); 233 if (!serializer.Serialize(*copy.get())) { 234 LOG(ERROR) << "Failed to serialize RLZ data"; 235 NOTREACHED(); 236 return; 237 } 238 if (!base::ImportantFileWriter::WriteFileAtomically(store_path_, json_data)) 239 LOG(ERROR) << "Error writing RLZ store"; 240 } 241 242 bool RlzValueStoreChromeOS::AddValueToList(std::string list_name, 243 base::Value* value) { 244 base::ListValue* list_value = NULL; 245 if (!rlz_store_->GetList(list_name, &list_value)) { 246 list_value = new base::ListValue; 247 rlz_store_->Set(list_name, list_value); 248 } 249 list_value->AppendIfNotPresent(value); 250 return true; 251 } 252 253 bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name, 254 const base::Value& value) { 255 base::ListValue* list_value = NULL; 256 if (!rlz_store_->GetList(list_name, &list_value)) 257 return false; 258 size_t index; 259 list_value->Remove(value, &index); 260 return true; 261 } 262 263 namespace { 264 265 // RlzValueStoreChromeOS keeps its data in memory and only writes it to disk 266 // when ScopedRlzValueStoreLock goes out of scope. Hence, if several 267 // ScopedRlzValueStoreLocks are nested, they all need to use the same store 268 // object. 269 270 RecursiveCrossProcessLock g_recursive_lock = 271 RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER; 272 273 // This counts the nesting depth of |ScopedRlzValueStoreLock|. 274 int g_lock_depth = 0; 275 276 // This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|. 277 RlzValueStoreChromeOS* g_store = NULL; 278 279 } // namespace 280 281 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() { 282 bool got_cross_process_lock = 283 g_recursive_lock.TryGetCrossProcessLock(GetRlzStoreLockPath()); 284 // At this point, we hold the in-process lock, no matter the value of 285 // |got_cross_process_lock|. 286 287 ++g_lock_depth; 288 if (!got_cross_process_lock) { 289 // Acquiring cross-process lock failed, so simply return here. 290 // In-process lock will be released in dtor. 291 DCHECK(!g_store); 292 return; 293 } 294 295 if (g_lock_depth > 1) { 296 // Reuse the already existing store object. 297 DCHECK(g_store); 298 store_.reset(g_store); 299 return; 300 } 301 302 // This is the topmost lock, create a new store object. 303 DCHECK(!g_store); 304 g_store = new RlzValueStoreChromeOS(GetRlzStorePath()); 305 store_.reset(g_store); 306 } 307 308 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() { 309 --g_lock_depth; 310 DCHECK(g_lock_depth >= 0); 311 312 if (g_lock_depth > 0) { 313 // Other locks are still using store_, so don't free it yet. 314 ignore_result(store_.release()); 315 return; 316 } 317 318 g_store = NULL; 319 320 g_recursive_lock.ReleaseLock(); 321 } 322 323 RlzValueStore* ScopedRlzValueStoreLock::GetStore() { 324 return store_.get(); 325 } 326 327 namespace testing { 328 329 void SetRlzStoreDirectory(const base::FilePath& directory) { 330 g_testing_rlz_store_path_ = directory; 331 } 332 333 std::string RlzStoreFilenameStr() { 334 return GetRlzStorePath().value(); 335 } 336 337 } // namespace testing 338 339 } // namespace rlz_lib 340