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/base_paths.h"
      8 #include "base/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