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