Home | History | Annotate | Download | only in engine
      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
   1090