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
.from().empty());
376 if (removed_message_counts
.count(data_message
.from()) != 0)
377 removed_message_counts
[data_message
.from()]++;
379 removed_message_counts
[data_message
.from()] = 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())->from();
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::RemoveOutgoingMessage(const std::string
& persistent_id
,
714 const UpdateCallback
& callback
) {
715 blocking_task_runner_
->PostTask(
717 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
719 PersistentIdList(1, persistent_id
),
720 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
721 weak_ptr_factory_
.GetWeakPtr(),
725 void GCMStoreImpl::RemoveOutgoingMessages(
726 const PersistentIdList
& persistent_ids
,
727 const UpdateCallback
& callback
) {
728 blocking_task_runner_
->PostTask(
730 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
733 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
734 weak_ptr_factory_
.GetWeakPtr(),
738 void GCMStoreImpl::SetNextSerialNumber(int64 next_serial_number
,
739 const UpdateCallback
& callback
) {
740 blocking_task_runner_
->PostTask(
742 base::Bind(&GCMStoreImpl::Backend::SetNextSerialNumber
,
748 void GCMStoreImpl::AddUserSerialNumber(const std::string
& username
,
750 const UpdateCallback
& callback
) {
751 blocking_task_runner_
->PostTask(
753 base::Bind(&GCMStoreImpl::Backend::AddUserSerialNumber
,
760 void GCMStoreImpl::RemoveUserSerialNumber(const std::string
& username
,
761 const UpdateCallback
& callback
) {
762 blocking_task_runner_
->PostTask(
764 base::Bind(&GCMStoreImpl::Backend::RemoveUserSerialNumber
,
770 void GCMStoreImpl::LoadContinuation(const LoadCallback
& callback
,
771 scoped_ptr
<LoadResult
> result
) {
772 if (!result
->success
) {
773 callback
.Run(result
.Pass());
776 int num_throttled_apps
= 0;
777 for (OutgoingMessageMap::const_iterator
778 iter
= result
->outgoing_messages
.begin();
779 iter
!= result
->outgoing_messages
.end(); ++iter
) {
780 const mcs_proto::DataMessageStanza
* data_message
=
781 reinterpret_cast<mcs_proto::DataMessageStanza
*>(iter
->second
.get());
782 DCHECK(!data_message
->from().empty());
783 if (app_message_counts_
.count(data_message
->from()) == 0)
784 app_message_counts_
[data_message
->from()] = 1;
786 app_message_counts_
[data_message
->from()]++;
787 if (app_message_counts_
[data_message
->from()] == kMessagesPerAppLimit
)
788 num_throttled_apps
++;
790 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps
);
791 callback
.Run(result
.Pass());
794 void GCMStoreImpl::AddOutgoingMessageContinuation(
795 const UpdateCallback
& callback
,
796 const std::string
& app_id
,
799 DCHECK(app_message_counts_
[app_id
] > 0);
800 app_message_counts_
[app_id
]--;
802 callback
.Run(success
);
805 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
806 const UpdateCallback
& callback
,
808 const AppIdToMessageCountMap
& removed_message_counts
) {
813 for (AppIdToMessageCountMap::const_iterator iter
=
814 removed_message_counts
.begin();
815 iter
!= removed_message_counts
.end(); ++iter
) {
816 DCHECK_NE(app_message_counts_
.count(iter
->first
), 0U);
817 app_message_counts_
[iter
->first
] -= iter
->second
;
818 DCHECK_GE(app_message_counts_
[iter
->first
], 0);