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/file_util.h"
11 #include "base/files/file_path.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-";
78 std::string
MakeRegistrationKey(const std::string
& app_id
) {
79 return kRegistrationKeyStart
+ app_id
;
82 std::string
ParseRegistrationKey(const std::string
& key
) {
83 return key
.substr(arraysize(kRegistrationKeyStart
) - 1);
86 std::string
MakeIncomingKey(const std::string
& persistent_id
) {
87 return kIncomingMsgKeyStart
+ persistent_id
;
90 std::string
MakeOutgoingKey(const std::string
& persistent_id
) {
91 return kOutgoingMsgKeyStart
+ persistent_id
;
94 std::string
ParseOutgoingKey(const std::string
& key
) {
95 return key
.substr(arraysize(kOutgoingMsgKeyStart
) - 1);
98 std::string
MakeGServiceSettingKey(const std::string
& setting_name
) {
99 return kGServiceSettingKeyStart
+ setting_name
;
102 std::string
ParseGServiceSettingKey(const std::string
& key
) {
103 return key
.substr(arraysize(kGServiceSettingKeyStart
) - 1);
106 std::string
MakeAccountKey(const std::string
& account_id
) {
107 return kAccountKeyStart
+ account_id
;
110 std::string
ParseAccountKey(const std::string
& key
) {
111 return key
.substr(arraysize(kAccountKeyStart
) - 1);
114 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
115 // outlive the slice.
116 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
117 leveldb::Slice
MakeSlice(const base::StringPiece
& s
) {
118 return leveldb::Slice(s
.begin(), s
.size());
123 class GCMStoreImpl::Backend
124 : public base::RefCountedThreadSafe
<GCMStoreImpl::Backend
> {
126 Backend(const base::FilePath
& path
,
127 scoped_refptr
<base::SequencedTaskRunner
> foreground_runner
,
128 scoped_ptr
<Encryptor
> encryptor
);
130 // Blocking implementations of GCMStoreImpl methods.
131 void Load(const LoadCallback
& callback
);
133 void Destroy(const UpdateCallback
& callback
);
134 void SetDeviceCredentials(uint64 device_android_id
,
135 uint64 device_security_token
,
136 const UpdateCallback
& callback
);
137 void AddRegistration(const std::string
& app_id
,
138 const linked_ptr
<RegistrationInfo
>& registration
,
139 const UpdateCallback
& callback
);
140 void RemoveRegistration(const std::string
& app_id
,
141 const UpdateCallback
& callback
);
142 void AddIncomingMessage(const std::string
& persistent_id
,
143 const UpdateCallback
& callback
);
144 void RemoveIncomingMessages(const PersistentIdList
& persistent_ids
,
145 const UpdateCallback
& callback
);
146 void AddOutgoingMessage(const std::string
& persistent_id
,
147 const MCSMessage
& message
,
148 const UpdateCallback
& callback
);
149 void RemoveOutgoingMessages(
150 const PersistentIdList
& persistent_ids
,
151 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
153 void AddUserSerialNumber(const std::string
& username
,
155 const UpdateCallback
& callback
);
156 void RemoveUserSerialNumber(const std::string
& username
,
157 const UpdateCallback
& callback
);
158 void SetLastCheckinInfo(const base::Time
& time
,
159 const std::set
<std::string
>& accounts
,
160 const UpdateCallback
& callback
);
161 void SetGServicesSettings(
162 const std::map
<std::string
, std::string
>& settings
,
163 const std::string
& digest
,
164 const UpdateCallback
& callback
);
165 void AddAccountMapping(const AccountMapping
& account_mapping
,
166 const UpdateCallback
& callback
);
167 void RemoveAccountMapping(const std::string
& account_id
,
168 const UpdateCallback
& callback
);
171 friend class base::RefCountedThreadSafe
<Backend
>;
174 bool LoadDeviceCredentials(uint64
* android_id
, uint64
* security_token
);
175 bool LoadRegistrations(RegistrationInfoMap
* registrations
);
176 bool LoadIncomingMessages(std::vector
<std::string
>* incoming_messages
);
177 bool LoadOutgoingMessages(OutgoingMessageMap
* outgoing_messages
);
178 bool LoadLastCheckinInfo(base::Time
* last_checkin_time
,
179 std::set
<std::string
>* accounts
);
180 bool LoadGServicesSettings(std::map
<std::string
, std::string
>* settings
,
181 std::string
* digest
);
182 bool LoadAccountMappingInfo(AccountMappingMap
* account_mappings
);
184 const base::FilePath path_
;
185 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner_
;
186 scoped_ptr
<Encryptor
> encryptor_
;
188 scoped_ptr
<leveldb::DB
> db_
;
191 GCMStoreImpl::Backend::Backend(
192 const base::FilePath
& path
,
193 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner
,
194 scoped_ptr
<Encryptor
> encryptor
)
196 foreground_task_runner_(foreground_task_runner
),
197 encryptor_(encryptor
.Pass()) {
200 GCMStoreImpl::Backend::~Backend() {}
202 void GCMStoreImpl::Backend::Load(const LoadCallback
& callback
) {
203 scoped_ptr
<LoadResult
> result(new LoadResult());
205 LOG(ERROR
) << "Attempting to reload open database.";
206 foreground_task_runner_
->PostTask(FROM_HERE
,
208 base::Passed(&result
)));
212 leveldb::Options options
;
213 options
.create_if_missing
= true;
215 leveldb::Status status
=
216 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
);
217 UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status
.ok());
219 LOG(ERROR
) << "Failed to open database " << path_
.value() << ": "
220 << status
.ToString();
221 foreground_task_runner_
->PostTask(FROM_HERE
,
223 base::Passed(&result
)));
228 if (!LoadDeviceCredentials(&result
->device_android_id
,
229 &result
->device_security_token
) ||
230 !LoadRegistrations(&result
->registrations
) ||
231 !LoadIncomingMessages(&result
->incoming_messages
) ||
232 !LoadOutgoingMessages(&result
->outgoing_messages
) ||
233 !LoadLastCheckinInfo(&result
->last_checkin_time
,
234 &result
->last_checkin_accounts
) ||
235 !LoadGServicesSettings(&result
->gservices_settings
,
236 &result
->gservices_digest
) ||
237 !LoadAccountMappingInfo(&result
->account_mappings
)) {
239 foreground_task_runner_
->PostTask(FROM_HERE
,
241 base::Passed(&result
)));
245 // Only record histograms if GCM had already been set up for this device.
246 if (result
->device_android_id
!= 0 && result
->device_security_token
!= 0) {
248 if (base::GetFileSize(path_
, &file_size
)) {
249 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
250 static_cast<int>(file_size
/ 1024));
252 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
253 result
->registrations
.size());
254 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
255 result
->outgoing_messages
.size());
256 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
257 result
->incoming_messages
.size());
260 DVLOG(1) << "Succeeded in loading " << result
->registrations
.size()
261 << " registrations, "
262 << result
->incoming_messages
.size()
263 << " unacknowledged incoming messages and "
264 << result
->outgoing_messages
.size()
265 << " unacknowledged outgoing messages.";
266 result
->success
= true;
267 foreground_task_runner_
->PostTask(FROM_HERE
,
269 base::Passed(&result
)));
273 void GCMStoreImpl::Backend::Close() {
274 DVLOG(1) << "Closing GCM store.";
278 void GCMStoreImpl::Backend::Destroy(const UpdateCallback
& callback
) {
279 DVLOG(1) << "Destroying GCM store.";
281 const leveldb::Status s
=
282 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), leveldb::Options());
284 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
287 LOG(ERROR
) << "Destroy failed: " << s
.ToString();
288 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
291 void GCMStoreImpl::Backend::SetDeviceCredentials(
292 uint64 device_android_id
,
293 uint64 device_security_token
,
294 const UpdateCallback
& callback
) {
295 DVLOG(1) << "Saving device credentials with AID " << device_android_id
;
297 LOG(ERROR
) << "GCMStore db doesn't exist.";
298 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
302 leveldb::WriteOptions write_options
;
303 write_options
.sync
= true;
305 std::string encrypted_token
;
306 encryptor_
->EncryptString(base::Uint64ToString(device_security_token
),
308 std::string android_id_str
= base::Uint64ToString(device_android_id
);
310 db_
->Put(write_options
,
311 MakeSlice(kDeviceAIDKey
),
312 MakeSlice(android_id_str
));
315 write_options
, MakeSlice(kDeviceTokenKey
), MakeSlice(encrypted_token
));
318 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
321 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
322 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
325 void GCMStoreImpl::Backend::AddRegistration(
326 const std::string
& app_id
,
327 const linked_ptr
<RegistrationInfo
>& registration
,
328 const UpdateCallback
& callback
) {
329 DVLOG(1) << "Saving registration info for app: " << app_id
;
331 LOG(ERROR
) << "GCMStore db doesn't exist.";
332 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
335 leveldb::WriteOptions write_options
;
336 write_options
.sync
= true;
338 std::string key
= MakeRegistrationKey(app_id
);
339 std::string value
= registration
->SerializeAsString();
340 const leveldb::Status status
= db_
->Put(write_options
,
344 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
347 LOG(ERROR
) << "LevelDB put failed: " << status
.ToString();
348 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
351 void GCMStoreImpl::Backend::RemoveRegistration(const std::string
& app_id
,
352 const UpdateCallback
& callback
) {
354 LOG(ERROR
) << "GCMStore db doesn't exist.";
355 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
358 leveldb::WriteOptions write_options
;
359 write_options
.sync
= true;
361 leveldb::Status status
=
362 db_
->Delete(write_options
, MakeSlice(MakeRegistrationKey(app_id
)));
364 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
367 LOG(ERROR
) << "LevelDB remove failed: " << status
.ToString();
368 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
371 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string
& persistent_id
,
372 const UpdateCallback
& callback
) {
373 DVLOG(1) << "Saving incoming message with id " << persistent_id
;
375 LOG(ERROR
) << "GCMStore db doesn't exist.";
376 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
380 leveldb::WriteOptions write_options
;
381 write_options
.sync
= true;
383 std::string key
= MakeIncomingKey(persistent_id
);
384 const leveldb::Status s
= db_
->Put(write_options
,
386 MakeSlice(persistent_id
));
388 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
391 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
392 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
395 void GCMStoreImpl::Backend::RemoveIncomingMessages(
396 const PersistentIdList
& persistent_ids
,
397 const UpdateCallback
& callback
) {
399 LOG(ERROR
) << "GCMStore db doesn't exist.";
400 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
403 leveldb::WriteOptions write_options
;
404 write_options
.sync
= true;
407 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
408 iter
!= persistent_ids
.end();
410 DVLOG(1) << "Removing incoming message with id " << *iter
;
411 std::string key
= MakeIncomingKey(*iter
);
412 s
= db_
->Delete(write_options
, MakeSlice(key
));
417 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
420 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
421 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
424 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string
& persistent_id
,
425 const MCSMessage
& message
,
426 const UpdateCallback
& callback
) {
427 DVLOG(1) << "Saving outgoing message with id " << persistent_id
;
429 LOG(ERROR
) << "GCMStore db doesn't exist.";
430 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
433 leveldb::WriteOptions write_options
;
434 write_options
.sync
= true;
437 static_cast<char>(message
.tag()) + message
.SerializeAsString();
438 std::string key
= MakeOutgoingKey(persistent_id
);
439 const leveldb::Status s
= db_
->Put(write_options
,
443 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
446 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
447 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
450 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
451 const PersistentIdList
& persistent_ids
,
452 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
455 LOG(ERROR
) << "GCMStore db doesn't exist.";
456 foreground_task_runner_
->PostTask(FROM_HERE
,
459 AppIdToMessageCountMap()));
462 leveldb::ReadOptions read_options
;
463 leveldb::WriteOptions write_options
;
464 write_options
.sync
= true;
466 AppIdToMessageCountMap removed_message_counts
;
469 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
470 iter
!= persistent_ids
.end();
472 DVLOG(1) << "Removing outgoing message with id " << *iter
;
473 std::string outgoing_message
;
474 std::string key
= MakeOutgoingKey(*iter
);
475 s
= db_
->Get(read_options
,
480 mcs_proto::DataMessageStanza data_message
;
481 // Skip the initial tag byte and parse the rest to extract the message.
482 if (data_message
.ParseFromString(outgoing_message
.substr(1))) {
483 DCHECK(!data_message
.category().empty());
484 if (removed_message_counts
.count(data_message
.category()) != 0)
485 removed_message_counts
[data_message
.category()]++;
487 removed_message_counts
[data_message
.category()] = 1;
489 DVLOG(1) << "Removing outgoing message with id " << *iter
;
490 s
= db_
->Delete(write_options
, MakeSlice(key
));
495 foreground_task_runner_
->PostTask(FROM_HERE
,
498 removed_message_counts
));
501 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
502 foreground_task_runner_
->PostTask(FROM_HERE
,
505 AppIdToMessageCountMap()));
508 void GCMStoreImpl::Backend::SetLastCheckinInfo(
509 const base::Time
& time
,
510 const std::set
<std::string
>& accounts
,
511 const UpdateCallback
& callback
) {
512 leveldb::WriteBatch write_batch
;
514 int64 last_checkin_time_internal
= time
.ToInternalValue();
515 write_batch
.Put(MakeSlice(kLastCheckinTimeKey
),
516 MakeSlice(base::Int64ToString(last_checkin_time_internal
)));
518 std::string serialized_accounts
;
519 for (std::set
<std::string
>::iterator iter
= accounts
.begin();
520 iter
!= accounts
.end();
522 serialized_accounts
+= *iter
;
523 serialized_accounts
+= ",";
525 if (!serialized_accounts
.empty())
526 serialized_accounts
.erase(serialized_accounts
.length() - 1);
528 write_batch
.Put(MakeSlice(kLastCheckinAccountsKey
),
529 MakeSlice(serialized_accounts
));
531 leveldb::WriteOptions write_options
;
532 write_options
.sync
= true;
533 const leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
536 LOG(ERROR
) << "LevelDB set last checkin info failed: " << s
.ToString();
537 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
540 void GCMStoreImpl::Backend::SetGServicesSettings(
541 const std::map
<std::string
, std::string
>& settings
,
542 const std::string
& settings_digest
,
543 const UpdateCallback
& callback
) {
544 leveldb::WriteBatch write_batch
;
546 // Remove all existing settings.
547 leveldb::ReadOptions read_options
;
548 read_options
.verify_checksums
= true;
549 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
550 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
551 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
553 write_batch
.Delete(iter
->key());
556 // Add the new settings.
557 for (std::map
<std::string
, std::string
>::const_iterator iter
=
559 iter
!= settings
.end(); ++iter
) {
560 write_batch
.Put(MakeSlice(MakeGServiceSettingKey(iter
->first
)),
561 MakeSlice(iter
->second
));
564 // Update the settings digest.
565 write_batch
.Put(MakeSlice(kGServiceSettingsDigestKey
),
566 MakeSlice(settings_digest
));
568 // Write it all in a batch.
569 leveldb::WriteOptions write_options
;
570 write_options
.sync
= true;
572 leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
574 LOG(ERROR
) << "LevelDB GService Settings update failed: " << s
.ToString();
575 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
578 void GCMStoreImpl::Backend::AddAccountMapping(
579 const AccountMapping
& account_mapping
,
580 const UpdateCallback
& callback
) {
581 DVLOG(1) << "Saving account info for account with email: "
582 << account_mapping
.email
;
584 LOG(ERROR
) << "GCMStore db doesn't exist.";
585 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
589 leveldb::WriteOptions write_options
;
590 write_options
.sync
= true;
592 std::string data
= account_mapping
.SerializeAsString();
593 std::string key
= MakeAccountKey(account_mapping
.account_id
);
594 const leveldb::Status s
=
595 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(data
));
597 LOG(ERROR
) << "LevelDB adding account mapping failed: " << s
.ToString();
598 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
601 void GCMStoreImpl::Backend::RemoveAccountMapping(
602 const std::string
& account_id
,
603 const UpdateCallback
& callback
) {
605 LOG(ERROR
) << "GCMStore db doesn't exist.";
606 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
610 leveldb::WriteOptions write_options
;
611 write_options
.sync
= true;
614 db_
->Delete(write_options
, MakeSlice(MakeAccountKey(account_id
)));
617 LOG(ERROR
) << "LevelDB removal of account mapping failed: " << s
.ToString();
618 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
621 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64
* android_id
,
622 uint64
* security_token
) {
623 leveldb::ReadOptions read_options
;
624 read_options
.verify_checksums
= true;
627 leveldb::Status s
= db_
->Get(read_options
, MakeSlice(kDeviceAIDKey
), &result
);
629 if (!base::StringToUint64(result
, android_id
)) {
630 LOG(ERROR
) << "Failed to restore device id.";
634 s
= db_
->Get(read_options
, MakeSlice(kDeviceTokenKey
), &result
);
637 std::string decrypted_token
;
638 encryptor_
->DecryptString(result
, &decrypted_token
);
639 if (!base::StringToUint64(decrypted_token
, security_token
)) {
640 LOG(ERROR
) << "Failed to restore security token.";
646 if (s
.IsNotFound()) {
647 DVLOG(1) << "No credentials found.";
651 LOG(ERROR
) << "Error reading credentials from store.";
655 bool GCMStoreImpl::Backend::LoadRegistrations(
656 RegistrationInfoMap
* registrations
) {
657 leveldb::ReadOptions read_options
;
658 read_options
.verify_checksums
= true;
660 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
661 for (iter
->Seek(MakeSlice(kRegistrationKeyStart
));
662 iter
->Valid() && iter
->key().ToString() < kRegistrationKeyEnd
;
664 leveldb::Slice s
= iter
->value();
666 LOG(ERROR
) << "Error reading registration with key " << s
.ToString();
669 std::string app_id
= ParseRegistrationKey(iter
->key().ToString());
670 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
671 if (!registration
->ParseFromString(iter
->value().ToString())) {
672 LOG(ERROR
) << "Failed to parse registration with app id " << app_id
;
675 DVLOG(1) << "Found registration with app id " << app_id
;
676 (*registrations
)[app_id
] = registration
;
682 bool GCMStoreImpl::Backend::LoadIncomingMessages(
683 std::vector
<std::string
>* incoming_messages
) {
684 leveldb::ReadOptions read_options
;
685 read_options
.verify_checksums
= true;
687 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
688 for (iter
->Seek(MakeSlice(kIncomingMsgKeyStart
));
689 iter
->Valid() && iter
->key().ToString() < kIncomingMsgKeyEnd
;
691 leveldb::Slice s
= iter
->value();
693 LOG(ERROR
) << "Error reading incoming message with key "
694 << iter
->key().ToString();
697 DVLOG(1) << "Found incoming message with id " << s
.ToString();
698 incoming_messages
->push_back(s
.ToString());
704 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
705 OutgoingMessageMap
* outgoing_messages
) {
706 leveldb::ReadOptions read_options
;
707 read_options
.verify_checksums
= true;
709 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
710 for (iter
->Seek(MakeSlice(kOutgoingMsgKeyStart
));
711 iter
->Valid() && iter
->key().ToString() < kOutgoingMsgKeyEnd
;
713 leveldb::Slice s
= iter
->value();
715 LOG(ERROR
) << "Error reading incoming message with key " << s
.ToString();
718 uint8 tag
= iter
->value().data()[0];
719 std::string id
= ParseOutgoingKey(iter
->key().ToString());
720 scoped_ptr
<google::protobuf::MessageLite
> message(
721 BuildProtobufFromTag(tag
));
722 if (!message
.get() ||
723 !message
->ParseFromString(iter
->value().ToString().substr(1))) {
724 LOG(ERROR
) << "Failed to parse outgoing message with id " << id
725 << " and tag " << tag
;
728 DVLOG(1) << "Found outgoing message with id " << id
<< " of type "
729 << base::IntToString(tag
);
730 (*outgoing_messages
)[id
] = make_linked_ptr(message
.release());
736 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
737 base::Time
* last_checkin_time
,
738 std::set
<std::string
>* accounts
) {
739 leveldb::ReadOptions read_options
;
740 read_options
.verify_checksums
= true;
743 leveldb::Status s
= db_
->Get(read_options
,
744 MakeSlice(kLastCheckinTimeKey
),
746 int64 time_internal
= 0LL;
747 if (s
.ok() && !base::StringToInt64(result
, &time_internal
))
748 LOG(ERROR
) << "Failed to restore last checkin time. Using default = 0.";
750 // In case we cannot read last checkin time, we default it to 0, as we don't
751 // want that situation to cause the whole load to fail.
752 *last_checkin_time
= base::Time::FromInternalValue(time_internal
);
755 s
= db_
->Get(read_options
, MakeSlice(kLastCheckinAccountsKey
), &result
);
757 DVLOG(1) << "No accounts where stored during last run.";
759 base::StringTokenizer
t(result
, ",");
761 accounts
->insert(t
.token());
766 bool GCMStoreImpl::Backend::LoadGServicesSettings(
767 std::map
<std::string
, std::string
>* settings
,
768 std::string
* digest
) {
769 leveldb::ReadOptions read_options
;
770 read_options
.verify_checksums
= true;
772 // Load all of the GServices settings.
773 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
774 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
775 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
777 std::string value
= iter
->value().ToString();
779 LOG(ERROR
) << "Error reading GService Settings " << value
;
782 std::string id
= ParseGServiceSettingKey(iter
->key().ToString());
783 (*settings
)[id
] = value
;
784 DVLOG(1) << "Found G Service setting with key: " << id
785 << ", and value: " << value
;
788 // Load the settings digest. It's ok if it is empty.
789 db_
->Get(read_options
, MakeSlice(kGServiceSettingsDigestKey
), digest
);
794 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
795 AccountMappingMap
* account_mappings
) {
796 leveldb::ReadOptions read_options
;
797 read_options
.verify_checksums
= true;
799 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
800 for (iter
->Seek(MakeSlice(kAccountKeyStart
));
801 iter
->Valid() && iter
->key().ToString() < kAccountKeyEnd
;
803 AccountMapping account_mapping
;
804 account_mapping
.account_id
= ParseAccountKey(iter
->key().ToString());
805 if (!account_mapping
.ParseFromString(iter
->value().ToString())) {
806 DVLOG(1) << "Failed to parse account info with ID: "
807 << account_mapping
.account_id
;
810 DVLOG(1) << "Found account mapping with ID: " << account_mapping
.account_id
;
811 (*account_mappings
)[account_mapping
.account_id
] = account_mapping
;
817 GCMStoreImpl::GCMStoreImpl(
818 const base::FilePath
& path
,
819 scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
,
820 scoped_ptr
<Encryptor
> encryptor
)
821 : backend_(new Backend(path
,
822 base::MessageLoopProxy::current(),
824 blocking_task_runner_(blocking_task_runner
),
825 weak_ptr_factory_(this) {
828 GCMStoreImpl::~GCMStoreImpl() {}
830 void GCMStoreImpl::Load(const LoadCallback
& callback
) {
831 blocking_task_runner_
->PostTask(
833 base::Bind(&GCMStoreImpl::Backend::Load
,
835 base::Bind(&GCMStoreImpl::LoadContinuation
,
836 weak_ptr_factory_
.GetWeakPtr(),
840 void GCMStoreImpl::Close() {
841 weak_ptr_factory_
.InvalidateWeakPtrs();
842 app_message_counts_
.clear();
843 blocking_task_runner_
->PostTask(
845 base::Bind(&GCMStoreImpl::Backend::Close
, backend_
));
848 void GCMStoreImpl::Destroy(const UpdateCallback
& callback
) {
849 blocking_task_runner_
->PostTask(
851 base::Bind(&GCMStoreImpl::Backend::Destroy
, backend_
, callback
));
854 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id
,
855 uint64 device_security_token
,
856 const UpdateCallback
& callback
) {
857 blocking_task_runner_
->PostTask(
859 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials
,
862 device_security_token
,
866 void GCMStoreImpl::AddRegistration(
867 const std::string
& app_id
,
868 const linked_ptr
<RegistrationInfo
>& registration
,
869 const UpdateCallback
& callback
) {
870 blocking_task_runner_
->PostTask(
872 base::Bind(&GCMStoreImpl::Backend::AddRegistration
,
879 void GCMStoreImpl::RemoveRegistration(const std::string
& app_id
,
880 const UpdateCallback
& callback
) {
881 blocking_task_runner_
->PostTask(
883 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration
,
889 void GCMStoreImpl::AddIncomingMessage(const std::string
& persistent_id
,
890 const UpdateCallback
& callback
) {
891 blocking_task_runner_
->PostTask(
893 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage
,
899 void GCMStoreImpl::RemoveIncomingMessage(const std::string
& persistent_id
,
900 const UpdateCallback
& callback
) {
901 blocking_task_runner_
->PostTask(
903 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
905 PersistentIdList(1, persistent_id
),
909 void GCMStoreImpl::RemoveIncomingMessages(
910 const PersistentIdList
& persistent_ids
,
911 const UpdateCallback
& callback
) {
912 blocking_task_runner_
->PostTask(
914 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
920 bool GCMStoreImpl::AddOutgoingMessage(const std::string
& persistent_id
,
921 const MCSMessage
& message
,
922 const UpdateCallback
& callback
) {
923 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
924 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
925 &message
.GetProtobuf())->category();
926 DCHECK(!app_id
.empty());
927 if (app_message_counts_
.count(app_id
) == 0)
928 app_message_counts_
[app_id
] = 0;
929 if (app_message_counts_
[app_id
] < kMessagesPerAppLimit
) {
930 app_message_counts_
[app_id
]++;
932 blocking_task_runner_
->PostTask(
934 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
938 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation
,
939 weak_ptr_factory_
.GetWeakPtr(),
947 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string
& persistent_id
,
948 const MCSMessage
& message
,
949 const UpdateCallback
& callback
) {
950 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
951 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
952 &message
.GetProtobuf())->category();
953 DCHECK(!app_id
.empty());
954 // There should already be pending messages for this app.
955 DCHECK(app_message_counts_
.count(app_id
));
956 // TODO(zea): consider verifying the specific message already exists.
957 blocking_task_runner_
->PostTask(
959 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
966 void GCMStoreImpl::RemoveOutgoingMessage(const std::string
& persistent_id
,
967 const UpdateCallback
& callback
) {
968 blocking_task_runner_
->PostTask(
970 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
972 PersistentIdList(1, persistent_id
),
973 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
974 weak_ptr_factory_
.GetWeakPtr(),
978 void GCMStoreImpl::RemoveOutgoingMessages(
979 const PersistentIdList
& persistent_ids
,
980 const UpdateCallback
& callback
) {
981 blocking_task_runner_
->PostTask(
983 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
986 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
987 weak_ptr_factory_
.GetWeakPtr(),
991 void GCMStoreImpl::SetLastCheckinInfo(const base::Time
& time
,
992 const std::set
<std::string
>& accounts
,
993 const UpdateCallback
& callback
) {
994 blocking_task_runner_
->PostTask(
996 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo
,
1003 void GCMStoreImpl::SetGServicesSettings(
1004 const std::map
<std::string
, std::string
>& settings
,
1005 const std::string
& digest
,
1006 const UpdateCallback
& callback
) {
1007 blocking_task_runner_
->PostTask(
1009 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings
,
1016 void GCMStoreImpl::AddAccountMapping(const AccountMapping
& account_mapping
,
1017 const UpdateCallback
& callback
) {
1018 blocking_task_runner_
->PostTask(
1020 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping
,
1026 void GCMStoreImpl::RemoveAccountMapping(const std::string
& account_id
,
1027 const UpdateCallback
& callback
) {
1028 blocking_task_runner_
->PostTask(
1030 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping
,
1036 void GCMStoreImpl::LoadContinuation(const LoadCallback
& callback
,
1037 scoped_ptr
<LoadResult
> result
) {
1038 if (!result
->success
) {
1039 callback
.Run(result
.Pass());
1042 int num_throttled_apps
= 0;
1043 for (OutgoingMessageMap::const_iterator
1044 iter
= result
->outgoing_messages
.begin();
1045 iter
!= result
->outgoing_messages
.end(); ++iter
) {
1046 const mcs_proto::DataMessageStanza
* data_message
=
1047 reinterpret_cast<mcs_proto::DataMessageStanza
*>(iter
->second
.get());
1048 DCHECK(!data_message
->category().empty());
1049 if (app_message_counts_
.count(data_message
->category()) == 0)
1050 app_message_counts_
[data_message
->category()] = 1;
1052 app_message_counts_
[data_message
->category()]++;
1053 if (app_message_counts_
[data_message
->category()] == kMessagesPerAppLimit
)
1054 num_throttled_apps
++;
1056 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps
);
1057 callback
.Run(result
.Pass());
1060 void GCMStoreImpl::AddOutgoingMessageContinuation(
1061 const UpdateCallback
& callback
,
1062 const std::string
& app_id
,
1065 DCHECK(app_message_counts_
[app_id
] > 0);
1066 app_message_counts_
[app_id
]--;
1068 callback
.Run(success
);
1071 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1072 const UpdateCallback
& callback
,
1074 const AppIdToMessageCountMap
& removed_message_counts
) {
1076 callback
.Run(false);
1079 for (AppIdToMessageCountMap::const_iterator iter
=
1080 removed_message_counts
.begin();
1081 iter
!= removed_message_counts
.end(); ++iter
) {
1082 DCHECK_NE(app_message_counts_
.count(iter
->first
), 0U);
1083 app_message_counts_
[iter
->first
] -= iter
->second
;
1084 DCHECK_GE(app_message_counts_
[iter
->first
], 0);