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