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/engine/gservices_settings.h"
23 #include "google_apis/gcm/engine/mcs_client.h"
24 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "net/http/http_network_session.h"
27 #include "net/url_request/url_request_context.h"
34 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
36 // Note: In order to ensure a minimum of 20 seconds between server errors (for
37 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
38 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
40 const net::BackoffEntry::Policy kDefaultBackoffPolicy
= {
41 // Number of initial errors (in sequence) to ignore before applying
42 // exponential back-off rules.
45 // Initial delay for exponential back-off in ms.
46 30 * 1000, // 30 seconds.
48 // Factor by which the waiting time will be multiplied.
51 // Fuzzing percentage. ex: 10% will spread requests randomly
52 // between 90%-100% of the calculated time.
55 // Maximum amount of time we are willing to delay our request in ms.
56 10 * 60 * 1000, // 10 minutes.
58 // Time to keep an entry from being discarded even when it
59 // has no significant state, -1 to never discard.
62 // Don't use initial delay unless the last request was an error.
66 // Indicates a message type of the received message.
68 UNKNOWN
, // Undetermined type.
69 DATA_MESSAGE
, // Regular data message.
70 DELETED_MESSAGES
, // Messages were deleted on the server.
71 SEND_ERROR
, // Error sending a message.
74 // MCS endpoints. SSL Key pinning is done automatically due to the *.google.com
76 // Note: modifying the endpoints will affect the ability to compare the
77 // GCM.CurrentEnpoint histogram across versions.
78 const char kMCSEndpointMain
[] = "https://mtalk.google.com:5228";
79 const char kMCSEndpointFallback
[] = "https://mtalk.google.com:443";
81 const int kMaxRegistrationRetries
= 5;
82 const char kMessageTypeDataMessage
[] = "gcm";
83 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
84 const char kMessageTypeKey
[] = "message_type";
85 const char kMessageTypeSendErrorKey
[] = "send_error";
86 const char kSendErrorMessageIdKey
[] = "google.message_id";
87 const char kSendMessageFromValue
[] = "gcm@chrome.com";
88 const int64 kDefaultUserSerialNumber
= 0LL;
90 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus status
) {
92 case MCSClient::QUEUED
:
93 return GCMClient::SUCCESS
;
94 case MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
95 return GCMClient::NETWORK_ERROR
;
96 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
97 return GCMClient::NETWORK_ERROR
;
98 case MCSClient::MESSAGE_TOO_LARGE
:
99 return GCMClient::INVALID_PARAMETER
;
100 case MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
101 return GCMClient::NETWORK_ERROR
;
102 case MCSClient::TTL_EXCEEDED
:
103 return GCMClient::NETWORK_ERROR
;
104 case MCSClient::SENT
:
109 return GCMClientImpl::UNKNOWN_ERROR
;
112 MessageType
DecodeMessageType(const std::string
& value
) {
113 if (kMessageTypeDeletedMessagesKey
== value
)
114 return DELETED_MESSAGES
;
115 if (kMessageTypeSendErrorKey
== value
)
117 if (kMessageTypeDataMessage
== value
)
124 GCMInternalsBuilder::GCMInternalsBuilder() {}
125 GCMInternalsBuilder::~GCMInternalsBuilder() {}
127 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
128 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
131 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
132 const std::string
& version
,
134 ConnectionFactory
* connection_factory
,
136 GCMStatsRecorder
* recorder
) {
137 return make_scoped_ptr
<MCSClient
>(
138 new MCSClient(version
,
145 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
146 const std::vector
<GURL
>& endpoints
,
147 const net::BackoffEntry::Policy
& backoff_policy
,
148 scoped_refptr
<net::HttpNetworkSession
> network_session
,
149 net::NetLog
* net_log
,
150 GCMStatsRecorder
* recorder
) {
151 return make_scoped_ptr
<ConnectionFactory
>(
152 new ConnectionFactoryImpl(endpoints
,
159 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
160 : internals_builder_(internals_builder
.Pass()),
161 state_(UNINITIALIZED
),
162 clock_(internals_builder_
->BuildClock()),
163 url_request_context_getter_(NULL
),
164 pending_registration_requests_deleter_(&pending_registration_requests_
),
165 pending_unregistration_requests_deleter_(
166 &pending_unregistration_requests_
),
167 periodic_checkin_ptr_factory_(this),
168 weak_ptr_factory_(this) {
171 GCMClientImpl::~GCMClientImpl() {
174 void GCMClientImpl::Initialize(
175 const checkin_proto::ChromeBuildProto
& chrome_build_proto
,
176 const base::FilePath
& path
,
177 const std::vector
<std::string
>& account_ids
,
178 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
179 const scoped_refptr
<net::URLRequestContextGetter
>&
180 url_request_context_getter
,
181 Delegate
* delegate
) {
182 DCHECK_EQ(UNINITIALIZED
, state_
);
183 DCHECK(url_request_context_getter
);
186 url_request_context_getter_
= url_request_context_getter
;
187 const net::HttpNetworkSession::Params
* network_session_params
=
188 url_request_context_getter_
->GetURLRequestContext()->
189 GetNetworkSessionParams();
190 DCHECK(network_session_params
);
191 network_session_
= new net::HttpNetworkSession(*network_session_params
);
193 chrome_build_proto_
.CopyFrom(chrome_build_proto
);
194 account_ids_
= account_ids
;
196 gcm_store_
.reset(new GCMStoreImpl(path
, blocking_task_runner
));
197 gservices_settings_
.reset(new GServicesSettings(gcm_store_
.get()));
199 delegate_
= delegate
;
201 state_
= INITIALIZED
;
204 void GCMClientImpl::Load() {
205 DCHECK_EQ(INITIALIZED
, state_
);
207 // Once the loading is completed, the check-in will be initiated.
208 gcm_store_
->Load(base::Bind(&GCMClientImpl::OnLoadCompleted
,
209 weak_ptr_factory_
.GetWeakPtr()));
213 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
214 DCHECK_EQ(LOADING
, state_
);
216 if (!result
->success
) {
221 registrations_
= result
->registrations
;
222 device_checkin_info_
.android_id
= result
->device_android_id
;
223 device_checkin_info_
.secret
= result
->device_security_token
;
224 last_checkin_time_
= result
->last_checkin_time
;
225 gservices_settings_
->UpdateFromLoadResult(*result
);
226 InitializeMCSClient(result
.Pass());
228 if (device_checkin_info_
.IsValid()) {
229 SchedulePeriodicCheckin();
234 state_
= INITIAL_DEVICE_CHECKIN
;
235 device_checkin_info_
.Reset();
239 void GCMClientImpl::InitializeMCSClient(
240 scoped_ptr
<GCMStore::LoadResult
> result
) {
241 std::vector
<GURL
> endpoints
;
242 endpoints
.push_back(GURL(kMCSEndpointMain
));
243 endpoints
.push_back(GURL(kMCSEndpointFallback
));
244 connection_factory_
= internals_builder_
->BuildConnectionFactory(
246 kDefaultBackoffPolicy
,
250 mcs_client_
= internals_builder_
->BuildMCSClient(
251 chrome_build_proto_
.chrome_version(),
253 connection_factory_
.get(),
257 mcs_client_
->Initialize(
258 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
259 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
260 weak_ptr_factory_
.GetWeakPtr()),
261 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
262 weak_ptr_factory_
.GetWeakPtr()),
266 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
267 const CheckinInfo
& checkin_info
) {
268 DCHECK(!device_checkin_info_
.IsValid());
270 device_checkin_info_
.android_id
= checkin_info
.android_id
;
271 device_checkin_info_
.secret
= checkin_info
.secret
;
272 gcm_store_
->SetDeviceCredentials(
273 checkin_info
.android_id
, checkin_info
.secret
,
274 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
275 weak_ptr_factory_
.GetWeakPtr()));
280 void GCMClientImpl::OnReady() {
284 delegate_
->OnGCMReady();
287 void GCMClientImpl::StartMCSLogin() {
288 DCHECK_EQ(READY
, state_
);
289 DCHECK(device_checkin_info_
.IsValid());
290 mcs_client_
->Login(device_checkin_info_
.android_id
,
291 device_checkin_info_
.secret
);
294 void GCMClientImpl::ResetState() {
295 state_
= UNINITIALIZED
;
296 // TODO(fgorski): reset all of the necessart objects and start over.
299 void GCMClientImpl::StartCheckin() {
300 // Make sure no checkin is in progress.
301 if (checkin_request_
.get())
304 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
305 device_checkin_info_
.secret
,
306 gservices_settings_
->digest(),
308 chrome_build_proto_
);
309 checkin_request_
.reset(
310 new CheckinRequest(gservices_settings_
->checkin_url(),
312 kDefaultBackoffPolicy
,
313 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
314 weak_ptr_factory_
.GetWeakPtr()),
315 url_request_context_getter_
));
316 checkin_request_
->Start();
319 void GCMClientImpl::OnCheckinCompleted(
320 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
321 checkin_request_
.reset();
323 if (!checkin_response
.has_android_id() ||
324 !checkin_response
.has_security_token()) {
325 // TODO(fgorski): I don't think a retry here will help, we should probably
326 // start over. By checking in with (0, 0).
330 CheckinInfo checkin_info
;
331 checkin_info
.android_id
= checkin_response
.android_id();
332 checkin_info
.secret
= checkin_response
.security_token();
334 if (state_
== INITIAL_DEVICE_CHECKIN
) {
335 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
337 // checkin_info is not expected to change after a periodic checkin as it
338 // would invalidate the registratoin IDs.
339 DCHECK_EQ(READY
, state_
);
340 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
341 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
344 if (device_checkin_info_
.IsValid()) {
345 // First update G-services settings, as something might have changed.
346 gservices_settings_
->UpdateFromCheckinResponse(checkin_response
);
347 last_checkin_time_
= clock_
->Now();
348 gcm_store_
->SetLastCheckinTime(
350 base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback
,
351 weak_ptr_factory_
.GetWeakPtr()));
352 SchedulePeriodicCheckin();
356 void GCMClientImpl::SchedulePeriodicCheckin() {
357 // Make sure no checkin is in progress.
358 if (checkin_request_
.get())
361 // There should be only one periodic checkin pending at a time. Removing
362 // pending periodic checkin to schedule a new one.
363 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
365 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
366 if (time_to_next_checkin
< base::TimeDelta())
367 time_to_next_checkin
= base::TimeDelta();
369 base::MessageLoop::current()->PostDelayedTask(
371 base::Bind(&GCMClientImpl::StartCheckin
,
372 periodic_checkin_ptr_factory_
.GetWeakPtr()),
373 time_to_next_checkin
);
376 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
377 return last_checkin_time_
+ gservices_settings_
->checkin_interval() -
381 void GCMClientImpl::SetLastCheckinTimeCallback(bool success
) {
382 // TODO(fgorski): This is one of the signals that store needs a rebuild.
386 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
387 // TODO(fgorski): This is one of the signals that store needs a rebuild.
391 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
392 // TODO(fgorski): This is one of the signals that store needs a rebuild.
396 void GCMClientImpl::Stop() {
397 device_checkin_info_
.Reset();
398 connection_factory_
.reset();
400 checkin_request_
.reset();
401 pending_registration_requests_
.clear();
402 state_
= INITIALIZED
;
406 void GCMClientImpl::CheckOut() {
408 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed
,
409 weak_ptr_factory_
.GetWeakPtr()));
412 void GCMClientImpl::Register(const std::string
& app_id
,
413 const std::vector
<std::string
>& sender_ids
) {
414 DCHECK_EQ(state_
, READY
);
416 // If the same sender ids is provided, return the cached registration ID
418 RegistrationInfoMap::const_iterator registrations_iter
=
419 registrations_
.find(app_id
);
420 if (registrations_iter
!= registrations_
.end() &&
421 registrations_iter
->second
->sender_ids
== sender_ids
) {
422 delegate_
->OnRegisterFinished(
423 app_id
, registrations_iter
->second
->registration_id
, SUCCESS
);
427 RegistrationRequest::RequestInfo
request_info(
428 device_checkin_info_
.android_id
,
429 device_checkin_info_
.secret
,
432 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
434 RegistrationRequest
* registration_request
=
435 new RegistrationRequest(gservices_settings_
->registration_url(),
437 kDefaultBackoffPolicy
,
438 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
439 weak_ptr_factory_
.GetWeakPtr(),
442 kMaxRegistrationRetries
,
443 url_request_context_getter_
,
445 pending_registration_requests_
[app_id
] = registration_request
;
446 registration_request
->Start();
449 void GCMClientImpl::OnRegisterCompleted(
450 const std::string
& app_id
,
451 const std::vector
<std::string
>& sender_ids
,
452 RegistrationRequest::Status status
,
453 const std::string
& registration_id
) {
457 PendingRegistrationRequests::iterator iter
=
458 pending_registration_requests_
.find(app_id
);
459 if (iter
== pending_registration_requests_
.end())
460 result
= UNKNOWN_ERROR
;
461 else if (status
== RegistrationRequest::INVALID_SENDER
)
462 result
= INVALID_PARAMETER
;
463 else if (registration_id
.empty())
464 result
= SERVER_ERROR
;
468 if (result
== SUCCESS
) {
470 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
471 registration
->sender_ids
= sender_ids
;
472 registration
->registration_id
= registration_id
;
473 registrations_
[app_id
] = registration
;
475 // Save it in the persistent store.
476 gcm_store_
->AddRegistration(
479 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
480 weak_ptr_factory_
.GetWeakPtr()));
483 delegate_
->OnRegisterFinished(
484 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
486 if (iter
!= pending_registration_requests_
.end()) {
488 pending_registration_requests_
.erase(iter
);
492 void GCMClientImpl::Unregister(const std::string
& app_id
) {
493 DCHECK_EQ(state_
, READY
);
494 if (pending_unregistration_requests_
.count(app_id
) == 1)
497 // Remove from the cache and persistent store.
498 registrations_
.erase(app_id
);
499 gcm_store_
->RemoveRegistration(
501 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
502 weak_ptr_factory_
.GetWeakPtr()));
504 UnregistrationRequest::RequestInfo
request_info(
505 device_checkin_info_
.android_id
,
506 device_checkin_info_
.secret
,
509 UnregistrationRequest
* unregistration_request
=
510 new UnregistrationRequest(
511 gservices_settings_
->registration_url(),
513 kDefaultBackoffPolicy
,
514 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
515 weak_ptr_factory_
.GetWeakPtr(),
517 url_request_context_getter_
,
519 pending_unregistration_requests_
[app_id
] = unregistration_request
;
520 unregistration_request
->Start();
523 void GCMClientImpl::OnUnregisterCompleted(
524 const std::string
& app_id
,
525 UnregistrationRequest::Status status
) {
526 DVLOG(1) << "Unregister completed for app: " << app_id
527 << " with " << (status
? "success." : "failure.");
528 delegate_
->OnUnregisterFinished(
530 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
532 PendingUnregistrationRequests::iterator iter
=
533 pending_unregistration_requests_
.find(app_id
);
534 if (iter
== pending_unregistration_requests_
.end())
538 pending_unregistration_requests_
.erase(iter
);
541 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
542 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
543 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
546 void GCMClientImpl::Send(const std::string
& app_id
,
547 const std::string
& receiver_id
,
548 const OutgoingMessage
& message
) {
549 DCHECK_EQ(state_
, READY
);
551 mcs_proto::DataMessageStanza stanza
;
552 stanza
.set_ttl(message
.time_to_live
);
553 stanza
.set_sent(clock_
->Now().ToInternalValue() /
554 base::Time::kMicrosecondsPerSecond
);
555 stanza
.set_id(message
.id
);
556 stanza
.set_from(kSendMessageFromValue
);
557 stanza
.set_to(receiver_id
);
558 stanza
.set_category(app_id
);
560 for (MessageData::const_iterator iter
= message
.data
.begin();
561 iter
!= message
.data
.end();
563 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
564 app_data
->set_key(iter
->first
);
565 app_data
->set_value(iter
->second
);
568 MCSMessage
mcs_message(stanza
);
569 DVLOG(1) << "MCS message size: " << mcs_message
.size();
570 mcs_client_
->SendMessage(mcs_message
);
573 std::string
GCMClientImpl::GetStateString() const {
575 case GCMClientImpl::INITIALIZED
:
576 return "INITIALIZED";
577 case GCMClientImpl::UNINITIALIZED
:
578 return "UNINITIALIZED";
579 case GCMClientImpl::LOADING
:
581 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
582 return "INITIAL_DEVICE_CHECKIN";
583 case GCMClientImpl::READY
:
587 return std::string();
591 void GCMClientImpl::SetRecording(bool recording
) {
592 recorder_
.SetRecording(recording
);
595 void GCMClientImpl::ClearActivityLogs() {
599 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
600 GCMClient::GCMStatistics stats
;
601 stats
.gcm_client_created
= true;
602 stats
.is_recording
= recorder_
.is_recording();
603 stats
.gcm_client_state
= GetStateString();
604 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
605 if (mcs_client_
.get()) {
606 stats
.connection_state
= mcs_client_
->GetStateString();
607 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
608 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
610 if (device_checkin_info_
.android_id
> 0)
611 stats
.android_id
= device_checkin_info_
.android_id
;
612 recorder_
.CollectActivities(&stats
.recorded_activities
);
614 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
615 it
!= registrations_
.end(); ++it
) {
616 stats
.registered_app_ids
.push_back(it
->first
);
621 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
622 switch (message
.tag()) {
623 case kLoginResponseTag
:
624 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
626 case kDataMessageStanzaTag
:
627 DVLOG(1) << "A downstream message received. Processing...";
628 HandleIncomingMessage(message
);
631 NOTREACHED() << "Message with unexpected tag received by GCMClient";
636 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
637 const std::string
& app_id
,
638 const std::string
& message_id
,
639 MCSClient::MessageSendStatus status
) {
640 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
643 // TTL_EXCEEDED is singled out here, because it can happen long time after the
644 // message was sent. That is why it comes as |OnMessageSendError| event rather
645 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
646 // All other errors will be raised immediately, through asynchronous callback.
647 // It is expected that TTL_EXCEEDED will be issued for a message that was
648 // previously issued |OnSendFinished| with status SUCCESS.
649 // For now, we do not report that the message has been sent and acked
651 // TODO(jianli): Consider adding UMA for this status.
652 if (status
== MCSClient::TTL_EXCEEDED
) {
653 SendErrorDetails send_error_details
;
654 send_error_details
.message_id
= message_id
;
655 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
656 delegate_
->OnMessageSendError(app_id
, send_error_details
);
657 } else if (status
!= MCSClient::SENT
) {
658 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
662 void GCMClientImpl::OnMCSError() {
663 // TODO(fgorski): For now it replaces the initialization method. Long term it
664 // should have an error or status passed in.
667 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
670 const mcs_proto::DataMessageStanza
& data_message_stanza
=
671 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
672 message
.GetProtobuf());
673 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
675 // Copying all the data from the stanza to a MessageData object. When present,
676 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
678 MessageData message_data
;
679 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
680 std::string key
= data_message_stanza
.app_data(i
).key();
681 message_data
[key
] = data_message_stanza
.app_data(i
).value();
684 MessageType message_type
= DATA_MESSAGE
;
685 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
686 if (iter
!= message_data
.end()) {
687 message_type
= DecodeMessageType(iter
->second
);
688 message_data
.erase(iter
);
691 switch (message_type
) {
693 HandleIncomingDataMessage(data_message_stanza
, message_data
);
695 case DELETED_MESSAGES
:
696 recorder_
.RecordDataMessageRecieved(data_message_stanza
.category(),
697 data_message_stanza
.from(),
698 data_message_stanza
.ByteSize(),
700 GCMStatsRecorder::DELETED_MESSAGES
);
701 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
704 HandleIncomingSendError(data_message_stanza
, message_data
);
707 default: // Treat default the same as UNKNOWN.
708 DVLOG(1) << "Unknown message_type received. Message ignored. "
709 << "App ID: " << data_message_stanza
.category() << ".";
714 void GCMClientImpl::HandleIncomingDataMessage(
715 const mcs_proto::DataMessageStanza
& data_message_stanza
,
716 MessageData
& message_data
) {
717 std::string app_id
= data_message_stanza
.category();
719 // Drop the message when the app is not registered for the sender of the
721 RegistrationInfoMap::iterator iter
= registrations_
.find(app_id
);
722 bool not_registered
=
723 iter
== registrations_
.end() ||
724 std::find(iter
->second
->sender_ids
.begin(),
725 iter
->second
->sender_ids
.end(),
726 data_message_stanza
.from()) == iter
->second
->sender_ids
.end();
727 recorder_
.RecordDataMessageRecieved(app_id
, data_message_stanza
.from(),
728 data_message_stanza
.ByteSize(), !not_registered
,
729 GCMStatsRecorder::DATA_MESSAGE
);
730 if (not_registered
) {
734 IncomingMessage incoming_message
;
735 incoming_message
.sender_id
= data_message_stanza
.from();
736 if (data_message_stanza
.has_token())
737 incoming_message
.collapse_key
= data_message_stanza
.token();
738 incoming_message
.data
= message_data
;
739 delegate_
->OnMessageReceived(app_id
, incoming_message
);
742 void GCMClientImpl::HandleIncomingSendError(
743 const mcs_proto::DataMessageStanza
& data_message_stanza
,
744 MessageData
& message_data
) {
745 SendErrorDetails send_error_details
;
746 send_error_details
.additional_data
= message_data
;
747 send_error_details
.result
= SERVER_ERROR
;
749 MessageData::iterator iter
=
750 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
751 if (iter
!= send_error_details
.additional_data
.end()) {
752 send_error_details
.message_id
= iter
->second
;
753 send_error_details
.additional_data
.erase(iter
);
756 recorder_
.RecordIncomingSendError(
757 data_message_stanza
.category(),
758 data_message_stanza
.to(),
759 data_message_stanza
.id());
760 delegate_
->OnMessageSendError(data_message_stanza
.category(),