Roll src/third_party/WebKit 3529d49:06e8485 (svn 202554:202555)
[chromium-blink-merge.git] / google_apis / gcm / engine / gcm_store_impl.cc
blob8ac2521028ec444ee9a82919cc9f83156492b621
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"
8 #include "base/bind.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"
32 namespace gcm {
34 namespace {
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.
39 enum LoadStatus {
40 LOADING_SUCCEEDED,
41 RELOADING_OPEN_STORE,
42 OPENING_STORE_FAILED,
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,
53 STORE_DOES_NOT_EXIST,
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.
58 LOAD_STATUS_COUNT
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());
179 } // namespace
181 class GCMStoreImpl::Backend
182 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
183 public:
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);
190 void Close();
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&)>
210 callback);
211 void AddUserSerialNumber(const std::string& username,
212 int64 serial_number,
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,
230 int interval_ms,
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);
243 private:
244 friend class base::RefCountedThreadSafe<Backend>;
245 ~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)
272 : path_(path),
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;
282 if (db_.get()) {
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;
298 leveldb::DB* db;
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);
304 if (!status.ok()) {
305 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
306 << status.ToString();
307 return OPENING_STORE_FAILED;
310 db_.reset(db);
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) {
347 result->Reset();
348 result->store_does_not_exist = (load_status == STORE_DOES_NOT_EXIST);
349 foreground_task_runner_->PostTask(FROM_HERE,
350 base::Bind(callback,
351 base::Passed(&result)));
352 return;
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++;
363 else
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) {
369 int64 file_size = 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,
397 base::Bind(callback,
398 base::Passed(&result)));
399 return;
402 void GCMStoreImpl::Backend::Close() {
403 DVLOG(1) << "Closing GCM store.";
404 db_.reset();
407 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
408 DVLOG(1) << "Destroying GCM store.";
409 db_.reset();
410 const leveldb::Status s =
411 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
412 if (s.ok()) {
413 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
414 return;
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;
425 if (!db_.get()) {
426 LOG(ERROR) << "GCMStore db doesn't exist.";
427 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
428 return;
431 leveldb::WriteOptions write_options;
432 write_options.sync = true;
434 std::string encrypted_token;
435 encryptor_->EncryptString(base::Uint64ToString(device_security_token),
436 &encrypted_token);
437 std::string android_id_str = base::Uint64ToString(device_android_id);
438 leveldb::Status s =
439 db_->Put(write_options,
440 MakeSlice(kDeviceAIDKey),
441 MakeSlice(android_id_str));
442 if (s.ok()) {
443 s = db_->Put(
444 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
446 if (s.ok()) {
447 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
448 return;
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;
459 if (!db_.get()) {
460 LOG(ERROR) << "GCMStore db doesn't exist.";
461 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
462 return;
464 leveldb::WriteOptions write_options;
465 write_options.sync = true;
467 const leveldb::Status status = db_->Put(
468 write_options,
469 MakeSlice(MakeRegistrationKey(serialized_key)),
470 MakeSlice(serialized_value));
471 if (!status.ok())
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) {
480 if (!db_.get()) {
481 LOG(ERROR) << "GCMStore db doesn't exist.";
482 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
483 return;
485 leveldb::WriteOptions write_options;
486 write_options.sync = true;
488 leveldb::Status status = db_->Delete(
489 write_options, MakeSlice(MakeRegistrationKey(serialized_key)));
490 if (!status.ok())
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;
499 if (!db_.get()) {
500 LOG(ERROR) << "GCMStore db doesn't exist.";
501 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
502 return;
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,
510 MakeSlice(key),
511 MakeSlice(persistent_id));
512 if (s.ok()) {
513 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
514 return;
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) {
523 if (!db_.get()) {
524 LOG(ERROR) << "GCMStore db doesn't exist.";
525 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
526 return;
528 leveldb::WriteOptions write_options;
529 write_options.sync = true;
531 leveldb::Status s;
532 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
533 iter != persistent_ids.end();
534 ++iter) {
535 DVLOG(1) << "Removing incoming message with id " << *iter;
536 std::string key = MakeIncomingKey(*iter);
537 s = db_->Delete(write_options, MakeSlice(key));
538 if (!s.ok())
539 break;
541 if (s.ok()) {
542 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
543 return;
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;
553 if (!db_.get()) {
554 LOG(ERROR) << "GCMStore db doesn't exist.";
555 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
556 return;
558 leveldb::WriteOptions write_options;
559 write_options.sync = true;
561 std::string data =
562 static_cast<char>(message.tag()) + message.SerializeAsString();
563 std::string key = MakeOutgoingKey(persistent_id);
564 const leveldb::Status s = db_->Put(write_options,
565 MakeSlice(key),
566 MakeSlice(data));
567 if (s.ok()) {
568 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
569 return;
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&)>
578 callback) {
579 if (!db_.get()) {
580 LOG(ERROR) << "GCMStore db doesn't exist.";
581 foreground_task_runner_->PostTask(FROM_HERE,
582 base::Bind(callback,
583 false,
584 AppIdToMessageCountMap()));
585 return;
587 leveldb::ReadOptions read_options;
588 leveldb::WriteOptions write_options;
589 write_options.sync = true;
591 AppIdToMessageCountMap removed_message_counts;
593 leveldb::Status s;
594 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
595 iter != persistent_ids.end();
596 ++iter) {
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,
601 MakeSlice(key),
602 &outgoing_message);
603 if (!s.ok())
604 break;
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()]++;
611 else
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));
616 if (!s.ok())
617 break;
619 if (s.ok()) {
620 foreground_task_runner_->PostTask(FROM_HERE,
621 base::Bind(callback,
622 true,
623 removed_message_counts));
624 return;
626 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
627 foreground_task_runner_->PostTask(FROM_HERE,
628 base::Bind(callback,
629 false,
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();
646 ++iter) {
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);
660 if (!s.ok())
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(
669 FROM_HERE,
670 base::Bind(&GCMStoreImpl::Backend::AddInstanceIDData,
671 backend_,
672 app_id,
673 instance_id_data,
674 callback));
677 void GCMStoreImpl::RemoveInstanceIDData(const std::string& app_id,
678 const UpdateCallback& callback) {
679 blocking_task_runner_->PostTask(
680 FROM_HERE,
681 base::Bind(&GCMStoreImpl::Backend::RemoveInstanceIDData,
682 backend_,
683 app_id,
684 callback));
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;
699 iter->Next()) {
700 write_batch.Delete(iter->key());
703 // Add the new settings.
704 for (std::map<std::string, std::string>::const_iterator iter =
705 settings.begin();
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);
720 if (!s.ok())
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;
730 if (!db_.get()) {
731 LOG(ERROR) << "GCMStore db doesn't exist.";
732 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
733 return;
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));
743 if (!s.ok())
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) {
751 if (!db_.get()) {
752 LOG(ERROR) << "GCMStore db doesn't exist.";
753 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
754 return;
757 leveldb::WriteOptions write_options;
758 write_options.sync = true;
760 leveldb::Status s =
761 db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
763 if (!s.ok())
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.";
772 if (!db_.get()) {
773 LOG(ERROR) << "GCMStore db doesn't exist.";
774 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
775 return;
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())));
786 if (!s.ok())
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,
793 int interval_ms,
794 const UpdateCallback& callback) {
795 DVLOG(1) << "Saving a heartbeat interval: scope: " << scope
796 << " interval: " << interval_ms << "ms.";
797 if (!db_.get()) {
798 LOG(ERROR) << "GCMStore db doesn't exist.";
799 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
800 return;
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));
810 if (!s.ok())
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) {
818 if (!db_.get()) {
819 LOG(ERROR) << "GCMStore db doesn't exist.";
820 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
821 return;
824 leveldb::WriteOptions write_options;
825 write_options.sync = true;
827 leveldb::Status s =
828 db_->Delete(write_options, MakeSlice(MakeHeartbeatKey(scope)));
830 if (!s.ok()) {
831 LOG(ERROR) << "LevelDB removal of heartbeat interval failed: "
832 << s.ToString();
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.";
842 if (!db_.get()) {
843 LOG(ERROR) << "GCMStore db doesn't exist.";
844 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
845 return;
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,
853 MakeSlice(key),
854 MakeSlice(instance_id_data));
855 if (!status.ok())
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) {
864 if (!db_.get()) {
865 LOG(ERROR) << "GCMStore db doesn't exist.";
866 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
867 return;
869 leveldb::WriteOptions write_options;
870 write_options.sync = true;
872 leveldb::Status status =
873 db_->Delete(write_options, MakeSlice(MakeInstanceIDKey(app_id)));
874 if (!status.ok())
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;
885 if (!db_.get()) {
886 LOG(ERROR) << "GCMStore db doesn't exist.";
887 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
888 return;
891 leveldb::WriteOptions write_options;
892 write_options.sync = true;
894 const leveldb::Status s =
895 db_->Put(write_options, MakeSlice(key), MakeSlice(value));
897 if (!s.ok())
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;
907 std::string result;
908 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
909 if (s.ok()) {
910 if (!base::StringToUint64(result, android_id)) {
911 LOG(ERROR) << "Failed to restore device id.";
912 return false;
914 result.clear();
915 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
917 if (s.ok()) {
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.";
922 return false;
924 return true;
927 if (s.IsNotFound()) {
928 DVLOG(1) << "No credentials found.";
929 return true;
932 LOG(ERROR) << "Error reading credentials from store.";
933 return false;
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;
944 iter->Next()) {
945 leveldb::Slice s = iter->value();
946 if (s.size() <= 1) {
947 LOG(ERROR) << "Error reading registration with key " << s.ToString();
948 return false;
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();
955 return true;
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;
966 iter->Next()) {
967 leveldb::Slice s = iter->value();
968 if (s.empty()) {
969 LOG(ERROR) << "Error reading incoming message with key "
970 << iter->key().ToString();
971 return false;
973 DVLOG(1) << "Found incoming message with id " << s.ToString();
974 incoming_messages->push_back(s.ToString());
977 return true;
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;
988 iter->Next()) {
989 leveldb::Slice s = iter->value();
990 if (s.size() <= 1) {
991 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
992 return false;
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;
1002 return false;
1004 DVLOG(1) << "Found outgoing message with id " << id << " of type "
1005 << base::IntToString(tag);
1006 (*outgoing_messages)[id] = make_linked_ptr(message.release());
1009 return true;
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;
1018 std::string result;
1019 leveldb::Status s = db_->Get(read_options,
1020 MakeSlice(kLastCheckinTimeKey),
1021 &result);
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);
1032 accounts->clear();
1033 s = db_->Get(read_options, MakeSlice(kLastCheckinAccountsKey), &result);
1034 if (!s.ok())
1035 DVLOG(1) << "No accounts where stored during last run.";
1037 base::StringTokenizer t(result, ",");
1038 while (t.GetNext())
1039 accounts->insert(t.token());
1041 return true;
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;
1054 iter->Next()) {
1055 std::string value = iter->value().ToString();
1056 if (value.empty()) {
1057 LOG(ERROR) << "Error reading GService Settings " << value;
1058 return false;
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);
1069 return true;
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;
1080 iter->Next()) {
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;
1086 return false;
1088 DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
1089 account_mappings->push_back(account_mapping);
1092 return true;
1095 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
1096 base::Time* last_token_fetch_time) {
1097 leveldb::ReadOptions read_options;
1098 read_options.verify_checksums = true;
1100 std::string result;
1101 leveldb::Status s =
1102 db_->Get(read_options, MakeSlice(kLastTokenFetchTimeKey), &result);
1103 int64 time_internal = 0LL;
1104 if (s.ok() && !base::StringToInt64(result, &time_internal)) {
1105 LOG(ERROR) <<
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);
1113 return true;
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;
1124 iter->Next()) {
1125 std::string scope = ParseHeartbeatKey(iter->key().ToString());
1126 int interval_ms;
1127 if (!base::StringToInt(iter->value().ToString(), &interval_ms)) {
1128 DVLOG(1) << "Failed to parse heartbeat interval info with scope: "
1129 << scope;
1130 return false;
1132 DVLOG(1) << "Found heartbeat interval with scope: " << scope
1133 << " interval: " << interval_ms << "ms.";
1134 (*heartbeat_intervals)[scope] = interval_ms;
1137 return true;
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;
1148 iter->Next()) {
1149 leveldb::Slice s = iter->value();
1150 if (s.size() <= 1) {
1151 LOG(ERROR) << "Error reading IID data with key " << s.ToString();
1152 return false;
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();
1159 return true;
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(),
1168 encryptor.Pass())),
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(
1177 FROM_HERE,
1178 base::Bind(&GCMStoreImpl::Backend::Load,
1179 backend_,
1180 open_mode,
1181 base::Bind(&GCMStoreImpl::LoadContinuation,
1182 weak_ptr_factory_.GetWeakPtr(),
1183 callback)));
1186 void GCMStoreImpl::Close() {
1187 weak_ptr_factory_.InvalidateWeakPtrs();
1188 app_message_counts_.clear();
1189 blocking_task_runner_->PostTask(
1190 FROM_HERE,
1191 base::Bind(&GCMStoreImpl::Backend::Close, backend_));
1194 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
1195 blocking_task_runner_->PostTask(
1196 FROM_HERE,
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(
1204 FROM_HERE,
1205 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
1206 backend_,
1207 device_android_id,
1208 device_security_token,
1209 callback));
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(
1217 FROM_HERE,
1218 base::Bind(&GCMStoreImpl::Backend::AddRegistration,
1219 backend_,
1220 serialized_key,
1221 serialized_value,
1222 callback));
1225 void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
1226 const UpdateCallback& callback) {
1227 blocking_task_runner_->PostTask(
1228 FROM_HERE,
1229 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
1230 backend_,
1231 app_id,
1232 callback));
1235 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
1236 const UpdateCallback& callback) {
1237 blocking_task_runner_->PostTask(
1238 FROM_HERE,
1239 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
1240 backend_,
1241 persistent_id,
1242 callback));
1245 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
1246 const UpdateCallback& callback) {
1247 blocking_task_runner_->PostTask(
1248 FROM_HERE,
1249 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
1250 backend_,
1251 PersistentIdList(1, persistent_id),
1252 callback));
1255 void GCMStoreImpl::RemoveIncomingMessages(
1256 const PersistentIdList& persistent_ids,
1257 const UpdateCallback& callback) {
1258 blocking_task_runner_->PostTask(
1259 FROM_HERE,
1260 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
1261 backend_,
1262 persistent_ids,
1263 callback));
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(
1279 FROM_HERE,
1280 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1281 backend_,
1282 persistent_id,
1283 message,
1284 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
1285 weak_ptr_factory_.GetWeakPtr(),
1286 callback,
1287 app_id)));
1288 return true;
1290 return false;
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(
1304 FROM_HERE,
1305 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1306 backend_,
1307 persistent_id,
1308 message,
1309 callback));
1312 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
1313 const UpdateCallback& callback) {
1314 blocking_task_runner_->PostTask(
1315 FROM_HERE,
1316 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1317 backend_,
1318 PersistentIdList(1, persistent_id),
1319 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1320 weak_ptr_factory_.GetWeakPtr(),
1321 callback)));
1324 void GCMStoreImpl::RemoveOutgoingMessages(
1325 const PersistentIdList& persistent_ids,
1326 const UpdateCallback& callback) {
1327 blocking_task_runner_->PostTask(
1328 FROM_HERE,
1329 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1330 backend_,
1331 persistent_ids,
1332 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1333 weak_ptr_factory_.GetWeakPtr(),
1334 callback)));
1337 void GCMStoreImpl::SetLastCheckinInfo(const base::Time& time,
1338 const std::set<std::string>& accounts,
1339 const UpdateCallback& callback) {
1340 blocking_task_runner_->PostTask(
1341 FROM_HERE,
1342 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo,
1343 backend_,
1344 time,
1345 accounts,
1346 callback));
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(
1354 FROM_HERE,
1355 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
1356 backend_,
1357 settings,
1358 digest,
1359 callback));
1362 void GCMStoreImpl::AddAccountMapping(const AccountMapping& account_mapping,
1363 const UpdateCallback& callback) {
1364 blocking_task_runner_->PostTask(
1365 FROM_HERE,
1366 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
1367 backend_,
1368 account_mapping,
1369 callback));
1372 void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
1373 const UpdateCallback& callback) {
1374 blocking_task_runner_->PostTask(
1375 FROM_HERE,
1376 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
1377 backend_,
1378 account_id,
1379 callback));
1382 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time& time,
1383 const UpdateCallback& callback) {
1384 blocking_task_runner_->PostTask(
1385 FROM_HERE,
1386 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime,
1387 backend_,
1388 time,
1389 callback));
1392 void GCMStoreImpl::AddHeartbeatInterval(const std::string& scope,
1393 int interval_ms,
1394 const UpdateCallback& callback) {
1395 blocking_task_runner_->PostTask(
1396 FROM_HERE,
1397 base::Bind(&GCMStoreImpl::Backend::AddHeartbeatInterval,
1398 backend_,
1399 scope,
1400 interval_ms,
1401 callback));
1404 void GCMStoreImpl::RemoveHeartbeatInterval(const std::string& scope,
1405 const UpdateCallback& callback) {
1406 blocking_task_runner_->PostTask(
1407 FROM_HERE,
1408 base::Bind(&GCMStoreImpl::Backend::RemoveHeartbeatInterval,
1409 backend_,
1410 scope,
1411 callback));
1414 void GCMStoreImpl::SetValueForTesting(const std::string& key,
1415 const std::string& value,
1416 const UpdateCallback& callback) {
1417 blocking_task_runner_->PostTask(
1418 FROM_HERE,
1419 base::Bind(&GCMStoreImpl::Backend::SetValue,
1420 backend_,
1421 key,
1422 value,
1423 callback));
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());
1434 return;
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;
1445 else
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,
1457 bool success) {
1458 if (!success) {
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,
1467 bool success,
1468 const AppIdToMessageCountMap& removed_message_counts) {
1469 if (!success) {
1470 callback.Run(false);
1471 return;
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);
1480 callback.Run(true);
1483 } // namespace gcm