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 // 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