gcm_profile_service.cc revision 0529e5d033099cbfc42635f6f6183833b09dff6e
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 GetGCMStatistics(bool clear_logs); 180 void SetGCMRecording(bool recording); 181 182 // For testing purpose. Can be called from UI thread. Use with care. 183 GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); } 184 185 private: 186 friend class base::RefCountedThreadSafe<IOWorker>; 187 virtual ~IOWorker(); 188 189 base::WeakPtr<GCMProfileService> service_; 190 191 scoped_ptr<GCMClient> gcm_client_; 192}; 193 194GCMProfileService::IOWorker::IOWorker() { 195 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 196} 197 198GCMProfileService::IOWorker::~IOWorker() { 199} 200 201void GCMProfileService::IOWorker::Initialize( 202 scoped_ptr<GCMClientFactory> gcm_client_factory, 203 const base::FilePath& store_path, 204 const std::vector<std::string>& account_ids, 205 const scoped_refptr<net::URLRequestContextGetter>& 206 url_request_context_getter) { 207 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 208 209 gcm_client_ = gcm_client_factory->BuildInstance().Pass(); 210 211 checkin_proto::ChromeBuildProto chrome_build_proto; 212 chrome_build_proto.set_platform(GetPlatform()); 213 chrome_build_proto.set_chrome_version(GetVersion()); 214 chrome_build_proto.set_channel(GetChannel()); 215 216 scoped_refptr<base::SequencedWorkerPool> worker_pool( 217 content::BrowserThread::GetBlockingPool()); 218 scoped_refptr<base::SequencedTaskRunner> blocking_task_runner( 219 worker_pool->GetSequencedTaskRunnerWithShutdownBehavior( 220 worker_pool->GetSequenceToken(), 221 base::SequencedWorkerPool::SKIP_ON_SHUTDOWN)); 222 223 gcm_client_->Initialize(chrome_build_proto, 224 store_path, 225 account_ids, 226 blocking_task_runner, 227 url_request_context_getter, 228 this); 229} 230 231void GCMProfileService::IOWorker::Reset() { 232 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 233 234 // GCMClient instance must be destroyed from the same thread where it was 235 // created. 236 gcm_client_.reset(); 237} 238 239void GCMProfileService::IOWorker::OnRegisterFinished( 240 const std::string& app_id, 241 const std::string& registration_id, 242 GCMClient::Result result) { 243 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 244 245 content::BrowserThread::PostTask( 246 content::BrowserThread::UI, 247 FROM_HERE, 248 base::Bind(&GCMProfileService::RegisterFinished, 249 service_, 250 app_id, 251 registration_id, 252 result)); 253} 254 255void GCMProfileService::IOWorker::OnUnregisterFinished( 256 const std::string& app_id, 257 GCMClient::Result result) { 258 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 259 260 content::BrowserThread::PostTask( 261 content::BrowserThread::UI, 262 FROM_HERE, 263 base::Bind( 264 &GCMProfileService::UnregisterFinished, service_, app_id, result)); 265} 266 267void GCMProfileService::IOWorker::OnSendFinished( 268 const std::string& app_id, 269 const std::string& message_id, 270 GCMClient::Result result) { 271 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 272 273 content::BrowserThread::PostTask( 274 content::BrowserThread::UI, 275 FROM_HERE, 276 base::Bind(&GCMProfileService::SendFinished, 277 service_, 278 app_id, 279 message_id, 280 result)); 281} 282 283void GCMProfileService::IOWorker::OnMessageReceived( 284 const std::string& app_id, 285 const GCMClient::IncomingMessage& message) { 286 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 287 288 content::BrowserThread::PostTask( 289 content::BrowserThread::UI, 290 FROM_HERE, 291 base::Bind(&GCMProfileService::MessageReceived, 292 service_, 293 app_id, 294 message)); 295} 296 297void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) { 298 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 299 300 content::BrowserThread::PostTask( 301 content::BrowserThread::UI, 302 FROM_HERE, 303 base::Bind(&GCMProfileService::MessagesDeleted, 304 service_, 305 app_id)); 306} 307 308void GCMProfileService::IOWorker::OnMessageSendError( 309 const std::string& app_id, 310 const GCMClient::SendErrorDetails& send_error_details) { 311 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 312 313 content::BrowserThread::PostTask( 314 content::BrowserThread::UI, 315 FROM_HERE, 316 base::Bind(&GCMProfileService::MessageSendError, 317 service_, 318 app_id, 319 send_error_details)); 320} 321 322void GCMProfileService::IOWorker::OnGCMReady() { 323 content::BrowserThread::PostTask( 324 content::BrowserThread::UI, 325 FROM_HERE, 326 base::Bind(&GCMProfileService::GCMClientReady, 327 service_)); 328} 329 330void GCMProfileService::IOWorker::Load( 331 const base::WeakPtr<GCMProfileService>& service) { 332 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 333 334 service_ = service; 335 gcm_client_->Load(); 336} 337 338void GCMProfileService::IOWorker::Stop() { 339 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 340 341 gcm_client_->Stop(); 342} 343 344void GCMProfileService::IOWorker::CheckOut() { 345 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 346 347 gcm_client_->CheckOut(); 348 349 // Note that we still need to keep GCMClient instance alive since the profile 350 // might be signed in again. 351} 352 353void GCMProfileService::IOWorker::Register( 354 const std::string& app_id, 355 const std::vector<std::string>& sender_ids) { 356 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 357 358 gcm_client_->Register(app_id, sender_ids); 359} 360 361void GCMProfileService::IOWorker::Unregister(const std::string& app_id) { 362 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 363 364 gcm_client_->Unregister(app_id); 365} 366 367void GCMProfileService::IOWorker::Send( 368 const std::string& app_id, 369 const std::string& receiver_id, 370 const GCMClient::OutgoingMessage& message) { 371 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 372 373 gcm_client_->Send(app_id, receiver_id, message); 374} 375 376void GCMProfileService::IOWorker::GetGCMStatistics(bool clear_logs) { 377 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 378 gcm::GCMClient::GCMStatistics stats; 379 380 if (gcm_client_.get()) { 381 if (clear_logs) 382 gcm_client_->ClearActivityLogs(); 383 stats = gcm_client_->GetStatistics(); 384 } 385 386 content::BrowserThread::PostTask( 387 content::BrowserThread::UI, 388 FROM_HERE, 389 base::Bind(&GCMProfileService::GetGCMStatisticsFinished, 390 service_, 391 stats)); 392} 393 394void GCMProfileService::IOWorker::SetGCMRecording(bool recording) { 395 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO)); 396 gcm::GCMClient::GCMStatistics stats; 397 398 if (gcm_client_.get()) { 399 gcm_client_->SetRecording(recording); 400 stats = gcm_client_->GetStatistics(); 401 stats.gcm_client_created = true; 402 } 403 404 content::BrowserThread::PostTask( 405 content::BrowserThread::UI, 406 FROM_HERE, 407 base::Bind(&GCMProfileService::GetGCMStatisticsFinished, 408 service_, 409 stats)); 410} 411 412std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) { 413 switch (state) { 414 case GCMProfileService::ALWAYS_ENABLED: 415 return "ALWAYS_ENABLED"; 416 case GCMProfileService::ENABLED_FOR_APPS: 417 return "ENABLED_FOR_APPS"; 418 case GCMProfileService::ALWAYS_DISABLED: 419 return "ALWAYS_DISABLED"; 420 default: 421 NOTREACHED(); 422 return std::string(); 423 } 424} 425 426// static 427GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState( 428 Profile* profile) { 429 const base::Value* gcm_enabled_value = 430 profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled); 431 if (!gcm_enabled_value) 432 return ENABLED_FOR_APPS; 433 434 bool gcm_enabled = false; 435 if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled)) 436 return ENABLED_FOR_APPS; 437 438 return gcm_enabled ? ALWAYS_ENABLED : ALWAYS_DISABLED; 439} 440 441// static 442void GCMProfileService::RegisterProfilePrefs( 443 user_prefs::PrefRegistrySyncable* registry) { 444 // GCM support is only enabled by default for Canary/Dev/Custom builds. 445 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel(); 446 bool on_by_default = false; 447 if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN || 448 channel == chrome::VersionInfo::CHANNEL_CANARY || 449 channel == chrome::VersionInfo::CHANNEL_DEV) { 450 on_by_default = true; 451 } 452 registry->RegisterBooleanPref( 453 prefs::kGCMChannelEnabled, 454 on_by_default, 455 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF); 456} 457 458GCMProfileService::GCMProfileService(Profile* profile) 459 : profile_(profile), 460 gcm_client_ready_(false), 461 weak_ptr_factory_(this) { 462 DCHECK(!profile->IsOffTheRecord()); 463} 464 465GCMProfileService::~GCMProfileService() { 466} 467 468void GCMProfileService::Initialize( 469 scoped_ptr<GCMClientFactory> gcm_client_factory) { 470 registrar_.Add(this, 471 chrome::NOTIFICATION_PROFILE_DESTROYED, 472 content::Source<Profile>(profile_)); 473 474 SigninManagerFactory::GetForProfile(profile_)->AddObserver(this); 475 476 // Get the list of available accounts. 477 std::vector<std::string> account_ids; 478#if !defined(OS_ANDROID) 479 account_ids = 480 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts(); 481#endif 482 483 // Create and initialize the GCMClient. Note that this does not initiate the 484 // GCM check-in. 485 io_worker_ = new IOWorker(); 486 scoped_refptr<net::URLRequestContextGetter> url_request_context_getter = 487 profile_->GetRequestContext(); 488 content::BrowserThread::PostTask( 489 content::BrowserThread::IO, 490 FROM_HERE, 491 base::Bind(&GCMProfileService::IOWorker::Initialize, 492 io_worker_, 493 base::Passed(&gcm_client_factory), 494 profile_->GetPath().Append(chrome::kGCMStoreDirname), 495 account_ids, 496 url_request_context_getter)); 497 498 // Load from the GCM store and initiate the GCM check-in if the rollout signal 499 // indicates yes. 500 if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED) 501 EnsureLoaded(); 502} 503 504void GCMProfileService::Start() { 505 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 506 507 EnsureLoaded(); 508} 509 510void GCMProfileService::Stop() { 511 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 512 513 // No need to stop GCM service if not started yet. 514 if (username_.empty()) 515 return; 516 517 RemoveCachedData(); 518 519 content::BrowserThread::PostTask( 520 content::BrowserThread::IO, 521 FROM_HERE, 522 base::Bind(&GCMProfileService::IOWorker::Stop, io_worker_)); 523} 524 525void GCMProfileService::Shutdown() { 526 for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin(); 527 iter != app_handlers_.end(); ++iter) { 528 iter->second->ShutdownHandler(); 529 } 530 app_handlers_.clear(); 531 532 SigninManagerFactory::GetForProfile(profile_)->RemoveObserver(this); 533} 534 535void GCMProfileService::AddAppHandler(const std::string& app_id, 536 GCMAppHandler* handler) { 537 DCHECK(!app_id.empty()); 538 DCHECK(handler); 539 DCHECK(app_handlers_.find(app_id) == app_handlers_.end()); 540 541 app_handlers_[app_id] = handler; 542} 543 544void GCMProfileService::RemoveAppHandler(const std::string& app_id) { 545 DCHECK(!app_id.empty()); 546 547 app_handlers_.erase(app_id); 548} 549 550void GCMProfileService::Register(const std::string& app_id, 551 const std::vector<std::string>& sender_ids, 552 RegisterCallback callback) { 553 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 554 DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null()); 555 556 GCMClient::Result result = EnsureAppReady(app_id); 557 if (result != GCMClient::SUCCESS) { 558 callback.Run(std::string(), result); 559 return; 560 } 561 562 // If previous un/register operation is still in progress, bail out. 563 if (IsAsyncOperationPending(app_id)) { 564 callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING); 565 return; 566 } 567 568 register_callbacks_[app_id] = callback; 569 570 // Delay the register operation until GCMClient is ready. 571 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { 572 delayed_task_controller_->AddTask( 573 base::Bind(&GCMProfileService::DoRegister, 574 weak_ptr_factory_.GetWeakPtr(), 575 app_id, 576 sender_ids)); 577 return; 578 } 579 580 DoRegister(app_id, sender_ids); 581} 582 583void GCMProfileService::DoRegister(const std::string& app_id, 584 const std::vector<std::string>& sender_ids) { 585 std::map<std::string, RegisterCallback>::iterator callback_iter = 586 register_callbacks_.find(app_id); 587 if (callback_iter == register_callbacks_.end()) { 588 // The callback could have been removed when the app is uninstalled. 589 return; 590 } 591 592 // Normalize the sender IDs by making them sorted. 593 std::vector<std::string> normalized_sender_ids = sender_ids; 594 std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end()); 595 596 content::BrowserThread::PostTask( 597 content::BrowserThread::IO, 598 FROM_HERE, 599 base::Bind(&GCMProfileService::IOWorker::Register, 600 io_worker_, 601 app_id, 602 normalized_sender_ids)); 603} 604 605void GCMProfileService::Unregister(const std::string& app_id, 606 UnregisterCallback callback) { 607 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 608 DCHECK(!app_id.empty() && !callback.is_null()); 609 610 GCMClient::Result result = EnsureAppReady(app_id); 611 if (result != GCMClient::SUCCESS) { 612 callback.Run(result); 613 return; 614 } 615 616 // If previous un/register operation is still in progress, bail out. 617 if (IsAsyncOperationPending(app_id)) { 618 callback.Run(GCMClient::ASYNC_OPERATION_PENDING); 619 return; 620 } 621 622 unregister_callbacks_[app_id] = callback; 623 624 // Delay the unregister operation until GCMClient is ready. 625 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { 626 delayed_task_controller_->AddTask( 627 base::Bind(&GCMProfileService::DoUnregister, 628 weak_ptr_factory_.GetWeakPtr(), 629 app_id)); 630 return; 631 } 632 633 DoUnregister(app_id); 634} 635 636void GCMProfileService::DoUnregister(const std::string& app_id) { 637 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 638 639 // Ask the server to unregister it. There could be a small chance that the 640 // unregister request fails. If this occurs, it does not bring any harm since 641 // we simply reject the messages/events received from the server. 642 content::BrowserThread::PostTask( 643 content::BrowserThread::IO, 644 FROM_HERE, 645 base::Bind(&GCMProfileService::IOWorker::Unregister, 646 io_worker_, 647 app_id)); 648} 649 650void GCMProfileService::Send(const std::string& app_id, 651 const std::string& receiver_id, 652 const GCMClient::OutgoingMessage& message, 653 SendCallback callback) { 654 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 655 DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null()); 656 657 GCMClient::Result result = EnsureAppReady(app_id); 658 if (result != GCMClient::SUCCESS) { 659 callback.Run(std::string(), result); 660 return; 661 } 662 663 // If the message with send ID is still in progress, bail out. 664 std::pair<std::string, std::string> key(app_id, message.id); 665 if (send_callbacks_.find(key) != send_callbacks_.end()) { 666 callback.Run(message.id, GCMClient::INVALID_PARAMETER); 667 return; 668 } 669 670 send_callbacks_[key] = callback; 671 672 // Delay the send operation until all GCMClient is ready. 673 if (!delayed_task_controller_->CanRunTaskWithoutDelay()) { 674 delayed_task_controller_->AddTask( 675 base::Bind(&GCMProfileService::DoSend, 676 weak_ptr_factory_.GetWeakPtr(), 677 app_id, 678 receiver_id, 679 message)); 680 return; 681 } 682 683 DoSend(app_id, receiver_id, message); 684} 685 686void GCMProfileService::DoSend(const std::string& app_id, 687 const std::string& receiver_id, 688 const GCMClient::OutgoingMessage& message) { 689 content::BrowserThread::PostTask( 690 content::BrowserThread::IO, 691 FROM_HERE, 692 base::Bind(&GCMProfileService::IOWorker::Send, 693 io_worker_, 694 app_id, 695 receiver_id, 696 message)); 697} 698 699GCMClient* GCMProfileService::GetGCMClientForTesting() const { 700 return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL; 701} 702 703std::string GCMProfileService::SignedInUserName() const { 704 return username_; 705} 706 707bool GCMProfileService::IsGCMClientReady() const { 708 return gcm_client_ready_; 709} 710 711void GCMProfileService::GetGCMStatistics( 712 GetGCMStatisticsCallback callback, bool clear_logs) { 713 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 714 DCHECK(!callback.is_null()); 715 716 request_gcm_statistics_callback_ = callback; 717 content::BrowserThread::PostTask( 718 content::BrowserThread::IO, 719 FROM_HERE, 720 base::Bind(&GCMProfileService::IOWorker::GetGCMStatistics, 721 io_worker_, 722 clear_logs)); 723} 724 725void GCMProfileService::SetGCMRecording( 726 GetGCMStatisticsCallback callback, bool recording) { 727 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 728 729 request_gcm_statistics_callback_ = callback; 730 content::BrowserThread::PostTask( 731 content::BrowserThread::IO, 732 FROM_HERE, 733 base::Bind(&GCMProfileService::IOWorker::SetGCMRecording, 734 io_worker_, 735 recording)); 736} 737 738void GCMProfileService::Observe(int type, 739 const content::NotificationSource& source, 740 const content::NotificationDetails& details) { 741 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 742 743 switch (type) { 744 case chrome::NOTIFICATION_PROFILE_DESTROYED: 745 ResetGCMClient(); 746 break; 747 default: 748 NOTREACHED(); 749 } 750} 751 752void GCMProfileService::GoogleSigninSucceeded(const std::string& username, 753 const std::string& password) { 754 if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED) 755 EnsureLoaded(); 756} 757 758void GCMProfileService::GoogleSignedOut(const std::string& username) { 759 CheckOut(); 760} 761 762void GCMProfileService::EnsureLoaded() { 763 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_); 764 if (!manager) 765 return; 766 std::string username = manager->GetAuthenticatedUsername(); 767 if (username.empty()) 768 return; 769 770 // CheckIn could be called more than once when: 771 // 1) The password changes. 772 // 2) Register/send function calls it to ensure CheckIn is done. 773 if (username_ == username) 774 return; 775 username_ = username; 776 777 DCHECK(!delayed_task_controller_); 778 delayed_task_controller_.reset(new DelayedTaskController); 779 780 // This will load the data from the gcm store and trigger the check-in if 781 // the persisted check-in info is not found. 782 // Note that we need to pass weak pointer again since the existing weak 783 // pointer in IOWorker might have been invalidated when check-out occurs. 784 content::BrowserThread::PostTask( 785 content::BrowserThread::IO, 786 FROM_HERE, 787 base::Bind(&GCMProfileService::IOWorker::Load, 788 io_worker_, 789 weak_ptr_factory_.GetWeakPtr())); 790} 791 792void GCMProfileService::RemoveCachedData() { 793 // Remove all the queued tasks since they no longer make sense after 794 // GCM service is stopped. 795 weak_ptr_factory_.InvalidateWeakPtrs(); 796 797 username_.clear(); 798 gcm_client_ready_ = false; 799 delayed_task_controller_.reset(); 800 register_callbacks_.clear(); 801 send_callbacks_.clear(); 802} 803 804void GCMProfileService::CheckOut() { 805 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 806 807 // We still proceed with the check-out logic even if the check-in is not 808 // initiated in the current session. This will make sure that all the 809 // persisted data written previously will get purged. 810 811 RemoveCachedData(); 812 813 content::BrowserThread::PostTask( 814 content::BrowserThread::IO, 815 FROM_HERE, 816 base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_)); 817} 818 819void GCMProfileService::ResetGCMClient() { 820 content::BrowserThread::PostTask( 821 content::BrowserThread::IO, 822 FROM_HERE, 823 base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_)); 824} 825 826GCMClient::Result GCMProfileService::EnsureAppReady(const std::string& app_id) { 827 // Ensure that check-in has been done. 828 EnsureLoaded(); 829 830 // If the profile was not signed in, bail out. 831 if (username_.empty()) 832 return GCMClient::NOT_SIGNED_IN; 833 834 return GCMClient::SUCCESS; 835} 836 837bool GCMProfileService::IsAsyncOperationPending( 838 const std::string& app_id) const { 839 return register_callbacks_.find(app_id) != register_callbacks_.end() || 840 unregister_callbacks_.find(app_id) != unregister_callbacks_.end(); 841} 842 843void GCMProfileService::RegisterFinished(const std::string& app_id, 844 const std::string& registration_id, 845 GCMClient::Result result) { 846 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 847 848 std::map<std::string, RegisterCallback>::iterator callback_iter = 849 register_callbacks_.find(app_id); 850 if (callback_iter == register_callbacks_.end()) { 851 // The callback could have been removed when the app is uninstalled. 852 return; 853 } 854 855 RegisterCallback callback = callback_iter->second; 856 register_callbacks_.erase(callback_iter); 857 callback.Run(registration_id, result); 858} 859 860void GCMProfileService::UnregisterFinished(const std::string& app_id, 861 GCMClient::Result result) { 862 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 863 864 std::map<std::string, UnregisterCallback>::iterator callback_iter = 865 unregister_callbacks_.find(app_id); 866 if (callback_iter == unregister_callbacks_.end()) 867 return; 868 869 UnregisterCallback callback = callback_iter->second; 870 unregister_callbacks_.erase(callback_iter); 871 callback.Run(result); 872} 873 874void GCMProfileService::SendFinished(const std::string& app_id, 875 const std::string& message_id, 876 GCMClient::Result result) { 877 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 878 879 std::map<std::pair<std::string, std::string>, SendCallback>::iterator 880 callback_iter = send_callbacks_.find( 881 std::pair<std::string, std::string>(app_id, message_id)); 882 if (callback_iter == send_callbacks_.end()) { 883 // The callback could have been removed when the app is uninstalled. 884 return; 885 } 886 887 SendCallback callback = callback_iter->second; 888 send_callbacks_.erase(callback_iter); 889 callback.Run(message_id, result); 890} 891 892void GCMProfileService::MessageReceived(const std::string& app_id, 893 GCMClient::IncomingMessage message) { 894 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 895 896 // Drop the event if signed out. 897 if (username_.empty()) 898 return; 899 900 GetAppHandler(app_id)->OnMessage(app_id, message); 901} 902 903void GCMProfileService::MessagesDeleted(const std::string& app_id) { 904 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 905 906 // Drop the event if signed out. 907 if (username_.empty()) 908 return; 909 910 GetAppHandler(app_id)->OnMessagesDeleted(app_id); 911} 912 913void GCMProfileService::MessageSendError( 914 const std::string& app_id, 915 const GCMClient::SendErrorDetails& send_error_details) { 916 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 917 918 // Drop the event if signed out. 919 if (username_.empty()) 920 return; 921 922 GetAppHandler(app_id)->OnSendError(app_id, send_error_details); 923} 924 925void GCMProfileService::GCMClientReady() { 926 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 927 928 if (gcm_client_ready_) 929 return; 930 gcm_client_ready_ = true; 931 932 delayed_task_controller_->SetReady(); 933} 934 935GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) { 936 std::map<std::string, GCMAppHandler*>::const_iterator iter = 937 app_handlers_.find(app_id); 938 return iter == app_handlers_.end() ? &default_app_handler_ : iter->second; 939} 940 941void GCMProfileService::GetGCMStatisticsFinished( 942 GCMClient::GCMStatistics stats) { 943 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 944 request_gcm_statistics_callback_.Run(stats); 945} 946 947} // namespace gcm 948