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_account_mapper.h"
20 #include "components/gcm_driver/gcm_backoff_policy.h"
21 #include "google_apis/gcm/base/encryptor.h"
22 #include "google_apis/gcm/base/mcs_message.h"
23 #include "google_apis/gcm/base/mcs_util.h"
24 #include "google_apis/gcm/engine/checkin_request.h"
25 #include "google_apis/gcm/engine/connection_factory_impl.h"
26 #include "google_apis/gcm/engine/gcm_store_impl.h"
27 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
28 #include "google_apis/gcm/protocol/checkin.pb.h"
29 #include "google_apis/gcm/protocol/mcs.pb.h"
30 #include "net/http/http_network_session.h"
31 #include "net/http/http_transaction_factory.h"
32 #include "net/url_request/url_request_context.h"
39 // Indicates a message type of the received message.
41 UNKNOWN
, // Undetermined type.
42 DATA_MESSAGE
, // Regular data message.
43 DELETED_MESSAGES
, // Messages were deleted on the server.
44 SEND_ERROR
, // Error sending a message.
47 enum OutgoingMessageTTLCategory
{
49 TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
,
50 TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
,
51 TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
,
52 TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK
,
53 TTL_MORE_THAN_ONE_WEEK
,
55 // NOTE: always keep this entry at the end. Add new TTL category only
56 // immediately above this line. Make sure to update the corresponding
57 // histogram enum accordingly.
61 const int kMaxRegistrationRetries
= 5;
62 const char kMessageTypeDataMessage
[] = "gcm";
63 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
64 const char kMessageTypeKey
[] = "message_type";
65 const char kMessageTypeSendErrorKey
[] = "send_error";
66 const char kSendErrorMessageIdKey
[] = "google.message_id";
67 const char kSendMessageFromValue
[] = "gcm@chrome.com";
68 const int64 kDefaultUserSerialNumber
= 0LL;
70 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus status
) {
72 case MCSClient::QUEUED
:
73 return GCMClient::SUCCESS
;
74 case MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
75 return GCMClient::NETWORK_ERROR
;
76 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
77 return GCMClient::NETWORK_ERROR
;
78 case MCSClient::MESSAGE_TOO_LARGE
:
79 return GCMClient::INVALID_PARAMETER
;
80 case MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
81 return GCMClient::NETWORK_ERROR
;
82 case MCSClient::TTL_EXCEEDED
:
83 return GCMClient::NETWORK_ERROR
;
89 return GCMClientImpl::UNKNOWN_ERROR
;
92 void ToCheckinProtoVersion(
93 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
94 checkin_proto::ChromeBuildProto
* android_build_info
) {
95 checkin_proto::ChromeBuildProto_Platform platform
;
96 switch (chrome_build_info
.platform
) {
97 case GCMClient::PLATFORM_WIN
:
98 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN
;
100 case GCMClient::PLATFORM_MAC
:
101 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC
;
103 case GCMClient::PLATFORM_LINUX
:
104 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
106 case GCMClient::PLATFORM_IOS
:
107 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS
;
109 case GCMClient::PLATFORM_ANDROID
:
110 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID
;
112 case GCMClient::PLATFORM_CROS
:
113 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS
;
115 case GCMClient::PLATFORM_UNKNOWN
:
116 // For unknown platform, return as LINUX.
117 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
121 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
124 android_build_info
->set_platform(platform
);
126 checkin_proto::ChromeBuildProto_Channel channel
;
127 switch (chrome_build_info
.channel
) {
128 case GCMClient::CHANNEL_STABLE
:
129 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE
;
131 case GCMClient::CHANNEL_BETA
:
132 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA
;
134 case GCMClient::CHANNEL_DEV
:
135 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV
;
137 case GCMClient::CHANNEL_CANARY
:
138 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY
;
140 case GCMClient::CHANNEL_UNKNOWN
:
141 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
145 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
148 android_build_info
->set_channel(channel
);
150 android_build_info
->set_chrome_version(chrome_build_info
.version
);
153 MessageType
DecodeMessageType(const std::string
& value
) {
154 if (kMessageTypeDeletedMessagesKey
== value
)
155 return DELETED_MESSAGES
;
156 if (kMessageTypeSendErrorKey
== value
)
158 if (kMessageTypeDataMessage
== value
)
163 void RecordOutgoingMessageToUMA(
164 const gcm::GCMClient::OutgoingMessage
& message
) {
165 OutgoingMessageTTLCategory ttl_category
;
166 if (message
.time_to_live
== 0)
167 ttl_category
= TTL_ZERO
;
168 else if (message
.time_to_live
<= 60 )
169 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
;
170 else if (message
.time_to_live
<= 60 * 60)
171 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
;
172 else if (message
.time_to_live
<= 24 * 60 * 60)
173 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
;
175 ttl_category
= TTL_MAXIMUM
;
177 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
184 GCMInternalsBuilder::GCMInternalsBuilder() {}
185 GCMInternalsBuilder::~GCMInternalsBuilder() {}
187 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
188 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
191 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
192 const std::string
& version
,
194 ConnectionFactory
* connection_factory
,
196 GCMStatsRecorder
* recorder
) {
197 return scoped_ptr
<MCSClient
>(new MCSClient(
198 version
, clock
, connection_factory
, gcm_store
, recorder
));
201 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
202 const std::vector
<GURL
>& endpoints
,
203 const net::BackoffEntry::Policy
& backoff_policy
,
204 const scoped_refptr
<net::HttpNetworkSession
>& gcm_network_session
,
205 const scoped_refptr
<net::HttpNetworkSession
>& http_network_session
,
206 net::NetLog
* net_log
,
207 GCMStatsRecorder
* recorder
) {
208 return make_scoped_ptr
<ConnectionFactory
>(
209 new ConnectionFactoryImpl(endpoints
,
212 http_network_session
,
217 GCMClientImpl::CheckinInfo::CheckinInfo()
218 : android_id(0), secret(0), accounts_set(false) {
221 GCMClientImpl::CheckinInfo::~CheckinInfo() {
224 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
225 last_checkin_accounts
.clear();
226 for (std::map
<std::string
, std::string
>::iterator iter
=
227 account_tokens
.begin();
228 iter
!= account_tokens
.end();
230 last_checkin_accounts
.insert(iter
->first
);
234 void GCMClientImpl::CheckinInfo::Reset() {
237 accounts_set
= false;
238 account_tokens
.clear();
239 last_checkin_accounts
.clear();
242 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
243 : internals_builder_(internals_builder
.Pass()),
244 state_(UNINITIALIZED
),
246 start_mode_(DELAYED_START
),
247 clock_(internals_builder_
->BuildClock()),
248 url_request_context_getter_(NULL
),
249 pending_registration_requests_deleter_(&pending_registration_requests_
),
250 pending_unregistration_requests_deleter_(
251 &pending_unregistration_requests_
),
252 periodic_checkin_ptr_factory_(this),
253 weak_ptr_factory_(this) {
256 GCMClientImpl::~GCMClientImpl() {
259 void GCMClientImpl::Initialize(
260 const ChromeBuildInfo
& chrome_build_info
,
261 const base::FilePath
& path
,
262 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
263 const scoped_refptr
<net::URLRequestContextGetter
>&
264 url_request_context_getter
,
265 scoped_ptr
<Encryptor
> encryptor
,
266 GCMClient::Delegate
* delegate
) {
267 DCHECK_EQ(UNINITIALIZED
, state_
);
268 DCHECK(url_request_context_getter
.get());
271 url_request_context_getter_
= url_request_context_getter
;
272 const net::HttpNetworkSession::Params
* network_session_params
=
273 url_request_context_getter_
->GetURLRequestContext()->
274 GetNetworkSessionParams();
275 DCHECK(network_session_params
);
276 network_session_
= new net::HttpNetworkSession(*network_session_params
);
278 chrome_build_info_
= chrome_build_info
;
281 new GCMStoreImpl(path
, blocking_task_runner
, encryptor
.Pass()));
283 delegate_
= delegate
;
285 recorder_
.SetDelegate(this);
287 state_
= INITIALIZED
;
290 void GCMClientImpl::Start(StartMode start_mode
) {
291 DCHECK_NE(UNINITIALIZED
, state_
);
293 if (state_
== LOADED
) {
294 // Start the GCM if not yet.
295 if (start_mode
== IMMEDIATE_START
)
300 // The delay start behavior will be abandoned when Start has been called
301 // once with IMMEDIATE_START behavior.
302 if (start_mode
== IMMEDIATE_START
)
303 start_mode_
= IMMEDIATE_START
;
305 // Bail out if the loading is not started or completed.
306 if (state_
!= INITIALIZED
)
309 // Once the loading is completed, the check-in will be initiated.
310 gcm_store_
->Load(base::Bind(&GCMClientImpl::OnLoadCompleted
,
311 weak_ptr_factory_
.GetWeakPtr()));
315 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
316 DCHECK_EQ(LOADING
, state_
);
318 if (!result
->success
) {
323 registrations_
= result
->registrations
;
324 device_checkin_info_
.android_id
= result
->device_android_id
;
325 device_checkin_info_
.secret
= result
->device_security_token
;
326 device_checkin_info_
.last_checkin_accounts
= result
->last_checkin_accounts
;
327 // A case where there were previously no accounts reported with checkin is
328 // considered to be the same as when the list of accounts is empty. It enables
329 // scheduling a periodic checkin for devices with no signed in users
330 // immediately after restart, while keeping |accounts_set == false| delays the
331 // checkin until the list of accounts is set explicitly.
332 if (result
->last_checkin_accounts
.size() == 0)
333 device_checkin_info_
.accounts_set
= true;
334 last_checkin_time_
= result
->last_checkin_time
;
335 gservices_settings_
.UpdateFromLoadResult(*result
);
336 load_result_
= result
.Pass();
339 // Don't initiate the GCM connection when GCM is in delayed start mode and
340 // not any standalone app has registered GCM yet.
341 if (start_mode_
== DELAYED_START
&& !HasStandaloneRegisteredApp())
347 void GCMClientImpl::StartGCM() {
348 // Taking over the value of account_mappings before passing the ownership of
349 // load result to InitializeMCSClient.
350 std::vector
<AccountMapping
> account_mappings
;
351 account_mappings
.swap(load_result_
->account_mappings
);
352 base::Time last_token_fetch_time
= load_result_
->last_token_fetch_time
;
354 InitializeMCSClient();
356 if (device_checkin_info_
.IsValid()) {
357 SchedulePeriodicCheckin();
358 OnReady(account_mappings
, last_token_fetch_time
);
362 state_
= INITIAL_DEVICE_CHECKIN
;
363 device_checkin_info_
.Reset();
367 void GCMClientImpl::InitializeMCSClient() {
368 std::vector
<GURL
> endpoints
;
369 endpoints
.push_back(gservices_settings_
.GetMCSMainEndpoint());
370 endpoints
.push_back(gservices_settings_
.GetMCSFallbackEndpoint());
371 connection_factory_
= internals_builder_
->BuildConnectionFactory(
373 GetGCMBackoffPolicy(),
375 url_request_context_getter_
->GetURLRequestContext()
376 ->http_transaction_factory()
380 connection_factory_
->SetConnectionListener(this);
381 mcs_client_
= internals_builder_
->BuildMCSClient(
382 chrome_build_info_
.version
,
384 connection_factory_
.get(),
388 mcs_client_
->Initialize(
389 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
390 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
391 weak_ptr_factory_
.GetWeakPtr()),
392 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
393 weak_ptr_factory_
.GetWeakPtr()),
394 load_result_
.Pass());
397 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
398 const CheckinInfo
& checkin_info
) {
399 DCHECK(!device_checkin_info_
.IsValid());
401 device_checkin_info_
.android_id
= checkin_info
.android_id
;
402 device_checkin_info_
.secret
= checkin_info
.secret
;
403 // If accounts were not set by now, we can consider them set (to empty list)
404 // to make sure periodic checkins get scheduled after initial checkin.
405 device_checkin_info_
.accounts_set
= true;
406 gcm_store_
->SetDeviceCredentials(
407 checkin_info
.android_id
, checkin_info
.secret
,
408 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
409 weak_ptr_factory_
.GetWeakPtr()));
411 OnReady(std::vector
<AccountMapping
>(), base::Time());
414 void GCMClientImpl::OnReady(const std::vector
<AccountMapping
>& account_mappings
,
415 const base::Time
& last_token_fetch_time
) {
419 delegate_
->OnGCMReady(account_mappings
, last_token_fetch_time
);
422 void GCMClientImpl::StartMCSLogin() {
423 DCHECK_EQ(READY
, state_
);
424 DCHECK(device_checkin_info_
.IsValid());
425 mcs_client_
->Login(device_checkin_info_
.android_id
,
426 device_checkin_info_
.secret
);
429 void GCMClientImpl::ResetState() {
430 state_
= UNINITIALIZED
;
431 // TODO(fgorski): reset all of the necessart objects and start over.
434 void GCMClientImpl::SetAccountTokens(
435 const std::vector
<AccountTokenInfo
>& account_tokens
) {
436 device_checkin_info_
.account_tokens
.clear();
437 for (std::vector
<AccountTokenInfo
>::const_iterator iter
=
438 account_tokens
.begin();
439 iter
!= account_tokens
.end();
441 device_checkin_info_
.account_tokens
[iter
->email
] = iter
->access_token
;
444 bool accounts_set_before
= device_checkin_info_
.accounts_set
;
445 device_checkin_info_
.accounts_set
= true;
447 DVLOG(1) << "Set account called with: " << account_tokens
.size()
450 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
453 bool account_removed
= false;
454 for (std::set
<std::string
>::iterator iter
=
455 device_checkin_info_
.last_checkin_accounts
.begin();
456 iter
!= device_checkin_info_
.last_checkin_accounts
.end();
458 if (device_checkin_info_
.account_tokens
.find(*iter
) ==
459 device_checkin_info_
.account_tokens
.end()) {
460 account_removed
= true;
464 // Checkin will be forced when any of the accounts was removed during the
465 // current Chrome session or if there has been an account removed between the
466 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
467 // We only force checkin when user signs out. When there is a new account
468 // signed in, the periodic checkin will take care of adding the association in
470 if (account_removed
) {
471 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
472 checkin_request_
.reset();
474 } else if (!accounts_set_before
) {
475 SchedulePeriodicCheckin();
476 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
480 void GCMClientImpl::UpdateAccountMapping(
481 const AccountMapping
& account_mapping
) {
482 gcm_store_
->AddAccountMapping(account_mapping
,
483 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
484 weak_ptr_factory_
.GetWeakPtr()));
487 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
488 gcm_store_
->RemoveAccountMapping(
490 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
491 weak_ptr_factory_
.GetWeakPtr()));
494 void GCMClientImpl::SetLastTokenFetchTime(const base::Time
& time
) {
495 gcm_store_
->SetLastTokenFetchTime(
497 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
498 weak_ptr_factory_
.GetWeakPtr()));
501 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr
<base::Timer
> timer
) {
503 mcs_client_
->UpdateHeartbeatTimer(timer
.Pass());
506 void GCMClientImpl::StartCheckin() {
507 // Make sure no checkin is in progress.
508 if (checkin_request_
.get())
511 checkin_proto::ChromeBuildProto chrome_build_proto
;
512 ToCheckinProtoVersion(chrome_build_info_
, &chrome_build_proto
);
513 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
514 device_checkin_info_
.secret
,
515 device_checkin_info_
.account_tokens
,
516 gservices_settings_
.digest(),
518 checkin_request_
.reset(
519 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
521 GetGCMBackoffPolicy(),
522 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
523 weak_ptr_factory_
.GetWeakPtr()),
524 url_request_context_getter_
.get(),
526 // Taking a snapshot of the accounts count here, as there might be an asynch
527 // update of the account tokens while checkin is in progress.
528 device_checkin_info_
.SnapshotCheckinAccounts();
529 checkin_request_
->Start();
532 void GCMClientImpl::OnCheckinCompleted(
533 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
534 checkin_request_
.reset();
536 if (!checkin_response
.has_android_id() ||
537 !checkin_response
.has_security_token()) {
538 // TODO(fgorski): I don't think a retry here will help, we should probably
539 // start over. By checking in with (0, 0).
543 CheckinInfo checkin_info
;
544 checkin_info
.android_id
= checkin_response
.android_id();
545 checkin_info
.secret
= checkin_response
.security_token();
547 if (state_
== INITIAL_DEVICE_CHECKIN
) {
548 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
550 // checkin_info is not expected to change after a periodic checkin as it
551 // would invalidate the registratoin IDs.
552 DCHECK_EQ(READY
, state_
);
553 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
554 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
557 if (device_checkin_info_
.IsValid()) {
558 // First update G-services settings, as something might have changed.
559 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
560 gcm_store_
->SetGServicesSettings(
561 gservices_settings_
.settings_map(),
562 gservices_settings_
.digest(),
563 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
564 weak_ptr_factory_
.GetWeakPtr()));
567 last_checkin_time_
= clock_
->Now();
568 gcm_store_
->SetLastCheckinInfo(
570 device_checkin_info_
.last_checkin_accounts
,
571 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
572 weak_ptr_factory_
.GetWeakPtr()));
573 SchedulePeriodicCheckin();
577 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
581 void GCMClientImpl::SchedulePeriodicCheckin() {
582 // Make sure no checkin is in progress.
583 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
586 // There should be only one periodic checkin pending at a time. Removing
587 // pending periodic checkin to schedule a new one.
588 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
590 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
591 if (time_to_next_checkin
< base::TimeDelta())
592 time_to_next_checkin
= base::TimeDelta();
594 base::MessageLoop::current()->PostDelayedTask(
596 base::Bind(&GCMClientImpl::StartCheckin
,
597 periodic_checkin_ptr_factory_
.GetWeakPtr()),
598 time_to_next_checkin
);
601 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
602 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
606 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
607 // TODO(fgorski): This is one of the signals that store needs a rebuild.
611 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
612 // TODO(fgorski): This is one of the signals that store needs a rebuild.
616 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
617 // TODO(fgorski): This is one of the signals that store needs a rebuild.
621 void GCMClientImpl::DefaultStoreCallback(bool success
) {
625 void GCMClientImpl::IgnoreWriteResultCallback(bool success
) {
626 // TODO(fgorski): Ignoring the write result for now to make sure
627 // sync_intergration_tests are not broken.
630 void GCMClientImpl::Stop() {
631 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
633 DVLOG(1) << "Stopping the GCM Client";
634 weak_ptr_factory_
.InvalidateWeakPtrs();
635 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
636 device_checkin_info_
.Reset();
637 connection_factory_
.reset();
638 delegate_
->OnDisconnected();
640 checkin_request_
.reset();
641 // Delete all of the pending registration and unregistration requests.
642 STLDeleteValues(&pending_registration_requests_
);
643 STLDeleteValues(&pending_unregistration_requests_
);
644 state_
= INITIALIZED
;
648 void GCMClientImpl::Register(const std::string
& app_id
,
649 const std::vector
<std::string
>& sender_ids
) {
650 DCHECK_EQ(state_
, READY
);
652 // If the same sender ids is provided, return the cached registration ID
654 RegistrationInfoMap::const_iterator registrations_iter
=
655 registrations_
.find(app_id
);
656 if (registrations_iter
!= registrations_
.end() &&
657 registrations_iter
->second
->sender_ids
== sender_ids
) {
658 delegate_
->OnRegisterFinished(
659 app_id
, registrations_iter
->second
->registration_id
, SUCCESS
);
663 RegistrationRequest::RequestInfo
request_info(
664 device_checkin_info_
.android_id
,
665 device_checkin_info_
.secret
,
668 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
670 RegistrationRequest
* registration_request
=
671 new RegistrationRequest(gservices_settings_
.GetRegistrationURL(),
673 GetGCMBackoffPolicy(),
674 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
675 weak_ptr_factory_
.GetWeakPtr(),
678 kMaxRegistrationRetries
,
679 url_request_context_getter_
,
681 pending_registration_requests_
[app_id
] = registration_request
;
682 registration_request
->Start();
685 void GCMClientImpl::OnRegisterCompleted(
686 const std::string
& app_id
,
687 const std::vector
<std::string
>& sender_ids
,
688 RegistrationRequest::Status status
,
689 const std::string
& registration_id
) {
693 PendingRegistrationRequests::iterator iter
=
694 pending_registration_requests_
.find(app_id
);
695 if (iter
== pending_registration_requests_
.end())
696 result
= UNKNOWN_ERROR
;
697 else if (status
== RegistrationRequest::INVALID_SENDER
)
698 result
= INVALID_PARAMETER
;
699 else if (registration_id
.empty())
700 result
= SERVER_ERROR
;
704 if (result
== SUCCESS
) {
706 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
707 registration
->sender_ids
= sender_ids
;
708 registration
->registration_id
= registration_id
;
709 registrations_
[app_id
] = registration
;
711 // Save it in the persistent store.
712 gcm_store_
->AddRegistration(
715 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
716 weak_ptr_factory_
.GetWeakPtr()));
719 delegate_
->OnRegisterFinished(
720 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
722 if (iter
!= pending_registration_requests_
.end()) {
724 pending_registration_requests_
.erase(iter
);
728 void GCMClientImpl::Unregister(const std::string
& app_id
) {
729 DCHECK_EQ(state_
, READY
);
730 if (pending_unregistration_requests_
.count(app_id
) == 1)
733 // Remove from the cache and persistent store.
734 registrations_
.erase(app_id
);
735 gcm_store_
->RemoveRegistration(
737 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
738 weak_ptr_factory_
.GetWeakPtr()));
740 UnregistrationRequest::RequestInfo
request_info(
741 device_checkin_info_
.android_id
,
742 device_checkin_info_
.secret
,
745 UnregistrationRequest
* unregistration_request
= new UnregistrationRequest(
746 gservices_settings_
.GetRegistrationURL(),
748 GetGCMBackoffPolicy(),
749 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
750 weak_ptr_factory_
.GetWeakPtr(),
752 url_request_context_getter_
,
754 pending_unregistration_requests_
[app_id
] = unregistration_request
;
755 unregistration_request
->Start();
758 void GCMClientImpl::OnUnregisterCompleted(
759 const std::string
& app_id
,
760 UnregistrationRequest::Status status
) {
761 DVLOG(1) << "Unregister completed for app: " << app_id
762 << " with " << (status
? "success." : "failure.");
763 delegate_
->OnUnregisterFinished(
765 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
767 PendingUnregistrationRequests::iterator iter
=
768 pending_unregistration_requests_
.find(app_id
);
769 if (iter
== pending_unregistration_requests_
.end())
773 pending_unregistration_requests_
.erase(iter
);
776 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
777 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
778 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
781 void GCMClientImpl::Send(const std::string
& app_id
,
782 const std::string
& receiver_id
,
783 const OutgoingMessage
& message
) {
784 DCHECK_EQ(state_
, READY
);
786 RecordOutgoingMessageToUMA(message
);
788 mcs_proto::DataMessageStanza stanza
;
789 stanza
.set_ttl(message
.time_to_live
);
790 stanza
.set_sent(clock_
->Now().ToInternalValue() /
791 base::Time::kMicrosecondsPerSecond
);
792 stanza
.set_id(message
.id
);
793 stanza
.set_from(kSendMessageFromValue
);
794 stanza
.set_to(receiver_id
);
795 stanza
.set_category(app_id
);
797 for (MessageData::const_iterator iter
= message
.data
.begin();
798 iter
!= message
.data
.end();
800 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
801 app_data
->set_key(iter
->first
);
802 app_data
->set_value(iter
->second
);
805 MCSMessage
mcs_message(stanza
);
806 DVLOG(1) << "MCS message size: " << mcs_message
.size();
807 mcs_client_
->SendMessage(mcs_message
);
810 std::string
GCMClientImpl::GetStateString() const {
812 case GCMClientImpl::INITIALIZED
:
813 return "INITIALIZED";
814 case GCMClientImpl::UNINITIALIZED
:
815 return "UNINITIALIZED";
816 case GCMClientImpl::LOADING
:
818 case GCMClientImpl::LOADED
:
820 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
821 return "INITIAL_DEVICE_CHECKIN";
822 case GCMClientImpl::READY
:
826 return std::string();
830 void GCMClientImpl::SetRecording(bool recording
) {
831 recorder_
.SetRecording(recording
);
834 void GCMClientImpl::ClearActivityLogs() {
838 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
839 GCMClient::GCMStatistics stats
;
840 stats
.gcm_client_created
= true;
841 stats
.is_recording
= recorder_
.is_recording();
842 stats
.gcm_client_state
= GetStateString();
843 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
844 if (connection_factory_
.get())
845 stats
.connection_state
= connection_factory_
->GetConnectionStateString();
846 if (mcs_client_
.get()) {
847 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
848 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
850 if (device_checkin_info_
.android_id
> 0)
851 stats
.android_id
= device_checkin_info_
.android_id
;
852 recorder_
.CollectActivities(&stats
.recorded_activities
);
854 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
855 it
!= registrations_
.end(); ++it
) {
856 stats
.registered_app_ids
.push_back(it
->first
);
861 void GCMClientImpl::OnActivityRecorded() {
862 delegate_
->OnActivityRecorded();
865 void GCMClientImpl::OnConnected(const GURL
& current_server
,
866 const net::IPEndPoint
& ip_endpoint
) {
867 // TODO(gcm): expose current server in debug page.
868 delegate_
->OnActivityRecorded();
869 delegate_
->OnConnected(ip_endpoint
);
872 void GCMClientImpl::OnDisconnected() {
873 delegate_
->OnActivityRecorded();
874 delegate_
->OnDisconnected();
877 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
878 switch (message
.tag()) {
879 case kLoginResponseTag
:
880 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
882 case kDataMessageStanzaTag
:
883 DVLOG(1) << "A downstream message received. Processing...";
884 HandleIncomingMessage(message
);
887 NOTREACHED() << "Message with unexpected tag received by GCMClient";
892 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
893 const std::string
& app_id
,
894 const std::string
& message_id
,
895 MCSClient::MessageSendStatus status
) {
896 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
899 // TTL_EXCEEDED is singled out here, because it can happen long time after the
900 // message was sent. That is why it comes as |OnMessageSendError| event rather
901 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
902 // All other errors will be raised immediately, through asynchronous callback.
903 // It is expected that TTL_EXCEEDED will be issued for a message that was
904 // previously issued |OnSendFinished| with status SUCCESS.
905 // TODO(jianli): Consider adding UMA for this status.
906 if (status
== MCSClient::TTL_EXCEEDED
) {
907 SendErrorDetails send_error_details
;
908 send_error_details
.message_id
= message_id
;
909 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
910 delegate_
->OnMessageSendError(app_id
, send_error_details
);
911 } else if (status
== MCSClient::SENT
) {
912 delegate_
->OnSendAcknowledged(app_id
, message_id
);
914 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
918 void GCMClientImpl::OnMCSError() {
919 // TODO(fgorski): For now it replaces the initialization method. Long term it
920 // should have an error or status passed in.
923 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
926 const mcs_proto::DataMessageStanza
& data_message_stanza
=
927 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
928 message
.GetProtobuf());
929 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
931 // Copying all the data from the stanza to a MessageData object. When present,
932 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
934 MessageData message_data
;
935 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
936 std::string key
= data_message_stanza
.app_data(i
).key();
937 message_data
[key
] = data_message_stanza
.app_data(i
).value();
940 MessageType message_type
= DATA_MESSAGE
;
941 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
942 if (iter
!= message_data
.end()) {
943 message_type
= DecodeMessageType(iter
->second
);
944 message_data
.erase(iter
);
947 switch (message_type
) {
949 HandleIncomingDataMessage(data_message_stanza
, message_data
);
951 case DELETED_MESSAGES
:
952 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
953 data_message_stanza
.from(),
954 data_message_stanza
.ByteSize(),
956 GCMStatsRecorder::DELETED_MESSAGES
);
957 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
960 HandleIncomingSendError(data_message_stanza
, message_data
);
963 default: // Treat default the same as UNKNOWN.
964 DVLOG(1) << "Unknown message_type received. Message ignored. "
965 << "App ID: " << data_message_stanza
.category() << ".";
970 void GCMClientImpl::HandleIncomingDataMessage(
971 const mcs_proto::DataMessageStanza
& data_message_stanza
,
972 MessageData
& message_data
) {
973 std::string app_id
= data_message_stanza
.category();
975 // Drop the message when the app is not registered for the sender of the
977 RegistrationInfoMap::iterator iter
= registrations_
.find(app_id
);
978 bool not_registered
=
979 iter
== registrations_
.end() ||
980 std::find(iter
->second
->sender_ids
.begin(),
981 iter
->second
->sender_ids
.end(),
982 data_message_stanza
.from()) == iter
->second
->sender_ids
.end();
983 recorder_
.RecordDataMessageReceived(app_id
, data_message_stanza
.from(),
984 data_message_stanza
.ByteSize(), !not_registered
,
985 GCMStatsRecorder::DATA_MESSAGE
);
986 if (not_registered
) {
990 IncomingMessage incoming_message
;
991 incoming_message
.sender_id
= data_message_stanza
.from();
992 if (data_message_stanza
.has_token())
993 incoming_message
.collapse_key
= data_message_stanza
.token();
994 incoming_message
.data
= message_data
;
995 delegate_
->OnMessageReceived(app_id
, incoming_message
);
998 void GCMClientImpl::HandleIncomingSendError(
999 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1000 MessageData
& message_data
) {
1001 SendErrorDetails send_error_details
;
1002 send_error_details
.additional_data
= message_data
;
1003 send_error_details
.result
= SERVER_ERROR
;
1005 MessageData::iterator iter
=
1006 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
1007 if (iter
!= send_error_details
.additional_data
.end()) {
1008 send_error_details
.message_id
= iter
->second
;
1009 send_error_details
.additional_data
.erase(iter
);
1012 recorder_
.RecordIncomingSendError(
1013 data_message_stanza
.category(),
1014 data_message_stanza
.to(),
1015 data_message_stanza
.id());
1016 delegate_
->OnMessageSendError(data_message_stanza
.category(),
1017 send_error_details
);
1020 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1021 if (registrations_
.empty())
1023 // Note that account mapper is not counted as a standalone app since it is
1024 // automatically started when other app uses GCM.
1025 return registrations_
.size() > 1 ||
1026 !registrations_
.count(kGCMAccountMapperAppId
);