• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
GetRlzStorePath()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.
GetRlzStoreLockPath()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.
GetKeyName(std::string key,AccessPoint access_point)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.
GetKeyName(std::string key,Product product)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 
RlzValueStoreChromeOS(const base::FilePath & store_path)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 
~RlzValueStoreChromeOS()88 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
89   WriteStore();
90 }
91 
HasAccess(AccessType type)92 bool RlzValueStoreChromeOS::HasAccess(AccessType type) {
93   DCHECK(CalledOnValidThread());
94   return type == kReadAccess || !read_only_;
95 }
96 
WritePingTime(Product product,int64 time)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 
ReadPingTime(Product product,int64 * time)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 
ClearPingTime(Product product)111 bool RlzValueStoreChromeOS::ClearPingTime(Product product) {
112   DCHECK(CalledOnValidThread());
113   rlz_store_->Remove(GetKeyName(kPingTimeKey, product), NULL);
114   return true;
115 }
116 
WriteAccessPointRlz(AccessPoint access_point,const char * new_rlz)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 
ReadAccessPointRlz(AccessPoint access_point,char * rlz,size_t rlz_size)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 
ClearAccessPointRlz(AccessPoint access_point)140 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point) {
141   DCHECK(CalledOnValidThread());
142   rlz_store_->Remove(GetKeyName(kAccessPointKey, access_point), NULL);
143   return true;
144 }
145 
AddProductEvent(Product product,const char * event_rlz)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 
ReadProductEvents(Product product,std::vector<std::string> * events)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 
ClearProductEvent(Product product,const char * event_rlz)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 
ClearAllProductEvents(Product product)177 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product) {
178   DCHECK(CalledOnValidThread());
179   rlz_store_->Remove(GetKeyName(kProductEventKey, product), NULL);
180   return true;
181 }
182 
AddStatefulEvent(Product product,const char * event_rlz)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 
IsStatefulEvent(Product product,const char * event_rlz)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 
ClearAllStatefulEvents(Product product)200 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product) {
201   DCHECK(CalledOnValidThread());
202   rlz_store_->Remove(GetKeyName(kStatefulEventKey, product), NULL);
203   return true;
204 }
205 
CollectGarbage()206 void RlzValueStoreChromeOS::CollectGarbage() {
207   DCHECK(CalledOnValidThread());
208   NOTIMPLEMENTED();
209 }
210 
ReadStore()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 
WriteStore()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 
AddValueToList(std::string list_name,base::Value * value)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 
RemoveValueFromList(std::string list_name,const base::Value & value)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 
ScopedRlzValueStoreLock()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 
~ScopedRlzValueStoreLock()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 
GetStore()326 RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
327   return store_.get();
328 }
329 
330 namespace testing {
331 
SetRlzStoreDirectory(const base::FilePath & directory)332 void SetRlzStoreDirectory(const base::FilePath& directory) {
333   g_testing_rlz_store_path_ = directory;
334 }
335 
RlzStoreFilenameStr()336 std::string RlzStoreFilenameStr() {
337   return GetRlzStorePath().value();
338 }
339 
340 }  // namespace testing
341 
342 }  // namespace rlz_lib
343