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.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/src/include/leveldb/db.h"
27 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
33 // Limit to the number of outstanding messages per app.
34 const int kMessagesPerAppLimit
= 20;
36 // ---- LevelDB keys. ----
37 // Key for this device's android id.
38 const char kDeviceAIDKey
[] = "device_aid_key";
39 // Key for this device's android security token.
40 const char kDeviceTokenKey
[] = "device_token_key";
41 // Lowest lexicographically ordered app ids.
42 // Used for prefixing app id.
43 const char kRegistrationKeyStart
[] = "reg1-";
44 // Key guaranteed to be higher than all app ids.
45 // Used for limiting iteration.
46 const char kRegistrationKeyEnd
[] = "reg2-";
47 // Lowest lexicographically ordered incoming message key.
48 // Used for prefixing messages.
49 const char kIncomingMsgKeyStart
[] = "incoming1-";
50 // Key guaranteed to be higher than all incoming message keys.
51 // Used for limiting iteration.
52 const char kIncomingMsgKeyEnd
[] = "incoming2-";
53 // Lowest lexicographically ordered outgoing message key.
54 // Used for prefixing outgoing messages.
55 const char kOutgoingMsgKeyStart
[] = "outgoing1-";
56 // Key guaranteed to be higher than all outgoing message keys.
57 // Used for limiting iteration.
58 const char kOutgoingMsgKeyEnd
[] = "outgoing2-";
59 // Lowest lexicographically ordered G-service settings key.
60 // Used for prefixing G-services settings.
61 const char kGServiceSettingKeyStart
[] = "gservice1-";
62 // Key guaranteed to be higher than all G-services settings keys.
63 // Used for limiting iteration.
64 const char kGServiceSettingKeyEnd
[] = "gservice2-";
65 // Key for digest of the last G-services settings update.
66 const char kGServiceSettingsDigestKey
[] = "gservices_digest";
67 // Key used to indicate how many accounts were last checked in with this device.
68 const char kLastCheckinAccountsKey
[] = "last_checkin_accounts_count";
69 // Key used to timestamp last checkin (marked with G services settings update).
70 const char kLastCheckinTimeKey
[] = "last_checkin_time";
71 // Lowest lexicographically ordered account key.
72 // Used for prefixing account information.
73 const char kAccountKeyStart
[] = "account1-";
74 // Key guaranteed to be higher than all account keys.
75 // Used for limiting iteration.
76 const char kAccountKeyEnd
[] = "account2-";
77 // Key used for last token fetch time.
78 const char kLastTokenFetchTimeKey
[] = "last_token_fetch_time";
80 std::string
MakeRegistrationKey(const std::string
& app_id
) {
81 return kRegistrationKeyStart
+ app_id
;
84 std::string
ParseRegistrationKey(const std::string
& key
) {
85 return key
.substr(arraysize(kRegistrationKeyStart
) - 1);
88 std::string
MakeIncomingKey(const std::string
& persistent_id
) {
89 return kIncomingMsgKeyStart
+ persistent_id
;
92 std::string
MakeOutgoingKey(const std::string
& persistent_id
) {
93 return kOutgoingMsgKeyStart
+ persistent_id
;
96 std::string
ParseOutgoingKey(const std::string
& key
) {
97 return key
.substr(arraysize(kOutgoingMsgKeyStart
) - 1);
100 std::string
MakeGServiceSettingKey(const std::string
& setting_name
) {
101 return kGServiceSettingKeyStart
+ setting_name
;
104 std::string
ParseGServiceSettingKey(const std::string
& key
) {
105 return key
.substr(arraysize(kGServiceSettingKeyStart
) - 1);
108 std::string
MakeAccountKey(const std::string
& account_id
) {
109 return kAccountKeyStart
+ account_id
;
112 std::string
ParseAccountKey(const std::string
& key
) {
113 return key
.substr(arraysize(kAccountKeyStart
) - 1);
116 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
117 // outlive the slice.
118 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
119 leveldb::Slice
MakeSlice(const base::StringPiece
& s
) {
120 return leveldb::Slice(s
.begin(), s
.size());
125 class GCMStoreImpl::Backend
126 : public base::RefCountedThreadSafe
<GCMStoreImpl::Backend
> {
128 Backend(const base::FilePath
& path
,
129 scoped_refptr
<base::SequencedTaskRunner
> foreground_runner
,
130 scoped_ptr
<Encryptor
> encryptor
);
132 // Blocking implementations of GCMStoreImpl methods.
133 void Load(const LoadCallback
& callback
);
135 void Destroy(const UpdateCallback
& callback
);
136 void SetDeviceCredentials(uint64 device_android_id
,
137 uint64 device_security_token
,
138 const UpdateCallback
& callback
);
139 void AddRegistration(const std::string
& app_id
,
140 const linked_ptr
<RegistrationInfo
>& registration
,
141 const UpdateCallback
& callback
);
142 void RemoveRegistration(const std::string
& app_id
,
143 const UpdateCallback
& callback
);
144 void AddIncomingMessage(const std::string
& persistent_id
,
145 const UpdateCallback
& callback
);
146 void RemoveIncomingMessages(const PersistentIdList
& persistent_ids
,
147 const UpdateCallback
& callback
);
148 void AddOutgoingMessage(const std::string
& persistent_id
,
149 const MCSMessage
& message
,
150 const UpdateCallback
& callback
);
151 void RemoveOutgoingMessages(
152 const PersistentIdList
& persistent_ids
,
153 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
155 void AddUserSerialNumber(const std::string
& username
,
157 const UpdateCallback
& callback
);
158 void RemoveUserSerialNumber(const std::string
& username
,
159 const UpdateCallback
& callback
);
160 void SetLastCheckinInfo(const base::Time
& time
,
161 const std::set
<std::string
>& accounts
,
162 const UpdateCallback
& callback
);
163 void SetGServicesSettings(
164 const std::map
<std::string
, std::string
>& settings
,
165 const std::string
& digest
,
166 const UpdateCallback
& callback
);
167 void AddAccountMapping(const AccountMapping
& account_mapping
,
168 const UpdateCallback
& callback
);
169 void RemoveAccountMapping(const std::string
& account_id
,
170 const UpdateCallback
& callback
);
171 void SetLastTokenFetchTime(const base::Time
& time
,
172 const UpdateCallback
& callback
);
175 friend class base::RefCountedThreadSafe
<Backend
>;
178 bool LoadDeviceCredentials(uint64
* android_id
, uint64
* security_token
);
179 bool LoadRegistrations(RegistrationInfoMap
* registrations
);
180 bool LoadIncomingMessages(std::vector
<std::string
>* incoming_messages
);
181 bool LoadOutgoingMessages(OutgoingMessageMap
* outgoing_messages
);
182 bool LoadLastCheckinInfo(base::Time
* last_checkin_time
,
183 std::set
<std::string
>* accounts
);
184 bool LoadGServicesSettings(std::map
<std::string
, std::string
>* settings
,
185 std::string
* digest
);
186 bool LoadAccountMappingInfo(AccountMappings
* account_mappings
);
187 bool LoadLastTokenFetchTime(base::Time
* last_token_fetch_time
);
189 const base::FilePath path_
;
190 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner_
;
191 scoped_ptr
<Encryptor
> encryptor_
;
193 scoped_ptr
<leveldb::DB
> db_
;
196 GCMStoreImpl::Backend::Backend(
197 const base::FilePath
& path
,
198 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner
,
199 scoped_ptr
<Encryptor
> encryptor
)
201 foreground_task_runner_(foreground_task_runner
),
202 encryptor_(encryptor
.Pass()) {
205 GCMStoreImpl::Backend::~Backend() {}
207 void GCMStoreImpl::Backend::Load(const LoadCallback
& callback
) {
208 scoped_ptr
<LoadResult
> result(new LoadResult());
210 LOG(ERROR
) << "Attempting to reload open database.";
211 foreground_task_runner_
->PostTask(FROM_HERE
,
213 base::Passed(&result
)));
217 leveldb::Options options
;
218 options
.create_if_missing
= true;
220 leveldb::Status status
=
221 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
);
222 UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status
.ok());
224 LOG(ERROR
) << "Failed to open database " << path_
.value() << ": "
225 << status
.ToString();
226 foreground_task_runner_
->PostTask(FROM_HERE
,
228 base::Passed(&result
)));
233 if (!LoadDeviceCredentials(&result
->device_android_id
,
234 &result
->device_security_token
) ||
235 !LoadRegistrations(&result
->registrations
) ||
236 !LoadIncomingMessages(&result
->incoming_messages
) ||
237 !LoadOutgoingMessages(&result
->outgoing_messages
) ||
238 !LoadLastCheckinInfo(&result
->last_checkin_time
,
239 &result
->last_checkin_accounts
) ||
240 !LoadGServicesSettings(&result
->gservices_settings
,
241 &result
->gservices_digest
) ||
242 !LoadAccountMappingInfo(&result
->account_mappings
) ||
243 !LoadLastTokenFetchTime(&result
->last_token_fetch_time
)) {
245 foreground_task_runner_
->PostTask(FROM_HERE
,
247 base::Passed(&result
)));
251 // Only record histograms if GCM had already been set up for this device.
252 if (result
->device_android_id
!= 0 && result
->device_security_token
!= 0) {
254 if (base::GetFileSize(path_
, &file_size
)) {
255 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
256 static_cast<int>(file_size
/ 1024));
258 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
259 result
->registrations
.size());
260 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
261 result
->outgoing_messages
.size());
262 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
263 result
->incoming_messages
.size());
266 DVLOG(1) << "Succeeded in loading " << result
->registrations
.size()
267 << " registrations, "
268 << result
->incoming_messages
.size()
269 << " unacknowledged incoming messages and "
270 << result
->outgoing_messages
.size()
271 << " unacknowledged outgoing messages.";
272 result
->success
= true;
273 foreground_task_runner_
->PostTask(FROM_HERE
,
275 base::Passed(&result
)));
279 void GCMStoreImpl::Backend::Close() {
280 DVLOG(1) << "Closing GCM store.";
284 void GCMStoreImpl::Backend::Destroy(const UpdateCallback
& callback
) {
285 DVLOG(1) << "Destroying GCM store.";
287 const leveldb::Status s
=
288 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), leveldb::Options());
290 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
293 LOG(ERROR
) << "Destroy failed: " << s
.ToString();
294 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
297 void GCMStoreImpl::Backend::SetDeviceCredentials(
298 uint64 device_android_id
,
299 uint64 device_security_token
,
300 const UpdateCallback
& callback
) {
301 DVLOG(1) << "Saving device credentials with AID " << device_android_id
;
303 LOG(ERROR
) << "GCMStore db doesn't exist.";
304 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
308 leveldb::WriteOptions write_options
;
309 write_options
.sync
= true;
311 std::string encrypted_token
;
312 encryptor_
->EncryptString(base::Uint64ToString(device_security_token
),
314 std::string android_id_str
= base::Uint64ToString(device_android_id
);
316 db_
->Put(write_options
,
317 MakeSlice(kDeviceAIDKey
),
318 MakeSlice(android_id_str
));
321 write_options
, MakeSlice(kDeviceTokenKey
), MakeSlice(encrypted_token
));
324 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
327 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
328 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
331 void GCMStoreImpl::Backend::AddRegistration(
332 const std::string
& app_id
,
333 const linked_ptr
<RegistrationInfo
>& registration
,
334 const UpdateCallback
& callback
) {
335 DVLOG(1) << "Saving registration info for app: " << app_id
;
337 LOG(ERROR
) << "GCMStore db doesn't exist.";
338 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
341 leveldb::WriteOptions write_options
;
342 write_options
.sync
= true;
344 std::string key
= MakeRegistrationKey(app_id
);
345 std::string value
= registration
->SerializeAsString();
346 const leveldb::Status status
= db_
->Put(write_options
,
350 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
353 LOG(ERROR
) << "LevelDB put failed: " << status
.ToString();
354 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
357 void GCMStoreImpl::Backend::RemoveRegistration(const std::string
& app_id
,
358 const UpdateCallback
& callback
) {
360 LOG(ERROR
) << "GCMStore db doesn't exist.";
361 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
364 leveldb::WriteOptions write_options
;
365 write_options
.sync
= true;
367 leveldb::Status status
=
368 db_
->Delete(write_options
, MakeSlice(MakeRegistrationKey(app_id
)));
370 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
373 LOG(ERROR
) << "LevelDB remove failed: " << status
.ToString();
374 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
377 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string
& persistent_id
,
378 const UpdateCallback
& callback
) {
379 DVLOG(1) << "Saving incoming message with id " << persistent_id
;
381 LOG(ERROR
) << "GCMStore db doesn't exist.";
382 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
386 leveldb::WriteOptions write_options
;
387 write_options
.sync
= true;
389 std::string key
= MakeIncomingKey(persistent_id
);
390 const leveldb::Status s
= db_
->Put(write_options
,
392 MakeSlice(persistent_id
));
394 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
397 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
398 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
401 void GCMStoreImpl::Backend::RemoveIncomingMessages(
402 const PersistentIdList
& persistent_ids
,
403 const UpdateCallback
& callback
) {
405 LOG(ERROR
) << "GCMStore db doesn't exist.";
406 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
409 leveldb::WriteOptions write_options
;
410 write_options
.sync
= true;
413 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
414 iter
!= persistent_ids
.end();
416 DVLOG(1) << "Removing incoming message with id " << *iter
;
417 std::string key
= MakeIncomingKey(*iter
);
418 s
= db_
->Delete(write_options
, MakeSlice(key
));
423 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
426 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
427 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
430 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string
& persistent_id
,
431 const MCSMessage
& message
,
432 const UpdateCallback
& callback
) {
433 DVLOG(1) << "Saving outgoing message with id " << persistent_id
;
435 LOG(ERROR
) << "GCMStore db doesn't exist.";
436 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
439 leveldb::WriteOptions write_options
;
440 write_options
.sync
= true;
443 static_cast<char>(message
.tag()) + message
.SerializeAsString();
444 std::string key
= MakeOutgoingKey(persistent_id
);
445 const leveldb::Status s
= db_
->Put(write_options
,
449 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
452 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
453 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
456 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
457 const PersistentIdList
& persistent_ids
,
458 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
461 LOG(ERROR
) << "GCMStore db doesn't exist.";
462 foreground_task_runner_
->PostTask(FROM_HERE
,
465 AppIdToMessageCountMap()));
468 leveldb::ReadOptions read_options
;
469 leveldb::WriteOptions write_options
;
470 write_options
.sync
= true;
472 AppIdToMessageCountMap removed_message_counts
;
475 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
476 iter
!= persistent_ids
.end();
478 DVLOG(1) << "Removing outgoing message with id " << *iter
;
479 std::string outgoing_message
;
480 std::string key
= MakeOutgoingKey(*iter
);
481 s
= db_
->Get(read_options
,
486 mcs_proto::DataMessageStanza data_message
;
487 // Skip the initial tag byte and parse the rest to extract the message.
488 if (data_message
.ParseFromString(outgoing_message
.substr(1))) {
489 DCHECK(!data_message
.category().empty());
490 if (removed_message_counts
.count(data_message
.category()) != 0)
491 removed_message_counts
[data_message
.category()]++;
493 removed_message_counts
[data_message
.category()] = 1;
495 DVLOG(1) << "Removing outgoing message with id " << *iter
;
496 s
= db_
->Delete(write_options
, MakeSlice(key
));
501 foreground_task_runner_
->PostTask(FROM_HERE
,
504 removed_message_counts
));
507 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
508 foreground_task_runner_
->PostTask(FROM_HERE
,
511 AppIdToMessageCountMap()));
514 void GCMStoreImpl::Backend::SetLastCheckinInfo(
515 const base::Time
& time
,
516 const std::set
<std::string
>& accounts
,
517 const UpdateCallback
& callback
) {
518 leveldb::WriteBatch write_batch
;
520 int64 last_checkin_time_internal
= time
.ToInternalValue();
521 write_batch
.Put(MakeSlice(kLastCheckinTimeKey
),
522 MakeSlice(base::Int64ToString(last_checkin_time_internal
)));
524 std::string serialized_accounts
;
525 for (std::set
<std::string
>::iterator iter
= accounts
.begin();
526 iter
!= accounts
.end();
528 serialized_accounts
+= *iter
;
529 serialized_accounts
+= ",";
531 if (!serialized_accounts
.empty())
532 serialized_accounts
.erase(serialized_accounts
.length() - 1);
534 write_batch
.Put(MakeSlice(kLastCheckinAccountsKey
),
535 MakeSlice(serialized_accounts
));
537 leveldb::WriteOptions write_options
;
538 write_options
.sync
= true;
539 const leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
542 LOG(ERROR
) << "LevelDB set last checkin info failed: " << s
.ToString();
543 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
546 void GCMStoreImpl::Backend::SetGServicesSettings(
547 const std::map
<std::string
, std::string
>& settings
,
548 const std::string
& settings_digest
,
549 const UpdateCallback
& callback
) {
550 leveldb::WriteBatch write_batch
;
552 // Remove all existing settings.
553 leveldb::ReadOptions read_options
;
554 read_options
.verify_checksums
= true;
555 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
556 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
557 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
559 write_batch
.Delete(iter
->key());
562 // Add the new settings.
563 for (std::map
<std::string
, std::string
>::const_iterator iter
=
565 iter
!= settings
.end(); ++iter
) {
566 write_batch
.Put(MakeSlice(MakeGServiceSettingKey(iter
->first
)),
567 MakeSlice(iter
->second
));
570 // Update the settings digest.
571 write_batch
.Put(MakeSlice(kGServiceSettingsDigestKey
),
572 MakeSlice(settings_digest
));
574 // Write it all in a batch.
575 leveldb::WriteOptions write_options
;
576 write_options
.sync
= true;
578 leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
580 LOG(ERROR
) << "LevelDB GService Settings update failed: " << s
.ToString();
581 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
584 void GCMStoreImpl::Backend::AddAccountMapping(
585 const AccountMapping
& account_mapping
,
586 const UpdateCallback
& callback
) {
587 DVLOG(1) << "Saving account info for account with email: "
588 << account_mapping
.email
;
590 LOG(ERROR
) << "GCMStore db doesn't exist.";
591 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
595 leveldb::WriteOptions write_options
;
596 write_options
.sync
= true;
598 std::string data
= account_mapping
.SerializeAsString();
599 std::string key
= MakeAccountKey(account_mapping
.account_id
);
600 const leveldb::Status s
=
601 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(data
));
603 LOG(ERROR
) << "LevelDB adding account mapping failed: " << s
.ToString();
604 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
607 void GCMStoreImpl::Backend::RemoveAccountMapping(
608 const std::string
& account_id
,
609 const UpdateCallback
& callback
) {
611 LOG(ERROR
) << "GCMStore db doesn't exist.";
612 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
616 leveldb::WriteOptions write_options
;
617 write_options
.sync
= true;
620 db_
->Delete(write_options
, MakeSlice(MakeAccountKey(account_id
)));
623 LOG(ERROR
) << "LevelDB removal of account mapping failed: " << s
.ToString();
624 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
627 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
628 const base::Time
& time
,
629 const UpdateCallback
& callback
) {
630 DVLOG(1) << "Setting last token fetching time.";
632 LOG(ERROR
) << "GCMStore db doesn't exist.";
633 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
637 leveldb::WriteOptions write_options
;
638 write_options
.sync
= true;
640 const leveldb::Status s
=
641 db_
->Put(write_options
,
642 MakeSlice(kLastTokenFetchTimeKey
),
643 MakeSlice(base::Int64ToString(time
.ToInternalValue())));
646 LOG(ERROR
) << "LevelDB setting last token fetching time: " << s
.ToString();
647 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
650 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64
* android_id
,
651 uint64
* security_token
) {
652 leveldb::ReadOptions read_options
;
653 read_options
.verify_checksums
= true;
656 leveldb::Status s
= db_
->Get(read_options
, MakeSlice(kDeviceAIDKey
), &result
);
658 if (!base::StringToUint64(result
, android_id
)) {
659 LOG(ERROR
) << "Failed to restore device id.";
663 s
= db_
->Get(read_options
, MakeSlice(kDeviceTokenKey
), &result
);
666 std::string decrypted_token
;
667 encryptor_
->DecryptString(result
, &decrypted_token
);
668 if (!base::StringToUint64(decrypted_token
, security_token
)) {
669 LOG(ERROR
) << "Failed to restore security token.";
675 if (s
.IsNotFound()) {
676 DVLOG(1) << "No credentials found.";
680 LOG(ERROR
) << "Error reading credentials from store.";
684 bool GCMStoreImpl::Backend::LoadRegistrations(
685 RegistrationInfoMap
* registrations
) {
686 leveldb::ReadOptions read_options
;
687 read_options
.verify_checksums
= true;
689 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
690 for (iter
->Seek(MakeSlice(kRegistrationKeyStart
));
691 iter
->Valid() && iter
->key().ToString() < kRegistrationKeyEnd
;
693 leveldb::Slice s
= iter
->value();
695 LOG(ERROR
) << "Error reading registration with key " << s
.ToString();
698 std::string app_id
= ParseRegistrationKey(iter
->key().ToString());
699 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
700 if (!registration
->ParseFromString(iter
->value().ToString())) {
701 LOG(ERROR
) << "Failed to parse registration with app id " << app_id
;
704 DVLOG(1) << "Found registration with app id " << app_id
;
705 (*registrations
)[app_id
] = registration
;
711 bool GCMStoreImpl::Backend::LoadIncomingMessages(
712 std::vector
<std::string
>* incoming_messages
) {
713 leveldb::ReadOptions read_options
;
714 read_options
.verify_checksums
= true;
716 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
717 for (iter
->Seek(MakeSlice(kIncomingMsgKeyStart
));
718 iter
->Valid() && iter
->key().ToString() < kIncomingMsgKeyEnd
;
720 leveldb::Slice s
= iter
->value();
722 LOG(ERROR
) << "Error reading incoming message with key "
723 << iter
->key().ToString();
726 DVLOG(1) << "Found incoming message with id " << s
.ToString();
727 incoming_messages
->push_back(s
.ToString());
733 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
734 OutgoingMessageMap
* outgoing_messages
) {
735 leveldb::ReadOptions read_options
;
736 read_options
.verify_checksums
= true;
738 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
739 for (iter
->Seek(MakeSlice(kOutgoingMsgKeyStart
));
740 iter
->Valid() && iter
->key().ToString() < kOutgoingMsgKeyEnd
;
742 leveldb::Slice s
= iter
->value();
744 LOG(ERROR
) << "Error reading incoming message with key " << s
.ToString();
747 uint8 tag
= iter
->value().data()[0];
748 std::string id
= ParseOutgoingKey(iter
->key().ToString());
749 scoped_ptr
<google::protobuf::MessageLite
> message(
750 BuildProtobufFromTag(tag
));
751 if (!message
.get() ||
752 !message
->ParseFromString(iter
->value().ToString().substr(1))) {
753 LOG(ERROR
) << "Failed to parse outgoing message with id " << id
754 << " and tag " << tag
;
757 DVLOG(1) << "Found outgoing message with id " << id
<< " of type "
758 << base::IntToString(tag
);
759 (*outgoing_messages
)[id
] = make_linked_ptr(message
.release());
765 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
766 base::Time
* last_checkin_time
,
767 std::set
<std::string
>* accounts
) {
768 leveldb::ReadOptions read_options
;
769 read_options
.verify_checksums
= true;
772 leveldb::Status s
= db_
->Get(read_options
,
773 MakeSlice(kLastCheckinTimeKey
),
775 int64 time_internal
= 0LL;
776 if (s
.ok() && !base::StringToInt64(result
, &time_internal
))
777 LOG(ERROR
) << "Failed to restore last checkin time. Using default = 0.";
779 // In case we cannot read last checkin time, we default it to 0, as we don't
780 // want that situation to cause the whole load to fail.
781 *last_checkin_time
= base::Time::FromInternalValue(time_internal
);
784 s
= db_
->Get(read_options
, MakeSlice(kLastCheckinAccountsKey
), &result
);
786 DVLOG(1) << "No accounts where stored during last run.";
788 base::StringTokenizer
t(result
, ",");
790 accounts
->insert(t
.token());
795 bool GCMStoreImpl::Backend::LoadGServicesSettings(
796 std::map
<std::string
, std::string
>* settings
,
797 std::string
* digest
) {
798 leveldb::ReadOptions read_options
;
799 read_options
.verify_checksums
= true;
801 // Load all of the GServices settings.
802 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
803 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
804 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
806 std::string value
= iter
->value().ToString();
808 LOG(ERROR
) << "Error reading GService Settings " << value
;
811 std::string id
= ParseGServiceSettingKey(iter
->key().ToString());
812 (*settings
)[id
] = value
;
813 DVLOG(1) << "Found G Service setting with key: " << id
814 << ", and value: " << value
;
817 // Load the settings digest. It's ok if it is empty.
818 db_
->Get(read_options
, MakeSlice(kGServiceSettingsDigestKey
), digest
);
823 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
824 AccountMappings
* account_mappings
) {
825 leveldb::ReadOptions read_options
;
826 read_options
.verify_checksums
= true;
828 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
829 for (iter
->Seek(MakeSlice(kAccountKeyStart
));
830 iter
->Valid() && iter
->key().ToString() < kAccountKeyEnd
;
832 AccountMapping account_mapping
;
833 account_mapping
.account_id
= ParseAccountKey(iter
->key().ToString());
834 if (!account_mapping
.ParseFromString(iter
->value().ToString())) {
835 DVLOG(1) << "Failed to parse account info with ID: "
836 << account_mapping
.account_id
;
839 DVLOG(1) << "Found account mapping with ID: " << account_mapping
.account_id
;
840 account_mappings
->push_back(account_mapping
);
846 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
847 base::Time
* last_token_fetch_time
) {
848 leveldb::ReadOptions read_options
;
849 read_options
.verify_checksums
= true;
853 db_
->Get(read_options
, MakeSlice(kLastTokenFetchTimeKey
), &result
);
854 int64 time_internal
= 0LL;
855 if (s
.ok() && !base::StringToInt64(result
, &time_internal
))
856 LOG(ERROR
) << "Failed to restore last checkin time. Using default = 0.";
858 // In case we cannot read last token fetching time, we default it to 0.
859 *last_token_fetch_time
= base::Time::FromInternalValue(time_internal
);
864 GCMStoreImpl::GCMStoreImpl(
865 const base::FilePath
& path
,
866 scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
,
867 scoped_ptr
<Encryptor
> encryptor
)
868 : backend_(new Backend(path
,
869 base::MessageLoopProxy::current(),
871 blocking_task_runner_(blocking_task_runner
),
872 weak_ptr_factory_(this) {
875 GCMStoreImpl::~GCMStoreImpl() {}
877 void GCMStoreImpl::Load(const LoadCallback
& callback
) {
878 blocking_task_runner_
->PostTask(
880 base::Bind(&GCMStoreImpl::Backend::Load
,
882 base::Bind(&GCMStoreImpl::LoadContinuation
,
883 weak_ptr_factory_
.GetWeakPtr(),
887 void GCMStoreImpl::Close() {
888 weak_ptr_factory_
.InvalidateWeakPtrs();
889 app_message_counts_
.clear();
890 blocking_task_runner_
->PostTask(
892 base::Bind(&GCMStoreImpl::Backend::Close
, backend_
));
895 void GCMStoreImpl::Destroy(const UpdateCallback
& callback
) {
896 blocking_task_runner_
->PostTask(
898 base::Bind(&GCMStoreImpl::Backend::Destroy
, backend_
, callback
));
901 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id
,
902 uint64 device_security_token
,
903 const UpdateCallback
& callback
) {
904 blocking_task_runner_
->PostTask(
906 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials
,
909 device_security_token
,
913 void GCMStoreImpl::AddRegistration(
914 const std::string
& app_id
,
915 const linked_ptr
<RegistrationInfo
>& registration
,
916 const UpdateCallback
& callback
) {
917 blocking_task_runner_
->PostTask(
919 base::Bind(&GCMStoreImpl::Backend::AddRegistration
,
926 void GCMStoreImpl::RemoveRegistration(const std::string
& app_id
,
927 const UpdateCallback
& callback
) {
928 blocking_task_runner_
->PostTask(
930 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration
,
936 void GCMStoreImpl::AddIncomingMessage(const std::string
& persistent_id
,
937 const UpdateCallback
& callback
) {
938 blocking_task_runner_
->PostTask(
940 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage
,
946 void GCMStoreImpl::RemoveIncomingMessage(const std::string
& persistent_id
,
947 const UpdateCallback
& callback
) {
948 blocking_task_runner_
->PostTask(
950 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
952 PersistentIdList(1, persistent_id
),
956 void GCMStoreImpl::RemoveIncomingMessages(
957 const PersistentIdList
& persistent_ids
,
958 const UpdateCallback
& callback
) {
959 blocking_task_runner_
->PostTask(
961 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
967 bool GCMStoreImpl::AddOutgoingMessage(const std::string
& persistent_id
,
968 const MCSMessage
& message
,
969 const UpdateCallback
& callback
) {
970 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
971 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
972 &message
.GetProtobuf())->category();
973 DCHECK(!app_id
.empty());
974 if (app_message_counts_
.count(app_id
) == 0)
975 app_message_counts_
[app_id
] = 0;
976 if (app_message_counts_
[app_id
] < kMessagesPerAppLimit
) {
977 app_message_counts_
[app_id
]++;
979 blocking_task_runner_
->PostTask(
981 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
985 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation
,
986 weak_ptr_factory_
.GetWeakPtr(),
994 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string
& persistent_id
,
995 const MCSMessage
& message
,
996 const UpdateCallback
& callback
) {
997 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
998 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
999 &message
.GetProtobuf())->category();
1000 DCHECK(!app_id
.empty());
1001 // There should already be pending messages for this app.
1002 DCHECK(app_message_counts_
.count(app_id
));
1003 // TODO(zea): consider verifying the specific message already exists.
1004 blocking_task_runner_
->PostTask(
1006 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1013 void GCMStoreImpl::RemoveOutgoingMessage(const std::string
& persistent_id
,
1014 const UpdateCallback
& callback
) {
1015 blocking_task_runner_
->PostTask(
1017 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1019 PersistentIdList(1, persistent_id
),
1020 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1021 weak_ptr_factory_
.GetWeakPtr(),
1025 void GCMStoreImpl::RemoveOutgoingMessages(
1026 const PersistentIdList
& persistent_ids
,
1027 const UpdateCallback
& callback
) {
1028 blocking_task_runner_
->PostTask(
1030 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1033 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1034 weak_ptr_factory_
.GetWeakPtr(),
1038 void GCMStoreImpl::SetLastCheckinInfo(const base::Time
& time
,
1039 const std::set
<std::string
>& accounts
,
1040 const UpdateCallback
& callback
) {
1041 blocking_task_runner_
->PostTask(
1043 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo
,
1050 void GCMStoreImpl::SetGServicesSettings(
1051 const std::map
<std::string
, std::string
>& settings
,
1052 const std::string
& digest
,
1053 const UpdateCallback
& callback
) {
1054 blocking_task_runner_
->PostTask(
1056 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings
,
1063 void GCMStoreImpl::AddAccountMapping(const AccountMapping
& account_mapping
,
1064 const UpdateCallback
& callback
) {
1065 blocking_task_runner_
->PostTask(
1067 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping
,
1073 void GCMStoreImpl::RemoveAccountMapping(const std::string
& account_id
,
1074 const UpdateCallback
& callback
) {
1075 blocking_task_runner_
->PostTask(
1077 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping
,
1083 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time
& time
,
1084 const UpdateCallback
& callback
) {
1085 blocking_task_runner_
->PostTask(
1087 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime
,
1093 void GCMStoreImpl::LoadContinuation(const LoadCallback
& callback
,
1094 scoped_ptr
<LoadResult
> result
) {
1095 if (!result
->success
) {
1096 callback
.Run(result
.Pass());
1099 int num_throttled_apps
= 0;
1100 for (OutgoingMessageMap::const_iterator
1101 iter
= result
->outgoing_messages
.begin();
1102 iter
!= result
->outgoing_messages
.end(); ++iter
) {
1103 const mcs_proto::DataMessageStanza
* data_message
=
1104 reinterpret_cast<mcs_proto::DataMessageStanza
*>(iter
->second
.get());
1105 DCHECK(!data_message
->category().empty());
1106 if (app_message_counts_
.count(data_message
->category()) == 0)
1107 app_message_counts_
[data_message
->category()] = 1;
1109 app_message_counts_
[data_message
->category()]++;
1110 if (app_message_counts_
[data_message
->category()] == kMessagesPerAppLimit
)
1111 num_throttled_apps
++;
1113 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps
);
1114 callback
.Run(result
.Pass());
1117 void GCMStoreImpl::AddOutgoingMessageContinuation(
1118 const UpdateCallback
& callback
,
1119 const std::string
& app_id
,
1122 DCHECK(app_message_counts_
[app_id
] > 0);
1123 app_message_counts_
[app_id
]--;
1125 callback
.Run(success
);
1128 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1129 const UpdateCallback
& callback
,
1131 const AppIdToMessageCountMap
& removed_message_counts
) {
1133 callback
.Run(false);
1136 for (AppIdToMessageCountMap::const_iterator iter
=
1137 removed_message_counts
.begin();
1138 iter
!= removed_message_counts
.end(); ++iter
) {
1139 DCHECK_NE(app_message_counts_
.count(iter
->first
), 0U);
1140 app_message_counts_
[iter
->first
] -= iter
->second
;
1141 DCHECK_GE(app_message_counts_
[iter
->first
], 0);