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