Re-land: C++ readability review
[chromium-blink-merge.git] / google_apis / gcm / engine / gcm_store_impl.cc
blobbc526c4e64ca3b3c95a070aff5630c29f8923c8d
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_macros.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/env_chromium.h"
27 #include "third_party/leveldatabase/src/include/leveldb/db.h"
28 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
30 namespace gcm {
32 namespace {
34 // This enum is used in an UMA histogram (GCMLoadStatus enum defined in
35 // tools/metrics/histograms/histogram.xml). Hence the entries here shouldn't
36 // be deleted or re-ordered and new ones should be added to the end.
37 enum LoadStatus {
38 LOADING_SUCCEEDED,
39 RELOADING_OPEN_STORE,
40 OPENING_STORE_FAILED,
41 LOADING_DEVICE_CREDENTIALS_FAILED,
42 LOADING_REGISTRATION_FAILED,
43 LOADING_INCOMING_MESSAGES_FAILED,
44 LOADING_OUTGOING_MESSAGES_FAILED,
45 LOADING_LAST_CHECKIN_INFO_FAILED,
46 LOADING_GSERVICE_SETTINGS_FAILED,
47 LOADING_ACCOUNT_MAPPING_FAILED,
48 LOADING_LAST_TOKEN_TIME_FAILED,
50 // NOTE: always keep this entry at the end. Add new status types only
51 // immediately above this line. Make sure to update the corresponding
52 // histogram enum accordingly.
53 LOAD_STATUS_COUNT
56 // Limit to the number of outstanding messages per app.
57 const int kMessagesPerAppLimit = 20;
59 // ---- LevelDB keys. ----
60 // Key for this device's android id.
61 const char kDeviceAIDKey[] = "device_aid_key";
62 // Key for this device's android security token.
63 const char kDeviceTokenKey[] = "device_token_key";
64 // Lowest lexicographically ordered app ids.
65 // Used for prefixing app id.
66 const char kRegistrationKeyStart[] = "reg1-";
67 // Key guaranteed to be higher than all app ids.
68 // Used for limiting iteration.
69 const char kRegistrationKeyEnd[] = "reg2-";
70 // Lowest lexicographically ordered incoming message key.
71 // Used for prefixing messages.
72 const char kIncomingMsgKeyStart[] = "incoming1-";
73 // Key guaranteed to be higher than all incoming message keys.
74 // Used for limiting iteration.
75 const char kIncomingMsgKeyEnd[] = "incoming2-";
76 // Lowest lexicographically ordered outgoing message key.
77 // Used for prefixing outgoing messages.
78 const char kOutgoingMsgKeyStart[] = "outgoing1-";
79 // Key guaranteed to be higher than all outgoing message keys.
80 // Used for limiting iteration.
81 const char kOutgoingMsgKeyEnd[] = "outgoing2-";
82 // Lowest lexicographically ordered G-service settings key.
83 // Used for prefixing G-services settings.
84 const char kGServiceSettingKeyStart[] = "gservice1-";
85 // Key guaranteed to be higher than all G-services settings keys.
86 // Used for limiting iteration.
87 const char kGServiceSettingKeyEnd[] = "gservice2-";
88 // Key for digest of the last G-services settings update.
89 const char kGServiceSettingsDigestKey[] = "gservices_digest";
90 // Key used to indicate how many accounts were last checked in with this device.
91 const char kLastCheckinAccountsKey[] = "last_checkin_accounts_count";
92 // Key used to timestamp last checkin (marked with G services settings update).
93 const char kLastCheckinTimeKey[] = "last_checkin_time";
94 // Lowest lexicographically ordered account key.
95 // Used for prefixing account information.
96 const char kAccountKeyStart[] = "account1-";
97 // Key guaranteed to be higher than all account keys.
98 // Used for limiting iteration.
99 const char kAccountKeyEnd[] = "account2-";
100 // Key used for last token fetch time.
101 const char kLastTokenFetchTimeKey[] = "last_token_fetch_time";
103 std::string MakeRegistrationKey(const std::string& app_id) {
104 return kRegistrationKeyStart + app_id;
107 std::string ParseRegistrationKey(const std::string& key) {
108 return key.substr(arraysize(kRegistrationKeyStart) - 1);
111 std::string MakeIncomingKey(const std::string& persistent_id) {
112 return kIncomingMsgKeyStart + persistent_id;
115 std::string MakeOutgoingKey(const std::string& persistent_id) {
116 return kOutgoingMsgKeyStart + persistent_id;
119 std::string ParseOutgoingKey(const std::string& key) {
120 return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
123 std::string MakeGServiceSettingKey(const std::string& setting_name) {
124 return kGServiceSettingKeyStart + setting_name;
127 std::string ParseGServiceSettingKey(const std::string& key) {
128 return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
131 std::string MakeAccountKey(const std::string& account_id) {
132 return kAccountKeyStart + account_id;
135 std::string ParseAccountKey(const std::string& key) {
136 return key.substr(arraysize(kAccountKeyStart) - 1);
139 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
140 // outlive the slice.
141 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
142 leveldb::Slice MakeSlice(const base::StringPiece& s) {
143 return leveldb::Slice(s.begin(), s.size());
146 } // namespace
148 class GCMStoreImpl::Backend
149 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
150 public:
151 Backend(const base::FilePath& path,
152 scoped_refptr<base::SequencedTaskRunner> foreground_runner,
153 scoped_ptr<Encryptor> encryptor);
155 // Blocking implementations of GCMStoreImpl methods.
156 void Load(const LoadCallback& callback);
157 void Close();
158 void Destroy(const UpdateCallback& callback);
159 void SetDeviceCredentials(uint64 device_android_id,
160 uint64 device_security_token,
161 const UpdateCallback& callback);
162 void AddRegistration(const std::string& app_id,
163 const std::string& serialized_registration,
164 const UpdateCallback& callback);
165 void RemoveRegistration(const std::string& app_id,
166 const UpdateCallback& callback);
167 void AddIncomingMessage(const std::string& persistent_id,
168 const UpdateCallback& callback);
169 void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
170 const UpdateCallback& callback);
171 void AddOutgoingMessage(const std::string& persistent_id,
172 const MCSMessage& message,
173 const UpdateCallback& callback);
174 void RemoveOutgoingMessages(
175 const PersistentIdList& persistent_ids,
176 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
177 callback);
178 void AddUserSerialNumber(const std::string& username,
179 int64 serial_number,
180 const UpdateCallback& callback);
181 void RemoveUserSerialNumber(const std::string& username,
182 const UpdateCallback& callback);
183 void SetLastCheckinInfo(const base::Time& time,
184 const std::set<std::string>& accounts,
185 const UpdateCallback& callback);
186 void SetGServicesSettings(
187 const std::map<std::string, std::string>& settings,
188 const std::string& digest,
189 const UpdateCallback& callback);
190 void AddAccountMapping(const AccountMapping& account_mapping,
191 const UpdateCallback& callback);
192 void RemoveAccountMapping(const std::string& account_id,
193 const UpdateCallback& callback);
194 void SetLastTokenFetchTime(const base::Time& time,
195 const UpdateCallback& callback);
196 void SetValue(const std::string& key,
197 const std::string& value,
198 const UpdateCallback& callback);
200 private:
201 friend class base::RefCountedThreadSafe<Backend>;
202 ~Backend();
204 LoadStatus OpenStoreAndLoadData(LoadResult* result);
205 bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
206 bool LoadRegistrations(RegistrationInfoMap* registrations);
207 bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
208 bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
209 bool LoadLastCheckinInfo(base::Time* last_checkin_time,
210 std::set<std::string>* accounts);
211 bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
212 std::string* digest);
213 bool LoadAccountMappingInfo(AccountMappings* account_mappings);
214 bool LoadLastTokenFetchTime(base::Time* last_token_fetch_time);
216 const base::FilePath path_;
217 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
218 scoped_ptr<Encryptor> encryptor_;
220 scoped_ptr<leveldb::DB> db_;
223 GCMStoreImpl::Backend::Backend(
224 const base::FilePath& path,
225 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
226 scoped_ptr<Encryptor> encryptor)
227 : path_(path),
228 foreground_task_runner_(foreground_task_runner),
229 encryptor_(encryptor.Pass()) {
232 GCMStoreImpl::Backend::~Backend() {}
234 LoadStatus GCMStoreImpl::Backend::OpenStoreAndLoadData(LoadResult* result) {
235 LoadStatus load_status;
236 if (db_.get()) {
237 LOG(ERROR) << "Attempting to reload open database.";
238 return RELOADING_OPEN_STORE;
241 leveldb::Options options;
242 options.create_if_missing = true;
243 options.reuse_logs = leveldb_env::kDefaultLogReuseOptionValue;
244 leveldb::DB* db;
245 leveldb::Status status =
246 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
247 if (!status.ok()) {
248 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
249 << status.ToString();
250 return OPENING_STORE_FAILED;
253 db_.reset(db);
254 if (!LoadDeviceCredentials(&result->device_android_id,
255 &result->device_security_token)) {
256 return LOADING_DEVICE_CREDENTIALS_FAILED;
258 if (!LoadRegistrations(&result->registrations))
259 return LOADING_REGISTRATION_FAILED;
260 if (!LoadIncomingMessages(&result->incoming_messages))
261 return LOADING_INCOMING_MESSAGES_FAILED;
262 if (!LoadOutgoingMessages(&result->outgoing_messages))
263 return LOADING_OUTGOING_MESSAGES_FAILED;
264 if (!LoadLastCheckinInfo(&result->last_checkin_time,
265 &result->last_checkin_accounts)) {
266 return LOADING_LAST_CHECKIN_INFO_FAILED;
268 if (!LoadGServicesSettings(&result->gservices_settings,
269 &result->gservices_digest)) {
270 return load_status = LOADING_GSERVICE_SETTINGS_FAILED;
272 if (!LoadAccountMappingInfo(&result->account_mappings))
273 return LOADING_ACCOUNT_MAPPING_FAILED;
274 if (!LoadLastTokenFetchTime(&result->last_token_fetch_time))
275 return LOADING_LAST_TOKEN_TIME_FAILED;
277 return LOADING_SUCCEEDED;
280 void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
281 scoped_ptr<LoadResult> result(new LoadResult());
282 LoadStatus load_status = OpenStoreAndLoadData(result.get());
283 UMA_HISTOGRAM_ENUMERATION("GCM.LoadStatus", load_status, LOAD_STATUS_COUNT);
284 if (load_status != LOADING_SUCCEEDED) {
285 result->Reset();
286 foreground_task_runner_->PostTask(FROM_HERE,
287 base::Bind(callback,
288 base::Passed(&result)));
289 return;
292 // Only record histograms if GCM had already been set up for this device.
293 if (result->device_android_id != 0 && result->device_security_token != 0) {
294 int64 file_size = 0;
295 if (base::GetFileSize(path_, &file_size)) {
296 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
297 static_cast<int>(file_size / 1024));
299 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
300 result->registrations.size());
301 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
302 result->outgoing_messages.size());
303 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
304 result->incoming_messages.size());
307 DVLOG(1) << "Succeeded in loading " << result->registrations.size()
308 << " registrations, "
309 << result->incoming_messages.size()
310 << " unacknowledged incoming messages and "
311 << result->outgoing_messages.size()
312 << " unacknowledged outgoing messages.";
313 result->success = true;
314 foreground_task_runner_->PostTask(FROM_HERE,
315 base::Bind(callback,
316 base::Passed(&result)));
317 return;
320 void GCMStoreImpl::Backend::Close() {
321 DVLOG(1) << "Closing GCM store.";
322 db_.reset();
325 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
326 DVLOG(1) << "Destroying GCM store.";
327 db_.reset();
328 const leveldb::Status s =
329 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
330 if (s.ok()) {
331 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
332 return;
334 LOG(ERROR) << "Destroy failed: " << s.ToString();
335 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
338 void GCMStoreImpl::Backend::SetDeviceCredentials(
339 uint64 device_android_id,
340 uint64 device_security_token,
341 const UpdateCallback& callback) {
342 DVLOG(1) << "Saving device credentials with AID " << device_android_id;
343 if (!db_.get()) {
344 LOG(ERROR) << "GCMStore db doesn't exist.";
345 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
346 return;
349 leveldb::WriteOptions write_options;
350 write_options.sync = true;
352 std::string encrypted_token;
353 encryptor_->EncryptString(base::Uint64ToString(device_security_token),
354 &encrypted_token);
355 std::string android_id_str = base::Uint64ToString(device_android_id);
356 leveldb::Status s =
357 db_->Put(write_options,
358 MakeSlice(kDeviceAIDKey),
359 MakeSlice(android_id_str));
360 if (s.ok()) {
361 s = db_->Put(
362 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
364 if (s.ok()) {
365 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
366 return;
368 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
369 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
372 void GCMStoreImpl::Backend::AddRegistration(
373 const std::string& app_id,
374 const std::string& serialized_registration,
375 const UpdateCallback& callback) {
376 DVLOG(1) << "Saving registration info for app: " << app_id;
377 if (!db_.get()) {
378 LOG(ERROR) << "GCMStore db doesn't exist.";
379 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
380 return;
382 leveldb::WriteOptions write_options;
383 write_options.sync = true;
385 std::string key = MakeRegistrationKey(app_id);
386 const leveldb::Status status = db_->Put(write_options,
387 MakeSlice(key),
388 MakeSlice(serialized_registration));
389 if (status.ok()) {
390 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
391 return;
393 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
394 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
397 void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id,
398 const UpdateCallback& callback) {
399 if (!db_.get()) {
400 LOG(ERROR) << "GCMStore db doesn't exist.";
401 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
402 return;
404 leveldb::WriteOptions write_options;
405 write_options.sync = true;
407 leveldb::Status status =
408 db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
409 if (status.ok()) {
410 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
411 return;
413 LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
414 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
417 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
418 const UpdateCallback& callback) {
419 DVLOG(1) << "Saving incoming message with id " << persistent_id;
420 if (!db_.get()) {
421 LOG(ERROR) << "GCMStore db doesn't exist.";
422 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
423 return;
426 leveldb::WriteOptions write_options;
427 write_options.sync = true;
429 std::string key = MakeIncomingKey(persistent_id);
430 const leveldb::Status s = db_->Put(write_options,
431 MakeSlice(key),
432 MakeSlice(persistent_id));
433 if (s.ok()) {
434 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
435 return;
437 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
438 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
441 void GCMStoreImpl::Backend::RemoveIncomingMessages(
442 const PersistentIdList& persistent_ids,
443 const UpdateCallback& callback) {
444 if (!db_.get()) {
445 LOG(ERROR) << "GCMStore db doesn't exist.";
446 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
447 return;
449 leveldb::WriteOptions write_options;
450 write_options.sync = true;
452 leveldb::Status s;
453 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
454 iter != persistent_ids.end();
455 ++iter) {
456 DVLOG(1) << "Removing incoming message with id " << *iter;
457 std::string key = MakeIncomingKey(*iter);
458 s = db_->Delete(write_options, MakeSlice(key));
459 if (!s.ok())
460 break;
462 if (s.ok()) {
463 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
464 return;
466 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
467 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
470 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
471 const MCSMessage& message,
472 const UpdateCallback& callback) {
473 DVLOG(1) << "Saving outgoing message with id " << persistent_id;
474 if (!db_.get()) {
475 LOG(ERROR) << "GCMStore db doesn't exist.";
476 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
477 return;
479 leveldb::WriteOptions write_options;
480 write_options.sync = true;
482 std::string data =
483 static_cast<char>(message.tag()) + message.SerializeAsString();
484 std::string key = MakeOutgoingKey(persistent_id);
485 const leveldb::Status s = db_->Put(write_options,
486 MakeSlice(key),
487 MakeSlice(data));
488 if (s.ok()) {
489 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
490 return;
492 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
493 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
496 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
497 const PersistentIdList& persistent_ids,
498 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
499 callback) {
500 if (!db_.get()) {
501 LOG(ERROR) << "GCMStore db doesn't exist.";
502 foreground_task_runner_->PostTask(FROM_HERE,
503 base::Bind(callback,
504 false,
505 AppIdToMessageCountMap()));
506 return;
508 leveldb::ReadOptions read_options;
509 leveldb::WriteOptions write_options;
510 write_options.sync = true;
512 AppIdToMessageCountMap removed_message_counts;
514 leveldb::Status s;
515 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
516 iter != persistent_ids.end();
517 ++iter) {
518 DVLOG(1) << "Removing outgoing message with id " << *iter;
519 std::string outgoing_message;
520 std::string key = MakeOutgoingKey(*iter);
521 s = db_->Get(read_options,
522 MakeSlice(key),
523 &outgoing_message);
524 if (!s.ok())
525 break;
526 mcs_proto::DataMessageStanza data_message;
527 // Skip the initial tag byte and parse the rest to extract the message.
528 if (data_message.ParseFromString(outgoing_message.substr(1))) {
529 DCHECK(!data_message.category().empty());
530 if (removed_message_counts.count(data_message.category()) != 0)
531 removed_message_counts[data_message.category()]++;
532 else
533 removed_message_counts[data_message.category()] = 1;
535 DVLOG(1) << "Removing outgoing message with id " << *iter;
536 s = db_->Delete(write_options, MakeSlice(key));
537 if (!s.ok())
538 break;
540 if (s.ok()) {
541 foreground_task_runner_->PostTask(FROM_HERE,
542 base::Bind(callback,
543 true,
544 removed_message_counts));
545 return;
547 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
548 foreground_task_runner_->PostTask(FROM_HERE,
549 base::Bind(callback,
550 false,
551 AppIdToMessageCountMap()));
554 void GCMStoreImpl::Backend::SetLastCheckinInfo(
555 const base::Time& time,
556 const std::set<std::string>& accounts,
557 const UpdateCallback& callback) {
558 leveldb::WriteBatch write_batch;
560 int64 last_checkin_time_internal = time.ToInternalValue();
561 write_batch.Put(MakeSlice(kLastCheckinTimeKey),
562 MakeSlice(base::Int64ToString(last_checkin_time_internal)));
564 std::string serialized_accounts;
565 for (std::set<std::string>::iterator iter = accounts.begin();
566 iter != accounts.end();
567 ++iter) {
568 serialized_accounts += *iter;
569 serialized_accounts += ",";
571 if (!serialized_accounts.empty())
572 serialized_accounts.erase(serialized_accounts.length() - 1);
574 write_batch.Put(MakeSlice(kLastCheckinAccountsKey),
575 MakeSlice(serialized_accounts));
577 leveldb::WriteOptions write_options;
578 write_options.sync = true;
579 const leveldb::Status s = db_->Write(write_options, &write_batch);
581 if (!s.ok())
582 LOG(ERROR) << "LevelDB set last checkin info failed: " << s.ToString();
583 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
586 void GCMStoreImpl::Backend::SetGServicesSettings(
587 const std::map<std::string, std::string>& settings,
588 const std::string& settings_digest,
589 const UpdateCallback& callback) {
590 leveldb::WriteBatch write_batch;
592 // Remove all existing settings.
593 leveldb::ReadOptions read_options;
594 read_options.verify_checksums = true;
595 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
596 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
597 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
598 iter->Next()) {
599 write_batch.Delete(iter->key());
602 // Add the new settings.
603 for (std::map<std::string, std::string>::const_iterator iter =
604 settings.begin();
605 iter != settings.end(); ++iter) {
606 write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
607 MakeSlice(iter->second));
610 // Update the settings digest.
611 write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
612 MakeSlice(settings_digest));
614 // Write it all in a batch.
615 leveldb::WriteOptions write_options;
616 write_options.sync = true;
618 leveldb::Status s = db_->Write(write_options, &write_batch);
619 if (!s.ok())
620 LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
621 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
624 void GCMStoreImpl::Backend::AddAccountMapping(
625 const AccountMapping& account_mapping,
626 const UpdateCallback& callback) {
627 DVLOG(1) << "Saving account info for account with email: "
628 << account_mapping.email;
629 if (!db_.get()) {
630 LOG(ERROR) << "GCMStore db doesn't exist.";
631 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
632 return;
635 leveldb::WriteOptions write_options;
636 write_options.sync = true;
638 std::string data = account_mapping.SerializeAsString();
639 std::string key = MakeAccountKey(account_mapping.account_id);
640 const leveldb::Status s =
641 db_->Put(write_options, MakeSlice(key), MakeSlice(data));
642 if (!s.ok())
643 LOG(ERROR) << "LevelDB adding account mapping failed: " << s.ToString();
644 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
647 void GCMStoreImpl::Backend::RemoveAccountMapping(
648 const std::string& account_id,
649 const UpdateCallback& callback) {
650 if (!db_.get()) {
651 LOG(ERROR) << "GCMStore db doesn't exist.";
652 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
653 return;
656 leveldb::WriteOptions write_options;
657 write_options.sync = true;
659 leveldb::Status s =
660 db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
662 if (!s.ok())
663 LOG(ERROR) << "LevelDB removal of account mapping failed: " << s.ToString();
664 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
667 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
668 const base::Time& time,
669 const UpdateCallback& callback) {
670 DVLOG(1) << "Setting last token fetching time.";
671 if (!db_.get()) {
672 LOG(ERROR) << "GCMStore db doesn't exist.";
673 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
674 return;
677 leveldb::WriteOptions write_options;
678 write_options.sync = true;
680 const leveldb::Status s =
681 db_->Put(write_options,
682 MakeSlice(kLastTokenFetchTimeKey),
683 MakeSlice(base::Int64ToString(time.ToInternalValue())));
685 if (!s.ok())
686 LOG(ERROR) << "LevelDB setting last token fetching time: " << s.ToString();
687 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
690 void GCMStoreImpl::Backend::SetValue(const std::string& key,
691 const std::string& value,
692 const UpdateCallback& callback) {
693 DVLOG(1) << "Injecting a value to GCM Store for testing. Key: "
694 << key << ", Value: " << value;
695 if (!db_.get()) {
696 LOG(ERROR) << "GCMStore db doesn't exist.";
697 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
698 return;
701 leveldb::WriteOptions write_options;
702 write_options.sync = true;
704 const leveldb::Status s =
705 db_->Put(write_options, MakeSlice(key), MakeSlice(value));
707 if (!s.ok())
708 LOG(ERROR) << "LevelDB had problems injecting a value: " << s.ToString();
709 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
712 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
713 uint64* security_token) {
714 leveldb::ReadOptions read_options;
715 read_options.verify_checksums = true;
717 std::string result;
718 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
719 if (s.ok()) {
720 if (!base::StringToUint64(result, android_id)) {
721 LOG(ERROR) << "Failed to restore device id.";
722 return false;
724 result.clear();
725 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
727 if (s.ok()) {
728 std::string decrypted_token;
729 encryptor_->DecryptString(result, &decrypted_token);
730 if (!base::StringToUint64(decrypted_token, security_token)) {
731 LOG(ERROR) << "Failed to restore security token.";
732 return false;
734 return true;
737 if (s.IsNotFound()) {
738 DVLOG(1) << "No credentials found.";
739 return true;
742 LOG(ERROR) << "Error reading credentials from store.";
743 return false;
746 bool GCMStoreImpl::Backend::LoadRegistrations(
747 RegistrationInfoMap* registrations) {
748 leveldb::ReadOptions read_options;
749 read_options.verify_checksums = true;
751 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
752 for (iter->Seek(MakeSlice(kRegistrationKeyStart));
753 iter->Valid() && iter->key().ToString() < kRegistrationKeyEnd;
754 iter->Next()) {
755 leveldb::Slice s = iter->value();
756 if (s.size() <= 1) {
757 LOG(ERROR) << "Error reading registration with key " << s.ToString();
758 return false;
760 std::string app_id = ParseRegistrationKey(iter->key().ToString());
761 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
762 if (!registration->ParseFromString(iter->value().ToString())) {
763 LOG(ERROR) << "Failed to parse registration with app id " << app_id;
764 return false;
766 DVLOG(1) << "Found registration with app id " << app_id;
767 (*registrations)[app_id] = registration;
770 return true;
773 bool GCMStoreImpl::Backend::LoadIncomingMessages(
774 std::vector<std::string>* incoming_messages) {
775 leveldb::ReadOptions read_options;
776 read_options.verify_checksums = true;
778 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
779 for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
780 iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
781 iter->Next()) {
782 leveldb::Slice s = iter->value();
783 if (s.empty()) {
784 LOG(ERROR) << "Error reading incoming message with key "
785 << iter->key().ToString();
786 return false;
788 DVLOG(1) << "Found incoming message with id " << s.ToString();
789 incoming_messages->push_back(s.ToString());
792 return true;
795 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
796 OutgoingMessageMap* outgoing_messages) {
797 leveldb::ReadOptions read_options;
798 read_options.verify_checksums = true;
800 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
801 for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
802 iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
803 iter->Next()) {
804 leveldb::Slice s = iter->value();
805 if (s.size() <= 1) {
806 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
807 return false;
809 uint8 tag = iter->value().data()[0];
810 std::string id = ParseOutgoingKey(iter->key().ToString());
811 scoped_ptr<google::protobuf::MessageLite> message(
812 BuildProtobufFromTag(tag));
813 if (!message.get() ||
814 !message->ParseFromString(iter->value().ToString().substr(1))) {
815 LOG(ERROR) << "Failed to parse outgoing message with id " << id
816 << " and tag " << tag;
817 return false;
819 DVLOG(1) << "Found outgoing message with id " << id << " of type "
820 << base::IntToString(tag);
821 (*outgoing_messages)[id] = make_linked_ptr(message.release());
824 return true;
827 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
828 base::Time* last_checkin_time,
829 std::set<std::string>* accounts) {
830 leveldb::ReadOptions read_options;
831 read_options.verify_checksums = true;
833 std::string result;
834 leveldb::Status s = db_->Get(read_options,
835 MakeSlice(kLastCheckinTimeKey),
836 &result);
837 int64 time_internal = 0LL;
838 if (s.ok() && !base::StringToInt64(result, &time_internal)) {
839 LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
840 time_internal = 0LL;
843 // In case we cannot read last checkin time, we default it to 0, as we don't
844 // want that situation to cause the whole load to fail.
845 *last_checkin_time = base::Time::FromInternalValue(time_internal);
847 accounts->clear();
848 s = db_->Get(read_options, MakeSlice(kLastCheckinAccountsKey), &result);
849 if (!s.ok())
850 DVLOG(1) << "No accounts where stored during last run.";
852 base::StringTokenizer t(result, ",");
853 while (t.GetNext())
854 accounts->insert(t.token());
856 return true;
859 bool GCMStoreImpl::Backend::LoadGServicesSettings(
860 std::map<std::string, std::string>* settings,
861 std::string* digest) {
862 leveldb::ReadOptions read_options;
863 read_options.verify_checksums = true;
865 // Load all of the GServices settings.
866 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
867 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
868 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
869 iter->Next()) {
870 std::string value = iter->value().ToString();
871 if (value.empty()) {
872 LOG(ERROR) << "Error reading GService Settings " << value;
873 return false;
875 std::string id = ParseGServiceSettingKey(iter->key().ToString());
876 (*settings)[id] = value;
877 DVLOG(1) << "Found G Service setting with key: " << id
878 << ", and value: " << value;
881 // Load the settings digest. It's ok if it is empty.
882 db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
884 return true;
887 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
888 AccountMappings* account_mappings) {
889 leveldb::ReadOptions read_options;
890 read_options.verify_checksums = true;
892 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
893 for (iter->Seek(MakeSlice(kAccountKeyStart));
894 iter->Valid() && iter->key().ToString() < kAccountKeyEnd;
895 iter->Next()) {
896 AccountMapping account_mapping;
897 account_mapping.account_id = ParseAccountKey(iter->key().ToString());
898 if (!account_mapping.ParseFromString(iter->value().ToString())) {
899 DVLOG(1) << "Failed to parse account info with ID: "
900 << account_mapping.account_id;
901 return false;
903 DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
904 account_mappings->push_back(account_mapping);
907 return true;
910 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
911 base::Time* last_token_fetch_time) {
912 leveldb::ReadOptions read_options;
913 read_options.verify_checksums = true;
915 std::string result;
916 leveldb::Status s =
917 db_->Get(read_options, MakeSlice(kLastTokenFetchTimeKey), &result);
918 int64 time_internal = 0LL;
919 if (s.ok() && !base::StringToInt64(result, &time_internal)) {
920 LOG(ERROR) <<
921 "Failed to restore last token fetching time. Using default = 0.";
922 time_internal = 0LL;
925 // In case we cannot read last token fetching time, we default it to 0.
926 *last_token_fetch_time = base::Time::FromInternalValue(time_internal);
928 return true;
931 GCMStoreImpl::GCMStoreImpl(
932 const base::FilePath& path,
933 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
934 scoped_ptr<Encryptor> encryptor)
935 : backend_(new Backend(path,
936 base::MessageLoopProxy::current(),
937 encryptor.Pass())),
938 blocking_task_runner_(blocking_task_runner),
939 weak_ptr_factory_(this) {
942 GCMStoreImpl::~GCMStoreImpl() {}
944 void GCMStoreImpl::Load(const LoadCallback& callback) {
945 blocking_task_runner_->PostTask(
946 FROM_HERE,
947 base::Bind(&GCMStoreImpl::Backend::Load,
948 backend_,
949 base::Bind(&GCMStoreImpl::LoadContinuation,
950 weak_ptr_factory_.GetWeakPtr(),
951 callback)));
954 void GCMStoreImpl::Close() {
955 weak_ptr_factory_.InvalidateWeakPtrs();
956 app_message_counts_.clear();
957 blocking_task_runner_->PostTask(
958 FROM_HERE,
959 base::Bind(&GCMStoreImpl::Backend::Close, backend_));
962 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
963 blocking_task_runner_->PostTask(
964 FROM_HERE,
965 base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
968 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
969 uint64 device_security_token,
970 const UpdateCallback& callback) {
971 blocking_task_runner_->PostTask(
972 FROM_HERE,
973 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
974 backend_,
975 device_android_id,
976 device_security_token,
977 callback));
980 void GCMStoreImpl::AddRegistration(
981 const std::string& app_id,
982 const linked_ptr<RegistrationInfo>& registration,
983 const UpdateCallback& callback) {
984 std::string serialized_registration = registration->SerializeAsString();
985 blocking_task_runner_->PostTask(
986 FROM_HERE,
987 base::Bind(&GCMStoreImpl::Backend::AddRegistration,
988 backend_,
989 app_id,
990 serialized_registration,
991 callback));
994 void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
995 const UpdateCallback& callback) {
996 blocking_task_runner_->PostTask(
997 FROM_HERE,
998 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
999 backend_,
1000 app_id,
1001 callback));
1004 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
1005 const UpdateCallback& callback) {
1006 blocking_task_runner_->PostTask(
1007 FROM_HERE,
1008 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
1009 backend_,
1010 persistent_id,
1011 callback));
1014 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
1015 const UpdateCallback& callback) {
1016 blocking_task_runner_->PostTask(
1017 FROM_HERE,
1018 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
1019 backend_,
1020 PersistentIdList(1, persistent_id),
1021 callback));
1024 void GCMStoreImpl::RemoveIncomingMessages(
1025 const PersistentIdList& persistent_ids,
1026 const UpdateCallback& callback) {
1027 blocking_task_runner_->PostTask(
1028 FROM_HERE,
1029 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
1030 backend_,
1031 persistent_ids,
1032 callback));
1035 bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
1036 const MCSMessage& message,
1037 const UpdateCallback& callback) {
1038 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
1039 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
1040 &message.GetProtobuf())->category();
1041 DCHECK(!app_id.empty());
1042 if (app_message_counts_.count(app_id) == 0)
1043 app_message_counts_[app_id] = 0;
1044 if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
1045 app_message_counts_[app_id]++;
1047 blocking_task_runner_->PostTask(
1048 FROM_HERE,
1049 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1050 backend_,
1051 persistent_id,
1052 message,
1053 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
1054 weak_ptr_factory_.GetWeakPtr(),
1055 callback,
1056 app_id)));
1057 return true;
1059 return false;
1062 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
1063 const MCSMessage& message,
1064 const UpdateCallback& callback) {
1065 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
1066 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
1067 &message.GetProtobuf())->category();
1068 DCHECK(!app_id.empty());
1069 // There should already be pending messages for this app.
1070 DCHECK(app_message_counts_.count(app_id));
1071 // TODO(zea): consider verifying the specific message already exists.
1072 blocking_task_runner_->PostTask(
1073 FROM_HERE,
1074 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1075 backend_,
1076 persistent_id,
1077 message,
1078 callback));
1081 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
1082 const UpdateCallback& callback) {
1083 blocking_task_runner_->PostTask(
1084 FROM_HERE,
1085 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1086 backend_,
1087 PersistentIdList(1, persistent_id),
1088 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1089 weak_ptr_factory_.GetWeakPtr(),
1090 callback)));
1093 void GCMStoreImpl::RemoveOutgoingMessages(
1094 const PersistentIdList& persistent_ids,
1095 const UpdateCallback& callback) {
1096 blocking_task_runner_->PostTask(
1097 FROM_HERE,
1098 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1099 backend_,
1100 persistent_ids,
1101 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1102 weak_ptr_factory_.GetWeakPtr(),
1103 callback)));
1106 void GCMStoreImpl::SetLastCheckinInfo(const base::Time& time,
1107 const std::set<std::string>& accounts,
1108 const UpdateCallback& callback) {
1109 blocking_task_runner_->PostTask(
1110 FROM_HERE,
1111 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo,
1112 backend_,
1113 time,
1114 accounts,
1115 callback));
1118 void GCMStoreImpl::SetGServicesSettings(
1119 const std::map<std::string, std::string>& settings,
1120 const std::string& digest,
1121 const UpdateCallback& callback) {
1122 blocking_task_runner_->PostTask(
1123 FROM_HERE,
1124 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
1125 backend_,
1126 settings,
1127 digest,
1128 callback));
1131 void GCMStoreImpl::AddAccountMapping(const AccountMapping& account_mapping,
1132 const UpdateCallback& callback) {
1133 blocking_task_runner_->PostTask(
1134 FROM_HERE,
1135 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
1136 backend_,
1137 account_mapping,
1138 callback));
1141 void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
1142 const UpdateCallback& callback) {
1143 blocking_task_runner_->PostTask(
1144 FROM_HERE,
1145 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
1146 backend_,
1147 account_id,
1148 callback));
1151 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time& time,
1152 const UpdateCallback& callback) {
1153 blocking_task_runner_->PostTask(
1154 FROM_HERE,
1155 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime,
1156 backend_,
1157 time,
1158 callback));
1161 void GCMStoreImpl::SetValueForTesting(const std::string& key,
1162 const std::string& value,
1163 const UpdateCallback& callback) {
1164 blocking_task_runner_->PostTask(
1165 FROM_HERE,
1166 base::Bind(&GCMStoreImpl::Backend::SetValue,
1167 backend_,
1168 key,
1169 value,
1170 callback));
1173 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
1174 scoped_ptr<LoadResult> result) {
1175 if (!result->success) {
1176 callback.Run(result.Pass());
1177 return;
1179 int num_throttled_apps = 0;
1180 for (OutgoingMessageMap::const_iterator
1181 iter = result->outgoing_messages.begin();
1182 iter != result->outgoing_messages.end(); ++iter) {
1183 const mcs_proto::DataMessageStanza* data_message =
1184 reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
1185 DCHECK(!data_message->category().empty());
1186 if (app_message_counts_.count(data_message->category()) == 0)
1187 app_message_counts_[data_message->category()] = 1;
1188 else
1189 app_message_counts_[data_message->category()]++;
1190 if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
1191 num_throttled_apps++;
1193 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
1194 callback.Run(result.Pass());
1197 void GCMStoreImpl::AddOutgoingMessageContinuation(
1198 const UpdateCallback& callback,
1199 const std::string& app_id,
1200 bool success) {
1201 if (!success) {
1202 DCHECK(app_message_counts_[app_id] > 0);
1203 app_message_counts_[app_id]--;
1205 callback.Run(success);
1208 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1209 const UpdateCallback& callback,
1210 bool success,
1211 const AppIdToMessageCountMap& removed_message_counts) {
1212 if (!success) {
1213 callback.Run(false);
1214 return;
1216 for (AppIdToMessageCountMap::const_iterator iter =
1217 removed_message_counts.begin();
1218 iter != removed_message_counts.end(); ++iter) {
1219 DCHECK_NE(app_message_counts_.count(iter->first), 0U);
1220 app_message_counts_[iter->first] -= iter->second;
1221 DCHECK_GE(app_message_counts_[iter->first], 0);
1223 callback.Run(true);
1226 } // namespace gcm