1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "components/gcm_driver/gcm_client_impl.h"
8 #include "base/files/file_path.h"
9 #include "base/logging.h"
10 #include "base/memory/scoped_ptr.h"
11 #include "base/message_loop/message_loop.h"
12 #include "base/metrics/histogram.h"
13 #include "base/sequenced_task_runner.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/time/default_clock.h"
18 #include "base/timer/timer.h"
19 #include "components/gcm_driver/gcm_backoff_policy.h"
20 #include "google_apis/gcm/base/encryptor.h"
21 #include "google_apis/gcm/base/mcs_message.h"
22 #include "google_apis/gcm/base/mcs_util.h"
23 #include "google_apis/gcm/engine/checkin_request.h"
24 #include "google_apis/gcm/engine/connection_factory_impl.h"
25 #include "google_apis/gcm/engine/gcm_store_impl.h"
26 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
27 #include "google_apis/gcm/protocol/checkin.pb.h"
28 #include "google_apis/gcm/protocol/mcs.pb.h"
29 #include "net/http/http_network_session.h"
30 #include "net/http/http_transaction_factory.h"
31 #include "net/url_request/url_request_context.h"
38 // Indicates a message type of the received message.
40 UNKNOWN
, // Undetermined type.
41 DATA_MESSAGE
, // Regular data message.
42 DELETED_MESSAGES
, // Messages were deleted on the server.
43 SEND_ERROR
, // Error sending a message.
46 enum OutgoingMessageTTLCategory
{
48 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
,
49 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
,
50 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
,
51 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK
,
52 TTL_MORE_THAN_ONE_WEEK
,
54 // NOTE: always keep this entry at the end. Add new TTL category only
55 // immediately above this line. Make sure to update the corresponding
56 // histogram enum accordingly.
60 const int kMaxRegistrationRetries
= 5;
61 const char kMessageTypeDataMessage
[] = "gcm";
62 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
63 const char kMessageTypeKey
[] = "message_type";
64 const char kMessageTypeSendErrorKey
[] = "send_error";
65 const char kSendErrorMessageIdKey
[] = "google.message_id";
66 const char kSendMessageFromValue
[] = "gcm@chrome.com";
67 const int64 kDefaultUserSerialNumber
= 0LL;
69 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus status
) {
71 case MCSClient::QUEUED
:
72 return GCMClient::SUCCESS
;
73 case MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
74 return GCMClient::NETWORK_ERROR
;
75 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
76 return GCMClient::NETWORK_ERROR
;
77 case MCSClient::MESSAGE_TOO_LARGE
:
78 return GCMClient::INVALID_PARAMETER
;
79 case MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
80 return GCMClient::NETWORK_ERROR
;
81 case MCSClient::TTL_EXCEEDED
:
82 return GCMClient::NETWORK_ERROR
;
88 return GCMClientImpl::UNKNOWN_ERROR
;
91 void ToCheckinProtoVersion(
92 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
93 checkin_proto::ChromeBuildProto
* android_build_info
) {
94 checkin_proto::ChromeBuildProto_Platform platform
;
95 switch (chrome_build_info
.platform
) {
96 case GCMClient::PLATFORM_WIN
:
97 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN
;
99 case GCMClient::PLATFORM_MAC
:
100 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC
;
102 case GCMClient::PLATFORM_LINUX
:
103 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
105 case GCMClient::PLATFORM_IOS
:
106 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS
;
108 case GCMClient::PLATFORM_ANDROID
:
109 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID
;
111 case GCMClient::PLATFORM_CROS
:
112 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS
;
114 case GCMClient::PLATFORM_UNKNOWN
:
115 // For unknown platform, return as LINUX.
116 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
120 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
123 android_build_info
->set_platform(platform
);
125 checkin_proto::ChromeBuildProto_Channel channel
;
126 switch (chrome_build_info
.channel
) {
127 case GCMClient::CHANNEL_STABLE
:
128 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE
;
130 case GCMClient::CHANNEL_BETA
:
131 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA
;
133 case GCMClient::CHANNEL_DEV
:
134 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV
;
136 case GCMClient::CHANNEL_CANARY
:
137 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY
;
139 case GCMClient::CHANNEL_UNKNOWN
:
140 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
144 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
147 android_build_info
->set_channel(channel
);
149 android_build_info
->set_chrome_version(chrome_build_info
.version
);
152 MessageType
DecodeMessageType(const std::string
& value
) {
153 if (kMessageTypeDeletedMessagesKey
== value
)
154 return DELETED_MESSAGES
;
155 if (kMessageTypeSendErrorKey
== value
)
157 if (kMessageTypeDataMessage
== value
)
162 void RecordOutgoingMessageToUMA(
163 const gcm::GCMClient::OutgoingMessage
& message
) {
164 OutgoingMessageTTLCategory ttl_category
;
165 if (message
.time_to_live
== 0)
166 ttl_category
= TTL_ZERO
;
167 else if (message
.time_to_live
<= 60 )
168 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
;
169 else if (message
.time_to_live
<= 60 * 60)
170 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
;
171 else if (message
.time_to_live
<= 24 * 60 * 60)
172 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
;
174 ttl_category
= TTL_MAXIMUM
;
176 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
183 GCMInternalsBuilder::GCMInternalsBuilder() {}
184 GCMInternalsBuilder::~GCMInternalsBuilder() {}
186 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
187 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
190 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
191 const std::string
& version
,
193 ConnectionFactory
* connection_factory
,
195 GCMStatsRecorder
* recorder
) {
196 return scoped_ptr
<MCSClient
>(new MCSClient(
197 version
, clock
, connection_factory
, gcm_store
, recorder
));
200 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
201 const std::vector
<GURL
>& endpoints
,
202 const net::BackoffEntry::Policy
& backoff_policy
,
203 const scoped_refptr
<net::HttpNetworkSession
>& gcm_network_session
,
204 const scoped_refptr
<net::HttpNetworkSession
>& http_network_session
,
205 net::NetLog
* net_log
,
206 GCMStatsRecorder
* recorder
) {
207 return make_scoped_ptr
<ConnectionFactory
>(
208 new ConnectionFactoryImpl(endpoints
,
211 http_network_session
,
216 GCMClientImpl::CheckinInfo::CheckinInfo()
217 : android_id(0), secret(0), accounts_set(false) {
220 GCMClientImpl::CheckinInfo::~CheckinInfo() {
223 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
224 last_checkin_accounts
.clear();
225 for (std::map
<std::string
, std::string
>::iterator iter
=
226 account_tokens
.begin();
227 iter
!= account_tokens
.end();
229 last_checkin_accounts
.insert(iter
->first
);
233 void GCMClientImpl::CheckinInfo::Reset() {
236 accounts_set
= false;
237 account_tokens
.clear();
238 last_checkin_accounts
.clear();
241 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
242 : internals_builder_(internals_builder
.Pass()),
243 state_(UNINITIALIZED
),
245 clock_(internals_builder_
->BuildClock()),
246 url_request_context_getter_(NULL
),
247 pending_registration_requests_deleter_(&pending_registration_requests_
),
248 pending_unregistration_requests_deleter_(
249 &pending_unregistration_requests_
),
250 periodic_checkin_ptr_factory_(this),
251 weak_ptr_factory_(this) {
254 GCMClientImpl::~GCMClientImpl() {
257 void GCMClientImpl::Initialize(
258 const ChromeBuildInfo
& chrome_build_info
,
259 const base::FilePath
& path
,
260 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
261 const scoped_refptr
<net::URLRequestContextGetter
>&
262 url_request_context_getter
,
263 scoped_ptr
<Encryptor
> encryptor
,
264 GCMClient::Delegate
* delegate
) {
265 DCHECK_EQ(UNINITIALIZED
, state_
);
266 DCHECK(url_request_context_getter
.get());
269 url_request_context_getter_
= url_request_context_getter
;
270 const net::HttpNetworkSession::Params
* network_session_params
=
271 url_request_context_getter_
->GetURLRequestContext()->
272 GetNetworkSessionParams();
273 DCHECK(network_session_params
);
274 network_session_
= new net::HttpNetworkSession(*network_session_params
);
276 chrome_build_info_
= chrome_build_info
;
279 new GCMStoreImpl(path
, blocking_task_runner
, encryptor
.Pass()));
281 delegate_
= delegate
;
283 recorder_
.SetDelegate(this);
285 state_
= INITIALIZED
;
288 void GCMClientImpl::Start() {
289 DCHECK_EQ(INITIALIZED
, state_
);
291 // Once the loading is completed, the check-in will be initiated.
292 gcm_store_
->Load(base::Bind(&GCMClientImpl::OnLoadCompleted
,
293 weak_ptr_factory_
.GetWeakPtr()));
297 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
298 DCHECK_EQ(LOADING
, state_
);
300 if (!result
->success
) {
305 registrations_
= result
->registrations
;
306 device_checkin_info_
.android_id
= result
->device_android_id
;
307 device_checkin_info_
.secret
= result
->device_security_token
;
308 device_checkin_info_
.last_checkin_accounts
= result
->last_checkin_accounts
;
309 // A case where there were previously no accounts reported with checkin is
310 // considered to be the same as when the list of accounts is empty. It enables
311 // scheduling a periodic checkin for devices with no signed in users
312 // immediately after restart, while keeping |accounts_set == false| delays the
313 // checkin until the list of accounts is set explicitly.
314 if (result
->last_checkin_accounts
.size() == 0)
315 device_checkin_info_
.accounts_set
= true;
316 last_checkin_time_
= result
->last_checkin_time
;
317 gservices_settings_
.UpdateFromLoadResult(*result
);
318 // Taking over the value of account_mappings before passing the ownership of
319 // load result to InitializeMCSClient.
320 std::vector
<AccountMapping
> account_mappings
;
321 account_mappings
.swap(result
->account_mappings
);
322 base::Time last_token_fetch_time
= result
->last_token_fetch_time
;
324 InitializeMCSClient(result
.Pass());
326 if (device_checkin_info_
.IsValid()) {
327 SchedulePeriodicCheckin();
328 OnReady(account_mappings
, last_token_fetch_time
);
332 state_
= INITIAL_DEVICE_CHECKIN
;
333 device_checkin_info_
.Reset();
337 void GCMClientImpl::InitializeMCSClient(
338 scoped_ptr
<GCMStore::LoadResult
> result
) {
339 std::vector
<GURL
> endpoints
;
340 endpoints
.push_back(gservices_settings_
.GetMCSMainEndpoint());
341 endpoints
.push_back(gservices_settings_
.GetMCSFallbackEndpoint());
342 connection_factory_
= internals_builder_
->BuildConnectionFactory(
344 GetGCMBackoffPolicy(),
346 url_request_context_getter_
->GetURLRequestContext()
347 ->http_transaction_factory()
351 connection_factory_
->SetConnectionListener(this);
352 mcs_client_
= internals_builder_
->BuildMCSClient(
353 chrome_build_info_
.version
,
355 connection_factory_
.get(),
359 mcs_client_
->Initialize(
360 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
361 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
362 weak_ptr_factory_
.GetWeakPtr()),
363 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
364 weak_ptr_factory_
.GetWeakPtr()),
368 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
369 const CheckinInfo
& checkin_info
) {
370 DCHECK(!device_checkin_info_
.IsValid());
372 device_checkin_info_
.android_id
= checkin_info
.android_id
;
373 device_checkin_info_
.secret
= checkin_info
.secret
;
374 // If accounts were not set by now, we can consider them set (to empty list)
375 // to make sure periodic checkins get scheduled after initial checkin.
376 device_checkin_info_
.accounts_set
= true;
377 gcm_store_
->SetDeviceCredentials(
378 checkin_info
.android_id
, checkin_info
.secret
,
379 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
380 weak_ptr_factory_
.GetWeakPtr()));
382 OnReady(std::vector
<AccountMapping
>(), base::Time());
385 void GCMClientImpl::OnReady(const std::vector
<AccountMapping
>& account_mappings
,
386 const base::Time
& last_token_fetch_time
) {
390 delegate_
->OnGCMReady(account_mappings
, last_token_fetch_time
);
393 void GCMClientImpl::StartMCSLogin() {
394 DCHECK_EQ(READY
, state_
);
395 DCHECK(device_checkin_info_
.IsValid());
396 mcs_client_
->Login(device_checkin_info_
.android_id
,
397 device_checkin_info_
.secret
);
400 void GCMClientImpl::ResetState() {
401 state_
= UNINITIALIZED
;
402 // TODO(fgorski): reset all of the necessart objects and start over.
405 void GCMClientImpl::SetAccountTokens(
406 const std::vector
<AccountTokenInfo
>& account_tokens
) {
407 device_checkin_info_
.account_tokens
.clear();
408 for (std::vector
<AccountTokenInfo
>::const_iterator iter
=
409 account_tokens
.begin();
410 iter
!= account_tokens
.end();
412 device_checkin_info_
.account_tokens
[iter
->email
] = iter
->access_token
;
415 bool accounts_set_before
= device_checkin_info_
.accounts_set
;
416 device_checkin_info_
.accounts_set
= true;
418 DVLOG(1) << "Set account called with: " << account_tokens
.size()
421 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
424 bool account_removed
= false;
425 for (std::set
<std::string
>::iterator iter
=
426 device_checkin_info_
.last_checkin_accounts
.begin();
427 iter
!= device_checkin_info_
.last_checkin_accounts
.end();
429 if (device_checkin_info_
.account_tokens
.find(*iter
) ==
430 device_checkin_info_
.account_tokens
.end()) {
431 account_removed
= true;
435 // Checkin will be forced when any of the accounts was removed during the
436 // current Chrome session or if there has been an account removed between the
437 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
438 // We only force checkin when user signs out. When there is a new account
439 // signed in, the periodic checkin will take care of adding the association in
441 if (account_removed
) {
442 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
443 checkin_request_
.reset();
445 } else if (!accounts_set_before
) {
446 SchedulePeriodicCheckin();
447 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
451 void GCMClientImpl::UpdateAccountMapping(
452 const AccountMapping
& account_mapping
) {
453 gcm_store_
->AddAccountMapping(account_mapping
,
454 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
455 weak_ptr_factory_
.GetWeakPtr()));
458 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
459 gcm_store_
->RemoveAccountMapping(
461 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
462 weak_ptr_factory_
.GetWeakPtr()));
465 void GCMClientImpl::SetLastTokenFetchTime(const base::Time
& time
) {
466 gcm_store_
->SetLastTokenFetchTime(
468 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
469 weak_ptr_factory_
.GetWeakPtr()));
472 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr
<base::Timer
> timer
) {
474 mcs_client_
->UpdateHeartbeatTimer(timer
.Pass());
477 void GCMClientImpl::StartCheckin() {
478 // Make sure no checkin is in progress.
479 if (checkin_request_
.get())
482 checkin_proto::ChromeBuildProto chrome_build_proto
;
483 ToCheckinProtoVersion(chrome_build_info_
, &chrome_build_proto
);
484 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
485 device_checkin_info_
.secret
,
486 device_checkin_info_
.account_tokens
,
487 gservices_settings_
.digest(),
489 checkin_request_
.reset(
490 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
492 GetGCMBackoffPolicy(),
493 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
494 weak_ptr_factory_
.GetWeakPtr()),
495 url_request_context_getter_
.get(),
497 // Taking a snapshot of the accounts count here, as there might be an asynch
498 // update of the account tokens while checkin is in progress.
499 device_checkin_info_
.SnapshotCheckinAccounts();
500 checkin_request_
->Start();
503 void GCMClientImpl::OnCheckinCompleted(
504 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
505 checkin_request_
.reset();
507 if (!checkin_response
.has_android_id() ||
508 !checkin_response
.has_security_token()) {
509 // TODO(fgorski): I don't think a retry here will help, we should probably
510 // start over. By checking in with (0, 0).
514 CheckinInfo checkin_info
;
515 checkin_info
.android_id
= checkin_response
.android_id();
516 checkin_info
.secret
= checkin_response
.security_token();
518 if (state_
== INITIAL_DEVICE_CHECKIN
) {
519 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
521 // checkin_info is not expected to change after a periodic checkin as it
522 // would invalidate the registratoin IDs.
523 DCHECK_EQ(READY
, state_
);
524 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
525 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
528 if (device_checkin_info_
.IsValid()) {
529 // First update G-services settings, as something might have changed.
530 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
531 gcm_store_
->SetGServicesSettings(
532 gservices_settings_
.settings_map(),
533 gservices_settings_
.digest(),
534 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
535 weak_ptr_factory_
.GetWeakPtr()));
538 last_checkin_time_
= clock_
->Now();
539 gcm_store_
->SetLastCheckinInfo(
541 device_checkin_info_
.last_checkin_accounts
,
542 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
543 weak_ptr_factory_
.GetWeakPtr()));
544 SchedulePeriodicCheckin();
548 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
552 void GCMClientImpl::SchedulePeriodicCheckin() {
553 // Make sure no checkin is in progress.
554 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
557 // There should be only one periodic checkin pending at a time. Removing
558 // pending periodic checkin to schedule a new one.
559 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
561 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
562 if (time_to_next_checkin
< base::TimeDelta())
563 time_to_next_checkin
= base::TimeDelta();
565 base::MessageLoop::current()->PostDelayedTask(
567 base::Bind(&GCMClientImpl::StartCheckin
,
568 periodic_checkin_ptr_factory_
.GetWeakPtr()),
569 time_to_next_checkin
);
572 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
573 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
577 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
578 // TODO(fgorski): This is one of the signals that store needs a rebuild.
582 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
583 // TODO(fgorski): This is one of the signals that store needs a rebuild.
587 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
588 // TODO(fgorski): This is one of the signals that store needs a rebuild.
592 void GCMClientImpl::DefaultStoreCallback(bool success
) {
596 void GCMClientImpl::IgnoreWriteResultCallback(bool success
) {
597 // TODO(fgorski): Ignoring the write result for now to make sure
598 // sync_intergration_tests are not broken.
601 void GCMClientImpl::Stop() {
602 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
604 DVLOG(1) << "Stopping the GCM Client";
605 weak_ptr_factory_
.InvalidateWeakPtrs();
606 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
607 device_checkin_info_
.Reset();
608 connection_factory_
.reset();
609 delegate_
->OnDisconnected();
611 checkin_request_
.reset();
612 // Delete all of the pending registration and unregistration requests.
613 STLDeleteValues(&pending_registration_requests_
);
614 STLDeleteValues(&pending_unregistration_requests_
);
615 state_
= INITIALIZED
;
619 void GCMClientImpl::CheckOut() {
621 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed
,
622 weak_ptr_factory_
.GetWeakPtr()));
625 void GCMClientImpl::Register(const std::string
& app_id
,
626 const std::vector
<std::string
>& sender_ids
) {
627 DCHECK_EQ(state_
, READY
);
629 // If the same sender ids is provided, return the cached registration ID
631 RegistrationInfoMap::const_iterator registrations_iter
=
632 registrations_
.find(app_id
);
633 if (registrations_iter
!= registrations_
.end() &&
634 registrations_iter
->second
->sender_ids
== sender_ids
) {
635 delegate_
->OnRegisterFinished(
636 app_id
, registrations_iter
->second
->registration_id
, SUCCESS
);
640 RegistrationRequest::RequestInfo
request_info(
641 device_checkin_info_
.android_id
,
642 device_checkin_info_
.secret
,
645 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
647 RegistrationRequest
* registration_request
=
648 new RegistrationRequest(gservices_settings_
.GetRegistrationURL(),
650 GetGCMBackoffPolicy(),
651 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
652 weak_ptr_factory_
.GetWeakPtr(),
655 kMaxRegistrationRetries
,
656 url_request_context_getter_
,
658 pending_registration_requests_
[app_id
] = registration_request
;
659 registration_request
->Start();
662 void GCMClientImpl::OnRegisterCompleted(
663 const std::string
& app_id
,
664 const std::vector
<std::string
>& sender_ids
,
665 RegistrationRequest::Status status
,
666 const std::string
& registration_id
) {
670 PendingRegistrationRequests::iterator iter
=
671 pending_registration_requests_
.find(app_id
);
672 if (iter
== pending_registration_requests_
.end())
673 result
= UNKNOWN_ERROR
;
674 else if (status
== RegistrationRequest::INVALID_SENDER
)
675 result
= INVALID_PARAMETER
;
676 else if (registration_id
.empty())
677 result
= SERVER_ERROR
;
681 if (result
== SUCCESS
) {
683 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
684 registration
->sender_ids
= sender_ids
;
685 registration
->registration_id
= registration_id
;
686 registrations_
[app_id
] = registration
;
688 // Save it in the persistent store.
689 gcm_store_
->AddRegistration(
692 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
693 weak_ptr_factory_
.GetWeakPtr()));
696 delegate_
->OnRegisterFinished(
697 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
699 if (iter
!= pending_registration_requests_
.end()) {
701 pending_registration_requests_
.erase(iter
);
705 void GCMClientImpl::Unregister(const std::string
& app_id
) {
706 DCHECK_EQ(state_
, READY
);
707 if (pending_unregistration_requests_
.count(app_id
) == 1)
710 // Remove from the cache and persistent store.
711 registrations_
.erase(app_id
);
712 gcm_store_
->RemoveRegistration(
714 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
715 weak_ptr_factory_
.GetWeakPtr()));
717 UnregistrationRequest::RequestInfo
request_info(
718 device_checkin_info_
.android_id
,
719 device_checkin_info_
.secret
,
722 UnregistrationRequest
* unregistration_request
= new UnregistrationRequest(
723 gservices_settings_
.GetRegistrationURL(),
725 GetGCMBackoffPolicy(),
726 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
727 weak_ptr_factory_
.GetWeakPtr(),
729 url_request_context_getter_
,
731 pending_unregistration_requests_
[app_id
] = unregistration_request
;
732 unregistration_request
->Start();
735 void GCMClientImpl::OnUnregisterCompleted(
736 const std::string
& app_id
,
737 UnregistrationRequest::Status status
) {
738 DVLOG(1) << "Unregister completed for app: " << app_id
739 << " with " << (status
? "success." : "failure.");
740 delegate_
->OnUnregisterFinished(
742 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
744 PendingUnregistrationRequests::iterator iter
=
745 pending_unregistration_requests_
.find(app_id
);
746 if (iter
== pending_unregistration_requests_
.end())
750 pending_unregistration_requests_
.erase(iter
);
753 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
754 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
755 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
758 void GCMClientImpl::Send(const std::string
& app_id
,
759 const std::string
& receiver_id
,
760 const OutgoingMessage
& message
) {
761 DCHECK_EQ(state_
, READY
);
763 RecordOutgoingMessageToUMA(message
);
765 mcs_proto::DataMessageStanza stanza
;
766 stanza
.set_ttl(message
.time_to_live
);
767 stanza
.set_sent(clock_
->Now().ToInternalValue() /
768 base::Time::kMicrosecondsPerSecond
);
769 stanza
.set_id(message
.id
);
770 stanza
.set_from(kSendMessageFromValue
);
771 stanza
.set_to(receiver_id
);
772 stanza
.set_category(app_id
);
774 for (MessageData::const_iterator iter
= message
.data
.begin();
775 iter
!= message
.data
.end();
777 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
778 app_data
->set_key(iter
->first
);
779 app_data
->set_value(iter
->second
);
782 MCSMessage
mcs_message(stanza
);
783 DVLOG(1) << "MCS message size: " << mcs_message
.size();
784 mcs_client_
->SendMessage(mcs_message
);
787 std::string
GCMClientImpl::GetStateString() const {
789 case GCMClientImpl::INITIALIZED
:
790 return "INITIALIZED";
791 case GCMClientImpl::UNINITIALIZED
:
792 return "UNINITIALIZED";
793 case GCMClientImpl::LOADING
:
795 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
796 return "INITIAL_DEVICE_CHECKIN";
797 case GCMClientImpl::READY
:
801 return std::string();
805 void GCMClientImpl::SetRecording(bool recording
) {
806 recorder_
.SetRecording(recording
);
809 void GCMClientImpl::ClearActivityLogs() {
813 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
814 GCMClient::GCMStatistics stats
;
815 stats
.gcm_client_created
= true;
816 stats
.is_recording
= recorder_
.is_recording();
817 stats
.gcm_client_state
= GetStateString();
818 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
819 if (connection_factory_
.get())
820 stats
.connection_state
= connection_factory_
->GetConnectionStateString();
821 if (mcs_client_
.get()) {
822 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
823 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
825 if (device_checkin_info_
.android_id
> 0)
826 stats
.android_id
= device_checkin_info_
.android_id
;
827 recorder_
.CollectActivities(&stats
.recorded_activities
);
829 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
830 it
!= registrations_
.end(); ++it
) {
831 stats
.registered_app_ids
.push_back(it
->first
);
836 void GCMClientImpl::OnActivityRecorded() {
837 delegate_
->OnActivityRecorded();
840 void GCMClientImpl::OnConnected(const GURL
& current_server
,
841 const net::IPEndPoint
& ip_endpoint
) {
842 // TODO(gcm): expose current server in debug page.
843 delegate_
->OnActivityRecorded();
844 delegate_
->OnConnected(ip_endpoint
);
847 void GCMClientImpl::OnDisconnected() {
848 delegate_
->OnActivityRecorded();
849 delegate_
->OnDisconnected();
852 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
853 switch (message
.tag()) {
854 case kLoginResponseTag
:
855 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
857 case kDataMessageStanzaTag
:
858 DVLOG(1) << "A downstream message received. Processing...";
859 HandleIncomingMessage(message
);
862 NOTREACHED() << "Message with unexpected tag received by GCMClient";
867 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
868 const std::string
& app_id
,
869 const std::string
& message_id
,
870 MCSClient::MessageSendStatus status
) {
871 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
874 // TTL_EXCEEDED is singled out here, because it can happen long time after the
875 // message was sent. That is why it comes as |OnMessageSendError| event rather
876 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
877 // All other errors will be raised immediately, through asynchronous callback.
878 // It is expected that TTL_EXCEEDED will be issued for a message that was
879 // previously issued |OnSendFinished| with status SUCCESS.
880 // TODO(jianli): Consider adding UMA for this status.
881 if (status
== MCSClient::TTL_EXCEEDED
) {
882 SendErrorDetails send_error_details
;
883 send_error_details
.message_id
= message_id
;
884 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
885 delegate_
->OnMessageSendError(app_id
, send_error_details
);
886 } else if (status
== MCSClient::SENT
) {
887 delegate_
->OnSendAcknowledged(app_id
, message_id
);
889 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
893 void GCMClientImpl::OnMCSError() {
894 // TODO(fgorski): For now it replaces the initialization method. Long term it
895 // should have an error or status passed in.
898 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
901 const mcs_proto::DataMessageStanza
& data_message_stanza
=
902 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
903 message
.GetProtobuf());
904 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
906 // Copying all the data from the stanza to a MessageData object. When present,
907 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
909 MessageData message_data
;
910 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
911 std::string key
= data_message_stanza
.app_data(i
).key();
912 message_data
[key
] = data_message_stanza
.app_data(i
).value();
915 MessageType message_type
= DATA_MESSAGE
;
916 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
917 if (iter
!= message_data
.end()) {
918 message_type
= DecodeMessageType(iter
->second
);
919 message_data
.erase(iter
);
922 switch (message_type
) {
924 HandleIncomingDataMessage(data_message_stanza
, message_data
);
926 case DELETED_MESSAGES
:
927 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
928 data_message_stanza
.from(),
929 data_message_stanza
.ByteSize(),
931 GCMStatsRecorder::DELETED_MESSAGES
);
932 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
935 HandleIncomingSendError(data_message_stanza
, message_data
);
938 default: // Treat default the same as UNKNOWN.
939 DVLOG(1) << "Unknown message_type received. Message ignored. "
940 << "App ID: " << data_message_stanza
.category() << ".";
945 void GCMClientImpl::HandleIncomingDataMessage(
946 const mcs_proto::DataMessageStanza
& data_message_stanza
,
947 MessageData
& message_data
) {
948 std::string app_id
= data_message_stanza
.category();
950 // Drop the message when the app is not registered for the sender of the
952 RegistrationInfoMap::iterator iter
= registrations_
.find(app_id
);
953 bool not_registered
=
954 iter
== registrations_
.end() ||
955 std::find(iter
->second
->sender_ids
.begin(),
956 iter
->second
->sender_ids
.end(),
957 data_message_stanza
.from()) == iter
->second
->sender_ids
.end();
958 recorder_
.RecordDataMessageReceived(app_id
, data_message_stanza
.from(),
959 data_message_stanza
.ByteSize(), !not_registered
,
960 GCMStatsRecorder::DATA_MESSAGE
);
961 if (not_registered
) {
965 IncomingMessage incoming_message
;
966 incoming_message
.sender_id
= data_message_stanza
.from();
967 if (data_message_stanza
.has_token())
968 incoming_message
.collapse_key
= data_message_stanza
.token();
969 incoming_message
.data
= message_data
;
970 delegate_
->OnMessageReceived(app_id
, incoming_message
);
973 void GCMClientImpl::HandleIncomingSendError(
974 const mcs_proto::DataMessageStanza
& data_message_stanza
,
975 MessageData
& message_data
) {
976 SendErrorDetails send_error_details
;
977 send_error_details
.additional_data
= message_data
;
978 send_error_details
.result
= SERVER_ERROR
;
980 MessageData::iterator iter
=
981 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
982 if (iter
!= send_error_details
.additional_data
.end()) {
983 send_error_details
.message_id
= iter
->second
;
984 send_error_details
.additional_data
.erase(iter
);
987 recorder_
.RecordIncomingSendError(
988 data_message_stanza
.category(),
989 data_message_stanza
.to(),
990 data_message_stanza
.id());
991 delegate_
->OnMessageSendError(data_message_stanza
.category(),