1 // Copyright 2013 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 "google_apis/gcm/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/mcs_message.h"
18 #include "google_apis/gcm/base/mcs_util.h"
19 #include "google_apis/gcm/engine/checkin_request.h"
20 #include "google_apis/gcm/engine/connection_factory_impl.h"
21 #include "google_apis/gcm/engine/gcm_store_impl.h"
22 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
23 #include "google_apis/gcm/protocol/mcs.pb.h"
24 #include "net/http/http_network_session.h"
25 #include "net/url_request/url_request_context.h"
32 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
34 // Note: In order to ensure a minimum of 20 seconds between server errors (for
35 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
36 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
38 const net::BackoffEntry::Policy kDefaultBackoffPolicy
= {
39 // Number of initial errors (in sequence) to ignore before applying
40 // exponential back-off rules.
43 // Initial delay for exponential back-off in ms.
44 30 * 1000, // 30 seconds.
46 // Factor by which the waiting time will be multiplied.
49 // Fuzzing percentage. ex: 10% will spread requests randomly
50 // between 90%-100% of the calculated time.
53 // Maximum amount of time we are willing to delay our request in ms.
54 10 * 60 * 1000, // 10 minutes.
56 // Time to keep an entry from being discarded even when it
57 // has no significant state, -1 to never discard.
60 // Don't use initial delay unless the last request was an error.
64 // Indicates a message type of the received message.
66 UNKNOWN
, // Undetermined type.
67 DATA_MESSAGE
, // Regular data message.
68 DELETED_MESSAGES
, // Messages were deleted on the server.
69 SEND_ERROR
, // Error sending a message.
72 enum OutgoingMessageTTLCategory
{
74 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
,
75 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
,
76 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
,
77 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK
,
78 TTL_MORE_THAN_ONE_WEEK
,
80 // NOTE: always keep this entry at the end. Add new TTL category only
81 // immediately above this line. Make sure to update the corresponding
82 // histogram enum accordingly.
86 // MCS endpoints. SSL Key pinning is done automatically due to the *.google.com
88 // Note: modifying the endpoints will affect the ability to compare the
89 // GCM.CurrentEnpoint histogram across versions.
90 const char kMCSEndpointMain
[] = "https://mtalk.google.com:5228";
91 const char kMCSEndpointFallback
[] = "https://mtalk.google.com:443";
93 const int kMaxRegistrationRetries
= 5;
94 const char kMessageTypeDataMessage
[] = "gcm";
95 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
96 const char kMessageTypeKey
[] = "message_type";
97 const char kMessageTypeSendErrorKey
[] = "send_error";
98 const char kSendErrorMessageIdKey
[] = "google.message_id";
99 const char kSendMessageFromValue
[] = "gcm@chrome.com";
100 const int64 kDefaultUserSerialNumber
= 0LL;
102 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus status
) {
104 case MCSClient::QUEUED
:
105 return GCMClient::SUCCESS
;
106 case MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
107 return GCMClient::NETWORK_ERROR
;
108 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
109 return GCMClient::NETWORK_ERROR
;
110 case MCSClient::MESSAGE_TOO_LARGE
:
111 return GCMClient::INVALID_PARAMETER
;
112 case MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
113 return GCMClient::NETWORK_ERROR
;
114 case MCSClient::TTL_EXCEEDED
:
115 return GCMClient::NETWORK_ERROR
;
116 case MCSClient::SENT
:
121 return GCMClientImpl::UNKNOWN_ERROR
;
124 MessageType
DecodeMessageType(const std::string
& value
) {
125 if (kMessageTypeDeletedMessagesKey
== value
)
126 return DELETED_MESSAGES
;
127 if (kMessageTypeSendErrorKey
== value
)
129 if (kMessageTypeDataMessage
== value
)
134 void RecordOutgoingMessageToUMA(
135 const gcm::GCMClient::OutgoingMessage
& message
) {
136 OutgoingMessageTTLCategory ttl_category
;
137 if (message
.time_to_live
== 0)
138 ttl_category
= TTL_ZERO
;
139 else if (message
.time_to_live
<= 60 )
140 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
;
141 else if (message
.time_to_live
<= 60 * 60)
142 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
;
143 else if (message
.time_to_live
<= 24 * 60 * 60)
144 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
;
145 else if (message
.time_to_live
<= 7 * 24 * 60 * 60)
146 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK
;
147 else if (message
.time_to_live
< gcm::GCMClient::OutgoingMessage::kMaximumTTL
)
148 ttl_category
= TTL_MORE_THAN_ONE_WEEK
;
150 ttl_category
= TTL_MAXIMUM
;
152 UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
159 GCMInternalsBuilder::GCMInternalsBuilder() {}
160 GCMInternalsBuilder::~GCMInternalsBuilder() {}
162 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
163 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
166 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
167 const std::string
& version
,
169 ConnectionFactory
* connection_factory
,
171 GCMStatsRecorder
* recorder
) {
172 return make_scoped_ptr
<MCSClient
>(
173 new MCSClient(version
,
180 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
181 const std::vector
<GURL
>& endpoints
,
182 const net::BackoffEntry::Policy
& backoff_policy
,
183 scoped_refptr
<net::HttpNetworkSession
> network_session
,
184 net::NetLog
* net_log
,
185 GCMStatsRecorder
* recorder
) {
186 return make_scoped_ptr
<ConnectionFactory
>(
187 new ConnectionFactoryImpl(endpoints
,
194 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
195 : internals_builder_(internals_builder
.Pass()),
196 state_(UNINITIALIZED
),
197 clock_(internals_builder_
->BuildClock()),
198 url_request_context_getter_(NULL
),
199 pending_registration_requests_deleter_(&pending_registration_requests_
),
200 pending_unregistration_requests_deleter_(
201 &pending_unregistration_requests_
),
202 periodic_checkin_ptr_factory_(this),
203 weak_ptr_factory_(this) {
206 GCMClientImpl::~GCMClientImpl() {
209 void GCMClientImpl::Initialize(
210 const checkin_proto::ChromeBuildProto
& chrome_build_proto
,
211 const base::FilePath
& path
,
212 const std::vector
<std::string
>& account_ids
,
213 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
214 const scoped_refptr
<net::URLRequestContextGetter
>&
215 url_request_context_getter
,
216 Delegate
* delegate
) {
217 DCHECK_EQ(UNINITIALIZED
, state_
);
218 DCHECK(url_request_context_getter
);
221 url_request_context_getter_
= url_request_context_getter
;
222 const net::HttpNetworkSession::Params
* network_session_params
=
223 url_request_context_getter_
->GetURLRequestContext()->
224 GetNetworkSessionParams();
225 DCHECK(network_session_params
);
226 network_session_
= new net::HttpNetworkSession(*network_session_params
);
228 chrome_build_proto_
.CopyFrom(chrome_build_proto
);
229 account_ids_
= account_ids
;
231 gcm_store_
.reset(new GCMStoreImpl(path
, blocking_task_runner
));
233 delegate_
= delegate
;
235 state_
= INITIALIZED
;
238 void GCMClientImpl::Load() {
239 DCHECK_EQ(INITIALIZED
, state_
);
241 // Once the loading is completed, the check-in will be initiated.
242 gcm_store_
->Load(base::Bind(&GCMClientImpl::OnLoadCompleted
,
243 weak_ptr_factory_
.GetWeakPtr()));
247 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
248 DCHECK_EQ(LOADING
, state_
);
250 if (!result
->success
) {
255 registrations_
= result
->registrations
;
256 device_checkin_info_
.android_id
= result
->device_android_id
;
257 device_checkin_info_
.secret
= result
->device_security_token
;
258 last_checkin_time_
= result
->last_checkin_time
;
259 gservices_settings_
.UpdateFromLoadResult(*result
);
260 InitializeMCSClient(result
.Pass());
262 if (device_checkin_info_
.IsValid()) {
263 SchedulePeriodicCheckin();
268 state_
= INITIAL_DEVICE_CHECKIN
;
269 device_checkin_info_
.Reset();
273 void GCMClientImpl::InitializeMCSClient(
274 scoped_ptr
<GCMStore::LoadResult
> result
) {
275 std::vector
<GURL
> endpoints
;
276 endpoints
.push_back(GURL(kMCSEndpointMain
));
277 endpoints
.push_back(GURL(kMCSEndpointFallback
));
278 connection_factory_
= internals_builder_
->BuildConnectionFactory(
280 kDefaultBackoffPolicy
,
284 mcs_client_
= internals_builder_
->BuildMCSClient(
285 chrome_build_proto_
.chrome_version(),
287 connection_factory_
.get(),
291 mcs_client_
->Initialize(
292 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
293 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
294 weak_ptr_factory_
.GetWeakPtr()),
295 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
296 weak_ptr_factory_
.GetWeakPtr()),
300 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
301 const CheckinInfo
& checkin_info
) {
302 DCHECK(!device_checkin_info_
.IsValid());
304 device_checkin_info_
.android_id
= checkin_info
.android_id
;
305 device_checkin_info_
.secret
= checkin_info
.secret
;
306 gcm_store_
->SetDeviceCredentials(
307 checkin_info
.android_id
, checkin_info
.secret
,
308 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
309 weak_ptr_factory_
.GetWeakPtr()));
314 void GCMClientImpl::OnReady() {
318 delegate_
->OnGCMReady();
321 void GCMClientImpl::StartMCSLogin() {
322 DCHECK_EQ(READY
, state_
);
323 DCHECK(device_checkin_info_
.IsValid());
324 mcs_client_
->Login(device_checkin_info_
.android_id
,
325 device_checkin_info_
.secret
);
328 void GCMClientImpl::ResetState() {
329 state_
= UNINITIALIZED
;
330 // TODO(fgorski): reset all of the necessart objects and start over.
333 void GCMClientImpl::StartCheckin() {
334 // Make sure no checkin is in progress.
335 if (checkin_request_
.get())
338 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
339 device_checkin_info_
.secret
,
340 gservices_settings_
.digest(),
342 chrome_build_proto_
);
343 checkin_request_
.reset(
344 new CheckinRequest(gservices_settings_
.checkin_url(),
346 kDefaultBackoffPolicy
,
347 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
348 weak_ptr_factory_
.GetWeakPtr()),
349 url_request_context_getter_
,
351 checkin_request_
->Start();
354 void GCMClientImpl::OnCheckinCompleted(
355 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
356 checkin_request_
.reset();
358 if (!checkin_response
.has_android_id() ||
359 !checkin_response
.has_security_token()) {
360 // TODO(fgorski): I don't think a retry here will help, we should probably
361 // start over. By checking in with (0, 0).
365 CheckinInfo checkin_info
;
366 checkin_info
.android_id
= checkin_response
.android_id();
367 checkin_info
.secret
= checkin_response
.security_token();
369 if (state_
== INITIAL_DEVICE_CHECKIN
) {
370 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
372 // checkin_info is not expected to change after a periodic checkin as it
373 // would invalidate the registratoin IDs.
374 DCHECK_EQ(READY
, state_
);
375 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
376 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
379 if (device_checkin_info_
.IsValid()) {
380 // First update G-services settings, as something might have changed.
381 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
382 gcm_store_
->SetGServicesSettings(
383 gservices_settings_
.GetSettingsMap(),
384 gservices_settings_
.digest(),
385 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
386 weak_ptr_factory_
.GetWeakPtr()));
389 last_checkin_time_
= clock_
->Now();
390 gcm_store_
->SetLastCheckinTime(
392 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback
,
393 weak_ptr_factory_
.GetWeakPtr()));
394 SchedulePeriodicCheckin();
398 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
402 void GCMClientImpl::SchedulePeriodicCheckin() {
403 // Make sure no checkin is in progress.
404 if (checkin_request_
.get())
407 // There should be only one periodic checkin pending at a time. Removing
408 // pending periodic checkin to schedule a new one.
409 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
411 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
412 if (time_to_next_checkin
< base::TimeDelta())
413 time_to_next_checkin
= base::TimeDelta();
415 base::MessageLoop::current()->PostDelayedTask(
417 base::Bind(&GCMClientImpl::StartCheckin
,
418 periodic_checkin_ptr_factory_
.GetWeakPtr()),
419 time_to_next_checkin
);
422 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
423 return last_checkin_time_
+ gservices_settings_
.checkin_interval() -
427 void GCMClientImpl::SetLastCheckinTimeCallback(bool success
) {
428 // TODO(fgorski): This is one of the signals that store needs a rebuild.
432 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
433 // TODO(fgorski): This is one of the signals that store needs a rebuild.
437 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
438 // TODO(fgorski): This is one of the signals that store needs a rebuild.
442 void GCMClientImpl::Stop() {
443 device_checkin_info_
.Reset();
444 connection_factory_
.reset();
446 checkin_request_
.reset();
447 pending_registration_requests_
.clear();
448 state_
= INITIALIZED
;
452 void GCMClientImpl::CheckOut() {
454 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed
,
455 weak_ptr_factory_
.GetWeakPtr()));
458 void GCMClientImpl::Register(const std::string
& app_id
,
459 const std::vector
<std::string
>& sender_ids
) {
460 DCHECK_EQ(state_
, READY
);
462 // If the same sender ids is provided, return the cached registration ID
464 RegistrationInfoMap::const_iterator registrations_iter
=
465 registrations_
.find(app_id
);
466 if (registrations_iter
!= registrations_
.end() &&
467 registrations_iter
->second
->sender_ids
== sender_ids
) {
468 delegate_
->OnRegisterFinished(
469 app_id
, registrations_iter
->second
->registration_id
, SUCCESS
);
473 RegistrationRequest::RequestInfo
request_info(
474 device_checkin_info_
.android_id
,
475 device_checkin_info_
.secret
,
478 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
480 RegistrationRequest
* registration_request
=
481 new RegistrationRequest(gservices_settings_
.registration_url(),
483 kDefaultBackoffPolicy
,
484 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
485 weak_ptr_factory_
.GetWeakPtr(),
488 kMaxRegistrationRetries
,
489 url_request_context_getter_
,
491 pending_registration_requests_
[app_id
] = registration_request
;
492 registration_request
->Start();
495 void GCMClientImpl::OnRegisterCompleted(
496 const std::string
& app_id
,
497 const std::vector
<std::string
>& sender_ids
,
498 RegistrationRequest::Status status
,
499 const std::string
& registration_id
) {
503 PendingRegistrationRequests::iterator iter
=
504 pending_registration_requests_
.find(app_id
);
505 if (iter
== pending_registration_requests_
.end())
506 result
= UNKNOWN_ERROR
;
507 else if (status
== RegistrationRequest::INVALID_SENDER
)
508 result
= INVALID_PARAMETER
;
509 else if (registration_id
.empty())
510 result
= SERVER_ERROR
;
514 if (result
== SUCCESS
) {
516 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
517 registration
->sender_ids
= sender_ids
;
518 registration
->registration_id
= registration_id
;
519 registrations_
[app_id
] = registration
;
521 // Save it in the persistent store.
522 gcm_store_
->AddRegistration(
525 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
526 weak_ptr_factory_
.GetWeakPtr()));
529 delegate_
->OnRegisterFinished(
530 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
532 if (iter
!= pending_registration_requests_
.end()) {
534 pending_registration_requests_
.erase(iter
);
538 void GCMClientImpl::Unregister(const std::string
& app_id
) {
539 DCHECK_EQ(state_
, READY
);
540 if (pending_unregistration_requests_
.count(app_id
) == 1)
543 // Remove from the cache and persistent store.
544 registrations_
.erase(app_id
);
545 gcm_store_
->RemoveRegistration(
547 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
548 weak_ptr_factory_
.GetWeakPtr()));
550 UnregistrationRequest::RequestInfo
request_info(
551 device_checkin_info_
.android_id
,
552 device_checkin_info_
.secret
,
555 UnregistrationRequest
* unregistration_request
=
556 new UnregistrationRequest(
557 gservices_settings_
.registration_url(),
559 kDefaultBackoffPolicy
,
560 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
561 weak_ptr_factory_
.GetWeakPtr(),
563 url_request_context_getter_
,
565 pending_unregistration_requests_
[app_id
] = unregistration_request
;
566 unregistration_request
->Start();
569 void GCMClientImpl::OnUnregisterCompleted(
570 const std::string
& app_id
,
571 UnregistrationRequest::Status status
) {
572 DVLOG(1) << "Unregister completed for app: " << app_id
573 << " with " << (status
? "success." : "failure.");
574 delegate_
->OnUnregisterFinished(
576 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
578 PendingUnregistrationRequests::iterator iter
=
579 pending_unregistration_requests_
.find(app_id
);
580 if (iter
== pending_unregistration_requests_
.end())
584 pending_unregistration_requests_
.erase(iter
);
587 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
588 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
589 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
592 void GCMClientImpl::Send(const std::string
& app_id
,
593 const std::string
& receiver_id
,
594 const OutgoingMessage
& message
) {
595 DCHECK_EQ(state_
, READY
);
597 RecordOutgoingMessageToUMA(message
);
599 mcs_proto::DataMessageStanza stanza
;
600 stanza
.set_ttl(message
.time_to_live
);
601 stanza
.set_sent(clock_
->Now().ToInternalValue() /
602 base::Time::kMicrosecondsPerSecond
);
603 stanza
.set_id(message
.id
);
604 stanza
.set_from(kSendMessageFromValue
);
605 stanza
.set_to(receiver_id
);
606 stanza
.set_category(app_id
);
608 for (MessageData::const_iterator iter
= message
.data
.begin();
609 iter
!= message
.data
.end();
611 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
612 app_data
->set_key(iter
->first
);
613 app_data
->set_value(iter
->second
);
616 MCSMessage
mcs_message(stanza
);
617 DVLOG(1) << "MCS message size: " << mcs_message
.size();
618 mcs_client_
->SendMessage(mcs_message
);
621 std::string
GCMClientImpl::GetStateString() const {
623 case GCMClientImpl::INITIALIZED
:
624 return "INITIALIZED";
625 case GCMClientImpl::UNINITIALIZED
:
626 return "UNINITIALIZED";
627 case GCMClientImpl::LOADING
:
629 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
630 return "INITIAL_DEVICE_CHECKIN";
631 case GCMClientImpl::READY
:
635 return std::string();
639 void GCMClientImpl::SetRecording(bool recording
) {
640 recorder_
.SetRecording(recording
);
643 void GCMClientImpl::ClearActivityLogs() {
647 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
648 GCMClient::GCMStatistics stats
;
649 stats
.gcm_client_created
= true;
650 stats
.is_recording
= recorder_
.is_recording();
651 stats
.gcm_client_state
= GetStateString();
652 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
653 if (mcs_client_
.get()) {
654 stats
.connection_state
= mcs_client_
->GetStateString();
655 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
656 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
658 if (device_checkin_info_
.android_id
> 0)
659 stats
.android_id
= device_checkin_info_
.android_id
;
660 recorder_
.CollectActivities(&stats
.recorded_activities
);
662 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
663 it
!= registrations_
.end(); ++it
) {
664 stats
.registered_app_ids
.push_back(it
->first
);
669 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
670 switch (message
.tag()) {
671 case kLoginResponseTag
:
672 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
674 case kDataMessageStanzaTag
:
675 DVLOG(1) << "A downstream message received. Processing...";
676 HandleIncomingMessage(message
);
679 NOTREACHED() << "Message with unexpected tag received by GCMClient";
684 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
685 const std::string
& app_id
,
686 const std::string
& message_id
,
687 MCSClient::MessageSendStatus status
) {
688 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
691 // TTL_EXCEEDED is singled out here, because it can happen long time after the
692 // message was sent. That is why it comes as |OnMessageSendError| event rather
693 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
694 // All other errors will be raised immediately, through asynchronous callback.
695 // It is expected that TTL_EXCEEDED will be issued for a message that was
696 // previously issued |OnSendFinished| with status SUCCESS.
697 // For now, we do not report that the message has been sent and acked
699 // TODO(jianli): Consider adding UMA for this status.
700 if (status
== MCSClient::TTL_EXCEEDED
) {
701 SendErrorDetails send_error_details
;
702 send_error_details
.message_id
= message_id
;
703 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
704 delegate_
->OnMessageSendError(app_id
, send_error_details
);
705 } else if (status
!= MCSClient::SENT
) {
706 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
710 void GCMClientImpl::OnMCSError() {
711 // TODO(fgorski): For now it replaces the initialization method. Long term it
712 // should have an error or status passed in.
715 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
718 const mcs_proto::DataMessageStanza
& data_message_stanza
=
719 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
720 message
.GetProtobuf());
721 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
723 // Copying all the data from the stanza to a MessageData object. When present,
724 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
726 MessageData message_data
;
727 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
728 std::string key
= data_message_stanza
.app_data(i
).key();
729 message_data
[key
] = data_message_stanza
.app_data(i
).value();
732 MessageType message_type
= DATA_MESSAGE
;
733 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
734 if (iter
!= message_data
.end()) {
735 message_type
= DecodeMessageType(iter
->second
);
736 message_data
.erase(iter
);
739 switch (message_type
) {
741 HandleIncomingDataMessage(data_message_stanza
, message_data
);
743 case DELETED_MESSAGES
:
744 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
745 data_message_stanza
.from(),
746 data_message_stanza
.ByteSize(),
748 GCMStatsRecorder::DELETED_MESSAGES
);
749 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
752 HandleIncomingSendError(data_message_stanza
, message_data
);
755 default: // Treat default the same as UNKNOWN.
756 DVLOG(1) << "Unknown message_type received. Message ignored. "
757 << "App ID: " << data_message_stanza
.category() << ".";
762 void GCMClientImpl::HandleIncomingDataMessage(
763 const mcs_proto::DataMessageStanza
& data_message_stanza
,
764 MessageData
& message_data
) {
765 std::string app_id
= data_message_stanza
.category();
767 // Drop the message when the app is not registered for the sender of the
769 RegistrationInfoMap::iterator iter
= registrations_
.find(app_id
);
770 bool not_registered
=
771 iter
== registrations_
.end() ||
772 std::find(iter
->second
->sender_ids
.begin(),
773 iter
->second
->sender_ids
.end(),
774 data_message_stanza
.from()) == iter
->second
->sender_ids
.end();
775 recorder_
.RecordDataMessageReceived(app_id
, data_message_stanza
.from(),
776 data_message_stanza
.ByteSize(), !not_registered
,
777 GCMStatsRecorder::DATA_MESSAGE
);
778 if (not_registered
) {
782 IncomingMessage incoming_message
;
783 incoming_message
.sender_id
= data_message_stanza
.from();
784 if (data_message_stanza
.has_token())
785 incoming_message
.collapse_key
= data_message_stanza
.token();
786 incoming_message
.data
= message_data
;
787 delegate_
->OnMessageReceived(app_id
, incoming_message
);
790 void GCMClientImpl::HandleIncomingSendError(
791 const mcs_proto::DataMessageStanza
& data_message_stanza
,
792 MessageData
& message_data
) {
793 SendErrorDetails send_error_details
;
794 send_error_details
.additional_data
= message_data
;
795 send_error_details
.result
= SERVER_ERROR
;
797 MessageData::iterator iter
=
798 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
799 if (iter
!= send_error_details
.additional_data
.end()) {
800 send_error_details
.message_id
= iter
->second
;
801 send_error_details
.additional_data
.erase(iter
);
804 recorder_
.RecordIncomingSendError(
805 data_message_stanza
.category(),
806 data_message_stanza
.to(),
807 data_message_stanza
.id());
808 delegate_
->OnMessageSendError(data_message_stanza
.category(),