gcm_profile_service.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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 <algorithm> 8#include <string> 9#include <vector> 10 11#include "base/base64.h" 12#include "base/files/file_path.h" 13#include "base/logging.h" 14#include "base/path_service.h" 15#include "base/prefs/pref_service.h" 16#include "base/strings/string_number_conversions.h" 17#include "base/threading/sequenced_worker_pool.h" 18#include "chrome/browser/chrome_notification_types.h" 19#include "chrome/browser/profiles/profile.h" 20#include "chrome/browser/services/gcm/gcm_app_handler.h" 21#include "chrome/browser/services/gcm/gcm_client_factory.h" 22#include "chrome/browser/signin/profile_oauth2_token_service_factory.h" 23#include "chrome/browser/signin/signin_manager_factory.h" 24#include "chrome/common/chrome_constants.h" 25#include "chrome/common/chrome_paths.h" 26#include "chrome/common/chrome_version_info.h" 27#include "chrome/common/pref_names.h" 28#include "components/signin/core/browser/profile_oauth2_token_service.h" 29#include "components/signin/core/browser/signin_manager.h" 30#include "components/user_prefs/pref_registry_syncable.h" 31#include "content/public/browser/browser_thread.h" 32#include "content/public/browser/notification_details.h" 33#include "content/public/browser/notification_source.h" 34#include "google_apis/gcm/protocol/android_checkin.pb.h" 35#include "net/url_request/url_request_context_getter.h" 36 37namespace gcm { 38 39namespace { 40 41checkin_proto::ChromeBuildProto_Platform GetPlatform() { 42#if defined(OS_WIN) 43 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN; 44#elif defined(OS_MACOSX) 45 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC; 46#elif defined(OS_IOS) 47 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS; 48#elif defined(OS_CHROMEOS) 49 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS; 50#elif defined(OS_LINUX) 51 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX; 52#else 53 // For all other platforms, return as LINUX. 54 return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX; 55#endif 56} 57 58std::string GetVersion() { 59 chrome::VersionInfo version_info; 60 return version_info.Version(); 61} 62 63checkin_proto::ChromeBuildProto_Channel GetChannel() { 64 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 65 switch (channel) { 66 case chrome::VersionInfo::CHANNEL_UNKNOWN: 67 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN; 68 case chrome::VersionInfo::CHANNEL_CANARY: 69 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY; 70 case chrome::VersionInfo::CHANNEL_DEV: 71 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV; 72 case chrome::VersionInfo::CHANNEL_BETA: 73 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA; 74 case chrome::VersionInfo::CHANNEL_STABLE: 75 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE; 76 default: 77 NOTREACHED(); 78 return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN; 79 }; 80} 81 82} // namespace 83 84// Helper class to save tasks to run until we're ready to execute them. 85class GCMProfileService::DelayedTaskController { 86 public: 87 DelayedTaskController(); 88 ~DelayedTaskController(); 89 90 // Adds a task that will be invoked once we're ready. 91 void AddTask(base::Closure task); 92 93 // Sets ready status. It is ready only when check-in is completed and 94 // the GCMClient is fully initialized. 95 void SetReady(); 96 97 // Returns true if it is ready to perform tasks. 98 bool CanRunTaskWithoutDelay() const; 99 100 private: 101 void RunTasks(); 102 103 // Flag that indicates that GCM is ready. 104 bool ready_; 105 106 std::vector<base::Closure> delayed_tasks_; 107}; 108 109GCMProfileService::DelayedTaskController::DelayedTaskController() 110 : ready_(false) { 111} 112 113GCMProfileService::DelayedTaskController::~DelayedTaskController() { 114} 115 116void GCMProfileService::DelayedTaskController::AddTask(base::Closure task) { 117 delayed_tasks_.push_back(task); 118} 119 120void GCMProfileService::DelayedTaskController::SetReady() { 121 ready_ = true; 122 RunTasks(); 123} 124 125bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay() const { 126 return ready_; 127} 128 129void GCMProfileService::DelayedTaskController::RunTasks() { 130 DCHECK(ready_); 131 132 for (size_t i = 0; i < delayed_tasks_.size(); ++i) 133 delayed_tasks_[i].Run(); 134 delayed_tasks_.clear(); 135} 136 137class GCMProfileService::IOWorker 138 : public GCMClient::Delegate, 139 public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{ 140 public: 141 // Called on UI thread. 142 IOWorker(); 143 144 // Overridden from GCMClient::Delegate: 145 // Called on IO thread. 146 virtual void OnRegisterFinished(const std::string& app_id, 147 const std::string& registration_id, 148 GCMClient::Result result) OVERRIDE; 149 virtual void OnUnregisterFinished(const std::string& app_id, 150 GCMClient::Result result) OVERRIDE; 151 virtual void OnSendFinished(const std::string& app_id, 152 const std::string& message_id, 153 GCMClient::Result result) OVERRIDE; 154 virtual void OnMessageReceived( 155 const std::string& app_id, 156 const GCMClient::IncomingMessage& message) OVERRIDE; 157 virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE; 158 virtual void OnMessageSendError( 159 const std::string& app_id, 160 const GCMClient::SendErrorDetails& send_error_details) OVERRIDE; 161 virtual void OnGCMReady() OVERRIDE; 162 163 // Called on IO thread. 164 void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory, 165 const base::FilePath& store_path, 166 const std::vector<std::string>& account_ids, 167 const scoped_refptr<net::URLRequestContextGetter>& 168 url_request_context_getter); 169 void Reset(); 170 void Load(const base::WeakPtr<GCMProfileService>& service); 171 void Stop(); 172 void CheckOut(); 173 void Register(const std::string& app_id, 174 const std::vector<std::string>& sender_ids); 175 void Unregister(const std::string& app_id); 176 void Send(const std::string& app_id, 177 const std::string& receiver_id, 178 const GCMClient::OutgoingMessage& message); 179 void RequestGCMStatistics(); 180 181 // For testing purpose. Can be called from UI thread. Use with care. 182 GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); } 183 184 private: 185 friend class base::RefCountedThreadSafe<IOWorker>; 186 virtual ~IOWorker(); 187 188 base::WeakPtr<GCMProfileService> service_; 189 190 scoped_ptr<GCMClient> gcm_client_; 191}; 192 193GCMProfileService::IOWorker::IOWorker() { 194 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 195} 196 197GCMProfileService::IOWorker::~IOWorker() { 198} 199 200void GCMProfileService::IOWorker::Initialize( 201 scoped_ptr<GCMClientFactory> gcm_client_factory, 202 const base::FilePath& store_path, 203 const std::vector<std::string>& account_ids, 204 const scoped_refptr<net::URLRequestContextGetter>& 205 url_request_context_getter) { 206 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 207 208 gcm_client_ = gcm_client_factory->BuildInstance().Pass(); 209 210 checkin_proto::ChromeBuildProto chrome_build_proto; 211 chrome_build_proto.set_platform(GetPlatform()); 212 chrome_build_proto.set_chrome_version(GetVersion()); 213 chrome_build_proto.set_channel(GetChannel()); 214 215 scoped_refptr<base::SequencedWorkerPool> worker_pool( 216 content::BrowserThread::GetBlockingPool()); 217 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner( 218 worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( 219 worker_pool->GetSequenceToken(), 220 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); 221 222 gcm_client_->Initialize(chrome_build_proto, 223 store_path, 224 account_ids, 225 blocking_task_runner, 226 url_request_context_getter, 227 this); 228} 229 230void GCMProfileService::IOWorker::Reset() { 231 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 232 233 // GCMClient instance must be destroyed from the same thread where it was 234 // created. 235 gcm_client_.reset(); 236} 237 238void GCMProfileService::IOWorker::OnRegisterFinished( 239 const std::string& app_id, 240 const std::string& registration_id, 241 GCMClient::Result result) { 242 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 243 244 content::BrowserThread::PostTask( 245 content::BrowserThread::UI, 246 FROM_HERE, 247 base::Bind(&GCMProfileService::RegisterFinished, 248 service_, 249 app_id, 250 registration_id, 251 result)); 252} 253 254void GCMProfileService::IOWorker::OnUnregisterFinished( 255 const std::string& app_id, 256 GCMClient::Result result) { 257 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 258 259 content::BrowserThread::PostTask( 260 content::BrowserThread::UI, 261 FROM_HERE, 262 base::Bind( 263 &GCMProfileService::UnregisterFinished, service_, app_id, result)); 264} 265 266void GCMProfileService::IOWorker::OnSendFinished( 267 const std::string& app_id, 268 const std::string& message_id, 269 GCMClient::Result result) { 270 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 271 272 content::BrowserThread::PostTask( 273 content::BrowserThread::UI, 274 FROM_HERE, 275 base::Bind(&GCMProfileService::SendFinished, 276 service_, 277 app_id, 278 message_id, 279 result)); 280} 281 282void GCMProfileService::IOWorker::OnMessageReceived( 283 const std::string& app_id, 284 const GCMClient::IncomingMessage& message) { 285 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 286 287 content::BrowserThread::PostTask( 288 content::BrowserThread::UI, 289 FROM_HERE, 290 base::Bind(&GCMProfileService::MessageReceived, 291 service_, 292 app_id, 293 message)); 294} 295 296void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) { 297 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 298 299 content::BrowserThread::PostTask( 300 content::BrowserThread::UI, 301 FROM_HERE, 302 base::Bind(&GCMProfileService::MessagesDeleted, 303 service_, 304 app_id)); 305} 306 307void GCMProfileService::IOWorker::OnMessageSendError( 308 const std::string& app_id, 309 const GCMClient::SendErrorDetails& send_error_details) { 310 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 311 312 content::BrowserThread::PostTask( 313 content::BrowserThread::UI, 314 FROM_HERE, 315 base::Bind(&GCMProfileService::MessageSendError, 316 service_, 317 app_id, 318 send_error_details)); 319} 320 321void GCMProfileService::IOWorker::OnGCMReady() { 322 content::BrowserThread::PostTask( 323 content::BrowserThread::UI, 324 FROM_HERE, 325 base::Bind(&GCMProfileService::GCMClientReady, 326 service_)); 327} 328 329void GCMProfileService::IOWorker::Load( 330 const base::WeakPtr<GCMProfileService>& service) { 331 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 332 333 service_ = service; 334 gcm_client_->Load(); 335} 336 337void GCMProfileService::IOWorker::Stop() { 338 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 339 340 gcm_client_->Stop(); 341} 342 343void GCMProfileService::IOWorker::CheckOut() { 344 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 345 346 gcm_client_->CheckOut(); 347 348 // Note that we still need to keep GCMClient instance alive since the profile 349 // might be signed in again. 350} 351 352void GCMProfileService::IOWorker::Register( 353 const std::string& app_id, 354 const std::vector<std::string>& sender_ids) { 355 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 356 357 gcm_client_->Register(app_id, sender_ids); 358} 359 360void GCMProfileService::IOWorker::Unregister(const std::string& app_id) { 361 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 362 363 gcm_client_->Unregister(app_id); 364} 365 366void GCMProfileService::IOWorker::Send( 367 const std::string& app_id, 368 const std::string& receiver_id, 369 const GCMClient::OutgoingMessage& message) { 370 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 371 372 gcm_client_->Send(app_id, receiver_id, message); 373} 374 375void GCMProfileService::IOWorker::RequestGCMStatistics() { 376 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 377 gcm::GCMClient::GCMStatistics stats; 378 379 if (gcm_client_.get()) { 380 stats.gcm_client_created = true; 381 stats = gcm_client_->GetStatistics(); 382 } 383 384 content::BrowserThread::PostTask( 385 content::BrowserThread::UI, 386 FROM_HERE, 387 base::Bind(&GCMProfileService::RequestGCMStatisticsFinished, 388 service_, 389 stats)); 390} 391 392std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) { 393 switch (state) { 394 case GCMProfileService::ALWAYS_ENABLED: 395 return "ALWAYS_ENABLED"; 396 case GCMProfileService::ENABLED_FOR_APPS: 397 return "ENABLED_FOR_APPS"; 398 case GCMProfileService::ALWAYS_DISABLED: 399 return "ALWAYS_DISABLED"; 400 default: 401 NOTREACHED(); 402 return std::string(); 403 } 404} 405 406// static 407GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState( 408 Profile* profile) { 409 const base::Value* gcm_enabled_value = 410 profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled); 411 if (!gcm_enabled_value) 412 return ENABLED_FOR_APPS; 413 414 bool gcm_enabled = false; 415 if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled)) 416 return ENABLED_FOR_APPS; 417 418 return gcm_enabled ? ALWAYS_ENABLED : ALWAYS_DISABLED; 419} 420 421// static 422void GCMProfileService::RegisterProfilePrefs( 423 user_prefs::PrefRegistrySyncable* registry) { 424 // GCM support is only enabled by default for Canary/Dev/Custom builds. 425 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 426 bool on_by_default = false; 427 if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || 428 channel == chrome::VersionInfo::CHANNEL_CANARY || 429 channel == chrome::VersionInfo::CHANNEL_DEV) { 430 on_by_default = true; 431 } 432 registry->RegisterBooleanPref( 433 prefs::kGCMChannelEnabled, 434 on_by_default, 435 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 436} 437 438GCMProfileService::GCMProfileService(Profile* profile) 439 : profile_(profile), 440 gcm_client_ready_(false), 441 weak_ptr_factory_(this) { 442 DCHECK(!profile->IsOffTheRecord()); 443} 444 445GCMProfileService::~GCMProfileService() { 446} 447 448void GCMProfileService::Initialize( 449 scoped_ptr<GCMClientFactory> gcm_client_factory) { 450 registrar_.Add(this, 451 chrome::NOTIFICATION_PROFILE_DESTROYED, 452 content::Source<Profile>(profile_)); 453 454 SigninManagerFactory::GetForProfile(profile_)->AddObserver(this); 455 456 // Get the list of available accounts. 457 std::vector<std::string> account_ids; 458#if !defined(OS_ANDROID) 459 account_ids = 460 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts(); 461#endif 462 463 // Create and initialize the GCMClient. Note that this does not initiate the 464 // GCM check-in. 465 io_worker_ = new IOWorker(); 466 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter = 467 profile_->GetRequestContext(); 468 content::BrowserThread::PostTask( 469 content::BrowserThread::IO, 470 FROM_HERE, 471 base::Bind(&GCMProfileService::IOWorker::Initialize, 472 io_worker_, 473 base::Passed(&gcm_client_factory), 474 profile_->GetPath().Append(chrome::kGCMStoreDirname), 475 account_ids, 476 url_request_context_getter)); 477 478 // Load from the GCM store and initiate the GCM check-in if the rollout signal 479 // indicates yes. 480 if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED) 481 EnsureLoaded(); 482} 483 484void GCMProfileService::Start() { 485 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 486 487 EnsureLoaded(); 488} 489 490void GCMProfileService::Stop() { 491 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 492 493 // No need to stop GCM service if not started yet. 494 if (username_.empty()) 495 return; 496 497 RemoveCachedData(); 498 499 content::BrowserThread::PostTask( 500 content::BrowserThread::IO, 501 FROM_HERE, 502 base::Bind(&GCMProfileService::IOWorker::Stop, io_worker_)); 503} 504 505void GCMProfileService::Shutdown() { 506 for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin(); 507 iter != app_handlers_.end(); ++iter) { 508 iter->second->ShutdownHandler(); 509 } 510 app_handlers_.clear(); 511 512 SigninManagerFactory::GetForProfile(profile_)->RemoveObserver(this); 513} 514 515void GCMProfileService::AddAppHandler(const std::string& app_id, 516 GCMAppHandler* handler) { 517 DCHECK(!app_id.empty()); 518 DCHECK(handler); 519 DCHECK(app_handlers_.find(app_id) == app_handlers_.end()); 520 521 app_handlers_[app_id] = handler; 522} 523 524void GCMProfileService::RemoveAppHandler(const std::string& app_id) { 525 DCHECK(!app_id.empty()); 526 527 app_handlers_.erase(app_id); 528} 529 530void GCMProfileService::Register(const std::string& app_id, 531 const std::vector<std::string>& sender_ids, 532 RegisterCallback callback) { 533 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 534 DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null()); 535 536 GCMClient::Result result = EnsureAppReady(app_id); 537 if (result != GCMClient::SUCCESS) { 538 callback.Run(std::string(), result); 539 return; 540 } 541 542 // If previous un/register operation is still in progress, bail out. 543 if (IsAsyncOperationPending(app_id)) { 544 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); 545 return; 546 } 547 548 register_callbacks_[app_id] = callback; 549 550 // Delay the register operation until GCMClient is ready. 551 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { 552 delayed_task_controller_->AddTask( 553 base::Bind(&GCMProfileService::DoRegister, 554 weak_ptr_factory_.GetWeakPtr(), 555 app_id, 556 sender_ids)); 557 return; 558 } 559 560 DoRegister(app_id, sender_ids); 561} 562 563void GCMProfileService::DoRegister(const std::string& app_id, 564 const std::vector<std::string>& sender_ids) { 565 std::map<std::string, RegisterCallback>::iterator callback_iter = 566 register_callbacks_.find(app_id); 567 if (callback_iter == register_callbacks_.end()) { 568 // The callback could have been removed when the app is uninstalled. 569 return; 570 } 571 572 // Normalize the sender IDs by making them sorted. 573 std::vector<std::string> normalized_sender_ids = sender_ids; 574 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); 575 576 content::BrowserThread::PostTask( 577 content::BrowserThread::IO, 578 FROM_HERE, 579 base::Bind(&GCMProfileService::IOWorker::Register, 580 io_worker_, 581 app_id, 582 normalized_sender_ids)); 583} 584 585void GCMProfileService::Unregister(const std::string& app_id, 586 UnregisterCallback callback) { 587 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 588 DCHECK(!app_id.empty() && !callback.is_null()); 589 590 GCMClient::Result result = EnsureAppReady(app_id); 591 if (result != GCMClient::SUCCESS) { 592 callback.Run(result); 593 return; 594 } 595 596 // If previous un/register operation is still in progress, bail out. 597 if (IsAsyncOperationPending(app_id)) { 598 callback.Run(GCMClient::ASYNC_OPERATION_PENDING); 599 return; 600 } 601 602 unregister_callbacks_[app_id] = callback; 603 604 // Delay the unregister operation until GCMClient is ready. 605 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { 606 delayed_task_controller_->AddTask( 607 base::Bind(&GCMProfileService::DoUnregister, 608 weak_ptr_factory_.GetWeakPtr(), 609 app_id)); 610 return; 611 } 612 613 DoUnregister(app_id); 614} 615 616void GCMProfileService::DoUnregister(const std::string& app_id) { 617 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 618 619 // Ask the server to unregister it. There could be a small chance that the 620 // unregister request fails. If this occurs, it does not bring any harm since 621 // we simply reject the messages/events received from the server. 622 content::BrowserThread::PostTask( 623 content::BrowserThread::IO, 624 FROM_HERE, 625 base::Bind(&GCMProfileService::IOWorker::Unregister, 626 io_worker_, 627 app_id)); 628} 629 630void GCMProfileService::Send(const std::string& app_id, 631 const std::string& receiver_id, 632 const GCMClient::OutgoingMessage& message, 633 SendCallback callback) { 634 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 635 DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null()); 636 637 GCMClient::Result result = EnsureAppReady(app_id); 638 if (result != GCMClient::SUCCESS) { 639 callback.Run(std::string(), result); 640 return; 641 } 642 643 // If the message with send ID is still in progress, bail out. 644 std::pair<std::string, std::string> key(app_id, message.id); 645 if (send_callbacks_.find(key) != send_callbacks_.end()) { 646 callback.Run(message.id, GCMClient::INVALID_PARAMETER); 647 return; 648 } 649 650 send_callbacks_[key] = callback; 651 652 // Delay the send operation until all GCMClient is ready. 653 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { 654 delayed_task_controller_->AddTask( 655 base::Bind(&GCMProfileService::DoSend, 656 weak_ptr_factory_.GetWeakPtr(), 657 app_id, 658 receiver_id, 659 message)); 660 return; 661 } 662 663 DoSend(app_id, receiver_id, message); 664} 665 666void GCMProfileService::DoSend(const std::string& app_id, 667 const std::string& receiver_id, 668 const GCMClient::OutgoingMessage& message) { 669 content::BrowserThread::PostTask( 670 content::BrowserThread::IO, 671 FROM_HERE, 672 base::Bind(&GCMProfileService::IOWorker::Send, 673 io_worker_, 674 app_id, 675 receiver_id, 676 message)); 677} 678 679GCMClient* GCMProfileService::GetGCMClientForTesting() const { 680 return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL; 681} 682 683std::string GCMProfileService::SignedInUserName() const { 684 return username_; 685} 686 687bool GCMProfileService::IsGCMClientReady() const { 688 return gcm_client_ready_; 689} 690 691void GCMProfileService::RequestGCMStatistics( 692 RequestGCMStatisticsCallback callback) { 693 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 694 DCHECK(!callback.is_null()); 695 696 request_gcm_statistics_callback_ = callback; 697 content::BrowserThread::PostTask( 698 content::BrowserThread::IO, 699 FROM_HERE, 700 base::Bind(&GCMProfileService::IOWorker::RequestGCMStatistics, 701 io_worker_)); 702} 703 704void GCMProfileService::Observe(int type, 705 const content::NotificationSource& source, 706 const content::NotificationDetails& details) { 707 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 708 709 switch (type) { 710 case chrome::NOTIFICATION_PROFILE_DESTROYED: 711 ResetGCMClient(); 712 break; 713 default: 714 NOTREACHED(); 715 } 716} 717 718void GCMProfileService::GoogleSigninSucceeded(const std::string& username, 719 const std::string& password) { 720 if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED) 721 EnsureLoaded(); 722} 723 724void GCMProfileService::GoogleSignedOut(const std::string& username) { 725 CheckOut(); 726} 727 728void GCMProfileService::EnsureLoaded() { 729 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_); 730 if (!manager) 731 return; 732 std::string username = manager->GetAuthenticatedUsername(); 733 if (username.empty()) 734 return; 735 736 // CheckIn could be called more than once when: 737 // 1) The password changes. 738 // 2) Register/send function calls it to ensure CheckIn is done. 739 if (username_ == username) 740 return; 741 username_ = username; 742 743 DCHECK(!delayed_task_controller_); 744 delayed_task_controller_.reset(new DelayedTaskController); 745 746 // This will load the data from the gcm store and trigger the check-in if 747 // the persisted check-in info is not found. 748 // Note that we need to pass weak pointer again since the existing weak 749 // pointer in IOWorker might have been invalidated when check-out occurs. 750 content::BrowserThread::PostTask( 751 content::BrowserThread::IO, 752 FROM_HERE, 753 base::Bind(&GCMProfileService::IOWorker::Load, 754 io_worker_, 755 weak_ptr_factory_.GetWeakPtr())); 756} 757 758void GCMProfileService::RemoveCachedData() { 759 // Remove all the queued tasks since they no longer make sense after 760 // GCM service is stopped. 761 weak_ptr_factory_.InvalidateWeakPtrs(); 762 763 username_.clear(); 764 gcm_client_ready_ = false; 765 delayed_task_controller_.reset(); 766 register_callbacks_.clear(); 767 send_callbacks_.clear(); 768} 769 770void GCMProfileService::CheckOut() { 771 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 772 773 // We still proceed with the check-out logic even if the check-in is not 774 // initiated in the current session. This will make sure that all the 775 // persisted data written previously will get purged. 776 777 RemoveCachedData(); 778 779 content::BrowserThread::PostTask( 780 content::BrowserThread::IO, 781 FROM_HERE, 782 base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_)); 783} 784 785void GCMProfileService::ResetGCMClient() { 786 content::BrowserThread::PostTask( 787 content::BrowserThread::IO, 788 FROM_HERE, 789 base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_)); 790} 791 792GCMClient::Result GCMProfileService::EnsureAppReady(const std::string& app_id) { 793 // Ensure that check-in has been done. 794 EnsureLoaded(); 795 796 // If the profile was not signed in, bail out. 797 if (username_.empty()) 798 return GCMClient::NOT_SIGNED_IN; 799 800 return GCMClient::SUCCESS; 801} 802 803bool GCMProfileService::IsAsyncOperationPending( 804 const std::string& app_id) const { 805 return register_callbacks_.find(app_id) != register_callbacks_.end() || 806 unregister_callbacks_.find(app_id) != unregister_callbacks_.end(); 807} 808 809void GCMProfileService::RegisterFinished(const std::string& app_id, 810 const std::string& registration_id, 811 GCMClient::Result result) { 812 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 813 814 std::map<std::string, RegisterCallback>::iterator callback_iter = 815 register_callbacks_.find(app_id); 816 if (callback_iter == register_callbacks_.end()) { 817 // The callback could have been removed when the app is uninstalled. 818 return; 819 } 820 821 RegisterCallback callback = callback_iter->second; 822 register_callbacks_.erase(callback_iter); 823 callback.Run(registration_id, result); 824} 825 826void GCMProfileService::UnregisterFinished(const std::string& app_id, 827 GCMClient::Result result) { 828 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 829 830 std::map<std::string, UnregisterCallback>::iterator callback_iter = 831 unregister_callbacks_.find(app_id); 832 if (callback_iter == unregister_callbacks_.end()) 833 return; 834 835 UnregisterCallback callback = callback_iter->second; 836 unregister_callbacks_.erase(callback_iter); 837 callback.Run(result); 838} 839 840void GCMProfileService::SendFinished(const std::string& app_id, 841 const std::string& message_id, 842 GCMClient::Result result) { 843 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 844 845 std::map<std::pair<std::string, std::string>, SendCallback>::iterator 846 callback_iter = send_callbacks_.find( 847 std::pair<std::string, std::string>(app_id, message_id)); 848 if (callback_iter == send_callbacks_.end()) { 849 // The callback could have been removed when the app is uninstalled. 850 return; 851 } 852 853 SendCallback callback = callback_iter->second; 854 send_callbacks_.erase(callback_iter); 855 callback.Run(message_id, result); 856} 857 858void GCMProfileService::MessageReceived(const std::string& app_id, 859 GCMClient::IncomingMessage message) { 860 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 861 862 // Drop the event if signed out. 863 if (username_.empty()) 864 return; 865 866 GetAppHandler(app_id)->OnMessage(app_id, message); 867} 868 869void GCMProfileService::MessagesDeleted(const std::string& app_id) { 870 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 871 872 // Drop the event if signed out. 873 if (username_.empty()) 874 return; 875 876 GetAppHandler(app_id)->OnMessagesDeleted(app_id); 877} 878 879void GCMProfileService::MessageSendError( 880 const std::string& app_id, 881 const GCMClient::SendErrorDetails& send_error_details) { 882 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 883 884 // Drop the event if signed out. 885 if (username_.empty()) 886 return; 887 888 GetAppHandler(app_id)->OnSendError(app_id, send_error_details); 889} 890 891void GCMProfileService::GCMClientReady() { 892 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 893 894 if (gcm_client_ready_) 895 return; 896 gcm_client_ready_ = true; 897 898 delayed_task_controller_->SetReady(); 899} 900 901GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) { 902 std::map<std::string, GCMAppHandler*>::const_iterator iter = 903 app_handlers_.find(app_id); 904 return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; 905} 906 907void GCMProfileService::RequestGCMStatisticsFinished( 908 GCMClient::GCMStatistics stats) { 909 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 910 911 request_gcm_statistics_callback_.Run(stats); 912} 913 914} // namespace gcm 915