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 // In the case that the store does not exist, set |state| back to
385 // INITIALIZED such that store loading could be triggered again when
386 // Start() is called with IMMEDIATE_START.
387 state_
= INITIALIZED
;
389 // Otherwise, destroy the store to try again.
394 gcm_store_reset_
= false;
396 device_checkin_info_
.android_id
= result
->device_android_id
;
397 device_checkin_info_
.secret
= result
->device_security_token
;
398 device_checkin_info_
.last_checkin_accounts
= result
->last_checkin_accounts
;
399 // A case where there were previously no accounts reported with checkin is
400 // considered to be the same as when the list of accounts is empty. It enables
401 // scheduling a periodic checkin for devices with no signed in users
402 // immediately after restart, while keeping |accounts_set == false| delays the
403 // checkin until the list of accounts is set explicitly.
404 if (result
->last_checkin_accounts
.size() == 0)
405 device_checkin_info_
.accounts_set
= true;
406 last_checkin_time_
= result
->last_checkin_time
;
407 gservices_settings_
.UpdateFromLoadResult(*result
);
409 for (auto iter
= result
->registrations
.begin();
410 iter
!= result
->registrations
.end();
412 std::string registration_id
;
413 scoped_ptr
<RegistrationInfo
> registration
=
414 RegistrationInfo::BuildFromString(
415 iter
->first
, iter
->second
, ®istration_id
);
416 // TODO(jianli): Add UMA to track the error case.
417 if (registration
.get())
418 registrations_
[make_linked_ptr(registration
.release())] = registration_id
;
421 for (auto iter
= result
->instance_id_data
.begin();
422 iter
!= result
->instance_id_data
.end();
424 std::string instance_id
;
425 std::string extra_data
;
426 if (DeserializeInstanceIDData(iter
->second
, &instance_id
, &extra_data
))
427 instance_id_data_
[iter
->first
] = std::make_pair(instance_id
, extra_data
);
430 load_result_
= result
.Pass();
433 // Don't initiate the GCM connection when GCM is in delayed start mode and
434 // not any standalone app has registered GCM yet.
435 if (start_mode_
== DELAYED_START
&& !HasStandaloneRegisteredApp()) {
436 // If no standalone app is using GCM and the device ID is present, schedule
437 // to have the store wiped out.
438 if (device_checkin_info_
.android_id
) {
439 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
440 FROM_HERE
, base::Bind(&GCMClientImpl::DestroyStoreWhenNotNeeded
,
441 destroying_gcm_store_ptr_factory_
.GetWeakPtr()),
442 base::TimeDelta::FromMilliseconds(kDestroyGCMStoreDelayMS
));
451 void GCMClientImpl::StartGCM() {
452 // Taking over the value of account_mappings before passing the ownership of
453 // load result to InitializeMCSClient.
454 std::vector
<AccountMapping
> account_mappings
;
455 account_mappings
.swap(load_result_
->account_mappings
);
456 base::Time last_token_fetch_time
= load_result_
->last_token_fetch_time
;
458 InitializeMCSClient();
460 if (device_checkin_info_
.IsValid()) {
461 SchedulePeriodicCheckin();
462 OnReady(account_mappings
, last_token_fetch_time
);
466 state_
= INITIAL_DEVICE_CHECKIN
;
467 device_checkin_info_
.Reset();
471 void GCMClientImpl::InitializeMCSClient() {
472 std::vector
<GURL
> endpoints
;
473 endpoints
.push_back(gservices_settings_
.GetMCSMainEndpoint());
474 endpoints
.push_back(gservices_settings_
.GetMCSFallbackEndpoint());
475 connection_factory_
= internals_builder_
->BuildConnectionFactory(
477 GetGCMBackoffPolicy(),
479 url_request_context_getter_
->GetURLRequestContext()
480 ->http_transaction_factory()
484 connection_factory_
->SetConnectionListener(this);
485 mcs_client_
= internals_builder_
->BuildMCSClient(
486 chrome_build_info_
.version
,
488 connection_factory_
.get(),
492 mcs_client_
->Initialize(
493 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
494 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
495 weak_ptr_factory_
.GetWeakPtr()),
496 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
497 weak_ptr_factory_
.GetWeakPtr()),
498 load_result_
.Pass());
501 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
502 const CheckinInfo
& checkin_info
) {
503 DCHECK(!device_checkin_info_
.IsValid());
505 device_checkin_info_
.android_id
= checkin_info
.android_id
;
506 device_checkin_info_
.secret
= checkin_info
.secret
;
507 // If accounts were not set by now, we can consider them set (to empty list)
508 // to make sure periodic checkins get scheduled after initial checkin.
509 device_checkin_info_
.accounts_set
= true;
510 gcm_store_
->SetDeviceCredentials(
511 checkin_info
.android_id
, checkin_info
.secret
,
512 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
513 weak_ptr_factory_
.GetWeakPtr()));
515 OnReady(std::vector
<AccountMapping
>(), base::Time());
518 void GCMClientImpl::OnReady(const std::vector
<AccountMapping
>& account_mappings
,
519 const base::Time
& last_token_fetch_time
) {
523 delegate_
->OnGCMReady(account_mappings
, last_token_fetch_time
);
526 void GCMClientImpl::StartMCSLogin() {
527 DCHECK_EQ(READY
, state_
);
528 DCHECK(device_checkin_info_
.IsValid());
529 mcs_client_
->Login(device_checkin_info_
.android_id
,
530 device_checkin_info_
.secret
);
533 void GCMClientImpl::DestroyStoreWhenNotNeeded() {
534 if (state_
!= LOADED
|| start_mode_
!= DELAYED_START
)
537 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::DestroyStoreCallback
,
538 weak_ptr_factory_
.GetWeakPtr()));
541 void GCMClientImpl::ResetStore() {
542 DCHECK_EQ(LOADING
, state_
);
544 // If already being reset, don't do it again. We want to prevent from
545 // resetting and loading from the store again and again.
546 if (gcm_store_reset_
) {
547 RecordResetStoreErrorToUMA(INFINITE_STORE_RESET
);
548 state_
= UNINITIALIZED
;
551 gcm_store_reset_
= true;
553 // Destroy the GCM store to start over.
554 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::ResetStoreCallback
,
555 weak_ptr_factory_
.GetWeakPtr()));
558 void GCMClientImpl::SetAccountTokens(
559 const std::vector
<AccountTokenInfo
>& account_tokens
) {
560 device_checkin_info_
.account_tokens
.clear();
561 for (std::vector
<AccountTokenInfo
>::const_iterator iter
=
562 account_tokens
.begin();
563 iter
!= account_tokens
.end();
565 device_checkin_info_
.account_tokens
[iter
->email
] = iter
->access_token
;
568 bool accounts_set_before
= device_checkin_info_
.accounts_set
;
569 device_checkin_info_
.accounts_set
= true;
571 DVLOG(1) << "Set account called with: " << account_tokens
.size()
574 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
577 bool account_removed
= false;
578 for (std::set
<std::string
>::iterator iter
=
579 device_checkin_info_
.last_checkin_accounts
.begin();
580 iter
!= device_checkin_info_
.last_checkin_accounts
.end();
582 if (device_checkin_info_
.account_tokens
.find(*iter
) ==
583 device_checkin_info_
.account_tokens
.end()) {
584 account_removed
= true;
588 // Checkin will be forced when any of the accounts was removed during the
589 // current Chrome session or if there has been an account removed between the
590 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
591 // We only force checkin when user signs out. When there is a new account
592 // signed in, the periodic checkin will take care of adding the association in
594 if (account_removed
) {
595 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
596 checkin_request_
.reset();
598 } else if (!accounts_set_before
) {
599 SchedulePeriodicCheckin();
600 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
604 void GCMClientImpl::UpdateAccountMapping(
605 const AccountMapping
& account_mapping
) {
606 gcm_store_
->AddAccountMapping(account_mapping
,
607 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
608 weak_ptr_factory_
.GetWeakPtr()));
611 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
612 gcm_store_
->RemoveAccountMapping(
614 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
615 weak_ptr_factory_
.GetWeakPtr()));
618 void GCMClientImpl::SetLastTokenFetchTime(const base::Time
& time
) {
619 gcm_store_
->SetLastTokenFetchTime(
621 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
622 weak_ptr_factory_
.GetWeakPtr()));
625 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr
<base::Timer
> timer
) {
627 mcs_client_
->UpdateHeartbeatTimer(timer
.Pass());
630 void GCMClientImpl::AddInstanceIDData(const std::string
& app_id
,
631 const std::string
& instance_id
,
632 const std::string
& extra_data
) {
633 instance_id_data_
[app_id
] = std::make_pair(instance_id
, extra_data
);
634 gcm_store_
->AddInstanceIDData(
636 SerializeInstanceIDData(instance_id
, extra_data
),
637 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
638 weak_ptr_factory_
.GetWeakPtr()));
641 void GCMClientImpl::RemoveInstanceIDData(const std::string
& app_id
) {
642 instance_id_data_
.erase(app_id
);
643 gcm_store_
->RemoveInstanceIDData(
645 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
646 weak_ptr_factory_
.GetWeakPtr()));
649 void GCMClientImpl::GetInstanceIDData(const std::string
& app_id
,
650 std::string
* instance_id
,
651 std::string
* extra_data
) {
652 DCHECK(instance_id
&& extra_data
);
654 auto iter
= instance_id_data_
.find(app_id
);
655 if (iter
== instance_id_data_
.end())
657 *instance_id
= iter
->second
.first
;
658 *extra_data
= iter
->second
.second
;
661 void GCMClientImpl::AddHeartbeatInterval(const std::string
& scope
,
664 mcs_client_
->AddHeartbeatInterval(scope
, interval_ms
);
667 void GCMClientImpl::RemoveHeartbeatInterval(const std::string
& scope
) {
669 mcs_client_
->RemoveHeartbeatInterval(scope
);
672 void GCMClientImpl::StartCheckin() {
673 // Make sure no checkin is in progress.
674 if (checkin_request_
.get())
677 checkin_proto::ChromeBuildProto chrome_build_proto
;
678 ToCheckinProtoVersion(chrome_build_info_
, &chrome_build_proto
);
679 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
680 device_checkin_info_
.secret
,
681 device_checkin_info_
.account_tokens
,
682 gservices_settings_
.digest(),
684 checkin_request_
.reset(
685 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
687 GetGCMBackoffPolicy(),
688 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
689 weak_ptr_factory_
.GetWeakPtr()),
690 url_request_context_getter_
.get(),
692 // Taking a snapshot of the accounts count here, as there might be an asynch
693 // update of the account tokens while checkin is in progress.
694 device_checkin_info_
.SnapshotCheckinAccounts();
695 checkin_request_
->Start();
698 void GCMClientImpl::OnCheckinCompleted(
699 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
700 checkin_request_
.reset();
702 if (!checkin_response
.has_android_id() ||
703 !checkin_response
.has_security_token()) {
704 // TODO(fgorski): I don't think a retry here will help, we should probably
705 // start over. By checking in with (0, 0).
709 CheckinInfo checkin_info
;
710 checkin_info
.android_id
= checkin_response
.android_id();
711 checkin_info
.secret
= checkin_response
.security_token();
713 if (state_
== INITIAL_DEVICE_CHECKIN
) {
714 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
716 // checkin_info is not expected to change after a periodic checkin as it
717 // would invalidate the registratoin IDs.
718 DCHECK_EQ(READY
, state_
);
719 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
720 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
723 if (device_checkin_info_
.IsValid()) {
724 // First update G-services settings, as something might have changed.
725 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
726 gcm_store_
->SetGServicesSettings(
727 gservices_settings_
.settings_map(),
728 gservices_settings_
.digest(),
729 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
730 weak_ptr_factory_
.GetWeakPtr()));
733 last_checkin_time_
= clock_
->Now();
734 gcm_store_
->SetLastCheckinInfo(
736 device_checkin_info_
.last_checkin_accounts
,
737 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
738 weak_ptr_factory_
.GetWeakPtr()));
739 SchedulePeriodicCheckin();
743 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
747 void GCMClientImpl::SchedulePeriodicCheckin() {
748 // Make sure no checkin is in progress.
749 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
752 // There should be only one periodic checkin pending at a time. Removing
753 // pending periodic checkin to schedule a new one.
754 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
756 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
757 if (time_to_next_checkin
< base::TimeDelta())
758 time_to_next_checkin
= base::TimeDelta();
760 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
761 FROM_HERE
, base::Bind(&GCMClientImpl::StartCheckin
,
762 periodic_checkin_ptr_factory_
.GetWeakPtr()),
763 time_to_next_checkin
);
766 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
767 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
771 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
772 // TODO(fgorski): This is one of the signals that store needs a rebuild.
776 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
777 // TODO(fgorski): This is one of the signals that store needs a rebuild.
781 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
782 // TODO(fgorski): This is one of the signals that store needs a rebuild.
786 void GCMClientImpl::DefaultStoreCallback(bool success
) {
790 void GCMClientImpl::IgnoreWriteResultCallback(bool success
) {
791 // TODO(fgorski): Ignoring the write result for now to make sure
792 // sync_intergration_tests are not broken.
795 void GCMClientImpl::DestroyStoreCallback(bool success
) {
799 LOG(ERROR
) << "Failed to destroy GCM store";
800 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED
);
801 state_
= UNINITIALIZED
;
805 state_
= INITIALIZED
;
808 void GCMClientImpl::ResetStoreCallback(bool success
) {
810 LOG(ERROR
) << "Failed to reset GCM store";
811 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED
);
812 state_
= UNINITIALIZED
;
816 state_
= INITIALIZED
;
820 void GCMClientImpl::Stop() {
821 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
823 DVLOG(1) << "Stopping the GCM Client";
825 state_
= INITIALIZED
;
829 void GCMClientImpl::ResetCache() {
830 weak_ptr_factory_
.InvalidateWeakPtrs();
831 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
832 device_checkin_info_
.Reset();
833 connection_factory_
.reset();
834 delegate_
->OnDisconnected();
836 checkin_request_
.reset();
837 // Delete all of the pending registration and unregistration requests.
838 pending_registration_requests_
.clear();
839 pending_unregistration_requests_
.clear();
842 void GCMClientImpl::Register(
843 const linked_ptr
<RegistrationInfo
>& registration_info
) {
844 DCHECK_EQ(state_
, READY
);
846 // Find and use the cached registration ID.
847 RegistrationInfoMap::const_iterator registrations_iter
=
848 registrations_
.find(registration_info
);
849 if (registrations_iter
!= registrations_
.end()) {
852 // For GCM registration, we also match the sender IDs since multiple
853 // registrations are not supported.
854 const GCMRegistrationInfo
* gcm_registration_info
=
855 GCMRegistrationInfo::FromRegistrationInfo(registration_info
.get());
856 if (gcm_registration_info
) {
857 const GCMRegistrationInfo
* cached_gcm_registration_info
=
858 GCMRegistrationInfo::FromRegistrationInfo(
859 registrations_iter
->first
.get());
860 DCHECK(cached_gcm_registration_info
);
861 if (cached_gcm_registration_info
&&
862 gcm_registration_info
->sender_ids
!=
863 cached_gcm_registration_info
->sender_ids
) {
869 delegate_
->OnRegisterFinished(
870 registration_info
, registrations_iter
->second
, SUCCESS
);
875 scoped_ptr
<RegistrationRequest::CustomRequestHandler
> request_handler
;
876 std::string source_to_record
;
878 const GCMRegistrationInfo
* gcm_registration_info
=
879 GCMRegistrationInfo::FromRegistrationInfo(registration_info
.get());
880 if (gcm_registration_info
) {
882 for (auto iter
= gcm_registration_info
->sender_ids
.begin();
883 iter
!= gcm_registration_info
->sender_ids
.end();
885 if (!senders
.empty())
887 senders
.append(*iter
);
889 UMA_HISTOGRAM_COUNTS("GCM.RegistrationSenderIdCount",
890 gcm_registration_info
->sender_ids
.size());
892 request_handler
.reset(new GCMRegistrationRequestHandler(senders
));
893 source_to_record
= senders
;
896 const InstanceIDTokenInfo
* instance_id_token_info
=
897 InstanceIDTokenInfo::FromRegistrationInfo(registration_info
.get());
898 if (instance_id_token_info
) {
899 auto instance_id_iter
= instance_id_data_
.find(registration_info
->app_id
);
900 DCHECK(instance_id_iter
!= instance_id_data_
.end());
902 request_handler
.reset(new InstanceIDGetTokenRequestHandler(
903 instance_id_iter
->second
.first
,
904 instance_id_token_info
->authorized_entity
,
905 instance_id_token_info
->scope
,
906 ConstructGCMVersion(chrome_build_info_
.version
),
907 instance_id_token_info
->options
));
908 source_to_record
= instance_id_token_info
->authorized_entity
+ "/" +
909 instance_id_token_info
->scope
;
912 RegistrationRequest::RequestInfo
request_info(
913 device_checkin_info_
.android_id
,
914 device_checkin_info_
.secret
,
915 registration_info
->app_id
);
917 scoped_ptr
<RegistrationRequest
> registration_request(new RegistrationRequest(
918 gservices_settings_
.GetRegistrationURL(), request_info
,
919 request_handler
.Pass(), GetGCMBackoffPolicy(),
920 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
921 weak_ptr_factory_
.GetWeakPtr(), registration_info
),
922 kMaxRegistrationRetries
, url_request_context_getter_
, &recorder_
,
924 registration_request
->Start();
925 pending_registration_requests_
.insert(registration_info
,
926 registration_request
.Pass());
929 void GCMClientImpl::OnRegisterCompleted(
930 const linked_ptr
<RegistrationInfo
>& registration_info
,
931 RegistrationRequest::Status status
,
932 const std::string
& registration_id
) {
936 PendingRegistrationRequests::const_iterator iter
=
937 pending_registration_requests_
.find(registration_info
);
938 if (iter
== pending_registration_requests_
.end())
939 result
= UNKNOWN_ERROR
;
940 else if (status
== RegistrationRequest::INVALID_SENDER
)
941 result
= INVALID_PARAMETER
;
942 else if (registration_id
.empty())
943 result
= SERVER_ERROR
;
947 if (result
== SUCCESS
) {
949 // Note that the existing cached record has to be removed first because
950 // otherwise the key value in registrations_ will not be updated. For GCM
951 // registrations, the key consists of pair of app_id and sender_ids though
952 // only app_id is used in the comparison.
953 registrations_
.erase(registration_info
);
954 registrations_
[registration_info
] = registration_id
;
956 // Save it in the persistent store.
957 gcm_store_
->AddRegistration(
958 registration_info
->GetSerializedKey(),
959 registration_info
->GetSerializedValue(registration_id
),
960 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
961 weak_ptr_factory_
.GetWeakPtr()));
964 delegate_
->OnRegisterFinished(
966 result
== SUCCESS
? registration_id
: std::string(),
969 if (iter
!= pending_registration_requests_
.end())
970 pending_registration_requests_
.erase(iter
);
973 void GCMClientImpl::Unregister(
974 const linked_ptr
<RegistrationInfo
>& registration_info
) {
975 DCHECK_EQ(state_
, READY
);
977 scoped_ptr
<UnregistrationRequest::CustomRequestHandler
> request_handler
;
978 std::string source_to_record
;
980 const GCMRegistrationInfo
* gcm_registration_info
=
981 GCMRegistrationInfo::FromRegistrationInfo(registration_info
.get());
982 if (gcm_registration_info
) {
983 request_handler
.reset(
984 new GCMUnregistrationRequestHandler(registration_info
->app_id
));
987 const InstanceIDTokenInfo
* instance_id_token_info
=
988 InstanceIDTokenInfo::FromRegistrationInfo(registration_info
.get());
989 if (instance_id_token_info
) {
990 auto instance_id_iter
= instance_id_data_
.find(registration_info
->app_id
);
991 if (instance_id_iter
== instance_id_data_
.end()) {
992 // This should not be reached since we should not delete tokens when
993 // an InstanceID has not been created yet.
997 request_handler
.reset(new InstanceIDDeleteTokenRequestHandler(
998 instance_id_iter
->second
.first
,
999 instance_id_token_info
->authorized_entity
,
1000 instance_id_token_info
->scope
,
1001 ConstructGCMVersion(chrome_build_info_
.version
)));
1002 source_to_record
= instance_id_token_info
->authorized_entity
+ "/" +
1003 instance_id_token_info
->scope
;
1006 // Remove the registration/token(s) from the cache and the store.
1007 // TODO(jianli): Remove it only when the request is successful.
1008 if (instance_id_token_info
&&
1009 instance_id_token_info
->authorized_entity
== "*" &&
1010 instance_id_token_info
->scope
== "*") {
1011 // If authorized_entity and scope are '*', find and remove all associated
1013 bool token_found
= false;
1014 for (auto iter
= registrations_
.begin();
1015 iter
!= registrations_
.end();) {
1016 InstanceIDTokenInfo
* cached_instance_id_token_info
=
1017 InstanceIDTokenInfo::FromRegistrationInfo(iter
->first
.get());
1018 if (cached_instance_id_token_info
&&
1019 cached_instance_id_token_info
->app_id
== registration_info
->app_id
) {
1021 gcm_store_
->RemoveRegistration(
1022 cached_instance_id_token_info
->GetSerializedKey(),
1023 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
1024 weak_ptr_factory_
.GetWeakPtr()));
1025 registrations_
.erase(iter
++);
1031 // If no token is found for the Instance ID, don't need to unregister
1032 // since the Instance ID is not sent to the server yet.
1034 OnUnregisterCompleted(registration_info
,
1035 UnregistrationRequest::SUCCESS
);
1039 auto iter
= registrations_
.find(registration_info
);
1040 if (iter
== registrations_
.end()) {
1041 delegate_
->OnUnregisterFinished(registration_info
, INVALID_PARAMETER
);
1044 registrations_
.erase(iter
);
1046 gcm_store_
->RemoveRegistration(
1047 registration_info
->GetSerializedKey(),
1048 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
1049 weak_ptr_factory_
.GetWeakPtr()));
1052 UnregistrationRequest::RequestInfo
request_info(
1053 device_checkin_info_
.android_id
,
1054 device_checkin_info_
.secret
,
1055 registration_info
->app_id
);
1057 scoped_ptr
<UnregistrationRequest
> unregistration_request(
1058 new UnregistrationRequest(
1059 gservices_settings_
.GetRegistrationURL(), request_info
,
1060 request_handler
.Pass(), GetGCMBackoffPolicy(),
1061 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
1062 weak_ptr_factory_
.GetWeakPtr(), registration_info
),
1063 kMaxUnregistrationRetries
, url_request_context_getter_
, &recorder_
,
1065 unregistration_request
->Start();
1066 pending_unregistration_requests_
.insert(registration_info
,
1067 unregistration_request
.Pass());
1070 void GCMClientImpl::OnUnregisterCompleted(
1071 const linked_ptr
<RegistrationInfo
>& registration_info
,
1072 UnregistrationRequest::Status status
) {
1073 DVLOG(1) << "Unregister completed for app: " << registration_info
->app_id
1074 << " with " << (status
? "success." : "failure.");
1078 case UnregistrationRequest::SUCCESS
:
1081 case UnregistrationRequest::INVALID_PARAMETERS
:
1082 result
= INVALID_PARAMETER
;
1085 // All other errors are treated as SERVER_ERROR.
1086 result
= SERVER_ERROR
;
1089 delegate_
->OnUnregisterFinished(registration_info
, result
);
1091 pending_unregistration_requests_
.erase(registration_info
);
1094 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
1095 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
1096 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
1099 void GCMClientImpl::Send(const std::string
& app_id
,
1100 const std::string
& receiver_id
,
1101 const OutgoingMessage
& message
) {
1102 DCHECK_EQ(state_
, READY
);
1104 RecordOutgoingMessageToUMA(message
);
1106 mcs_proto::DataMessageStanza stanza
;
1107 stanza
.set_ttl(message
.time_to_live
);
1108 stanza
.set_sent(clock_
->Now().ToInternalValue() /
1109 base::Time::kMicrosecondsPerSecond
);
1110 stanza
.set_id(message
.id
);
1111 stanza
.set_from(kSendMessageFromValue
);
1112 stanza
.set_to(receiver_id
);
1113 stanza
.set_category(app_id
);
1115 for (MessageData::const_iterator iter
= message
.data
.begin();
1116 iter
!= message
.data
.end();
1118 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
1119 app_data
->set_key(iter
->first
);
1120 app_data
->set_value(iter
->second
);
1123 MCSMessage
mcs_message(stanza
);
1124 DVLOG(1) << "MCS message size: " << mcs_message
.size();
1125 mcs_client_
->SendMessage(mcs_message
);
1128 std::string
GCMClientImpl::GetStateString() const {
1130 case GCMClientImpl::INITIALIZED
:
1131 return "INITIALIZED";
1132 case GCMClientImpl::UNINITIALIZED
:
1133 return "UNINITIALIZED";
1134 case GCMClientImpl::LOADING
:
1136 case GCMClientImpl::LOADED
:
1138 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
1139 return "INITIAL_DEVICE_CHECKIN";
1140 case GCMClientImpl::READY
:
1144 return std::string();
1148 void GCMClientImpl::SetRecording(bool recording
) {
1149 recorder_
.SetRecording(recording
);
1152 void GCMClientImpl::ClearActivityLogs() {
1156 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
1157 GCMClient::GCMStatistics stats
;
1158 stats
.gcm_client_created
= true;
1159 stats
.is_recording
= recorder_
.is_recording();
1160 stats
.gcm_client_state
= GetStateString();
1161 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
1162 if (connection_factory_
.get())
1163 stats
.connection_state
= connection_factory_
->GetConnectionStateString();
1164 if (mcs_client_
.get()) {
1165 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
1166 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
1168 if (device_checkin_info_
.android_id
> 0)
1169 stats
.android_id
= device_checkin_info_
.android_id
;
1170 recorder_
.CollectActivities(&stats
.recorded_activities
);
1172 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
1173 it
!= registrations_
.end(); ++it
) {
1174 stats
.registered_app_ids
.push_back(it
->first
->app_id
);
1179 void GCMClientImpl::OnActivityRecorded() {
1180 delegate_
->OnActivityRecorded();
1183 void GCMClientImpl::OnConnected(const GURL
& current_server
,
1184 const net::IPEndPoint
& ip_endpoint
) {
1185 // TODO(gcm): expose current server in debug page.
1186 delegate_
->OnActivityRecorded();
1187 delegate_
->OnConnected(ip_endpoint
);
1190 void GCMClientImpl::OnDisconnected() {
1191 delegate_
->OnActivityRecorded();
1192 delegate_
->OnDisconnected();
1195 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
1196 switch (message
.tag()) {
1197 case kLoginResponseTag
:
1198 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
1200 case kDataMessageStanzaTag
:
1201 DVLOG(1) << "A downstream message received. Processing...";
1202 HandleIncomingMessage(message
);
1205 NOTREACHED() << "Message with unexpected tag received by GCMClient";
1210 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
1211 const std::string
& app_id
,
1212 const std::string
& message_id
,
1213 MCSClient::MessageSendStatus status
) {
1214 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
1217 // TTL_EXCEEDED is singled out here, because it can happen long time after the
1218 // message was sent. That is why it comes as |OnMessageSendError| event rather
1219 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
1220 // All other errors will be raised immediately, through asynchronous callback.
1221 // It is expected that TTL_EXCEEDED will be issued for a message that was
1222 // previously issued |OnSendFinished| with status SUCCESS.
1223 // TODO(jianli): Consider adding UMA for this status.
1224 if (status
== MCSClient::TTL_EXCEEDED
) {
1225 SendErrorDetails send_error_details
;
1226 send_error_details
.message_id
= message_id
;
1227 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
1228 delegate_
->OnMessageSendError(app_id
, send_error_details
);
1229 } else if (status
== MCSClient::SENT
) {
1230 delegate_
->OnSendAcknowledged(app_id
, message_id
);
1232 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
1236 void GCMClientImpl::OnMCSError() {
1237 // TODO(fgorski): For now it replaces the initialization method. Long term it
1238 // should have an error or status passed in.
1241 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
1244 const mcs_proto::DataMessageStanza
& data_message_stanza
=
1245 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
1246 message
.GetProtobuf());
1247 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
1249 // Copying all the data from the stanza to a MessageData object. When present,
1250 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
1252 MessageData message_data
;
1253 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
1254 std::string key
= data_message_stanza
.app_data(i
).key();
1255 message_data
[key
] = data_message_stanza
.app_data(i
).value();
1258 MessageType message_type
= DATA_MESSAGE
;
1259 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
1260 if (iter
!= message_data
.end()) {
1261 message_type
= DecodeMessageType(iter
->second
);
1262 message_data
.erase(iter
);
1265 switch (message_type
) {
1267 HandleIncomingDataMessage(data_message_stanza
, message_data
);
1269 case DELETED_MESSAGES
:
1270 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
1271 data_message_stanza
.from(),
1272 data_message_stanza
.ByteSize(),
1274 GCMStatsRecorder::DELETED_MESSAGES
);
1275 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
1278 HandleIncomingSendError(data_message_stanza
, message_data
);
1281 default: // Treat default the same as UNKNOWN.
1282 DVLOG(1) << "Unknown message_type received. Message ignored. "
1283 << "App ID: " << data_message_stanza
.category() << ".";
1288 void GCMClientImpl::HandleIncomingDataMessage(
1289 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1290 MessageData
& message_data
) {
1291 std::string app_id
= data_message_stanza
.category();
1292 std::string sender
= data_message_stanza
.from();
1294 // Drop the message when the app is not registered for the sender of the
1296 bool registered
= false;
1298 // First, find among all GCM registrations.
1299 scoped_ptr
<GCMRegistrationInfo
> gcm_registration(new GCMRegistrationInfo
);
1300 gcm_registration
->app_id
= app_id
;
1301 auto gcm_registration_iter
= registrations_
.find(
1302 make_linked_ptr
<RegistrationInfo
>(gcm_registration
.release()));
1303 if (gcm_registration_iter
!= registrations_
.end()) {
1304 GCMRegistrationInfo
* cached_gcm_registration
=
1305 GCMRegistrationInfo::FromRegistrationInfo(
1306 gcm_registration_iter
->first
.get());
1307 if (cached_gcm_registration
&&
1308 std::find(cached_gcm_registration
->sender_ids
.begin(),
1309 cached_gcm_registration
->sender_ids
.end(),
1310 sender
) != cached_gcm_registration
->sender_ids
.end()) {
1315 // Then, find among all InstanceID registrations.
1317 scoped_ptr
<InstanceIDTokenInfo
> instance_id_token(new InstanceIDTokenInfo
);
1318 instance_id_token
->app_id
= app_id
;
1319 instance_id_token
->authorized_entity
= sender
;
1320 instance_id_token
->scope
= kGCMScope
;
1321 auto instance_id_token_iter
= registrations_
.find(
1322 make_linked_ptr
<RegistrationInfo
>(instance_id_token
.release()));
1323 if (instance_id_token_iter
!= registrations_
.end())
1327 recorder_
.RecordDataMessageReceived(app_id
, sender
,
1328 data_message_stanza
.ByteSize(), registered
,
1329 GCMStatsRecorder::DATA_MESSAGE
);
1333 IncomingMessage incoming_message
;
1334 incoming_message
.sender_id
= data_message_stanza
.from();
1335 if (data_message_stanza
.has_token())
1336 incoming_message
.collapse_key
= data_message_stanza
.token();
1337 incoming_message
.data
= message_data
;
1338 delegate_
->OnMessageReceived(app_id
, incoming_message
);
1341 void GCMClientImpl::HandleIncomingSendError(
1342 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1343 MessageData
& message_data
) {
1344 SendErrorDetails send_error_details
;
1345 send_error_details
.additional_data
= message_data
;
1346 send_error_details
.result
= SERVER_ERROR
;
1348 MessageData::iterator iter
=
1349 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
1350 if (iter
!= send_error_details
.additional_data
.end()) {
1351 send_error_details
.message_id
= iter
->second
;
1352 send_error_details
.additional_data
.erase(iter
);
1355 recorder_
.RecordIncomingSendError(
1356 data_message_stanza
.category(),
1357 data_message_stanza
.to(),
1358 data_message_stanza
.id());
1359 delegate_
->OnMessageSendError(data_message_stanza
.category(),
1360 send_error_details
);
1363 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1364 if (registrations_
.empty())
1366 // Note that account mapper is not counted as a standalone app since it is
1367 // automatically started when other app uses GCM.
1368 return registrations_
.size() > 1 ||
1369 !ExistsGCMRegistrationInMap(registrations_
, kGCMAccountMapperAppId
);