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