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/location.h"
10 #include "base/logging.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/metrics/histogram_macros.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/stl_util.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/time/default_clock.h"
20 #include "base/timer/timer.h"
21 #include "components/gcm_driver/gcm_account_mapper.h"
22 #include "components/gcm_driver/gcm_backoff_policy.h"
23 #include "google_apis/gcm/base/encryptor.h"
24 #include "google_apis/gcm/base/mcs_message.h"
25 #include "google_apis/gcm/base/mcs_util.h"
26 #include "google_apis/gcm/engine/checkin_request.h"
27 #include "google_apis/gcm/engine/connection_factory_impl.h"
28 #include "google_apis/gcm/engine/gcm_registration_request_handler.h"
29 #include "google_apis/gcm/engine/gcm_store_impl.h"
30 #include "google_apis/gcm/engine/gcm_unregistration_request_handler.h"
31 #include "google_apis/gcm/engine/instance_id_delete_token_request_handler.h"
32 #include "google_apis/gcm/engine/instance_id_get_token_request_handler.h"
33 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
34 #include "google_apis/gcm/protocol/checkin.pb.h"
35 #include "google_apis/gcm/protocol/mcs.pb.h"
36 #include "net/http/http_network_session.h"
37 #include "net/http/http_transaction_factory.h"
38 #include "net/url_request/url_request_context.h"
45 // Indicates a message type of the received message.
47 UNKNOWN
, // Undetermined type.
48 DATA_MESSAGE
, // Regular data message.
49 DELETED_MESSAGES
, // Messages were deleted on the server.
50 SEND_ERROR
, // Error sending a message.
53 enum OutgoingMessageTTLCategory
{
55 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
,
56 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
,
57 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
,
58 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK
,
59 TTL_MORE_THAN_ONE_WEEK
,
61 // NOTE: always keep this entry at the end. Add new TTL category only
62 // immediately above this line. Make sure to update the corresponding
63 // histogram enum accordingly.
67 enum ResetStoreError
{
68 DESTROYING_STORE_FAILED
,
70 // NOTE: always keep this entry at the end. Add new value only immediately
71 // above this line. Make sure to update the corresponding histogram enum
73 RESET_STORE_ERROR_COUNT
76 const char kGCMScope
[] = "GCM";
77 const int kMaxRegistrationRetries
= 5;
78 const int kMaxUnregistrationRetries
= 5;
79 const char kMessageTypeDataMessage
[] = "gcm";
80 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
81 const char kMessageTypeKey
[] = "message_type";
82 const char kMessageTypeSendErrorKey
[] = "send_error";
83 const char kSendErrorMessageIdKey
[] = "google.message_id";
84 const char kSendMessageFromValue
[] = "gcm@chrome.com";
85 const int64 kDefaultUserSerialNumber
= 0LL;
86 const int kDestroyGCMStoreDelayMS
= 5 * 60 * 1000; // 5 minutes.
88 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus status
) {
90 case MCSClient::QUEUED
:
91 return GCMClient::SUCCESS
;
92 case MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
93 return GCMClient::NETWORK_ERROR
;
94 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
95 return GCMClient::NETWORK_ERROR
;
96 case MCSClient::MESSAGE_TOO_LARGE
:
97 return GCMClient::INVALID_PARAMETER
;
98 case MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
99 return GCMClient::NETWORK_ERROR
;
100 case MCSClient::TTL_EXCEEDED
:
101 return GCMClient::NETWORK_ERROR
;
102 case MCSClient::SENT
:
107 return GCMClientImpl::UNKNOWN_ERROR
;
110 void ToCheckinProtoVersion(
111 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
112 checkin_proto::ChromeBuildProto
* android_build_info
) {
113 checkin_proto::ChromeBuildProto_Platform platform
;
114 switch (chrome_build_info
.platform
) {
115 case GCMClient::PLATFORM_WIN
:
116 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN
;
118 case GCMClient::PLATFORM_MAC
:
119 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC
;
121 case GCMClient::PLATFORM_LINUX
:
122 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
124 case GCMClient::PLATFORM_IOS
:
125 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS
;
127 case GCMClient::PLATFORM_ANDROID
:
128 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID
;
130 case GCMClient::PLATFORM_CROS
:
131 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS
;
133 case GCMClient::PLATFORM_UNKNOWN
:
134 // For unknown platform, return as LINUX.
135 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
139 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
142 android_build_info
->set_platform(platform
);
144 checkin_proto::ChromeBuildProto_Channel channel
;
145 switch (chrome_build_info
.channel
) {
146 case GCMClient::CHANNEL_STABLE
:
147 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE
;
149 case GCMClient::CHANNEL_BETA
:
150 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA
;
152 case GCMClient::CHANNEL_DEV
:
153 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV
;
155 case GCMClient::CHANNEL_CANARY
:
156 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY
;
158 case GCMClient::CHANNEL_UNKNOWN
:
159 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
163 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
166 android_build_info
->set_channel(channel
);
168 android_build_info
->set_chrome_version(chrome_build_info
.version
);
171 MessageType
DecodeMessageType(const std::string
& value
) {
172 if (kMessageTypeDeletedMessagesKey
== value
)
173 return DELETED_MESSAGES
;
174 if (kMessageTypeSendErrorKey
== value
)
176 if (kMessageTypeDataMessage
== value
)
181 int ConstructGCMVersion(const std::string
& chrome_version
) {
182 // Major Chrome version is passed as GCM version.
183 size_t pos
= chrome_version
.find('.');
184 if (pos
== std::string::npos
) {
191 base::StringPiece(chrome_version
.c_str(), pos
), &gcm_version
);
195 std::string
SerializeInstanceIDData(const std::string
& instance_id
,
196 const std::string
& extra_data
) {
197 DCHECK(!instance_id
.empty() && !extra_data
.empty());
198 DCHECK(instance_id
.find(',') == std::string::npos
);
199 return instance_id
+ "," + extra_data
;
202 bool DeserializeInstanceIDData(const std::string
& serialized_data
,
203 std::string
* instance_id
,
204 std::string
* extra_data
) {
205 DCHECK(instance_id
&& extra_data
);
206 std::size_t pos
= serialized_data
.find(',');
207 if (pos
== std::string::npos
)
209 *instance_id
= serialized_data
.substr(0, pos
);
210 *extra_data
= serialized_data
.substr(pos
+ 1);
211 return !instance_id
->empty() && !extra_data
->empty();
214 void RecordOutgoingMessageToUMA(const gcm::OutgoingMessage
& message
) {
215 OutgoingMessageTTLCategory ttl_category
;
216 if (message
.time_to_live
== 0)
217 ttl_category
= TTL_ZERO
;
218 else if (message
.time_to_live
<= 60 )
219 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
;
220 else if (message
.time_to_live
<= 60 * 60)
221 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
;
222 else if (message
.time_to_live
<= 24 * 60 * 60)
223 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
;
225 ttl_category
= TTL_MAXIMUM
;
227 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
232 void RecordResetStoreErrorToUMA(ResetStoreError error
) {
233 UMA_HISTOGRAM_ENUMERATION("GCM.ResetStore", error
, RESET_STORE_ERROR_COUNT
);
238 GCMInternalsBuilder::GCMInternalsBuilder() {}
239 GCMInternalsBuilder::~GCMInternalsBuilder() {}
241 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
242 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
245 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
246 const std::string
& version
,
248 ConnectionFactory
* connection_factory
,
250 GCMStatsRecorder
* recorder
) {
251 return scoped_ptr
<MCSClient
>(new MCSClient(
252 version
, clock
, connection_factory
, gcm_store
, recorder
));
255 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
256 const std::vector
<GURL
>& endpoints
,
257 const net::BackoffEntry::Policy
& backoff_policy
,
258 const scoped_refptr
<net::HttpNetworkSession
>& gcm_network_session
,
259 const scoped_refptr
<net::HttpNetworkSession
>& http_network_session
,
260 net::NetLog
* net_log
,
261 GCMStatsRecorder
* recorder
) {
262 return make_scoped_ptr
<ConnectionFactory
>(
263 new ConnectionFactoryImpl(endpoints
,
266 http_network_session
,
271 GCMClientImpl::CheckinInfo::CheckinInfo()
272 : android_id(0), secret(0), accounts_set(false) {
275 GCMClientImpl::CheckinInfo::~CheckinInfo() {
278 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
279 last_checkin_accounts
.clear();
280 for (std::map
<std::string
, std::string
>::iterator iter
=
281 account_tokens
.begin();
282 iter
!= account_tokens
.end();
284 last_checkin_accounts
.insert(iter
->first
);
288 void GCMClientImpl::CheckinInfo::Reset() {
291 accounts_set
= false;
292 account_tokens
.clear();
293 last_checkin_accounts
.clear();
296 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
297 : internals_builder_(internals_builder
.Pass()),
298 state_(UNINITIALIZED
),
300 start_mode_(DELAYED_START
),
301 clock_(internals_builder_
->BuildClock()),
302 gcm_store_reset_(false),
303 url_request_context_getter_(NULL
),
304 periodic_checkin_ptr_factory_(this),
305 destroying_gcm_store_ptr_factory_(this),
306 weak_ptr_factory_(this) {
309 GCMClientImpl::~GCMClientImpl() {
312 void GCMClientImpl::Initialize(
313 const ChromeBuildInfo
& chrome_build_info
,
314 const base::FilePath
& path
,
315 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
316 const scoped_refptr
<net::URLRequestContextGetter
>&
317 url_request_context_getter
,
318 scoped_ptr
<Encryptor
> encryptor
,
319 GCMClient::Delegate
* delegate
) {
320 DCHECK_EQ(UNINITIALIZED
, state_
);
321 DCHECK(url_request_context_getter
.get());
324 url_request_context_getter_
= url_request_context_getter
;
325 const net::HttpNetworkSession::Params
* network_session_params
=
326 url_request_context_getter_
->GetURLRequestContext()->
327 GetNetworkSessionParams();
328 DCHECK(network_session_params
);
329 network_session_
= new net::HttpNetworkSession(*network_session_params
);
331 chrome_build_info_
= chrome_build_info
;
334 new GCMStoreImpl(path
, blocking_task_runner
, encryptor
.Pass()));
336 delegate_
= delegate
;
338 recorder_
.SetDelegate(this);
340 state_
= INITIALIZED
;
343 void GCMClientImpl::Start(StartMode start_mode
) {
344 DCHECK_NE(UNINITIALIZED
, state_
);
346 if (state_
== LOADED
) {
347 // Start the GCM if not yet.
348 if (start_mode
== IMMEDIATE_START
) {
349 // Give up the scheduling to wipe out the store since now some one starts
351 destroying_gcm_store_ptr_factory_
.InvalidateWeakPtrs();
358 // The delay start behavior will be abandoned when Start has been called
359 // once with IMMEDIATE_START behavior.
360 if (start_mode
== IMMEDIATE_START
)
361 start_mode_
= IMMEDIATE_START
;
363 // Bail out if the loading is not started or completed.
364 if (state_
!= INITIALIZED
)
367 // Once the loading is completed, the check-in will be initiated.
368 // If we're in lazy start mode, don't create a new store since none is really
369 // using GCM functionality yet.
371 (start_mode
== IMMEDIATE_START
) ?
372 GCMStore::CREATE_IF_MISSING
:
373 GCMStore::DO_NOT_CREATE
,
374 base::Bind(&GCMClientImpl::OnLoadCompleted
,
375 weak_ptr_factory_
.GetWeakPtr()));
379 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
380 DCHECK_EQ(LOADING
, state_
);
382 if (!result
->success
) {
383 if (result
->store_does_not_exist
) {
384 if (start_mode_
== IMMEDIATE_START
) {
385 // An immediate start was requested during the delayed start that just
386 // completed. Perform it now.
387 gcm_store_
->Load(GCMStore::CREATE_IF_MISSING
,
388 base::Bind(&GCMClientImpl::OnLoadCompleted
,
389 weak_ptr_factory_
.GetWeakPtr()));
391 // In the case that the store does not exist, set |state_| back to
392 // INITIALIZED such that store loading could be triggered again when
393 // Start() is called with IMMEDIATE_START.
394 state_
= INITIALIZED
;
397 // Otherwise, destroy the store to try again.
402 gcm_store_reset_
= false;
404 device_checkin_info_
.android_id
= result
->device_android_id
;
405 device_checkin_info_
.secret
= result
->device_security_token
;
406 device_checkin_info_
.last_checkin_accounts
= result
->last_checkin_accounts
;
407 // A case where there were previously no accounts reported with checkin is
408 // considered to be the same as when the list of accounts is empty. It enables
409 // scheduling a periodic checkin for devices with no signed in users
410 // immediately after restart, while keeping |accounts_set == false| delays the
411 // checkin until the list of accounts is set explicitly.
412 if (result
->last_checkin_accounts
.size() == 0)
413 device_checkin_info_
.accounts_set
= true;
414 last_checkin_time_
= result
->last_checkin_time
;
415 gservices_settings_
.UpdateFromLoadResult(*result
);
417 for (auto iter
= result
->registrations
.begin();
418 iter
!= result
->registrations
.end();
420 std::string registration_id
;
421 scoped_ptr
<RegistrationInfo
> registration
=
422 RegistrationInfo::BuildFromString(
423 iter
->first
, iter
->second
, ®istration_id
);
424 // TODO(jianli): Add UMA to track the error case.
425 if (registration
.get())
426 registrations_
[make_linked_ptr(registration
.release())] = registration_id
;
429 for (auto iter
= result
->instance_id_data
.begin();
430 iter
!= result
->instance_id_data
.end();
432 std::string instance_id
;
433 std::string extra_data
;
434 if (DeserializeInstanceIDData(iter
->second
, &instance_id
, &extra_data
))
435 instance_id_data_
[iter
->first
] = std::make_pair(instance_id
, extra_data
);
438 load_result_
= result
.Pass();
441 // Don't initiate the GCM connection when GCM is in delayed start mode and
442 // not any standalone app has registered GCM yet.
443 if (start_mode_
== DELAYED_START
&& !HasStandaloneRegisteredApp()) {
444 // If no standalone app is using GCM and the device ID is present, schedule
445 // to have the store wiped out.
446 if (device_checkin_info_
.android_id
) {
447 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
448 FROM_HERE
, base::Bind(&GCMClientImpl::DestroyStoreWhenNotNeeded
,
449 destroying_gcm_store_ptr_factory_
.GetWeakPtr()),
450 base::TimeDelta::FromMilliseconds(kDestroyGCMStoreDelayMS
));
459 void GCMClientImpl::StartGCM() {
460 // Taking over the value of account_mappings before passing the ownership of
461 // load result to InitializeMCSClient.
462 std::vector
<AccountMapping
> account_mappings
;
463 account_mappings
.swap(load_result_
->account_mappings
);
464 base::Time last_token_fetch_time
= load_result_
->last_token_fetch_time
;
466 InitializeMCSClient();
468 if (device_checkin_info_
.IsValid()) {
469 SchedulePeriodicCheckin();
470 OnReady(account_mappings
, last_token_fetch_time
);
474 state_
= INITIAL_DEVICE_CHECKIN
;
475 device_checkin_info_
.Reset();
479 void GCMClientImpl::InitializeMCSClient() {
480 std::vector
<GURL
> endpoints
;
481 endpoints
.push_back(gservices_settings_
.GetMCSMainEndpoint());
482 endpoints
.push_back(gservices_settings_
.GetMCSFallbackEndpoint());
483 connection_factory_
= internals_builder_
->BuildConnectionFactory(
485 GetGCMBackoffPolicy(),
487 url_request_context_getter_
->GetURLRequestContext()
488 ->http_transaction_factory()
492 connection_factory_
->SetConnectionListener(this);
493 mcs_client_
= internals_builder_
->BuildMCSClient(
494 chrome_build_info_
.version
,
496 connection_factory_
.get(),
500 mcs_client_
->Initialize(
501 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
502 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
503 weak_ptr_factory_
.GetWeakPtr()),
504 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
505 weak_ptr_factory_
.GetWeakPtr()),
506 load_result_
.Pass());
509 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
510 const CheckinInfo
& checkin_info
) {
511 DCHECK(!device_checkin_info_
.IsValid());
513 device_checkin_info_
.android_id
= checkin_info
.android_id
;
514 device_checkin_info_
.secret
= checkin_info
.secret
;
515 // If accounts were not set by now, we can consider them set (to empty list)
516 // to make sure periodic checkins get scheduled after initial checkin.
517 device_checkin_info_
.accounts_set
= true;
518 gcm_store_
->SetDeviceCredentials(
519 checkin_info
.android_id
, checkin_info
.secret
,
520 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
521 weak_ptr_factory_
.GetWeakPtr()));
523 OnReady(std::vector
<AccountMapping
>(), base::Time());
526 void GCMClientImpl::OnReady(const std::vector
<AccountMapping
>& account_mappings
,
527 const base::Time
& last_token_fetch_time
) {
531 delegate_
->OnGCMReady(account_mappings
, last_token_fetch_time
);
534 void GCMClientImpl::StartMCSLogin() {
535 DCHECK_EQ(READY
, state_
);
536 DCHECK(device_checkin_info_
.IsValid());
537 mcs_client_
->Login(device_checkin_info_
.android_id
,
538 device_checkin_info_
.secret
);
541 void GCMClientImpl::DestroyStoreWhenNotNeeded() {
542 if (state_
!= LOADED
|| start_mode_
!= DELAYED_START
)
545 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::DestroyStoreCallback
,
546 weak_ptr_factory_
.GetWeakPtr()));
549 void GCMClientImpl::ResetStore() {
550 DCHECK_EQ(LOADING
, state_
);
552 // If already being reset, don't do it again. We want to prevent from
553 // resetting and loading from the store again and again.
554 if (gcm_store_reset_
) {
555 RecordResetStoreErrorToUMA(INFINITE_STORE_RESET
);
556 state_
= UNINITIALIZED
;
559 gcm_store_reset_
= true;
561 // Destroy the GCM store to start over.
562 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::ResetStoreCallback
,
563 weak_ptr_factory_
.GetWeakPtr()));
566 void GCMClientImpl::SetAccountTokens(
567 const std::vector
<AccountTokenInfo
>& account_tokens
) {
568 device_checkin_info_
.account_tokens
.clear();
569 for (std::vector
<AccountTokenInfo
>::const_iterator iter
=
570 account_tokens
.begin();
571 iter
!= account_tokens
.end();
573 device_checkin_info_
.account_tokens
[iter
->email
] = iter
->access_token
;
576 bool accounts_set_before
= device_checkin_info_
.accounts_set
;
577 device_checkin_info_
.accounts_set
= true;
579 DVLOG(1) << "Set account called with: " << account_tokens
.size()
582 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
585 bool account_removed
= false;
586 for (std::set
<std::string
>::iterator iter
=
587 device_checkin_info_
.last_checkin_accounts
.begin();
588 iter
!= device_checkin_info_
.last_checkin_accounts
.end();
590 if (device_checkin_info_
.account_tokens
.find(*iter
) ==
591 device_checkin_info_
.account_tokens
.end()) {
592 account_removed
= true;
596 // Checkin will be forced when any of the accounts was removed during the
597 // current Chrome session or if there has been an account removed between the
598 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
599 // We only force checkin when user signs out. When there is a new account
600 // signed in, the periodic checkin will take care of adding the association in
602 if (account_removed
) {
603 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
604 checkin_request_
.reset();
606 } else if (!accounts_set_before
) {
607 SchedulePeriodicCheckin();
608 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
612 void GCMClientImpl::UpdateAccountMapping(
613 const AccountMapping
& account_mapping
) {
614 gcm_store_
->AddAccountMapping(account_mapping
,
615 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
616 weak_ptr_factory_
.GetWeakPtr()));
619 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
620 gcm_store_
->RemoveAccountMapping(
622 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
623 weak_ptr_factory_
.GetWeakPtr()));
626 void GCMClientImpl::SetLastTokenFetchTime(const base::Time
& time
) {
627 gcm_store_
->SetLastTokenFetchTime(
629 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
630 weak_ptr_factory_
.GetWeakPtr()));
633 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr
<base::Timer
> timer
) {
635 mcs_client_
->UpdateHeartbeatTimer(timer
.Pass());
638 void GCMClientImpl::AddInstanceIDData(const std::string
& app_id
,
639 const std::string
& instance_id
,
640 const std::string
& extra_data
) {
641 instance_id_data_
[app_id
] = std::make_pair(instance_id
, extra_data
);
642 gcm_store_
->AddInstanceIDData(
644 SerializeInstanceIDData(instance_id
, extra_data
),
645 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
646 weak_ptr_factory_
.GetWeakPtr()));
649 void GCMClientImpl::RemoveInstanceIDData(const std::string
& app_id
) {
650 instance_id_data_
.erase(app_id
);
651 gcm_store_
->RemoveInstanceIDData(
653 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
654 weak_ptr_factory_
.GetWeakPtr()));
657 void GCMClientImpl::GetInstanceIDData(const std::string
& app_id
,
658 std::string
* instance_id
,
659 std::string
* extra_data
) {
660 DCHECK(instance_id
&& extra_data
);
662 auto iter
= instance_id_data_
.find(app_id
);
663 if (iter
== instance_id_data_
.end())
665 *instance_id
= iter
->second
.first
;
666 *extra_data
= iter
->second
.second
;
669 void GCMClientImpl::AddHeartbeatInterval(const std::string
& scope
,
672 mcs_client_
->AddHeartbeatInterval(scope
, interval_ms
);
675 void GCMClientImpl::RemoveHeartbeatInterval(const std::string
& scope
) {
677 mcs_client_
->RemoveHeartbeatInterval(scope
);
680 void GCMClientImpl::StartCheckin() {
681 // Make sure no checkin is in progress.
682 if (checkin_request_
.get())
685 checkin_proto::ChromeBuildProto chrome_build_proto
;
686 ToCheckinProtoVersion(chrome_build_info_
, &chrome_build_proto
);
687 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
688 device_checkin_info_
.secret
,
689 device_checkin_info_
.account_tokens
,
690 gservices_settings_
.digest(),
692 checkin_request_
.reset(
693 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
695 GetGCMBackoffPolicy(),
696 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
697 weak_ptr_factory_
.GetWeakPtr()),
698 url_request_context_getter_
.get(),
700 // Taking a snapshot of the accounts count here, as there might be an asynch
701 // update of the account tokens while checkin is in progress.
702 device_checkin_info_
.SnapshotCheckinAccounts();
703 checkin_request_
->Start();
706 void GCMClientImpl::OnCheckinCompleted(
707 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
708 checkin_request_
.reset();
710 if (!checkin_response
.has_android_id() ||
711 !checkin_response
.has_security_token()) {
712 // TODO(fgorski): I don't think a retry here will help, we should probably
713 // start over. By checking in with (0, 0).
717 CheckinInfo checkin_info
;
718 checkin_info
.android_id
= checkin_response
.android_id();
719 checkin_info
.secret
= checkin_response
.security_token();
721 if (state_
== INITIAL_DEVICE_CHECKIN
) {
722 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
724 // checkin_info is not expected to change after a periodic checkin as it
725 // would invalidate the registratoin IDs.
726 DCHECK_EQ(READY
, state_
);
727 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
728 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
731 if (device_checkin_info_
.IsValid()) {
732 // First update G-services settings, as something might have changed.
733 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
734 gcm_store_
->SetGServicesSettings(
735 gservices_settings_
.settings_map(),
736 gservices_settings_
.digest(),
737 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
738 weak_ptr_factory_
.GetWeakPtr()));
741 last_checkin_time_
= clock_
->Now();
742 gcm_store_
->SetLastCheckinInfo(
744 device_checkin_info_
.last_checkin_accounts
,
745 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
746 weak_ptr_factory_
.GetWeakPtr()));
747 SchedulePeriodicCheckin();
751 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
755 void GCMClientImpl::SchedulePeriodicCheckin() {
756 // Make sure no checkin is in progress.
757 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
760 // There should be only one periodic checkin pending at a time. Removing
761 // pending periodic checkin to schedule a new one.
762 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
764 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
765 if (time_to_next_checkin
< base::TimeDelta())
766 time_to_next_checkin
= base::TimeDelta();
768 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
769 FROM_HERE
, base::Bind(&GCMClientImpl::StartCheckin
,
770 periodic_checkin_ptr_factory_
.GetWeakPtr()),
771 time_to_next_checkin
);
774 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
775 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
779 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
780 // TODO(fgorski): This is one of the signals that store needs a rebuild.
784 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
785 // TODO(fgorski): This is one of the signals that store needs a rebuild.
789 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
790 // TODO(fgorski): This is one of the signals that store needs a rebuild.
794 void GCMClientImpl::DefaultStoreCallback(bool success
) {
798 void GCMClientImpl::IgnoreWriteResultCallback(bool success
) {
799 // TODO(fgorski): Ignoring the write result for now to make sure
800 // sync_intergration_tests are not broken.
803 void GCMClientImpl::DestroyStoreCallback(bool success
) {
807 LOG(ERROR
) << "Failed to destroy GCM store";
808 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED
);
809 state_
= UNINITIALIZED
;
813 state_
= INITIALIZED
;
816 void GCMClientImpl::ResetStoreCallback(bool success
) {
818 LOG(ERROR
) << "Failed to reset GCM store";
819 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED
);
820 state_
= UNINITIALIZED
;
824 state_
= INITIALIZED
;
828 void GCMClientImpl::Stop() {
829 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
831 DVLOG(1) << "Stopping the GCM Client";
833 state_
= INITIALIZED
;
837 void GCMClientImpl::ResetCache() {
838 weak_ptr_factory_
.InvalidateWeakPtrs();
839 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
840 device_checkin_info_
.Reset();
841 connection_factory_
.reset();
842 delegate_
->OnDisconnected();
844 checkin_request_
.reset();
845 // Delete all of the pending registration and unregistration requests.
846 pending_registration_requests_
.clear();
847 pending_unregistration_requests_
.clear();
850 void GCMClientImpl::Register(
851 const linked_ptr
<RegistrationInfo
>& registration_info
) {
852 DCHECK_EQ(state_
, READY
);
854 // Find and use the cached registration ID.
855 RegistrationInfoMap::const_iterator registrations_iter
=
856 registrations_
.find(registration_info
);
857 if (registrations_iter
!= registrations_
.end()) {
860 // For GCM registration, we also match the sender IDs since multiple
861 // registrations are not supported.
862 const GCMRegistrationInfo
* gcm_registration_info
=
863 GCMRegistrationInfo::FromRegistrationInfo(registration_info
.get());
864 if (gcm_registration_info
) {
865 const GCMRegistrationInfo
* cached_gcm_registration_info
=
866 GCMRegistrationInfo::FromRegistrationInfo(
867 registrations_iter
->first
.get());
868 DCHECK(cached_gcm_registration_info
);
869 if (cached_gcm_registration_info
&&
870 gcm_registration_info
->sender_ids
!=
871 cached_gcm_registration_info
->sender_ids
) {
877 delegate_
->OnRegisterFinished(
878 registration_info
, registrations_iter
->second
, SUCCESS
);
883 scoped_ptr
<RegistrationRequest::CustomRequestHandler
> request_handler
;
884 std::string source_to_record
;
886 const GCMRegistrationInfo
* gcm_registration_info
=
887 GCMRegistrationInfo::FromRegistrationInfo(registration_info
.get());
888 if (gcm_registration_info
) {
890 for (auto iter
= gcm_registration_info
->sender_ids
.begin();
891 iter
!= gcm_registration_info
->sender_ids
.end();
893 if (!senders
.empty())
895 senders
.append(*iter
);
897 UMA_HISTOGRAM_COUNTS("GCM.RegistrationSenderIdCount",
898 gcm_registration_info
->sender_ids
.size());
900 request_handler
.reset(new GCMRegistrationRequestHandler(senders
));
901 source_to_record
= senders
;
904 const InstanceIDTokenInfo
* instance_id_token_info
=
905 InstanceIDTokenInfo::FromRegistrationInfo(registration_info
.get());
906 if (instance_id_token_info
) {
907 auto instance_id_iter
= instance_id_data_
.find(registration_info
->app_id
);
908 DCHECK(instance_id_iter
!= instance_id_data_
.end());
910 request_handler
.reset(new InstanceIDGetTokenRequestHandler(
911 instance_id_iter
->second
.first
,
912 instance_id_token_info
->authorized_entity
,
913 instance_id_token_info
->scope
,
914 ConstructGCMVersion(chrome_build_info_
.version
),
915 instance_id_token_info
->options
));
916 source_to_record
= instance_id_token_info
->authorized_entity
+ "/" +
917 instance_id_token_info
->scope
;
920 RegistrationRequest::RequestInfo
request_info(
921 device_checkin_info_
.android_id
,
922 device_checkin_info_
.secret
,
923 registration_info
->app_id
);
925 scoped_ptr
<RegistrationRequest
> registration_request(new RegistrationRequest(
926 gservices_settings_
.GetRegistrationURL(), request_info
,
927 request_handler
.Pass(), GetGCMBackoffPolicy(),
928 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
929 weak_ptr_factory_
.GetWeakPtr(), registration_info
),
930 kMaxRegistrationRetries
, url_request_context_getter_
, &recorder_
,
932 registration_request
->Start();
933 pending_registration_requests_
.insert(registration_info
,
934 registration_request
.Pass());
937 void GCMClientImpl::OnRegisterCompleted(
938 const linked_ptr
<RegistrationInfo
>& registration_info
,
939 RegistrationRequest::Status status
,
940 const std::string
& registration_id
) {
944 PendingRegistrationRequests::const_iterator iter
=
945 pending_registration_requests_
.find(registration_info
);
946 if (iter
== pending_registration_requests_
.end())
947 result
= UNKNOWN_ERROR
;
948 else if (status
== RegistrationRequest::INVALID_SENDER
)
949 result
= INVALID_PARAMETER
;
950 else if (registration_id
.empty())
951 result
= SERVER_ERROR
;
955 if (result
== SUCCESS
) {
957 // Note that the existing cached record has to be removed first because
958 // otherwise the key value in registrations_ will not be updated. For GCM
959 // registrations, the key consists of pair of app_id and sender_ids though
960 // only app_id is used in the comparison.
961 registrations_
.erase(registration_info
);
962 registrations_
[registration_info
] = registration_id
;
964 // Save it in the persistent store.
965 gcm_store_
->AddRegistration(
966 registration_info
->GetSerializedKey(),
967 registration_info
->GetSerializedValue(registration_id
),
968 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
969 weak_ptr_factory_
.GetWeakPtr()));
972 delegate_
->OnRegisterFinished(
974 result
== SUCCESS
? registration_id
: std::string(),
977 if (iter
!= pending_registration_requests_
.end())
978 pending_registration_requests_
.erase(iter
);
981 void GCMClientImpl::Unregister(
982 const linked_ptr
<RegistrationInfo
>& registration_info
) {
983 DCHECK_EQ(state_
, READY
);
985 scoped_ptr
<UnregistrationRequest::CustomRequestHandler
> request_handler
;
986 std::string source_to_record
;
988 const GCMRegistrationInfo
* gcm_registration_info
=
989 GCMRegistrationInfo::FromRegistrationInfo(registration_info
.get());
990 if (gcm_registration_info
) {
991 request_handler
.reset(
992 new GCMUnregistrationRequestHandler(registration_info
->app_id
));
995 const InstanceIDTokenInfo
* instance_id_token_info
=
996 InstanceIDTokenInfo::FromRegistrationInfo(registration_info
.get());
997 if (instance_id_token_info
) {
998 auto instance_id_iter
= instance_id_data_
.find(registration_info
->app_id
);
999 if (instance_id_iter
== instance_id_data_
.end()) {
1000 // This should not be reached since we should not delete tokens when
1001 // an InstanceID has not been created yet.
1005 request_handler
.reset(new InstanceIDDeleteTokenRequestHandler(
1006 instance_id_iter
->second
.first
,
1007 instance_id_token_info
->authorized_entity
,
1008 instance_id_token_info
->scope
,
1009 ConstructGCMVersion(chrome_build_info_
.version
)));
1010 source_to_record
= instance_id_token_info
->authorized_entity
+ "/" +
1011 instance_id_token_info
->scope
;
1014 // Remove the registration/token(s) from the cache and the store.
1015 // TODO(jianli): Remove it only when the request is successful.
1016 if (instance_id_token_info
&&
1017 instance_id_token_info
->authorized_entity
== "*" &&
1018 instance_id_token_info
->scope
== "*") {
1019 // If authorized_entity and scope are '*', find and remove all associated
1021 bool token_found
= false;
1022 for (auto iter
= registrations_
.begin();
1023 iter
!= registrations_
.end();) {
1024 InstanceIDTokenInfo
* cached_instance_id_token_info
=
1025 InstanceIDTokenInfo::FromRegistrationInfo(iter
->first
.get());
1026 if (cached_instance_id_token_info
&&
1027 cached_instance_id_token_info
->app_id
== registration_info
->app_id
) {
1029 gcm_store_
->RemoveRegistration(
1030 cached_instance_id_token_info
->GetSerializedKey(),
1031 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
1032 weak_ptr_factory_
.GetWeakPtr()));
1033 registrations_
.erase(iter
++);
1039 // If no token is found for the Instance ID, don't need to unregister
1040 // since the Instance ID is not sent to the server yet.
1042 OnUnregisterCompleted(registration_info
,
1043 UnregistrationRequest::SUCCESS
);
1047 auto iter
= registrations_
.find(registration_info
);
1048 if (iter
== registrations_
.end()) {
1049 delegate_
->OnUnregisterFinished(registration_info
, INVALID_PARAMETER
);
1052 registrations_
.erase(iter
);
1054 gcm_store_
->RemoveRegistration(
1055 registration_info
->GetSerializedKey(),
1056 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
1057 weak_ptr_factory_
.GetWeakPtr()));
1060 UnregistrationRequest::RequestInfo
request_info(
1061 device_checkin_info_
.android_id
,
1062 device_checkin_info_
.secret
,
1063 registration_info
->app_id
);
1065 scoped_ptr
<UnregistrationRequest
> unregistration_request(
1066 new UnregistrationRequest(
1067 gservices_settings_
.GetRegistrationURL(), request_info
,
1068 request_handler
.Pass(), GetGCMBackoffPolicy(),
1069 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
1070 weak_ptr_factory_
.GetWeakPtr(), registration_info
),
1071 kMaxUnregistrationRetries
, url_request_context_getter_
, &recorder_
,
1073 unregistration_request
->Start();
1074 pending_unregistration_requests_
.insert(registration_info
,
1075 unregistration_request
.Pass());
1078 void GCMClientImpl::OnUnregisterCompleted(
1079 const linked_ptr
<RegistrationInfo
>& registration_info
,
1080 UnregistrationRequest::Status status
) {
1081 DVLOG(1) << "Unregister completed for app: " << registration_info
->app_id
1082 << " with " << (status
? "success." : "failure.");
1086 case UnregistrationRequest::SUCCESS
:
1089 case UnregistrationRequest::INVALID_PARAMETERS
:
1090 result
= INVALID_PARAMETER
;
1093 // All other errors are treated as SERVER_ERROR.
1094 result
= SERVER_ERROR
;
1097 delegate_
->OnUnregisterFinished(registration_info
, result
);
1099 pending_unregistration_requests_
.erase(registration_info
);
1102 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
1103 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
1104 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
1107 void GCMClientImpl::Send(const std::string
& app_id
,
1108 const std::string
& receiver_id
,
1109 const OutgoingMessage
& message
) {
1110 DCHECK_EQ(state_
, READY
);
1112 RecordOutgoingMessageToUMA(message
);
1114 mcs_proto::DataMessageStanza stanza
;
1115 stanza
.set_ttl(message
.time_to_live
);
1116 stanza
.set_sent(clock_
->Now().ToInternalValue() /
1117 base::Time::kMicrosecondsPerSecond
);
1118 stanza
.set_id(message
.id
);
1119 stanza
.set_from(kSendMessageFromValue
);
1120 stanza
.set_to(receiver_id
);
1121 stanza
.set_category(app_id
);
1123 for (MessageData::const_iterator iter
= message
.data
.begin();
1124 iter
!= message
.data
.end();
1126 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
1127 app_data
->set_key(iter
->first
);
1128 app_data
->set_value(iter
->second
);
1131 MCSMessage
mcs_message(stanza
);
1132 DVLOG(1) << "MCS message size: " << mcs_message
.size();
1133 mcs_client_
->SendMessage(mcs_message
);
1136 std::string
GCMClientImpl::GetStateString() const {
1138 case GCMClientImpl::INITIALIZED
:
1139 return "INITIALIZED";
1140 case GCMClientImpl::UNINITIALIZED
:
1141 return "UNINITIALIZED";
1142 case GCMClientImpl::LOADING
:
1144 case GCMClientImpl::LOADED
:
1146 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
1147 return "INITIAL_DEVICE_CHECKIN";
1148 case GCMClientImpl::READY
:
1152 return std::string();
1156 void GCMClientImpl::SetRecording(bool recording
) {
1157 recorder_
.SetRecording(recording
);
1160 void GCMClientImpl::ClearActivityLogs() {
1164 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
1165 GCMClient::GCMStatistics stats
;
1166 stats
.gcm_client_created
= true;
1167 stats
.is_recording
= recorder_
.is_recording();
1168 stats
.gcm_client_state
= GetStateString();
1169 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
1170 if (connection_factory_
.get())
1171 stats
.connection_state
= connection_factory_
->GetConnectionStateString();
1172 if (mcs_client_
.get()) {
1173 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
1174 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
1176 if (device_checkin_info_
.android_id
> 0)
1177 stats
.android_id
= device_checkin_info_
.android_id
;
1178 recorder_
.CollectActivities(&stats
.recorded_activities
);
1180 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
1181 it
!= registrations_
.end(); ++it
) {
1182 stats
.registered_app_ids
.push_back(it
->first
->app_id
);
1187 void GCMClientImpl::OnActivityRecorded() {
1188 delegate_
->OnActivityRecorded();
1191 void GCMClientImpl::OnConnected(const GURL
& current_server
,
1192 const net::IPEndPoint
& ip_endpoint
) {
1193 // TODO(gcm): expose current server in debug page.
1194 delegate_
->OnActivityRecorded();
1195 delegate_
->OnConnected(ip_endpoint
);
1198 void GCMClientImpl::OnDisconnected() {
1199 delegate_
->OnActivityRecorded();
1200 delegate_
->OnDisconnected();
1203 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
1204 switch (message
.tag()) {
1205 case kLoginResponseTag
:
1206 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
1208 case kDataMessageStanzaTag
:
1209 DVLOG(1) << "A downstream message received. Processing...";
1210 HandleIncomingMessage(message
);
1213 NOTREACHED() << "Message with unexpected tag received by GCMClient";
1218 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
1219 const std::string
& app_id
,
1220 const std::string
& message_id
,
1221 MCSClient::MessageSendStatus status
) {
1222 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
1225 // TTL_EXCEEDED is singled out here, because it can happen long time after the
1226 // message was sent. That is why it comes as |OnMessageSendError| event rather
1227 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
1228 // All other errors will be raised immediately, through asynchronous callback.
1229 // It is expected that TTL_EXCEEDED will be issued for a message that was
1230 // previously issued |OnSendFinished| with status SUCCESS.
1231 // TODO(jianli): Consider adding UMA for this status.
1232 if (status
== MCSClient::TTL_EXCEEDED
) {
1233 SendErrorDetails send_error_details
;
1234 send_error_details
.message_id
= message_id
;
1235 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
1236 delegate_
->OnMessageSendError(app_id
, send_error_details
);
1237 } else if (status
== MCSClient::SENT
) {
1238 delegate_
->OnSendAcknowledged(app_id
, message_id
);
1240 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
1244 void GCMClientImpl::OnMCSError() {
1245 // TODO(fgorski): For now it replaces the initialization method. Long term it
1246 // should have an error or status passed in.
1249 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
1252 const mcs_proto::DataMessageStanza
& data_message_stanza
=
1253 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
1254 message
.GetProtobuf());
1255 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
1257 // Copying all the data from the stanza to a MessageData object. When present,
1258 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
1260 MessageData message_data
;
1261 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
1262 std::string key
= data_message_stanza
.app_data(i
).key();
1263 message_data
[key
] = data_message_stanza
.app_data(i
).value();
1266 MessageType message_type
= DATA_MESSAGE
;
1267 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
1268 if (iter
!= message_data
.end()) {
1269 message_type
= DecodeMessageType(iter
->second
);
1270 message_data
.erase(iter
);
1273 switch (message_type
) {
1275 HandleIncomingDataMessage(data_message_stanza
, message_data
);
1277 case DELETED_MESSAGES
:
1278 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
1279 data_message_stanza
.from(),
1280 data_message_stanza
.ByteSize(),
1282 GCMStatsRecorder::DELETED_MESSAGES
);
1283 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
1286 HandleIncomingSendError(data_message_stanza
, message_data
);
1289 default: // Treat default the same as UNKNOWN.
1290 DVLOG(1) << "Unknown message_type received. Message ignored. "
1291 << "App ID: " << data_message_stanza
.category() << ".";
1296 void GCMClientImpl::HandleIncomingDataMessage(
1297 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1298 MessageData
& message_data
) {
1299 std::string app_id
= data_message_stanza
.category();
1300 std::string sender
= data_message_stanza
.from();
1302 // Drop the message when the app is not registered for the sender of the
1304 bool registered
= false;
1306 // First, find among all GCM registrations.
1307 scoped_ptr
<GCMRegistrationInfo
> gcm_registration(new GCMRegistrationInfo
);
1308 gcm_registration
->app_id
= app_id
;
1309 auto gcm_registration_iter
= registrations_
.find(
1310 make_linked_ptr
<RegistrationInfo
>(gcm_registration
.release()));
1311 if (gcm_registration_iter
!= registrations_
.end()) {
1312 GCMRegistrationInfo
* cached_gcm_registration
=
1313 GCMRegistrationInfo::FromRegistrationInfo(
1314 gcm_registration_iter
->first
.get());
1315 if (cached_gcm_registration
&&
1316 std::find(cached_gcm_registration
->sender_ids
.begin(),
1317 cached_gcm_registration
->sender_ids
.end(),
1318 sender
) != cached_gcm_registration
->sender_ids
.end()) {
1323 // Then, find among all InstanceID registrations.
1325 scoped_ptr
<InstanceIDTokenInfo
> instance_id_token(new InstanceIDTokenInfo
);
1326 instance_id_token
->app_id
= app_id
;
1327 instance_id_token
->authorized_entity
= sender
;
1328 instance_id_token
->scope
= kGCMScope
;
1329 auto instance_id_token_iter
= registrations_
.find(
1330 make_linked_ptr
<RegistrationInfo
>(instance_id_token
.release()));
1331 if (instance_id_token_iter
!= registrations_
.end())
1335 recorder_
.RecordDataMessageReceived(app_id
, sender
,
1336 data_message_stanza
.ByteSize(), registered
,
1337 GCMStatsRecorder::DATA_MESSAGE
);
1341 IncomingMessage incoming_message
;
1342 incoming_message
.sender_id
= data_message_stanza
.from();
1343 if (data_message_stanza
.has_token())
1344 incoming_message
.collapse_key
= data_message_stanza
.token();
1345 incoming_message
.data
= message_data
;
1346 incoming_message
.raw_data
= data_message_stanza
.raw_data();
1348 delegate_
->OnMessageReceived(app_id
, incoming_message
);
1351 void GCMClientImpl::HandleIncomingSendError(
1352 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1353 MessageData
& message_data
) {
1354 SendErrorDetails send_error_details
;
1355 send_error_details
.additional_data
= message_data
;
1356 send_error_details
.result
= SERVER_ERROR
;
1358 MessageData::iterator iter
=
1359 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
1360 if (iter
!= send_error_details
.additional_data
.end()) {
1361 send_error_details
.message_id
= iter
->second
;
1362 send_error_details
.additional_data
.erase(iter
);
1365 recorder_
.RecordIncomingSendError(
1366 data_message_stanza
.category(),
1367 data_message_stanza
.to(),
1368 data_message_stanza
.id());
1369 delegate_
->OnMessageSendError(data_message_stanza
.category(),
1370 send_error_details
);
1373 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1374 if (registrations_
.empty())
1376 // Note that account mapper is not counted as a standalone app since it is
1377 // automatically started when other app uses GCM.
1378 return registrations_
.size() > 1 ||
1379 !ExistsGCMRegistrationInMap(registrations_
, kGCMAccountMapperAppId
);