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