Fix sort order of unlaunched apps on app list start page.
[chromium-blink-merge.git] / google_apis / gcm / engine / gcm_store_impl.cc
blob8f701a145329f20e8552e9f41633ef345c4831f1
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/src/include/leveldb/db.h"
27 #include "third_party/leveldatabase/src/include/leveldb/write_batch.h"
29 namespace gcm {
31 namespace {
33 // This enum is used in an UMA histogram (GCMLoadStatus enum defined in
34 // tools/metrics/histograms/histogram.xml). Hence the entries here shouldn't
35 // be deleted or re-ordered and new ones should be added to the end.
36 enum LoadStatus {
37 LOADING_SUCCEEDED,
38 RELOADING_OPEN_STORE,
39 OPENING_STORE_FAILED,
40 LOADING_DEVICE_CREDENTIALS_FAILED,
41 LOADING_REGISTRATION_FAILED,
42 LOADING_INCOMING_MESSAGES_FAILED,
43 LOADING_OUTGOING_MESSAGES_FAILED,
44 LOADING_LAST_CHECKIN_INFO_FAILED,
45 LOADING_GSERVICE_SETTINGS_FAILED,
46 LOADING_ACCOUNT_MAPPING_FAILED,
47 LOADING_LAST_TOKEN_TIME_FAILED,
49 // NOTE: always keep this entry at the end. Add new status types only
50 // immediately above this line. Make sure to update the corresponding
51 // histogram enum accordingly.
52 LOAD_STATUS_COUNT
55 // Limit to the number of outstanding messages per app.
56 const int kMessagesPerAppLimit = 20;
58 // ---- LevelDB keys. ----
59 // Key for this device's android id.
60 const char kDeviceAIDKey[] = "device_aid_key";
61 // Key for this device's android security token.
62 const char kDeviceTokenKey[] = "device_token_key";
63 // Lowest lexicographically ordered app ids.
64 // Used for prefixing app id.
65 const char kRegistrationKeyStart[] = "reg1-";
66 // Key guaranteed to be higher than all app ids.
67 // Used for limiting iteration.
68 const char kRegistrationKeyEnd[] = "reg2-";
69 // Lowest lexicographically ordered incoming message key.
70 // Used for prefixing messages.
71 const char kIncomingMsgKeyStart[] = "incoming1-";
72 // Key guaranteed to be higher than all incoming message keys.
73 // Used for limiting iteration.
74 const char kIncomingMsgKeyEnd[] = "incoming2-";
75 // Lowest lexicographically ordered outgoing message key.
76 // Used for prefixing outgoing messages.
77 const char kOutgoingMsgKeyStart[] = "outgoing1-";
78 // Key guaranteed to be higher than all outgoing message keys.
79 // Used for limiting iteration.
80 const char kOutgoingMsgKeyEnd[] = "outgoing2-";
81 // Lowest lexicographically ordered G-service settings key.
82 // Used for prefixing G-services settings.
83 const char kGServiceSettingKeyStart[] = "gservice1-";
84 // Key guaranteed to be higher than all G-services settings keys.
85 // Used for limiting iteration.
86 const char kGServiceSettingKeyEnd[] = "gservice2-";
87 // Key for digest of the last G-services settings update.
88 const char kGServiceSettingsDigestKey[] = "gservices_digest";
89 // Key used to indicate how many accounts were last checked in with this device.
90 const char kLastCheckinAccountsKey[] = "last_checkin_accounts_count";
91 // Key used to timestamp last checkin (marked with G services settings update).
92 const char kLastCheckinTimeKey[] = "last_checkin_time";
93 // Lowest lexicographically ordered account key.
94 // Used for prefixing account information.
95 const char kAccountKeyStart[] = "account1-";
96 // Key guaranteed to be higher than all account keys.
97 // Used for limiting iteration.
98 const char kAccountKeyEnd[] = "account2-";
99 // Key used for last token fetch time.
100 const char kLastTokenFetchTimeKey[] = "last_token_fetch_time";
102 std::string MakeRegistrationKey(const std::string& app_id) {
103 return kRegistrationKeyStart + app_id;
106 std::string ParseRegistrationKey(const std::string& key) {
107 return key.substr(arraysize(kRegistrationKeyStart) - 1);
110 std::string MakeIncomingKey(const std::string& persistent_id) {
111 return kIncomingMsgKeyStart + persistent_id;
114 std::string MakeOutgoingKey(const std::string& persistent_id) {
115 return kOutgoingMsgKeyStart + persistent_id;
118 std::string ParseOutgoingKey(const std::string& key) {
119 return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
122 std::string MakeGServiceSettingKey(const std::string& setting_name) {
123 return kGServiceSettingKeyStart + setting_name;
126 std::string ParseGServiceSettingKey(const std::string& key) {
127 return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
130 std::string MakeAccountKey(const std::string& account_id) {
131 return kAccountKeyStart + account_id;
134 std::string ParseAccountKey(const std::string& key) {
135 return key.substr(arraysize(kAccountKeyStart) - 1);
138 // Note: leveldb::Slice keeps a pointer to the data in |s|, which must therefore
139 // outlive the slice.
140 // For example: MakeSlice(MakeOutgoingKey(x)) is invalid.
141 leveldb::Slice MakeSlice(const base::StringPiece& s) {
142 return leveldb::Slice(s.begin(), s.size());
145 } // namespace
147 class GCMStoreImpl::Backend
148 : public base::RefCountedThreadSafe<GCMStoreImpl::Backend> {
149 public:
150 Backend(const base::FilePath& path,
151 scoped_refptr<base::SequencedTaskRunner> foreground_runner,
152 scoped_ptr<Encryptor> encryptor);
154 // Blocking implementations of GCMStoreImpl methods.
155 void Load(const LoadCallback& callback);
156 void Close();
157 void Destroy(const UpdateCallback& callback);
158 void SetDeviceCredentials(uint64 device_android_id,
159 uint64 device_security_token,
160 const UpdateCallback& callback);
161 void AddRegistration(const std::string& app_id,
162 const std::string& serialized_registration,
163 const UpdateCallback& callback);
164 void RemoveRegistration(const std::string& app_id,
165 const UpdateCallback& callback);
166 void AddIncomingMessage(const std::string& persistent_id,
167 const UpdateCallback& callback);
168 void RemoveIncomingMessages(const PersistentIdList& persistent_ids,
169 const UpdateCallback& callback);
170 void AddOutgoingMessage(const std::string& persistent_id,
171 const MCSMessage& message,
172 const UpdateCallback& callback);
173 void RemoveOutgoingMessages(
174 const PersistentIdList& persistent_ids,
175 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
176 callback);
177 void AddUserSerialNumber(const std::string& username,
178 int64 serial_number,
179 const UpdateCallback& callback);
180 void RemoveUserSerialNumber(const std::string& username,
181 const UpdateCallback& callback);
182 void SetLastCheckinInfo(const base::Time& time,
183 const std::set<std::string>& accounts,
184 const UpdateCallback& callback);
185 void SetGServicesSettings(
186 const std::map<std::string, std::string>& settings,
187 const std::string& digest,
188 const UpdateCallback& callback);
189 void AddAccountMapping(const AccountMapping& account_mapping,
190 const UpdateCallback& callback);
191 void RemoveAccountMapping(const std::string& account_id,
192 const UpdateCallback& callback);
193 void SetLastTokenFetchTime(const base::Time& time,
194 const UpdateCallback& callback);
195 void SetValue(const std::string& key,
196 const std::string& value,
197 const UpdateCallback& callback);
199 private:
200 friend class base::RefCountedThreadSafe<Backend>;
201 ~Backend();
203 LoadStatus OpenStoreAndLoadData(LoadResult* result);
204 bool LoadDeviceCredentials(uint64* android_id, uint64* security_token);
205 bool LoadRegistrations(RegistrationInfoMap* registrations);
206 bool LoadIncomingMessages(std::vector<std::string>* incoming_messages);
207 bool LoadOutgoingMessages(OutgoingMessageMap* outgoing_messages);
208 bool LoadLastCheckinInfo(base::Time* last_checkin_time,
209 std::set<std::string>* accounts);
210 bool LoadGServicesSettings(std::map<std::string, std::string>* settings,
211 std::string* digest);
212 bool LoadAccountMappingInfo(AccountMappings* account_mappings);
213 bool LoadLastTokenFetchTime(base::Time* last_token_fetch_time);
215 const base::FilePath path_;
216 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
217 scoped_ptr<Encryptor> encryptor_;
219 scoped_ptr<leveldb::DB> db_;
222 GCMStoreImpl::Backend::Backend(
223 const base::FilePath& path,
224 scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,
225 scoped_ptr<Encryptor> encryptor)
226 : path_(path),
227 foreground_task_runner_(foreground_task_runner),
228 encryptor_(encryptor.Pass()) {
231 GCMStoreImpl::Backend::~Backend() {}
233 LoadStatus GCMStoreImpl::Backend::OpenStoreAndLoadData(LoadResult* result) {
234 LoadStatus load_status;
235 if (db_.get()) {
236 LOG(ERROR) << "Attempting to reload open database.";
237 return RELOADING_OPEN_STORE;
240 leveldb::Options options;
241 options.create_if_missing = true;
242 leveldb::DB* db;
243 leveldb::Status status =
244 leveldb::DB::Open(options, path_.AsUTF8Unsafe(), &db);
245 if (!status.ok()) {
246 LOG(ERROR) << "Failed to open database " << path_.value() << ": "
247 << status.ToString();
248 return OPENING_STORE_FAILED;
251 db_.reset(db);
252 if (!LoadDeviceCredentials(&result->device_android_id,
253 &result->device_security_token)) {
254 return LOADING_DEVICE_CREDENTIALS_FAILED;
256 if (!LoadRegistrations(&result->registrations))
257 return LOADING_REGISTRATION_FAILED;
258 if (!LoadIncomingMessages(&result->incoming_messages))
259 return LOADING_INCOMING_MESSAGES_FAILED;
260 if (!LoadOutgoingMessages(&result->outgoing_messages))
261 return LOADING_OUTGOING_MESSAGES_FAILED;
262 if (!LoadLastCheckinInfo(&result->last_checkin_time,
263 &result->last_checkin_accounts)) {
264 return LOADING_LAST_CHECKIN_INFO_FAILED;
266 if (!LoadGServicesSettings(&result->gservices_settings,
267 &result->gservices_digest)) {
268 return load_status = LOADING_GSERVICE_SETTINGS_FAILED;
270 if (!LoadAccountMappingInfo(&result->account_mappings))
271 return LOADING_ACCOUNT_MAPPING_FAILED;
272 if (!LoadLastTokenFetchTime(&result->last_token_fetch_time))
273 return LOADING_LAST_TOKEN_TIME_FAILED;
275 return LOADING_SUCCEEDED;
278 void GCMStoreImpl::Backend::Load(const LoadCallback& callback) {
279 scoped_ptr<LoadResult> result(new LoadResult());
280 LoadStatus load_status = OpenStoreAndLoadData(result.get());
281 UMA_HISTOGRAM_ENUMERATION("GCM.LoadStatus", load_status, LOAD_STATUS_COUNT);
282 if (load_status != LOADING_SUCCEEDED) {
283 result->Reset();
284 foreground_task_runner_->PostTask(FROM_HERE,
285 base::Bind(callback,
286 base::Passed(&result)));
287 return;
290 // Only record histograms if GCM had already been set up for this device.
291 if (result->device_android_id != 0 && result->device_security_token != 0) {
292 int64 file_size = 0;
293 if (base::GetFileSize(path_, &file_size)) {
294 UMA_HISTOGRAM_COUNTS("GCM.StoreSizeKB",
295 static_cast<int>(file_size / 1024));
297 UMA_HISTOGRAM_COUNTS("GCM.RestoredRegistrations",
298 result->registrations.size());
299 UMA_HISTOGRAM_COUNTS("GCM.RestoredOutgoingMessages",
300 result->outgoing_messages.size());
301 UMA_HISTOGRAM_COUNTS("GCM.RestoredIncomingMessages",
302 result->incoming_messages.size());
305 DVLOG(1) << "Succeeded in loading " << result->registrations.size()
306 << " registrations, "
307 << result->incoming_messages.size()
308 << " unacknowledged incoming messages and "
309 << result->outgoing_messages.size()
310 << " unacknowledged outgoing messages.";
311 result->success = true;
312 foreground_task_runner_->PostTask(FROM_HERE,
313 base::Bind(callback,
314 base::Passed(&result)));
315 return;
318 void GCMStoreImpl::Backend::Close() {
319 DVLOG(1) << "Closing GCM store.";
320 db_.reset();
323 void GCMStoreImpl::Backend::Destroy(const UpdateCallback& callback) {
324 DVLOG(1) << "Destroying GCM store.";
325 db_.reset();
326 const leveldb::Status s =
327 leveldb::DestroyDB(path_.AsUTF8Unsafe(), leveldb::Options());
328 if (s.ok()) {
329 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
330 return;
332 LOG(ERROR) << "Destroy failed: " << s.ToString();
333 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
336 void GCMStoreImpl::Backend::SetDeviceCredentials(
337 uint64 device_android_id,
338 uint64 device_security_token,
339 const UpdateCallback& callback) {
340 DVLOG(1) << "Saving device credentials with AID " << device_android_id;
341 if (!db_.get()) {
342 LOG(ERROR) << "GCMStore db doesn't exist.";
343 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
344 return;
347 leveldb::WriteOptions write_options;
348 write_options.sync = true;
350 std::string encrypted_token;
351 encryptor_->EncryptString(base::Uint64ToString(device_security_token),
352 &encrypted_token);
353 std::string android_id_str = base::Uint64ToString(device_android_id);
354 leveldb::Status s =
355 db_->Put(write_options,
356 MakeSlice(kDeviceAIDKey),
357 MakeSlice(android_id_str));
358 if (s.ok()) {
359 s = db_->Put(
360 write_options, MakeSlice(kDeviceTokenKey), MakeSlice(encrypted_token));
362 if (s.ok()) {
363 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
364 return;
366 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
367 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
370 void GCMStoreImpl::Backend::AddRegistration(
371 const std::string& app_id,
372 const std::string& serialized_registration,
373 const UpdateCallback& callback) {
374 DVLOG(1) << "Saving registration info for app: " << app_id;
375 if (!db_.get()) {
376 LOG(ERROR) << "GCMStore db doesn't exist.";
377 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
378 return;
380 leveldb::WriteOptions write_options;
381 write_options.sync = true;
383 std::string key = MakeRegistrationKey(app_id);
384 const leveldb::Status status = db_->Put(write_options,
385 MakeSlice(key),
386 MakeSlice(serialized_registration));
387 if (status.ok()) {
388 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
389 return;
391 LOG(ERROR) << "LevelDB put failed: " << status.ToString();
392 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
395 void GCMStoreImpl::Backend::RemoveRegistration(const std::string& app_id,
396 const UpdateCallback& callback) {
397 if (!db_.get()) {
398 LOG(ERROR) << "GCMStore db doesn't exist.";
399 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
400 return;
402 leveldb::WriteOptions write_options;
403 write_options.sync = true;
405 leveldb::Status status =
406 db_->Delete(write_options, MakeSlice(MakeRegistrationKey(app_id)));
407 if (status.ok()) {
408 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
409 return;
411 LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
412 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
415 void GCMStoreImpl::Backend::AddIncomingMessage(const std::string& persistent_id,
416 const UpdateCallback& callback) {
417 DVLOG(1) << "Saving incoming message with id " << persistent_id;
418 if (!db_.get()) {
419 LOG(ERROR) << "GCMStore db doesn't exist.";
420 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
421 return;
424 leveldb::WriteOptions write_options;
425 write_options.sync = true;
427 std::string key = MakeIncomingKey(persistent_id);
428 const leveldb::Status s = db_->Put(write_options,
429 MakeSlice(key),
430 MakeSlice(persistent_id));
431 if (s.ok()) {
432 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
433 return;
435 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
436 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
439 void GCMStoreImpl::Backend::RemoveIncomingMessages(
440 const PersistentIdList& persistent_ids,
441 const UpdateCallback& callback) {
442 if (!db_.get()) {
443 LOG(ERROR) << "GCMStore db doesn't exist.";
444 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
445 return;
447 leveldb::WriteOptions write_options;
448 write_options.sync = true;
450 leveldb::Status s;
451 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
452 iter != persistent_ids.end();
453 ++iter) {
454 DVLOG(1) << "Removing incoming message with id " << *iter;
455 std::string key = MakeIncomingKey(*iter);
456 s = db_->Delete(write_options, MakeSlice(key));
457 if (!s.ok())
458 break;
460 if (s.ok()) {
461 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
462 return;
464 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
465 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
468 void GCMStoreImpl::Backend::AddOutgoingMessage(const std::string& persistent_id,
469 const MCSMessage& message,
470 const UpdateCallback& callback) {
471 DVLOG(1) << "Saving outgoing message with id " << persistent_id;
472 if (!db_.get()) {
473 LOG(ERROR) << "GCMStore db doesn't exist.";
474 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
475 return;
477 leveldb::WriteOptions write_options;
478 write_options.sync = true;
480 std::string data =
481 static_cast<char>(message.tag()) + message.SerializeAsString();
482 std::string key = MakeOutgoingKey(persistent_id);
483 const leveldb::Status s = db_->Put(write_options,
484 MakeSlice(key),
485 MakeSlice(data));
486 if (s.ok()) {
487 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
488 return;
490 LOG(ERROR) << "LevelDB put failed: " << s.ToString();
491 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
494 void GCMStoreImpl::Backend::RemoveOutgoingMessages(
495 const PersistentIdList& persistent_ids,
496 const base::Callback<void(bool, const AppIdToMessageCountMap&)>
497 callback) {
498 if (!db_.get()) {
499 LOG(ERROR) << "GCMStore db doesn't exist.";
500 foreground_task_runner_->PostTask(FROM_HERE,
501 base::Bind(callback,
502 false,
503 AppIdToMessageCountMap()));
504 return;
506 leveldb::ReadOptions read_options;
507 leveldb::WriteOptions write_options;
508 write_options.sync = true;
510 AppIdToMessageCountMap removed_message_counts;
512 leveldb::Status s;
513 for (PersistentIdList::const_iterator iter = persistent_ids.begin();
514 iter != persistent_ids.end();
515 ++iter) {
516 DVLOG(1) << "Removing outgoing message with id " << *iter;
517 std::string outgoing_message;
518 std::string key = MakeOutgoingKey(*iter);
519 s = db_->Get(read_options,
520 MakeSlice(key),
521 &outgoing_message);
522 if (!s.ok())
523 break;
524 mcs_proto::DataMessageStanza data_message;
525 // Skip the initial tag byte and parse the rest to extract the message.
526 if (data_message.ParseFromString(outgoing_message.substr(1))) {
527 DCHECK(!data_message.category().empty());
528 if (removed_message_counts.count(data_message.category()) != 0)
529 removed_message_counts[data_message.category()]++;
530 else
531 removed_message_counts[data_message.category()] = 1;
533 DVLOG(1) << "Removing outgoing message with id " << *iter;
534 s = db_->Delete(write_options, MakeSlice(key));
535 if (!s.ok())
536 break;
538 if (s.ok()) {
539 foreground_task_runner_->PostTask(FROM_HERE,
540 base::Bind(callback,
541 true,
542 removed_message_counts));
543 return;
545 LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
546 foreground_task_runner_->PostTask(FROM_HERE,
547 base::Bind(callback,
548 false,
549 AppIdToMessageCountMap()));
552 void GCMStoreImpl::Backend::SetLastCheckinInfo(
553 const base::Time& time,
554 const std::set<std::string>& accounts,
555 const UpdateCallback& callback) {
556 leveldb::WriteBatch write_batch;
558 int64 last_checkin_time_internal = time.ToInternalValue();
559 write_batch.Put(MakeSlice(kLastCheckinTimeKey),
560 MakeSlice(base::Int64ToString(last_checkin_time_internal)));
562 std::string serialized_accounts;
563 for (std::set<std::string>::iterator iter = accounts.begin();
564 iter != accounts.end();
565 ++iter) {
566 serialized_accounts += *iter;
567 serialized_accounts += ",";
569 if (!serialized_accounts.empty())
570 serialized_accounts.erase(serialized_accounts.length() - 1);
572 write_batch.Put(MakeSlice(kLastCheckinAccountsKey),
573 MakeSlice(serialized_accounts));
575 leveldb::WriteOptions write_options;
576 write_options.sync = true;
577 const leveldb::Status s = db_->Write(write_options, &write_batch);
579 if (!s.ok())
580 LOG(ERROR) << "LevelDB set last checkin info failed: " << s.ToString();
581 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
584 void GCMStoreImpl::Backend::SetGServicesSettings(
585 const std::map<std::string, std::string>& settings,
586 const std::string& settings_digest,
587 const UpdateCallback& callback) {
588 leveldb::WriteBatch write_batch;
590 // Remove all existing settings.
591 leveldb::ReadOptions read_options;
592 read_options.verify_checksums = true;
593 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
594 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
595 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
596 iter->Next()) {
597 write_batch.Delete(iter->key());
600 // Add the new settings.
601 for (std::map<std::string, std::string>::const_iterator iter =
602 settings.begin();
603 iter != settings.end(); ++iter) {
604 write_batch.Put(MakeSlice(MakeGServiceSettingKey(iter->first)),
605 MakeSlice(iter->second));
608 // Update the settings digest.
609 write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
610 MakeSlice(settings_digest));
612 // Write it all in a batch.
613 leveldb::WriteOptions write_options;
614 write_options.sync = true;
616 leveldb::Status s = db_->Write(write_options, &write_batch);
617 if (!s.ok())
618 LOG(ERROR) << "LevelDB GService Settings update failed: " << s.ToString();
619 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
622 void GCMStoreImpl::Backend::AddAccountMapping(
623 const AccountMapping& account_mapping,
624 const UpdateCallback& callback) {
625 DVLOG(1) << "Saving account info for account with email: "
626 << account_mapping.email;
627 if (!db_.get()) {
628 LOG(ERROR) << "GCMStore db doesn't exist.";
629 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
630 return;
633 leveldb::WriteOptions write_options;
634 write_options.sync = true;
636 std::string data = account_mapping.SerializeAsString();
637 std::string key = MakeAccountKey(account_mapping.account_id);
638 const leveldb::Status s =
639 db_->Put(write_options, MakeSlice(key), MakeSlice(data));
640 if (!s.ok())
641 LOG(ERROR) << "LevelDB adding account mapping failed: " << s.ToString();
642 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
645 void GCMStoreImpl::Backend::RemoveAccountMapping(
646 const std::string& account_id,
647 const UpdateCallback& callback) {
648 if (!db_.get()) {
649 LOG(ERROR) << "GCMStore db doesn't exist.";
650 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
651 return;
654 leveldb::WriteOptions write_options;
655 write_options.sync = true;
657 leveldb::Status s =
658 db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
660 if (!s.ok())
661 LOG(ERROR) << "LevelDB removal of account mapping failed: " << s.ToString();
662 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
665 void GCMStoreImpl::Backend::SetLastTokenFetchTime(
666 const base::Time& time,
667 const UpdateCallback& callback) {
668 DVLOG(1) << "Setting last token fetching time.";
669 if (!db_.get()) {
670 LOG(ERROR) << "GCMStore db doesn't exist.";
671 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
672 return;
675 leveldb::WriteOptions write_options;
676 write_options.sync = true;
678 const leveldb::Status s =
679 db_->Put(write_options,
680 MakeSlice(kLastTokenFetchTimeKey),
681 MakeSlice(base::Int64ToString(time.ToInternalValue())));
683 if (!s.ok())
684 LOG(ERROR) << "LevelDB setting last token fetching time: " << s.ToString();
685 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
688 void GCMStoreImpl::Backend::SetValue(const std::string& key,
689 const std::string& value,
690 const UpdateCallback& callback) {
691 DVLOG(1) << "Injecting a value to GCM Store for testing. Key: "
692 << key << ", Value: " << value;
693 if (!db_.get()) {
694 LOG(ERROR) << "GCMStore db doesn't exist.";
695 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
696 return;
699 leveldb::WriteOptions write_options;
700 write_options.sync = true;
702 const leveldb::Status s =
703 db_->Put(write_options, MakeSlice(key), MakeSlice(value));
705 if (!s.ok())
706 LOG(ERROR) << "LevelDB had problems injecting a value: " << s.ToString();
707 foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, s.ok()));
710 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
711 uint64* security_token) {
712 leveldb::ReadOptions read_options;
713 read_options.verify_checksums = true;
715 std::string result;
716 leveldb::Status s = db_->Get(read_options, MakeSlice(kDeviceAIDKey), &result);
717 if (s.ok()) {
718 if (!base::StringToUint64(result, android_id)) {
719 LOG(ERROR) << "Failed to restore device id.";
720 return false;
722 result.clear();
723 s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
725 if (s.ok()) {
726 std::string decrypted_token;
727 encryptor_->DecryptString(result, &decrypted_token);
728 if (!base::StringToUint64(decrypted_token, security_token)) {
729 LOG(ERROR) << "Failed to restore security token.";
730 return false;
732 return true;
735 if (s.IsNotFound()) {
736 DVLOG(1) << "No credentials found.";
737 return true;
740 LOG(ERROR) << "Error reading credentials from store.";
741 return false;
744 bool GCMStoreImpl::Backend::LoadRegistrations(
745 RegistrationInfoMap* registrations) {
746 leveldb::ReadOptions read_options;
747 read_options.verify_checksums = true;
749 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
750 for (iter->Seek(MakeSlice(kRegistrationKeyStart));
751 iter->Valid() && iter->key().ToString() < kRegistrationKeyEnd;
752 iter->Next()) {
753 leveldb::Slice s = iter->value();
754 if (s.size() <= 1) {
755 LOG(ERROR) << "Error reading registration with key " << s.ToString();
756 return false;
758 std::string app_id = ParseRegistrationKey(iter->key().ToString());
759 linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
760 if (!registration->ParseFromString(iter->value().ToString())) {
761 LOG(ERROR) << "Failed to parse registration with app id " << app_id;
762 return false;
764 DVLOG(1) << "Found registration with app id " << app_id;
765 (*registrations)[app_id] = registration;
768 return true;
771 bool GCMStoreImpl::Backend::LoadIncomingMessages(
772 std::vector<std::string>* incoming_messages) {
773 leveldb::ReadOptions read_options;
774 read_options.verify_checksums = true;
776 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
777 for (iter->Seek(MakeSlice(kIncomingMsgKeyStart));
778 iter->Valid() && iter->key().ToString() < kIncomingMsgKeyEnd;
779 iter->Next()) {
780 leveldb::Slice s = iter->value();
781 if (s.empty()) {
782 LOG(ERROR) << "Error reading incoming message with key "
783 << iter->key().ToString();
784 return false;
786 DVLOG(1) << "Found incoming message with id " << s.ToString();
787 incoming_messages->push_back(s.ToString());
790 return true;
793 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
794 OutgoingMessageMap* outgoing_messages) {
795 leveldb::ReadOptions read_options;
796 read_options.verify_checksums = true;
798 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
799 for (iter->Seek(MakeSlice(kOutgoingMsgKeyStart));
800 iter->Valid() && iter->key().ToString() < kOutgoingMsgKeyEnd;
801 iter->Next()) {
802 leveldb::Slice s = iter->value();
803 if (s.size() <= 1) {
804 LOG(ERROR) << "Error reading incoming message with key " << s.ToString();
805 return false;
807 uint8 tag = iter->value().data()[0];
808 std::string id = ParseOutgoingKey(iter->key().ToString());
809 scoped_ptr<google::protobuf::MessageLite> message(
810 BuildProtobufFromTag(tag));
811 if (!message.get() ||
812 !message->ParseFromString(iter->value().ToString().substr(1))) {
813 LOG(ERROR) << "Failed to parse outgoing message with id " << id
814 << " and tag " << tag;
815 return false;
817 DVLOG(1) << "Found outgoing message with id " << id << " of type "
818 << base::IntToString(tag);
819 (*outgoing_messages)[id] = make_linked_ptr(message.release());
822 return true;
825 bool GCMStoreImpl::Backend::LoadLastCheckinInfo(
826 base::Time* last_checkin_time,
827 std::set<std::string>* accounts) {
828 leveldb::ReadOptions read_options;
829 read_options.verify_checksums = true;
831 std::string result;
832 leveldb::Status s = db_->Get(read_options,
833 MakeSlice(kLastCheckinTimeKey),
834 &result);
835 int64 time_internal = 0LL;
836 if (s.ok() && !base::StringToInt64(result, &time_internal)) {
837 LOG(ERROR) << "Failed to restore last checkin time. Using default = 0.";
838 time_internal = 0LL;
841 // In case we cannot read last checkin time, we default it to 0, as we don't
842 // want that situation to cause the whole load to fail.
843 *last_checkin_time = base::Time::FromInternalValue(time_internal);
845 accounts->clear();
846 s = db_->Get(read_options, MakeSlice(kLastCheckinAccountsKey), &result);
847 if (!s.ok())
848 DVLOG(1) << "No accounts where stored during last run.";
850 base::StringTokenizer t(result, ",");
851 while (t.GetNext())
852 accounts->insert(t.token());
854 return true;
857 bool GCMStoreImpl::Backend::LoadGServicesSettings(
858 std::map<std::string, std::string>* settings,
859 std::string* digest) {
860 leveldb::ReadOptions read_options;
861 read_options.verify_checksums = true;
863 // Load all of the GServices settings.
864 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
865 for (iter->Seek(MakeSlice(kGServiceSettingKeyStart));
866 iter->Valid() && iter->key().ToString() < kGServiceSettingKeyEnd;
867 iter->Next()) {
868 std::string value = iter->value().ToString();
869 if (value.empty()) {
870 LOG(ERROR) << "Error reading GService Settings " << value;
871 return false;
873 std::string id = ParseGServiceSettingKey(iter->key().ToString());
874 (*settings)[id] = value;
875 DVLOG(1) << "Found G Service setting with key: " << id
876 << ", and value: " << value;
879 // Load the settings digest. It's ok if it is empty.
880 db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
882 return true;
885 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
886 AccountMappings* account_mappings) {
887 leveldb::ReadOptions read_options;
888 read_options.verify_checksums = true;
890 scoped_ptr<leveldb::Iterator> iter(db_->NewIterator(read_options));
891 for (iter->Seek(MakeSlice(kAccountKeyStart));
892 iter->Valid() && iter->key().ToString() < kAccountKeyEnd;
893 iter->Next()) {
894 AccountMapping account_mapping;
895 account_mapping.account_id = ParseAccountKey(iter->key().ToString());
896 if (!account_mapping.ParseFromString(iter->value().ToString())) {
897 DVLOG(1) << "Failed to parse account info with ID: "
898 << account_mapping.account_id;
899 return false;
901 DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
902 account_mappings->push_back(account_mapping);
905 return true;
908 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
909 base::Time* last_token_fetch_time) {
910 leveldb::ReadOptions read_options;
911 read_options.verify_checksums = true;
913 std::string result;
914 leveldb::Status s =
915 db_->Get(read_options, MakeSlice(kLastTokenFetchTimeKey), &result);
916 int64 time_internal = 0LL;
917 if (s.ok() && !base::StringToInt64(result, &time_internal)) {
918 LOG(ERROR) <<
919 "Failed to restore last token fetching time. Using default = 0.";
920 time_internal = 0LL;
923 // In case we cannot read last token fetching time, we default it to 0.
924 *last_token_fetch_time = base::Time::FromInternalValue(time_internal);
926 return true;
929 GCMStoreImpl::GCMStoreImpl(
930 const base::FilePath& path,
931 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,
932 scoped_ptr<Encryptor> encryptor)
933 : backend_(new Backend(path,
934 base::MessageLoopProxy::current(),
935 encryptor.Pass())),
936 blocking_task_runner_(blocking_task_runner),
937 weak_ptr_factory_(this) {
940 GCMStoreImpl::~GCMStoreImpl() {}
942 void GCMStoreImpl::Load(const LoadCallback& callback) {
943 blocking_task_runner_->PostTask(
944 FROM_HERE,
945 base::Bind(&GCMStoreImpl::Backend::Load,
946 backend_,
947 base::Bind(&GCMStoreImpl::LoadContinuation,
948 weak_ptr_factory_.GetWeakPtr(),
949 callback)));
952 void GCMStoreImpl::Close() {
953 weak_ptr_factory_.InvalidateWeakPtrs();
954 app_message_counts_.clear();
955 blocking_task_runner_->PostTask(
956 FROM_HERE,
957 base::Bind(&GCMStoreImpl::Backend::Close, backend_));
960 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
961 blocking_task_runner_->PostTask(
962 FROM_HERE,
963 base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
966 void GCMStoreImpl::SetDeviceCredentials(uint64 device_android_id,
967 uint64 device_security_token,
968 const UpdateCallback& callback) {
969 blocking_task_runner_->PostTask(
970 FROM_HERE,
971 base::Bind(&GCMStoreImpl::Backend::SetDeviceCredentials,
972 backend_,
973 device_android_id,
974 device_security_token,
975 callback));
978 void GCMStoreImpl::AddRegistration(
979 const std::string& app_id,
980 const linked_ptr<RegistrationInfo>& registration,
981 const UpdateCallback& callback) {
982 std::string serialized_registration = registration->SerializeAsString();
983 blocking_task_runner_->PostTask(
984 FROM_HERE,
985 base::Bind(&GCMStoreImpl::Backend::AddRegistration,
986 backend_,
987 app_id,
988 serialized_registration,
989 callback));
992 void GCMStoreImpl::RemoveRegistration(const std::string& app_id,
993 const UpdateCallback& callback) {
994 blocking_task_runner_->PostTask(
995 FROM_HERE,
996 base::Bind(&GCMStoreImpl::Backend::RemoveRegistration,
997 backend_,
998 app_id,
999 callback));
1002 void GCMStoreImpl::AddIncomingMessage(const std::string& persistent_id,
1003 const UpdateCallback& callback) {
1004 blocking_task_runner_->PostTask(
1005 FROM_HERE,
1006 base::Bind(&GCMStoreImpl::Backend::AddIncomingMessage,
1007 backend_,
1008 persistent_id,
1009 callback));
1012 void GCMStoreImpl::RemoveIncomingMessage(const std::string& persistent_id,
1013 const UpdateCallback& callback) {
1014 blocking_task_runner_->PostTask(
1015 FROM_HERE,
1016 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
1017 backend_,
1018 PersistentIdList(1, persistent_id),
1019 callback));
1022 void GCMStoreImpl::RemoveIncomingMessages(
1023 const PersistentIdList& persistent_ids,
1024 const UpdateCallback& callback) {
1025 blocking_task_runner_->PostTask(
1026 FROM_HERE,
1027 base::Bind(&GCMStoreImpl::Backend::RemoveIncomingMessages,
1028 backend_,
1029 persistent_ids,
1030 callback));
1033 bool GCMStoreImpl::AddOutgoingMessage(const std::string& persistent_id,
1034 const MCSMessage& message,
1035 const UpdateCallback& callback) {
1036 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
1037 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
1038 &message.GetProtobuf())->category();
1039 DCHECK(!app_id.empty());
1040 if (app_message_counts_.count(app_id) == 0)
1041 app_message_counts_[app_id] = 0;
1042 if (app_message_counts_[app_id] < kMessagesPerAppLimit) {
1043 app_message_counts_[app_id]++;
1045 blocking_task_runner_->PostTask(
1046 FROM_HERE,
1047 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1048 backend_,
1049 persistent_id,
1050 message,
1051 base::Bind(&GCMStoreImpl::AddOutgoingMessageContinuation,
1052 weak_ptr_factory_.GetWeakPtr(),
1053 callback,
1054 app_id)));
1055 return true;
1057 return false;
1060 void GCMStoreImpl::OverwriteOutgoingMessage(const std::string& persistent_id,
1061 const MCSMessage& message,
1062 const UpdateCallback& callback) {
1063 DCHECK_EQ(message.tag(), kDataMessageStanzaTag);
1064 std::string app_id = reinterpret_cast<const mcs_proto::DataMessageStanza*>(
1065 &message.GetProtobuf())->category();
1066 DCHECK(!app_id.empty());
1067 // There should already be pending messages for this app.
1068 DCHECK(app_message_counts_.count(app_id));
1069 // TODO(zea): consider verifying the specific message already exists.
1070 blocking_task_runner_->PostTask(
1071 FROM_HERE,
1072 base::Bind(&GCMStoreImpl::Backend::AddOutgoingMessage,
1073 backend_,
1074 persistent_id,
1075 message,
1076 callback));
1079 void GCMStoreImpl::RemoveOutgoingMessage(const std::string& persistent_id,
1080 const UpdateCallback& callback) {
1081 blocking_task_runner_->PostTask(
1082 FROM_HERE,
1083 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1084 backend_,
1085 PersistentIdList(1, persistent_id),
1086 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1087 weak_ptr_factory_.GetWeakPtr(),
1088 callback)));
1091 void GCMStoreImpl::RemoveOutgoingMessages(
1092 const PersistentIdList& persistent_ids,
1093 const UpdateCallback& callback) {
1094 blocking_task_runner_->PostTask(
1095 FROM_HERE,
1096 base::Bind(&GCMStoreImpl::Backend::RemoveOutgoingMessages,
1097 backend_,
1098 persistent_ids,
1099 base::Bind(&GCMStoreImpl::RemoveOutgoingMessagesContinuation,
1100 weak_ptr_factory_.GetWeakPtr(),
1101 callback)));
1104 void GCMStoreImpl::SetLastCheckinInfo(const base::Time& time,
1105 const std::set<std::string>& accounts,
1106 const UpdateCallback& callback) {
1107 blocking_task_runner_->PostTask(
1108 FROM_HERE,
1109 base::Bind(&GCMStoreImpl::Backend::SetLastCheckinInfo,
1110 backend_,
1111 time,
1112 accounts,
1113 callback));
1116 void GCMStoreImpl::SetGServicesSettings(
1117 const std::map<std::string, std::string>& settings,
1118 const std::string& digest,
1119 const UpdateCallback& callback) {
1120 blocking_task_runner_->PostTask(
1121 FROM_HERE,
1122 base::Bind(&GCMStoreImpl::Backend::SetGServicesSettings,
1123 backend_,
1124 settings,
1125 digest,
1126 callback));
1129 void GCMStoreImpl::AddAccountMapping(const AccountMapping& account_mapping,
1130 const UpdateCallback& callback) {
1131 blocking_task_runner_->PostTask(
1132 FROM_HERE,
1133 base::Bind(&GCMStoreImpl::Backend::AddAccountMapping,
1134 backend_,
1135 account_mapping,
1136 callback));
1139 void GCMStoreImpl::RemoveAccountMapping(const std::string& account_id,
1140 const UpdateCallback& callback) {
1141 blocking_task_runner_->PostTask(
1142 FROM_HERE,
1143 base::Bind(&GCMStoreImpl::Backend::RemoveAccountMapping,
1144 backend_,
1145 account_id,
1146 callback));
1149 void GCMStoreImpl::SetLastTokenFetchTime(const base::Time& time,
1150 const UpdateCallback& callback) {
1151 blocking_task_runner_->PostTask(
1152 FROM_HERE,
1153 base::Bind(&GCMStoreImpl::Backend::SetLastTokenFetchTime,
1154 backend_,
1155 time,
1156 callback));
1159 void GCMStoreImpl::SetValueForTesting(const std::string& key,
1160 const std::string& value,
1161 const UpdateCallback& callback) {
1162 blocking_task_runner_->PostTask(
1163 FROM_HERE,
1164 base::Bind(&GCMStoreImpl::Backend::SetValue,
1165 backend_,
1166 key,
1167 value,
1168 callback));
1171 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
1172 scoped_ptr<LoadResult> result) {
1173 if (!result->success) {
1174 callback.Run(result.Pass());
1175 return;
1177 int num_throttled_apps = 0;
1178 for (OutgoingMessageMap::const_iterator
1179 iter = result->outgoing_messages.begin();
1180 iter != result->outgoing_messages.end(); ++iter) {
1181 const mcs_proto::DataMessageStanza* data_message =
1182 reinterpret_cast<mcs_proto::DataMessageStanza*>(iter->second.get());
1183 DCHECK(!data_message->category().empty());
1184 if (app_message_counts_.count(data_message->category()) == 0)
1185 app_message_counts_[data_message->category()] = 1;
1186 else
1187 app_message_counts_[data_message->category()]++;
1188 if (app_message_counts_[data_message->category()] == kMessagesPerAppLimit)
1189 num_throttled_apps++;
1191 UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
1192 callback.Run(result.Pass());
1195 void GCMStoreImpl::AddOutgoingMessageContinuation(
1196 const UpdateCallback& callback,
1197 const std::string& app_id,
1198 bool success) {
1199 if (!success) {
1200 DCHECK(app_message_counts_[app_id] > 0);
1201 app_message_counts_[app_id]--;
1203 callback.Run(success);
1206 void GCMStoreImpl::RemoveOutgoingMessagesContinuation(
1207 const UpdateCallback& callback,
1208 bool success,
1209 const AppIdToMessageCountMap& removed_message_counts) {
1210 if (!success) {
1211 callback.Run(false);
1212 return;
1214 for (AppIdToMessageCountMap::const_iterator iter =
1215 removed_message_counts.begin();
1216 iter != removed_message_counts.end(); ++iter) {
1217 DCHECK_NE(app_message_counts_.count(iter->first), 0U);
1218 app_message_counts_[iter->first] -= iter->second;
1219 DCHECK_GE(app_message_counts_[iter->first], 0);
1221 callback.Run(true);
1224 } // namespace gcm