1 // Copyright 2014 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 "google_apis/gcm/engine/gcm_store_impl.h"
7 #include "base/basictypes.h"
9 #include "base/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/metrics/histogram_macros.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/time/time.h"
21 #include "base/tracked_objects.h"
22 #include "google_apis/gcm/base/encryptor.h"
23 #include "google_apis/gcm/base/mcs_message.h"
24 #include "google_apis/gcm/base/mcs_util.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "third_party/leveldatabase/env_chromium.h"
27 #include "third_party/leveldatabase/src/include/leveldb/db.h"
28 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
34 // This enum is used in an UMA histogram (GCMLoadStatus enum defined in
35 // tools/metrics/histograms/histogram.xml). Hence the entries here shouldn't
36 // be deleted or re-ordered and new ones should be added to the end.
41 LOADING_DEVICE_CREDENTIALS_FAILED
,
42 LOADING_REGISTRATION_FAILED
,
43 LOADING_INCOMING_MESSAGES_FAILED
,
44 LOADING_OUTGOING_MESSAGES_FAILED
,
45 LOADING_LAST_CHECKIN_INFO_FAILED
,
46 LOADING_GSERVICE_SETTINGS_FAILED
,
47 LOADING_ACCOUNT_MAPPING_FAILED
,
48 LOADING_LAST_TOKEN_TIME_FAILED
,
50 // NOTE: always keep this entry at the end. Add new status types only
51 // immediately above this line. Make sure to update the corresponding
52 // histogram enum accordingly.
56 // Limit to the number of outstanding messages per app.
57 const int kMessagesPerAppLimit
= 20;
59 // ---- LevelDB keys. ----
60 // Key for this device's android id.
61 const char kDeviceAIDKey
[] = "device_aid_key";
62 // Key for this device's android security token.
63 const char kDeviceTokenKey
[] = "device_token_key";
64 // Lowest lexicographically ordered app ids.
65 // Used for prefixing app id.
66 const char kRegistrationKeyStart
[] = "reg1-";
67 // Key guaranteed to be higher than all app ids.
68 // Used for limiting iteration.
69 const char kRegistrationKeyEnd
[] = "reg2-";
70 // Lowest lexicographically ordered incoming message key.
71 // Used for prefixing messages.
72 const char kIncomingMsgKeyStart
[] = "incoming1-";
73 // Key guaranteed to be higher than all incoming message keys.
74 // Used for limiting iteration.
75 const char kIncomingMsgKeyEnd
[] = "incoming2-";
76 // Lowest lexicographically ordered outgoing message key.
77 // Used for prefixing outgoing messages.
78 const char kOutgoingMsgKeyStart
[] = "outgoing1-";
79 // Key guaranteed to be higher than all outgoing message keys.
80 // Used for limiting iteration.
81 const char kOutgoingMsgKeyEnd
[] = "outgoing2-";
82 // Lowest lexicographically ordered G-service settings key.
83 // Used for prefixing G-services settings.
84 const char kGServiceSettingKeyStart
[] = "gservice1-";
85 // Key guaranteed to be higher than all G-services settings keys.
86 // Used for limiting iteration.
87 const char kGServiceSettingKeyEnd
[] = "gservice2-";
88 // Key for digest of the last G-services settings update.
89 const char kGServiceSettingsDigestKey
[] = "gservices_digest";
90 // Key used to indicate how many accounts were last checked in with this device.
91 const char kLastCheckinAccountsKey
[] = "last_checkin_accounts_count";
92 // Key used to timestamp last checkin (marked with G services settings update).
93 const char kLastCheckinTimeKey
[] = "last_checkin_time";
94 // Lowest lexicographically ordered account key.
95 // Used for prefixing account information.
96 const char kAccountKeyStart
[] = "account1-";
97 // Key guaranteed to be higher than all account keys.
98 // Used for limiting iteration.
99 const char kAccountKeyEnd
[] = "account2-";
100 // Key used for last token fetch time.
101 const char kLastTokenFetchTimeKey
[] = "last_token_fetch_time";
103 std::string
MakeRegistrationKey(const std::string
& app_id
) {
104 return kRegistrationKeyStart
+ app_id
;
107 std::string
ParseRegistrationKey(const std::string
& key
) {
108 return key
.substr(arraysize(kRegistrationKeyStart
) - 1);
111 std::string
MakeIncomingKey(const std::string
& persistent_id
) {
112 return kIncomingMsgKeyStart
+ persistent_id
;
115 std::string
MakeOutgoingKey(const std::string
& persistent_id
) {
116 return kOutgoingMsgKeyStart
+ persistent_id
;
119 std::string
ParseOutgoingKey(const std::string
& key
) {
120 return key
.substr(arraysize(kOutgoingMsgKeyStart
) - 1);
123 std::string
MakeGServiceSettingKey(const std::string
& setting_name
) {
124 return kGServiceSettingKeyStart
+ setting_name
;
127 std::string
ParseGServiceSettingKey(const std::string
& key
) {
128 return key
.substr(arraysize(kGServiceSettingKeyStart
) - 1);
131 std::string
MakeAccountKey(const std::string
& account_id
) {
132 return kAccountKeyStart
+ account_id
;
135 std::string
ParseAccountKey(const std::string
& key
) {
136 return key
.substr(arraysize(kAccountKeyStart
) - 1);
139 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
140 // outlive the slice.
141 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
142 leveldb::Slice
MakeSlice(const base::StringPiece
& s
) {
143 return leveldb::Slice(s
.begin(), s
.size());
148 class GCMStoreImpl::Backend
149 : public base::RefCountedThreadSafe
<GCMStoreImpl::Backend
> {
151 Backend(const base::FilePath
& path
,
152 scoped_refptr
<base::SequencedTaskRunner
> foreground_runner
,
153 scoped_ptr
<Encryptor
> encryptor
);
155 // Blocking implementations of GCMStoreImpl methods.
156 void Load(const LoadCallback
& callback
);
158 void Destroy(const UpdateCallback
& callback
);
159 void SetDeviceCredentials(uint64 device_android_id
,
160 uint64 device_security_token
,
161 const UpdateCallback
& callback
);
162 void AddRegistration(const std::string
& app_id
,
163 const std::string
& serialized_registration
,
164 const UpdateCallback
& callback
);
165 void RemoveRegistration(const std::string
& app_id
,
166 const UpdateCallback
& callback
);
167 void AddIncomingMessage(const std::string
& persistent_id
,
168 const UpdateCallback
& callback
);
169 void RemoveIncomingMessages(const PersistentIdList
& persistent_ids
,
170 const UpdateCallback
& callback
);
171 void AddOutgoingMessage(const std::string
& persistent_id
,
172 const MCSMessage
& message
,
173 const UpdateCallback
& callback
);
174 void RemoveOutgoingMessages(
175 const PersistentIdList
& persistent_ids
,
176 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
178 void AddUserSerialNumber(const std::string
& username
,
180 const UpdateCallback
& callback
);
181 void RemoveUserSerialNumber(const std::string
& username
,
182 const UpdateCallback
& callback
);
183 void SetLastCheckinInfo(const base::Time
& time
,
184 const std::set
<std::string
>& accounts
,
185 const UpdateCallback
& callback
);
186 void SetGServicesSettings(
187 const std::map
<std::string
, std::string
>& settings
,
188 const std::string
& digest
,
189 const UpdateCallback
& callback
);
190 void AddAccountMapping(const AccountMapping
& account_mapping
,
191 const UpdateCallback
& callback
);
192 void RemoveAccountMapping(const std::string
& account_id
,
193 const UpdateCallback
& callback
);
194 void SetLastTokenFetchTime(const base::Time
& time
,
195 const UpdateCallback
& callback
);
196 void SetValue(const std::string
& key
,
197 const std::string
& value
,
198 const UpdateCallback
& callback
);
201 friend class base::RefCountedThreadSafe
<Backend
>;
204 LoadStatus
OpenStoreAndLoadData(LoadResult
* result
);
205 bool LoadDeviceCredentials(uint64
* android_id
, uint64
* security_token
);
206 bool LoadRegistrations(RegistrationInfoMap
* registrations
);
207 bool LoadIncomingMessages(std::vector
<std::string
>* incoming_messages
);
208 bool LoadOutgoingMessages(OutgoingMessageMap
* outgoing_messages
);
209 bool LoadLastCheckinInfo(base::Time
* last_checkin_time
,
210 std::set
<std::string
>* accounts
);
211 bool LoadGServicesSettings(std::map
<std::string
, std::string
>* settings
,
212 std::string
* digest
);
213 bool LoadAccountMappingInfo(AccountMappings
* account_mappings
);
214 bool LoadLastTokenFetchTime(base::Time
* last_token_fetch_time
);
216 const base::FilePath path_
;
217 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner_
;
218 scoped_ptr
<Encryptor
> encryptor_
;
220 scoped_ptr
<leveldb::DB
> db_
;
223 GCMStoreImpl::Backend::Backend(
224 const base::FilePath
& path
,
225 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner
,
226 scoped_ptr
<Encryptor
> encryptor
)
228 foreground_task_runner_(foreground_task_runner
),
229 encryptor_(encryptor
.Pass()) {
232 GCMStoreImpl::Backend::~Backend() {}
234 LoadStatus
GCMStoreImpl::Backend::OpenStoreAndLoadData(LoadResult
* result
) {
235 LoadStatus load_status
;
237 LOG(ERROR
) << "Attempting to reload open database.";
238 return RELOADING_OPEN_STORE
;
241 leveldb::Options options
;
242 options
.create_if_missing
= true;
243 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
245 leveldb::Status status
=
246 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
);
248 LOG(ERROR
) << "Failed to open database " << path_
.value() << ": "
249 << status
.ToString();
250 return OPENING_STORE_FAILED
;
254 if (!LoadDeviceCredentials(&result
->device_android_id
,
255 &result
->device_security_token
)) {
256 return LOADING_DEVICE_CREDENTIALS_FAILED
;
258 if (!LoadRegistrations(&result
->registrations
))
259 return LOADING_REGISTRATION_FAILED
;
260 if (!LoadIncomingMessages(&result
->incoming_messages
))
261 return LOADING_INCOMING_MESSAGES_FAILED
;
262 if (!LoadOutgoingMessages(&result
->outgoing_messages
))
263 return LOADING_OUTGOING_MESSAGES_FAILED
;
264 if (!LoadLastCheckinInfo(&result
->last_checkin_time
,
265 &result
->last_checkin_accounts
)) {
266 return LOADING_LAST_CHECKIN_INFO_FAILED
;
268 if (!LoadGServicesSettings(&result
->gservices_settings
,
269 &result
->gservices_digest
)) {
270 return load_status
= LOADING_GSERVICE_SETTINGS_FAILED
;
272 if (!LoadAccountMappingInfo(&result
->account_mappings
))
273 return LOADING_ACCOUNT_MAPPING_FAILED
;
274 if (!LoadLastTokenFetchTime(&result
->last_token_fetch_time
))
275 return LOADING_LAST_TOKEN_TIME_FAILED
;
277 return LOADING_SUCCEEDED
;
280 void GCMStoreImpl::Backend::Load(const LoadCallback
& callback
) {
281 scoped_ptr
<LoadResult
> result(new LoadResult());
282 LoadStatus load_status
= OpenStoreAndLoadData(result
.get());
283 UMA_HISTOGRAM_ENUMERATION("GCM.LoadStatus", load_status
, LOAD_STATUS_COUNT
);
284 if (load_status
!= LOADING_SUCCEEDED
) {
286 foreground_task_runner_
->PostTask(FROM_HERE
,
288 base::Passed(&result
)));
292 // Only record histograms if GCM had already been set up for this device.
293 if (result
->device_android_id
!= 0 && result
->device_security_token
!= 0) {
295 if (base::GetFileSize(path_
, &file_size
)) {
296 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
297 static_cast<int>(file_size
/ 1024));
299 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
300 result
->registrations
.size());
301 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
302 result
->outgoing_messages
.size());
303 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
304 result
->incoming_messages
.size());
307 DVLOG(1) << "Succeeded in loading " << result
->registrations
.size()
308 << " registrations, "
309 << result
->incoming_messages
.size()
310 << " unacknowledged incoming messages and "
311 << result
->outgoing_messages
.size()
312 << " unacknowledged outgoing messages.";
313 result
->success
= true;
314 foreground_task_runner_
->PostTask(FROM_HERE
,
316 base::Passed(&result
)));
320 void GCMStoreImpl::Backend::Close() {
321 DVLOG(1) << "Closing GCM store.";
325 void GCMStoreImpl::Backend::Destroy(const UpdateCallback
& callback
) {
326 DVLOG(1) << "Destroying GCM store.";
328 const leveldb::Status s
=
329 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), leveldb::Options());
331 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
334 LOG(ERROR
) << "Destroy failed: " << s
.ToString();
335 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
338 void GCMStoreImpl::Backend::SetDeviceCredentials(
339 uint64 device_android_id
,
340 uint64 device_security_token
,
341 const UpdateCallback
& callback
) {
342 DVLOG(1) << "Saving device credentials with AID " << device_android_id
;
344 LOG(ERROR
) << "GCMStore db doesn't exist.";
345 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
349 leveldb::WriteOptions write_options
;
350 write_options
.sync
= true;
352 std::string encrypted_token
;
353 encryptor_
->EncryptString(base::Uint64ToString(device_security_token
),
355 std::string android_id_str
= base::Uint64ToString(device_android_id
);
357 db_
->Put(write_options
,
358 MakeSlice(kDeviceAIDKey
),
359 MakeSlice(android_id_str
));
362 write_options
, MakeSlice(kDeviceTokenKey
), MakeSlice(encrypted_token
));
365 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
368 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
369 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
372 void GCMStoreImpl::Backend::AddRegistration(
373 const std::string
& app_id
,
374 const std::string
& serialized_registration
,
375 const UpdateCallback
& callback
) {
376 DVLOG(1) << "Saving registration info for app: " << app_id
;
378 LOG(ERROR
) << "GCMStore db doesn't exist.";
379 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
382 leveldb::WriteOptions write_options
;
383 write_options
.sync
= true;
385 std::string key
= MakeRegistrationKey(app_id
);
386 const leveldb::Status status
= db_
->Put(write_options
,
388 MakeSlice(serialized_registration
));
390 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
393 LOG(ERROR
) << "LevelDB put failed: " << status
.ToString();
394 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
397 void GCMStoreImpl::Backend::RemoveRegistration(const std::string
& app_id
,
398 const UpdateCallback
& callback
) {
400 LOG(ERROR
) << "GCMStore db doesn't exist.";
401 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
404 leveldb::WriteOptions write_options
;
405 write_options
.sync
= true;
407 leveldb::Status status
=
408 db_
->Delete(write_options
, MakeSlice(MakeRegistrationKey(app_id
)));
410 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
413 LOG(ERROR
) << "LevelDB remove failed: " << status
.ToString();
414 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
417 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string
& persistent_id
,
418 const UpdateCallback
& callback
) {
419 DVLOG(1) << "Saving incoming message with id " << persistent_id
;
421 LOG(ERROR
) << "GCMStore db doesn't exist.";
422 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
426 leveldb::WriteOptions write_options
;
427 write_options
.sync
= true;
429 std::string key
= MakeIncomingKey(persistent_id
);
430 const leveldb::Status s
= db_
->Put(write_options
,
432 MakeSlice(persistent_id
));
434 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
437 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
438 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
441 void GCMStoreImpl::Backend::RemoveIncomingMessages(
442 const PersistentIdList
& persistent_ids
,
443 const UpdateCallback
& callback
) {
445 LOG(ERROR
) << "GCMStore db doesn't exist.";
446 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
449 leveldb::WriteOptions write_options
;
450 write_options
.sync
= true;
453 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
454 iter
!= persistent_ids
.end();
456 DVLOG(1) << "Removing incoming message with id " << *iter
;
457 std::string key
= MakeIncomingKey(*iter
);
458 s
= db_
->Delete(write_options
, MakeSlice(key
));
463 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
466 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
467 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
470 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string
& persistent_id
,
471 const MCSMessage
& message
,
472 const UpdateCallback
& callback
) {
473 DVLOG(1) << "Saving outgoing message with id " << persistent_id
;
475 LOG(ERROR
) << "GCMStore db doesn't exist.";
476 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
479 leveldb::WriteOptions write_options
;
480 write_options
.sync
= true;
483 static_cast<char>(message
.tag()) + message
.SerializeAsString();
484 std::string key
= MakeOutgoingKey(persistent_id
);
485 const leveldb::Status s
= db_
->Put(write_options
,
489 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
492 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
493 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
496 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
497 const PersistentIdList
& persistent_ids
,
498 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
501 LOG(ERROR
) << "GCMStore db doesn't exist.";
502 foreground_task_runner_
->PostTask(FROM_HERE
,
505 AppIdToMessageCountMap()));
508 leveldb::ReadOptions read_options
;
509 leveldb::WriteOptions write_options
;
510 write_options
.sync
= true;
512 AppIdToMessageCountMap removed_message_counts
;
515 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
516 iter
!= persistent_ids
.end();
518 DVLOG(1) << "Removing outgoing message with id " << *iter
;
519 std::string outgoing_message
;
520 std::string key
= MakeOutgoingKey(*iter
);
521 s
= db_
->Get(read_options
,
526 mcs_proto::DataMessageStanza data_message
;
527 // Skip the initial tag byte and parse the rest to extract the message.
528 if (data_message
.ParseFromString(outgoing_message
.substr(1))) {
529 DCHECK(!data_message
.category().empty());
530 if (removed_message_counts
.count(data_message
.category()) != 0)
531 removed_message_counts
[data_message
.category()]++;
533 removed_message_counts
[data_message
.category()] = 1;
535 DVLOG(1) << "Removing outgoing message with id " << *iter
;
536 s
= db_
->Delete(write_options
, MakeSlice(key
));
541 foreground_task_runner_
->PostTask(FROM_HERE
,
544 removed_message_counts
));
547 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
548 foreground_task_runner_
->PostTask(FROM_HERE
,
551 AppIdToMessageCountMap()));
554 void GCMStoreImpl::Backend::SetLastCheckinInfo(
555 const base::Time
& time
,
556 const std::set
<std::string
>& accounts
,
557 const UpdateCallback
& callback
) {
558 leveldb::WriteBatch write_batch
;
560 int64 last_checkin_time_internal
= time
.ToInternalValue();
561 write_batch
.Put(MakeSlice(kLastCheckinTimeKey
),
562 MakeSlice(base::Int64ToString(last_checkin_time_internal
)));
564 std::string serialized_accounts
;
565 for (std::set
<std::string
>::iterator iter
= accounts
.begin();
566 iter
!= accounts
.end();
568 serialized_accounts
+= *iter
;
569 serialized_accounts
+= ",";
571 if (!serialized_accounts
.empty())
572 serialized_accounts
.erase(serialized_accounts
.length() - 1);
574 write_batch
.Put(MakeSlice(kLastCheckinAccountsKey
),
575 MakeSlice(serialized_accounts
));
577 leveldb::WriteOptions write_options
;
578 write_options
.sync
= true;
579 const leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
582 LOG(ERROR
) << "LevelDB set last checkin info failed: " << s
.ToString();
583 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
586 void GCMStoreImpl::Backend::SetGServicesSettings(
587 const std::map
<std::string
, std::string
>& settings
,
588 const std::string
& settings_digest
,
589 const UpdateCallback
& callback
) {
590 leveldb::WriteBatch write_batch
;
592 // Remove all existing settings.
593 leveldb::ReadOptions read_options
;
594 read_options
.verify_checksums
= true;
595 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
596 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
597 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
599 write_batch
.Delete(iter
->key());
602 // Add the new settings.
603 for (std::map
<std::string
, std::string
>::const_iterator iter
=
605 iter
!= settings
.end(); ++iter
) {
606 write_batch
.Put(MakeSlice(MakeGServiceSettingKey(iter
->first
)),
607 MakeSlice(iter
->second
));
610 // Update the settings digest.
611 write_batch
.Put(MakeSlice(kGServiceSettingsDigestKey
),
612 MakeSlice(settings_digest
));
614 // Write it all in a batch.
615 leveldb::WriteOptions write_options
;
616 write_options
.sync
= true;
618 leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
620 LOG(ERROR
) << "LevelDB GService Settings update failed: " << s
.ToString();
621 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
624 void GCMStoreImpl::Backend::AddAccountMapping(
625 const AccountMapping
& account_mapping
,
626 const UpdateCallback
& callback
) {
627 DVLOG(1) << "Saving account info for account with email: "
628 << account_mapping
.email
;
630 LOG(ERROR
) << "GCMStore db doesn't exist.";
631 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
635 leveldb::WriteOptions write_options
;
636 write_options
.sync
= true;
638 std::string data
= account_mapping
.SerializeAsString();
639 std::string key
= MakeAccountKey(account_mapping
.account_id
);
640 const leveldb::Status s
=
641 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(data
));
643 LOG(ERROR
) << "LevelDB adding account mapping failed: " << s
.ToString();
644 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
647 void GCMStoreImpl::Backend::RemoveAccountMapping(
648 const std::string
& account_id
,
649 const UpdateCallback
& callback
) {
651 LOG(ERROR
) << "GCMStore db doesn't exist.";
652 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
656 leveldb::WriteOptions write_options
;
657 write_options
.sync
= true;
660 db_
->Delete(write_options
, MakeSlice(MakeAccountKey(account_id
)));
663 LOG(ERROR
) << "LevelDB removal of account mapping failed: " << s
.ToString();
664 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
667 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
668 const base::Time
& time
,
669 const UpdateCallback
& callback
) {
670 DVLOG(1) << "Setting last token fetching time.";
672 LOG(ERROR
) << "GCMStore db doesn't exist.";
673 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
677 leveldb::WriteOptions write_options
;
678 write_options
.sync
= true;
680 const leveldb::Status s
=
681 db_
->Put(write_options
,
682 MakeSlice(kLastTokenFetchTimeKey
),
683 MakeSlice(base::Int64ToString(time
.ToInternalValue())));
686 LOG(ERROR
) << "LevelDB setting last token fetching time: " << s
.ToString();
687 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
690 void GCMStoreImpl::Backend::SetValue(const std::string
& key
,
691 const std::string
& value
,
692 const UpdateCallback
& callback
) {
693 DVLOG(1) << "Injecting a value to GCM Store for testing. Key: "
694 << key
<< ", Value: " << value
;
696 LOG(ERROR
) << "GCMStore db doesn't exist.";
697 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
701 leveldb::WriteOptions write_options
;
702 write_options
.sync
= true;
704 const leveldb::Status s
=
705 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(value
));
708 LOG(ERROR
) << "LevelDB had problems injecting a value: " << s
.ToString();
709 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
712 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64
* android_id
,
713 uint64
* security_token
) {
714 leveldb::ReadOptions read_options
;
715 read_options
.verify_checksums
= true;
718 leveldb::Status s
= db_
->Get(read_options
, MakeSlice(kDeviceAIDKey
), &result
);
720 if (!base::StringToUint64(result
, android_id
)) {
721 LOG(ERROR
) << "Failed to restore device id.";
725 s
= db_
->Get(read_options
, MakeSlice(kDeviceTokenKey
), &result
);
728 std::string decrypted_token
;
729 encryptor_
->DecryptString(result
, &decrypted_token
);
730 if (!base::StringToUint64(decrypted_token
, security_token
)) {
731 LOG(ERROR
) << "Failed to restore security token.";
737 if (s
.IsNotFound()) {
738 DVLOG(1) << "No credentials found.";
742 LOG(ERROR
) << "Error reading credentials from store.";
746 bool GCMStoreImpl::Backend::LoadRegistrations(
747 RegistrationInfoMap
* registrations
) {
748 leveldb::ReadOptions read_options
;
749 read_options
.verify_checksums
= true;
751 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
752 for (iter
->Seek(MakeSlice(kRegistrationKeyStart
));
753 iter
->Valid() && iter
->key().ToString() < kRegistrationKeyEnd
;
755 leveldb::Slice s
= iter
->value();
757 LOG(ERROR
) << "Error reading registration with key " << s
.ToString();
760 std::string app_id
= ParseRegistrationKey(iter
->key().ToString());
761 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
762 if (!registration
->ParseFromString(iter
->value().ToString())) {
763 LOG(ERROR
) << "Failed to parse registration with app id " << app_id
;
766 DVLOG(1) << "Found registration with app id " << app_id
;
767 (*registrations
)[app_id
] = registration
;
773 bool GCMStoreImpl::Backend::LoadIncomingMessages(
774 std::vector
<std::string
>* incoming_messages
) {
775 leveldb::ReadOptions read_options
;
776 read_options
.verify_checksums
= true;
778 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
779 for (iter
->Seek(MakeSlice(kIncomingMsgKeyStart
));
780 iter
->Valid() && iter
->key().ToString() < kIncomingMsgKeyEnd
;
782 leveldb::Slice s
= iter
->value();
784 LOG(ERROR
) << "Error reading incoming message with key "
785 << iter
->key().ToString();
788 DVLOG(1) << "Found incoming message with id " << s
.ToString();
789 incoming_messages
->push_back(s
.ToString());
795 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
796 OutgoingMessageMap
* outgoing_messages
) {
797 leveldb::ReadOptions read_options
;
798 read_options
.verify_checksums
= true;
800 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
801 for (iter
->Seek(MakeSlice(kOutgoingMsgKeyStart
));
802 iter
->Valid() && iter
->key().ToString() < kOutgoingMsgKeyEnd
;
804 leveldb::Slice s
= iter
->value();
806 LOG(ERROR
) << "Error reading incoming message with key " << s
.ToString();
809 uint8 tag
= iter
->value().data()[0];
810 std::string id
= ParseOutgoingKey(iter
->key().ToString());
811 scoped_ptr
<google::protobuf::MessageLite
> message(
812 BuildProtobufFromTag(tag
));
813 if (!message
.get() ||
814 !message
->ParseFromString(iter
->value().ToString().substr(1))) {
815 LOG(ERROR
) << "Failed to parse outgoing message with id " << id
816 << " and tag " << tag
;
819 DVLOG(1) << "Found outgoing message with id " << id
<< " of type "
820 << base::IntToString(tag
);
821 (*outgoing_messages
)[id
] = make_linked_ptr(message
.release());
827 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
828 base::Time
* last_checkin_time
,
829 std::set
<std::string
>* accounts
) {
830 leveldb::ReadOptions read_options
;
831 read_options
.verify_checksums
= true;
834 leveldb::Status s
= db_
->Get(read_options
,
835 MakeSlice(kLastCheckinTimeKey
),
837 int64 time_internal
= 0LL;
838 if (s
.ok() && !base::StringToInt64(result
, &time_internal
)) {
839 LOG(ERROR
) << "Failed to restore last checkin time. Using default = 0.";
843 // In case we cannot read last checkin time, we default it to 0, as we don't
844 // want that situation to cause the whole load to fail.
845 *last_checkin_time
= base::Time::FromInternalValue(time_internal
);
848 s
= db_
->Get(read_options
, MakeSlice(kLastCheckinAccountsKey
), &result
);
850 DVLOG(1) << "No accounts where stored during last run.";
852 base::StringTokenizer
t(result
, ",");
854 accounts
->insert(t
.token());
859 bool GCMStoreImpl::Backend::LoadGServicesSettings(
860 std::map
<std::string
, std::string
>* settings
,
861 std::string
* digest
) {
862 leveldb::ReadOptions read_options
;
863 read_options
.verify_checksums
= true;
865 // Load all of the GServices settings.
866 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
867 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
868 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
870 std::string value
= iter
->value().ToString();
872 LOG(ERROR
) << "Error reading GService Settings " << value
;
875 std::string id
= ParseGServiceSettingKey(iter
->key().ToString());
876 (*settings
)[id
] = value
;
877 DVLOG(1) << "Found G Service setting with key: " << id
878 << ", and value: " << value
;
881 // Load the settings digest. It's ok if it is empty.
882 db_
->Get(read_options
, MakeSlice(kGServiceSettingsDigestKey
), digest
);
887 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
888 AccountMappings
* account_mappings
) {
889 leveldb::ReadOptions read_options
;
890 read_options
.verify_checksums
= true;
892 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
893 for (iter
->Seek(MakeSlice(kAccountKeyStart
));
894 iter
->Valid() && iter
->key().ToString() < kAccountKeyEnd
;
896 AccountMapping account_mapping
;
897 account_mapping
.account_id
= ParseAccountKey(iter
->key().ToString());
898 if (!account_mapping
.ParseFromString(iter
->value().ToString())) {
899 DVLOG(1) << "Failed to parse account info with ID: "
900 << account_mapping
.account_id
;
903 DVLOG(1) << "Found account mapping with ID: " << account_mapping
.account_id
;
904 account_mappings
->push_back(account_mapping
);
910 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
911 base::Time
* last_token_fetch_time
) {
912 leveldb::ReadOptions read_options
;
913 read_options
.verify_checksums
= true;
917 db_
->Get(read_options
, MakeSlice(kLastTokenFetchTimeKey
), &result
);
918 int64 time_internal
= 0LL;
919 if (s
.ok() && !base::StringToInt64(result
, &time_internal
)) {
921 "Failed to restore last token fetching time. Using default = 0.";
925 // In case we cannot read last token fetching time, we default it to 0.
926 *last_token_fetch_time
= base::Time::FromInternalValue(time_internal
);
931 GCMStoreImpl::GCMStoreImpl(
932 const base::FilePath
& path
,
933 scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
,
934 scoped_ptr
<Encryptor
> encryptor
)
935 : backend_(new Backend(path
,
936 base::MessageLoopProxy::current(),
938 blocking_task_runner_(blocking_task_runner
),
939 weak_ptr_factory_(this) {
942 GCMStoreImpl::~GCMStoreImpl() {}
944 void GCMStoreImpl::Load(const LoadCallback
& callback
) {
945 blocking_task_runner_
->PostTask(
947 base::Bind(&GCMStoreImpl::Backend::Load
,
949 base::Bind(&GCMStoreImpl::LoadContinuation
,
950 weak_ptr_factory_
.GetWeakPtr(),
954 void GCMStoreImpl::Close() {
955 weak_ptr_factory_
.InvalidateWeakPtrs();
956 app_message_counts_
.clear();
957 blocking_task_runner_
->PostTask(
959 base::Bind(&GCMStoreImpl::Backend::Close
, backend_
));
962 void GCMStoreImpl::Destroy(const UpdateCallback
& callback
) {
963 blocking_task_runner_
->PostTask(
965 base::Bind(&GCMStoreImpl::Backend::Destroy
, backend_
, callback
));
968 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id
,
969 uint64 device_security_token
,
970 const UpdateCallback
& callback
) {
971 blocking_task_runner_
->PostTask(
973 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials
,
976 device_security_token
,
980 void GCMStoreImpl::AddRegistration(
981 const std::string
& app_id
,
982 const linked_ptr
<RegistrationInfo
>& registration
,
983 const UpdateCallback
& callback
) {
984 std::string serialized_registration
= registration
->SerializeAsString();
985 blocking_task_runner_
->PostTask(
987 base::Bind(&GCMStoreImpl::Backend::AddRegistration
,
990 serialized_registration
,
994 void GCMStoreImpl::RemoveRegistration(const std::string
& app_id
,
995 const UpdateCallback
& callback
) {
996 blocking_task_runner_
->PostTask(
998 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration
,
1004 void GCMStoreImpl::AddIncomingMessage(const std::string
& persistent_id
,
1005 const UpdateCallback
& callback
) {
1006 blocking_task_runner_
->PostTask(
1008 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage
,
1014 void GCMStoreImpl::RemoveIncomingMessage(const std::string
& persistent_id
,
1015 const UpdateCallback
& callback
) {
1016 blocking_task_runner_
->PostTask(
1018 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
1020 PersistentIdList(1, persistent_id
),
1024 void GCMStoreImpl::RemoveIncomingMessages(
1025 const PersistentIdList
& persistent_ids
,
1026 const UpdateCallback
& callback
) {
1027 blocking_task_runner_
->PostTask(
1029 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
1035 bool GCMStoreImpl::AddOutgoingMessage(const std::string
& persistent_id
,
1036 const MCSMessage
& message
,
1037 const UpdateCallback
& callback
) {
1038 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
1039 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
1040 &message
.GetProtobuf())->category();
1041 DCHECK(!app_id
.empty());
1042 if (app_message_counts_
.count(app_id
) == 0)
1043 app_message_counts_
[app_id
] = 0;
1044 if (app_message_counts_
[app_id
] < kMessagesPerAppLimit
) {
1045 app_message_counts_
[app_id
]++;
1047 blocking_task_runner_
->PostTask(
1049 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1053 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation
,
1054 weak_ptr_factory_
.GetWeakPtr(),
1062 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string
& persistent_id
,
1063 const MCSMessage
& message
,
1064 const UpdateCallback
& callback
) {
1065 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
1066 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
1067 &message
.GetProtobuf())->category();
1068 DCHECK(!app_id
.empty());
1069 // There should already be pending messages for this app.
1070 DCHECK(app_message_counts_
.count(app_id
));
1071 // TODO(zea): consider verifying the specific message already exists.
1072 blocking_task_runner_
->PostTask(
1074 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1081 void GCMStoreImpl::RemoveOutgoingMessage(const std::string
& persistent_id
,
1082 const UpdateCallback
& callback
) {
1083 blocking_task_runner_
->PostTask(
1085 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1087 PersistentIdList(1, persistent_id
),
1088 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1089 weak_ptr_factory_
.GetWeakPtr(),
1093 void GCMStoreImpl::RemoveOutgoingMessages(
1094 const PersistentIdList
& persistent_ids
,
1095 const UpdateCallback
& callback
) {
1096 blocking_task_runner_
->PostTask(
1098 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1101 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1102 weak_ptr_factory_
.GetWeakPtr(),
1106 void GCMStoreImpl::SetLastCheckinInfo(const base::Time
& time
,
1107 const std::set
<std::string
>& accounts
,
1108 const UpdateCallback
& callback
) {
1109 blocking_task_runner_
->PostTask(
1111 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo
,
1118 void GCMStoreImpl::SetGServicesSettings(
1119 const std::map
<std::string
, std::string
>& settings
,
1120 const std::string
& digest
,
1121 const UpdateCallback
& callback
) {
1122 blocking_task_runner_
->PostTask(
1124 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings
,
1131 void GCMStoreImpl::AddAccountMapping(const AccountMapping
& account_mapping
,
1132 const UpdateCallback
& callback
) {
1133 blocking_task_runner_
->PostTask(
1135 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping
,
1141 void GCMStoreImpl::RemoveAccountMapping(const std::string
& account_id
,
1142 const UpdateCallback
& callback
) {
1143 blocking_task_runner_
->PostTask(
1145 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping
,
1151 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time
& time
,
1152 const UpdateCallback
& callback
) {
1153 blocking_task_runner_
->PostTask(
1155 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime
,
1161 void GCMStoreImpl::SetValueForTesting(const std::string
& key
,
1162 const std::string
& value
,
1163 const UpdateCallback
& callback
) {
1164 blocking_task_runner_
->PostTask(
1166 base::Bind(&GCMStoreImpl::Backend::SetValue
,
1173 void GCMStoreImpl::LoadContinuation(const LoadCallback
& callback
,
1174 scoped_ptr
<LoadResult
> result
) {
1175 if (!result
->success
) {
1176 callback
.Run(result
.Pass());
1179 int num_throttled_apps
= 0;
1180 for (OutgoingMessageMap::const_iterator
1181 iter
= result
->outgoing_messages
.begin();
1182 iter
!= result
->outgoing_messages
.end(); ++iter
) {
1183 const mcs_proto::DataMessageStanza
* data_message
=
1184 reinterpret_cast<mcs_proto::DataMessageStanza
*>(iter
->second
.get());
1185 DCHECK(!data_message
->category().empty());
1186 if (app_message_counts_
.count(data_message
->category()) == 0)
1187 app_message_counts_
[data_message
->category()] = 1;
1189 app_message_counts_
[data_message
->category()]++;
1190 if (app_message_counts_
[data_message
->category()] == kMessagesPerAppLimit
)
1191 num_throttled_apps
++;
1193 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps
);
1194 callback
.Run(result
.Pass());
1197 void GCMStoreImpl::AddOutgoingMessageContinuation(
1198 const UpdateCallback
& callback
,
1199 const std::string
& app_id
,
1202 DCHECK(app_message_counts_
[app_id
] > 0);
1203 app_message_counts_
[app_id
]--;
1205 callback
.Run(success
);
1208 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1209 const UpdateCallback
& callback
,
1211 const AppIdToMessageCountMap
& removed_message_counts
) {
1213 callback
.Run(false);
1216 for (AppIdToMessageCountMap::const_iterator iter
=
1217 removed_message_counts
.begin();
1218 iter
!= removed_message_counts
.end(); ++iter
) {
1219 DCHECK_NE(app_message_counts_
.count(iter
->first
), 0U);
1220 app_message_counts_
[iter
->first
] -= iter
->second
;
1221 DCHECK_GE(app_message_counts_
[iter
->first
], 0);