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