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.
5 #include "rlz/chromeos/lib/rlz_value_store_chromeos.h"
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"
24 const char kPingTimeKey
[] = "ping_time";
25 const char kAccessPointKey
[] = "access_points";
26 const char kProductEventKey
[] = "product_events";
27 const char kStatefulEventKey
[] = "stateful_events";
29 // Brand name used when there is no supplementary brand name.
30 const char kNoSupplementaryBrand
[] = "_";
32 // RLZ store filename.
33 const base::FilePath::CharType kRLZDataFileName
[] =
34 FILE_PATH_LITERAL("RLZ Data");
36 // RLZ store lock filename
37 const base::FilePath::CharType kRLZLockFileName
[] =
38 FILE_PATH_LITERAL("RLZ Data.lock");
40 // RLZ store path for testing.
41 base::FilePath g_testing_rlz_store_path_
;
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
);
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
);
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();
61 brand
= kNoSupplementaryBrand
;
62 return key
+ "." + GetAccessPointName(access_point
) + "." + brand
;
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();
69 brand
= kNoSupplementaryBrand
;
70 return key
+ "." + GetProductName(product
) + "." + brand
;
75 RlzValueStoreChromeOS::RlzValueStoreChromeOS(const base::FilePath
& store_path
)
76 : rlz_store_(new base::DictionaryValue
),
77 store_path_(store_path
),
82 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
86 bool RlzValueStoreChromeOS::HasAccess(AccessType type
) {
87 DCHECK(CalledOnValidThread());
88 return type
== kReadAccess
|| !read_only_
;
91 bool RlzValueStoreChromeOS::WritePingTime(Product product
, int64 time
) {
92 DCHECK(CalledOnValidThread());
93 rlz_store_
->SetString(GetKeyName(kPingTimeKey
, product
),
94 base::Int64ToString(time
));
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
);
105 bool RlzValueStoreChromeOS::ClearPingTime(Product product
) {
106 DCHECK(CalledOnValidThread());
107 rlz_store_
->Remove(GetKeyName(kPingTimeKey
, product
), NULL
);
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
);
119 bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point
,
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
);
134 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point
) {
135 DCHECK(CalledOnValidThread());
136 rlz_store_
->Remove(GetKeyName(kAccessPointKey
, access_point
), NULL
);
140 bool RlzValueStoreChromeOS::AddProductEvent(Product product
,
141 const char* event_rlz
) {
142 DCHECK(CalledOnValidThread());
143 return AddValueToList(GetKeyName(kProductEventKey
, product
),
144 base::Value::CreateStringValue(event_rlz
));
147 bool RlzValueStoreChromeOS::ReadProductEvents(
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
))
155 for (size_t i
= 0; i
< events_list
->GetSize(); ++i
) {
157 if (events_list
->GetString(i
, &event
))
158 events
->push_back(event
);
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
),
171 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product
) {
172 DCHECK(CalledOnValidThread());
173 rlz_store_
->Remove(GetKeyName(kProductEventKey
, product
), NULL
);
177 bool RlzValueStoreChromeOS::AddStatefulEvent(Product product
,
178 const char* event_rlz
) {
179 DCHECK(CalledOnValidThread());
180 return AddValueToList(GetKeyName(kStatefulEventKey
, product
),
181 base::Value::CreateStringValue(event_rlz
));
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
),
191 events_list
->Find(event_value
) != events_list
->end();
194 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product
) {
195 DCHECK(CalledOnValidThread());
196 rlz_store_
->Remove(GetKeyName(kStatefulEventKey
, product
), NULL
);
200 void RlzValueStoreChromeOS::CollectGarbage() {
201 DCHECK(CalledOnValidThread());
205 void RlzValueStoreChromeOS::ReadStore() {
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
:
215 case JSONFileValueSerializer::JSON_NO_ERROR
:
217 rlz_store_
.reset(static_cast<base::DictionaryValue
*>(value
.release()));
220 LOG(ERROR
) << "Error reading RLZ store: " << error_msg
;
224 void RlzValueStoreChromeOS::WriteStore() {
225 std::string json_data
;
226 JSONStringValueSerializer
serializer(&json_data
);
227 serializer
.set_pretty_print(true);
228 scoped_ptr
<DictionaryValue
> copy(rlz_store_
->DeepCopyWithoutEmptyChildren());
229 if (!serializer
.Serialize(*copy
.get())) {
230 LOG(ERROR
) << "Failed to serialize RLZ data";
234 if (!base::ImportantFileWriter::WriteFileAtomically(store_path_
, json_data
))
235 LOG(ERROR
) << "Error writing RLZ store";
238 bool RlzValueStoreChromeOS::AddValueToList(std::string list_name
,
239 base::Value
* value
) {
240 base::ListValue
* list_value
= NULL
;
241 if (!rlz_store_
->GetList(list_name
, &list_value
)) {
242 list_value
= new base::ListValue
;
243 rlz_store_
->Set(list_name
, list_value
);
245 list_value
->AppendIfNotPresent(value
);
249 bool RlzValueStoreChromeOS::RemoveValueFromList(std::string list_name
,
250 const base::Value
& value
) {
251 base::ListValue
* list_value
= NULL
;
252 if (!rlz_store_
->GetList(list_name
, &list_value
))
255 list_value
->Remove(value
, &index
);
261 // RlzValueStoreChromeOS keeps its data in memory and only writes it to disk
262 // when ScopedRlzValueStoreLock goes out of scope. Hence, if several
263 // ScopedRlzValueStoreLocks are nested, they all need to use the same store
266 RecursiveCrossProcessLock g_recursive_lock
=
267 RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER
;
269 // This counts the nesting depth of |ScopedRlzValueStoreLock|.
270 int g_lock_depth
= 0;
272 // This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|.
273 RlzValueStoreChromeOS
* g_store
= NULL
;
277 ScopedRlzValueStoreLock::ScopedRlzValueStoreLock() {
278 bool got_cross_process_lock
=
279 g_recursive_lock
.TryGetCrossProcessLock(GetRlzStoreLockPath());
280 // At this point, we hold the in-process lock, no matter the value of
281 // |got_cross_process_lock|.
284 if (!got_cross_process_lock
) {
285 // Acquiring cross-process lock failed, so simply return here.
286 // In-process lock will be released in dtor.
291 if (g_lock_depth
> 1) {
292 // Reuse the already existing store object.
294 store_
.reset(g_store
);
298 // This is the topmost lock, create a new store object.
300 g_store
= new RlzValueStoreChromeOS(GetRlzStorePath());
301 store_
.reset(g_store
);
304 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
306 DCHECK(g_lock_depth
>= 0);
308 if (g_lock_depth
> 0) {
309 // Other locks are still using store_, so don't free it yet.
310 ignore_result(store_
.release());
316 g_recursive_lock
.ReleaseLock();
319 RlzValueStore
* ScopedRlzValueStoreLock::GetStore() {
325 void SetRlzStoreDirectory(const base::FilePath
& directory
) {
326 g_testing_rlz_store_path_
= directory
;
329 std::string
RlzStoreFilenameStr() {
330 return GetRlzStorePath().value();
333 } // namespace testing
335 } // namespace rlz_lib