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/tracked_objects.h"
20 #include "components/webdata/encryptor/encryptor.h"
21 #include "google_apis/gcm/base/mcs_message.h"
22 #include "google_apis/gcm/base/mcs_util.h"
23 #include "google_apis/gcm/protocol/mcs.pb.h"
24 #include "third_party/leveldatabase/src/include/leveldb/db.h"
30 // Limit to the number of outstanding messages per app.
31 const int kMessagesPerAppLimit = 20;
33 // ---- LevelDB keys. ----
34 // Key for this device's android id.
35 const char kDeviceAIDKey[] = "device_aid_key";
36 // Key for this device's android security token.
37 const char kDeviceTokenKey[] = "device_token_key";
38 // Lowest lexicographically ordered incoming message key.
39 // Used for prefixing messages.
40 const char kIncomingMsgKeyStart[] = "incoming1-";
41 // Key guaranteed to be higher than all incoming message keys.
42 // Used for limiting iteration.
43 const char kIncomingMsgKeyEnd[] = "incoming2-";
44 // Key for next serial number assigned to the user.
45 const char kNextSerialNumberKey[] = "next_serial_number_key";
46 // Lowest lexicographically ordered outgoing message key.
47 // Used for prefixing outgoing messages.
48 const char kOutgoingMsgKeyStart[] = "outgoing1-";
49 // Key guaranteed to be higher than all outgoing message keys.
50 // Used for limiting iteration.
51 const char kOutgoingMsgKeyEnd[] = "outgoing2-";
52 // Lowest lexicographically ordered username.
53 // Used for prefixing username to serial number mappings.
54 const char kUserSerialNumberKeyStart[] = "user1-";
55 // Key guaranteed to be higher than all usernames.
56 // Used for limiting iteration.
57 const char kUserSerialNumberKeyEnd[] = "user2-";
59 // Value indicating that serial number was not assigned.
60 const int64 kSerialNumberMissing = -1LL;
62 std::string MakeIncomingKey(const std::string& persistent_id) {
63 return kIncomingMsgKeyStart + persistent_id;
66 std::string MakeOutgoingKey(const std::string& persistent_id) {
67 return kOutgoingMsgKeyStart + persistent_id;
70 std::string MakeUserSerialNumberKey(const std::string& username) {
71 return kUserSerialNumberKeyStart + username;
74 std::string ParseOutgoingKey(const std::string& key) {
75 return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
78 std::string ParseUsername(const std::string& key) {
79 return key.substr(arraysize(kUserSerialNumberKeyStart) - 1);
82 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
84 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
85 leveldb::Slice MakeSlice(const base::StringPiece& s) {
86 return leveldb::Slice(s.begin(), s.size());
91 class GCMStoreImpl::Backend
92 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
94 Backend(const base::FilePath& path,
95 scoped_refptr<base::SequencedTaskRunner> foreground_runner);
97 // Blocking implementations of GCMStoreImpl methods.
98 void Load(const LoadCallback& callback);
99 void Destroy(const UpdateCallback& callback);
100 void SetDeviceCredentials(uint64 device_android_id,
101 uint64 device_security_token,
102 const UpdateCallback& callback);
103 void AddIncomingMessage(const std::string& persistent_id,
104 const UpdateCallback& callback);
105 void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
106 const UpdateCallback& callback);
107 void AddOutgoingMessage(const std::string& persistent_id,
108 const MCSMessage& message,
109 const UpdateCallback& callback);
110 void RemoveOutgoingMessages(
111 const PersistentIdList& persistent_ids,
112 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
114 void AddUserSerialNumber(const std::string& username,
116 const UpdateCallback& callback);
117 void RemoveUserSerialNumber(const std::string& username,
118 const UpdateCallback& callback);
119 void SetNextSerialNumber(int64 serial_number, const UpdateCallback& callback);
122 friend class base::RefCountedThreadSafe<Backend>;
125 bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
126 bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
127 bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
128 bool LoadNextSerialNumber(int64* next_serial_number);
129 bool LoadUserSerialNumberMap(
130 std::map<std::string, int64>* user_serial_number_map);
132 const base::FilePath path_;
133 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
135 scoped_ptr<leveldb::DB> db_;
138 GCMStoreImpl::Backend::Backend(
139 const base::FilePath& path,
140 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner)
141 : path_(path), foreground_task_runner_(foreground_task_runner) {}
143 GCMStoreImpl::Backend::~Backend() {}
145 void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
146 scoped_ptr<LoadResult> result(new LoadResult());
148 LOG(ERROR) << "Attempting to reload open database.";
149 foreground_task_runner_->PostTask(FROM_HERE,
151 base::Passed(&result)));
155 leveldb::Options options;
156 options.create_if_missing = true;
158 leveldb::Status status =
159 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
160 UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status.ok());
162 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
163 << status.ToString();
164 foreground_task_runner_->PostTask(FROM_HERE,
166 base::Passed(&result)));
171 if (!LoadDeviceCredentials(&result->device_android_id,
172 &result->device_security_token) ||
173 !LoadIncomingMessages(&result->incoming_messages) ||
174 !LoadOutgoingMessages(&result->outgoing_messages) ||
175 !LoadNextSerialNumber(
176 &result->serial_number_mappings.next_serial_number) ||
177 !LoadUserSerialNumberMap(
178 &result->serial_number_mappings.user_serial_numbers)) {
179 result->device_android_id = 0;
180 result->device_security_token = 0;
181 result->incoming_messages.clear();
182 result->outgoing_messages.clear();
183 foreground_task_runner_->PostTask(FROM_HERE,
185 base::Passed(&result)));
189 // Only record histograms if GCM had already been set up for this device.
190 if (result->device_android_id != 0 && result->device_security_token != 0) {
192 if (base::GetFileSize(path_, &file_size)) {
193 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
194 static_cast<int>(file_size / 1024));
196 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
197 result->outgoing_messages.size());
198 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
199 result->incoming_messages.size());
200 UMA_HISTOGRAM_COUNTS(
202 result->serial_number_mappings.user_serial_numbers.size());
205 DVLOG(1) << "Succeeded in loading " << result->incoming_messages.size()
206 << " unacknowledged incoming messages and "
207 << result->outgoing_messages.size()
208 << " unacknowledged outgoing messages.";
209 result->success = true;
210 foreground_task_runner_->PostTask(FROM_HERE,
212 base::Passed(&result)));
216 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
217 DVLOG(1) << "Destroying GCM store.";
219 const leveldb::Status s =
220 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
222 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
225 LOG(ERROR) << "Destroy failed: " << s.ToString();
226 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
229 void GCMStoreImpl::Backend::SetDeviceCredentials(
230 uint64 device_android_id,
231 uint64 device_security_token,
232 const UpdateCallback& callback) {
233 DVLOG(1) << "Saving device credentials with AID " << device_android_id;
235 LOG(ERROR) << "GCMStore db doesn't exist.";
236 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
240 leveldb::WriteOptions write_options;
241 write_options.sync = true;
243 std::string encrypted_token;
244 Encryptor::EncryptString(base::Uint64ToString(device_security_token),
246 std::string android_id_str = base::Uint64ToString(device_android_id);
248 db_->Put(write_options,
249 MakeSlice(kDeviceAIDKey),
250 MakeSlice(android_id_str));
253 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
256 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
259 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
260 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
263 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
264 const UpdateCallback& callback) {
265 DVLOG(1) << "Saving incoming message with id " << persistent_id;
267 LOG(ERROR) << "GCMStore db doesn't exist.";
268 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
272 leveldb::WriteOptions write_options;
273 write_options.sync = true;
275 std::string key = MakeIncomingKey(persistent_id);
276 const leveldb::Status s = db_->Put(write_options,
278 MakeSlice(persistent_id));
280 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
283 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
284 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
287 void GCMStoreImpl::Backend::RemoveIncomingMessages(
288 const PersistentIdList& persistent_ids,
289 const UpdateCallback& callback) {
291 LOG(ERROR) << "GCMStore db doesn't exist.";
292 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
295 leveldb::WriteOptions write_options;
296 write_options.sync = true;
299 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
300 iter != persistent_ids.end();
302 DVLOG(1) << "Removing incoming message with id " << *iter;
303 std::string key = MakeIncomingKey(*iter);
304 s = db_->Delete(write_options, MakeSlice(key));
309 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
312 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
313 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
316 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
317 const MCSMessage& message,
318 const UpdateCallback& callback) {
319 DVLOG(1) << "Saving outgoing message with id " << persistent_id;
321 LOG(ERROR) << "GCMStore db doesn't exist.";
322 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
325 leveldb::WriteOptions write_options;
326 write_options.sync = true;
329 static_cast<char>(message.tag()) + message.SerializeAsString();
330 std::string key = MakeOutgoingKey(persistent_id);
331 const leveldb::Status s = db_->Put(write_options,
335 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
338 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
339 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
342 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
343 const PersistentIdList& persistent_ids,
344 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
347 LOG(ERROR) << "GCMStore db doesn't exist.";
348 foreground_task_runner_->PostTask(FROM_HERE,
351 AppIdToMessageCountMap()));
354 leveldb::ReadOptions read_options;
355 leveldb::WriteOptions write_options;
356 write_options.sync = true;
358 AppIdToMessageCountMap removed_message_counts;
361 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
362 iter != persistent_ids.end();
364 DVLOG(1) << "Removing outgoing message with id " << *iter;
365 std::string outgoing_message;
366 std::string key = MakeOutgoingKey(*iter);
367 s = db_->Get(read_options,
372 mcs_proto::DataMessageStanza data_message;
373 // Skip the initial tag byte and parse the rest to extract the message.
374 if (data_message.ParseFromString(outgoing_message.substr(1))) {
375 DCHECK(!data_message.category().empty());
376 if (removed_message_counts.count(data_message.category()) != 0)
377 removed_message_counts[data_message.category()]++;
379 removed_message_counts[data_message.category()] = 1;
381 DVLOG(1) << "Removing outgoing message with id " << *iter;
382 s = db_->Delete(write_options, MakeSlice(key));
387 foreground_task_runner_->PostTask(FROM_HERE,
390 removed_message_counts));
393 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
394 foreground_task_runner_->PostTask(FROM_HERE,
397 AppIdToMessageCountMap()));
400 void GCMStoreImpl::Backend::AddUserSerialNumber(
401 const std::string& username,
403 const UpdateCallback& callback) {
404 DVLOG(1) << "Saving username to serial number mapping for user: " << username;
406 LOG(ERROR) << "GCMStore db doesn't exist.";
407 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
410 leveldb::WriteOptions write_options;
411 write_options.sync = true;
413 std::string key = MakeUserSerialNumberKey(username);
414 std::string serial_number_str = base::Int64ToString(serial_number);
415 const leveldb::Status status =
416 db_->Put(write_options,
418 MakeSlice(serial_number_str));
420 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
423 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
424 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
427 void GCMStoreImpl::Backend::RemoveUserSerialNumber(
428 const std::string& username,
429 const UpdateCallback& callback) {
431 LOG(ERROR) << "GCMStore db doesn't exist.";
432 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
435 leveldb::WriteOptions write_options;
436 write_options.sync = true;
438 leveldb::Status status = db_->Delete(write_options, MakeSlice(username));
440 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
443 LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
444 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
447 void GCMStoreImpl::Backend::SetNextSerialNumber(
448 int64 next_serial_number,
449 const UpdateCallback& callback) {
450 DVLOG(1) << "Updating the value of next user serial number to: "
451 << next_serial_number;
453 LOG(ERROR) << "GCMStore db doesn't exist.";
454 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
457 leveldb::WriteOptions write_options;
458 write_options.sync = true;
460 std::string serial_number_str = base::Int64ToString(next_serial_number);
461 const leveldb::Status status =
462 db_->Put(write_options,
463 MakeSlice(kNextSerialNumberKey),
464 MakeSlice(serial_number_str));
466 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
469 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
470 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
473 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
474 uint64* security_token) {
475 leveldb::ReadOptions read_options;
476 read_options.verify_checksums = true;
479 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
481 if (!base::StringToUint64(result, android_id)) {
482 LOG(ERROR) << "Failed to restore device id.";
486 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
489 std::string decrypted_token;
490 Encryptor::DecryptString(result, &decrypted_token);
491 if (!base::StringToUint64(decrypted_token, security_token)) {
492 LOG(ERROR) << "Failed to restore security token.";
498 if (s.IsNotFound()) {
499 DVLOG(1) << "No credentials found.";
503 LOG(ERROR) << "Error reading credentials from store.";
507 bool GCMStoreImpl::Backend::LoadIncomingMessages(
508 std::vector<std::string>* incoming_messages) {
509 leveldb::ReadOptions read_options;
510 read_options.verify_checksums = true;
512 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
513 for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
514 iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
516 leveldb::Slice s = iter->value();
518 LOG(ERROR) << "Error reading incoming message with key "
519 << iter->key().ToString();
522 DVLOG(1) << "Found incoming message with id " << s.ToString();
523 incoming_messages->push_back(s.ToString());
529 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
530 OutgoingMessageMap* outgoing_messages) {
531 leveldb::ReadOptions read_options;
532 read_options.verify_checksums = true;
534 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
535 for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
536 iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
538 leveldb::Slice s = iter->value();
540 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
543 uint8 tag = iter->value().data()[0];
544 std::string id = ParseOutgoingKey(iter->key().ToString());
545 scoped_ptr<google::protobuf::MessageLite> message(
546 BuildProtobufFromTag(tag));
547 if (!message.get() ||
548 !message->ParseFromString(iter->value().ToString().substr(1))) {
549 LOG(ERROR) << "Failed to parse outgoing message with id " << id
550 << " and tag " << tag;
553 DVLOG(1) << "Found outgoing message with id " << id << " of type "
554 << base::IntToString(tag);
555 (*outgoing_messages)[id] = make_linked_ptr(message.release());
561 bool GCMStoreImpl::Backend::LoadNextSerialNumber(int64* next_serial_number) {
562 leveldb::ReadOptions read_options;
563 read_options.verify_checksums = true;
566 leveldb::Status status =
567 db_->Get(read_options, MakeSlice(kNextSerialNumberKey), &result);
569 if (!base::StringToInt64(result, next_serial_number)) {
570 LOG(ERROR) << "Failed to restore the next serial number.";
576 if (status.IsNotFound()) {
577 DVLOG(1) << "No next serial number found.";
581 LOG(ERROR) << "Error when reading the next serial number.";
585 bool GCMStoreImpl::Backend::LoadUserSerialNumberMap(
586 std::map<std::string, int64>* user_serial_number_map) {
587 leveldb::ReadOptions read_options;
588 read_options.verify_checksums = true;
590 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
591 for (iter->Seek(MakeSlice(kUserSerialNumberKeyStart));
592 iter->Valid() && iter->key().ToString() < kUserSerialNumberKeyEnd;
594 std::string username = ParseUsername(iter->key().ToString());
595 if (username.empty()) {
596 LOG(ERROR) << "Error reading username. It should not be empty.";
599 std::string serial_number_string = iter->value().ToString();
600 int64 serial_number = kSerialNumberMissing;
601 if (!base::StringToInt64(serial_number_string, &serial_number)) {
602 LOG(ERROR) << "Error reading user serial number for user: " << username;
606 (*user_serial_number_map)[username] = serial_number;
612 GCMStoreImpl::GCMStoreImpl(
613 bool use_mock_keychain,
614 const base::FilePath& path,
615 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner)
616 : backend_(new Backend(path, base::MessageLoopProxy::current())),
617 blocking_task_runner_(blocking_task_runner),
618 weak_ptr_factory_(this) {
619 // On OSX, prevent the Keychain permissions popup during unit tests.
620 #if defined(OS_MACOSX)
621 Encryptor::UseMockKeychain(use_mock_keychain);
625 GCMStoreImpl::~GCMStoreImpl() {}
627 void GCMStoreImpl::Load(const LoadCallback& callback) {
628 blocking_task_runner_->PostTask(
630 base::Bind(&GCMStoreImpl::Backend::Load,
632 base::Bind(&GCMStoreImpl::LoadContinuation,
633 weak_ptr_factory_.GetWeakPtr(),
637 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
638 blocking_task_runner_->PostTask(
640 base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
643 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
644 uint64 device_security_token,
645 const UpdateCallback& callback) {
646 blocking_task_runner_->PostTask(
648 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
651 device_security_token,
655 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
656 const UpdateCallback& callback) {
657 blocking_task_runner_->PostTask(
659 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
665 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
666 const UpdateCallback& callback) {
667 blocking_task_runner_->PostTask(
669 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
671 PersistentIdList(1, persistent_id),
675 void GCMStoreImpl::RemoveIncomingMessages(
676 const PersistentIdList& persistent_ids,
677 const UpdateCallback& callback) {
678 blocking_task_runner_->PostTask(
680 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
686 bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
687 const MCSMessage& message,
688 const UpdateCallback& callback) {
689 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
690 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
691 &message.GetProtobuf())->category();
692 DCHECK(!app_id.empty());
693 if (app_message_counts_.count(app_id) == 0)
694 app_message_counts_[app_id] = 0;
695 if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
696 app_message_counts_[app_id]++;
698 blocking_task_runner_->PostTask(
700 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
704 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
705 weak_ptr_factory_.GetWeakPtr(),
713 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
714 const MCSMessage& message,
715 const UpdateCallback& callback) {
716 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
717 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
718 &message.GetProtobuf())->category();
719 DCHECK(!app_id.empty());
720 // There should already be pending messages for this app.
721 DCHECK(app_message_counts_.count(app_id));
722 // TODO(zea): consider verifying the specific message already exists.
723 blocking_task_runner_->PostTask(
725 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
732 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
733 const UpdateCallback& callback) {
734 blocking_task_runner_->PostTask(
736 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
738 PersistentIdList(1, persistent_id),
739 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
740 weak_ptr_factory_.GetWeakPtr(),
744 void GCMStoreImpl::RemoveOutgoingMessages(
745 const PersistentIdList& persistent_ids,
746 const UpdateCallback& callback) {
747 blocking_task_runner_->PostTask(
749 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
752 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
753 weak_ptr_factory_.GetWeakPtr(),
757 void GCMStoreImpl::SetNextSerialNumber(int64 next_serial_number,
758 const UpdateCallback& callback) {
759 blocking_task_runner_->PostTask(
761 base::Bind(&GCMStoreImpl::Backend::SetNextSerialNumber,
767 void GCMStoreImpl::AddUserSerialNumber(const std::string& username,
769 const UpdateCallback& callback) {
770 blocking_task_runner_->PostTask(
772 base::Bind(&GCMStoreImpl::Backend::AddUserSerialNumber,
779 void GCMStoreImpl::RemoveUserSerialNumber(const std::string& username,
780 const UpdateCallback& callback) {
781 blocking_task_runner_->PostTask(
783 base::Bind(&GCMStoreImpl::Backend::RemoveUserSerialNumber,
789 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
790 scoped_ptr<LoadResult> result) {
791 if (!result->success) {
792 callback.Run(result.Pass());
795 int num_throttled_apps = 0;
796 for (OutgoingMessageMap::const_iterator
797 iter = result->outgoing_messages.begin();
798 iter != result->outgoing_messages.end(); ++iter) {
799 const mcs_proto::DataMessageStanza* data_message =
800 reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
801 DCHECK(!data_message->category().empty());
802 if (app_message_counts_.count(data_message->category()) == 0)
803 app_message_counts_[data_message->category()] = 1;
805 app_message_counts_[data_message->category()]++;
806 if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
807 num_throttled_apps++;
809 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
810 callback.Run(result.Pass());
813 void GCMStoreImpl::AddOutgoingMessageContinuation(
814 const UpdateCallback& callback,
815 const std::string& app_id,
818 DCHECK(app_message_counts_[app_id] > 0);
819 app_message_counts_[app_id]--;
821 callback.Run(success);
824 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
825 const UpdateCallback& callback,
827 const AppIdToMessageCountMap& removed_message_counts) {
832 for (AppIdToMessageCountMap::const_iterator iter =
833 removed_message_counts.begin();
834 iter != removed_message_counts.end(); ++iter) {
835 DCHECK_NE(app_message_counts_.count(iter->first), 0U);
836 app_message_counts_[iter->first] -= iter->second;
837 DCHECK_GE(app_message_counts_[iter->first], 0);