Home | History | Annotate | Download | only in gcm_driver
      1 // Copyright 2014 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 "components/gcm_driver/gcm_client_impl.h"
      6 
      7 #include "base/bind.h"
      8 #include "base/files/file_path.h"
      9 #include "base/logging.h"
     10 #include "base/memory/scoped_ptr.h"
     11 #include "base/message_loop/message_loop.h"
     12 #include "base/metrics/histogram.h"
     13 #include "base/sequenced_task_runner.h"
     14 #include "base/strings/string_number_conversions.h"
     15 #include "base/strings/stringprintf.h"
     16 #include "base/time/default_clock.h"
     17 #include "google_apis/gcm/base/encryptor.h"
     18 #include "google_apis/gcm/base/mcs_message.h"
     19 #include "google_apis/gcm/base/mcs_util.h"
     20 #include "google_apis/gcm/engine/checkin_request.h"
     21 #include "google_apis/gcm/engine/connection_factory_impl.h"
     22 #include "google_apis/gcm/engine/gcm_store_impl.h"
     23 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
     24 #include "google_apis/gcm/protocol/checkin.pb.h"
     25 #include "google_apis/gcm/protocol/mcs.pb.h"
     26 #include "net/http/http_network_session.h"
     27 #include "net/url_request/url_request_context.h"
     28 #include "url/gurl.h"
     29 
     30 namespace gcm {
     31 
     32 namespace {
     33 
     34 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
     35 // retries.
     36 // Note: In order to ensure a minimum of 20 seconds between server errors (for
     37 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
     38 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
     39 // themselves.
     40 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
     41   // Number of initial errors (in sequence) to ignore before applying
     42   // exponential back-off rules.
     43   0,
     44 
     45   // Initial delay for exponential back-off in ms.
     46   30 * 1000,  // 30 seconds.
     47 
     48   // Factor by which the waiting time will be multiplied.
     49   2,
     50 
     51   // Fuzzing percentage. ex: 10% will spread requests randomly
     52   // between 90%-100% of the calculated time.
     53   0.33,  // 33%.
     54 
     55   // Maximum amount of time we are willing to delay our request in ms.
     56   10 * 60 * 1000, // 10 minutes.
     57 
     58   // Time to keep an entry from being discarded even when it
     59   // has no significant state, -1 to never discard.
     60   -1,
     61 
     62   // Don't use initial delay unless the last request was an error.
     63   false,
     64 };
     65 
     66 // Indicates a message type of the received message.
     67 enum MessageType {
     68   UNKNOWN,           // Undetermined type.
     69   DATA_MESSAGE,      // Regular data message.
     70   DELETED_MESSAGES,  // Messages were deleted on the server.
     71   SEND_ERROR,        // Error sending a message.
     72 };
     73 
     74 enum OutgoingMessageTTLCategory {
     75   TTL_ZERO,
     76   TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
     77   TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
     78   TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
     79   TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
     80   TTL_MORE_THAN_ONE_WEEK,
     81   TTL_MAXIMUM,
     82   // NOTE: always keep this entry at the end. Add new TTL category only
     83   // immediately above this line. Make sure to update the corresponding
     84   // histogram enum accordingly.
     85   TTL_CATEGORY_COUNT
     86 };
     87 
     88 const int kMaxRegistrationRetries = 5;
     89 const char kMessageTypeDataMessage[] = "gcm";
     90 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
     91 const char kMessageTypeKey[] = "message_type";
     92 const char kMessageTypeSendErrorKey[] = "send_error";
     93 const char kSendErrorMessageIdKey[] = "google.message_id";
     94 const char kSendMessageFromValue[] = "gcm (at) chrome.com";
     95 const int64 kDefaultUserSerialNumber = 0LL;
     96 
     97 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
     98   switch (status) {
     99     case MCSClient::QUEUED:
    100       return GCMClient::SUCCESS;
    101     case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
    102       return GCMClient::NETWORK_ERROR;
    103     case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
    104       return GCMClient::NETWORK_ERROR;
    105     case MCSClient::MESSAGE_TOO_LARGE:
    106       return GCMClient::INVALID_PARAMETER;
    107     case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
    108       return GCMClient::NETWORK_ERROR;
    109     case MCSClient::TTL_EXCEEDED:
    110       return GCMClient::NETWORK_ERROR;
    111     case MCSClient::SENT:
    112     default:
    113       NOTREACHED();
    114       break;
    115   }
    116   return GCMClientImpl::UNKNOWN_ERROR;
    117 }
    118 
    119 void ToCheckinProtoVersion(
    120     const GCMClient::ChromeBuildInfo& chrome_build_info,
    121     checkin_proto::ChromeBuildProto* android_build_info) {
    122   checkin_proto::ChromeBuildProto_Platform platform;
    123   switch (chrome_build_info.platform) {
    124     case GCMClient::PLATFORM_WIN:
    125       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
    126       break;
    127     case GCMClient::PLATFORM_MAC:
    128       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
    129       break;
    130     case GCMClient::PLATFORM_LINUX:
    131       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
    132       break;
    133     case GCMClient::PLATFORM_IOS:
    134       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
    135       break;
    136     case GCMClient::PLATFORM_ANDROID:
    137       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
    138       break;
    139     case GCMClient::PLATFORM_CROS:
    140       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
    141       break;
    142     case GCMClient::PLATFORM_UNKNOWN:
    143       // For unknown platform, return as LINUX.
    144       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
    145       break;
    146     default:
    147       NOTREACHED();
    148       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
    149       break;
    150   }
    151   android_build_info->set_platform(platform);
    152 
    153   checkin_proto::ChromeBuildProto_Channel channel;
    154   switch (chrome_build_info.channel) {
    155     case GCMClient::CHANNEL_STABLE:
    156       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
    157       break;
    158     case GCMClient::CHANNEL_BETA:
    159       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
    160       break;
    161     case GCMClient::CHANNEL_DEV:
    162       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
    163       break;
    164     case GCMClient::CHANNEL_CANARY:
    165       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
    166       break;
    167     case GCMClient::CHANNEL_UNKNOWN:
    168       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
    169       break;
    170     default:
    171       NOTREACHED();
    172       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
    173       break;
    174   }
    175   android_build_info->set_channel(channel);
    176 
    177   android_build_info->set_chrome_version(chrome_build_info.version);
    178 }
    179 
    180 MessageType DecodeMessageType(const std::string& value) {
    181   if (kMessageTypeDeletedMessagesKey == value)
    182     return DELETED_MESSAGES;
    183   if (kMessageTypeSendErrorKey == value)
    184     return SEND_ERROR;
    185   if (kMessageTypeDataMessage == value)
    186     return DATA_MESSAGE;
    187   return UNKNOWN;
    188 }
    189 
    190 void RecordOutgoingMessageToUMA(
    191     const gcm::GCMClient::OutgoingMessage& message) {
    192   OutgoingMessageTTLCategory ttl_category;
    193   if (message.time_to_live == 0)
    194     ttl_category = TTL_ZERO;
    195   else if (message.time_to_live <= 60 )
    196     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
    197   else if (message.time_to_live <= 60 * 60)
    198     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
    199   else if (message.time_to_live <= 24 * 60 * 60)
    200     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
    201   else if (message.time_to_live <= 7 * 24 * 60 * 60)
    202     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
    203   else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
    204     ttl_category = TTL_MORE_THAN_ONE_WEEK;
    205   else
    206     ttl_category = TTL_MAXIMUM;
    207 
    208   UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
    209                             ttl_category,
    210                             TTL_CATEGORY_COUNT);
    211 }
    212 
    213 }  // namespace
    214 
    215 GCMInternalsBuilder::GCMInternalsBuilder() {}
    216 GCMInternalsBuilder::~GCMInternalsBuilder() {}
    217 
    218 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
    219   return make_scoped_ptr<base::Clock>(new base::DefaultClock());
    220 }
    221 
    222 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
    223     const std::string& version,
    224     base::Clock* clock,
    225     ConnectionFactory* connection_factory,
    226     GCMStore* gcm_store,
    227     GCMStatsRecorder* recorder) {
    228   return make_scoped_ptr<MCSClient>(
    229       new MCSClient(version,
    230                     clock,
    231                     connection_factory,
    232                     gcm_store,
    233                     recorder));
    234 }
    235 
    236 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
    237       const std::vector<GURL>& endpoints,
    238       const net::BackoffEntry::Policy& backoff_policy,
    239       scoped_refptr<net::HttpNetworkSession> network_session,
    240       net::NetLog* net_log,
    241       GCMStatsRecorder* recorder) {
    242   return make_scoped_ptr<ConnectionFactory>(
    243       new ConnectionFactoryImpl(endpoints,
    244                                 backoff_policy,
    245                                 network_session,
    246                                 net_log,
    247                                 recorder));
    248 }
    249 
    250 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
    251     : internals_builder_(internals_builder.Pass()),
    252       state_(UNINITIALIZED),
    253       delegate_(NULL),
    254       clock_(internals_builder_->BuildClock()),
    255       url_request_context_getter_(NULL),
    256       pending_registration_requests_deleter_(&pending_registration_requests_),
    257       pending_unregistration_requests_deleter_(
    258           &pending_unregistration_requests_),
    259       periodic_checkin_ptr_factory_(this),
    260       weak_ptr_factory_(this) {
    261 }
    262 
    263 GCMClientImpl::~GCMClientImpl() {
    264 }
    265 
    266 void GCMClientImpl::Initialize(
    267     const ChromeBuildInfo& chrome_build_info,
    268     const base::FilePath& path,
    269     const scoped_refptr<base::SequencedTaskRunner>& blocking_task_runner,
    270     const scoped_refptr<net::URLRequestContextGetter>&
    271         url_request_context_getter,
    272     scoped_ptr<Encryptor> encryptor,
    273     GCMClient::Delegate* delegate) {
    274   DCHECK_EQ(UNINITIALIZED, state_);
    275   DCHECK(url_request_context_getter);
    276   DCHECK(delegate);
    277 
    278   url_request_context_getter_ = url_request_context_getter;
    279   const net::HttpNetworkSession::Params* network_session_params =
    280       url_request_context_getter_->GetURLRequestContext()->
    281           GetNetworkSessionParams();
    282   DCHECK(network_session_params);
    283   network_session_ = new net::HttpNetworkSession(*network_session_params);
    284 
    285   chrome_build_info_ = chrome_build_info;
    286 
    287   gcm_store_.reset(
    288       new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
    289 
    290   delegate_ = delegate;
    291 
    292   recorder_.SetDelegate(this);
    293 
    294   state_ = INITIALIZED;
    295 }
    296 
    297 void GCMClientImpl::Start() {
    298   DCHECK_EQ(INITIALIZED, state_);
    299 
    300   // Once the loading is completed, the check-in will be initiated.
    301   gcm_store_->Load(base::Bind(&GCMClientImpl::OnLoadCompleted,
    302                               weak_ptr_factory_.GetWeakPtr()));
    303   state_ = LOADING;
    304 }
    305 
    306 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
    307   DCHECK_EQ(LOADING, state_);
    308 
    309   if (!result->success) {
    310     ResetState();
    311     return;
    312   }
    313 
    314   registrations_ = result->registrations;
    315   device_checkin_info_.android_id = result->device_android_id;
    316   device_checkin_info_.secret = result->device_security_token;
    317   last_checkin_time_ = result->last_checkin_time;
    318   gservices_settings_.UpdateFromLoadResult(*result);
    319   InitializeMCSClient(result.Pass());
    320 
    321   if (device_checkin_info_.IsValid()) {
    322     SchedulePeriodicCheckin();
    323     OnReady();
    324     return;
    325   }
    326 
    327   state_ = INITIAL_DEVICE_CHECKIN;
    328   device_checkin_info_.Reset();
    329   StartCheckin();
    330 }
    331 
    332 void GCMClientImpl::InitializeMCSClient(
    333     scoped_ptr<GCMStore::LoadResult> result) {
    334   std::vector<GURL> endpoints;
    335   endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
    336   endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
    337   connection_factory_ = internals_builder_->BuildConnectionFactory(
    338       endpoints,
    339       kDefaultBackoffPolicy,
    340       network_session_,
    341       net_log_.net_log(),
    342       &recorder_);
    343   connection_factory_->SetConnectionListener(this);
    344   mcs_client_ = internals_builder_->BuildMCSClient(
    345       chrome_build_info_.version,
    346       clock_.get(),
    347       connection_factory_.get(),
    348       gcm_store_.get(),
    349       &recorder_).Pass();
    350 
    351   mcs_client_->Initialize(
    352       base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
    353       base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
    354                  weak_ptr_factory_.GetWeakPtr()),
    355       base::Bind(&GCMClientImpl::OnMessageSentToMCS,
    356                  weak_ptr_factory_.GetWeakPtr()),
    357       result.Pass());
    358 }
    359 
    360 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
    361     const CheckinInfo& checkin_info) {
    362   DCHECK(!device_checkin_info_.IsValid());
    363 
    364   device_checkin_info_.android_id = checkin_info.android_id;
    365   device_checkin_info_.secret = checkin_info.secret;
    366   gcm_store_->SetDeviceCredentials(
    367       checkin_info.android_id, checkin_info.secret,
    368       base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
    369                  weak_ptr_factory_.GetWeakPtr()));
    370 
    371   OnReady();
    372 }
    373 
    374 void GCMClientImpl::OnReady() {
    375   state_ = READY;
    376   StartMCSLogin();
    377 
    378   delegate_->OnGCMReady();
    379 }
    380 
    381 void GCMClientImpl::StartMCSLogin() {
    382   DCHECK_EQ(READY, state_);
    383   DCHECK(device_checkin_info_.IsValid());
    384   mcs_client_->Login(device_checkin_info_.android_id,
    385                      device_checkin_info_.secret);
    386 }
    387 
    388 void GCMClientImpl::ResetState() {
    389   state_ = UNINITIALIZED;
    390   // TODO(fgorski): reset all of the necessart objects and start over.
    391 }
    392 
    393 void GCMClientImpl::StartCheckin() {
    394   // Make sure no checkin is in progress.
    395   if (checkin_request_.get())
    396     return;
    397 
    398   checkin_proto::ChromeBuildProto chrome_build_proto;
    399   ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
    400   CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
    401                                            device_checkin_info_.secret,
    402                                            gservices_settings_.digest(),
    403                                            chrome_build_proto);
    404   checkin_request_.reset(
    405       new CheckinRequest(gservices_settings_.GetCheckinURL(),
    406                          request_info,
    407                          kDefaultBackoffPolicy,
    408                          base::Bind(&GCMClientImpl::OnCheckinCompleted,
    409                                     weak_ptr_factory_.GetWeakPtr()),
    410                          url_request_context_getter_,
    411                          &recorder_));
    412   checkin_request_->Start();
    413 }
    414 
    415 void GCMClientImpl::OnCheckinCompleted(
    416     const checkin_proto::AndroidCheckinResponse& checkin_response) {
    417   checkin_request_.reset();
    418 
    419   if (!checkin_response.has_android_id() ||
    420       !checkin_response.has_security_token()) {
    421     // TODO(fgorski): I don't think a retry here will help, we should probably
    422     // start over. By checking in with (0, 0).
    423     return;
    424   }
    425 
    426   CheckinInfo checkin_info;
    427   checkin_info.android_id = checkin_response.android_id();
    428   checkin_info.secret = checkin_response.security_token();
    429 
    430   if (state_ == INITIAL_DEVICE_CHECKIN) {
    431     OnFirstTimeDeviceCheckinCompleted(checkin_info);
    432   } else {
    433     // checkin_info is not expected to change after a periodic checkin as it
    434     // would invalidate the registratoin IDs.
    435     DCHECK_EQ(READY, state_);
    436     DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
    437     DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
    438   }
    439 
    440   if (device_checkin_info_.IsValid()) {
    441     // First update G-services settings, as something might have changed.
    442     if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
    443       gcm_store_->SetGServicesSettings(
    444           gservices_settings_.settings_map(),
    445           gservices_settings_.digest(),
    446           base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
    447                      weak_ptr_factory_.GetWeakPtr()));
    448     }
    449 
    450     last_checkin_time_ = clock_->Now();
    451     gcm_store_->SetLastCheckinTime(
    452         last_checkin_time_,
    453         base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
    454                    weak_ptr_factory_.GetWeakPtr()));
    455     SchedulePeriodicCheckin();
    456   }
    457 }
    458 
    459 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
    460   DCHECK(success);
    461 }
    462 
    463 void GCMClientImpl::SchedulePeriodicCheckin() {
    464   // Make sure no checkin is in progress.
    465   if (checkin_request_.get())
    466     return;
    467 
    468   // There should be only one periodic checkin pending at a time. Removing
    469   // pending periodic checkin to schedule a new one.
    470   periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
    471 
    472   base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
    473   if (time_to_next_checkin < base::TimeDelta())
    474     time_to_next_checkin = base::TimeDelta();
    475 
    476   base::MessageLoop::current()->PostDelayedTask(
    477       FROM_HERE,
    478       base::Bind(&GCMClientImpl::StartCheckin,
    479                  periodic_checkin_ptr_factory_.GetWeakPtr()),
    480       time_to_next_checkin);
    481 }
    482 
    483 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
    484   return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
    485          clock_->Now();
    486 }
    487 
    488 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
    489   // TODO(fgorski): This is one of the signals that store needs a rebuild.
    490   DCHECK(success);
    491 }
    492 
    493 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
    494   // TODO(fgorski): This is one of the signals that store needs a rebuild.
    495   DCHECK(success);
    496 }
    497 
    498 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
    499   // TODO(fgorski): This is one of the signals that store needs a rebuild.
    500   DCHECK(success);
    501 }
    502 
    503 void GCMClientImpl::Stop() {
    504   weak_ptr_factory_.InvalidateWeakPtrs();
    505   device_checkin_info_.Reset();
    506   connection_factory_.reset();
    507   delegate_->OnDisconnected();
    508   mcs_client_.reset();
    509   checkin_request_.reset();
    510   pending_registration_requests_.clear();
    511   state_ = INITIALIZED;
    512   gcm_store_->Close();
    513 }
    514 
    515 void GCMClientImpl::CheckOut() {
    516   Stop();
    517   gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
    518                                  weak_ptr_factory_.GetWeakPtr()));
    519 }
    520 
    521 void GCMClientImpl::Register(const std::string& app_id,
    522                              const std::vector<std::string>& sender_ids) {
    523   DCHECK_EQ(state_, READY);
    524 
    525   // If the same sender ids is provided, return the cached registration ID
    526   // directly.
    527   RegistrationInfoMap::const_iterator registrations_iter =
    528       registrations_.find(app_id);
    529   if (registrations_iter != registrations_.end() &&
    530       registrations_iter->second->sender_ids == sender_ids) {
    531     delegate_->OnRegisterFinished(
    532         app_id, registrations_iter->second->registration_id, SUCCESS);
    533     return;
    534   }
    535 
    536   RegistrationRequest::RequestInfo request_info(
    537       device_checkin_info_.android_id,
    538       device_checkin_info_.secret,
    539       app_id,
    540       sender_ids);
    541   DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
    542 
    543   RegistrationRequest* registration_request =
    544       new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
    545                               request_info,
    546                               kDefaultBackoffPolicy,
    547                               base::Bind(&GCMClientImpl::OnRegisterCompleted,
    548                                          weak_ptr_factory_.GetWeakPtr(),
    549                                          app_id,
    550                                          sender_ids),
    551                               kMaxRegistrationRetries,
    552                               url_request_context_getter_,
    553                               &recorder_);
    554   pending_registration_requests_[app_id] = registration_request;
    555   registration_request->Start();
    556 }
    557 
    558 void GCMClientImpl::OnRegisterCompleted(
    559     const std::string& app_id,
    560     const std::vector<std::string>& sender_ids,
    561     RegistrationRequest::Status status,
    562     const std::string& registration_id) {
    563   DCHECK(delegate_);
    564 
    565   Result result;
    566   PendingRegistrationRequests::iterator iter =
    567       pending_registration_requests_.find(app_id);
    568   if (iter == pending_registration_requests_.end())
    569     result = UNKNOWN_ERROR;
    570   else if (status == RegistrationRequest::INVALID_SENDER)
    571     result = INVALID_PARAMETER;
    572   else if (registration_id.empty())
    573     result = SERVER_ERROR;
    574   else
    575     result = SUCCESS;
    576 
    577   if (result == SUCCESS) {
    578     // Cache it.
    579     linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
    580     registration->sender_ids = sender_ids;
    581     registration->registration_id = registration_id;
    582     registrations_[app_id] = registration;
    583 
    584     // Save it in the persistent store.
    585     gcm_store_->AddRegistration(
    586         app_id,
    587         registration,
    588         base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
    589                    weak_ptr_factory_.GetWeakPtr()));
    590   }
    591 
    592   delegate_->OnRegisterFinished(
    593       app_id, result == SUCCESS ? registration_id : std::string(), result);
    594 
    595   if (iter != pending_registration_requests_.end()) {
    596     delete iter->second;
    597     pending_registration_requests_.erase(iter);
    598   }
    599 }
    600 
    601 void GCMClientImpl::Unregister(const std::string& app_id) {
    602   DCHECK_EQ(state_, READY);
    603   if (pending_unregistration_requests_.count(app_id) == 1)
    604     return;
    605 
    606   // Remove from the cache and persistent store.
    607   registrations_.erase(app_id);
    608   gcm_store_->RemoveRegistration(
    609       app_id,
    610       base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
    611                  weak_ptr_factory_.GetWeakPtr()));
    612 
    613   UnregistrationRequest::RequestInfo request_info(
    614       device_checkin_info_.android_id,
    615       device_checkin_info_.secret,
    616       app_id);
    617 
    618   UnregistrationRequest* unregistration_request = new UnregistrationRequest(
    619       gservices_settings_.GetRegistrationURL(),
    620       request_info,
    621       kDefaultBackoffPolicy,
    622       base::Bind(&GCMClientImpl::OnUnregisterCompleted,
    623                  weak_ptr_factory_.GetWeakPtr(),
    624                  app_id),
    625       url_request_context_getter_,
    626       &recorder_);
    627   pending_unregistration_requests_[app_id] = unregistration_request;
    628   unregistration_request->Start();
    629 }
    630 
    631 void GCMClientImpl::OnUnregisterCompleted(
    632     const std::string& app_id,
    633     UnregistrationRequest::Status status) {
    634   DVLOG(1) << "Unregister completed for app: " << app_id
    635            << " with " << (status ? "success." : "failure.");
    636   delegate_->OnUnregisterFinished(
    637       app_id,
    638       status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
    639 
    640   PendingUnregistrationRequests::iterator iter =
    641       pending_unregistration_requests_.find(app_id);
    642   if (iter == pending_unregistration_requests_.end())
    643     return;
    644 
    645   delete iter->second;
    646   pending_unregistration_requests_.erase(iter);
    647 }
    648 
    649 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
    650   DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
    651   UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
    652 }
    653 
    654 void GCMClientImpl::Send(const std::string& app_id,
    655                          const std::string& receiver_id,
    656                          const OutgoingMessage& message) {
    657   DCHECK_EQ(state_, READY);
    658 
    659   RecordOutgoingMessageToUMA(message);
    660 
    661   mcs_proto::DataMessageStanza stanza;
    662   stanza.set_ttl(message.time_to_live);
    663   stanza.set_sent(clock_->Now().ToInternalValue() /
    664                   base::Time::kMicrosecondsPerSecond);
    665   stanza.set_id(message.id);
    666   stanza.set_from(kSendMessageFromValue);
    667   stanza.set_to(receiver_id);
    668   stanza.set_category(app_id);
    669 
    670   for (MessageData::const_iterator iter = message.data.begin();
    671        iter != message.data.end();
    672        ++iter) {
    673     mcs_proto::AppData* app_data = stanza.add_app_data();
    674     app_data->set_key(iter->first);
    675     app_data->set_value(iter->second);
    676   }
    677 
    678   MCSMessage mcs_message(stanza);
    679   DVLOG(1) << "MCS message size: " << mcs_message.size();
    680   mcs_client_->SendMessage(mcs_message);
    681 }
    682 
    683 std::string GCMClientImpl::GetStateString() const {
    684   switch(state_) {
    685     case GCMClientImpl::INITIALIZED:
    686       return "INITIALIZED";
    687     case GCMClientImpl::UNINITIALIZED:
    688       return "UNINITIALIZED";
    689     case GCMClientImpl::LOADING:
    690       return "LOADING";
    691     case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
    692       return "INITIAL_DEVICE_CHECKIN";
    693     case GCMClientImpl::READY:
    694       return "READY";
    695     default:
    696       NOTREACHED();
    697       return std::string();
    698   }
    699 }
    700 
    701 void GCMClientImpl::SetRecording(bool recording) {
    702   recorder_.SetRecording(recording);
    703 }
    704 
    705 void GCMClientImpl::ClearActivityLogs() {
    706   recorder_.Clear();
    707 }
    708 
    709 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
    710   GCMClient::GCMStatistics stats;
    711   stats.gcm_client_created = true;
    712   stats.is_recording = recorder_.is_recording();
    713   stats.gcm_client_state = GetStateString();
    714   stats.connection_client_created = mcs_client_.get() != NULL;
    715   if (connection_factory_.get())
    716     stats.connection_state = connection_factory_->GetConnectionStateString();
    717   if (mcs_client_.get()) {
    718     stats.send_queue_size = mcs_client_->GetSendQueueSize();
    719     stats.resend_queue_size = mcs_client_->GetResendQueueSize();
    720   }
    721   if (device_checkin_info_.android_id > 0)
    722     stats.android_id = device_checkin_info_.android_id;
    723   recorder_.CollectActivities(&stats.recorded_activities);
    724 
    725   for (RegistrationInfoMap::const_iterator it = registrations_.begin();
    726        it != registrations_.end(); ++it) {
    727     stats.registered_app_ids.push_back(it->first);
    728   }
    729   return stats;
    730 }
    731 
    732 void GCMClientImpl::OnActivityRecorded() {
    733   delegate_->OnActivityRecorded();
    734 }
    735 
    736 void GCMClientImpl::OnConnected(const GURL& current_server,
    737                                 const net::IPEndPoint& ip_endpoint) {
    738   // TODO(gcm): expose current server in debug page.
    739   delegate_->OnActivityRecorded();
    740   delegate_->OnConnected(ip_endpoint);
    741 }
    742 
    743 void GCMClientImpl::OnDisconnected() {
    744   delegate_->OnActivityRecorded();
    745   delegate_->OnDisconnected();
    746 }
    747 
    748 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
    749   switch (message.tag()) {
    750     case kLoginResponseTag:
    751       DVLOG(1) << "Login response received by GCM Client. Ignoring.";
    752       return;
    753     case kDataMessageStanzaTag:
    754       DVLOG(1) << "A downstream message received. Processing...";
    755       HandleIncomingMessage(message);
    756       return;
    757     default:
    758       NOTREACHED() << "Message with unexpected tag received by GCMClient";
    759       return;
    760   }
    761 }
    762 
    763 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
    764                                        const std::string& app_id,
    765                                        const std::string& message_id,
    766                                        MCSClient::MessageSendStatus status) {
    767   DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
    768   DCHECK(delegate_);
    769 
    770   // TTL_EXCEEDED is singled out here, because it can happen long time after the
    771   // message was sent. That is why it comes as |OnMessageSendError| event rather
    772   // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
    773   // All other errors will be raised immediately, through asynchronous callback.
    774   // It is expected that TTL_EXCEEDED will be issued for a message that was
    775   // previously issued |OnSendFinished| with status SUCCESS.
    776   // For now, we do not report that the message has been sent and acked
    777   // successfully.
    778   // TODO(jianli): Consider adding UMA for this status.
    779   if (status == MCSClient::TTL_EXCEEDED) {
    780     SendErrorDetails send_error_details;
    781     send_error_details.message_id = message_id;
    782     send_error_details.result = GCMClient::TTL_EXCEEDED;
    783     delegate_->OnMessageSendError(app_id, send_error_details);
    784   } else if (status != MCSClient::SENT) {
    785     delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
    786   }
    787 }
    788 
    789 void GCMClientImpl::OnMCSError() {
    790   // TODO(fgorski): For now it replaces the initialization method. Long term it
    791   // should have an error or status passed in.
    792 }
    793 
    794 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
    795   DCHECK(delegate_);
    796 
    797   const mcs_proto::DataMessageStanza& data_message_stanza =
    798       reinterpret_cast<const mcs_proto::DataMessageStanza&>(
    799           message.GetProtobuf());
    800   DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
    801 
    802   // Copying all the data from the stanza to a MessageData object. When present,
    803   // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
    804   // later.
    805   MessageData message_data;
    806   for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
    807     std::string key = data_message_stanza.app_data(i).key();
    808     message_data[key] = data_message_stanza.app_data(i).value();
    809   }
    810 
    811   MessageType message_type = DATA_MESSAGE;
    812   MessageData::iterator iter = message_data.find(kMessageTypeKey);
    813   if (iter != message_data.end()) {
    814     message_type = DecodeMessageType(iter->second);
    815     message_data.erase(iter);
    816   }
    817 
    818   switch (message_type) {
    819     case DATA_MESSAGE:
    820       HandleIncomingDataMessage(data_message_stanza, message_data);
    821       break;
    822     case DELETED_MESSAGES:
    823       recorder_.RecordDataMessageReceived(data_message_stanza.category(),
    824                                           data_message_stanza.from(),
    825                                           data_message_stanza.ByteSize(),
    826                                           true,
    827                                           GCMStatsRecorder::DELETED_MESSAGES);
    828       delegate_->OnMessagesDeleted(data_message_stanza.category());
    829       break;
    830     case SEND_ERROR:
    831       HandleIncomingSendError(data_message_stanza, message_data);
    832       break;
    833     case UNKNOWN:
    834     default:  // Treat default the same as UNKNOWN.
    835       DVLOG(1) << "Unknown message_type received. Message ignored. "
    836                << "App ID: " << data_message_stanza.category() << ".";
    837       break;
    838   }
    839 }
    840 
    841 void GCMClientImpl::HandleIncomingDataMessage(
    842     const mcs_proto::DataMessageStanza& data_message_stanza,
    843     MessageData& message_data) {
    844   std::string app_id = data_message_stanza.category();
    845 
    846   // Drop the message when the app is not registered for the sender of the
    847   // message.
    848   RegistrationInfoMap::iterator iter = registrations_.find(app_id);
    849   bool not_registered =
    850       iter == registrations_.end() ||
    851       std::find(iter->second->sender_ids.begin(),
    852                 iter->second->sender_ids.end(),
    853                 data_message_stanza.from()) == iter->second->sender_ids.end();
    854   recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
    855       data_message_stanza.ByteSize(), !not_registered,
    856       GCMStatsRecorder::DATA_MESSAGE);
    857   if (not_registered) {
    858     return;
    859   }
    860 
    861   IncomingMessage incoming_message;
    862   incoming_message.sender_id = data_message_stanza.from();
    863   if (data_message_stanza.has_token())
    864     incoming_message.collapse_key = data_message_stanza.token();
    865   incoming_message.data = message_data;
    866   delegate_->OnMessageReceived(app_id, incoming_message);
    867 }
    868 
    869 void GCMClientImpl::HandleIncomingSendError(
    870     const mcs_proto::DataMessageStanza& data_message_stanza,
    871     MessageData& message_data) {
    872   SendErrorDetails send_error_details;
    873   send_error_details.additional_data = message_data;
    874   send_error_details.result = SERVER_ERROR;
    875 
    876   MessageData::iterator iter =
    877       send_error_details.additional_data.find(kSendErrorMessageIdKey);
    878   if (iter != send_error_details.additional_data.end()) {
    879     send_error_details.message_id = iter->second;
    880     send_error_details.additional_data.erase(iter);
    881   }
    882 
    883   recorder_.RecordIncomingSendError(
    884       data_message_stanza.category(),
    885       data_message_stanza.to(),
    886       data_message_stanza.id());
    887   delegate_->OnMessageSendError(data_message_stanza.category(),
    888                                 send_error_details);
    889 }
    890 
    891 }  // namespace gcm
    892