• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MakeRegistrationKey(const std::string & app_id)69 std::string MakeRegistrationKey(const std::string& app_id) {
70   return kRegistrationKeyStart + app_id;
71 }
72 
ParseRegistrationKey(const std::string & key)73 std::string ParseRegistrationKey(const std::string& key) {
74   return key.substr(arraysize(kRegistrationKeyStart) - 1);
75 }
76 
MakeIncomingKey(const std::string & persistent_id)77 std::string MakeIncomingKey(const std::string& persistent_id) {
78   return kIncomingMsgKeyStart + persistent_id;
79 }
80 
MakeOutgoingKey(const std::string & persistent_id)81 std::string MakeOutgoingKey(const std::string& persistent_id) {
82   return kOutgoingMsgKeyStart + persistent_id;
83 }
84 
ParseOutgoingKey(const std::string & key)85 std::string ParseOutgoingKey(const std::string& key) {
86   return key.substr(arraysize(kOutgoingMsgKeyStart) - 1);
87 }
88 
MakeGServiceSettingKey(const std::string & setting_name)89 std::string MakeGServiceSettingKey(const std::string& setting_name) {
90   return kGServiceSettingKeyStart + setting_name;
91 }
92 
ParseGServiceSettingKey(const std::string & key)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.
MakeSlice(const base::StringPiece & s)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 
Backend(const base::FilePath & path,scoped_refptr<base::SequencedTaskRunner> foreground_task_runner,scoped_ptr<Encryptor> encryptor)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 
~Backend()176 GCMStoreImpl::Backend::~Backend() {}
177 
Load(const LoadCallback & callback)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 
Close()254 void GCMStoreImpl::Backend::Close() {
255   DVLOG(1) << "Closing GCM store.";
256   db_.reset();
257 }
258 
Destroy(const UpdateCallback & callback)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 
SetDeviceCredentials(uint64 device_android_id,uint64 device_security_token,const UpdateCallback & callback)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 
AddRegistration(const std::string & app_id,const linked_ptr<RegistrationInfo> & registration,const UpdateCallback & callback)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 
RemoveRegistration(const std::string & app_id,const UpdateCallback & callback)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 
AddIncomingMessage(const std::string & persistent_id,const UpdateCallback & callback)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 
RemoveIncomingMessages(const PersistentIdList & persistent_ids,const UpdateCallback & callback)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 
AddOutgoingMessage(const std::string & persistent_id,const MCSMessage & message,const UpdateCallback & callback)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 
RemoveOutgoingMessages(const PersistentIdList & persistent_ids,const base::Callback<void (bool,const AppIdToMessageCountMap &)> callback)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 
SetLastCheckinTime(const base::Time & last_checkin_time,const UpdateCallback & callback)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 
SetGServicesSettings(const std::map<std::string,std::string> & settings,const std::string & settings_digest,const UpdateCallback & callback)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 
LoadDeviceCredentials(uint64 * android_id,uint64 * security_token)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 
LoadRegistrations(RegistrationInfoMap * registrations)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 
LoadIncomingMessages(std::vector<std::string> * incoming_messages)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 
LoadOutgoingMessages(OutgoingMessageMap * outgoing_messages)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 
LoadLastCheckinTime(base::Time * last_checkin_time)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 
LoadGServicesSettings(std::map<std::string,std::string> * settings,std::string * digest)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 
GCMStoreImpl(const base::FilePath & path,scoped_refptr<base::SequencedTaskRunner> blocking_task_runner,scoped_ptr<Encryptor> encryptor)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 
~GCMStoreImpl()718 GCMStoreImpl::~GCMStoreImpl() {}
719 
Load(const LoadCallback & callback)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 
Close()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 
Destroy(const UpdateCallback & callback)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 
SetDeviceCredentials(uint64 device_android_id,uint64 device_security_token,const UpdateCallback & callback)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 
AddRegistration(const std::string & app_id,const linked_ptr<RegistrationInfo> & registration,const UpdateCallback & callback)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 
RemoveRegistration(const std::string & app_id,const UpdateCallback & callback)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 
AddIncomingMessage(const std::string & persistent_id,const UpdateCallback & callback)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 
RemoveIncomingMessage(const std::string & persistent_id,const UpdateCallback & callback)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 
RemoveIncomingMessages(const PersistentIdList & persistent_ids,const UpdateCallback & callback)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 
AddOutgoingMessage(const std::string & persistent_id,const MCSMessage & message,const UpdateCallback & callback)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 
OverwriteOutgoingMessage(const std::string & persistent_id,const MCSMessage & message,const UpdateCallback & callback)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 
RemoveOutgoingMessage(const std::string & persistent_id,const UpdateCallback & callback)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 
RemoveOutgoingMessages(const PersistentIdList & persistent_ids,const UpdateCallback & callback)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 
SetLastCheckinTime(const base::Time & last_checkin_time,const UpdateCallback & callback)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 
SetGServicesSettings(const std::map<std::string,std::string> & settings,const std::string & digest,const UpdateCallback & callback)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 
LoadContinuation(const LoadCallback & callback,scoped_ptr<LoadResult> result)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 
AddOutgoingMessageContinuation(const UpdateCallback & callback,const std::string & app_id,bool success)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 
RemoveOutgoingMessagesContinuation(const UpdateCallback & callback,bool success,const AppIdToMessageCountMap & removed_message_counts)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