Upstream version 5.34.104.0
[platform/framework/web/crosswalk.git] / src / rlz / chromeos / lib / rlz_value_store_chromeos.cc
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                         new base::StringValue(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                         new base::StringValue(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<base::DictionaryValue> copy(
229       rlz_store_->DeepCopyWithoutEmptyChildren());
230   if (!serializer.Serialize(*copy.get())) {
231     LOG(ERROR) << "Failed to serialize RLZ data";
232     NOTREACHED();
233     return;
234   }
235   if (!base::ImportantFileWriter::WriteFileAtomically(store_path_, json_data))
236     LOG(ERROR) << "Error writing RLZ store";
237 }
238
239 bool RlzValueStoreChromeOS::AddValueToList(std::string list_name,
240                                            base::Value* value) {
241   base::ListValue* list_value = NULL;
242   if (!rlz_store_->GetList(list_name, &list_value)) {
243     list_value = new base::ListValue;
244     rlz_store_->Set(list_name, list_value);
245   }
246   list_value->AppendIfNotPresent(value);
247   return true;
248 }
249
250 bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name,
251                                                 const base::Value& value) {
252   base::ListValue* list_value = NULL;
253   if (!rlz_store_->GetList(list_name, &list_value))
254     return false;
255   size_t index;
256   list_value->Remove(value, &index);
257   return true;
258 }
259
260 namespace {
261
262 // RlzValueStoreChromeOS keeps its data in memory and only writes it to disk
263 // when ScopedRlzValueStoreLock goes out of scope. Hence, if several
264 // ScopedRlzValueStoreLocks are nested, they all need to use the same store
265 // object.
266
267 RecursiveCrossProcessLock g_recursive_lock =
268     RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER;
269
270 // This counts the nesting depth of |ScopedRlzValueStoreLock|.
271 int g_lock_depth = 0;
272
273 // This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|.
274 RlzValueStoreChromeOS* g_store = NULL;
275
276 }  // namespace
277
278 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
279   bool got_cross_process_lock =
280       g_recursive_lock.TryGetCrossProcessLock(GetRlzStoreLockPath());
281   // At this point, we hold the in-process lock, no matter the value of
282   // |got_cross_process_lock|.
283
284   ++g_lock_depth;
285   if (!got_cross_process_lock) {
286     // Acquiring cross-process lock failed, so simply return here.
287     // In-process lock will be released in dtor.
288     DCHECK(!g_store);
289     return;
290   }
291
292   if (g_lock_depth > 1) {
293     // Reuse the already existing store object.
294     DCHECK(g_store);
295     store_.reset(g_store);
296     return;
297   }
298
299   // This is the topmost lock, create a new store object.
300   DCHECK(!g_store);
301   g_store = new RlzValueStoreChromeOS(GetRlzStorePath());
302   store_.reset(g_store);
303 }
304
305 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
306   --g_lock_depth;
307   DCHECK(g_lock_depth >= 0);
308
309   if (g_lock_depth > 0) {
310     // Other locks are still using store_, so don't free it yet.
311     ignore_result(store_.release());
312     return;
313   }
314
315   g_store = NULL;
316
317   g_recursive_lock.ReleaseLock();
318 }
319
320 RlzValueStore* ScopedRlzValueStoreLock::GetStore() {
321   return store_.get();
322 }
323
324 namespace testing {
325
326 void SetRlzStoreDirectory(const base::FilePath& directory) {
327   g_testing_rlz_store_path_ = directory;
328 }
329
330 std::string RlzStoreFilenameStr() {
331   return GetRlzStorePath().value();
332 }
333
334 }  // namespace testing
335
336 }  // namespace rlz_lib