1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "google_apis/gcm/engine/gcm_store_impl.h"
7 #include "base/basictypes.h"
9 #include "base/callback.h"
10 #include "base/files/file_path.h"
11 #include "base/files/file_util.h"
12 #include "base/logging.h"
13 #include "base/metrics/histogram_macros.h"
14 #include "base/profiler/scoped_tracker.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/strings/string_util.h"
21 #include "base/thread_task_runner_handle.h"
22 #include "base/time/time.h"
23 #include "base/tracked_objects.h"
24 #include "google_apis/gcm/base/encryptor.h"
25 #include "google_apis/gcm/base/mcs_message.h"
26 #include "google_apis/gcm/base/mcs_util.h"
27 #include "google_apis/gcm/protocol/mcs.pb.h"
28 #include "third_party/leveldatabase/env_chromium.h"
29 #include "third_party/leveldatabase/src/include/leveldb/db.h"
30 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
36 // This enum is used in an UMA histogram (GCMLoadStatus enum defined in
37 // tools/metrics/histograms/histogram.xml). Hence the entries here shouldn't
38 // be deleted or re-ordered and new ones should be added to the end.
43 LOADING_DEVICE_CREDENTIALS_FAILED
,
44 LOADING_REGISTRATION_FAILED
,
45 LOADING_INCOMING_MESSAGES_FAILED
,
46 LOADING_OUTGOING_MESSAGES_FAILED
,
47 LOADING_LAST_CHECKIN_INFO_FAILED
,
48 LOADING_GSERVICE_SETTINGS_FAILED
,
49 LOADING_ACCOUNT_MAPPING_FAILED
,
50 LOADING_LAST_TOKEN_TIME_FAILED
,
51 LOADING_HEARTBEAT_INTERVALS_FAILED
,
52 LOADING_INSTANCE_ID_DATA_FAILED
,
55 // NOTE: always keep this entry at the end. Add new status types only
56 // immediately above this line. Make sure to update the corresponding
57 // histogram enum accordingly.
61 // Limit to the number of outstanding messages per app.
62 const int kMessagesPerAppLimit
= 20;
64 // ---- LevelDB keys. ----
65 // Key for this device's android id.
66 const char kDeviceAIDKey
[] = "device_aid_key";
67 // Key for this device's android security token.
68 const char kDeviceTokenKey
[] = "device_token_key";
69 // Lowest lexicographically ordered app ids.
70 // Used for prefixing app id.
71 const char kRegistrationKeyStart
[] = "reg1-";
72 // Key guaranteed to be higher than all app ids.
73 // Used for limiting iteration.
74 const char kRegistrationKeyEnd
[] = "reg2-";
75 // Lowest lexicographically ordered incoming message key.
76 // Used for prefixing messages.
77 const char kIncomingMsgKeyStart
[] = "incoming1-";
78 // Key guaranteed to be higher than all incoming message keys.
79 // Used for limiting iteration.
80 const char kIncomingMsgKeyEnd
[] = "incoming2-";
81 // Lowest lexicographically ordered outgoing message key.
82 // Used for prefixing outgoing messages.
83 const char kOutgoingMsgKeyStart
[] = "outgoing1-";
84 // Key guaranteed to be higher than all outgoing message keys.
85 // Used for limiting iteration.
86 const char kOutgoingMsgKeyEnd
[] = "outgoing2-";
87 // Lowest lexicographically ordered G-service settings key.
88 // Used for prefixing G-services settings.
89 const char kGServiceSettingKeyStart
[] = "gservice1-";
90 // Key guaranteed to be higher than all G-services settings keys.
91 // Used for limiting iteration.
92 const char kGServiceSettingKeyEnd
[] = "gservice2-";
93 // Key for digest of the last G-services settings update.
94 const char kGServiceSettingsDigestKey
[] = "gservices_digest";
95 // Key used to indicate how many accounts were last checked in with this device.
96 const char kLastCheckinAccountsKey
[] = "last_checkin_accounts_count";
97 // Key used to timestamp last checkin (marked with G services settings update).
98 const char kLastCheckinTimeKey
[] = "last_checkin_time";
99 // Lowest lexicographically ordered account key.
100 // Used for prefixing account information.
101 const char kAccountKeyStart
[] = "account1-";
102 // Key guaranteed to be higher than all account keys.
103 // Used for limiting iteration.
104 const char kAccountKeyEnd
[] = "account2-";
105 // Lowest lexicographically ordered heartbeat key.
106 // Used for prefixing account information.
107 const char kHeartbeatKeyStart
[] = "heartbeat1-";
108 // Key guaranteed to be higher than all heartbeat keys.
109 // Used for limiting iteration.
110 const char kHeartbeatKeyEnd
[] = "heartbeat2-";
111 // Key used for last token fetch time.
112 const char kLastTokenFetchTimeKey
[] = "last_token_fetch_time";
113 // Lowest lexicographically ordered app ids.
114 // Used for prefixing app id.
115 const char kInstanceIDKeyStart
[] = "iid1-";
116 // Key guaranteed to be higher than all app ids.
117 // Used for limiting iteration.
118 const char kInstanceIDKeyEnd
[] = "iid2-";
120 std::string
MakeRegistrationKey(const std::string
& app_id
) {
121 return kRegistrationKeyStart
+ app_id
;
124 std::string
ParseRegistrationKey(const std::string
& key
) {
125 return key
.substr(arraysize(kRegistrationKeyStart
) - 1);
128 std::string
MakeIncomingKey(const std::string
& persistent_id
) {
129 return kIncomingMsgKeyStart
+ persistent_id
;
132 std::string
MakeOutgoingKey(const std::string
& persistent_id
) {
133 return kOutgoingMsgKeyStart
+ persistent_id
;
136 std::string
ParseOutgoingKey(const std::string
& key
) {
137 return key
.substr(arraysize(kOutgoingMsgKeyStart
) - 1);
140 std::string
MakeGServiceSettingKey(const std::string
& setting_name
) {
141 return kGServiceSettingKeyStart
+ setting_name
;
144 std::string
ParseGServiceSettingKey(const std::string
& key
) {
145 return key
.substr(arraysize(kGServiceSettingKeyStart
) - 1);
148 std::string
MakeAccountKey(const std::string
& account_id
) {
149 return kAccountKeyStart
+ account_id
;
152 std::string
ParseAccountKey(const std::string
& key
) {
153 return key
.substr(arraysize(kAccountKeyStart
) - 1);
156 std::string
MakeHeartbeatKey(const std::string
& scope
) {
157 return kHeartbeatKeyStart
+ scope
;
160 std::string
ParseHeartbeatKey(const std::string
& key
) {
161 return key
.substr(arraysize(kHeartbeatKeyStart
) - 1);
164 std::string
MakeInstanceIDKey(const std::string
& app_id
) {
165 return kInstanceIDKeyStart
+ app_id
;
168 std::string
ParseInstanceIDKey(const std::string
& key
) {
169 return key
.substr(arraysize(kInstanceIDKeyStart
) - 1);
172 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
173 // outlive the slice.
174 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
175 leveldb::Slice
MakeSlice(const base::StringPiece
& s
) {
176 return leveldb::Slice(s
.begin(), s
.size());
181 class GCMStoreImpl::Backend
182 : public base::RefCountedThreadSafe
<GCMStoreImpl::Backend
> {
184 Backend(const base::FilePath
& path
,
185 scoped_refptr
<base::SequencedTaskRunner
> foreground_runner
,
186 scoped_ptr
<Encryptor
> encryptor
);
188 // Blocking implementations of GCMStoreImpl methods.
189 void Load(StoreOpenMode open_mode
, const LoadCallback
& callback
);
191 void Destroy(const UpdateCallback
& callback
);
192 void SetDeviceCredentials(uint64 device_android_id
,
193 uint64 device_security_token
,
194 const UpdateCallback
& callback
);
195 void AddRegistration(const std::string
& serialized_key
,
196 const std::string
& serialized_value
,
197 const UpdateCallback
& callback
);
198 void RemoveRegistration(const std::string
& serialized_key
,
199 const UpdateCallback
& callback
);
200 void AddIncomingMessage(const std::string
& persistent_id
,
201 const UpdateCallback
& callback
);
202 void RemoveIncomingMessages(const PersistentIdList
& persistent_ids
,
203 const UpdateCallback
& callback
);
204 void AddOutgoingMessage(const std::string
& persistent_id
,
205 const MCSMessage
& message
,
206 const UpdateCallback
& callback
);
207 void RemoveOutgoingMessages(
208 const PersistentIdList
& persistent_ids
,
209 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
211 void AddUserSerialNumber(const std::string
& username
,
213 const UpdateCallback
& callback
);
214 void RemoveUserSerialNumber(const std::string
& username
,
215 const UpdateCallback
& callback
);
216 void SetLastCheckinInfo(const base::Time
& time
,
217 const std::set
<std::string
>& accounts
,
218 const UpdateCallback
& callback
);
219 void SetGServicesSettings(
220 const std::map
<std::string
, std::string
>& settings
,
221 const std::string
& digest
,
222 const UpdateCallback
& callback
);
223 void AddAccountMapping(const AccountMapping
& account_mapping
,
224 const UpdateCallback
& callback
);
225 void RemoveAccountMapping(const std::string
& account_id
,
226 const UpdateCallback
& callback
);
227 void SetLastTokenFetchTime(const base::Time
& time
,
228 const UpdateCallback
& callback
);
229 void AddHeartbeatInterval(const std::string
& scope
,
231 const UpdateCallback
& callback
);
232 void RemoveHeartbeatInterval(const std::string
& scope
,
233 const UpdateCallback
& callback
);
234 void AddInstanceIDData(const std::string
& app_id
,
235 const std::string
& instance_id_data
,
236 const UpdateCallback
& callback
);
237 void RemoveInstanceIDData(const std::string
& app_id
,
238 const UpdateCallback
& callback
);
239 void SetValue(const std::string
& key
,
240 const std::string
& value
,
241 const UpdateCallback
& callback
);
244 friend class base::RefCountedThreadSafe
<Backend
>;
247 LoadStatus
OpenStoreAndLoadData(StoreOpenMode open_mode
, LoadResult
* result
);
248 bool LoadDeviceCredentials(uint64
* android_id
, uint64
* security_token
);
249 bool LoadRegistrations(std::map
<std::string
, std::string
>* registrations
);
250 bool LoadIncomingMessages(std::vector
<std::string
>* incoming_messages
);
251 bool LoadOutgoingMessages(OutgoingMessageMap
* outgoing_messages
);
252 bool LoadLastCheckinInfo(base::Time
* last_checkin_time
,
253 std::set
<std::string
>* accounts
);
254 bool LoadGServicesSettings(std::map
<std::string
, std::string
>* settings
,
255 std::string
* digest
);
256 bool LoadAccountMappingInfo(AccountMappings
* account_mappings
);
257 bool LoadLastTokenFetchTime(base::Time
* last_token_fetch_time
);
258 bool LoadHeartbeatIntervals(std::map
<std::string
, int>* heartbeat_intervals
);
259 bool LoadInstanceIDData(std::map
<std::string
, std::string
>* instance_id_data
);
261 const base::FilePath path_
;
262 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner_
;
263 scoped_ptr
<Encryptor
> encryptor_
;
265 scoped_ptr
<leveldb::DB
> db_
;
268 GCMStoreImpl::Backend::Backend(
269 const base::FilePath
& path
,
270 scoped_refptr
<base::SequencedTaskRunner
> foreground_task_runner
,
271 scoped_ptr
<Encryptor
> encryptor
)
273 foreground_task_runner_(foreground_task_runner
),
274 encryptor_(encryptor
.Pass()) {
277 GCMStoreImpl::Backend::~Backend() {}
279 LoadStatus
GCMStoreImpl::Backend::OpenStoreAndLoadData(StoreOpenMode open_mode
,
280 LoadResult
* result
) {
281 LoadStatus load_status
;
283 LOG(ERROR
) << "Attempting to reload open database.";
284 return RELOADING_OPEN_STORE
;
287 // Checks if the store exists or not. Calling DB::Open with create_if_missing
288 // not set will still create a new directory if the store does not exist.
289 if (open_mode
== DO_NOT_CREATE
&& !base::DirectoryExists(path_
)) {
290 DVLOG(2) << "Database " << path_
.value() << " does not exist";
291 return STORE_DOES_NOT_EXIST
;
294 leveldb::Options options
;
295 options
.create_if_missing
= open_mode
== CREATE_IF_MISSING
;
296 options
.reuse_logs
= leveldb_env::kDefaultLogReuseOptionValue
;
297 options
.paranoid_checks
= true;
299 leveldb::Status status
=
300 leveldb::DB::Open(options
, path_
.AsUTF8Unsafe(), &db
);
301 UMA_HISTOGRAM_ENUMERATION("GCM.Database.Open",
302 leveldb_env::GetLevelDBStatusUMAValue(status
),
303 leveldb_env::LEVELDB_STATUS_MAX
);
305 LOG(ERROR
) << "Failed to open database " << path_
.value() << ": "
306 << status
.ToString();
307 return OPENING_STORE_FAILED
;
311 if (!LoadDeviceCredentials(&result
->device_android_id
,
312 &result
->device_security_token
)) {
313 return LOADING_DEVICE_CREDENTIALS_FAILED
;
315 if (!LoadRegistrations(&result
->registrations
))
316 return LOADING_REGISTRATION_FAILED
;
317 if (!LoadIncomingMessages(&result
->incoming_messages
))
318 return LOADING_INCOMING_MESSAGES_FAILED
;
319 if (!LoadOutgoingMessages(&result
->outgoing_messages
))
320 return LOADING_OUTGOING_MESSAGES_FAILED
;
321 if (!LoadLastCheckinInfo(&result
->last_checkin_time
,
322 &result
->last_checkin_accounts
)) {
323 return LOADING_LAST_CHECKIN_INFO_FAILED
;
325 if (!LoadGServicesSettings(&result
->gservices_settings
,
326 &result
->gservices_digest
)) {
327 return load_status
= LOADING_GSERVICE_SETTINGS_FAILED
;
329 if (!LoadAccountMappingInfo(&result
->account_mappings
))
330 return LOADING_ACCOUNT_MAPPING_FAILED
;
331 if (!LoadLastTokenFetchTime(&result
->last_token_fetch_time
))
332 return LOADING_LAST_TOKEN_TIME_FAILED
;
333 if (!LoadHeartbeatIntervals(&result
->heartbeat_intervals
))
334 return LOADING_HEARTBEAT_INTERVALS_FAILED
;
335 if (!LoadInstanceIDData(&result
->instance_id_data
))
336 return LOADING_INSTANCE_ID_DATA_FAILED
;
338 return LOADING_SUCCEEDED
;
341 void GCMStoreImpl::Backend::Load(StoreOpenMode open_mode
,
342 const LoadCallback
& callback
) {
343 scoped_ptr
<LoadResult
> result(new LoadResult());
344 LoadStatus load_status
= OpenStoreAndLoadData(open_mode
, result
.get());
345 UMA_HISTOGRAM_ENUMERATION("GCM.LoadStatus", load_status
, LOAD_STATUS_COUNT
);
346 if (load_status
!= LOADING_SUCCEEDED
) {
348 result
->store_does_not_exist
= (load_status
== STORE_DOES_NOT_EXIST
);
349 foreground_task_runner_
->PostTask(FROM_HERE
,
351 base::Passed(&result
)));
355 // |result->registrations| contains both GCM registrations and InstanceID
356 // tokens. Count them separately.
357 int gcm_registration_count
= 0;
358 int instance_id_token_count
= 0;
359 for (const auto& registration
: result
->registrations
) {
360 if (base::StartsWith(registration
.first
, "iid-",
361 base::CompareCase::SENSITIVE
))
362 instance_id_token_count
++;
364 gcm_registration_count
++;
367 // Only record histograms if GCM had already been set up for this device.
368 if (result
->device_android_id
!= 0 && result
->device_security_token
!= 0) {
370 if (base::GetFileSize(path_
, &file_size
)) {
371 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
372 static_cast<int>(file_size
/ 1024));
375 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations", gcm_registration_count
);
376 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
377 result
->outgoing_messages
.size());
378 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
379 result
->incoming_messages
.size());
381 UMA_HISTOGRAM_COUNTS("InstanceID.RestoredTokenCount",
382 instance_id_token_count
);
383 UMA_HISTOGRAM_COUNTS("InstanceID.RestoredIDCount",
384 result
->instance_id_data
.size());
387 DVLOG(1) << "Succeeded in loading "
388 << gcm_registration_count
<< " GCM registrations, "
389 << result
->incoming_messages
.size()
390 << " unacknowledged incoming messages "
391 << result
->outgoing_messages
.size()
392 << " unacknowledged outgoing messages, "
393 << result
->instance_id_data
.size() << " Instance IDs, "
394 << instance_id_token_count
<< " InstanceID tokens.";
395 result
->success
= true;
396 foreground_task_runner_
->PostTask(FROM_HERE
,
398 base::Passed(&result
)));
402 void GCMStoreImpl::Backend::Close() {
403 DVLOG(1) << "Closing GCM store.";
407 void GCMStoreImpl::Backend::Destroy(const UpdateCallback
& callback
) {
408 DVLOG(1) << "Destroying GCM store.";
410 const leveldb::Status s
=
411 leveldb::DestroyDB(path_
.AsUTF8Unsafe(), leveldb::Options());
413 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
416 LOG(ERROR
) << "Destroy failed: " << s
.ToString();
417 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
420 void GCMStoreImpl::Backend::SetDeviceCredentials(
421 uint64 device_android_id
,
422 uint64 device_security_token
,
423 const UpdateCallback
& callback
) {
424 DVLOG(1) << "Saving device credentials with AID " << device_android_id
;
426 LOG(ERROR
) << "GCMStore db doesn't exist.";
427 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
431 leveldb::WriteOptions write_options
;
432 write_options
.sync
= true;
434 std::string encrypted_token
;
435 encryptor_
->EncryptString(base::Uint64ToString(device_security_token
),
437 std::string android_id_str
= base::Uint64ToString(device_android_id
);
439 db_
->Put(write_options
,
440 MakeSlice(kDeviceAIDKey
),
441 MakeSlice(android_id_str
));
444 write_options
, MakeSlice(kDeviceTokenKey
), MakeSlice(encrypted_token
));
447 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
450 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
451 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
454 void GCMStoreImpl::Backend::AddRegistration(
455 const std::string
& serialized_key
,
456 const std::string
& serialized_value
,
457 const UpdateCallback
& callback
) {
458 DVLOG(1) << "Saving registration info for app: " << serialized_key
;
460 LOG(ERROR
) << "GCMStore db doesn't exist.";
461 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
464 leveldb::WriteOptions write_options
;
465 write_options
.sync
= true;
467 const leveldb::Status status
= db_
->Put(
469 MakeSlice(MakeRegistrationKey(serialized_key
)),
470 MakeSlice(serialized_value
));
472 LOG(ERROR
) << "LevelDB put failed: " << status
.ToString();
473 foreground_task_runner_
->PostTask(
474 FROM_HERE
, base::Bind(callback
, status
.ok()));
477 void GCMStoreImpl::Backend::RemoveRegistration(
478 const std::string
& serialized_key
,
479 const UpdateCallback
& callback
) {
481 LOG(ERROR
) << "GCMStore db doesn't exist.";
482 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
485 leveldb::WriteOptions write_options
;
486 write_options
.sync
= true;
488 leveldb::Status status
= db_
->Delete(
489 write_options
, MakeSlice(MakeRegistrationKey(serialized_key
)));
491 LOG(ERROR
) << "LevelDB remove failed: " << status
.ToString();
492 foreground_task_runner_
->PostTask(
493 FROM_HERE
, base::Bind(callback
, status
.ok()));
496 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string
& persistent_id
,
497 const UpdateCallback
& callback
) {
498 DVLOG(1) << "Saving incoming message with id " << persistent_id
;
500 LOG(ERROR
) << "GCMStore db doesn't exist.";
501 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
505 leveldb::WriteOptions write_options
;
506 write_options
.sync
= true;
508 std::string key
= MakeIncomingKey(persistent_id
);
509 const leveldb::Status s
= db_
->Put(write_options
,
511 MakeSlice(persistent_id
));
513 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
516 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
517 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
520 void GCMStoreImpl::Backend::RemoveIncomingMessages(
521 const PersistentIdList
& persistent_ids
,
522 const UpdateCallback
& callback
) {
524 LOG(ERROR
) << "GCMStore db doesn't exist.";
525 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
528 leveldb::WriteOptions write_options
;
529 write_options
.sync
= true;
532 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
533 iter
!= persistent_ids
.end();
535 DVLOG(1) << "Removing incoming message with id " << *iter
;
536 std::string key
= MakeIncomingKey(*iter
);
537 s
= db_
->Delete(write_options
, MakeSlice(key
));
542 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
545 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
546 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
549 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string
& persistent_id
,
550 const MCSMessage
& message
,
551 const UpdateCallback
& callback
) {
552 DVLOG(1) << "Saving outgoing message with id " << persistent_id
;
554 LOG(ERROR
) << "GCMStore db doesn't exist.";
555 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
558 leveldb::WriteOptions write_options
;
559 write_options
.sync
= true;
562 static_cast<char>(message
.tag()) + message
.SerializeAsString();
563 std::string key
= MakeOutgoingKey(persistent_id
);
564 const leveldb::Status s
= db_
->Put(write_options
,
568 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, true));
571 LOG(ERROR
) << "LevelDB put failed: " << s
.ToString();
572 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
575 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
576 const PersistentIdList
& persistent_ids
,
577 const base::Callback
<void(bool, const AppIdToMessageCountMap
&)>
580 LOG(ERROR
) << "GCMStore db doesn't exist.";
581 foreground_task_runner_
->PostTask(FROM_HERE
,
584 AppIdToMessageCountMap()));
587 leveldb::ReadOptions read_options
;
588 leveldb::WriteOptions write_options
;
589 write_options
.sync
= true;
591 AppIdToMessageCountMap removed_message_counts
;
594 for (PersistentIdList::const_iterator iter
= persistent_ids
.begin();
595 iter
!= persistent_ids
.end();
597 DVLOG(1) << "Removing outgoing message with id " << *iter
;
598 std::string outgoing_message
;
599 std::string key
= MakeOutgoingKey(*iter
);
600 s
= db_
->Get(read_options
,
605 mcs_proto::DataMessageStanza data_message
;
606 // Skip the initial tag byte and parse the rest to extract the message.
607 if (data_message
.ParseFromString(outgoing_message
.substr(1))) {
608 DCHECK(!data_message
.category().empty());
609 if (removed_message_counts
.count(data_message
.category()) != 0)
610 removed_message_counts
[data_message
.category()]++;
612 removed_message_counts
[data_message
.category()] = 1;
614 DVLOG(1) << "Removing outgoing message with id " << *iter
;
615 s
= db_
->Delete(write_options
, MakeSlice(key
));
620 foreground_task_runner_
->PostTask(FROM_HERE
,
623 removed_message_counts
));
626 LOG(ERROR
) << "LevelDB remove failed: " << s
.ToString();
627 foreground_task_runner_
->PostTask(FROM_HERE
,
630 AppIdToMessageCountMap()));
633 void GCMStoreImpl::Backend::SetLastCheckinInfo(
634 const base::Time
& time
,
635 const std::set
<std::string
>& accounts
,
636 const UpdateCallback
& callback
) {
637 leveldb::WriteBatch write_batch
;
639 int64 last_checkin_time_internal
= time
.ToInternalValue();
640 write_batch
.Put(MakeSlice(kLastCheckinTimeKey
),
641 MakeSlice(base::Int64ToString(last_checkin_time_internal
)));
643 std::string serialized_accounts
;
644 for (std::set
<std::string
>::iterator iter
= accounts
.begin();
645 iter
!= accounts
.end();
647 serialized_accounts
+= *iter
;
648 serialized_accounts
+= ",";
650 if (!serialized_accounts
.empty())
651 serialized_accounts
.erase(serialized_accounts
.length() - 1);
653 write_batch
.Put(MakeSlice(kLastCheckinAccountsKey
),
654 MakeSlice(serialized_accounts
));
656 leveldb::WriteOptions write_options
;
657 write_options
.sync
= true;
658 const leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
661 LOG(ERROR
) << "LevelDB set last checkin info failed: " << s
.ToString();
662 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
665 void GCMStoreImpl::AddInstanceIDData(const std::string
& app_id
,
666 const std::string
& instance_id_data
,
667 const UpdateCallback
& callback
) {
668 blocking_task_runner_
->PostTask(
670 base::Bind(&GCMStoreImpl::Backend::AddInstanceIDData
,
677 void GCMStoreImpl::RemoveInstanceIDData(const std::string
& app_id
,
678 const UpdateCallback
& callback
) {
679 blocking_task_runner_
->PostTask(
681 base::Bind(&GCMStoreImpl::Backend::RemoveInstanceIDData
,
687 void GCMStoreImpl::Backend::SetGServicesSettings(
688 const std::map
<std::string
, std::string
>& settings
,
689 const std::string
& settings_digest
,
690 const UpdateCallback
& callback
) {
691 leveldb::WriteBatch write_batch
;
693 // Remove all existing settings.
694 leveldb::ReadOptions read_options
;
695 read_options
.verify_checksums
= true;
696 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
697 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
698 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
700 write_batch
.Delete(iter
->key());
703 // Add the new settings.
704 for (std::map
<std::string
, std::string
>::const_iterator iter
=
706 iter
!= settings
.end(); ++iter
) {
707 write_batch
.Put(MakeSlice(MakeGServiceSettingKey(iter
->first
)),
708 MakeSlice(iter
->second
));
711 // Update the settings digest.
712 write_batch
.Put(MakeSlice(kGServiceSettingsDigestKey
),
713 MakeSlice(settings_digest
));
715 // Write it all in a batch.
716 leveldb::WriteOptions write_options
;
717 write_options
.sync
= true;
719 leveldb::Status s
= db_
->Write(write_options
, &write_batch
);
721 LOG(ERROR
) << "LevelDB GService Settings update failed: " << s
.ToString();
722 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
725 void GCMStoreImpl::Backend::AddAccountMapping(
726 const AccountMapping
& account_mapping
,
727 const UpdateCallback
& callback
) {
728 DVLOG(1) << "Saving account info for account with email: "
729 << account_mapping
.email
;
731 LOG(ERROR
) << "GCMStore db doesn't exist.";
732 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
736 leveldb::WriteOptions write_options
;
737 write_options
.sync
= true;
739 std::string data
= account_mapping
.SerializeAsString();
740 std::string key
= MakeAccountKey(account_mapping
.account_id
);
741 const leveldb::Status s
=
742 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(data
));
744 LOG(ERROR
) << "LevelDB adding account mapping failed: " << s
.ToString();
745 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
748 void GCMStoreImpl::Backend::RemoveAccountMapping(
749 const std::string
& account_id
,
750 const UpdateCallback
& callback
) {
752 LOG(ERROR
) << "GCMStore db doesn't exist.";
753 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
757 leveldb::WriteOptions write_options
;
758 write_options
.sync
= true;
761 db_
->Delete(write_options
, MakeSlice(MakeAccountKey(account_id
)));
764 LOG(ERROR
) << "LevelDB removal of account mapping failed: " << s
.ToString();
765 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
768 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
769 const base::Time
& time
,
770 const UpdateCallback
& callback
) {
771 DVLOG(1) << "Setting last token fetching time.";
773 LOG(ERROR
) << "GCMStore db doesn't exist.";
774 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
778 leveldb::WriteOptions write_options
;
779 write_options
.sync
= true;
781 const leveldb::Status s
=
782 db_
->Put(write_options
,
783 MakeSlice(kLastTokenFetchTimeKey
),
784 MakeSlice(base::Int64ToString(time
.ToInternalValue())));
787 LOG(ERROR
) << "LevelDB setting last token fetching time: " << s
.ToString();
788 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
791 void GCMStoreImpl::Backend::AddHeartbeatInterval(
792 const std::string
& scope
,
794 const UpdateCallback
& callback
) {
795 DVLOG(1) << "Saving a heartbeat interval: scope: " << scope
796 << " interval: " << interval_ms
<< "ms.";
798 LOG(ERROR
) << "GCMStore db doesn't exist.";
799 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
803 leveldb::WriteOptions write_options
;
804 write_options
.sync
= true;
806 std::string data
= base::IntToString(interval_ms
);
807 std::string key
= MakeHeartbeatKey(scope
);
808 const leveldb::Status s
=
809 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(data
));
811 LOG(ERROR
) << "LevelDB adding heartbeat interval failed: " << s
.ToString();
812 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
815 void GCMStoreImpl::Backend::RemoveHeartbeatInterval(
816 const std::string
& scope
,
817 const UpdateCallback
& callback
) {
819 LOG(ERROR
) << "GCMStore db doesn't exist.";
820 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
824 leveldb::WriteOptions write_options
;
825 write_options
.sync
= true;
828 db_
->Delete(write_options
, MakeSlice(MakeHeartbeatKey(scope
)));
831 LOG(ERROR
) << "LevelDB removal of heartbeat interval failed: "
834 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
837 void GCMStoreImpl::Backend::AddInstanceIDData(
838 const std::string
& app_id
,
839 const std::string
& instance_id_data
,
840 const UpdateCallback
& callback
) {
841 DVLOG(1) << "Adding Instance ID data.";
843 LOG(ERROR
) << "GCMStore db doesn't exist.";
844 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
848 leveldb::WriteOptions write_options
;
849 write_options
.sync
= true;
851 std::string key
= MakeInstanceIDKey(app_id
);
852 const leveldb::Status status
= db_
->Put(write_options
,
854 MakeSlice(instance_id_data
));
856 LOG(ERROR
) << "LevelDB put failed: " << status
.ToString();
857 foreground_task_runner_
->PostTask(
858 FROM_HERE
, base::Bind(callback
, status
.ok()));
861 void GCMStoreImpl::Backend::RemoveInstanceIDData(
862 const std::string
& app_id
,
863 const UpdateCallback
& callback
) {
865 LOG(ERROR
) << "GCMStore db doesn't exist.";
866 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
869 leveldb::WriteOptions write_options
;
870 write_options
.sync
= true;
872 leveldb::Status status
=
873 db_
->Delete(write_options
, MakeSlice(MakeInstanceIDKey(app_id
)));
875 LOG(ERROR
) << "LevelDB remove failed: " << status
.ToString();
876 foreground_task_runner_
->PostTask(
877 FROM_HERE
, base::Bind(callback
, status
.ok()));
880 void GCMStoreImpl::Backend::SetValue(const std::string
& key
,
881 const std::string
& value
,
882 const UpdateCallback
& callback
) {
883 DVLOG(1) << "Injecting a value to GCM Store for testing. Key: "
884 << key
<< ", Value: " << value
;
886 LOG(ERROR
) << "GCMStore db doesn't exist.";
887 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, false));
891 leveldb::WriteOptions write_options
;
892 write_options
.sync
= true;
894 const leveldb::Status s
=
895 db_
->Put(write_options
, MakeSlice(key
), MakeSlice(value
));
898 LOG(ERROR
) << "LevelDB had problems injecting a value: " << s
.ToString();
899 foreground_task_runner_
->PostTask(FROM_HERE
, base::Bind(callback
, s
.ok()));
902 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64
* android_id
,
903 uint64
* security_token
) {
904 leveldb::ReadOptions read_options
;
905 read_options
.verify_checksums
= true;
908 leveldb::Status s
= db_
->Get(read_options
, MakeSlice(kDeviceAIDKey
), &result
);
910 if (!base::StringToUint64(result
, android_id
)) {
911 LOG(ERROR
) << "Failed to restore device id.";
915 s
= db_
->Get(read_options
, MakeSlice(kDeviceTokenKey
), &result
);
918 std::string decrypted_token
;
919 encryptor_
->DecryptString(result
, &decrypted_token
);
920 if (!base::StringToUint64(decrypted_token
, security_token
)) {
921 LOG(ERROR
) << "Failed to restore security token.";
927 if (s
.IsNotFound()) {
928 DVLOG(1) << "No credentials found.";
932 LOG(ERROR
) << "Error reading credentials from store.";
936 bool GCMStoreImpl::Backend::LoadRegistrations(
937 std::map
<std::string
, std::string
>* registrations
) {
938 leveldb::ReadOptions read_options
;
939 read_options
.verify_checksums
= true;
941 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
942 for (iter
->Seek(MakeSlice(kRegistrationKeyStart
));
943 iter
->Valid() && iter
->key().ToString() < kRegistrationKeyEnd
;
945 leveldb::Slice s
= iter
->value();
947 LOG(ERROR
) << "Error reading registration with key " << s
.ToString();
950 std::string app_id
= ParseRegistrationKey(iter
->key().ToString());
951 DVLOG(1) << "Found registration with app id " << app_id
;
952 (*registrations
)[app_id
] = iter
->value().ToString();
958 bool GCMStoreImpl::Backend::LoadIncomingMessages(
959 std::vector
<std::string
>* incoming_messages
) {
960 leveldb::ReadOptions read_options
;
961 read_options
.verify_checksums
= true;
963 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
964 for (iter
->Seek(MakeSlice(kIncomingMsgKeyStart
));
965 iter
->Valid() && iter
->key().ToString() < kIncomingMsgKeyEnd
;
967 leveldb::Slice s
= iter
->value();
969 LOG(ERROR
) << "Error reading incoming message with key "
970 << iter
->key().ToString();
973 DVLOG(1) << "Found incoming message with id " << s
.ToString();
974 incoming_messages
->push_back(s
.ToString());
980 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
981 OutgoingMessageMap
* outgoing_messages
) {
982 leveldb::ReadOptions read_options
;
983 read_options
.verify_checksums
= true;
985 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
986 for (iter
->Seek(MakeSlice(kOutgoingMsgKeyStart
));
987 iter
->Valid() && iter
->key().ToString() < kOutgoingMsgKeyEnd
;
989 leveldb::Slice s
= iter
->value();
991 LOG(ERROR
) << "Error reading incoming message with key " << s
.ToString();
994 uint8 tag
= iter
->value().data()[0];
995 std::string id
= ParseOutgoingKey(iter
->key().ToString());
996 scoped_ptr
<google::protobuf::MessageLite
> message(
997 BuildProtobufFromTag(tag
));
998 if (!message
.get() ||
999 !message
->ParseFromString(iter
->value().ToString().substr(1))) {
1000 LOG(ERROR
) << "Failed to parse outgoing message with id " << id
1001 << " and tag " << tag
;
1004 DVLOG(1) << "Found outgoing message with id " << id
<< " of type "
1005 << base::IntToString(tag
);
1006 (*outgoing_messages
)[id
] = make_linked_ptr(message
.release());
1012 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
1013 base::Time
* last_checkin_time
,
1014 std::set
<std::string
>* accounts
) {
1015 leveldb::ReadOptions read_options
;
1016 read_options
.verify_checksums
= true;
1019 leveldb::Status s
= db_
->Get(read_options
,
1020 MakeSlice(kLastCheckinTimeKey
),
1022 int64 time_internal
= 0LL;
1023 if (s
.ok() && !base::StringToInt64(result
, &time_internal
)) {
1024 LOG(ERROR
) << "Failed to restore last checkin time. Using default = 0.";
1025 time_internal
= 0LL;
1028 // In case we cannot read last checkin time, we default it to 0, as we don't
1029 // want that situation to cause the whole load to fail.
1030 *last_checkin_time
= base::Time::FromInternalValue(time_internal
);
1033 s
= db_
->Get(read_options
, MakeSlice(kLastCheckinAccountsKey
), &result
);
1035 DVLOG(1) << "No accounts where stored during last run.";
1037 base::StringTokenizer
t(result
, ",");
1039 accounts
->insert(t
.token());
1044 bool GCMStoreImpl::Backend::LoadGServicesSettings(
1045 std::map
<std::string
, std::string
>* settings
,
1046 std::string
* digest
) {
1047 leveldb::ReadOptions read_options
;
1048 read_options
.verify_checksums
= true;
1050 // Load all of the GServices settings.
1051 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
1052 for (iter
->Seek(MakeSlice(kGServiceSettingKeyStart
));
1053 iter
->Valid() && iter
->key().ToString() < kGServiceSettingKeyEnd
;
1055 std::string value
= iter
->value().ToString();
1056 if (value
.empty()) {
1057 LOG(ERROR
) << "Error reading GService Settings " << value
;
1060 std::string id
= ParseGServiceSettingKey(iter
->key().ToString());
1061 (*settings
)[id
] = value
;
1062 DVLOG(1) << "Found G Service setting with key: " << id
1063 << ", and value: " << value
;
1066 // Load the settings digest. It's ok if it is empty.
1067 db_
->Get(read_options
, MakeSlice(kGServiceSettingsDigestKey
), digest
);
1072 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
1073 AccountMappings
* account_mappings
) {
1074 leveldb::ReadOptions read_options
;
1075 read_options
.verify_checksums
= true;
1077 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
1078 for (iter
->Seek(MakeSlice(kAccountKeyStart
));
1079 iter
->Valid() && iter
->key().ToString() < kAccountKeyEnd
;
1081 AccountMapping account_mapping
;
1082 account_mapping
.account_id
= ParseAccountKey(iter
->key().ToString());
1083 if (!account_mapping
.ParseFromString(iter
->value().ToString())) {
1084 DVLOG(1) << "Failed to parse account info with ID: "
1085 << account_mapping
.account_id
;
1088 DVLOG(1) << "Found account mapping with ID: " << account_mapping
.account_id
;
1089 account_mappings
->push_back(account_mapping
);
1095 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
1096 base::Time
* last_token_fetch_time
) {
1097 leveldb::ReadOptions read_options
;
1098 read_options
.verify_checksums
= true;
1102 db_
->Get(read_options
, MakeSlice(kLastTokenFetchTimeKey
), &result
);
1103 int64 time_internal
= 0LL;
1104 if (s
.ok() && !base::StringToInt64(result
, &time_internal
)) {
1106 "Failed to restore last token fetching time. Using default = 0.";
1107 time_internal
= 0LL;
1110 // In case we cannot read last token fetching time, we default it to 0.
1111 *last_token_fetch_time
= base::Time::FromInternalValue(time_internal
);
1116 bool GCMStoreImpl::Backend::LoadHeartbeatIntervals(
1117 std::map
<std::string
, int>* heartbeat_intervals
) {
1118 leveldb::ReadOptions read_options
;
1119 read_options
.verify_checksums
= true;
1121 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
1122 for (iter
->Seek(MakeSlice(kHeartbeatKeyStart
));
1123 iter
->Valid() && iter
->key().ToString() < kHeartbeatKeyEnd
;
1125 std::string scope
= ParseHeartbeatKey(iter
->key().ToString());
1127 if (!base::StringToInt(iter
->value().ToString(), &interval_ms
)) {
1128 DVLOG(1) << "Failed to parse heartbeat interval info with scope: "
1132 DVLOG(1) << "Found heartbeat interval with scope: " << scope
1133 << " interval: " << interval_ms
<< "ms.";
1134 (*heartbeat_intervals
)[scope
] = interval_ms
;
1140 bool GCMStoreImpl::Backend::LoadInstanceIDData(
1141 std::map
<std::string
, std::string
>* instance_id_data
) {
1142 leveldb::ReadOptions read_options
;
1143 read_options
.verify_checksums
= true;
1145 scoped_ptr
<leveldb::Iterator
> iter(db_
->NewIterator(read_options
));
1146 for (iter
->Seek(MakeSlice(kInstanceIDKeyStart
));
1147 iter
->Valid() && iter
->key().ToString() < kInstanceIDKeyEnd
;
1149 leveldb::Slice s
= iter
->value();
1150 if (s
.size() <= 1) {
1151 LOG(ERROR
) << "Error reading IID data with key " << s
.ToString();
1154 std::string app_id
= ParseInstanceIDKey(iter
->key().ToString());
1155 DVLOG(1) << "Found IID data with app id " << app_id
;
1156 (*instance_id_data
)[app_id
] = s
.ToString();
1162 GCMStoreImpl::GCMStoreImpl(
1163 const base::FilePath
& path
,
1164 scoped_refptr
<base::SequencedTaskRunner
> blocking_task_runner
,
1165 scoped_ptr
<Encryptor
> encryptor
)
1166 : backend_(new Backend(path
,
1167 base::ThreadTaskRunnerHandle::Get(),
1169 blocking_task_runner_(blocking_task_runner
),
1170 weak_ptr_factory_(this) {
1173 GCMStoreImpl::~GCMStoreImpl() {}
1175 void GCMStoreImpl::Load(StoreOpenMode open_mode
, const LoadCallback
& callback
) {
1176 blocking_task_runner_
->PostTask(
1178 base::Bind(&GCMStoreImpl::Backend::Load
,
1181 base::Bind(&GCMStoreImpl::LoadContinuation
,
1182 weak_ptr_factory_
.GetWeakPtr(),
1186 void GCMStoreImpl::Close() {
1187 weak_ptr_factory_
.InvalidateWeakPtrs();
1188 app_message_counts_
.clear();
1189 blocking_task_runner_
->PostTask(
1191 base::Bind(&GCMStoreImpl::Backend::Close
, backend_
));
1194 void GCMStoreImpl::Destroy(const UpdateCallback
& callback
) {
1195 blocking_task_runner_
->PostTask(
1197 base::Bind(&GCMStoreImpl::Backend::Destroy
, backend_
, callback
));
1200 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id
,
1201 uint64 device_security_token
,
1202 const UpdateCallback
& callback
) {
1203 blocking_task_runner_
->PostTask(
1205 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials
,
1208 device_security_token
,
1212 void GCMStoreImpl::AddRegistration(
1213 const std::string
& serialized_key
,
1214 const std::string
& serialized_value
,
1215 const UpdateCallback
& callback
) {
1216 blocking_task_runner_
->PostTask(
1218 base::Bind(&GCMStoreImpl::Backend::AddRegistration
,
1225 void GCMStoreImpl::RemoveRegistration(const std::string
& app_id
,
1226 const UpdateCallback
& callback
) {
1227 blocking_task_runner_
->PostTask(
1229 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration
,
1235 void GCMStoreImpl::AddIncomingMessage(const std::string
& persistent_id
,
1236 const UpdateCallback
& callback
) {
1237 blocking_task_runner_
->PostTask(
1239 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage
,
1245 void GCMStoreImpl::RemoveIncomingMessage(const std::string
& persistent_id
,
1246 const UpdateCallback
& callback
) {
1247 blocking_task_runner_
->PostTask(
1249 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
1251 PersistentIdList(1, persistent_id
),
1255 void GCMStoreImpl::RemoveIncomingMessages(
1256 const PersistentIdList
& persistent_ids
,
1257 const UpdateCallback
& callback
) {
1258 blocking_task_runner_
->PostTask(
1260 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages
,
1266 bool GCMStoreImpl::AddOutgoingMessage(const std::string
& persistent_id
,
1267 const MCSMessage
& message
,
1268 const UpdateCallback
& callback
) {
1269 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
1270 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
1271 &message
.GetProtobuf())->category();
1272 DCHECK(!app_id
.empty());
1273 if (app_message_counts_
.count(app_id
) == 0)
1274 app_message_counts_
[app_id
] = 0;
1275 if (app_message_counts_
[app_id
] < kMessagesPerAppLimit
) {
1276 app_message_counts_
[app_id
]++;
1278 blocking_task_runner_
->PostTask(
1280 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1284 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation
,
1285 weak_ptr_factory_
.GetWeakPtr(),
1293 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string
& persistent_id
,
1294 const MCSMessage
& message
,
1295 const UpdateCallback
& callback
) {
1296 DCHECK_EQ(message
.tag(), kDataMessageStanzaTag
);
1297 std::string app_id
= reinterpret_cast<const mcs_proto::DataMessageStanza
*>(
1298 &message
.GetProtobuf())->category();
1299 DCHECK(!app_id
.empty());
1300 // There should already be pending messages for this app.
1301 DCHECK(app_message_counts_
.count(app_id
));
1302 // TODO(zea): consider verifying the specific message already exists.
1303 blocking_task_runner_
->PostTask(
1305 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage
,
1312 void GCMStoreImpl::RemoveOutgoingMessage(const std::string
& persistent_id
,
1313 const UpdateCallback
& callback
) {
1314 blocking_task_runner_
->PostTask(
1316 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1318 PersistentIdList(1, persistent_id
),
1319 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1320 weak_ptr_factory_
.GetWeakPtr(),
1324 void GCMStoreImpl::RemoveOutgoingMessages(
1325 const PersistentIdList
& persistent_ids
,
1326 const UpdateCallback
& callback
) {
1327 blocking_task_runner_
->PostTask(
1329 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages
,
1332 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation
,
1333 weak_ptr_factory_
.GetWeakPtr(),
1337 void GCMStoreImpl::SetLastCheckinInfo(const base::Time
& time
,
1338 const std::set
<std::string
>& accounts
,
1339 const UpdateCallback
& callback
) {
1340 blocking_task_runner_
->PostTask(
1342 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo
,
1349 void GCMStoreImpl::SetGServicesSettings(
1350 const std::map
<std::string
, std::string
>& settings
,
1351 const std::string
& digest
,
1352 const UpdateCallback
& callback
) {
1353 blocking_task_runner_
->PostTask(
1355 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings
,
1362 void GCMStoreImpl::AddAccountMapping(const AccountMapping
& account_mapping
,
1363 const UpdateCallback
& callback
) {
1364 blocking_task_runner_
->PostTask(
1366 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping
,
1372 void GCMStoreImpl::RemoveAccountMapping(const std::string
& account_id
,
1373 const UpdateCallback
& callback
) {
1374 blocking_task_runner_
->PostTask(
1376 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping
,
1382 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time
& time
,
1383 const UpdateCallback
& callback
) {
1384 blocking_task_runner_
->PostTask(
1386 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime
,
1392 void GCMStoreImpl::AddHeartbeatInterval(const std::string
& scope
,
1394 const UpdateCallback
& callback
) {
1395 blocking_task_runner_
->PostTask(
1397 base::Bind(&GCMStoreImpl::Backend::AddHeartbeatInterval
,
1404 void GCMStoreImpl::RemoveHeartbeatInterval(const std::string
& scope
,
1405 const UpdateCallback
& callback
) {
1406 blocking_task_runner_
->PostTask(
1408 base::Bind(&GCMStoreImpl::Backend::RemoveHeartbeatInterval
,
1414 void GCMStoreImpl::SetValueForTesting(const std::string
& key
,
1415 const std::string
& value
,
1416 const UpdateCallback
& callback
) {
1417 blocking_task_runner_
->PostTask(
1419 base::Bind(&GCMStoreImpl::Backend::SetValue
,
1426 void GCMStoreImpl::LoadContinuation(const LoadCallback
& callback
,
1427 scoped_ptr
<LoadResult
> result
) {
1428 // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is fixed.
1429 tracked_objects::ScopedTracker
tracking_profile(
1430 FROM_HERE_WITH_EXPLICIT_FUNCTION(
1431 "477117 GCMStoreImpl::LoadContinuation"));
1432 if (!result
->success
) {
1433 callback
.Run(result
.Pass());
1436 int num_throttled_apps
= 0;
1437 for (OutgoingMessageMap::const_iterator
1438 iter
= result
->outgoing_messages
.begin();
1439 iter
!= result
->outgoing_messages
.end(); ++iter
) {
1440 const mcs_proto::DataMessageStanza
* data_message
=
1441 reinterpret_cast<mcs_proto::DataMessageStanza
*>(iter
->second
.get());
1442 DCHECK(!data_message
->category().empty());
1443 if (app_message_counts_
.count(data_message
->category()) == 0)
1444 app_message_counts_
[data_message
->category()] = 1;
1446 app_message_counts_
[data_message
->category()]++;
1447 if (app_message_counts_
[data_message
->category()] == kMessagesPerAppLimit
)
1448 num_throttled_apps
++;
1450 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps
);
1451 callback
.Run(result
.Pass());
1454 void GCMStoreImpl::AddOutgoingMessageContinuation(
1455 const UpdateCallback
& callback
,
1456 const std::string
& app_id
,
1459 DCHECK(app_message_counts_
[app_id
] > 0);
1460 app_message_counts_
[app_id
]--;
1462 callback
.Run(success
);
1465 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1466 const UpdateCallback
& callback
,
1468 const AppIdToMessageCountMap
& removed_message_counts
) {
1470 callback
.Run(false);
1473 for (AppIdToMessageCountMap::const_iterator iter
=
1474 removed_message_counts
.begin();
1475 iter
!= removed_message_counts
.end(); ++iter
) {
1476 DCHECK_NE(app_message_counts_
.count(iter
->first
), 0U);
1477 app_message_counts_
[iter
->first
] -= iter
->second
;
1478 DCHECK_GE(app_message_counts_
[iter
->first
], 0);