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