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.
5 #include "components/gcm_driver/gcm_client_impl.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"
35 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
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
41 const net::BackoffEntry::Policy kDefaultBackoffPolicy
= {
42 // Number of initial errors (in sequence) to ignore before applying
43 // exponential back-off rules.
46 // Initial delay for exponential back-off in ms.
47 30 * 1000, // 30 seconds.
49 // Factor by which the waiting time will be multiplied.
52 // Fuzzing percentage. ex: 10% will spread requests randomly
53 // between 90%-100% of the calculated time.
56 // Maximum amount of time we are willing to delay our request in ms.
57 10 * 60 * 1000, // 10 minutes.
59 // Time to keep an entry from being discarded even when it
60 // has no significant state, -1 to never discard.
63 // Don't use initial delay unless the last request was an error.
67 // Indicates a message type of the received message.
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.
75 enum OutgoingMessageTTLCategory
{
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
,
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.
89 const int kMaxRegistrationRetries
= 5;
90 const char kMessageTypeDataMessage
[] = "gcm";
91 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
92 const char kMessageTypeKey
[] = "message_type";
93 const char kMessageTypeSendErrorKey
[] = "send_error";
94 const char kSendErrorMessageIdKey
[] = "google.message_id";
95 const char kSendMessageFromValue
[] = "gcm@chrome.com";
96 const int64 kDefaultUserSerialNumber
= 0LL;
98 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus 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
:
117 return GCMClientImpl::UNKNOWN_ERROR
;
120 void 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
;
128 case GCMClient::PLATFORM_MAC
:
129 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC
;
131 case GCMClient::PLATFORM_LINUX
:
132 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
134 case GCMClient::PLATFORM_IOS
:
135 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS
;
137 case GCMClient::PLATFORM_ANDROID
:
138 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID
;
140 case GCMClient::PLATFORM_CROS
:
141 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS
;
143 case GCMClient::PLATFORM_UNKNOWN
:
144 // For unknown platform, return as LINUX.
145 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
149 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
152 android_build_info
->set_platform(platform
);
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
;
159 case GCMClient::CHANNEL_BETA
:
160 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA
;
162 case GCMClient::CHANNEL_DEV
:
163 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV
;
165 case GCMClient::CHANNEL_CANARY
:
166 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY
;
168 case GCMClient::CHANNEL_UNKNOWN
:
169 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
173 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
176 android_build_info
->set_channel(channel
);
178 android_build_info
->set_chrome_version(chrome_build_info
.version
);
181 MessageType
DecodeMessageType(const std::string
& value
) {
182 if (kMessageTypeDeletedMessagesKey
== value
)
183 return DELETED_MESSAGES
;
184 if (kMessageTypeSendErrorKey
== value
)
186 if (kMessageTypeDataMessage
== value
)
191 void 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
;
207 ttl_category
= TTL_MAXIMUM
;
209 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
216 GCMInternalsBuilder::GCMInternalsBuilder() {}
217 GCMInternalsBuilder::~GCMInternalsBuilder() {}
219 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
220 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
223 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
224 const std::string
& version
,
226 ConnectionFactory
* connection_factory
,
228 GCMStatsRecorder
* recorder
) {
229 return make_scoped_ptr
<MCSClient
>(
230 new MCSClient(version
,
237 scoped_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
,
248 http_network_session
,
253 GCMClientImpl::CheckinInfo::CheckinInfo()
254 : android_id(0), secret(0), accounts_set(false) {
257 GCMClientImpl::CheckinInfo::~CheckinInfo() {
260 void 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();
266 last_checkin_accounts
.insert(iter
->first
);
270 void GCMClientImpl::CheckinInfo::Reset() {
273 accounts_set
= false;
274 account_tokens
.clear();
275 last_checkin_accounts
.clear();
278 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
279 : internals_builder_(internals_builder
.Pass()),
280 state_(UNINITIALIZED
),
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) {
291 GCMClientImpl::~GCMClientImpl() {
294 void 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
.get());
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
);
313 chrome_build_info_
= chrome_build_info
;
316 new GCMStoreImpl(path
, blocking_task_runner
, encryptor
.Pass()));
318 delegate_
= delegate
;
320 recorder_
.SetDelegate(this);
322 state_
= INITIALIZED
;
325 void GCMClientImpl::Start() {
326 DCHECK_EQ(INITIALIZED
, state_
);
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()));
334 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
335 DCHECK_EQ(LOADING
, state_
);
337 if (!result
->success
) {
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());
357 if (device_checkin_info_
.IsValid()) {
358 SchedulePeriodicCheckin();
363 state_
= INITIAL_DEVICE_CHECKIN
;
364 device_checkin_info_
.Reset();
368 void 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(
375 kDefaultBackoffPolicy
,
377 url_request_context_getter_
->GetURLRequestContext()
378 ->http_transaction_factory()
382 connection_factory_
->SetConnectionListener(this);
383 mcs_client_
= internals_builder_
->BuildMCSClient(
384 chrome_build_info_
.version
,
386 connection_factory_
.get(),
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()),
399 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
400 const CheckinInfo
& checkin_info
) {
401 DCHECK(!device_checkin_info_
.IsValid());
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()));
416 void GCMClientImpl::OnReady() {
420 delegate_
->OnGCMReady();
423 void 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
);
430 void GCMClientImpl::ResetState() {
431 state_
= UNINITIALIZED
;
432 // TODO(fgorski): reset all of the necessart objects and start over.
435 void 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;
441 DVLOG(1) << "Set account called with: " << account_tokens
.size()
444 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
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();
452 if (account_tokens
.find(*iter
) == account_tokens
.end())
453 account_removed
= true;
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
462 if (account_removed
) {
463 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
464 checkin_request_
.reset();
466 } else if (!accounts_set_before
) {
467 SchedulePeriodicCheckin();
468 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
472 void GCMClientImpl::UpdateAccountMapping(
473 const AccountMapping
& account_mapping
) {
474 gcm_store_
->AddAccountMapping(account_mapping
,
475 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
476 weak_ptr_factory_
.GetWeakPtr()));
479 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
480 gcm_store_
->RemoveAccountMapping(
482 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
483 weak_ptr_factory_
.GetWeakPtr()));
486 void GCMClientImpl::StartCheckin() {
487 // Make sure no checkin is in progress.
488 if (checkin_request_
.get())
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(),
498 checkin_request_
.reset(
499 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
501 kDefaultBackoffPolicy
,
502 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
503 weak_ptr_factory_
.GetWeakPtr()),
504 url_request_context_getter_
.get(),
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();
512 void GCMClientImpl::OnCheckinCompleted(
513 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
514 checkin_request_
.reset();
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).
523 CheckinInfo checkin_info
;
524 checkin_info
.android_id
= checkin_response
.android_id();
525 checkin_info
.secret
= checkin_response
.security_token();
527 if (state_
== INITIAL_DEVICE_CHECKIN
) {
528 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
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
);
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()));
547 last_checkin_time_
= clock_
->Now();
548 gcm_store_
->SetLastCheckinInfo(
550 device_checkin_info_
.last_checkin_accounts
,
551 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
552 weak_ptr_factory_
.GetWeakPtr()));
553 SchedulePeriodicCheckin();
557 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
561 void GCMClientImpl::SchedulePeriodicCheckin() {
562 // Make sure no checkin is in progress.
563 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
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();
570 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
571 if (time_to_next_checkin
< base::TimeDelta())
572 time_to_next_checkin
= base::TimeDelta();
574 base::MessageLoop::current()->PostDelayedTask(
576 base::Bind(&GCMClientImpl::StartCheckin
,
577 periodic_checkin_ptr_factory_
.GetWeakPtr()),
578 time_to_next_checkin
);
581 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
582 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
586 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
587 // TODO(fgorski): This is one of the signals that store needs a rebuild.
591 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
592 // TODO(fgorski): This is one of the signals that store needs a rebuild.
596 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
597 // TODO(fgorski): This is one of the signals that store needs a rebuild.
601 void GCMClientImpl::DefaultStoreCallback(bool success
) {
605 void GCMClientImpl::Stop() {
606 weak_ptr_factory_
.InvalidateWeakPtrs();
607 device_checkin_info_
.Reset();
608 connection_factory_
.reset();
609 delegate_
->OnDisconnected();
611 checkin_request_
.reset();
612 pending_registration_requests_
.clear();
613 state_
= INITIALIZED
;
617 void GCMClientImpl::CheckOut() {
619 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed
,
620 weak_ptr_factory_
.GetWeakPtr()));
623 void GCMClientImpl::Register(const std::string
& app_id
,
624 const std::vector
<std::string
>& sender_ids
) {
625 DCHECK_EQ(state_
, READY
);
627 // If the same sender ids is provided, return the cached registration ID
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
);
638 RegistrationRequest::RequestInfo
request_info(
639 device_checkin_info_
.android_id
,
640 device_checkin_info_
.secret
,
643 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
645 RegistrationRequest
* registration_request
=
646 new RegistrationRequest(gservices_settings_
.GetRegistrationURL(),
648 kDefaultBackoffPolicy
,
649 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
650 weak_ptr_factory_
.GetWeakPtr(),
653 kMaxRegistrationRetries
,
654 url_request_context_getter_
,
656 pending_registration_requests_
[app_id
] = registration_request
;
657 registration_request
->Start();
660 void 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
) {
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
;
679 if (result
== SUCCESS
) {
681 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
682 registration
->sender_ids
= sender_ids
;
683 registration
->registration_id
= registration_id
;
684 registrations_
[app_id
] = registration
;
686 // Save it in the persistent store.
687 gcm_store_
->AddRegistration(
690 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
691 weak_ptr_factory_
.GetWeakPtr()));
694 delegate_
->OnRegisterFinished(
695 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
697 if (iter
!= pending_registration_requests_
.end()) {
699 pending_registration_requests_
.erase(iter
);
703 void GCMClientImpl::Unregister(const std::string
& app_id
) {
704 DCHECK_EQ(state_
, READY
);
705 if (pending_unregistration_requests_
.count(app_id
) == 1)
708 // Remove from the cache and persistent store.
709 registrations_
.erase(app_id
);
710 gcm_store_
->RemoveRegistration(
712 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
713 weak_ptr_factory_
.GetWeakPtr()));
715 UnregistrationRequest::RequestInfo
request_info(
716 device_checkin_info_
.android_id
,
717 device_checkin_info_
.secret
,
720 UnregistrationRequest
* unregistration_request
= new UnregistrationRequest(
721 gservices_settings_
.GetRegistrationURL(),
723 kDefaultBackoffPolicy
,
724 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
725 weak_ptr_factory_
.GetWeakPtr(),
727 url_request_context_getter_
,
729 pending_unregistration_requests_
[app_id
] = unregistration_request
;
730 unregistration_request
->Start();
733 void 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(
740 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
742 PendingUnregistrationRequests::iterator iter
=
743 pending_unregistration_requests_
.find(app_id
);
744 if (iter
== pending_unregistration_requests_
.end())
748 pending_unregistration_requests_
.erase(iter
);
751 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
752 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
753 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
756 void GCMClientImpl::Send(const std::string
& app_id
,
757 const std::string
& receiver_id
,
758 const OutgoingMessage
& message
) {
759 DCHECK_EQ(state_
, READY
);
761 RecordOutgoingMessageToUMA(message
);
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
);
772 for (MessageData::const_iterator iter
= message
.data
.begin();
773 iter
!= message
.data
.end();
775 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
776 app_data
->set_key(iter
->first
);
777 app_data
->set_value(iter
->second
);
780 MCSMessage
mcs_message(stanza
);
781 DVLOG(1) << "MCS message size: " << mcs_message
.size();
782 mcs_client_
->SendMessage(mcs_message
);
785 std::string
GCMClientImpl::GetStateString() const {
787 case GCMClientImpl::INITIALIZED
:
788 return "INITIALIZED";
789 case GCMClientImpl::UNINITIALIZED
:
790 return "UNINITIALIZED";
791 case GCMClientImpl::LOADING
:
793 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
794 return "INITIAL_DEVICE_CHECKIN";
795 case GCMClientImpl::READY
:
799 return std::string();
803 void GCMClientImpl::SetRecording(bool recording
) {
804 recorder_
.SetRecording(recording
);
807 void GCMClientImpl::ClearActivityLogs() {
811 GCMClient::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();
823 if (device_checkin_info_
.android_id
> 0)
824 stats
.android_id
= device_checkin_info_
.android_id
;
825 recorder_
.CollectActivities(&stats
.recorded_activities
);
827 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
828 it
!= registrations_
.end(); ++it
) {
829 stats
.registered_app_ids
.push_back(it
->first
);
834 void GCMClientImpl::OnActivityRecorded() {
835 delegate_
->OnActivityRecorded();
838 void 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
);
845 void GCMClientImpl::OnDisconnected() {
846 delegate_
->OnActivityRecorded();
847 delegate_
->OnDisconnected();
850 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
851 switch (message
.tag()) {
852 case kLoginResponseTag
:
853 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
855 case kDataMessageStanzaTag
:
856 DVLOG(1) << "A downstream message received. Processing...";
857 HandleIncomingMessage(message
);
860 NOTREACHED() << "Message with unexpected tag received by GCMClient";
865 void 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
);
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
);
887 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
891 void GCMClientImpl::OnMCSError() {
892 // TODO(fgorski): For now it replaces the initialization method. Long term it
893 // should have an error or status passed in.
896 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
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
);
904 // Copying all the data from the stanza to a MessageData object. When present,
905 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
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();
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
);
920 switch (message_type
) {
922 HandleIncomingDataMessage(data_message_stanza
, message_data
);
924 case DELETED_MESSAGES
:
925 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
926 data_message_stanza
.from(),
927 data_message_stanza
.ByteSize(),
929 GCMStatsRecorder::DELETED_MESSAGES
);
930 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
933 HandleIncomingSendError(data_message_stanza
, message_data
);
936 default: // Treat default the same as UNKNOWN.
937 DVLOG(1) << "Unknown message_type received. Message ignored. "
938 << "App ID: " << data_message_stanza
.category() << ".";
943 void GCMClientImpl::HandleIncomingDataMessage(
944 const mcs_proto::DataMessageStanza
& data_message_stanza
,
945 MessageData
& message_data
) {
946 std::string app_id
= data_message_stanza
.category();
948 // Drop the message when the app is not registered for the sender of the
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
) {
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
);
971 void 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
;
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
);
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(),