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 32namespace gcm { 33 34namespace { 35 36// Indicates a message type of the received message. 37enum 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 44enum 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 58const int kMaxRegistrationRetries = 5; 59const char kMessageTypeDataMessage[] = "gcm"; 60const char kMessageTypeDeletedMessagesKey[] = "deleted_messages"; 61const char kMessageTypeKey[] = "message_type"; 62const char kMessageTypeSendErrorKey[] = "send_error"; 63const char kSendErrorMessageIdKey[] = "google.message_id"; 64const char kSendMessageFromValue[] = "gcm@chrome.com"; 65const int64 kDefaultUserSerialNumber = 0LL; 66 67GCMClient::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 89void 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 150MessageType 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 160void 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 181GCMInternalsBuilder::GCMInternalsBuilder() {} 182GCMInternalsBuilder::~GCMInternalsBuilder() {} 183 184scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() { 185 return make_scoped_ptr<base::Clock>(new base::DefaultClock()); 186} 187 188scoped_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 202scoped_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 218GCMClientImpl::CheckinInfo::CheckinInfo() 219 : android_id(0), secret(0), accounts_set(false) { 220} 221 222GCMClientImpl::CheckinInfo::~CheckinInfo() { 223} 224 225void 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 235void 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 243GCMClientImpl::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 256GCMClientImpl::~GCMClientImpl() { 257} 258 259void 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 290void 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 299void 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 338void 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 369void 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 386void GCMClientImpl::OnReady( 387 const std::vector<AccountMapping>& account_mappings) { 388 state_ = READY; 389 StartMCSLogin(); 390 391 delegate_->OnGCMReady(account_mappings); 392} 393 394void 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 401void GCMClientImpl::ResetState() { 402 state_ = UNINITIALIZED; 403 // TODO(fgorski): reset all of the necessart objects and start over. 404} 405 406void 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 443void 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 450void 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 457void 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 483void 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 528void GCMClientImpl::SetGServicesSettingsCallback(bool success) { 529 DCHECK(success); 530} 531 532void 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 552base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const { 553 return last_checkin_time_ + gservices_settings_.GetCheckinInterval() - 554 clock_->Now(); 555} 556 557void GCMClientImpl::SetLastCheckinInfoCallback(bool success) { 558 // TODO(fgorski): This is one of the signals that store needs a rebuild. 559 DCHECK(success); 560} 561 562void GCMClientImpl::SetDeviceCredentialsCallback(bool success) { 563 // TODO(fgorski): This is one of the signals that store needs a rebuild. 564 DCHECK(success); 565} 566 567void GCMClientImpl::UpdateRegistrationCallback(bool success) { 568 // TODO(fgorski): This is one of the signals that store needs a rebuild. 569 DCHECK(success); 570} 571 572void GCMClientImpl::DefaultStoreCallback(bool success) { 573 DCHECK(success); 574} 575 576void 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 588void GCMClientImpl::CheckOut() { 589 Stop(); 590 gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed, 591 weak_ptr_factory_.GetWeakPtr())); 592} 593 594void 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 631void 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 674void 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 704void 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 722void GCMClientImpl::OnGCMStoreDestroyed(bool success) { 723 DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!"; 724 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success); 725} 726 727void 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 756std::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 774void GCMClientImpl::SetRecording(bool recording) { 775 recorder_.SetRecording(recording); 776} 777 778void GCMClientImpl::ClearActivityLogs() { 779 recorder_.Clear(); 780} 781 782GCMClient::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 805void GCMClientImpl::OnActivityRecorded() { 806 delegate_->OnActivityRecorded(); 807} 808 809void 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 816void GCMClientImpl::OnDisconnected() { 817 delegate_->OnActivityRecorded(); 818 delegate_->OnDisconnected(); 819} 820 821void 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 836void 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 862void 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 867void 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 914void 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 942void 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