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