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 "components/gcm_driver/gcm_backoff_policy.h"
19 #include "components/timers/alarm_timer.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 #if defined(OS_CHROMEOS)
197 return scoped_ptr
<MCSClient
>(new MCSClient(
198 version
, clock
, connection_factory
, gcm_store
, recorder
,
199 make_scoped_ptr(new timers::AlarmTimer(true, /* retain user task */
200 false /* non-repeating */))));
202 return scoped_ptr
<MCSClient
>(new MCSClient(
203 version
, clock
, connection_factory
, gcm_store
, recorder
,
204 make_scoped_ptr(new base::Timer(true, /* retain user task */
205 false /* non-repeating */))));
206 #endif // defined(OS_CHROMEOS)
209 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
210 const std::vector
<GURL
>& endpoints
,
211 const net::BackoffEntry::Policy
& backoff_policy
,
212 const scoped_refptr
<net::HttpNetworkSession
>& gcm_network_session
,
213 const scoped_refptr
<net::HttpNetworkSession
>& http_network_session
,
214 net::NetLog
* net_log
,
215 GCMStatsRecorder
* recorder
) {
216 return make_scoped_ptr
<ConnectionFactory
>(
217 new ConnectionFactoryImpl(endpoints
,
220 http_network_session
,
225 GCMClientImpl::CheckinInfo::CheckinInfo()
226 : android_id(0), secret(0), accounts_set(false) {
229 GCMClientImpl::CheckinInfo::~CheckinInfo() {
232 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
233 last_checkin_accounts
.clear();
234 for (std::map
<std::string
, std::string
>::iterator iter
=
235 account_tokens
.begin();
236 iter
!= account_tokens
.end();
238 last_checkin_accounts
.insert(iter
->first
);
242 void GCMClientImpl::CheckinInfo::Reset() {
245 accounts_set
= false;
246 account_tokens
.clear();
247 last_checkin_accounts
.clear();
250 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
251 : internals_builder_(internals_builder
.Pass()),
252 state_(UNINITIALIZED
),
254 clock_(internals_builder_
->BuildClock()),
255 url_request_context_getter_(NULL
),
256 pending_registration_requests_deleter_(&pending_registration_requests_
),
257 pending_unregistration_requests_deleter_(
258 &pending_unregistration_requests_
),
259 periodic_checkin_ptr_factory_(this),
260 weak_ptr_factory_(this) {
263 GCMClientImpl::~GCMClientImpl() {
266 void GCMClientImpl::Initialize(
267 const ChromeBuildInfo
& chrome_build_info
,
268 const base::FilePath
& path
,
269 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
270 const scoped_refptr
<net::URLRequestContextGetter
>&
271 url_request_context_getter
,
272 scoped_ptr
<Encryptor
> encryptor
,
273 GCMClient::Delegate
* delegate
) {
274 DCHECK_EQ(UNINITIALIZED
, state_
);
275 DCHECK(url_request_context_getter
.get());
278 url_request_context_getter_
= url_request_context_getter
;
279 const net::HttpNetworkSession::Params
* network_session_params
=
280 url_request_context_getter_
->GetURLRequestContext()->
281 GetNetworkSessionParams();
282 DCHECK(network_session_params
);
283 network_session_
= new net::HttpNetworkSession(*network_session_params
);
285 chrome_build_info_
= chrome_build_info
;
288 new GCMStoreImpl(path
, blocking_task_runner
, encryptor
.Pass()));
290 delegate_
= delegate
;
292 recorder_
.SetDelegate(this);
294 state_
= INITIALIZED
;
297 void GCMClientImpl::Start() {
298 DCHECK_EQ(INITIALIZED
, state_
);
300 // Once the loading is completed, the check-in will be initiated.
301 gcm_store_
->Load(base::Bind(&GCMClientImpl::OnLoadCompleted
,
302 weak_ptr_factory_
.GetWeakPtr()));
306 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
307 DCHECK_EQ(LOADING
, state_
);
309 if (!result
->success
) {
314 registrations_
= result
->registrations
;
315 device_checkin_info_
.android_id
= result
->device_android_id
;
316 device_checkin_info_
.secret
= result
->device_security_token
;
317 device_checkin_info_
.last_checkin_accounts
= result
->last_checkin_accounts
;
318 // A case where there were previously no accounts reported with checkin is
319 // considered to be the same as when the list of accounts is empty. It enables
320 // scheduling a periodic checkin for devices with no signed in users
321 // immediately after restart, while keeping |accounts_set == false| delays the
322 // checkin until the list of accounts is set explicitly.
323 if (result
->last_checkin_accounts
.size() == 0)
324 device_checkin_info_
.accounts_set
= true;
325 last_checkin_time_
= result
->last_checkin_time
;
326 gservices_settings_
.UpdateFromLoadResult(*result
);
327 // Taking over the value of account_mappings before passing the ownership of
328 // load result to InitializeMCSClient.
329 std::vector
<AccountMapping
> account_mappings
;
330 account_mappings
.swap(result
->account_mappings
);
331 base::Time last_token_fetch_time
= result
->last_token_fetch_time
;
333 InitializeMCSClient(result
.Pass());
335 if (device_checkin_info_
.IsValid()) {
336 SchedulePeriodicCheckin();
337 OnReady(account_mappings
, last_token_fetch_time
);
341 state_
= INITIAL_DEVICE_CHECKIN
;
342 device_checkin_info_
.Reset();
346 void GCMClientImpl::InitializeMCSClient(
347 scoped_ptr
<GCMStore::LoadResult
> result
) {
348 std::vector
<GURL
> endpoints
;
349 endpoints
.push_back(gservices_settings_
.GetMCSMainEndpoint());
350 endpoints
.push_back(gservices_settings_
.GetMCSFallbackEndpoint());
351 connection_factory_
= internals_builder_
->BuildConnectionFactory(
353 GetGCMBackoffPolicy(),
355 url_request_context_getter_
->GetURLRequestContext()
356 ->http_transaction_factory()
360 connection_factory_
->SetConnectionListener(this);
361 mcs_client_
= internals_builder_
->BuildMCSClient(
362 chrome_build_info_
.version
,
364 connection_factory_
.get(),
368 mcs_client_
->Initialize(
369 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
370 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
371 weak_ptr_factory_
.GetWeakPtr()),
372 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
373 weak_ptr_factory_
.GetWeakPtr()),
377 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
378 const CheckinInfo
& checkin_info
) {
379 DCHECK(!device_checkin_info_
.IsValid());
381 device_checkin_info_
.android_id
= checkin_info
.android_id
;
382 device_checkin_info_
.secret
= checkin_info
.secret
;
383 // If accounts were not set by now, we can consider them set (to empty list)
384 // to make sure periodic checkins get scheduled after initial checkin.
385 device_checkin_info_
.accounts_set
= true;
386 gcm_store_
->SetDeviceCredentials(
387 checkin_info
.android_id
, checkin_info
.secret
,
388 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
389 weak_ptr_factory_
.GetWeakPtr()));
391 OnReady(std::vector
<AccountMapping
>(), base::Time());
394 void GCMClientImpl::OnReady(const std::vector
<AccountMapping
>& account_mappings
,
395 const base::Time
& last_token_fetch_time
) {
399 delegate_
->OnGCMReady(account_mappings
, last_token_fetch_time
);
402 void GCMClientImpl::StartMCSLogin() {
403 DCHECK_EQ(READY
, state_
);
404 DCHECK(device_checkin_info_
.IsValid());
405 mcs_client_
->Login(device_checkin_info_
.android_id
,
406 device_checkin_info_
.secret
);
409 void GCMClientImpl::ResetState() {
410 state_
= UNINITIALIZED
;
411 // TODO(fgorski): reset all of the necessart objects and start over.
414 void GCMClientImpl::SetAccountTokens(
415 const std::vector
<AccountTokenInfo
>& account_tokens
) {
416 device_checkin_info_
.account_tokens
.clear();
417 for (std::vector
<AccountTokenInfo
>::const_iterator iter
=
418 account_tokens
.begin();
419 iter
!= account_tokens
.end();
421 device_checkin_info_
.account_tokens
[iter
->email
] = iter
->access_token
;
424 bool accounts_set_before
= device_checkin_info_
.accounts_set
;
425 device_checkin_info_
.accounts_set
= true;
427 DVLOG(1) << "Set account called with: " << account_tokens
.size()
430 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
433 bool account_removed
= false;
434 for (std::set
<std::string
>::iterator iter
=
435 device_checkin_info_
.last_checkin_accounts
.begin();
436 iter
!= device_checkin_info_
.last_checkin_accounts
.end();
438 if (device_checkin_info_
.account_tokens
.find(*iter
) ==
439 device_checkin_info_
.account_tokens
.end()) {
440 account_removed
= true;
444 // Checkin will be forced when any of the accounts was removed during the
445 // current Chrome session or if there has been an account removed between the
446 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
447 // We only force checkin when user signs out. When there is a new account
448 // signed in, the periodic checkin will take care of adding the association in
450 if (account_removed
) {
451 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
452 checkin_request_
.reset();
454 } else if (!accounts_set_before
) {
455 SchedulePeriodicCheckin();
456 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
460 void GCMClientImpl::UpdateAccountMapping(
461 const AccountMapping
& account_mapping
) {
462 gcm_store_
->AddAccountMapping(account_mapping
,
463 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
464 weak_ptr_factory_
.GetWeakPtr()));
467 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
468 gcm_store_
->RemoveAccountMapping(
470 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
471 weak_ptr_factory_
.GetWeakPtr()));
474 void GCMClientImpl::SetLastTokenFetchTime(const base::Time
& time
) {
475 gcm_store_
->SetLastTokenFetchTime(
477 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
478 weak_ptr_factory_
.GetWeakPtr()));
481 void GCMClientImpl::StartCheckin() {
482 // Make sure no checkin is in progress.
483 if (checkin_request_
.get())
486 checkin_proto::ChromeBuildProto chrome_build_proto
;
487 ToCheckinProtoVersion(chrome_build_info_
, &chrome_build_proto
);
488 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
489 device_checkin_info_
.secret
,
490 device_checkin_info_
.account_tokens
,
491 gservices_settings_
.digest(),
493 checkin_request_
.reset(
494 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
496 GetGCMBackoffPolicy(),
497 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
498 weak_ptr_factory_
.GetWeakPtr()),
499 url_request_context_getter_
.get(),
501 // Taking a snapshot of the accounts count here, as there might be an asynch
502 // update of the account tokens while checkin is in progress.
503 device_checkin_info_
.SnapshotCheckinAccounts();
504 checkin_request_
->Start();
507 void GCMClientImpl::OnCheckinCompleted(
508 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
509 checkin_request_
.reset();
511 if (!checkin_response
.has_android_id() ||
512 !checkin_response
.has_security_token()) {
513 // TODO(fgorski): I don't think a retry here will help, we should probably
514 // start over. By checking in with (0, 0).
518 CheckinInfo checkin_info
;
519 checkin_info
.android_id
= checkin_response
.android_id();
520 checkin_info
.secret
= checkin_response
.security_token();
522 if (state_
== INITIAL_DEVICE_CHECKIN
) {
523 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
525 // checkin_info is not expected to change after a periodic checkin as it
526 // would invalidate the registratoin IDs.
527 DCHECK_EQ(READY
, state_
);
528 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
529 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
532 if (device_checkin_info_
.IsValid()) {
533 // First update G-services settings, as something might have changed.
534 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
535 gcm_store_
->SetGServicesSettings(
536 gservices_settings_
.settings_map(),
537 gservices_settings_
.digest(),
538 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
539 weak_ptr_factory_
.GetWeakPtr()));
542 last_checkin_time_
= clock_
->Now();
543 gcm_store_
->SetLastCheckinInfo(
545 device_checkin_info_
.last_checkin_accounts
,
546 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
547 weak_ptr_factory_
.GetWeakPtr()));
548 SchedulePeriodicCheckin();
552 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
556 void GCMClientImpl::SchedulePeriodicCheckin() {
557 // Make sure no checkin is in progress.
558 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
561 // There should be only one periodic checkin pending at a time. Removing
562 // pending periodic checkin to schedule a new one.
563 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
565 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
566 if (time_to_next_checkin
< base::TimeDelta())
567 time_to_next_checkin
= base::TimeDelta();
569 base::MessageLoop::current()->PostDelayedTask(
571 base::Bind(&GCMClientImpl::StartCheckin
,
572 periodic_checkin_ptr_factory_
.GetWeakPtr()),
573 time_to_next_checkin
);
576 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
577 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
581 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
582 // TODO(fgorski): This is one of the signals that store needs a rebuild.
586 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
587 // TODO(fgorski): This is one of the signals that store needs a rebuild.
591 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
592 // TODO(fgorski): This is one of the signals that store needs a rebuild.
596 void GCMClientImpl::DefaultStoreCallback(bool success
) {
600 void GCMClientImpl::IgnoreWriteResultCallback(bool success
) {
601 // TODO(fgorski): Ignoring the write result for now to make sure
602 // sync_intergration_tests are not broken.
605 void GCMClientImpl::Stop() {
606 DVLOG(1) << "Stopping the GCM Client";
607 weak_ptr_factory_
.InvalidateWeakPtrs();
608 device_checkin_info_
.Reset();
609 connection_factory_
.reset();
610 delegate_
->OnDisconnected();
612 checkin_request_
.reset();
613 // Delete all of the pending registration requests, whithout telling the
615 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
617 STLDeleteValues(&pending_registration_requests_
);
618 state_
= INITIALIZED
;
622 void GCMClientImpl::CheckOut() {
624 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed
,
625 weak_ptr_factory_
.GetWeakPtr()));
628 void GCMClientImpl::Register(const std::string
& app_id
,
629 const std::vector
<std::string
>& sender_ids
) {
630 DCHECK_EQ(state_
, READY
);
632 // If the same sender ids is provided, return the cached registration ID
634 RegistrationInfoMap::const_iterator registrations_iter
=
635 registrations_
.find(app_id
);
636 if (registrations_iter
!= registrations_
.end() &&
637 registrations_iter
->second
->sender_ids
== sender_ids
) {
638 delegate_
->OnRegisterFinished(
639 app_id
, registrations_iter
->second
->registration_id
, SUCCESS
);
643 RegistrationRequest::RequestInfo
request_info(
644 device_checkin_info_
.android_id
,
645 device_checkin_info_
.secret
,
648 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
650 RegistrationRequest
* registration_request
=
651 new RegistrationRequest(gservices_settings_
.GetRegistrationURL(),
653 GetGCMBackoffPolicy(),
654 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
655 weak_ptr_factory_
.GetWeakPtr(),
658 kMaxRegistrationRetries
,
659 url_request_context_getter_
,
661 pending_registration_requests_
[app_id
] = registration_request
;
662 registration_request
->Start();
665 void GCMClientImpl::OnRegisterCompleted(
666 const std::string
& app_id
,
667 const std::vector
<std::string
>& sender_ids
,
668 RegistrationRequest::Status status
,
669 const std::string
& registration_id
) {
673 PendingRegistrationRequests::iterator iter
=
674 pending_registration_requests_
.find(app_id
);
675 if (iter
== pending_registration_requests_
.end())
676 result
= UNKNOWN_ERROR
;
677 else if (status
== RegistrationRequest::INVALID_SENDER
)
678 result
= INVALID_PARAMETER
;
679 else if (registration_id
.empty())
680 result
= SERVER_ERROR
;
684 if (result
== SUCCESS
) {
686 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
687 registration
->sender_ids
= sender_ids
;
688 registration
->registration_id
= registration_id
;
689 registrations_
[app_id
] = registration
;
691 // Save it in the persistent store.
692 gcm_store_
->AddRegistration(
695 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
696 weak_ptr_factory_
.GetWeakPtr()));
699 delegate_
->OnRegisterFinished(
700 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
702 if (iter
!= pending_registration_requests_
.end()) {
704 pending_registration_requests_
.erase(iter
);
708 void GCMClientImpl::Unregister(const std::string
& app_id
) {
709 DCHECK_EQ(state_
, READY
);
710 if (pending_unregistration_requests_
.count(app_id
) == 1)
713 // Remove from the cache and persistent store.
714 registrations_
.erase(app_id
);
715 gcm_store_
->RemoveRegistration(
717 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
718 weak_ptr_factory_
.GetWeakPtr()));
720 UnregistrationRequest::RequestInfo
request_info(
721 device_checkin_info_
.android_id
,
722 device_checkin_info_
.secret
,
725 UnregistrationRequest
* unregistration_request
= new UnregistrationRequest(
726 gservices_settings_
.GetRegistrationURL(),
728 GetGCMBackoffPolicy(),
729 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
730 weak_ptr_factory_
.GetWeakPtr(),
732 url_request_context_getter_
,
734 pending_unregistration_requests_
[app_id
] = unregistration_request
;
735 unregistration_request
->Start();
738 void GCMClientImpl::OnUnregisterCompleted(
739 const std::string
& app_id
,
740 UnregistrationRequest::Status status
) {
741 DVLOG(1) << "Unregister completed for app: " << app_id
742 << " with " << (status
? "success." : "failure.");
743 delegate_
->OnUnregisterFinished(
745 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
747 PendingUnregistrationRequests::iterator iter
=
748 pending_unregistration_requests_
.find(app_id
);
749 if (iter
== pending_unregistration_requests_
.end())
753 pending_unregistration_requests_
.erase(iter
);
756 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
757 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
758 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
761 void GCMClientImpl::Send(const std::string
& app_id
,
762 const std::string
& receiver_id
,
763 const OutgoingMessage
& message
) {
764 DCHECK_EQ(state_
, READY
);
766 RecordOutgoingMessageToUMA(message
);
768 mcs_proto::DataMessageStanza stanza
;
769 stanza
.set_ttl(message
.time_to_live
);
770 stanza
.set_sent(clock_
->Now().ToInternalValue() /
771 base::Time::kMicrosecondsPerSecond
);
772 stanza
.set_id(message
.id
);
773 stanza
.set_from(kSendMessageFromValue
);
774 stanza
.set_to(receiver_id
);
775 stanza
.set_category(app_id
);
777 for (MessageData::const_iterator iter
= message
.data
.begin();
778 iter
!= message
.data
.end();
780 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
781 app_data
->set_key(iter
->first
);
782 app_data
->set_value(iter
->second
);
785 MCSMessage
mcs_message(stanza
);
786 DVLOG(1) << "MCS message size: " << mcs_message
.size();
787 mcs_client_
->SendMessage(mcs_message
);
790 std::string
GCMClientImpl::GetStateString() const {
792 case GCMClientImpl::INITIALIZED
:
793 return "INITIALIZED";
794 case GCMClientImpl::UNINITIALIZED
:
795 return "UNINITIALIZED";
796 case GCMClientImpl::LOADING
:
798 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
799 return "INITIAL_DEVICE_CHECKIN";
800 case GCMClientImpl::READY
:
804 return std::string();
808 void GCMClientImpl::SetRecording(bool recording
) {
809 recorder_
.SetRecording(recording
);
812 void GCMClientImpl::ClearActivityLogs() {
816 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
817 GCMClient::GCMStatistics stats
;
818 stats
.gcm_client_created
= true;
819 stats
.is_recording
= recorder_
.is_recording();
820 stats
.gcm_client_state
= GetStateString();
821 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
822 if (connection_factory_
.get())
823 stats
.connection_state
= connection_factory_
->GetConnectionStateString();
824 if (mcs_client_
.get()) {
825 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
826 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
828 if (device_checkin_info_
.android_id
> 0)
829 stats
.android_id
= device_checkin_info_
.android_id
;
830 recorder_
.CollectActivities(&stats
.recorded_activities
);
832 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
833 it
!= registrations_
.end(); ++it
) {
834 stats
.registered_app_ids
.push_back(it
->first
);
839 void GCMClientImpl::OnActivityRecorded() {
840 delegate_
->OnActivityRecorded();
843 void GCMClientImpl::OnConnected(const GURL
& current_server
,
844 const net::IPEndPoint
& ip_endpoint
) {
845 // TODO(gcm): expose current server in debug page.
846 delegate_
->OnActivityRecorded();
847 delegate_
->OnConnected(ip_endpoint
);
850 void GCMClientImpl::OnDisconnected() {
851 delegate_
->OnActivityRecorded();
852 delegate_
->OnDisconnected();
855 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
856 switch (message
.tag()) {
857 case kLoginResponseTag
:
858 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
860 case kDataMessageStanzaTag
:
861 DVLOG(1) << "A downstream message received. Processing...";
862 HandleIncomingMessage(message
);
865 NOTREACHED() << "Message with unexpected tag received by GCMClient";
870 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
871 const std::string
& app_id
,
872 const std::string
& message_id
,
873 MCSClient::MessageSendStatus status
) {
874 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
877 // TTL_EXCEEDED is singled out here, because it can happen long time after the
878 // message was sent. That is why it comes as |OnMessageSendError| event rather
879 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
880 // All other errors will be raised immediately, through asynchronous callback.
881 // It is expected that TTL_EXCEEDED will be issued for a message that was
882 // previously issued |OnSendFinished| with status SUCCESS.
883 // TODO(jianli): Consider adding UMA for this status.
884 if (status
== MCSClient::TTL_EXCEEDED
) {
885 SendErrorDetails send_error_details
;
886 send_error_details
.message_id
= message_id
;
887 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
888 delegate_
->OnMessageSendError(app_id
, send_error_details
);
889 } else if (status
== MCSClient::SENT
) {
890 delegate_
->OnSendAcknowledged(app_id
, message_id
);
892 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
896 void GCMClientImpl::OnMCSError() {
897 // TODO(fgorski): For now it replaces the initialization method. Long term it
898 // should have an error or status passed in.
901 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
904 const mcs_proto::DataMessageStanza
& data_message_stanza
=
905 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
906 message
.GetProtobuf());
907 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
909 // Copying all the data from the stanza to a MessageData object. When present,
910 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
912 MessageData message_data
;
913 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
914 std::string key
= data_message_stanza
.app_data(i
).key();
915 message_data
[key
] = data_message_stanza
.app_data(i
).value();
918 MessageType message_type
= DATA_MESSAGE
;
919 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
920 if (iter
!= message_data
.end()) {
921 message_type
= DecodeMessageType(iter
->second
);
922 message_data
.erase(iter
);
925 switch (message_type
) {
927 HandleIncomingDataMessage(data_message_stanza
, message_data
);
929 case DELETED_MESSAGES
:
930 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
931 data_message_stanza
.from(),
932 data_message_stanza
.ByteSize(),
934 GCMStatsRecorder::DELETED_MESSAGES
);
935 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
938 HandleIncomingSendError(data_message_stanza
, message_data
);
941 default: // Treat default the same as UNKNOWN.
942 DVLOG(1) << "Unknown message_type received. Message ignored. "
943 << "App ID: " << data_message_stanza
.category() << ".";
948 void GCMClientImpl::HandleIncomingDataMessage(
949 const mcs_proto::DataMessageStanza
& data_message_stanza
,
950 MessageData
& message_data
) {
951 std::string app_id
= data_message_stanza
.category();
953 // Drop the message when the app is not registered for the sender of the
955 RegistrationInfoMap::iterator iter
= registrations_
.find(app_id
);
956 bool not_registered
=
957 iter
== registrations_
.end() ||
958 std::find(iter
->second
->sender_ids
.begin(),
959 iter
->second
->sender_ids
.end(),
960 data_message_stanza
.from()) == iter
->second
->sender_ids
.end();
961 recorder_
.RecordDataMessageReceived(app_id
, data_message_stanza
.from(),
962 data_message_stanza
.ByteSize(), !not_registered
,
963 GCMStatsRecorder::DATA_MESSAGE
);
964 if (not_registered
) {
968 IncomingMessage incoming_message
;
969 incoming_message
.sender_id
= data_message_stanza
.from();
970 if (data_message_stanza
.has_token())
971 incoming_message
.collapse_key
= data_message_stanza
.token();
972 incoming_message
.data
= message_data
;
973 delegate_
->OnMessageReceived(app_id
, incoming_message
);
976 void GCMClientImpl::HandleIncomingSendError(
977 const mcs_proto::DataMessageStanza
& data_message_stanza
,
978 MessageData
& message_data
) {
979 SendErrorDetails send_error_details
;
980 send_error_details
.additional_data
= message_data
;
981 send_error_details
.result
= SERVER_ERROR
;
983 MessageData::iterator iter
=
984 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
985 if (iter
!= send_error_details
.additional_data
.end()) {
986 send_error_details
.message_id
= iter
->second
;
987 send_error_details
.additional_data
.erase(iter
);
990 recorder_
.RecordIncomingSendError(
991 data_message_stanza
.category(),
992 data_message_stanza
.to(),
993 data_message_stanza
.id());
994 delegate_
->OnMessageSendError(data_message_stanza
.category(),