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