summaryrefslogtreecommitdiffstats
path: root/chrome/browser/services/gcm/gcm_service.cc
diff options
context:
space:
mode:
Diffstat (limited to 'chrome/browser/services/gcm/gcm_service.cc')
-rw-r--r--chrome/browser/services/gcm/gcm_service.cc850
1 files changed, 850 insertions, 0 deletions
diff --git a/chrome/browser/services/gcm/gcm_service.cc b/chrome/browser/services/gcm/gcm_service.cc
new file mode 100644
index 0000000..0b5c74d
--- /dev/null
+++ b/chrome/browser/services/gcm/gcm_service.cc
@@ -0,0 +1,850 @@
+// Copyright 2014 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/browser/services/gcm/gcm_service.h"
+
+#include <algorithm>
+#include <utility>
+
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/location.h"
+#include "base/logging.h"
+#include "base/sequenced_task_runner.h"
+#include "base/threading/sequenced_worker_pool.h"
+#include "chrome/browser/services/gcm/gcm_app_handler.h"
+#include "chrome/browser/services/gcm/gcm_client_factory.h"
+#include "chrome/common/chrome_version_info.h"
+#include "content/public/browser/browser_thread.h"
+#include "google_apis/gaia/oauth2_token_service.h"
+#include "google_apis/gcm/protocol/android_checkin.pb.h"
+#include "net/url_request/url_request_context_getter.h"
+
+namespace gcm {
+
+namespace {
+
+checkin_proto::ChromeBuildProto_Platform GetPlatform() {
+#if defined(OS_WIN)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
+#elif defined(OS_MACOSX)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
+#elif defined(OS_IOS)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
+#elif defined(OS_CHROMEOS)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
+#elif defined(OS_LINUX)
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#else
+ // For all other platforms, return as LINUX.
+ return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
+#endif
+}
+
+std::string GetVersion() {
+ chrome::VersionInfo version_info;
+ return version_info.Version();
+}
+
+checkin_proto::ChromeBuildProto_Channel GetChannel() {
+ chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
+ switch (channel) {
+ case chrome::VersionInfo::CHANNEL_UNKNOWN:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
+ case chrome::VersionInfo::CHANNEL_CANARY:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
+ case chrome::VersionInfo::CHANNEL_DEV:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
+ case chrome::VersionInfo::CHANNEL_BETA:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
+ case chrome::VersionInfo::CHANNEL_STABLE:
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
+ default:
+ NOTREACHED();
+ return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
+ };
+}
+
+} // namespace
+
+// Helper class to save tasks to run until we're ready to execute them.
+class GCMService::DelayedTaskController {
+ public:
+ DelayedTaskController();
+ ~DelayedTaskController();
+
+ // Adds a task that will be invoked once we're ready.
+ void AddTask(const base::Closure& task);
+
+ // Sets ready status. It is ready only when check-in is completed and
+ // the GCMClient is fully initialized.
+ void SetReady();
+
+ // Returns true if it is ready to perform tasks.
+ bool CanRunTaskWithoutDelay() const;
+
+ private:
+ void RunTasks();
+
+ // Flag that indicates that GCM is ready.
+ bool ready_;
+
+ std::vector<base::Closure> delayed_tasks_;
+
+ DISALLOW_COPY_AND_ASSIGN(DelayedTaskController);
+};
+
+GCMService::DelayedTaskController::DelayedTaskController() : ready_(false) {
+}
+
+GCMService::DelayedTaskController::~DelayedTaskController() {
+}
+
+void GCMService::DelayedTaskController::AddTask(const base::Closure& task) {
+ delayed_tasks_.push_back(task);
+}
+
+void GCMService::DelayedTaskController::SetReady() {
+ ready_ = true;
+ RunTasks();
+}
+
+bool GCMService::DelayedTaskController::CanRunTaskWithoutDelay() const {
+ return ready_;
+}
+
+void GCMService::DelayedTaskController::RunTasks() {
+ DCHECK(ready_);
+
+ for (size_t i = 0; i < delayed_tasks_.size(); ++i)
+ delayed_tasks_[i].Run();
+ delayed_tasks_.clear();
+}
+
+class GCMService::IOWorker : public GCMClient::Delegate {
+ public:
+ // Called on UI thread.
+ IOWorker();
+ virtual ~IOWorker();
+
+ // Overridden from GCMClient::Delegate:
+ // Called on IO thread.
+ virtual void OnRegisterFinished(const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) OVERRIDE;
+ virtual void OnUnregisterFinished(const std::string& app_id,
+ GCMClient::Result result) OVERRIDE;
+ virtual void OnSendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) OVERRIDE;
+ virtual void OnMessageReceived(
+ const std::string& app_id,
+ const GCMClient::IncomingMessage& message) OVERRIDE;
+ virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
+ virtual void OnMessageSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
+ virtual void OnGCMReady() OVERRIDE;
+
+ // Called on IO thread.
+ void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory,
+ const base::FilePath& store_path,
+ const std::vector<std::string>& account_ids,
+ const scoped_refptr<net::URLRequestContextGetter>&
+ url_request_context_getter);
+ void Load(const base::WeakPtr<GCMService>& service);
+ void Stop();
+ void CheckOut();
+ void Register(const std::string& app_id,
+ const std::vector<std::string>& sender_ids);
+ void Unregister(const std::string& app_id);
+ void Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message);
+ void GetGCMStatistics(bool clear_logs);
+ void SetGCMRecording(bool recording);
+
+ // For testing purpose. Can be called from UI thread. Use with care.
+ GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); }
+
+ private:
+ base::WeakPtr<GCMService> service_;
+
+ scoped_ptr<GCMClient> gcm_client_;
+
+ DISALLOW_COPY_AND_ASSIGN(IOWorker);
+};
+
+GCMService::IOWorker::IOWorker() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+}
+
+GCMService::IOWorker::~IOWorker() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+}
+
+void GCMService::IOWorker::Initialize(
+ scoped_ptr<GCMClientFactory> gcm_client_factory,
+ const base::FilePath& store_path,
+ const std::vector<std::string>& account_ids,
+ const scoped_refptr<net::URLRequestContextGetter>&
+ url_request_context_getter) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_ = gcm_client_factory->BuildInstance().Pass();
+
+ checkin_proto::ChromeBuildProto chrome_build_proto;
+ chrome_build_proto.set_platform(GetPlatform());
+ chrome_build_proto.set_chrome_version(GetVersion());
+ chrome_build_proto.set_channel(GetChannel());
+
+ scoped_refptr<base::SequencedWorkerPool> worker_pool(
+ content::BrowserThread::GetBlockingPool());
+ scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
+ worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
+ worker_pool->GetSequenceToken(),
+ base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
+
+ gcm_client_->Initialize(chrome_build_proto,
+ store_path,
+ account_ids,
+ blocking_task_runner,
+ url_request_context_getter,
+ this);
+}
+
+void GCMService::IOWorker::OnRegisterFinished(
+ const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::RegisterFinished,
+ service_,
+ app_id,
+ registration_id,
+ result));
+}
+
+void GCMService::IOWorker::OnUnregisterFinished(const std::string& app_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::UnregisterFinished, service_, app_id, result));
+}
+
+void GCMService::IOWorker::OnSendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::SendFinished,
+ service_,
+ app_id,
+ message_id,
+ result));
+}
+
+void GCMService::IOWorker::OnMessageReceived(
+ const std::string& app_id,
+ const GCMClient::IncomingMessage& message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::MessageReceived,
+ service_,
+ app_id,
+ message));
+}
+
+void GCMService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::MessagesDeleted,
+ service_,
+ app_id));
+}
+
+void GCMService::IOWorker::OnMessageSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::MessageSendError,
+ service_,
+ app_id,
+ send_error_details));
+}
+
+void GCMService::IOWorker::OnGCMReady() {
+ content::BrowserThread::PostTask(content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::GCMClientReady,
+ service_));
+}
+
+void GCMService::IOWorker::Load(const base::WeakPtr<GCMService>& service) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ service_ = service;
+ gcm_client_->Load();
+}
+
+void GCMService::IOWorker::Stop() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Stop();
+}
+
+void GCMService::IOWorker::CheckOut() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->CheckOut();
+
+ // Note that we still need to keep GCMClient instance alive since the
+ // GCMService may check in again.
+}
+
+void GCMService::IOWorker::Register(
+ const std::string& app_id,
+ const std::vector<std::string>& sender_ids) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Register(app_id, sender_ids);
+}
+
+void GCMService::IOWorker::Unregister(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Unregister(app_id);
+}
+
+void GCMService::IOWorker::Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+
+ gcm_client_->Send(app_id, receiver_id, message);
+}
+
+void GCMService::IOWorker::GetGCMStatistics(bool clear_logs) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ gcm::GCMClient::GCMStatistics stats;
+
+ if (gcm_client_.get()) {
+ if (clear_logs)
+ gcm_client_->ClearActivityLogs();
+ stats = gcm_client_->GetStatistics();
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats));
+}
+
+void GCMService::IOWorker::SetGCMRecording(bool recording) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
+ gcm::GCMClient::GCMStatistics stats;
+
+ if (gcm_client_.get()) {
+ gcm_client_->SetRecording(recording);
+ stats = gcm_client_->GetStatistics();
+ stats.gcm_client_created = true;
+ }
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::UI,
+ FROM_HERE,
+ base::Bind(&GCMService::GetGCMStatisticsFinished, service_, stats));
+}
+
+GCMService::GCMService(scoped_ptr<IdentityProvider> identity_provider)
+ : identity_provider_(identity_provider.Pass()),
+ gcm_client_ready_(false),
+ weak_ptr_factory_(this) {
+}
+
+GCMService::~GCMService() {
+}
+
+void GCMService::Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory) {
+ // Get the list of available accounts.
+ std::vector<std::string> account_ids;
+#if !defined(OS_ANDROID)
+ account_ids = identity_provider_->GetTokenService()->GetAccounts();
+#endif
+
+ // Create and initialize the GCMClient. Note that this does not initiate the
+ // GCM check-in.
+ DCHECK(!io_worker_);
+ io_worker_.reset(new IOWorker());
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Initialize,
+ base::Unretained(io_worker_.get()),
+ base::Passed(&gcm_client_factory),
+ GetStorePath(),
+ account_ids,
+ GetURLRequestContextGetter()));
+
+ // Load from the GCM store and initiate the GCM check-in if the rollout signal
+ // indicates yes.
+ if (ShouldStartAutomatically())
+ EnsureLoaded();
+
+ identity_provider_->AddObserver(this);
+}
+
+void GCMService::Start() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ EnsureLoaded();
+}
+
+void GCMService::Stop() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // No need to stop GCM service if not started yet.
+ if (account_id_.empty())
+ return;
+
+ RemoveCachedData();
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Stop,
+ base::Unretained(io_worker_.get())));
+}
+
+void GCMService::ShutdownService() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ identity_provider_->RemoveObserver(this);
+ for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
+ iter != app_handlers_.end(); ++iter) {
+ iter->second->ShutdownHandler();
+ }
+ app_handlers_.clear();
+ content::BrowserThread::DeleteSoon(content::BrowserThread::IO,
+ FROM_HERE,
+ io_worker_.release());
+}
+
+void GCMService::AddAppHandler(const std::string& app_id,
+ GCMAppHandler* handler) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(handler);
+ DCHECK(app_handlers_.find(app_id) == app_handlers_.end());
+
+ app_handlers_[app_id] = handler;
+}
+
+void GCMService::RemoveAppHandler(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+
+ app_handlers_.erase(app_id);
+}
+
+void GCMService::Register(const std::string& app_id,
+ const std::vector<std::string>& sender_ids,
+ RegisterCallback callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(!sender_ids.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureAppReady(app_id);
+ if (result != GCMClient::SUCCESS) {
+ callback.Run(std::string(), result);
+ return;
+ }
+
+ // If previous un/register operation is still in progress, bail out.
+ if (IsAsyncOperationPending(app_id)) {
+ callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
+ return;
+ }
+
+ register_callbacks_[app_id] = callback;
+
+ // Delay the register operation until GCMClient is ready.
+ if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
+ delayed_task_controller_->AddTask(base::Bind(&GCMService::DoRegister,
+ weak_ptr_factory_.GetWeakPtr(),
+ app_id,
+ sender_ids));
+ return;
+ }
+
+ DoRegister(app_id, sender_ids);
+}
+
+void GCMService::DoRegister(const std::string& app_id,
+ const std::vector<std::string>& sender_ids) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ std::map<std::string, RegisterCallback>::iterator callback_iter =
+ register_callbacks_.find(app_id);
+ if (callback_iter == register_callbacks_.end()) {
+ // The callback could have been removed when the app is uninstalled.
+ return;
+ }
+
+ // Normalize the sender IDs by making them sorted.
+ std::vector<std::string> normalized_sender_ids = sender_ids;
+ std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Register,
+ base::Unretained(io_worker_.get()),
+ app_id,
+ normalized_sender_ids));
+}
+
+void GCMService::Unregister(const std::string& app_id,
+ UnregisterCallback callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureAppReady(app_id);
+ if (result != GCMClient::SUCCESS) {
+ callback.Run(result);
+ return;
+ }
+
+ // If previous un/register operation is still in progress, bail out.
+ if (IsAsyncOperationPending(app_id)) {
+ callback.Run(GCMClient::ASYNC_OPERATION_PENDING);
+ return;
+ }
+
+ unregister_callbacks_[app_id] = callback;
+
+ // Delay the unregister operation until GCMClient is ready.
+ if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
+ delayed_task_controller_->AddTask(base::Bind(&GCMService::DoUnregister,
+ weak_ptr_factory_.GetWeakPtr(),
+ app_id));
+ return;
+ }
+
+ DoUnregister(app_id);
+}
+
+void GCMService::DoUnregister(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Ask the server to unregister it. There could be a small chance that the
+ // unregister request fails. If this occurs, it does not bring any harm since
+ // we simply reject the messages/events received from the server.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Unregister,
+ base::Unretained(io_worker_.get()),
+ app_id));
+}
+
+void GCMService::Send(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message,
+ SendCallback callback) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!app_id.empty());
+ DCHECK(!receiver_id.empty());
+ DCHECK(!callback.is_null());
+
+ GCMClient::Result result = EnsureAppReady(app_id);
+ if (result != GCMClient::SUCCESS) {
+ callback.Run(std::string(), result);
+ return;
+ }
+
+ // If the message with send ID is still in progress, bail out.
+ std::pair<std::string, std::string> key(app_id, message.id);
+ if (send_callbacks_.find(key) != send_callbacks_.end()) {
+ callback.Run(message.id, GCMClient::INVALID_PARAMETER);
+ return;
+ }
+
+ send_callbacks_[key] = callback;
+
+ // Delay the send operation until all GCMClient is ready.
+ if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
+ delayed_task_controller_->AddTask(base::Bind(&GCMService::DoSend,
+ weak_ptr_factory_.GetWeakPtr(),
+ app_id,
+ receiver_id,
+ message));
+ return;
+ }
+
+ DoSend(app_id, receiver_id, message);
+}
+
+void GCMService::DoSend(const std::string& app_id,
+ const std::string& receiver_id,
+ const GCMClient::OutgoingMessage& message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Send,
+ base::Unretained(io_worker_.get()),
+ app_id,
+ receiver_id,
+ message));
+}
+
+GCMClient* GCMService::GetGCMClientForTesting() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL;
+}
+
+bool GCMService::IsStarted() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return !account_id_.empty();
+}
+
+bool GCMService::IsGCMClientReady() const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return gcm_client_ready_;
+}
+
+void GCMService::GetGCMStatistics(GetGCMStatisticsCallback callback,
+ bool clear_logs) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ DCHECK(!callback.is_null());
+
+ request_gcm_statistics_callback_ = callback;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::GetGCMStatistics,
+ base::Unretained(io_worker_.get()),
+ clear_logs));
+}
+
+void GCMService::SetGCMRecording(GetGCMStatisticsCallback callback,
+ bool recording) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ request_gcm_statistics_callback_ = callback;
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::SetGCMRecording,
+ base::Unretained(io_worker_.get()),
+ recording));
+}
+
+void GCMService::OnActiveAccountLogin() {
+ if (ShouldStartAutomatically())
+ EnsureLoaded();
+}
+
+void GCMService::OnActiveAccountLogout() {
+ CheckOut();
+}
+
+void GCMService::EnsureLoaded() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ const std::string account_id = identity_provider_->GetActiveAccountId();
+ if (account_id.empty())
+ return;
+
+ // CheckIn could be called more than once when:
+ // 1) The password changes.
+ // 2) Register/send function calls it to ensure CheckIn is done.
+ if (account_id_ == account_id)
+ return;
+ account_id_ = account_id;
+
+ DCHECK(!delayed_task_controller_);
+ delayed_task_controller_.reset(new DelayedTaskController);
+
+ // This will load the data from the gcm store and trigger the check-in if
+ // the persisted check-in info is not found.
+ // Note that we need to pass weak pointer again since the existing weak
+ // pointer in IOWorker might have been invalidated when check-out occurs.
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::Load,
+ base::Unretained(io_worker_.get()),
+ weak_ptr_factory_.GetWeakPtr()));
+}
+
+void GCMService::RemoveCachedData() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // Remove all the queued tasks since they no longer make sense after
+ // GCM service is stopped.
+ weak_ptr_factory_.InvalidateWeakPtrs();
+
+ account_id_.clear();
+ gcm_client_ready_ = false;
+ delayed_task_controller_.reset();
+ register_callbacks_.clear();
+ send_callbacks_.clear();
+}
+
+void GCMService::CheckOut() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // We still proceed with the check-out logic even if the check-in is not
+ // initiated in the current session. This will make sure that all the
+ // persisted data written previously will get purged.
+
+ RemoveCachedData();
+
+ content::BrowserThread::PostTask(
+ content::BrowserThread::IO,
+ FROM_HERE,
+ base::Bind(&GCMService::IOWorker::CheckOut,
+ base::Unretained(io_worker_.get())));
+}
+
+GCMClient::Result GCMService::EnsureAppReady(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ // Ensure that check-in has been done.
+ EnsureLoaded();
+
+ // If the service was not started, bail out.
+ if (account_id_.empty())
+ return GCMClient::NOT_SIGNED_IN;
+
+ return GCMClient::SUCCESS;
+}
+
+bool GCMService::IsAsyncOperationPending(const std::string& app_id) const {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ return register_callbacks_.find(app_id) != register_callbacks_.end() ||
+ unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
+}
+
+void GCMService::RegisterFinished(const std::string& app_id,
+ const std::string& registration_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ std::map<std::string, RegisterCallback>::iterator callback_iter =
+ register_callbacks_.find(app_id);
+ if (callback_iter == register_callbacks_.end()) {
+ // The callback could have been removed when the app is uninstalled.
+ return;
+ }
+
+ RegisterCallback callback = callback_iter->second;
+ register_callbacks_.erase(callback_iter);
+ callback.Run(registration_id, result);
+}
+
+void GCMService::UnregisterFinished(const std::string& app_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ std::map<std::string, UnregisterCallback>::iterator callback_iter =
+ unregister_callbacks_.find(app_id);
+ if (callback_iter == unregister_callbacks_.end())
+ return;
+
+ UnregisterCallback callback = callback_iter->second;
+ unregister_callbacks_.erase(callback_iter);
+ callback.Run(result);
+}
+
+void GCMService::SendFinished(const std::string& app_id,
+ const std::string& message_id,
+ GCMClient::Result result) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ std::map<std::pair<std::string, std::string>, SendCallback>::iterator
+ callback_iter = send_callbacks_.find(
+ std::pair<std::string, std::string>(app_id, message_id));
+ if (callback_iter == send_callbacks_.end()) {
+ // The callback could have been removed when the app is uninstalled.
+ return;
+ }
+
+ SendCallback callback = callback_iter->second;
+ send_callbacks_.erase(callback_iter);
+ callback.Run(message_id, result);
+}
+
+void GCMService::MessageReceived(const std::string& app_id,
+ GCMClient::IncomingMessage message) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Drop the event if signed out.
+ if (account_id_.empty())
+ return;
+
+ GetAppHandler(app_id)->OnMessage(app_id, message);
+}
+
+void GCMService::MessagesDeleted(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Drop the event if signed out.
+ if (account_id_.empty())
+ return;
+
+ GetAppHandler(app_id)->OnMessagesDeleted(app_id);
+}
+
+void GCMService::MessageSendError(
+ const std::string& app_id,
+ const GCMClient::SendErrorDetails& send_error_details) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ // Drop the event if signed out.
+ if (account_id_.empty())
+ return;
+
+ GetAppHandler(app_id)->OnSendError(app_id, send_error_details);
+}
+
+void GCMService::GCMClientReady() {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ if (gcm_client_ready_)
+ return;
+ gcm_client_ready_ = true;
+
+ delayed_task_controller_->SetReady();
+}
+
+GCMAppHandler* GCMService::GetAppHandler(const std::string& app_id) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+
+ std::map<std::string, GCMAppHandler*>::const_iterator iter =
+ app_handlers_.find(app_id);
+ return iter == app_handlers_.end() ? &default_app_handler_ : iter->second;
+}
+
+void GCMService::GetGCMStatisticsFinished(
+ GCMClient::GCMStatistics stats) {
+ DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
+ request_gcm_statistics_callback_.Run(stats);
+}
+
+} // namespace gcm