1 // Copyright (c) 2013 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 "chrome/browser/services/gcm/gcm_profile_service.h" 6 7 #include "base/base64.h" 8 #include "base/logging.h" 9 #include "base/prefs/pref_service.h" 10 #include "base/strings/string_number_conversions.h" 11 #include "chrome/browser/chrome_notification_types.h" 12 #include "chrome/browser/extensions/extension_service.h" 13 #include "chrome/browser/extensions/extension_system.h" 14 #include "chrome/browser/extensions/state_store.h" 15 #include "chrome/browser/services/gcm/gcm_event_router.h" 16 #include "chrome/browser/signin/signin_manager.h" 17 #include "chrome/browser/signin/signin_manager_factory.h" 18 #include "chrome/common/chrome_version_info.h" 19 #include "chrome/common/pref_names.h" 20 #include "components/user_prefs/pref_registry_syncable.h" 21 #include "components/webdata/encryptor/encryptor.h" 22 #include "content/public/browser/browser_thread.h" 23 #include "content/public/browser/notification_details.h" 24 #include "content/public/browser/notification_source.h" 25 #include "extensions/common/extension.h" 26 27 using extensions::Extension; 28 29 namespace gcm { 30 31 const char kRegistrationKey[] = "gcm.registration"; 32 const char kSendersKey[] = "senders"; 33 const char kRegistrationIDKey[] = "reg_id"; 34 35 class GCMProfileService::IOWorker 36 : public GCMClient::Delegate, 37 public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{ 38 public: 39 explicit IOWorker(const base::WeakPtr<GCMProfileService>& service); 40 41 // Overridden from GCMClient::Delegate: 42 // Called from IO thread. 43 virtual void OnCheckInFinished(const GCMClient::CheckInInfo& checkin_info, 44 GCMClient::Result result) OVERRIDE; 45 virtual void OnRegisterFinished(const std::string& app_id, 46 const std::string& registration_id, 47 GCMClient::Result result) OVERRIDE; 48 virtual void OnSendFinished(const std::string& app_id, 49 const std::string& message_id, 50 GCMClient::Result result) OVERRIDE; 51 virtual void OnMessageReceived( 52 const std::string& app_id, 53 const GCMClient::IncomingMessage& message) OVERRIDE; 54 virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE; 55 virtual void OnMessageSendError(const std::string& app_id, 56 const std::string& message_id, 57 GCMClient::Result result) OVERRIDE; 58 virtual GCMClient::CheckInInfo GetCheckInInfo() const OVERRIDE; 59 virtual void OnLoadingCompleted() OVERRIDE; 60 virtual base::TaskRunner* GetFileTaskRunner() OVERRIDE; 61 62 void CheckIn(const std::string& username); 63 void SetCheckInInfo(GCMClient::CheckInInfo checkin_info); 64 void CheckOut(); 65 void Register(const std::string& username, 66 const std::string& app_id, 67 const std::vector<std::string>& sender_ids, 68 const std::string& cert); 69 void Unregister(const std::string& username, const std::string& app_id); 70 void Send(const std::string& username, 71 const std::string& app_id, 72 const std::string& receiver_id, 73 const GCMClient::OutgoingMessage& message); 74 75 private: 76 friend class base::RefCountedThreadSafe<IOWorker>; 77 virtual ~IOWorker(); 78 79 const base::WeakPtr<GCMProfileService> service_; 80 81 // The checkin info obtained from the server for the signed in user associated 82 // with the profile. 83 GCMClient::CheckInInfo checkin_info_; 84 }; 85 86 GCMProfileService::IOWorker::IOWorker( 87 const base::WeakPtr<GCMProfileService>& service) 88 : service_(service) { 89 } 90 91 GCMProfileService::IOWorker::~IOWorker() { 92 } 93 94 void GCMProfileService::IOWorker::OnCheckInFinished( 95 const GCMClient::CheckInInfo& checkin_info, 96 GCMClient::Result result) { 97 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 98 99 checkin_info_ = checkin_info; 100 101 content::BrowserThread::PostTask( 102 content::BrowserThread::UI, 103 FROM_HERE, 104 base::Bind(&GCMProfileService::CheckInFinished, 105 service_, 106 checkin_info_, 107 result)); 108 } 109 110 void GCMProfileService::IOWorker::OnRegisterFinished( 111 const std::string& app_id, 112 const std::string& registration_id, 113 GCMClient::Result result) { 114 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 115 116 content::BrowserThread::PostTask( 117 content::BrowserThread::UI, 118 FROM_HERE, 119 base::Bind(&GCMProfileService::RegisterFinished, 120 service_, 121 app_id, 122 registration_id, 123 result)); 124 } 125 126 void GCMProfileService::IOWorker::OnSendFinished( 127 const std::string& app_id, 128 const std::string& message_id, 129 GCMClient::Result result) { 130 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 131 132 content::BrowserThread::PostTask( 133 content::BrowserThread::UI, 134 FROM_HERE, 135 base::Bind(&GCMProfileService::SendFinished, 136 service_, 137 app_id, 138 message_id, 139 result)); 140 } 141 142 void GCMProfileService::IOWorker::OnMessageReceived( 143 const std::string& app_id, 144 const GCMClient::IncomingMessage& message) { 145 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 146 147 content::BrowserThread::PostTask( 148 content::BrowserThread::UI, 149 FROM_HERE, 150 base::Bind(&GCMProfileService::MessageReceived, 151 service_, 152 app_id, 153 message)); 154 } 155 156 void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) { 157 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 158 159 content::BrowserThread::PostTask( 160 content::BrowserThread::UI, 161 FROM_HERE, 162 base::Bind(&GCMProfileService::MessagesDeleted, 163 service_, 164 app_id)); 165 } 166 167 void GCMProfileService::IOWorker::OnMessageSendError( 168 const std::string& app_id, 169 const std::string& message_id, 170 GCMClient::Result result) { 171 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 172 173 content::BrowserThread::PostTask( 174 content::BrowserThread::UI, 175 FROM_HERE, 176 base::Bind(&GCMProfileService::MessageSendError, 177 service_, 178 app_id, 179 message_id, 180 result)); 181 } 182 183 GCMClient::CheckInInfo GCMProfileService::IOWorker::GetCheckInInfo() const { 184 return checkin_info_; 185 } 186 187 void GCMProfileService::IOWorker::OnLoadingCompleted() { 188 // TODO(jianli): to be implemented. 189 } 190 191 base::TaskRunner* GCMProfileService::IOWorker::GetFileTaskRunner() { 192 // TODO(jianli): to be implemented. 193 return NULL; 194 } 195 196 void GCMProfileService::IOWorker::CheckIn(const std::string& username) { 197 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 198 199 GCMClient::Get()->CheckIn(username, this); 200 } 201 202 void GCMProfileService::IOWorker::SetCheckInInfo( 203 GCMClient::CheckInInfo checkin_info) { 204 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 205 206 checkin_info_ = checkin_info; 207 } 208 209 void GCMProfileService::IOWorker::CheckOut() { 210 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 211 212 checkin_info_.Reset(); 213 } 214 215 void GCMProfileService::IOWorker::Register( 216 const std::string& username, 217 const std::string& app_id, 218 const std::vector<std::string>& sender_ids, 219 const std::string& cert) { 220 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 221 DCHECK(checkin_info_.IsValid()); 222 223 GCMClient::Get()->Register(username, app_id, cert, sender_ids); 224 } 225 226 void GCMProfileService::IOWorker::Unregister(const std::string& username, 227 const std::string& app_id) { 228 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 229 DCHECK(checkin_info_.IsValid()); 230 231 GCMClient::Get()->Unregister(username, app_id); 232 } 233 234 void GCMProfileService::IOWorker::Send( 235 const std::string& username, 236 const std::string& app_id, 237 const std::string& receiver_id, 238 const GCMClient::OutgoingMessage& message) { 239 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 240 DCHECK(checkin_info_.IsValid()); 241 242 GCMClient::Get()->Send(username, app_id, receiver_id, message); 243 } 244 245 GCMProfileService::RegistrationInfo::RegistrationInfo() { 246 } 247 248 GCMProfileService::RegistrationInfo::~RegistrationInfo() { 249 } 250 251 bool GCMProfileService::enable_gcm_for_testing_ = false; 252 253 // static 254 bool GCMProfileService::IsGCMEnabled() { 255 if (enable_gcm_for_testing_) 256 return true; 257 258 // GCM support is only enabled for Canary/Dev builds. 259 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 260 return channel == chrome::VersionInfo::CHANNEL_UNKNOWN || 261 channel == chrome::VersionInfo::CHANNEL_CANARY || 262 channel == chrome::VersionInfo::CHANNEL_DEV; 263 } 264 265 // static 266 void GCMProfileService::RegisterProfilePrefs( 267 user_prefs::PrefRegistrySyncable* registry) { 268 registry->RegisterUint64Pref( 269 prefs::kGCMUserAccountID, 270 0, 271 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 272 registry->RegisterStringPref( 273 prefs::kGCMUserToken, 274 "", 275 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 276 } 277 278 GCMProfileService::GCMProfileService(Profile* profile) 279 : profile_(profile), 280 testing_delegate_(NULL), 281 weak_ptr_factory_(this) { 282 Init(); 283 } 284 285 GCMProfileService::GCMProfileService(Profile* profile, 286 TestingDelegate* testing_delegate) 287 : profile_(profile), 288 testing_delegate_(testing_delegate), 289 weak_ptr_factory_(this) { 290 Init(); 291 } 292 293 GCMProfileService::~GCMProfileService() { 294 } 295 296 void GCMProfileService::Init() { 297 // This has to be done first since CheckIn depends on it. 298 io_worker_ = new IOWorker(weak_ptr_factory_.GetWeakPtr()); 299 300 // In case that the profile has been signed in before GCMProfileService is 301 // created. 302 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_); 303 if (manager) 304 username_ = manager->GetAuthenticatedUsername(); 305 if (!username_.empty()) 306 AddUser(); 307 308 registrar_.Add(this, 309 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL, 310 content::Source<Profile>(profile_)); 311 registrar_.Add(this, 312 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT, 313 content::Source<Profile>(profile_)); 314 // TODO(jianli): move extension specific logic out of GCMProfileService. 315 registrar_.Add(this, 316 chrome::NOTIFICATION_EXTENSION_LOADED, 317 content::Source<Profile>(profile_)); 318 registrar_.Add(this, 319 chrome:: NOTIFICATION_EXTENSION_UNINSTALLED, 320 content::Source<Profile>(profile_)); 321 } 322 323 void GCMProfileService::Register(const std::string& app_id, 324 const std::vector<std::string>& sender_ids, 325 const std::string& cert, 326 RegisterCallback callback) { 327 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 328 DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null()); 329 330 // If previous register operation is still in progress, bail out. 331 if (register_callbacks_.find(app_id) != register_callbacks_.end()) { 332 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); 333 return; 334 } 335 336 // Normalize the sender IDs by making them sorted. 337 std::vector<std::string> normalized_sender_ids = sender_ids; 338 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); 339 340 // If the same sender ids is provided, return the cached registration ID 341 // directly. 342 RegistrationInfoMap::const_iterator registration_info_iter = 343 registration_info_map_.find(app_id); 344 if (registration_info_iter != registration_info_map_.end() && 345 registration_info_iter->second.sender_ids == normalized_sender_ids) { 346 callback.Run(registration_info_iter->second.registration_id, 347 GCMClient::SUCCESS); 348 return; 349 } 350 351 // Cache the sender IDs. The registration ID will be filled when the 352 // registration completes. 353 RegistrationInfo registration_info; 354 registration_info.sender_ids = normalized_sender_ids; 355 registration_info_map_[app_id] = registration_info; 356 357 register_callbacks_[app_id] = callback; 358 359 content::BrowserThread::PostTask( 360 content::BrowserThread::IO, 361 FROM_HERE, 362 base::Bind(&GCMProfileService::IOWorker::Register, 363 io_worker_, 364 username_, 365 app_id, 366 normalized_sender_ids, 367 cert)); 368 } 369 370 void GCMProfileService::Send(const std::string& app_id, 371 const std::string& receiver_id, 372 const GCMClient::OutgoingMessage& message, 373 SendCallback callback) { 374 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 375 DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null()); 376 377 std::pair<std::string, std::string> key(app_id, message.id); 378 if (send_callbacks_.find(key) != send_callbacks_.end()) { 379 callback.Run(message.id, GCMClient::INVALID_PARAMETER); 380 return; 381 } 382 send_callbacks_[key] = callback; 383 384 content::BrowserThread::PostTask( 385 content::BrowserThread::IO, 386 FROM_HERE, 387 base::Bind(&GCMProfileService::IOWorker::Send, 388 io_worker_, 389 username_, 390 app_id, 391 receiver_id, 392 message)); 393 } 394 395 void GCMProfileService::Observe(int type, 396 const content::NotificationSource& source, 397 const content::NotificationDetails& details) { 398 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 399 400 switch (type) { 401 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: { 402 const GoogleServiceSigninSuccessDetails* signin_details = 403 content::Details<GoogleServiceSigninSuccessDetails>(details).ptr(); 404 // If re-signin occurs due to password change, there is no need to do 405 // check-in again. 406 if (username_ != signin_details->username) { 407 username_ = signin_details->username; 408 DCHECK(!username_.empty()); 409 AddUser(); 410 } 411 break; 412 } 413 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT: 414 username_.clear(); 415 RemoveUser(); 416 break; 417 case chrome::NOTIFICATION_EXTENSION_LOADED: { 418 extensions::Extension* extension = 419 content::Details<extensions::Extension>(details).ptr(); 420 // No need to load the persisted registration info if the extension does 421 // not have the GCM permission. 422 if (extension->HasAPIPermission(extensions::APIPermission::kGcm)) 423 ReadRegistrationInfo(extension->id()); 424 break; 425 } 426 case chrome:: NOTIFICATION_EXTENSION_UNINSTALLED: { 427 extensions::Extension* extension = 428 content::Details<extensions::Extension>(details).ptr(); 429 Unregister(extension->id()); 430 break; 431 } 432 default: 433 NOTREACHED(); 434 } 435 } 436 437 void GCMProfileService::AddUser() { 438 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 439 440 // Try to read persisted check-in info from the profile's prefs store. 441 PrefService* pref_service = profile_->GetPrefs(); 442 uint64 android_id = pref_service->GetUint64(prefs::kGCMUserAccountID); 443 std::string base64_token = pref_service->GetString(prefs::kGCMUserToken); 444 std::string encrypted_secret; 445 base::Base64Decode(base::StringPiece(base64_token), &encrypted_secret); 446 if (android_id && !encrypted_secret.empty()) { 447 std::string decrypted_secret; 448 Encryptor::DecryptString(encrypted_secret, &decrypted_secret); 449 uint64 secret = 0; 450 if (base::StringToUint64(decrypted_secret, &secret) && secret) { 451 GCMClient::CheckInInfo checkin_info; 452 checkin_info.android_id = android_id; 453 checkin_info.secret = secret; 454 content::BrowserThread::PostTask( 455 content::BrowserThread::IO, 456 FROM_HERE, 457 base::Bind(&GCMProfileService::IOWorker::SetCheckInInfo, 458 io_worker_, 459 checkin_info)); 460 461 if (testing_delegate_) 462 testing_delegate_->CheckInFinished(checkin_info, GCMClient::SUCCESS); 463 464 return; 465 } 466 } 467 468 content::BrowserThread::PostTask( 469 content::BrowserThread::IO, 470 FROM_HERE, 471 base::Bind(&GCMProfileService::IOWorker::CheckIn, 472 io_worker_, 473 username_)); 474 } 475 476 void GCMProfileService::RemoveUser() { 477 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 478 479 PrefService* pref_service = profile_->GetPrefs(); 480 pref_service->ClearPref(prefs::kGCMUserAccountID); 481 pref_service->ClearPref(prefs::kGCMUserToken); 482 483 content::BrowserThread::PostTask( 484 content::BrowserThread::IO, 485 FROM_HERE, 486 base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_)); 487 } 488 489 void GCMProfileService::Unregister(const std::string& app_id) { 490 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 491 492 // This is unlikely to happen because the app will not be uninstalled before 493 // the asynchronous extension function completes. 494 DCHECK(register_callbacks_.find(app_id) == register_callbacks_.end()); 495 496 // Remove the cached registration info. If not found, there is no need to 497 // ask the server to unregister it. 498 RegistrationInfoMap::iterator registration_info_iter = 499 registration_info_map_.find(app_id); 500 if (registration_info_iter == registration_info_map_.end()) 501 return; 502 registration_info_map_.erase(registration_info_iter); 503 504 // Remove the persisted registration info. 505 DeleteRegistrationInfo(app_id); 506 507 // Ask the server to unregister it. There could be a small chance that the 508 // unregister request fails. If this occurs, it does not bring any harm since 509 // we simply reject the messages/events received from the server. 510 content::BrowserThread::PostTask( 511 content::BrowserThread::IO, 512 FROM_HERE, 513 base::Bind(&GCMProfileService::IOWorker::Unregister, 514 io_worker_, 515 username_, 516 app_id)); 517 } 518 519 void GCMProfileService::CheckInFinished(GCMClient::CheckInInfo checkin_info, 520 GCMClient::Result result) { 521 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 522 523 // Save the check-in info into the profile's prefs store. 524 PrefService* pref_service = profile_->GetPrefs(); 525 pref_service->SetUint64(prefs::kGCMUserAccountID, checkin_info.android_id); 526 527 // Encrypt the secret for persisting purpose. 528 std::string encrypted_secret; 529 Encryptor::EncryptString(base::Uint64ToString(checkin_info.secret), 530 &encrypted_secret); 531 532 // |encrypted_secret| might contain binary data and our prefs store only 533 // works for the text. 534 std::string base64_token; 535 base::Base64Encode(encrypted_secret, &base64_token); 536 pref_service->SetString(prefs::kGCMUserToken, base64_token); 537 538 if (testing_delegate_) 539 testing_delegate_->CheckInFinished(checkin_info, result); 540 } 541 542 void GCMProfileService::RegisterFinished(std::string app_id, 543 std::string registration_id, 544 GCMClient::Result result) { 545 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 546 547 std::map<std::string, RegisterCallback>::iterator callback_iter = 548 register_callbacks_.find(app_id); 549 if (callback_iter == register_callbacks_.end()) { 550 // The callback could have been removed when the app is uninstalled. 551 return; 552 } 553 554 // Cache the registration ID if the registration succeeds. Otherwise, 555 // removed the cached info. 556 RegistrationInfoMap::iterator registration_info_iter = 557 registration_info_map_.find(app_id); 558 // This is unlikely to happen because the app will not be uninstalled before 559 // the asynchronous extension function completes. 560 DCHECK(registration_info_iter != registration_info_map_.end()); 561 if (result == GCMClient::SUCCESS) { 562 registration_info_iter->second.registration_id = registration_id; 563 WriteRegistrationInfo(app_id); 564 } else { 565 registration_info_map_.erase(registration_info_iter); 566 } 567 568 RegisterCallback callback = callback_iter->second; 569 register_callbacks_.erase(callback_iter); 570 callback.Run(registration_id, result); 571 } 572 573 void GCMProfileService::SendFinished(std::string app_id, 574 std::string message_id, 575 GCMClient::Result result) { 576 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 577 578 std::map<std::pair<std::string, std::string>, SendCallback>::iterator 579 callback_iter = send_callbacks_.find( 580 std::pair<std::string, std::string>(app_id, message_id)); 581 if (callback_iter == send_callbacks_.end()) { 582 // The callback could have been removed when the app is uninstalled. 583 return; 584 } 585 586 SendCallback callback = callback_iter->second; 587 send_callbacks_.erase(callback_iter); 588 callback.Run(message_id, result); 589 } 590 591 void GCMProfileService::MessageReceived(std::string app_id, 592 GCMClient::IncomingMessage message) { 593 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 594 595 GetEventRouter(app_id)->OnMessage(app_id, message); 596 } 597 598 void GCMProfileService::MessagesDeleted(std::string app_id) { 599 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 600 601 GetEventRouter(app_id)->OnMessagesDeleted(app_id); 602 } 603 604 void GCMProfileService::MessageSendError(std::string app_id, 605 std::string message_id, 606 GCMClient::Result result) { 607 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 608 609 GetEventRouter(app_id)->OnSendError(app_id, message_id, result); 610 } 611 612 GCMEventRouter* GCMProfileService::GetEventRouter(const std::string& app_id) { 613 if (testing_delegate_ && testing_delegate_->GetEventRouter()) 614 return testing_delegate_->GetEventRouter(); 615 // TODO(fgorski): check and create the event router for JS routing. 616 return js_event_router_.get(); 617 } 618 619 void GCMProfileService::DeleteRegistrationInfo(const std::string& app_id) { 620 extensions::StateStore* storage = 621 extensions::ExtensionSystem::Get(profile_)->state_store(); 622 if (!storage) 623 return; 624 625 storage->RemoveExtensionValue(app_id, kRegistrationKey); 626 } 627 628 void GCMProfileService::WriteRegistrationInfo(const std::string& app_id) { 629 extensions::StateStore* storage = 630 extensions::ExtensionSystem::Get(profile_)->state_store(); 631 if (!storage) 632 return; 633 634 RegistrationInfoMap::const_iterator registration_info_iter = 635 registration_info_map_.find(app_id); 636 if (registration_info_iter == registration_info_map_.end()) 637 return; 638 const RegistrationInfo& registration_info = registration_info_iter->second; 639 640 scoped_ptr<base::ListValue> senders_list(new base::ListValue()); 641 for (std::vector<std::string>::const_iterator senders_iter = 642 registration_info.sender_ids.begin(); 643 senders_iter != registration_info.sender_ids.end(); 644 ++senders_iter) { 645 senders_list->AppendString(*senders_iter); 646 } 647 648 scoped_ptr<base::DictionaryValue> registration_info_dict( 649 new base::DictionaryValue()); 650 registration_info_dict->Set(kSendersKey, senders_list.release()); 651 registration_info_dict->SetString(kRegistrationIDKey, 652 registration_info.registration_id); 653 654 storage->SetExtensionValue( 655 app_id, kRegistrationKey, registration_info_dict.PassAs<base::Value>()); 656 } 657 658 void GCMProfileService::ReadRegistrationInfo(const std::string& app_id) { 659 extensions::StateStore* storage = 660 extensions::ExtensionSystem::Get(profile_)->state_store(); 661 if (!storage) 662 return; 663 664 storage->GetExtensionValue( 665 app_id, 666 kRegistrationKey, 667 base::Bind( 668 &GCMProfileService::ReadRegistrationInfoFinished, 669 weak_ptr_factory_.GetWeakPtr(), 670 app_id)); 671 } 672 673 void GCMProfileService::ReadRegistrationInfoFinished( 674 std::string app_id, 675 scoped_ptr<base::Value> value) { 676 RegistrationInfo registration_info; 677 if (!ParsePersistedRegistrationInfo(value.Pass(), ®istration_info)) { 678 // Delete the persisted data if it is corrupted. 679 DeleteRegistrationInfo(app_id); 680 return; 681 } 682 683 registration_info_map_[app_id] = registration_info; 684 685 // TODO(jianli): The waiting would be removed once we support delaying running 686 // register operation until the persistent loading completes. 687 if (testing_delegate_) 688 testing_delegate_->LoadingFromPersistentStoreFinished(); 689 } 690 691 bool GCMProfileService::ParsePersistedRegistrationInfo( 692 scoped_ptr<base::Value> value, 693 RegistrationInfo* registration_info) { 694 base::DictionaryValue* dict = NULL; 695 if (!value.get() || !value->GetAsDictionary(&dict)) 696 return false; 697 698 if (!dict->GetString(kRegistrationIDKey, ®istration_info->registration_id)) 699 return false; 700 701 const base::ListValue* senders_list = NULL; 702 if (!dict->GetList(kSendersKey, &senders_list) || !senders_list->GetSize()) 703 return false; 704 for (size_t i = 0; i < senders_list->GetSize(); ++i) { 705 std::string sender; 706 if (!senders_list->GetString(i, &sender)) 707 return false; 708 registration_info->sender_ids.push_back(sender); 709 } 710 711 return true; 712 } 713 714 // static 715 const char* GCMProfileService::GetPersistentRegisterKeyForTesting() { 716 return kRegistrationKey; 717 } 718 719 } // namespace gcm 720