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/time/time.h"
20 #include "base/tracked_objects.h"
21 #include "google_apis/gcm/base/encryptor.h"
22 #include "google_apis/gcm/base/mcs_message.h"
23 #include "google_apis/gcm/base/mcs_util.h"
24 #include "google_apis/gcm/protocol/mcs.pb.h"
25 #include "third_party/leveldatabase/src/include/leveldb/db.h"
26 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
32 // Limit to the number of outstanding messages per app.
33 const int kMessagesPerAppLimit = 20;
35 // ---- LevelDB keys. ----
36 // Key for this device's android id.
37 const char kDeviceAIDKey[] = "device_aid_key";
38 // Key for this device's android security token.
39 const char kDeviceTokenKey[] = "device_token_key";
40 // Lowest lexicographically ordered app ids.
41 // Used for prefixing app id.
42 const char kRegistrationKeyStart[] = "reg1-";
43 // Key guaranteed to be higher than all app ids.
44 // Used for limiting iteration.
45 const char kRegistrationKeyEnd[] = "reg2-";
46 // Lowest lexicographically ordered incoming message key.
47 // Used for prefixing messages.
48 const char kIncomingMsgKeyStart[] = "incoming1-";
49 // Key guaranteed to be higher than all incoming message keys.
50 // Used for limiting iteration.
51 const char kIncomingMsgKeyEnd[] = "incoming2-";
52 // Lowest lexicographically ordered outgoing message key.
53 // Used for prefixing outgoing messages.
54 const char kOutgoingMsgKeyStart[] = "outgoing1-";
55 // Key guaranteed to be higher than all outgoing message keys.
56 // Used for limiting iteration.
57 const char kOutgoingMsgKeyEnd[] = "outgoing2-";
58 // Lowest lexicographically ordered G-service settings key.
59 // Used for prefixing G-services settings.
60 const char kGServiceSettingKeyStart[] = "gservice1-";
61 // Key guaranteed to be higher than all G-services settings keys.
62 // Used for limiting iteration.
63 const char kGServiceSettingKeyEnd[] = "gservice2-";
64 // Key for digest of the last G-services settings update.
65 const char kGServiceSettingsDigestKey[] = "gservices_digest";
66 // Key used to timestamp last checkin (marked with G services settings update).
67 const char kLastCheckinTimeKey[] = "last_checkin_time";
69 std::string MakeRegistrationKey(const std::string& app_id) {
70 return kRegistrationKeyStart + app_id;
73 std::string ParseRegistrationKey(const std::string& key) {
74 return key.substr(arraysize(kRegistrationKeyStart) - 1);
77 std::string MakeIncomingKey(const std::string& persistent_id) {
78 return kIncomingMsgKeyStart + persistent_id;
81 std::string MakeOutgoingKey(const std::string& persistent_id) {
82 return kOutgoingMsgKeyStart + persistent_id;
85 std::string ParseOutgoingKey(const std::string& key) {
86 return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
89 std::string MakeGServiceSettingKey(const std::string& setting_name) {
90 return kGServiceSettingKeyStart + setting_name;
93 std::string ParseGServiceSettingKey(const std::string& key) {
94 return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
97 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
99 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
100 leveldb::Slice MakeSlice(const base::StringPiece& s) {
101 return leveldb::Slice(s.begin(), s.size());
106 class GCMStoreImpl::Backend
107 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
109 Backend(const base::FilePath& path,
110 scoped_refptr<base::SequencedTaskRunner> foreground_runner,
111 scoped_ptr<Encryptor> encryptor);
113 // Blocking implementations of GCMStoreImpl methods.
114 void Load(const LoadCallback& callback);
116 void Destroy(const UpdateCallback& callback);
117 void SetDeviceCredentials(uint64 device_android_id,
118 uint64 device_security_token,
119 const UpdateCallback& callback);
120 void AddRegistration(const std::string& app_id,
121 const linked_ptr<RegistrationInfo>& registration,
122 const UpdateCallback& callback);
123 void RemoveRegistration(const std::string& app_id,
124 const UpdateCallback& callback);
125 void AddIncomingMessage(const std::string& persistent_id,
126 const UpdateCallback& callback);
127 void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
128 const UpdateCallback& callback);
129 void AddOutgoingMessage(const std::string& persistent_id,
130 const MCSMessage& message,
131 const UpdateCallback& callback);
132 void RemoveOutgoingMessages(
133 const PersistentIdList& persistent_ids,
134 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
136 void AddUserSerialNumber(const std::string& username,
138 const UpdateCallback& callback);
139 void RemoveUserSerialNumber(const std::string& username,
140 const UpdateCallback& callback);
141 void SetLastCheckinTime(const base::Time& last_checkin_time,
142 const UpdateCallback& callback);
143 void SetGServicesSettings(
144 const std::map<std::string, std::string>& settings,
145 const std::string& digest,
146 const UpdateCallback& callback);
149 friend class base::RefCountedThreadSafe<Backend>;
152 bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
153 bool LoadRegistrations(RegistrationInfoMap* registrations);
154 bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
155 bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
156 bool LoadLastCheckinTime(base::Time* last_checkin_time);
157 bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
158 std::string* digest);
160 const base::FilePath path_;
161 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
162 scoped_ptr<Encryptor> encryptor_;
164 scoped_ptr<leveldb::DB> db_;
167 GCMStoreImpl::Backend::Backend(
168 const base::FilePath& path,
169 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
170 scoped_ptr<Encryptor> encryptor)
172 foreground_task_runner_(foreground_task_runner),
173 encryptor_(encryptor.Pass()) {
176 GCMStoreImpl::Backend::~Backend() {}
178 void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
179 scoped_ptr<LoadResult> result(new LoadResult());
181 LOG(ERROR) << "Attempting to reload open database.";
182 foreground_task_runner_->PostTask(FROM_HERE,
184 base::Passed(&result)));
188 leveldb::Options options;
189 options.create_if_missing = true;
191 leveldb::Status status =
192 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
193 UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status.ok());
195 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
196 << status.ToString();
197 foreground_task_runner_->PostTask(FROM_HERE,
199 base::Passed(&result)));
204 if (!LoadDeviceCredentials(&result->device_android_id,
205 &result->device_security_token) ||
206 !LoadRegistrations(&result->registrations) ||
207 !LoadIncomingMessages(&result->incoming_messages) ||
208 !LoadOutgoingMessages(&result->outgoing_messages) ||
209 !LoadLastCheckinTime(&result->last_checkin_time) ||
210 !LoadGServicesSettings(&result->gservices_settings,
211 &result->gservices_digest)) {
212 result->device_android_id = 0;
213 result->device_security_token = 0;
214 result->registrations.clear();
215 result->incoming_messages.clear();
216 result->outgoing_messages.clear();
217 result->gservices_settings.clear();
218 result->gservices_digest.clear();
219 result->last_checkin_time = base::Time::FromInternalValue(0LL);
220 foreground_task_runner_->PostTask(FROM_HERE,
222 base::Passed(&result)));
226 // Only record histograms if GCM had already been set up for this device.
227 if (result->device_android_id != 0 && result->device_security_token != 0) {
229 if (base::GetFileSize(path_, &file_size)) {
230 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
231 static_cast<int>(file_size / 1024));
233 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
234 result->registrations.size());
235 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
236 result->outgoing_messages.size());
237 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
238 result->incoming_messages.size());
241 DVLOG(1) << "Succeeded in loading " << result->registrations.size()
242 << " registrations, "
243 << result->incoming_messages.size()
244 << " unacknowledged incoming messages and "
245 << result->outgoing_messages.size()
246 << " unacknowledged outgoing messages.";
247 result->success = true;
248 foreground_task_runner_->PostTask(FROM_HERE,
250 base::Passed(&result)));
254 void GCMStoreImpl::Backend::Close() {
255 DVLOG(1) << "Closing GCM store.";
259 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
260 DVLOG(1) << "Destroying GCM store.";
262 const leveldb::Status s =
263 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
265 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
268 LOG(ERROR) << "Destroy failed: " << s.ToString();
269 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
272 void GCMStoreImpl::Backend::SetDeviceCredentials(
273 uint64 device_android_id,
274 uint64 device_security_token,
275 const UpdateCallback& callback) {
276 DVLOG(1) << "Saving device credentials with AID " << device_android_id;
278 LOG(ERROR) << "GCMStore db doesn't exist.";
279 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
283 leveldb::WriteOptions write_options;
284 write_options.sync = true;
286 std::string encrypted_token;
287 encryptor_->EncryptString(base::Uint64ToString(device_security_token),
289 std::string android_id_str = base::Uint64ToString(device_android_id);
291 db_->Put(write_options,
292 MakeSlice(kDeviceAIDKey),
293 MakeSlice(android_id_str));
296 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
299 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
302 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
303 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
306 void GCMStoreImpl::Backend::AddRegistration(
307 const std::string& app_id,
308 const linked_ptr<RegistrationInfo>& registration,
309 const UpdateCallback& callback) {
310 DVLOG(1) << "Saving registration info for app: " << app_id;
312 LOG(ERROR) << "GCMStore db doesn't exist.";
313 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
316 leveldb::WriteOptions write_options;
317 write_options.sync = true;
319 std::string key = MakeRegistrationKey(app_id);
320 std::string value = registration->SerializeAsString();
321 const leveldb::Status status = db_->Put(write_options,
325 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
328 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
329 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
332 void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id,
333 const UpdateCallback& callback) {
335 LOG(ERROR) << "GCMStore db doesn't exist.";
336 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
339 leveldb::WriteOptions write_options;
340 write_options.sync = true;
342 leveldb::Status status =
343 db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
345 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
348 LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
349 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
352 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
353 const UpdateCallback& callback) {
354 DVLOG(1) << "Saving incoming message with id " << persistent_id;
356 LOG(ERROR) << "GCMStore db doesn't exist.";
357 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
361 leveldb::WriteOptions write_options;
362 write_options.sync = true;
364 std::string key = MakeIncomingKey(persistent_id);
365 const leveldb::Status s = db_->Put(write_options,
367 MakeSlice(persistent_id));
369 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
372 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
373 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
376 void GCMStoreImpl::Backend::RemoveIncomingMessages(
377 const PersistentIdList& persistent_ids,
378 const UpdateCallback& callback) {
380 LOG(ERROR) << "GCMStore db doesn't exist.";
381 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
384 leveldb::WriteOptions write_options;
385 write_options.sync = true;
388 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
389 iter != persistent_ids.end();
391 DVLOG(1) << "Removing incoming message with id " << *iter;
392 std::string key = MakeIncomingKey(*iter);
393 s = db_->Delete(write_options, MakeSlice(key));
398 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
401 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
402 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
405 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
406 const MCSMessage& message,
407 const UpdateCallback& callback) {
408 DVLOG(1) << "Saving outgoing message with id " << persistent_id;
410 LOG(ERROR) << "GCMStore db doesn't exist.";
411 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
414 leveldb::WriteOptions write_options;
415 write_options.sync = true;
418 static_cast<char>(message.tag()) + message.SerializeAsString();
419 std::string key = MakeOutgoingKey(persistent_id);
420 const leveldb::Status s = db_->Put(write_options,
424 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
427 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
428 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
431 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
432 const PersistentIdList& persistent_ids,
433 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
436 LOG(ERROR) << "GCMStore db doesn't exist.";
437 foreground_task_runner_->PostTask(FROM_HERE,
440 AppIdToMessageCountMap()));
443 leveldb::ReadOptions read_options;
444 leveldb::WriteOptions write_options;
445 write_options.sync = true;
447 AppIdToMessageCountMap removed_message_counts;
450 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
451 iter != persistent_ids.end();
453 DVLOG(1) << "Removing outgoing message with id " << *iter;
454 std::string outgoing_message;
455 std::string key = MakeOutgoingKey(*iter);
456 s = db_->Get(read_options,
461 mcs_proto::DataMessageStanza data_message;
462 // Skip the initial tag byte and parse the rest to extract the message.
463 if (data_message.ParseFromString(outgoing_message.substr(1))) {
464 DCHECK(!data_message.category().empty());
465 if (removed_message_counts.count(data_message.category()) != 0)
466 removed_message_counts[data_message.category()]++;
468 removed_message_counts[data_message.category()] = 1;
470 DVLOG(1) << "Removing outgoing message with id " << *iter;
471 s = db_->Delete(write_options, MakeSlice(key));
476 foreground_task_runner_->PostTask(FROM_HERE,
479 removed_message_counts));
482 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
483 foreground_task_runner_->PostTask(FROM_HERE,
486 AppIdToMessageCountMap()));
489 void GCMStoreImpl::Backend::SetLastCheckinTime(
490 const base::Time& last_checkin_time,
491 const UpdateCallback& callback) {
492 leveldb::WriteOptions write_options;
493 write_options.sync = true;
495 int64 last_checkin_time_internal = last_checkin_time.ToInternalValue();
496 const leveldb::Status s =
497 db_->Put(write_options,
498 MakeSlice(kLastCheckinTimeKey),
499 MakeSlice(base::Int64ToString(last_checkin_time_internal)));
502 LOG(ERROR) << "LevelDB set last checkin time failed: " << s.ToString();
503 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
506 void GCMStoreImpl::Backend::SetGServicesSettings(
507 const std::map<std::string, std::string>& settings,
508 const std::string& settings_digest,
509 const UpdateCallback& callback) {
510 leveldb::WriteBatch write_batch;
512 // Remove all existing settings.
513 leveldb::ReadOptions read_options;
514 read_options.verify_checksums = true;
515 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
516 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
517 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
519 write_batch.Delete(iter->key());
522 // Add the new settings.
523 for (std::map<std::string, std::string>::const_iterator iter =
525 iter != settings.end(); ++iter) {
526 write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
527 MakeSlice(iter->second));
530 // Update the settings digest.
531 write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
532 MakeSlice(settings_digest));
534 // Write it all in a batch.
535 leveldb::WriteOptions write_options;
536 write_options.sync = true;
538 leveldb::Status s = db_->Write(write_options, &write_batch);
540 LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
541 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
544 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
545 uint64* security_token) {
546 leveldb::ReadOptions read_options;
547 read_options.verify_checksums = true;
550 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
552 if (!base::StringToUint64(result, android_id)) {
553 LOG(ERROR) << "Failed to restore device id.";
557 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
560 std::string decrypted_token;
561 encryptor_->DecryptString(result, &decrypted_token);
562 if (!base::StringToUint64(decrypted_token, security_token)) {
563 LOG(ERROR) << "Failed to restore security token.";
569 if (s.IsNotFound()) {
570 DVLOG(1) << "No credentials found.";
574 LOG(ERROR) << "Error reading credentials from store.";
578 bool GCMStoreImpl::Backend::LoadRegistrations(
579 RegistrationInfoMap* registrations) {
580 leveldb::ReadOptions read_options;
581 read_options.verify_checksums = true;
583 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
584 for (iter->Seek(MakeSlice(kRegistrationKeyStart));
585 iter->Valid() && iter->key().ToString() < kRegistrationKeyEnd;
587 leveldb::Slice s = iter->value();
589 LOG(ERROR) << "Error reading registration with key " << s.ToString();
592 std::string app_id = ParseRegistrationKey(iter->key().ToString());
593 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
594 if (!registration->ParseFromString(iter->value().ToString())) {
595 LOG(ERROR) << "Failed to parse registration with app id " << app_id;
598 DVLOG(1) << "Found registration with app id " << app_id;
599 (*registrations)[app_id] = registration;
605 bool GCMStoreImpl::Backend::LoadIncomingMessages(
606 std::vector<std::string>* incoming_messages) {
607 leveldb::ReadOptions read_options;
608 read_options.verify_checksums = true;
610 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
611 for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
612 iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
614 leveldb::Slice s = iter->value();
616 LOG(ERROR) << "Error reading incoming message with key "
617 << iter->key().ToString();
620 DVLOG(1) << "Found incoming message with id " << s.ToString();
621 incoming_messages->push_back(s.ToString());
627 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
628 OutgoingMessageMap* outgoing_messages) {
629 leveldb::ReadOptions read_options;
630 read_options.verify_checksums = true;
632 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
633 for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
634 iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
636 leveldb::Slice s = iter->value();
638 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
641 uint8 tag = iter->value().data()[0];
642 std::string id = ParseOutgoingKey(iter->key().ToString());
643 scoped_ptr<google::protobuf::MessageLite> message(
644 BuildProtobufFromTag(tag));
645 if (!message.get() ||
646 !message->ParseFromString(iter->value().ToString().substr(1))) {
647 LOG(ERROR) << "Failed to parse outgoing message with id " << id
648 << " and tag " << tag;
651 DVLOG(1) << "Found outgoing message with id " << id << " of type "
652 << base::IntToString(tag);
653 (*outgoing_messages)[id] = make_linked_ptr(message.release());
659 bool GCMStoreImpl::Backend::LoadLastCheckinTime(
660 base::Time* last_checkin_time) {
661 leveldb::ReadOptions read_options;
662 read_options.verify_checksums = true;
665 leveldb::Status s = db_->Get(read_options,
666 MakeSlice(kLastCheckinTimeKey),
668 int64 time_internal = 0LL;
669 if (s.ok() && !base::StringToInt64(result, &time_internal))
670 LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
672 // In case we cannot read last checkin time, we default it to 0, as we don't
673 // want that situation to cause the whole load to fail.
674 *last_checkin_time = base::Time::FromInternalValue(time_internal);
679 bool GCMStoreImpl::Backend::LoadGServicesSettings(
680 std::map<std::string, std::string>* settings,
681 std::string* digest) {
682 leveldb::ReadOptions read_options;
683 read_options.verify_checksums = true;
685 // Load all of the GServices settings.
686 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
687 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
688 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
690 std::string value = iter->value().ToString();
692 LOG(ERROR) << "Error reading GService Settings " << value;
695 std::string id = ParseGServiceSettingKey(iter->key().ToString());
696 (*settings)[id] = value;
697 DVLOG(1) << "Found G Service setting with key: " << id
698 << ", and value: " << value;
701 // Load the settings digest. It's ok if it is empty.
702 db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
707 GCMStoreImpl::GCMStoreImpl(
708 const base::FilePath& path,
709 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
710 scoped_ptr<Encryptor> encryptor)
711 : backend_(new Backend(path,
712 base::MessageLoopProxy::current(),
714 blocking_task_runner_(blocking_task_runner),
715 weak_ptr_factory_(this) {
718 GCMStoreImpl::~GCMStoreImpl() {}
720 void GCMStoreImpl::Load(const LoadCallback& callback) {
721 blocking_task_runner_->PostTask(
723 base::Bind(&GCMStoreImpl::Backend::Load,
725 base::Bind(&GCMStoreImpl::LoadContinuation,
726 weak_ptr_factory_.GetWeakPtr(),
730 void GCMStoreImpl::Close() {
731 weak_ptr_factory_.InvalidateWeakPtrs();
732 app_message_counts_.clear();
733 blocking_task_runner_->PostTask(
735 base::Bind(&GCMStoreImpl::Backend::Close, backend_));
738 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
739 blocking_task_runner_->PostTask(
741 base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
744 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
745 uint64 device_security_token,
746 const UpdateCallback& callback) {
747 blocking_task_runner_->PostTask(
749 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
752 device_security_token,
756 void GCMStoreImpl::AddRegistration(
757 const std::string& app_id,
758 const linked_ptr<RegistrationInfo>& registration,
759 const UpdateCallback& callback) {
760 blocking_task_runner_->PostTask(
762 base::Bind(&GCMStoreImpl::Backend::AddRegistration,
769 void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
770 const UpdateCallback& callback) {
771 blocking_task_runner_->PostTask(
773 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
779 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
780 const UpdateCallback& callback) {
781 blocking_task_runner_->PostTask(
783 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
789 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
790 const UpdateCallback& callback) {
791 blocking_task_runner_->PostTask(
793 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
795 PersistentIdList(1, persistent_id),
799 void GCMStoreImpl::RemoveIncomingMessages(
800 const PersistentIdList& persistent_ids,
801 const UpdateCallback& callback) {
802 blocking_task_runner_->PostTask(
804 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
810 bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
811 const MCSMessage& message,
812 const UpdateCallback& callback) {
813 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
814 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
815 &message.GetProtobuf())->category();
816 DCHECK(!app_id.empty());
817 if (app_message_counts_.count(app_id) == 0)
818 app_message_counts_[app_id] = 0;
819 if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
820 app_message_counts_[app_id]++;
822 blocking_task_runner_->PostTask(
824 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
828 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
829 weak_ptr_factory_.GetWeakPtr(),
837 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
838 const MCSMessage& message,
839 const UpdateCallback& callback) {
840 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
841 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
842 &message.GetProtobuf())->category();
843 DCHECK(!app_id.empty());
844 // There should already be pending messages for this app.
845 DCHECK(app_message_counts_.count(app_id));
846 // TODO(zea): consider verifying the specific message already exists.
847 blocking_task_runner_->PostTask(
849 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
856 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
857 const UpdateCallback& callback) {
858 blocking_task_runner_->PostTask(
860 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
862 PersistentIdList(1, persistent_id),
863 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
864 weak_ptr_factory_.GetWeakPtr(),
868 void GCMStoreImpl::RemoveOutgoingMessages(
869 const PersistentIdList& persistent_ids,
870 const UpdateCallback& callback) {
871 blocking_task_runner_->PostTask(
873 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
876 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
877 weak_ptr_factory_.GetWeakPtr(),
881 void GCMStoreImpl::SetLastCheckinTime(const base::Time& last_checkin_time,
882 const UpdateCallback& callback) {
883 blocking_task_runner_->PostTask(
885 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinTime,
891 void GCMStoreImpl::SetGServicesSettings(
892 const std::map<std::string, std::string>& settings,
893 const std::string& digest,
894 const UpdateCallback& callback) {
895 blocking_task_runner_->PostTask(
897 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
904 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
905 scoped_ptr<LoadResult> result) {
906 if (!result->success) {
907 callback.Run(result.Pass());
910 int num_throttled_apps = 0;
911 for (OutgoingMessageMap::const_iterator
912 iter = result->outgoing_messages.begin();
913 iter != result->outgoing_messages.end(); ++iter) {
914 const mcs_proto::DataMessageStanza* data_message =
915 reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
916 DCHECK(!data_message->category().empty());
917 if (app_message_counts_.count(data_message->category()) == 0)
918 app_message_counts_[data_message->category()] = 1;
920 app_message_counts_[data_message->category()]++;
921 if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
922 num_throttled_apps++;
924 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
925 callback.Run(result.Pass());
928 void GCMStoreImpl::AddOutgoingMessageContinuation(
929 const UpdateCallback& callback,
930 const std::string& app_id,
933 DCHECK(app_message_counts_[app_id] > 0);
934 app_message_counts_[app_id]--;
936 callback.Run(success);
939 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
940 const UpdateCallback& callback,
942 const AppIdToMessageCountMap& removed_message_counts) {
947 for (AppIdToMessageCountMap::const_iterator iter =
948 removed_message_counts.begin();
949 iter != removed_message_counts.end(); ++iter) {
950 DCHECK_NE(app_message_counts_.count(iter->first), 0U);
951 app_message_counts_[iter->first] -= iter->second;
952 DCHECK_GE(app_message_counts_[iter->first], 0);