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