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