Home | History | Annotate | Download | only in lib
      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