| Index: google_apis/gcm/gcm_client_impl.cc
|
| diff --git a/google_apis/gcm/gcm_client_impl.cc b/google_apis/gcm/gcm_client_impl.cc
|
| deleted file mode 100644
|
| index b5a3ae6e5c15b4fed6a296193b57144d18429583..0000000000000000000000000000000000000000
|
| --- a/google_apis/gcm/gcm_client_impl.cc
|
| +++ /dev/null
|
| @@ -1,877 +0,0 @@
|
| -// Copyright 2013 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 "google_apis/gcm/gcm_client_impl.h"
|
| -
|
| -#include "base/bind.h"
|
| -#include "base/files/file_path.h"
|
| -#include "base/logging.h"
|
| -#include "base/memory/scoped_ptr.h"
|
| -#include "base/message_loop/message_loop.h"
|
| -#include "base/metrics/histogram.h"
|
| -#include "base/sequenced_task_runner.h"
|
| -#include "base/strings/string_number_conversions.h"
|
| -#include "base/strings/stringprintf.h"
|
| -#include "base/time/default_clock.h"
|
| -#include "google_apis/gcm/base/encryptor.h"
|
| -#include "google_apis/gcm/base/mcs_message.h"
|
| -#include "google_apis/gcm/base/mcs_util.h"
|
| -#include "google_apis/gcm/engine/checkin_request.h"
|
| -#include "google_apis/gcm/engine/connection_factory_impl.h"
|
| -#include "google_apis/gcm/engine/gcm_store_impl.h"
|
| -#include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
|
| -#include "google_apis/gcm/protocol/checkin.pb.h"
|
| -#include "google_apis/gcm/protocol/mcs.pb.h"
|
| -#include "net/http/http_network_session.h"
|
| -#include "net/url_request/url_request_context.h"
|
| -#include "url/gurl.h"
|
| -
|
| -namespace gcm {
|
| -
|
| -namespace {
|
| -
|
| -// Backoff policy. Shared across reconnection logic and checkin/(un)registration
|
| -// retries.
|
| -// Note: In order to ensure a minimum of 20 seconds between server errors (for
|
| -// server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
|
| -// TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
|
| -// themselves.
|
| -const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
|
| - // Number of initial errors (in sequence) to ignore before applying
|
| - // exponential back-off rules.
|
| - 0,
|
| -
|
| - // Initial delay for exponential back-off in ms.
|
| - 30 * 1000, // 30 seconds.
|
| -
|
| - // Factor by which the waiting time will be multiplied.
|
| - 2,
|
| -
|
| - // Fuzzing percentage. ex: 10% will spread requests randomly
|
| - // between 90%-100% of the calculated time.
|
| - 0.33, // 33%.
|
| -
|
| - // Maximum amount of time we are willing to delay our request in ms.
|
| - 10 * 60 * 1000, // 10 minutes.
|
| -
|
| - // Time to keep an entry from being discarded even when it
|
| - // has no significant state, -1 to never discard.
|
| - -1,
|
| -
|
| - // Don't use initial delay unless the last request was an error.
|
| - false,
|
| -};
|
| -
|
| -// Indicates a message type of the received message.
|
| -enum MessageType {
|
| - UNKNOWN, // Undetermined type.
|
| - DATA_MESSAGE, // Regular data message.
|
| - DELETED_MESSAGES, // Messages were deleted on the server.
|
| - SEND_ERROR, // Error sending a message.
|
| -};
|
| -
|
| -enum OutgoingMessageTTLCategory {
|
| - TTL_ZERO,
|
| - TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
|
| - TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
|
| - TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
|
| - TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
|
| - TTL_MORE_THAN_ONE_WEEK,
|
| - TTL_MAXIMUM,
|
| - // NOTE: always keep this entry at the end. Add new TTL category only
|
| - // immediately above this line. Make sure to update the corresponding
|
| - // histogram enum accordingly.
|
| - TTL_CATEGORY_COUNT
|
| -};
|
| -
|
| -const int kMaxRegistrationRetries = 5;
|
| -const char kMessageTypeDataMessage[] = "gcm";
|
| -const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
|
| -const char kMessageTypeKey[] = "message_type";
|
| -const char kMessageTypeSendErrorKey[] = "send_error";
|
| -const char kSendErrorMessageIdKey[] = "google.message_id";
|
| -const char kSendMessageFromValue[] = "gcm@chrome.com";
|
| -const int64 kDefaultUserSerialNumber = 0LL;
|
| -
|
| -GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
|
| - switch (status) {
|
| - case MCSClient::QUEUED:
|
| - return GCMClient::SUCCESS;
|
| - case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
|
| - return GCMClient::NETWORK_ERROR;
|
| - case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
|
| - return GCMClient::NETWORK_ERROR;
|
| - case MCSClient::MESSAGE_TOO_LARGE:
|
| - return GCMClient::INVALID_PARAMETER;
|
| - case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
|
| - return GCMClient::NETWORK_ERROR;
|
| - case MCSClient::TTL_EXCEEDED:
|
| - return GCMClient::NETWORK_ERROR;
|
| - case MCSClient::SENT:
|
| - default:
|
| - NOTREACHED();
|
| - break;
|
| - }
|
| - return GCMClientImpl::UNKNOWN_ERROR;
|
| -}
|
| -
|
| -void ToCheckinProtoVersion(
|
| - const GCMClient::ChromeBuildInfo& chrome_build_info,
|
| - checkin_proto::ChromeBuildProto* android_build_info) {
|
| - checkin_proto::ChromeBuildProto_Platform platform;
|
| - switch (chrome_build_info.platform) {
|
| - case GCMClient::PLATFORM_WIN:
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
|
| - break;
|
| - case GCMClient::PLATFORM_MAC:
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
|
| - break;
|
| - case GCMClient::PLATFORM_LINUX:
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
|
| - break;
|
| - case GCMClient::PLATFORM_IOS:
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
|
| - break;
|
| - case GCMClient::PLATFORM_ANDROID:
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
|
| - break;
|
| - case GCMClient::PLATFORM_CROS:
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
|
| - break;
|
| - case GCMClient::PLATFORM_UNKNOWN:
|
| - // For unknown platform, return as LINUX.
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
|
| - break;
|
| - }
|
| - android_build_info->set_platform(platform);
|
| -
|
| - checkin_proto::ChromeBuildProto_Channel channel;
|
| - switch (chrome_build_info.channel) {
|
| - case GCMClient::CHANNEL_STABLE:
|
| - channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
|
| - break;
|
| - case GCMClient::CHANNEL_BETA:
|
| - channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
|
| - break;
|
| - case GCMClient::CHANNEL_DEV:
|
| - channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
|
| - break;
|
| - case GCMClient::CHANNEL_CANARY:
|
| - channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
|
| - break;
|
| - case GCMClient::CHANNEL_UNKNOWN:
|
| - channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
|
| - break;
|
| - default:
|
| - NOTREACHED();
|
| - channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
|
| - break;
|
| - }
|
| - android_build_info->set_channel(channel);
|
| -
|
| - android_build_info->set_chrome_version(chrome_build_info.version);
|
| -}
|
| -
|
| -MessageType DecodeMessageType(const std::string& value) {
|
| - if (kMessageTypeDeletedMessagesKey == value)
|
| - return DELETED_MESSAGES;
|
| - if (kMessageTypeSendErrorKey == value)
|
| - return SEND_ERROR;
|
| - if (kMessageTypeDataMessage == value)
|
| - return DATA_MESSAGE;
|
| - return UNKNOWN;
|
| -}
|
| -
|
| -void RecordOutgoingMessageToUMA(
|
| - const gcm::GCMClient::OutgoingMessage& message) {
|
| - OutgoingMessageTTLCategory ttl_category;
|
| - if (message.time_to_live == 0)
|
| - ttl_category = TTL_ZERO;
|
| - else if (message.time_to_live <= 60 )
|
| - ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
|
| - else if (message.time_to_live <= 60 * 60)
|
| - ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
|
| - else if (message.time_to_live <= 24 * 60 * 60)
|
| - ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
|
| - else if (message.time_to_live <= 7 * 24 * 60 * 60)
|
| - ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
|
| - else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
|
| - ttl_category = TTL_MORE_THAN_ONE_WEEK;
|
| - else
|
| - ttl_category = TTL_MAXIMUM;
|
| -
|
| - UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
|
| - ttl_category,
|
| - TTL_CATEGORY_COUNT);
|
| -}
|
| -
|
| -} // namespace
|
| -
|
| -GCMInternalsBuilder::GCMInternalsBuilder() {}
|
| -GCMInternalsBuilder::~GCMInternalsBuilder() {}
|
| -
|
| -scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
|
| - return make_scoped_ptr<base::Clock>(new base::DefaultClock());
|
| -}
|
| -
|
| -scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
|
| - const std::string& version,
|
| - base::Clock* clock,
|
| - ConnectionFactory* connection_factory,
|
| - GCMStore* gcm_store,
|
| - GCMStatsRecorder* recorder) {
|
| - return make_scoped_ptr<MCSClient>(
|
| - new MCSClient(version,
|
| - clock,
|
| - connection_factory,
|
| - gcm_store,
|
| - recorder));
|
| -}
|
| -
|
| -scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
|
| - const std::vector<GURL>& endpoints,
|
| - const net::BackoffEntry::Policy& backoff_policy,
|
| - scoped_refptr<net::HttpNetworkSession> network_session,
|
| - net::NetLog* net_log,
|
| - GCMStatsRecorder* recorder) {
|
| - return make_scoped_ptr<ConnectionFactory>(
|
| - new ConnectionFactoryImpl(endpoints,
|
| - backoff_policy,
|
| - network_session,
|
| - net_log,
|
| - recorder));
|
| -}
|
| -
|
| -GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
|
| - : internals_builder_(internals_builder.Pass()),
|
| - state_(UNINITIALIZED),
|
| - clock_(internals_builder_->BuildClock()),
|
| - url_request_context_getter_(NULL),
|
| - pending_registration_requests_deleter_(&pending_registration_requests_),
|
| - pending_unregistration_requests_deleter_(
|
| - &pending_unregistration_requests_),
|
| - periodic_checkin_ptr_factory_(this),
|
| - weak_ptr_factory_(this) {
|
| -}
|
| -
|
| -GCMClientImpl::~GCMClientImpl() {
|
| -}
|
| -
|
| -void GCMClientImpl::Initialize(
|
| - const ChromeBuildInfo& chrome_build_info,
|
| - const base::FilePath& path,
|
| - const std::vector<std::string>& account_ids,
|
| - const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
|
| - const scoped_refptr<net::URLRequestContextGetter>&
|
| - url_request_context_getter,
|
| - scoped_ptr<Encryptor> encryptor,
|
| - GCMClient::Delegate* delegate) {
|
| - DCHECK_EQ(UNINITIALIZED, state_);
|
| - DCHECK(url_request_context_getter);
|
| - DCHECK(delegate);
|
| -
|
| - url_request_context_getter_ = url_request_context_getter;
|
| - const net::HttpNetworkSession::Params* network_session_params =
|
| - url_request_context_getter_->GetURLRequestContext()->
|
| - GetNetworkSessionParams();
|
| - DCHECK(network_session_params);
|
| - network_session_ = new net::HttpNetworkSession(*network_session_params);
|
| -
|
| - chrome_build_info_ = chrome_build_info;
|
| - account_ids_ = account_ids;
|
| -
|
| - gcm_store_.reset(
|
| - new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
|
| -
|
| - delegate_ = delegate;
|
| -
|
| - recorder_.SetDelegate(this);
|
| -
|
| - state_ = INITIALIZED;
|
| -}
|
| -
|
| -void GCMClientImpl::Start() {
|
| - DCHECK_EQ(INITIALIZED, state_);
|
| -
|
| - // Once the loading is completed, the check-in will be initiated.
|
| - gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - state_ = LOADING;
|
| -}
|
| -
|
| -void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
|
| - DCHECK_EQ(LOADING, state_);
|
| -
|
| - if (!result->success) {
|
| - ResetState();
|
| - return;
|
| - }
|
| -
|
| - registrations_ = result->registrations;
|
| - device_checkin_info_.android_id = result->device_android_id;
|
| - device_checkin_info_.secret = result->device_security_token;
|
| - last_checkin_time_ = result->last_checkin_time;
|
| - gservices_settings_.UpdateFromLoadResult(*result);
|
| - InitializeMCSClient(result.Pass());
|
| -
|
| - if (device_checkin_info_.IsValid()) {
|
| - SchedulePeriodicCheckin();
|
| - OnReady();
|
| - return;
|
| - }
|
| -
|
| - state_ = INITIAL_DEVICE_CHECKIN;
|
| - device_checkin_info_.Reset();
|
| - StartCheckin();
|
| -}
|
| -
|
| -void GCMClientImpl::InitializeMCSClient(
|
| - scoped_ptr<GCMStore::LoadResult> result) {
|
| - std::vector<GURL> endpoints;
|
| - endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
|
| - endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
|
| - connection_factory_ = internals_builder_->BuildConnectionFactory(
|
| - endpoints,
|
| - kDefaultBackoffPolicy,
|
| - network_session_,
|
| - net_log_.net_log(),
|
| - &recorder_);
|
| - mcs_client_ = internals_builder_->BuildMCSClient(
|
| - chrome_build_info_.version,
|
| - clock_.get(),
|
| - connection_factory_.get(),
|
| - gcm_store_.get(),
|
| - &recorder_).Pass();
|
| -
|
| - mcs_client_->Initialize(
|
| - base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
|
| - base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
|
| - weak_ptr_factory_.GetWeakPtr()),
|
| - base::Bind(&GCMClientImpl::OnMessageSentToMCS,
|
| - weak_ptr_factory_.GetWeakPtr()),
|
| - result.Pass());
|
| -}
|
| -
|
| -void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
|
| - const CheckinInfo& checkin_info) {
|
| - DCHECK(!device_checkin_info_.IsValid());
|
| -
|
| - device_checkin_info_.android_id = checkin_info.android_id;
|
| - device_checkin_info_.secret = checkin_info.secret;
|
| - gcm_store_->SetDeviceCredentials(
|
| - checkin_info.android_id, checkin_info.secret,
|
| - base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| -
|
| - OnReady();
|
| -}
|
| -
|
| -void GCMClientImpl::OnReady() {
|
| - state_ = READY;
|
| - StartMCSLogin();
|
| -
|
| - delegate_->OnGCMReady();
|
| -}
|
| -
|
| -void GCMClientImpl::StartMCSLogin() {
|
| - DCHECK_EQ(READY, state_);
|
| - DCHECK(device_checkin_info_.IsValid());
|
| - mcs_client_->Login(device_checkin_info_.android_id,
|
| - device_checkin_info_.secret);
|
| -}
|
| -
|
| -void GCMClientImpl::ResetState() {
|
| - state_ = UNINITIALIZED;
|
| - // TODO(fgorski): reset all of the necessart objects and start over.
|
| -}
|
| -
|
| -void GCMClientImpl::StartCheckin() {
|
| - // Make sure no checkin is in progress.
|
| - if (checkin_request_.get())
|
| - return;
|
| -
|
| - checkin_proto::ChromeBuildProto chrome_build_proto;
|
| - ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
|
| - CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
|
| - device_checkin_info_.secret,
|
| - gservices_settings_.digest(),
|
| - account_ids_,
|
| - chrome_build_proto);
|
| - checkin_request_.reset(
|
| - new CheckinRequest(gservices_settings_.GetCheckinURL(),
|
| - request_info,
|
| - kDefaultBackoffPolicy,
|
| - base::Bind(&GCMClientImpl::OnCheckinCompleted,
|
| - weak_ptr_factory_.GetWeakPtr()),
|
| - url_request_context_getter_,
|
| - &recorder_));
|
| - checkin_request_->Start();
|
| -}
|
| -
|
| -void GCMClientImpl::OnCheckinCompleted(
|
| - const checkin_proto::AndroidCheckinResponse& checkin_response) {
|
| - checkin_request_.reset();
|
| -
|
| - if (!checkin_response.has_android_id() ||
|
| - !checkin_response.has_security_token()) {
|
| - // TODO(fgorski): I don't think a retry here will help, we should probably
|
| - // start over. By checking in with (0, 0).
|
| - return;
|
| - }
|
| -
|
| - CheckinInfo checkin_info;
|
| - checkin_info.android_id = checkin_response.android_id();
|
| - checkin_info.secret = checkin_response.security_token();
|
| -
|
| - if (state_ == INITIAL_DEVICE_CHECKIN) {
|
| - OnFirstTimeDeviceCheckinCompleted(checkin_info);
|
| - } else {
|
| - // checkin_info is not expected to change after a periodic checkin as it
|
| - // would invalidate the registratoin IDs.
|
| - DCHECK_EQ(READY, state_);
|
| - DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
|
| - DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
|
| - }
|
| -
|
| - if (device_checkin_info_.IsValid()) {
|
| - // First update G-services settings, as something might have changed.
|
| - if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
|
| - gcm_store_->SetGServicesSettings(
|
| - gservices_settings_.settings_map(),
|
| - gservices_settings_.digest(),
|
| - base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - }
|
| -
|
| - last_checkin_time_ = clock_->Now();
|
| - gcm_store_->SetLastCheckinTime(
|
| - last_checkin_time_,
|
| - base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - SchedulePeriodicCheckin();
|
| - }
|
| -}
|
| -
|
| -void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
|
| - DCHECK(success);
|
| -}
|
| -
|
| -void GCMClientImpl::SchedulePeriodicCheckin() {
|
| - // Make sure no checkin is in progress.
|
| - if (checkin_request_.get())
|
| - return;
|
| -
|
| - // There should be only one periodic checkin pending at a time. Removing
|
| - // pending periodic checkin to schedule a new one.
|
| - periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
|
| -
|
| - base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
|
| - if (time_to_next_checkin < base::TimeDelta())
|
| - time_to_next_checkin = base::TimeDelta();
|
| -
|
| - base::MessageLoop::current()->PostDelayedTask(
|
| - FROM_HERE,
|
| - base::Bind(&GCMClientImpl::StartCheckin,
|
| - periodic_checkin_ptr_factory_.GetWeakPtr()),
|
| - time_to_next_checkin);
|
| -}
|
| -
|
| -base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
|
| - return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
|
| - clock_->Now();
|
| -}
|
| -
|
| -void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
|
| - // TODO(fgorski): This is one of the signals that store needs a rebuild.
|
| - DCHECK(success);
|
| -}
|
| -
|
| -void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
|
| - // TODO(fgorski): This is one of the signals that store needs a rebuild.
|
| - DCHECK(success);
|
| -}
|
| -
|
| -void GCMClientImpl::UpdateRegistrationCallback(bool success) {
|
| - // TODO(fgorski): This is one of the signals that store needs a rebuild.
|
| - DCHECK(success);
|
| -}
|
| -
|
| -void GCMClientImpl::Stop() {
|
| - device_checkin_info_.Reset();
|
| - connection_factory_.reset();
|
| - mcs_client_.reset();
|
| - checkin_request_.reset();
|
| - pending_registration_requests_.clear();
|
| - state_ = INITIALIZED;
|
| - gcm_store_->Close();
|
| -}
|
| -
|
| -void GCMClientImpl::CheckOut() {
|
| - Stop();
|
| - gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| -}
|
| -
|
| -void GCMClientImpl::Register(const std::string& app_id,
|
| - const std::vector<std::string>& sender_ids) {
|
| - DCHECK_EQ(state_, READY);
|
| -
|
| - // If the same sender ids is provided, return the cached registration ID
|
| - // directly.
|
| - RegistrationInfoMap::const_iterator registrations_iter =
|
| - registrations_.find(app_id);
|
| - if (registrations_iter != registrations_.end() &&
|
| - registrations_iter->second->sender_ids == sender_ids) {
|
| - delegate_->OnRegisterFinished(
|
| - app_id, registrations_iter->second->registration_id, SUCCESS);
|
| - return;
|
| - }
|
| -
|
| - RegistrationRequest::RequestInfo request_info(
|
| - device_checkin_info_.android_id,
|
| - device_checkin_info_.secret,
|
| - app_id,
|
| - sender_ids);
|
| - DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
|
| -
|
| - RegistrationRequest* registration_request =
|
| - new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
|
| - request_info,
|
| - kDefaultBackoffPolicy,
|
| - base::Bind(&GCMClientImpl::OnRegisterCompleted,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - app_id,
|
| - sender_ids),
|
| - kMaxRegistrationRetries,
|
| - url_request_context_getter_,
|
| - &recorder_);
|
| - pending_registration_requests_[app_id] = registration_request;
|
| - registration_request->Start();
|
| -}
|
| -
|
| -void GCMClientImpl::OnRegisterCompleted(
|
| - const std::string& app_id,
|
| - const std::vector<std::string>& sender_ids,
|
| - RegistrationRequest::Status status,
|
| - const std::string& registration_id) {
|
| - DCHECK(delegate_);
|
| -
|
| - Result result;
|
| - PendingRegistrationRequests::iterator iter =
|
| - pending_registration_requests_.find(app_id);
|
| - if (iter == pending_registration_requests_.end())
|
| - result = UNKNOWN_ERROR;
|
| - else if (status == RegistrationRequest::INVALID_SENDER)
|
| - result = INVALID_PARAMETER;
|
| - else if (registration_id.empty())
|
| - result = SERVER_ERROR;
|
| - else
|
| - result = SUCCESS;
|
| -
|
| - if (result == SUCCESS) {
|
| - // Cache it.
|
| - linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
|
| - registration->sender_ids = sender_ids;
|
| - registration->registration_id = registration_id;
|
| - registrations_[app_id] = registration;
|
| -
|
| - // Save it in the persistent store.
|
| - gcm_store_->AddRegistration(
|
| - app_id,
|
| - registration,
|
| - base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| - }
|
| -
|
| - delegate_->OnRegisterFinished(
|
| - app_id, result == SUCCESS ? registration_id : std::string(), result);
|
| -
|
| - if (iter != pending_registration_requests_.end()) {
|
| - delete iter->second;
|
| - pending_registration_requests_.erase(iter);
|
| - }
|
| -}
|
| -
|
| -void GCMClientImpl::Unregister(const std::string& app_id) {
|
| - DCHECK_EQ(state_, READY);
|
| - if (pending_unregistration_requests_.count(app_id) == 1)
|
| - return;
|
| -
|
| - // Remove from the cache and persistent store.
|
| - registrations_.erase(app_id);
|
| - gcm_store_->RemoveRegistration(
|
| - app_id,
|
| - base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
|
| - weak_ptr_factory_.GetWeakPtr()));
|
| -
|
| - UnregistrationRequest::RequestInfo request_info(
|
| - device_checkin_info_.android_id,
|
| - device_checkin_info_.secret,
|
| - app_id);
|
| -
|
| - UnregistrationRequest* unregistration_request = new UnregistrationRequest(
|
| - gservices_settings_.GetRegistrationURL(),
|
| - request_info,
|
| - kDefaultBackoffPolicy,
|
| - base::Bind(&GCMClientImpl::OnUnregisterCompleted,
|
| - weak_ptr_factory_.GetWeakPtr(),
|
| - app_id),
|
| - url_request_context_getter_,
|
| - &recorder_);
|
| - pending_unregistration_requests_[app_id] = unregistration_request;
|
| - unregistration_request->Start();
|
| -}
|
| -
|
| -void GCMClientImpl::OnUnregisterCompleted(
|
| - const std::string& app_id,
|
| - UnregistrationRequest::Status status) {
|
| - DVLOG(1) << "Unregister completed for app: " << app_id
|
| - << " with " << (status ? "success." : "failure.");
|
| - delegate_->OnUnregisterFinished(
|
| - app_id,
|
| - status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
|
| -
|
| - PendingUnregistrationRequests::iterator iter =
|
| - pending_unregistration_requests_.find(app_id);
|
| - if (iter == pending_unregistration_requests_.end())
|
| - return;
|
| -
|
| - delete iter->second;
|
| - pending_unregistration_requests_.erase(iter);
|
| -}
|
| -
|
| -void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
|
| - DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
|
| - UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
|
| -}
|
| -
|
| -void GCMClientImpl::Send(const std::string& app_id,
|
| - const std::string& receiver_id,
|
| - const OutgoingMessage& message) {
|
| - DCHECK_EQ(state_, READY);
|
| -
|
| - RecordOutgoingMessageToUMA(message);
|
| -
|
| - mcs_proto::DataMessageStanza stanza;
|
| - stanza.set_ttl(message.time_to_live);
|
| - stanza.set_sent(clock_->Now().ToInternalValue() /
|
| - base::Time::kMicrosecondsPerSecond);
|
| - stanza.set_id(message.id);
|
| - stanza.set_from(kSendMessageFromValue);
|
| - stanza.set_to(receiver_id);
|
| - stanza.set_category(app_id);
|
| -
|
| - for (MessageData::const_iterator iter = message.data.begin();
|
| - iter != message.data.end();
|
| - ++iter) {
|
| - mcs_proto::AppData* app_data = stanza.add_app_data();
|
| - app_data->set_key(iter->first);
|
| - app_data->set_value(iter->second);
|
| - }
|
| -
|
| - MCSMessage mcs_message(stanza);
|
| - DVLOG(1) << "MCS message size: " << mcs_message.size();
|
| - mcs_client_->SendMessage(mcs_message);
|
| -}
|
| -
|
| -std::string GCMClientImpl::GetStateString() const {
|
| - switch(state_) {
|
| - case GCMClientImpl::INITIALIZED:
|
| - return "INITIALIZED";
|
| - case GCMClientImpl::UNINITIALIZED:
|
| - return "UNINITIALIZED";
|
| - case GCMClientImpl::LOADING:
|
| - return "LOADING";
|
| - case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
|
| - return "INITIAL_DEVICE_CHECKIN";
|
| - case GCMClientImpl::READY:
|
| - return "READY";
|
| - default:
|
| - NOTREACHED();
|
| - return std::string();
|
| - }
|
| -}
|
| -
|
| -void GCMClientImpl::SetRecording(bool recording) {
|
| - recorder_.SetRecording(recording);
|
| -}
|
| -
|
| -void GCMClientImpl::ClearActivityLogs() {
|
| - recorder_.Clear();
|
| -}
|
| -
|
| -GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
|
| - GCMClient::GCMStatistics stats;
|
| - stats.gcm_client_created = true;
|
| - stats.is_recording = recorder_.is_recording();
|
| - stats.gcm_client_state = GetStateString();
|
| - stats.connection_client_created = mcs_client_.get() != NULL;
|
| - if (mcs_client_.get()) {
|
| - stats.connection_state = mcs_client_->GetStateString();
|
| - stats.send_queue_size = mcs_client_->GetSendQueueSize();
|
| - stats.resend_queue_size = mcs_client_->GetResendQueueSize();
|
| - }
|
| - if (device_checkin_info_.android_id > 0)
|
| - stats.android_id = device_checkin_info_.android_id;
|
| - recorder_.CollectActivities(&stats.recorded_activities);
|
| -
|
| - for (RegistrationInfoMap::const_iterator it = registrations_.begin();
|
| - it != registrations_.end(); ++it) {
|
| - stats.registered_app_ids.push_back(it->first);
|
| - }
|
| - return stats;
|
| -}
|
| -
|
| -void GCMClientImpl::OnActivityRecorded() {
|
| - delegate_->OnActivityRecorded();
|
| -}
|
| -
|
| -void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
|
| - switch (message.tag()) {
|
| - case kLoginResponseTag:
|
| - DVLOG(1) << "Login response received by GCM Client. Ignoring.";
|
| - return;
|
| - case kDataMessageStanzaTag:
|
| - DVLOG(1) << "A downstream message received. Processing...";
|
| - HandleIncomingMessage(message);
|
| - return;
|
| - default:
|
| - NOTREACHED() << "Message with unexpected tag received by GCMClient";
|
| - return;
|
| - }
|
| -}
|
| -
|
| -void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
|
| - const std::string& app_id,
|
| - const std::string& message_id,
|
| - MCSClient::MessageSendStatus status) {
|
| - DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
|
| - DCHECK(delegate_);
|
| -
|
| - // TTL_EXCEEDED is singled out here, because it can happen long time after the
|
| - // message was sent. That is why it comes as |OnMessageSendError| event rather
|
| - // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
|
| - // All other errors will be raised immediately, through asynchronous callback.
|
| - // It is expected that TTL_EXCEEDED will be issued for a message that was
|
| - // previously issued |OnSendFinished| with status SUCCESS.
|
| - // For now, we do not report that the message has been sent and acked
|
| - // successfully.
|
| - // TODO(jianli): Consider adding UMA for this status.
|
| - if (status == MCSClient::TTL_EXCEEDED) {
|
| - SendErrorDetails send_error_details;
|
| - send_error_details.message_id = message_id;
|
| - send_error_details.result = GCMClient::TTL_EXCEEDED;
|
| - delegate_->OnMessageSendError(app_id, send_error_details);
|
| - } else if (status != MCSClient::SENT) {
|
| - delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
|
| - }
|
| -}
|
| -
|
| -void GCMClientImpl::OnMCSError() {
|
| - // TODO(fgorski): For now it replaces the initialization method. Long term it
|
| - // should have an error or status passed in.
|
| -}
|
| -
|
| -void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
|
| - DCHECK(delegate_);
|
| -
|
| - const mcs_proto::DataMessageStanza& data_message_stanza =
|
| - reinterpret_cast<const mcs_proto::DataMessageStanza&>(
|
| - message.GetProtobuf());
|
| - DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
|
| -
|
| - // Copying all the data from the stanza to a MessageData object. When present,
|
| - // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
|
| - // later.
|
| - MessageData message_data;
|
| - for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
|
| - std::string key = data_message_stanza.app_data(i).key();
|
| - message_data[key] = data_message_stanza.app_data(i).value();
|
| - }
|
| -
|
| - MessageType message_type = DATA_MESSAGE;
|
| - MessageData::iterator iter = message_data.find(kMessageTypeKey);
|
| - if (iter != message_data.end()) {
|
| - message_type = DecodeMessageType(iter->second);
|
| - message_data.erase(iter);
|
| - }
|
| -
|
| - switch (message_type) {
|
| - case DATA_MESSAGE:
|
| - HandleIncomingDataMessage(data_message_stanza, message_data);
|
| - break;
|
| - case DELETED_MESSAGES:
|
| - recorder_.RecordDataMessageReceived(data_message_stanza.category(),
|
| - data_message_stanza.from(),
|
| - data_message_stanza.ByteSize(),
|
| - true,
|
| - GCMStatsRecorder::DELETED_MESSAGES);
|
| - delegate_->OnMessagesDeleted(data_message_stanza.category());
|
| - break;
|
| - case SEND_ERROR:
|
| - HandleIncomingSendError(data_message_stanza, message_data);
|
| - break;
|
| - case UNKNOWN:
|
| - default: // Treat default the same as UNKNOWN.
|
| - DVLOG(1) << "Unknown message_type received. Message ignored. "
|
| - << "App ID: " << data_message_stanza.category() << ".";
|
| - break;
|
| - }
|
| -}
|
| -
|
| -void GCMClientImpl::HandleIncomingDataMessage(
|
| - const mcs_proto::DataMessageStanza& data_message_stanza,
|
| - MessageData& message_data) {
|
| - std::string app_id = data_message_stanza.category();
|
| -
|
| - // Drop the message when the app is not registered for the sender of the
|
| - // message.
|
| - RegistrationInfoMap::iterator iter = registrations_.find(app_id);
|
| - bool not_registered =
|
| - iter == registrations_.end() ||
|
| - std::find(iter->second->sender_ids.begin(),
|
| - iter->second->sender_ids.end(),
|
| - data_message_stanza.from()) == iter->second->sender_ids.end();
|
| - recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
|
| - data_message_stanza.ByteSize(), !not_registered,
|
| - GCMStatsRecorder::DATA_MESSAGE);
|
| - if (not_registered) {
|
| - return;
|
| - }
|
| -
|
| - IncomingMessage incoming_message;
|
| - incoming_message.sender_id = data_message_stanza.from();
|
| - if (data_message_stanza.has_token())
|
| - incoming_message.collapse_key = data_message_stanza.token();
|
| - incoming_message.data = message_data;
|
| - delegate_->OnMessageReceived(app_id, incoming_message);
|
| -}
|
| -
|
| -void GCMClientImpl::HandleIncomingSendError(
|
| - const mcs_proto::DataMessageStanza& data_message_stanza,
|
| - MessageData& message_data) {
|
| - SendErrorDetails send_error_details;
|
| - send_error_details.additional_data = message_data;
|
| - send_error_details.result = SERVER_ERROR;
|
| -
|
| - MessageData::iterator iter =
|
| - send_error_details.additional_data.find(kSendErrorMessageIdKey);
|
| - if (iter != send_error_details.additional_data.end()) {
|
| - send_error_details.message_id = iter->second;
|
| - send_error_details.additional_data.erase(iter);
|
| - }
|
| -
|
| - recorder_.RecordIncomingSendError(
|
| - data_message_stanza.category(),
|
| - data_message_stanza.to(),
|
| - data_message_stanza.id());
|
| - delegate_->OnMessageSendError(data_message_stanza.category(),
|
| - send_error_details);
|
| -}
|
| -
|
| -} // namespace gcm
|
|
|