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/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"
26 const char kPingTimeKey
[] = "ping_time";
27 const char kAccessPointKey
[] = "access_points";
28 const char kProductEventKey
[] = "product_events";
29 const char kStatefulEventKey
[] = "stateful_events";
31 // Brand name used when there is no supplementary brand name.
32 const char kNoSupplementaryBrand
[] = "_";
34 // RLZ store filename.
35 const base::FilePath::CharType kRLZDataFileName
[] =
36 FILE_PATH_LITERAL("RLZ Data");
38 // RLZ store lock filename
39 const base::FilePath::CharType kRLZLockFileName
[] =
40 FILE_PATH_LITERAL("RLZ Data.lock");
42 // RLZ store path for testing.
43 base::FilePath g_testing_rlz_store_path_
;
45 // Returns file path of the RLZ storage.
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
);
54 // Returns file path of the RLZ storage lock file.
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
);
63 // Returns the dictionary key for storing access point-related prefs.
64 std::string
GetKeyName(std::string key
, AccessPoint access_point
) {
65 std::string brand
= SupplementaryBranding::GetBrand();
67 brand
= kNoSupplementaryBrand
;
68 return key
+ "." + GetAccessPointName(access_point
) + "." + brand
;
71 // Returns the dictionary key for storing product-related prefs.
72 std::string
GetKeyName(std::string key
, Product product
) {
73 std::string brand
= SupplementaryBranding::GetBrand();
75 brand
= kNoSupplementaryBrand
;
76 return key
+ "." + GetProductName(product
) + "." + brand
;
81 RlzValueStoreChromeOS::RlzValueStoreChromeOS(const base::FilePath
& store_path
)
82 : rlz_store_(new base::DictionaryValue
),
83 store_path_(store_path
),
88 RlzValueStoreChromeOS::~RlzValueStoreChromeOS() {
92 bool RlzValueStoreChromeOS::HasAccess(AccessType type
) {
93 DCHECK(CalledOnValidThread());
94 return type
== kReadAccess
|| !read_only_
;
97 bool RlzValueStoreChromeOS::WritePingTime(Product product
, int64 time
) {
98 DCHECK(CalledOnValidThread());
99 rlz_store_
->SetString(GetKeyName(kPingTimeKey
, product
),
100 base::Int64ToString(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
);
111 bool RlzValueStoreChromeOS::ClearPingTime(Product product
) {
112 DCHECK(CalledOnValidThread());
113 rlz_store_
->Remove(GetKeyName(kPingTimeKey
, product
), NULL
);
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
);
125 bool RlzValueStoreChromeOS::ReadAccessPointRlz(AccessPoint access_point
,
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
);
140 bool RlzValueStoreChromeOS::ClearAccessPointRlz(AccessPoint access_point
) {
141 DCHECK(CalledOnValidThread());
142 rlz_store_
->Remove(GetKeyName(kAccessPointKey
, access_point
), NULL
);
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
));
153 bool RlzValueStoreChromeOS::ReadProductEvents(
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
))
161 for (size_t i
= 0; i
< events_list
->GetSize(); ++i
) {
163 if (events_list
->GetString(i
, &event
))
164 events
->push_back(event
);
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
),
177 bool RlzValueStoreChromeOS::ClearAllProductEvents(Product product
) {
178 DCHECK(CalledOnValidThread());
179 rlz_store_
->Remove(GetKeyName(kProductEventKey
, product
), NULL
);
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
));
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
),
197 events_list
->Find(event_value
) != events_list
->end();
200 bool RlzValueStoreChromeOS::ClearAllStatefulEvents(Product product
) {
201 DCHECK(CalledOnValidThread());
202 rlz_store_
->Remove(GetKeyName(kStatefulEventKey
, product
), NULL
);
206 void RlzValueStoreChromeOS::CollectGarbage() {
207 DCHECK(CalledOnValidThread());
211 void RlzValueStoreChromeOS::ReadStore() {
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
:
221 case JSONFileValueSerializer::JSON_NO_ERROR
:
223 rlz_store_
.reset(static_cast<base::DictionaryValue
*>(value
.release()));
226 LOG(ERROR
) << "Error reading RLZ store: " << error_msg
;
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";
241 if (!base::ImportantFileWriter::WriteFileAtomically(store_path_
, json_data
))
242 LOG(ERROR
) << "Error writing RLZ store";
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
);
252 list_value
->AppendIfNotPresent(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
))
262 list_value
->Remove(value
, &index
);
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
273 RecursiveCrossProcessLock g_recursive_lock
=
274 RECURSIVE_CROSS_PROCESS_LOCK_INITIALIZER
;
276 // This counts the nesting depth of |ScopedRlzValueStoreLock|.
277 int g_lock_depth
= 0;
279 // This is the shared store object. Non-|NULL| only when |g_lock_depth > 0|.
280 RlzValueStoreChromeOS
* g_store
= NULL
;
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|.
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.
298 if (g_lock_depth
> 1) {
299 // Reuse the already existing store object.
301 store_
.reset(g_store
);
305 // This is the topmost lock, create a new store object.
307 g_store
= new RlzValueStoreChromeOS(GetRlzStorePath());
308 store_
.reset(g_store
);
311 ScopedRlzValueStoreLock::~ScopedRlzValueStoreLock() {
313 DCHECK(g_lock_depth
>= 0);
315 if (g_lock_depth
> 0) {
316 // Other locks are still using store_, so don't free it yet.
317 ignore_result(store_
.release());
323 g_recursive_lock
.ReleaseLock();
326 RlzValueStore
* ScopedRlzValueStoreLock::GetStore() {
332 void SetRlzStoreDirectory(const base::FilePath
& directory
) {
333 g_testing_rlz_store_path_
= directory
;
336 std::string
RlzStoreFilenameStr() {
337 return GetRlzStorePath().value();
340 } // namespace testing
342 } // namespace rlz_lib