Updating trunk VERSION from 2139.0 to 2140.0
[chromium-blink-merge.git] / google_apis / gcm / engine / gcm_store_impl.cc
blobf1f46ee3563320dbd3c21c14377de7ba9d20eca6
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/file_util.h"
11 #include "base/files/file_path.h"
12 #include "base/logging.h"
13 #include "base/message_loop/message_loop_proxy.h"
14 #include "base/metrics/histogram.h"
15 #include "base/sequenced_task_runner.h"
16 #include "base/stl_util.h"
17 #include "base/strings/string_number_conversions.h"
18 #include "base/strings/string_piece.h"
19 #include "base/strings/string_tokenizer.h"
20 #include "base/time/time.h"
21 #include "base/tracked_objects.h"
22 #include "google_apis/gcm/base/encryptor.h"
23 #include "google_apis/gcm/base/mcs_message.h"
24 #include "google_apis/gcm/base/mcs_util.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "third_party/leveldatabase/src/include/leveldb/db.h"
27 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
29 namespace gcm {
31 namespace {
33 // Limit to the number of outstanding messages per app.
34 const int kMessagesPerAppLimit = 20;
36 // ---- LevelDB keys. ----
37 // Key for this device's android id.
38 const char kDeviceAIDKey[] = "device_aid_key";
39 // Key for this device's android security token.
40 const char kDeviceTokenKey[] = "device_token_key";
41 // Lowest lexicographically ordered app ids.
42 // Used for prefixing app id.
43 const char kRegistrationKeyStart[] = "reg1-";
44 // Key guaranteed to be higher than all app ids.
45 // Used for limiting iteration.
46 const char kRegistrationKeyEnd[] = "reg2-";
47 // Lowest lexicographically ordered incoming message key.
48 // Used for prefixing messages.
49 const char kIncomingMsgKeyStart[] = "incoming1-";
50 // Key guaranteed to be higher than all incoming message keys.
51 // Used for limiting iteration.
52 const char kIncomingMsgKeyEnd[] = "incoming2-";
53 // Lowest lexicographically ordered outgoing message key.
54 // Used for prefixing outgoing messages.
55 const char kOutgoingMsgKeyStart[] = "outgoing1-";
56 // Key guaranteed to be higher than all outgoing message keys.
57 // Used for limiting iteration.
58 const char kOutgoingMsgKeyEnd[] = "outgoing2-";
59 // Lowest lexicographically ordered G-service settings key.
60 // Used for prefixing G-services settings.
61 const char kGServiceSettingKeyStart[] = "gservice1-";
62 // Key guaranteed to be higher than all G-services settings keys.
63 // Used for limiting iteration.
64 const char kGServiceSettingKeyEnd[] = "gservice2-";
65 // Key for digest of the last G-services settings update.
66 const char kGServiceSettingsDigestKey[] = "gservices_digest";
67 // Key used to indicate how many accounts were last checked in with this device.
68 const char kLastCheckinAccountsKey[] = "last_checkin_accounts_count";
69 // Key used to timestamp last checkin (marked with G services settings update).
70 const char kLastCheckinTimeKey[] = "last_checkin_time";
71 // Lowest lexicographically ordered account key.
72 // Used for prefixing account information.
73 const char kAccountKeyStart[] = "account1-";
74 // Key guaranteed to be higher than all account keys.
75 // Used for limiting iteration.
76 const char kAccountKeyEnd[] = "account2-";
78 std::string MakeRegistrationKey(const std::string& app_id) {
79 return kRegistrationKeyStart + app_id;
82 std::string ParseRegistrationKey(const std::string& key) {
83 return key.substr(arraysize(kRegistrationKeyStart) - 1);
86 std::string MakeIncomingKey(const std::string& persistent_id) {
87 return kIncomingMsgKeyStart + persistent_id;
90 std::string MakeOutgoingKey(const std::string& persistent_id) {
91 return kOutgoingMsgKeyStart + persistent_id;
94 std::string ParseOutgoingKey(const std::string& key) {
95 return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
98 std::string MakeGServiceSettingKey(const std::string& setting_name) {
99 return kGServiceSettingKeyStart + setting_name;
102 std::string ParseGServiceSettingKey(const std::string& key) {
103 return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
106 std::string MakeAccountKey(const std::string& account_id) {
107 return kAccountKeyStart + account_id;
110 std::string ParseAccountKey(const std::string& key) {
111 return key.substr(arraysize(kAccountKeyStart) - 1);
114 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
115 // outlive the slice.
116 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
117 leveldb::Slice MakeSlice(const base::StringPiece& s) {
118 return leveldb::Slice(s.begin(), s.size());
121 } // namespace
123 class GCMStoreImpl::Backend
124 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
125 public:
126 Backend(const base::FilePath& path,
127 scoped_refptr<base::SequencedTaskRunner> foreground_runner,
128 scoped_ptr<Encryptor> encryptor);
130 // Blocking implementations of GCMStoreImpl methods.
131 void Load(const LoadCallback& callback);
132 void Close();
133 void Destroy(const UpdateCallback& callback);
134 void SetDeviceCredentials(uint64 device_android_id,
135 uint64 device_security_token,
136 const UpdateCallback& callback);
137 void AddRegistration(const std::string& app_id,
138 const linked_ptr<RegistrationInfo>& registration,
139 const UpdateCallback& callback);
140 void RemoveRegistration(const std::string& app_id,
141 const UpdateCallback& callback);
142 void AddIncomingMessage(const std::string& persistent_id,
143 const UpdateCallback& callback);
144 void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
145 const UpdateCallback& callback);
146 void AddOutgoingMessage(const std::string& persistent_id,
147 const MCSMessage& message,
148 const UpdateCallback& callback);
149 void RemoveOutgoingMessages(
150 const PersistentIdList& persistent_ids,
151 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
152 callback);
153 void AddUserSerialNumber(const std::string& username,
154 int64 serial_number,
155 const UpdateCallback& callback);
156 void RemoveUserSerialNumber(const std::string& username,
157 const UpdateCallback& callback);
158 void SetLastCheckinInfo(const base::Time& time,
159 const std::set<std::string>& accounts,
160 const UpdateCallback& callback);
161 void SetGServicesSettings(
162 const std::map<std::string, std::string>& settings,
163 const std::string& digest,
164 const UpdateCallback& callback);
165 void AddAccountMapping(const AccountMapping& account_mapping,
166 const UpdateCallback& callback);
167 void RemoveAccountMapping(const std::string& account_id,
168 const UpdateCallback& callback);
170 private:
171 friend class base::RefCountedThreadSafe<Backend>;
172 ~Backend();
174 bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
175 bool LoadRegistrations(RegistrationInfoMap* registrations);
176 bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
177 bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
178 bool LoadLastCheckinInfo(base::Time* last_checkin_time,
179 std::set<std::string>* accounts);
180 bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
181 std::string* digest);
182 bool LoadAccountMappingInfo(AccountMappingMap* account_mappings);
184 const base::FilePath path_;
185 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
186 scoped_ptr<Encryptor> encryptor_;
188 scoped_ptr<leveldb::DB> db_;
191 GCMStoreImpl::Backend::Backend(
192 const base::FilePath& path,
193 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
194 scoped_ptr<Encryptor> encryptor)
195 : path_(path),
196 foreground_task_runner_(foreground_task_runner),
197 encryptor_(encryptor.Pass()) {
200 GCMStoreImpl::Backend::~Backend() {}
202 void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
203 scoped_ptr<LoadResult> result(new LoadResult());
204 if (db_.get()) {
205 LOG(ERROR) << "Attempting to reload open database.";
206 foreground_task_runner_->PostTask(FROM_HERE,
207 base::Bind(callback,
208 base::Passed(&result)));
209 return;
212 leveldb::Options options;
213 options.create_if_missing = true;
214 leveldb::DB* db;
215 leveldb::Status status =
216 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
217 UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status.ok());
218 if (!status.ok()) {
219 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
220 << status.ToString();
221 foreground_task_runner_->PostTask(FROM_HERE,
222 base::Bind(callback,
223 base::Passed(&result)));
224 return;
226 db_.reset(db);
228 if (!LoadDeviceCredentials(&result->device_android_id,
229 &result->device_security_token) ||
230 !LoadRegistrations(&result->registrations) ||
231 !LoadIncomingMessages(&result->incoming_messages) ||
232 !LoadOutgoingMessages(&result->outgoing_messages) ||
233 !LoadLastCheckinInfo(&result->last_checkin_time,
234 &result->last_checkin_accounts) ||
235 !LoadGServicesSettings(&result->gservices_settings,
236 &result->gservices_digest) ||
237 !LoadAccountMappingInfo(&result->account_mappings)) {
238 result->Reset();
239 foreground_task_runner_->PostTask(FROM_HERE,
240 base::Bind(callback,
241 base::Passed(&result)));
242 return;
245 // Only record histograms if GCM had already been set up for this device.
246 if (result->device_android_id != 0 && result->device_security_token != 0) {
247 int64 file_size = 0;
248 if (base::GetFileSize(path_, &file_size)) {
249 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
250 static_cast<int>(file_size / 1024));
252 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
253 result->registrations.size());
254 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
255 result->outgoing_messages.size());
256 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
257 result->incoming_messages.size());
260 DVLOG(1) << "Succeeded in loading " << result->registrations.size()
261 << " registrations, "
262 << result->incoming_messages.size()
263 << " unacknowledged incoming messages and "
264 << result->outgoing_messages.size()
265 << " unacknowledged outgoing messages.";
266 result->success = true;
267 foreground_task_runner_->PostTask(FROM_HERE,
268 base::Bind(callback,
269 base::Passed(&result)));
270 return;
273 void GCMStoreImpl::Backend::Close() {
274 DVLOG(1) << "Closing GCM store.";
275 db_.reset();
278 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
279 DVLOG(1) << "Destroying GCM store.";
280 db_.reset();
281 const leveldb::Status s =
282 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
283 if (s.ok()) {
284 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
285 return;
287 LOG(ERROR) << "Destroy failed: " << s.ToString();
288 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
291 void GCMStoreImpl::Backend::SetDeviceCredentials(
292 uint64 device_android_id,
293 uint64 device_security_token,
294 const UpdateCallback& callback) {
295 DVLOG(1) << "Saving device credentials with AID " << device_android_id;
296 if (!db_.get()) {
297 LOG(ERROR) << "GCMStore db doesn't exist.";
298 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
299 return;
302 leveldb::WriteOptions write_options;
303 write_options.sync = true;
305 std::string encrypted_token;
306 encryptor_->EncryptString(base::Uint64ToString(device_security_token),
307 &encrypted_token);
308 std::string android_id_str = base::Uint64ToString(device_android_id);
309 leveldb::Status s =
310 db_->Put(write_options,
311 MakeSlice(kDeviceAIDKey),
312 MakeSlice(android_id_str));
313 if (s.ok()) {
314 s = db_->Put(
315 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
317 if (s.ok()) {
318 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
319 return;
321 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
322 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
325 void GCMStoreImpl::Backend::AddRegistration(
326 const std::string& app_id,
327 const linked_ptr<RegistrationInfo>& registration,
328 const UpdateCallback& callback) {
329 DVLOG(1) << "Saving registration info for app: " << app_id;
330 if (!db_.get()) {
331 LOG(ERROR) << "GCMStore db doesn't exist.";
332 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
333 return;
335 leveldb::WriteOptions write_options;
336 write_options.sync = true;
338 std::string key = MakeRegistrationKey(app_id);
339 std::string value = registration->SerializeAsString();
340 const leveldb::Status status = db_->Put(write_options,
341 MakeSlice(key),
342 MakeSlice(value));
343 if (status.ok()) {
344 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
345 return;
347 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
348 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
351 void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id,
352 const UpdateCallback& callback) {
353 if (!db_.get()) {
354 LOG(ERROR) << "GCMStore db doesn't exist.";
355 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
356 return;
358 leveldb::WriteOptions write_options;
359 write_options.sync = true;
361 leveldb::Status status =
362 db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
363 if (status.ok()) {
364 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
365 return;
367 LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
368 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
371 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
372 const UpdateCallback& callback) {
373 DVLOG(1) << "Saving incoming message with id " << persistent_id;
374 if (!db_.get()) {
375 LOG(ERROR) << "GCMStore db doesn't exist.";
376 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
377 return;
380 leveldb::WriteOptions write_options;
381 write_options.sync = true;
383 std::string key = MakeIncomingKey(persistent_id);
384 const leveldb::Status s = db_->Put(write_options,
385 MakeSlice(key),
386 MakeSlice(persistent_id));
387 if (s.ok()) {
388 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
389 return;
391 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
392 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
395 void GCMStoreImpl::Backend::RemoveIncomingMessages(
396 const PersistentIdList& persistent_ids,
397 const UpdateCallback& callback) {
398 if (!db_.get()) {
399 LOG(ERROR) << "GCMStore db doesn't exist.";
400 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
401 return;
403 leveldb::WriteOptions write_options;
404 write_options.sync = true;
406 leveldb::Status s;
407 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
408 iter != persistent_ids.end();
409 ++iter) {
410 DVLOG(1) << "Removing incoming message with id " << *iter;
411 std::string key = MakeIncomingKey(*iter);
412 s = db_->Delete(write_options, MakeSlice(key));
413 if (!s.ok())
414 break;
416 if (s.ok()) {
417 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
418 return;
420 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
421 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
424 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
425 const MCSMessage& message,
426 const UpdateCallback& callback) {
427 DVLOG(1) << "Saving outgoing message with id " << persistent_id;
428 if (!db_.get()) {
429 LOG(ERROR) << "GCMStore db doesn't exist.";
430 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
431 return;
433 leveldb::WriteOptions write_options;
434 write_options.sync = true;
436 std::string data =
437 static_cast<char>(message.tag()) + message.SerializeAsString();
438 std::string key = MakeOutgoingKey(persistent_id);
439 const leveldb::Status s = db_->Put(write_options,
440 MakeSlice(key),
441 MakeSlice(data));
442 if (s.ok()) {
443 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
444 return;
446 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
447 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
450 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
451 const PersistentIdList& persistent_ids,
452 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
453 callback) {
454 if (!db_.get()) {
455 LOG(ERROR) << "GCMStore db doesn't exist.";
456 foreground_task_runner_->PostTask(FROM_HERE,
457 base::Bind(callback,
458 false,
459 AppIdToMessageCountMap()));
460 return;
462 leveldb::ReadOptions read_options;
463 leveldb::WriteOptions write_options;
464 write_options.sync = true;
466 AppIdToMessageCountMap removed_message_counts;
468 leveldb::Status s;
469 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
470 iter != persistent_ids.end();
471 ++iter) {
472 DVLOG(1) << "Removing outgoing message with id " << *iter;
473 std::string outgoing_message;
474 std::string key = MakeOutgoingKey(*iter);
475 s = db_->Get(read_options,
476 MakeSlice(key),
477 &outgoing_message);
478 if (!s.ok())
479 break;
480 mcs_proto::DataMessageStanza data_message;
481 // Skip the initial tag byte and parse the rest to extract the message.
482 if (data_message.ParseFromString(outgoing_message.substr(1))) {
483 DCHECK(!data_message.category().empty());
484 if (removed_message_counts.count(data_message.category()) != 0)
485 removed_message_counts[data_message.category()]++;
486 else
487 removed_message_counts[data_message.category()] = 1;
489 DVLOG(1) << "Removing outgoing message with id " << *iter;
490 s = db_->Delete(write_options, MakeSlice(key));
491 if (!s.ok())
492 break;
494 if (s.ok()) {
495 foreground_task_runner_->PostTask(FROM_HERE,
496 base::Bind(callback,
497 true,
498 removed_message_counts));
499 return;
501 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
502 foreground_task_runner_->PostTask(FROM_HERE,
503 base::Bind(callback,
504 false,
505 AppIdToMessageCountMap()));
508 void GCMStoreImpl::Backend::SetLastCheckinInfo(
509 const base::Time& time,
510 const std::set<std::string>& accounts,
511 const UpdateCallback& callback) {
512 leveldb::WriteBatch write_batch;
514 int64 last_checkin_time_internal = time.ToInternalValue();
515 write_batch.Put(MakeSlice(kLastCheckinTimeKey),
516 MakeSlice(base::Int64ToString(last_checkin_time_internal)));
518 std::string serialized_accounts;
519 for (std::set<std::string>::iterator iter = accounts.begin();
520 iter != accounts.end();
521 ++iter) {
522 serialized_accounts += *iter;
523 serialized_accounts += ",";
525 if (!serialized_accounts.empty())
526 serialized_accounts.erase(serialized_accounts.length() - 1);
528 write_batch.Put(MakeSlice(kLastCheckinAccountsKey),
529 MakeSlice(serialized_accounts));
531 leveldb::WriteOptions write_options;
532 write_options.sync = true;
533 const leveldb::Status s = db_->Write(write_options, &write_batch);
535 if (!s.ok())
536 LOG(ERROR) << "LevelDB set last checkin info failed: " << s.ToString();
537 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
540 void GCMStoreImpl::Backend::SetGServicesSettings(
541 const std::map<std::string, std::string>& settings,
542 const std::string& settings_digest,
543 const UpdateCallback& callback) {
544 leveldb::WriteBatch write_batch;
546 // Remove all existing settings.
547 leveldb::ReadOptions read_options;
548 read_options.verify_checksums = true;
549 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
550 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
551 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
552 iter->Next()) {
553 write_batch.Delete(iter->key());
556 // Add the new settings.
557 for (std::map<std::string, std::string>::const_iterator iter =
558 settings.begin();
559 iter != settings.end(); ++iter) {
560 write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
561 MakeSlice(iter->second));
564 // Update the settings digest.
565 write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
566 MakeSlice(settings_digest));
568 // Write it all in a batch.
569 leveldb::WriteOptions write_options;
570 write_options.sync = true;
572 leveldb::Status s = db_->Write(write_options, &write_batch);
573 if (!s.ok())
574 LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
575 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
578 void GCMStoreImpl::Backend::AddAccountMapping(
579 const AccountMapping& account_mapping,
580 const UpdateCallback& callback) {
581 DVLOG(1) << "Saving account info for account with email: "
582 << account_mapping.email;
583 if (!db_.get()) {
584 LOG(ERROR) << "GCMStore db doesn't exist.";
585 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
586 return;
589 leveldb::WriteOptions write_options;
590 write_options.sync = true;
592 std::string data = account_mapping.SerializeAsString();
593 std::string key = MakeAccountKey(account_mapping.account_id);
594 const leveldb::Status s =
595 db_->Put(write_options, MakeSlice(key), MakeSlice(data));
596 if (!s.ok())
597 LOG(ERROR) << "LevelDB adding account mapping failed: " << s.ToString();
598 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
601 void GCMStoreImpl::Backend::RemoveAccountMapping(
602 const std::string& account_id,
603 const UpdateCallback& callback) {
604 if (!db_.get()) {
605 LOG(ERROR) << "GCMStore db doesn't exist.";
606 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
607 return;
610 leveldb::WriteOptions write_options;
611 write_options.sync = true;
613 leveldb::Status s =
614 db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
616 if (!s.ok())
617 LOG(ERROR) << "LevelDB removal of account mapping failed: " << s.ToString();
618 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
621 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
622 uint64* security_token) {
623 leveldb::ReadOptions read_options;
624 read_options.verify_checksums = true;
626 std::string result;
627 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
628 if (s.ok()) {
629 if (!base::StringToUint64(result, android_id)) {
630 LOG(ERROR) << "Failed to restore device id.";
631 return false;
633 result.clear();
634 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
636 if (s.ok()) {
637 std::string decrypted_token;
638 encryptor_->DecryptString(result, &decrypted_token);
639 if (!base::StringToUint64(decrypted_token, security_token)) {
640 LOG(ERROR) << "Failed to restore security token.";
641 return false;
643 return true;
646 if (s.IsNotFound()) {
647 DVLOG(1) << "No credentials found.";
648 return true;
651 LOG(ERROR) << "Error reading credentials from store.";
652 return false;
655 bool GCMStoreImpl::Backend::LoadRegistrations(
656 RegistrationInfoMap* registrations) {
657 leveldb::ReadOptions read_options;
658 read_options.verify_checksums = true;
660 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
661 for (iter->Seek(MakeSlice(kRegistrationKeyStart));
662 iter->Valid() && iter->key().ToString() < kRegistrationKeyEnd;
663 iter->Next()) {
664 leveldb::Slice s = iter->value();
665 if (s.size() <= 1) {
666 LOG(ERROR) << "Error reading registration with key " << s.ToString();
667 return false;
669 std::string app_id = ParseRegistrationKey(iter->key().ToString());
670 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
671 if (!registration->ParseFromString(iter->value().ToString())) {
672 LOG(ERROR) << "Failed to parse registration with app id " << app_id;
673 return false;
675 DVLOG(1) << "Found registration with app id " << app_id;
676 (*registrations)[app_id] = registration;
679 return true;
682 bool GCMStoreImpl::Backend::LoadIncomingMessages(
683 std::vector<std::string>* incoming_messages) {
684 leveldb::ReadOptions read_options;
685 read_options.verify_checksums = true;
687 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
688 for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
689 iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
690 iter->Next()) {
691 leveldb::Slice s = iter->value();
692 if (s.empty()) {
693 LOG(ERROR) << "Error reading incoming message with key "
694 << iter->key().ToString();
695 return false;
697 DVLOG(1) << "Found incoming message with id " << s.ToString();
698 incoming_messages->push_back(s.ToString());
701 return true;
704 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
705 OutgoingMessageMap* outgoing_messages) {
706 leveldb::ReadOptions read_options;
707 read_options.verify_checksums = true;
709 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
710 for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
711 iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
712 iter->Next()) {
713 leveldb::Slice s = iter->value();
714 if (s.size() <= 1) {
715 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
716 return false;
718 uint8 tag = iter->value().data()[0];
719 std::string id = ParseOutgoingKey(iter->key().ToString());
720 scoped_ptr<google::protobuf::MessageLite> message(
721 BuildProtobufFromTag(tag));
722 if (!message.get() ||
723 !message->ParseFromString(iter->value().ToString().substr(1))) {
724 LOG(ERROR) << "Failed to parse outgoing message with id " << id
725 << " and tag " << tag;
726 return false;
728 DVLOG(1) << "Found outgoing message with id " << id << " of type "
729 << base::IntToString(tag);
730 (*outgoing_messages)[id] = make_linked_ptr(message.release());
733 return true;
736 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
737 base::Time* last_checkin_time,
738 std::set<std::string>* accounts) {
739 leveldb::ReadOptions read_options;
740 read_options.verify_checksums = true;
742 std::string result;
743 leveldb::Status s = db_->Get(read_options,
744 MakeSlice(kLastCheckinTimeKey),
745 &result);
746 int64 time_internal = 0LL;
747 if (s.ok() && !base::StringToInt64(result, &time_internal))
748 LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
750 // In case we cannot read last checkin time, we default it to 0, as we don't
751 // want that situation to cause the whole load to fail.
752 *last_checkin_time = base::Time::FromInternalValue(time_internal);
754 accounts->clear();
755 s = db_->Get(read_options, MakeSlice(kLastCheckinAccountsKey), &result);
756 if (!s.ok())
757 DVLOG(1) << "No accounts where stored during last run.";
759 base::StringTokenizer t(result, ",");
760 while (t.GetNext())
761 accounts->insert(t.token());
763 return true;
766 bool GCMStoreImpl::Backend::LoadGServicesSettings(
767 std::map<std::string, std::string>* settings,
768 std::string* digest) {
769 leveldb::ReadOptions read_options;
770 read_options.verify_checksums = true;
772 // Load all of the GServices settings.
773 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
774 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
775 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
776 iter->Next()) {
777 std::string value = iter->value().ToString();
778 if (value.empty()) {
779 LOG(ERROR) << "Error reading GService Settings " << value;
780 return false;
782 std::string id = ParseGServiceSettingKey(iter->key().ToString());
783 (*settings)[id] = value;
784 DVLOG(1) << "Found G Service setting with key: " << id
785 << ", and value: " << value;
788 // Load the settings digest. It's ok if it is empty.
789 db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
791 return true;
794 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
795 AccountMappingMap* account_mappings) {
796 leveldb::ReadOptions read_options;
797 read_options.verify_checksums = true;
799 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
800 for (iter->Seek(MakeSlice(kAccountKeyStart));
801 iter->Valid() && iter->key().ToString() < kAccountKeyEnd;
802 iter->Next()) {
803 AccountMapping account_mapping;
804 account_mapping.account_id = ParseAccountKey(iter->key().ToString());
805 if (!account_mapping.ParseFromString(iter->value().ToString())) {
806 DVLOG(1) << "Failed to parse account info with ID: "
807 << account_mapping.account_id;
808 return false;
810 DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
811 (*account_mappings)[account_mapping.account_id] = account_mapping;
814 return true;
817 GCMStoreImpl::GCMStoreImpl(
818 const base::FilePath& path,
819 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
820 scoped_ptr<Encryptor> encryptor)
821 : backend_(new Backend(path,
822 base::MessageLoopProxy::current(),
823 encryptor.Pass())),
824 blocking_task_runner_(blocking_task_runner),
825 weak_ptr_factory_(this) {
828 GCMStoreImpl::~GCMStoreImpl() {}
830 void GCMStoreImpl::Load(const LoadCallback& callback) {
831 blocking_task_runner_->PostTask(
832 FROM_HERE,
833 base::Bind(&GCMStoreImpl::Backend::Load,
834 backend_,
835 base::Bind(&GCMStoreImpl::LoadContinuation,
836 weak_ptr_factory_.GetWeakPtr(),
837 callback)));
840 void GCMStoreImpl::Close() {
841 weak_ptr_factory_.InvalidateWeakPtrs();
842 app_message_counts_.clear();
843 blocking_task_runner_->PostTask(
844 FROM_HERE,
845 base::Bind(&GCMStoreImpl::Backend::Close, backend_));
848 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
849 blocking_task_runner_->PostTask(
850 FROM_HERE,
851 base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
854 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
855 uint64 device_security_token,
856 const UpdateCallback& callback) {
857 blocking_task_runner_->PostTask(
858 FROM_HERE,
859 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
860 backend_,
861 device_android_id,
862 device_security_token,
863 callback));
866 void GCMStoreImpl::AddRegistration(
867 const std::string& app_id,
868 const linked_ptr<RegistrationInfo>& registration,
869 const UpdateCallback& callback) {
870 blocking_task_runner_->PostTask(
871 FROM_HERE,
872 base::Bind(&GCMStoreImpl::Backend::AddRegistration,
873 backend_,
874 app_id,
875 registration,
876 callback));
879 void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
880 const UpdateCallback& callback) {
881 blocking_task_runner_->PostTask(
882 FROM_HERE,
883 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
884 backend_,
885 app_id,
886 callback));
889 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
890 const UpdateCallback& callback) {
891 blocking_task_runner_->PostTask(
892 FROM_HERE,
893 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
894 backend_,
895 persistent_id,
896 callback));
899 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
900 const UpdateCallback& callback) {
901 blocking_task_runner_->PostTask(
902 FROM_HERE,
903 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
904 backend_,
905 PersistentIdList(1, persistent_id),
906 callback));
909 void GCMStoreImpl::RemoveIncomingMessages(
910 const PersistentIdList& persistent_ids,
911 const UpdateCallback& callback) {
912 blocking_task_runner_->PostTask(
913 FROM_HERE,
914 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
915 backend_,
916 persistent_ids,
917 callback));
920 bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
921 const MCSMessage& message,
922 const UpdateCallback& callback) {
923 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
924 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
925 &message.GetProtobuf())->category();
926 DCHECK(!app_id.empty());
927 if (app_message_counts_.count(app_id) == 0)
928 app_message_counts_[app_id] = 0;
929 if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
930 app_message_counts_[app_id]++;
932 blocking_task_runner_->PostTask(
933 FROM_HERE,
934 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
935 backend_,
936 persistent_id,
937 message,
938 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
939 weak_ptr_factory_.GetWeakPtr(),
940 callback,
941 app_id)));
942 return true;
944 return false;
947 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
948 const MCSMessage& message,
949 const UpdateCallback& callback) {
950 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
951 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
952 &message.GetProtobuf())->category();
953 DCHECK(!app_id.empty());
954 // There should already be pending messages for this app.
955 DCHECK(app_message_counts_.count(app_id));
956 // TODO(zea): consider verifying the specific message already exists.
957 blocking_task_runner_->PostTask(
958 FROM_HERE,
959 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
960 backend_,
961 persistent_id,
962 message,
963 callback));
966 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
967 const UpdateCallback& callback) {
968 blocking_task_runner_->PostTask(
969 FROM_HERE,
970 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
971 backend_,
972 PersistentIdList(1, persistent_id),
973 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
974 weak_ptr_factory_.GetWeakPtr(),
975 callback)));
978 void GCMStoreImpl::RemoveOutgoingMessages(
979 const PersistentIdList& persistent_ids,
980 const UpdateCallback& callback) {
981 blocking_task_runner_->PostTask(
982 FROM_HERE,
983 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
984 backend_,
985 persistent_ids,
986 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
987 weak_ptr_factory_.GetWeakPtr(),
988 callback)));
991 void GCMStoreImpl::SetLastCheckinInfo(const base::Time& time,
992 const std::set<std::string>& accounts,
993 const UpdateCallback& callback) {
994 blocking_task_runner_->PostTask(
995 FROM_HERE,
996 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo,
997 backend_,
998 time,
999 accounts,
1000 callback));
1003 void GCMStoreImpl::SetGServicesSettings(
1004 const std::map<std::string, std::string>& settings,
1005 const std::string& digest,
1006 const UpdateCallback& callback) {
1007 blocking_task_runner_->PostTask(
1008 FROM_HERE,
1009 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
1010 backend_,
1011 settings,
1012 digest,
1013 callback));
1016 void GCMStoreImpl::AddAccountMapping(const AccountMapping& account_mapping,
1017 const UpdateCallback& callback) {
1018 blocking_task_runner_->PostTask(
1019 FROM_HERE,
1020 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
1021 backend_,
1022 account_mapping,
1023 callback));
1026 void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
1027 const UpdateCallback& callback) {
1028 blocking_task_runner_->PostTask(
1029 FROM_HERE,
1030 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
1031 backend_,
1032 account_id,
1033 callback));
1036 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
1037 scoped_ptr<LoadResult> result) {
1038 if (!result->success) {
1039 callback.Run(result.Pass());
1040 return;
1042 int num_throttled_apps = 0;
1043 for (OutgoingMessageMap::const_iterator
1044 iter = result->outgoing_messages.begin();
1045 iter != result->outgoing_messages.end(); ++iter) {
1046 const mcs_proto::DataMessageStanza* data_message =
1047 reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
1048 DCHECK(!data_message->category().empty());
1049 if (app_message_counts_.count(data_message->category()) == 0)
1050 app_message_counts_[data_message->category()] = 1;
1051 else
1052 app_message_counts_[data_message->category()]++;
1053 if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
1054 num_throttled_apps++;
1056 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
1057 callback.Run(result.Pass());
1060 void GCMStoreImpl::AddOutgoingMessageContinuation(
1061 const UpdateCallback& callback,
1062 const std::string& app_id,
1063 bool success) {
1064 if (!success) {
1065 DCHECK(app_message_counts_[app_id] > 0);
1066 app_message_counts_[app_id]--;
1068 callback.Run(success);
1071 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1072 const UpdateCallback& callback,
1073 bool success,
1074 const AppIdToMessageCountMap& removed_message_counts) {
1075 if (!success) {
1076 callback.Run(false);
1077 return;
1079 for (AppIdToMessageCountMap::const_iterator iter =
1080 removed_message_counts.begin();
1081 iter != removed_message_counts.end(); ++iter) {
1082 DCHECK_NE(app_message_counts_.count(iter->first), 0U);
1083 app_message_counts_[iter->first] -= iter->second;
1084 DCHECK_GE(app_message_counts_[iter->first], 0);
1086 callback.Run(true);
1089 } // namespace gcm