Rename isSystemLocationEnabled to isLocationEnabled, as per internal review (185995).
[chromium-blink-merge.git] / google_apis / gcm / engine / gcm_store_impl.cc
blob14e382e61980af17971ce0dce32febb42148ccf5
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/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-";
77 // Key used for last token fetch time.
78 const char kLastTokenFetchTimeKey[] = "last_token_fetch_time";
80 std::string MakeRegistrationKey(const std::string& app_id) {
81 return kRegistrationKeyStart + app_id;
84 std::string ParseRegistrationKey(const std::string& key) {
85 return key.substr(arraysize(kRegistrationKeyStart) - 1);
88 std::string MakeIncomingKey(const std::string& persistent_id) {
89 return kIncomingMsgKeyStart + persistent_id;
92 std::string MakeOutgoingKey(const std::string& persistent_id) {
93 return kOutgoingMsgKeyStart + persistent_id;
96 std::string ParseOutgoingKey(const std::string& key) {
97 return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
100 std::string MakeGServiceSettingKey(const std::string& setting_name) {
101 return kGServiceSettingKeyStart + setting_name;
104 std::string ParseGServiceSettingKey(const std::string& key) {
105 return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
108 std::string MakeAccountKey(const std::string& account_id) {
109 return kAccountKeyStart + account_id;
112 std::string ParseAccountKey(const std::string& key) {
113 return key.substr(arraysize(kAccountKeyStart) - 1);
116 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
117 // outlive the slice.
118 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
119 leveldb::Slice MakeSlice(const base::StringPiece& s) {
120 return leveldb::Slice(s.begin(), s.size());
123 } // namespace
125 class GCMStoreImpl::Backend
126 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
127 public:
128 Backend(const base::FilePath& path,
129 scoped_refptr<base::SequencedTaskRunner> foreground_runner,
130 scoped_ptr<Encryptor> encryptor);
132 // Blocking implementations of GCMStoreImpl methods.
133 void Load(const LoadCallback& callback);
134 void Close();
135 void Destroy(const UpdateCallback& callback);
136 void SetDeviceCredentials(uint64 device_android_id,
137 uint64 device_security_token,
138 const UpdateCallback& callback);
139 void AddRegistration(const std::string& app_id,
140 const linked_ptr<RegistrationInfo>& registration,
141 const UpdateCallback& callback);
142 void RemoveRegistration(const std::string& app_id,
143 const UpdateCallback& callback);
144 void AddIncomingMessage(const std::string& persistent_id,
145 const UpdateCallback& callback);
146 void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
147 const UpdateCallback& callback);
148 void AddOutgoingMessage(const std::string& persistent_id,
149 const MCSMessage& message,
150 const UpdateCallback& callback);
151 void RemoveOutgoingMessages(
152 const PersistentIdList& persistent_ids,
153 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
154 callback);
155 void AddUserSerialNumber(const std::string& username,
156 int64 serial_number,
157 const UpdateCallback& callback);
158 void RemoveUserSerialNumber(const std::string& username,
159 const UpdateCallback& callback);
160 void SetLastCheckinInfo(const base::Time& time,
161 const std::set<std::string>& accounts,
162 const UpdateCallback& callback);
163 void SetGServicesSettings(
164 const std::map<std::string, std::string>& settings,
165 const std::string& digest,
166 const UpdateCallback& callback);
167 void AddAccountMapping(const AccountMapping& account_mapping,
168 const UpdateCallback& callback);
169 void RemoveAccountMapping(const std::string& account_id,
170 const UpdateCallback& callback);
171 void SetLastTokenFetchTime(const base::Time& time,
172 const UpdateCallback& callback);
174 private:
175 friend class base::RefCountedThreadSafe<Backend>;
176 ~Backend();
178 bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
179 bool LoadRegistrations(RegistrationInfoMap* registrations);
180 bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
181 bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
182 bool LoadLastCheckinInfo(base::Time* last_checkin_time,
183 std::set<std::string>* accounts);
184 bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
185 std::string* digest);
186 bool LoadAccountMappingInfo(AccountMappings* account_mappings);
187 bool LoadLastTokenFetchTime(base::Time* last_token_fetch_time);
189 const base::FilePath path_;
190 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
191 scoped_ptr<Encryptor> encryptor_;
193 scoped_ptr<leveldb::DB> db_;
196 GCMStoreImpl::Backend::Backend(
197 const base::FilePath& path,
198 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
199 scoped_ptr<Encryptor> encryptor)
200 : path_(path),
201 foreground_task_runner_(foreground_task_runner),
202 encryptor_(encryptor.Pass()) {
205 GCMStoreImpl::Backend::~Backend() {}
207 void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
208 scoped_ptr<LoadResult> result(new LoadResult());
209 if (db_.get()) {
210 LOG(ERROR) << "Attempting to reload open database.";
211 foreground_task_runner_->PostTask(FROM_HERE,
212 base::Bind(callback,
213 base::Passed(&result)));
214 return;
217 leveldb::Options options;
218 options.create_if_missing = true;
219 leveldb::DB* db;
220 leveldb::Status status =
221 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
222 UMA_HISTOGRAM_BOOLEAN("GCM.LoadSucceeded", status.ok());
223 if (!status.ok()) {
224 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
225 << status.ToString();
226 foreground_task_runner_->PostTask(FROM_HERE,
227 base::Bind(callback,
228 base::Passed(&result)));
229 return;
231 db_.reset(db);
233 if (!LoadDeviceCredentials(&result->device_android_id,
234 &result->device_security_token) ||
235 !LoadRegistrations(&result->registrations) ||
236 !LoadIncomingMessages(&result->incoming_messages) ||
237 !LoadOutgoingMessages(&result->outgoing_messages) ||
238 !LoadLastCheckinInfo(&result->last_checkin_time,
239 &result->last_checkin_accounts) ||
240 !LoadGServicesSettings(&result->gservices_settings,
241 &result->gservices_digest) ||
242 !LoadAccountMappingInfo(&result->account_mappings) ||
243 !LoadLastTokenFetchTime(&result->last_token_fetch_time)) {
244 result->Reset();
245 foreground_task_runner_->PostTask(FROM_HERE,
246 base::Bind(callback,
247 base::Passed(&result)));
248 return;
251 // Only record histograms if GCM had already been set up for this device.
252 if (result->device_android_id != 0 && result->device_security_token != 0) {
253 int64 file_size = 0;
254 if (base::GetFileSize(path_, &file_size)) {
255 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
256 static_cast<int>(file_size / 1024));
258 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
259 result->registrations.size());
260 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
261 result->outgoing_messages.size());
262 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
263 result->incoming_messages.size());
266 DVLOG(1) << "Succeeded in loading " << result->registrations.size()
267 << " registrations, "
268 << result->incoming_messages.size()
269 << " unacknowledged incoming messages and "
270 << result->outgoing_messages.size()
271 << " unacknowledged outgoing messages.";
272 result->success = true;
273 foreground_task_runner_->PostTask(FROM_HERE,
274 base::Bind(callback,
275 base::Passed(&result)));
276 return;
279 void GCMStoreImpl::Backend::Close() {
280 DVLOG(1) << "Closing GCM store.";
281 db_.reset();
284 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
285 DVLOG(1) << "Destroying GCM store.";
286 db_.reset();
287 const leveldb::Status s =
288 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
289 if (s.ok()) {
290 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
291 return;
293 LOG(ERROR) << "Destroy failed: " << s.ToString();
294 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
297 void GCMStoreImpl::Backend::SetDeviceCredentials(
298 uint64 device_android_id,
299 uint64 device_security_token,
300 const UpdateCallback& callback) {
301 DVLOG(1) << "Saving device credentials with AID " << device_android_id;
302 if (!db_.get()) {
303 LOG(ERROR) << "GCMStore db doesn't exist.";
304 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
305 return;
308 leveldb::WriteOptions write_options;
309 write_options.sync = true;
311 std::string encrypted_token;
312 encryptor_->EncryptString(base::Uint64ToString(device_security_token),
313 &encrypted_token);
314 std::string android_id_str = base::Uint64ToString(device_android_id);
315 leveldb::Status s =
316 db_->Put(write_options,
317 MakeSlice(kDeviceAIDKey),
318 MakeSlice(android_id_str));
319 if (s.ok()) {
320 s = db_->Put(
321 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
323 if (s.ok()) {
324 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
325 return;
327 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
328 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
331 void GCMStoreImpl::Backend::AddRegistration(
332 const std::string& app_id,
333 const linked_ptr<RegistrationInfo>& registration,
334 const UpdateCallback& callback) {
335 DVLOG(1) << "Saving registration info for app: " << app_id;
336 if (!db_.get()) {
337 LOG(ERROR) << "GCMStore db doesn't exist.";
338 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
339 return;
341 leveldb::WriteOptions write_options;
342 write_options.sync = true;
344 std::string key = MakeRegistrationKey(app_id);
345 std::string value = registration->SerializeAsString();
346 const leveldb::Status status = db_->Put(write_options,
347 MakeSlice(key),
348 MakeSlice(value));
349 if (status.ok()) {
350 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
351 return;
353 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
354 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
357 void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id,
358 const UpdateCallback& callback) {
359 if (!db_.get()) {
360 LOG(ERROR) << "GCMStore db doesn't exist.";
361 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
362 return;
364 leveldb::WriteOptions write_options;
365 write_options.sync = true;
367 leveldb::Status status =
368 db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
369 if (status.ok()) {
370 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
371 return;
373 LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
374 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
377 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
378 const UpdateCallback& callback) {
379 DVLOG(1) << "Saving incoming message with id " << persistent_id;
380 if (!db_.get()) {
381 LOG(ERROR) << "GCMStore db doesn't exist.";
382 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
383 return;
386 leveldb::WriteOptions write_options;
387 write_options.sync = true;
389 std::string key = MakeIncomingKey(persistent_id);
390 const leveldb::Status s = db_->Put(write_options,
391 MakeSlice(key),
392 MakeSlice(persistent_id));
393 if (s.ok()) {
394 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
395 return;
397 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
398 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
401 void GCMStoreImpl::Backend::RemoveIncomingMessages(
402 const PersistentIdList& persistent_ids,
403 const UpdateCallback& callback) {
404 if (!db_.get()) {
405 LOG(ERROR) << "GCMStore db doesn't exist.";
406 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
407 return;
409 leveldb::WriteOptions write_options;
410 write_options.sync = true;
412 leveldb::Status s;
413 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
414 iter != persistent_ids.end();
415 ++iter) {
416 DVLOG(1) << "Removing incoming message with id " << *iter;
417 std::string key = MakeIncomingKey(*iter);
418 s = db_->Delete(write_options, MakeSlice(key));
419 if (!s.ok())
420 break;
422 if (s.ok()) {
423 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
424 return;
426 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
427 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
430 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
431 const MCSMessage& message,
432 const UpdateCallback& callback) {
433 DVLOG(1) << "Saving outgoing message with id " << persistent_id;
434 if (!db_.get()) {
435 LOG(ERROR) << "GCMStore db doesn't exist.";
436 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
437 return;
439 leveldb::WriteOptions write_options;
440 write_options.sync = true;
442 std::string data =
443 static_cast<char>(message.tag()) + message.SerializeAsString();
444 std::string key = MakeOutgoingKey(persistent_id);
445 const leveldb::Status s = db_->Put(write_options,
446 MakeSlice(key),
447 MakeSlice(data));
448 if (s.ok()) {
449 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
450 return;
452 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
453 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
456 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
457 const PersistentIdList& persistent_ids,
458 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
459 callback) {
460 if (!db_.get()) {
461 LOG(ERROR) << "GCMStore db doesn't exist.";
462 foreground_task_runner_->PostTask(FROM_HERE,
463 base::Bind(callback,
464 false,
465 AppIdToMessageCountMap()));
466 return;
468 leveldb::ReadOptions read_options;
469 leveldb::WriteOptions write_options;
470 write_options.sync = true;
472 AppIdToMessageCountMap removed_message_counts;
474 leveldb::Status s;
475 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
476 iter != persistent_ids.end();
477 ++iter) {
478 DVLOG(1) << "Removing outgoing message with id " << *iter;
479 std::string outgoing_message;
480 std::string key = MakeOutgoingKey(*iter);
481 s = db_->Get(read_options,
482 MakeSlice(key),
483 &outgoing_message);
484 if (!s.ok())
485 break;
486 mcs_proto::DataMessageStanza data_message;
487 // Skip the initial tag byte and parse the rest to extract the message.
488 if (data_message.ParseFromString(outgoing_message.substr(1))) {
489 DCHECK(!data_message.category().empty());
490 if (removed_message_counts.count(data_message.category()) != 0)
491 removed_message_counts[data_message.category()]++;
492 else
493 removed_message_counts[data_message.category()] = 1;
495 DVLOG(1) << "Removing outgoing message with id " << *iter;
496 s = db_->Delete(write_options, MakeSlice(key));
497 if (!s.ok())
498 break;
500 if (s.ok()) {
501 foreground_task_runner_->PostTask(FROM_HERE,
502 base::Bind(callback,
503 true,
504 removed_message_counts));
505 return;
507 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
508 foreground_task_runner_->PostTask(FROM_HERE,
509 base::Bind(callback,
510 false,
511 AppIdToMessageCountMap()));
514 void GCMStoreImpl::Backend::SetLastCheckinInfo(
515 const base::Time& time,
516 const std::set<std::string>& accounts,
517 const UpdateCallback& callback) {
518 leveldb::WriteBatch write_batch;
520 int64 last_checkin_time_internal = time.ToInternalValue();
521 write_batch.Put(MakeSlice(kLastCheckinTimeKey),
522 MakeSlice(base::Int64ToString(last_checkin_time_internal)));
524 std::string serialized_accounts;
525 for (std::set<std::string>::iterator iter = accounts.begin();
526 iter != accounts.end();
527 ++iter) {
528 serialized_accounts += *iter;
529 serialized_accounts += ",";
531 if (!serialized_accounts.empty())
532 serialized_accounts.erase(serialized_accounts.length() - 1);
534 write_batch.Put(MakeSlice(kLastCheckinAccountsKey),
535 MakeSlice(serialized_accounts));
537 leveldb::WriteOptions write_options;
538 write_options.sync = true;
539 const leveldb::Status s = db_->Write(write_options, &write_batch);
541 if (!s.ok())
542 LOG(ERROR) << "LevelDB set last checkin info failed: " << s.ToString();
543 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
546 void GCMStoreImpl::Backend::SetGServicesSettings(
547 const std::map<std::string, std::string>& settings,
548 const std::string& settings_digest,
549 const UpdateCallback& callback) {
550 leveldb::WriteBatch write_batch;
552 // Remove all existing settings.
553 leveldb::ReadOptions read_options;
554 read_options.verify_checksums = true;
555 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
556 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
557 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
558 iter->Next()) {
559 write_batch.Delete(iter->key());
562 // Add the new settings.
563 for (std::map<std::string, std::string>::const_iterator iter =
564 settings.begin();
565 iter != settings.end(); ++iter) {
566 write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
567 MakeSlice(iter->second));
570 // Update the settings digest.
571 write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
572 MakeSlice(settings_digest));
574 // Write it all in a batch.
575 leveldb::WriteOptions write_options;
576 write_options.sync = true;
578 leveldb::Status s = db_->Write(write_options, &write_batch);
579 if (!s.ok())
580 LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
581 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
584 void GCMStoreImpl::Backend::AddAccountMapping(
585 const AccountMapping& account_mapping,
586 const UpdateCallback& callback) {
587 DVLOG(1) << "Saving account info for account with email: "
588 << account_mapping.email;
589 if (!db_.get()) {
590 LOG(ERROR) << "GCMStore db doesn't exist.";
591 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
592 return;
595 leveldb::WriteOptions write_options;
596 write_options.sync = true;
598 std::string data = account_mapping.SerializeAsString();
599 std::string key = MakeAccountKey(account_mapping.account_id);
600 const leveldb::Status s =
601 db_->Put(write_options, MakeSlice(key), MakeSlice(data));
602 if (!s.ok())
603 LOG(ERROR) << "LevelDB adding account mapping failed: " << s.ToString();
604 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
607 void GCMStoreImpl::Backend::RemoveAccountMapping(
608 const std::string& account_id,
609 const UpdateCallback& callback) {
610 if (!db_.get()) {
611 LOG(ERROR) << "GCMStore db doesn't exist.";
612 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
613 return;
616 leveldb::WriteOptions write_options;
617 write_options.sync = true;
619 leveldb::Status s =
620 db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
622 if (!s.ok())
623 LOG(ERROR) << "LevelDB removal of account mapping failed: " << s.ToString();
624 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
627 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
628 const base::Time& time,
629 const UpdateCallback& callback) {
630 DVLOG(1) << "Setting last token fetching time.";
631 if (!db_.get()) {
632 LOG(ERROR) << "GCMStore db doesn't exist.";
633 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
634 return;
637 leveldb::WriteOptions write_options;
638 write_options.sync = true;
640 const leveldb::Status s =
641 db_->Put(write_options,
642 MakeSlice(kLastTokenFetchTimeKey),
643 MakeSlice(base::Int64ToString(time.ToInternalValue())));
645 if (!s.ok())
646 LOG(ERROR) << "LevelDB setting last token fetching time: " << s.ToString();
647 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
650 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
651 uint64* security_token) {
652 leveldb::ReadOptions read_options;
653 read_options.verify_checksums = true;
655 std::string result;
656 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
657 if (s.ok()) {
658 if (!base::StringToUint64(result, android_id)) {
659 LOG(ERROR) << "Failed to restore device id.";
660 return false;
662 result.clear();
663 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
665 if (s.ok()) {
666 std::string decrypted_token;
667 encryptor_->DecryptString(result, &decrypted_token);
668 if (!base::StringToUint64(decrypted_token, security_token)) {
669 LOG(ERROR) << "Failed to restore security token.";
670 return false;
672 return true;
675 if (s.IsNotFound()) {
676 DVLOG(1) << "No credentials found.";
677 return true;
680 LOG(ERROR) << "Error reading credentials from store.";
681 return false;
684 bool GCMStoreImpl::Backend::LoadRegistrations(
685 RegistrationInfoMap* registrations) {
686 leveldb::ReadOptions read_options;
687 read_options.verify_checksums = true;
689 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
690 for (iter->Seek(MakeSlice(kRegistrationKeyStart));
691 iter->Valid() && iter->key().ToString() < kRegistrationKeyEnd;
692 iter->Next()) {
693 leveldb::Slice s = iter->value();
694 if (s.size() <= 1) {
695 LOG(ERROR) << "Error reading registration with key " << s.ToString();
696 return false;
698 std::string app_id = ParseRegistrationKey(iter->key().ToString());
699 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
700 if (!registration->ParseFromString(iter->value().ToString())) {
701 LOG(ERROR) << "Failed to parse registration with app id " << app_id;
702 return false;
704 DVLOG(1) << "Found registration with app id " << app_id;
705 (*registrations)[app_id] = registration;
708 return true;
711 bool GCMStoreImpl::Backend::LoadIncomingMessages(
712 std::vector<std::string>* incoming_messages) {
713 leveldb::ReadOptions read_options;
714 read_options.verify_checksums = true;
716 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
717 for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
718 iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
719 iter->Next()) {
720 leveldb::Slice s = iter->value();
721 if (s.empty()) {
722 LOG(ERROR) << "Error reading incoming message with key "
723 << iter->key().ToString();
724 return false;
726 DVLOG(1) << "Found incoming message with id " << s.ToString();
727 incoming_messages->push_back(s.ToString());
730 return true;
733 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
734 OutgoingMessageMap* outgoing_messages) {
735 leveldb::ReadOptions read_options;
736 read_options.verify_checksums = true;
738 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
739 for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
740 iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
741 iter->Next()) {
742 leveldb::Slice s = iter->value();
743 if (s.size() <= 1) {
744 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
745 return false;
747 uint8 tag = iter->value().data()[0];
748 std::string id = ParseOutgoingKey(iter->key().ToString());
749 scoped_ptr<google::protobuf::MessageLite> message(
750 BuildProtobufFromTag(tag));
751 if (!message.get() ||
752 !message->ParseFromString(iter->value().ToString().substr(1))) {
753 LOG(ERROR) << "Failed to parse outgoing message with id " << id
754 << " and tag " << tag;
755 return false;
757 DVLOG(1) << "Found outgoing message with id " << id << " of type "
758 << base::IntToString(tag);
759 (*outgoing_messages)[id] = make_linked_ptr(message.release());
762 return true;
765 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
766 base::Time* last_checkin_time,
767 std::set<std::string>* accounts) {
768 leveldb::ReadOptions read_options;
769 read_options.verify_checksums = true;
771 std::string result;
772 leveldb::Status s = db_->Get(read_options,
773 MakeSlice(kLastCheckinTimeKey),
774 &result);
775 int64 time_internal = 0LL;
776 if (s.ok() && !base::StringToInt64(result, &time_internal))
777 LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
779 // In case we cannot read last checkin time, we default it to 0, as we don't
780 // want that situation to cause the whole load to fail.
781 *last_checkin_time = base::Time::FromInternalValue(time_internal);
783 accounts->clear();
784 s = db_->Get(read_options, MakeSlice(kLastCheckinAccountsKey), &result);
785 if (!s.ok())
786 DVLOG(1) << "No accounts where stored during last run.";
788 base::StringTokenizer t(result, ",");
789 while (t.GetNext())
790 accounts->insert(t.token());
792 return true;
795 bool GCMStoreImpl::Backend::LoadGServicesSettings(
796 std::map<std::string, std::string>* settings,
797 std::string* digest) {
798 leveldb::ReadOptions read_options;
799 read_options.verify_checksums = true;
801 // Load all of the GServices settings.
802 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
803 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
804 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
805 iter->Next()) {
806 std::string value = iter->value().ToString();
807 if (value.empty()) {
808 LOG(ERROR) << "Error reading GService Settings " << value;
809 return false;
811 std::string id = ParseGServiceSettingKey(iter->key().ToString());
812 (*settings)[id] = value;
813 DVLOG(1) << "Found G Service setting with key: " << id
814 << ", and value: " << value;
817 // Load the settings digest. It's ok if it is empty.
818 db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
820 return true;
823 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
824 AccountMappings* account_mappings) {
825 leveldb::ReadOptions read_options;
826 read_options.verify_checksums = true;
828 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
829 for (iter->Seek(MakeSlice(kAccountKeyStart));
830 iter->Valid() && iter->key().ToString() < kAccountKeyEnd;
831 iter->Next()) {
832 AccountMapping account_mapping;
833 account_mapping.account_id = ParseAccountKey(iter->key().ToString());
834 if (!account_mapping.ParseFromString(iter->value().ToString())) {
835 DVLOG(1) << "Failed to parse account info with ID: "
836 << account_mapping.account_id;
837 return false;
839 DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
840 account_mappings->push_back(account_mapping);
843 return true;
846 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
847 base::Time* last_token_fetch_time) {
848 leveldb::ReadOptions read_options;
849 read_options.verify_checksums = true;
851 std::string result;
852 leveldb::Status s =
853 db_->Get(read_options, MakeSlice(kLastTokenFetchTimeKey), &result);
854 int64 time_internal = 0LL;
855 if (s.ok() && !base::StringToInt64(result, &time_internal))
856 LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
858 // In case we cannot read last token fetching time, we default it to 0.
859 *last_token_fetch_time = base::Time::FromInternalValue(time_internal);
861 return true;
864 GCMStoreImpl::GCMStoreImpl(
865 const base::FilePath& path,
866 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
867 scoped_ptr<Encryptor> encryptor)
868 : backend_(new Backend(path,
869 base::MessageLoopProxy::current(),
870 encryptor.Pass())),
871 blocking_task_runner_(blocking_task_runner),
872 weak_ptr_factory_(this) {
875 GCMStoreImpl::~GCMStoreImpl() {}
877 void GCMStoreImpl::Load(const LoadCallback& callback) {
878 blocking_task_runner_->PostTask(
879 FROM_HERE,
880 base::Bind(&GCMStoreImpl::Backend::Load,
881 backend_,
882 base::Bind(&GCMStoreImpl::LoadContinuation,
883 weak_ptr_factory_.GetWeakPtr(),
884 callback)));
887 void GCMStoreImpl::Close() {
888 weak_ptr_factory_.InvalidateWeakPtrs();
889 app_message_counts_.clear();
890 blocking_task_runner_->PostTask(
891 FROM_HERE,
892 base::Bind(&GCMStoreImpl::Backend::Close, backend_));
895 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
896 blocking_task_runner_->PostTask(
897 FROM_HERE,
898 base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
901 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
902 uint64 device_security_token,
903 const UpdateCallback& callback) {
904 blocking_task_runner_->PostTask(
905 FROM_HERE,
906 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
907 backend_,
908 device_android_id,
909 device_security_token,
910 callback));
913 void GCMStoreImpl::AddRegistration(
914 const std::string& app_id,
915 const linked_ptr<RegistrationInfo>& registration,
916 const UpdateCallback& callback) {
917 blocking_task_runner_->PostTask(
918 FROM_HERE,
919 base::Bind(&GCMStoreImpl::Backend::AddRegistration,
920 backend_,
921 app_id,
922 registration,
923 callback));
926 void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
927 const UpdateCallback& callback) {
928 blocking_task_runner_->PostTask(
929 FROM_HERE,
930 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
931 backend_,
932 app_id,
933 callback));
936 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
937 const UpdateCallback& callback) {
938 blocking_task_runner_->PostTask(
939 FROM_HERE,
940 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
941 backend_,
942 persistent_id,
943 callback));
946 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
947 const UpdateCallback& callback) {
948 blocking_task_runner_->PostTask(
949 FROM_HERE,
950 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
951 backend_,
952 PersistentIdList(1, persistent_id),
953 callback));
956 void GCMStoreImpl::RemoveIncomingMessages(
957 const PersistentIdList& persistent_ids,
958 const UpdateCallback& callback) {
959 blocking_task_runner_->PostTask(
960 FROM_HERE,
961 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
962 backend_,
963 persistent_ids,
964 callback));
967 bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
968 const MCSMessage& message,
969 const UpdateCallback& callback) {
970 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
971 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
972 &message.GetProtobuf())->category();
973 DCHECK(!app_id.empty());
974 if (app_message_counts_.count(app_id) == 0)
975 app_message_counts_[app_id] = 0;
976 if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
977 app_message_counts_[app_id]++;
979 blocking_task_runner_->PostTask(
980 FROM_HERE,
981 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
982 backend_,
983 persistent_id,
984 message,
985 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
986 weak_ptr_factory_.GetWeakPtr(),
987 callback,
988 app_id)));
989 return true;
991 return false;
994 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
995 const MCSMessage& message,
996 const UpdateCallback& callback) {
997 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
998 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
999 &message.GetProtobuf())->category();
1000 DCHECK(!app_id.empty());
1001 // There should already be pending messages for this app.
1002 DCHECK(app_message_counts_.count(app_id));
1003 // TODO(zea): consider verifying the specific message already exists.
1004 blocking_task_runner_->PostTask(
1005 FROM_HERE,
1006 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1007 backend_,
1008 persistent_id,
1009 message,
1010 callback));
1013 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
1014 const UpdateCallback& callback) {
1015 blocking_task_runner_->PostTask(
1016 FROM_HERE,
1017 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1018 backend_,
1019 PersistentIdList(1, persistent_id),
1020 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1021 weak_ptr_factory_.GetWeakPtr(),
1022 callback)));
1025 void GCMStoreImpl::RemoveOutgoingMessages(
1026 const PersistentIdList& persistent_ids,
1027 const UpdateCallback& callback) {
1028 blocking_task_runner_->PostTask(
1029 FROM_HERE,
1030 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1031 backend_,
1032 persistent_ids,
1033 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1034 weak_ptr_factory_.GetWeakPtr(),
1035 callback)));
1038 void GCMStoreImpl::SetLastCheckinInfo(const base::Time& time,
1039 const std::set<std::string>& accounts,
1040 const UpdateCallback& callback) {
1041 blocking_task_runner_->PostTask(
1042 FROM_HERE,
1043 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo,
1044 backend_,
1045 time,
1046 accounts,
1047 callback));
1050 void GCMStoreImpl::SetGServicesSettings(
1051 const std::map<std::string, std::string>& settings,
1052 const std::string& digest,
1053 const UpdateCallback& callback) {
1054 blocking_task_runner_->PostTask(
1055 FROM_HERE,
1056 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
1057 backend_,
1058 settings,
1059 digest,
1060 callback));
1063 void GCMStoreImpl::AddAccountMapping(const AccountMapping& account_mapping,
1064 const UpdateCallback& callback) {
1065 blocking_task_runner_->PostTask(
1066 FROM_HERE,
1067 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
1068 backend_,
1069 account_mapping,
1070 callback));
1073 void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
1074 const UpdateCallback& callback) {
1075 blocking_task_runner_->PostTask(
1076 FROM_HERE,
1077 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
1078 backend_,
1079 account_id,
1080 callback));
1083 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time& time,
1084 const UpdateCallback& callback) {
1085 blocking_task_runner_->PostTask(
1086 FROM_HERE,
1087 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime,
1088 backend_,
1089 time,
1090 callback));
1093 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
1094 scoped_ptr<LoadResult> result) {
1095 if (!result->success) {
1096 callback.Run(result.Pass());
1097 return;
1099 int num_throttled_apps = 0;
1100 for (OutgoingMessageMap::const_iterator
1101 iter = result->outgoing_messages.begin();
1102 iter != result->outgoing_messages.end(); ++iter) {
1103 const mcs_proto::DataMessageStanza* data_message =
1104 reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
1105 DCHECK(!data_message->category().empty());
1106 if (app_message_counts_.count(data_message->category()) == 0)
1107 app_message_counts_[data_message->category()] = 1;
1108 else
1109 app_message_counts_[data_message->category()]++;
1110 if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
1111 num_throttled_apps++;
1113 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
1114 callback.Run(result.Pass());
1117 void GCMStoreImpl::AddOutgoingMessageContinuation(
1118 const UpdateCallback& callback,
1119 const std::string& app_id,
1120 bool success) {
1121 if (!success) {
1122 DCHECK(app_message_counts_[app_id] > 0);
1123 app_message_counts_[app_id]--;
1125 callback.Run(success);
1128 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1129 const UpdateCallback& callback,
1130 bool success,
1131 const AppIdToMessageCountMap& removed_message_counts) {
1132 if (!success) {
1133 callback.Run(false);
1134 return;
1136 for (AppIdToMessageCountMap::const_iterator iter =
1137 removed_message_counts.begin();
1138 iter != removed_message_counts.end(); ++iter) {
1139 DCHECK_NE(app_message_counts_.count(iter->first), 0U);
1140 app_message_counts_[iter->first] -= iter->second;
1141 DCHECK_GE(app_message_counts_[iter->first], 0);
1143 callback.Run(true);
1146 } // namespace gcm