Upstream version 11.40.277.0
[platform/framework/web/crosswalk.git] / src / google_apis / gcm / engine / gcm_store_impl.cc
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.
4
5 #include "google_apis/gcm/engine/gcm_store_impl.h"
6
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"
28
29 namespace gcm {
30
31 namespace {
32
33 // Limit to the number of outstanding messages per app.
34 const int kMessagesPerAppLimit = 20;
35
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";
79
80 std::string MakeRegistrationKey(const std::string& app_id) {
81   return kRegistrationKeyStart + app_id;
82 }
83
84 std::string ParseRegistrationKey(const std::string& key) {
85   return key.substr(arraysize(kRegistrationKeyStart) - 1);
86 }
87
88 std::string MakeIncomingKey(const std::string& persistent_id) {
89   return kIncomingMsgKeyStart + persistent_id;
90 }
91
92 std::string MakeOutgoingKey(const std::string& persistent_id) {
93   return kOutgoingMsgKeyStart + persistent_id;
94 }
95
96 std::string ParseOutgoingKey(const std::string& key) {
97   return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
98 }
99
100 std::string MakeGServiceSettingKey(const std::string& setting_name) {
101   return kGServiceSettingKeyStart + setting_name;
102 }
103
104 std::string ParseGServiceSettingKey(const std::string& key) {
105   return key.substr(arraysize(kGServiceSettingKeyStart) - 1);
106 }
107
108 std::string MakeAccountKey(const std::string& account_id) {
109   return kAccountKeyStart + account_id;
110 }
111
112 std::string ParseAccountKey(const std::string& key) {
113   return key.substr(arraysize(kAccountKeyStart) - 1);
114 }
115
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());
121 }
122
123 }  // namespace
124
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);
131
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);
173
174  private:
175   friend class base::RefCountedThreadSafe<Backend>;
176   ~Backend();
177
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);
188
189   const base::FilePath path_;
190   scoped_refptr<base::SequencedTaskRunner> foreground_task_runner_;
191   scoped_ptr<Encryptor> encryptor_;
192
193   scoped_ptr<leveldb::DB> db_;
194 };
195
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()) {
203 }
204
205 GCMStoreImpl::Backend::~Backend() {}
206
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;
215   }
216
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;
230   }
231   db_.reset(db);
232
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;
249   }
250
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));
257     }
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());
264   }
265
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;
277 }
278
279 void GCMStoreImpl::Backend::Close() {
280   DVLOG(1) << "Closing GCM store.";
281   db_.reset();
282 }
283
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;
292   }
293   LOG(ERROR) << "Destroy failed: " << s.ToString();
294   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
295 }
296
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;
306   }
307
308   leveldb::WriteOptions write_options;
309   write_options.sync = true;
310
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));
322   }
323   if (s.ok()) {
324     foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
325     return;
326   }
327   LOG(ERROR) << "LevelDB put failed: " << s.ToString();
328   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
329 }
330
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;
340   }
341   leveldb::WriteOptions write_options;
342   write_options.sync = true;
343
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;
352   }
353   LOG(ERROR) << "LevelDB put failed: " << status.ToString();
354   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
355 }
356
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;
363   }
364   leveldb::WriteOptions write_options;
365   write_options.sync = true;
366
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;
372   }
373   LOG(ERROR) << "LevelDB remove failed: " << status.ToString();
374   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
375 }
376
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;
384   }
385
386   leveldb::WriteOptions write_options;
387   write_options.sync = true;
388
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;
396   }
397   LOG(ERROR) << "LevelDB put failed: " << s.ToString();
398   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
399 }
400
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;
408   }
409   leveldb::WriteOptions write_options;
410   write_options.sync = true;
411
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;
421   }
422   if (s.ok()) {
423     foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, true));
424     return;
425   }
426   LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
427   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
428 }
429
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;
438   }
439   leveldb::WriteOptions write_options;
440   write_options.sync = true;
441
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;
451   }
452   LOG(ERROR) << "LevelDB put failed: " << s.ToString();
453   foreground_task_runner_->PostTask(FROM_HERE, base::Bind(callback, false));
454 }
455
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;
467   }
468   leveldb::ReadOptions read_options;
469   leveldb::WriteOptions write_options;
470   write_options.sync = true;
471
472   AppIdToMessageCountMap removed_message_counts;
473
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;
494     }
495     DVLOG(1) << "Removing outgoing message with id " << *iter;
496     s = db_->Delete(write_options, MakeSlice(key));
497     if (!s.ok())
498       break;
499   }
500   if (s.ok()) {
501     foreground_task_runner_->PostTask(FROM_HERE,
502                                       base::Bind(callback,
503                                                  true,
504                                                  removed_message_counts));
505     return;
506   }
507   LOG(ERROR) << "LevelDB remove failed: " << s.ToString();
508   foreground_task_runner_->PostTask(FROM_HERE,
509                                     base::Bind(callback,
510                                                false,
511                                                AppIdToMessageCountMap()));
512 }
513
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;
519
520   int64 last_checkin_time_internal = time.ToInternalValue();
521   write_batch.Put(MakeSlice(kLastCheckinTimeKey),
522                   MakeSlice(base::Int64ToString(last_checkin_time_internal)));
523
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 += ",";
530   }
531   if (!serialized_accounts.empty())
532     serialized_accounts.erase(serialized_accounts.length() - 1);
533
534   write_batch.Put(MakeSlice(kLastCheckinAccountsKey),
535                   MakeSlice(serialized_accounts));
536
537   leveldb::WriteOptions write_options;
538   write_options.sync = true;
539   const leveldb::Status s = db_->Write(write_options, &write_batch);
540
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()));
544 }
545
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;
551
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());
560   }
561
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));
568   }
569
570   // Update the settings digest.
571   write_batch.Put(MakeSlice(kGServiceSettingsDigestKey),
572                   MakeSlice(settings_digest));
573
574   // Write it all in a batch.
575   leveldb::WriteOptions write_options;
576   write_options.sync = true;
577
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()));
582 }
583
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;
593   }
594
595   leveldb::WriteOptions write_options;
596   write_options.sync = true;
597
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()));
605 }
606
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;
614   }
615
616   leveldb::WriteOptions write_options;
617   write_options.sync = true;
618
619   leveldb::Status s =
620       db_->Delete(write_options, MakeSlice(MakeAccountKey(account_id)));
621
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()));
625 }
626
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;
635   }
636
637   leveldb::WriteOptions write_options;
638   write_options.sync = true;
639
640   const leveldb::Status s =
641       db_->Put(write_options,
642                MakeSlice(kLastTokenFetchTimeKey),
643                MakeSlice(base::Int64ToString(time.ToInternalValue())));
644
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()));
648 }
649
650 bool GCMStoreImpl::Backend::LoadDeviceCredentials(uint64* android_id,
651                                                   uint64* security_token) {
652   leveldb::ReadOptions read_options;
653   read_options.verify_checksums = true;
654
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;
661     }
662     result.clear();
663     s = db_->Get(read_options, MakeSlice(kDeviceTokenKey), &result);
664   }
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;
671     }
672     return true;
673   }
674
675   if (s.IsNotFound()) {
676     DVLOG(1) << "No credentials found.";
677     return true;
678   }
679
680   LOG(ERROR) << "Error reading credentials from store.";
681   return false;
682 }
683
684 bool GCMStoreImpl::Backend::LoadRegistrations(
685     RegistrationInfoMap* registrations) {
686   leveldb::ReadOptions read_options;
687   read_options.verify_checksums = true;
688
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;
697     }
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;
703     }
704     DVLOG(1) << "Found registration with app id " << app_id;
705     (*registrations)[app_id] = registration;
706   }
707
708   return true;
709 }
710
711 bool GCMStoreImpl::Backend::LoadIncomingMessages(
712     std::vector<std::string>* incoming_messages) {
713   leveldb::ReadOptions read_options;
714   read_options.verify_checksums = true;
715
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;
725     }
726     DVLOG(1) << "Found incoming message with id " << s.ToString();
727     incoming_messages->push_back(s.ToString());
728   }
729
730   return true;
731 }
732
733 bool GCMStoreImpl::Backend::LoadOutgoingMessages(
734     OutgoingMessageMap* outgoing_messages) {
735   leveldb::ReadOptions read_options;
736   read_options.verify_checksums = true;
737
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;
746     }
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;
756     }
757     DVLOG(1) << "Found outgoing message with id " << id << " of type "
758              << base::IntToString(tag);
759     (*outgoing_messages)[id] = make_linked_ptr(message.release());
760   }
761
762   return true;
763 }
764
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;
770
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.";
778
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);
782
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.";
787
788   base::StringTokenizer t(result, ",");
789   while (t.GetNext())
790     accounts->insert(t.token());
791
792   return true;
793 }
794
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;
800
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;
810     }
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;
815   }
816
817   // Load the settings digest. It's ok if it is empty.
818   db_->Get(read_options, MakeSlice(kGServiceSettingsDigestKey), digest);
819
820   return true;
821 }
822
823 bool GCMStoreImpl::Backend::LoadAccountMappingInfo(
824     AccountMappings* account_mappings) {
825   leveldb::ReadOptions read_options;
826   read_options.verify_checksums = true;
827
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;
838     }
839     DVLOG(1) << "Found account mapping with ID: " << account_mapping.account_id;
840     account_mappings->push_back(account_mapping);
841   }
842
843   return true;
844 }
845
846 bool GCMStoreImpl::Backend::LoadLastTokenFetchTime(
847     base::Time* last_token_fetch_time) {
848   leveldb::ReadOptions read_options;
849   read_options.verify_checksums = true;
850
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.";
857
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);
860
861   return true;
862 }
863
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) {
873 }
874
875 GCMStoreImpl::~GCMStoreImpl() {}
876
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)));
885 }
886
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_));
893 }
894
895 void GCMStoreImpl::Destroy(const UpdateCallback& callback) {
896   blocking_task_runner_->PostTask(
897       FROM_HERE,
898       base::Bind(&GCMStoreImpl::Backend::Destroy, backend_, callback));
899 }
900
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));
911 }
912
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));
924 }
925
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));
934 }
935
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));
944 }
945
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));
954 }
955
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));
965 }
966
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]++;
978
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;
990   }
991   return false;
992 }
993
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));
1011 }
1012
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)));
1023 }
1024
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)));
1036 }
1037
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));
1048 }
1049
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));
1061 }
1062
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));
1071 }
1072
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));
1081 }
1082
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));
1091 }
1092
1093 void GCMStoreImpl::LoadContinuation(const LoadCallback& callback,
1094                                     scoped_ptr<LoadResult> result) {
1095   if (!result->success) {
1096     callback.Run(result.Pass());
1097     return;
1098   }
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++;
1112   }
1113   UMA_HISTOGRAM_COUNTS("GCM.NumThrottledApps", num_throttled_apps);
1114   callback.Run(result.Pass());
1115 }
1116
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]--;
1124   }
1125   callback.Run(success);
1126 }
1127
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;
1135   }
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);
1142   }
1143   callback.Run(true);
1144 }
1145
1146 }  // namespace gcm