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_macros.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 enum ResetStoreError
{
62 DESTROYING_STORE_FAILED
,
64 // NOTE: always keep this entry at the end. Add new value only immediately
65 // above this line. Make sure to update the corresponding histogram enum
67 RESET_STORE_ERROR_COUNT
70 const int kMaxRegistrationRetries
= 5;
71 const char kMessageTypeDataMessage
[] = "gcm";
72 const char kMessageTypeDeletedMessagesKey
[] = "deleted_messages";
73 const char kMessageTypeKey
[] = "message_type";
74 const char kMessageTypeSendErrorKey
[] = "send_error";
75 const char kSendErrorMessageIdKey
[] = "google.message_id";
76 const char kSendMessageFromValue
[] = "gcm@chrome.com";
77 const int64 kDefaultUserSerialNumber
= 0LL;
79 GCMClient::Result
ToGCMClientResult(MCSClient::MessageSendStatus status
) {
81 case MCSClient::QUEUED
:
82 return GCMClient::SUCCESS
;
83 case MCSClient::QUEUE_SIZE_LIMIT_REACHED
:
84 return GCMClient::NETWORK_ERROR
;
85 case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED
:
86 return GCMClient::NETWORK_ERROR
;
87 case MCSClient::MESSAGE_TOO_LARGE
:
88 return GCMClient::INVALID_PARAMETER
;
89 case MCSClient::NO_CONNECTION_ON_ZERO_TTL
:
90 return GCMClient::NETWORK_ERROR
;
91 case MCSClient::TTL_EXCEEDED
:
92 return GCMClient::NETWORK_ERROR
;
98 return GCMClientImpl::UNKNOWN_ERROR
;
101 void ToCheckinProtoVersion(
102 const GCMClient::ChromeBuildInfo
& chrome_build_info
,
103 checkin_proto::ChromeBuildProto
* android_build_info
) {
104 checkin_proto::ChromeBuildProto_Platform platform
;
105 switch (chrome_build_info
.platform
) {
106 case GCMClient::PLATFORM_WIN
:
107 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN
;
109 case GCMClient::PLATFORM_MAC
:
110 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC
;
112 case GCMClient::PLATFORM_LINUX
:
113 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
115 case GCMClient::PLATFORM_IOS
:
116 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS
;
118 case GCMClient::PLATFORM_ANDROID
:
119 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID
;
121 case GCMClient::PLATFORM_CROS
:
122 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS
;
124 case GCMClient::PLATFORM_UNKNOWN
:
125 // For unknown platform, return as LINUX.
126 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
130 platform
= checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX
;
133 android_build_info
->set_platform(platform
);
135 checkin_proto::ChromeBuildProto_Channel channel
;
136 switch (chrome_build_info
.channel
) {
137 case GCMClient::CHANNEL_STABLE
:
138 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE
;
140 case GCMClient::CHANNEL_BETA
:
141 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA
;
143 case GCMClient::CHANNEL_DEV
:
144 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV
;
146 case GCMClient::CHANNEL_CANARY
:
147 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY
;
149 case GCMClient::CHANNEL_UNKNOWN
:
150 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
154 channel
= checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN
;
157 android_build_info
->set_channel(channel
);
159 android_build_info
->set_chrome_version(chrome_build_info
.version
);
162 MessageType
DecodeMessageType(const std::string
& value
) {
163 if (kMessageTypeDeletedMessagesKey
== value
)
164 return DELETED_MESSAGES
;
165 if (kMessageTypeSendErrorKey
== value
)
167 if (kMessageTypeDataMessage
== value
)
172 void RecordOutgoingMessageToUMA(
173 const gcm::GCMClient::OutgoingMessage
& message
) {
174 OutgoingMessageTTLCategory ttl_category
;
175 if (message
.time_to_live
== 0)
176 ttl_category
= TTL_ZERO
;
177 else if (message
.time_to_live
<= 60 )
178 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE
;
179 else if (message
.time_to_live
<= 60 * 60)
180 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR
;
181 else if (message
.time_to_live
<= 24 * 60 * 60)
182 ttl_category
= TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY
;
184 ttl_category
= TTL_MAXIMUM
;
186 UMA_HISTOGRAM_ENUMERATION("GCM.OutgoingMessageTTL",
191 void RecordResetStoreErrorToUMA(ResetStoreError error
) {
192 UMA_HISTOGRAM_ENUMERATION("GCM.ResetStore", error
, RESET_STORE_ERROR_COUNT
);
197 GCMInternalsBuilder::GCMInternalsBuilder() {}
198 GCMInternalsBuilder::~GCMInternalsBuilder() {}
200 scoped_ptr
<base::Clock
> GCMInternalsBuilder::BuildClock() {
201 return make_scoped_ptr
<base::Clock
>(new base::DefaultClock());
204 scoped_ptr
<MCSClient
> GCMInternalsBuilder::BuildMCSClient(
205 const std::string
& version
,
207 ConnectionFactory
* connection_factory
,
209 GCMStatsRecorder
* recorder
) {
210 return scoped_ptr
<MCSClient
>(new MCSClient(
211 version
, clock
, connection_factory
, gcm_store
, recorder
));
214 scoped_ptr
<ConnectionFactory
> GCMInternalsBuilder::BuildConnectionFactory(
215 const std::vector
<GURL
>& endpoints
,
216 const net::BackoffEntry::Policy
& backoff_policy
,
217 const scoped_refptr
<net::HttpNetworkSession
>& gcm_network_session
,
218 const scoped_refptr
<net::HttpNetworkSession
>& http_network_session
,
219 net::NetLog
* net_log
,
220 GCMStatsRecorder
* recorder
) {
221 return make_scoped_ptr
<ConnectionFactory
>(
222 new ConnectionFactoryImpl(endpoints
,
225 http_network_session
,
230 GCMClientImpl::CheckinInfo::CheckinInfo()
231 : android_id(0), secret(0), accounts_set(false) {
234 GCMClientImpl::CheckinInfo::~CheckinInfo() {
237 void GCMClientImpl::CheckinInfo::SnapshotCheckinAccounts() {
238 last_checkin_accounts
.clear();
239 for (std::map
<std::string
, std::string
>::iterator iter
=
240 account_tokens
.begin();
241 iter
!= account_tokens
.end();
243 last_checkin_accounts
.insert(iter
->first
);
247 void GCMClientImpl::CheckinInfo::Reset() {
250 accounts_set
= false;
251 account_tokens
.clear();
252 last_checkin_accounts
.clear();
255 GCMClientImpl::GCMClientImpl(scoped_ptr
<GCMInternalsBuilder
> internals_builder
)
256 : internals_builder_(internals_builder
.Pass()),
257 state_(UNINITIALIZED
),
259 start_mode_(DELAYED_START
),
260 clock_(internals_builder_
->BuildClock()),
261 gcm_store_reset_(false),
262 url_request_context_getter_(NULL
),
263 pending_registration_requests_deleter_(&pending_registration_requests_
),
264 pending_unregistration_requests_deleter_(
265 &pending_unregistration_requests_
),
266 periodic_checkin_ptr_factory_(this),
267 weak_ptr_factory_(this) {
270 GCMClientImpl::~GCMClientImpl() {
273 void GCMClientImpl::Initialize(
274 const ChromeBuildInfo
& chrome_build_info
,
275 const base::FilePath
& path
,
276 const scoped_refptr
<base::SequencedTaskRunner
>& blocking_task_runner
,
277 const scoped_refptr
<net::URLRequestContextGetter
>&
278 url_request_context_getter
,
279 scoped_ptr
<Encryptor
> encryptor
,
280 GCMClient::Delegate
* delegate
) {
281 DCHECK_EQ(UNINITIALIZED
, state_
);
282 DCHECK(url_request_context_getter
.get());
285 url_request_context_getter_
= url_request_context_getter
;
286 const net::HttpNetworkSession::Params
* network_session_params
=
287 url_request_context_getter_
->GetURLRequestContext()->
288 GetNetworkSessionParams();
289 DCHECK(network_session_params
);
290 network_session_
= new net::HttpNetworkSession(*network_session_params
);
292 chrome_build_info_
= chrome_build_info
;
295 new GCMStoreImpl(path
, blocking_task_runner
, encryptor
.Pass()));
297 delegate_
= delegate
;
299 recorder_
.SetDelegate(this);
301 state_
= INITIALIZED
;
304 void GCMClientImpl::Start(StartMode start_mode
) {
305 DCHECK_NE(UNINITIALIZED
, state_
);
307 if (state_
== LOADED
) {
308 // Start the GCM if not yet.
309 if (start_mode
== IMMEDIATE_START
)
314 // The delay start behavior will be abandoned when Start has been called
315 // once with IMMEDIATE_START behavior.
316 if (start_mode
== IMMEDIATE_START
)
317 start_mode_
= IMMEDIATE_START
;
319 // Bail out if the loading is not started or completed.
320 if (state_
!= INITIALIZED
)
323 // Once the loading is completed, the check-in will be initiated.
324 gcm_store_
->Load(base::Bind(&GCMClientImpl::OnLoadCompleted
,
325 weak_ptr_factory_
.GetWeakPtr()));
329 void GCMClientImpl::OnLoadCompleted(scoped_ptr
<GCMStore::LoadResult
> result
) {
330 DCHECK_EQ(LOADING
, state_
);
332 if (!result
->success
) {
336 gcm_store_reset_
= false;
338 registrations_
= result
->registrations
;
339 device_checkin_info_
.android_id
= result
->device_android_id
;
340 device_checkin_info_
.secret
= result
->device_security_token
;
341 device_checkin_info_
.last_checkin_accounts
= result
->last_checkin_accounts
;
342 // A case where there were previously no accounts reported with checkin is
343 // considered to be the same as when the list of accounts is empty. It enables
344 // scheduling a periodic checkin for devices with no signed in users
345 // immediately after restart, while keeping |accounts_set == false| delays the
346 // checkin until the list of accounts is set explicitly.
347 if (result
->last_checkin_accounts
.size() == 0)
348 device_checkin_info_
.accounts_set
= true;
349 last_checkin_time_
= result
->last_checkin_time
;
350 gservices_settings_
.UpdateFromLoadResult(*result
);
351 load_result_
= result
.Pass();
354 // Don't initiate the GCM connection when GCM is in delayed start mode and
355 // not any standalone app has registered GCM yet.
356 if (start_mode_
== DELAYED_START
&& !HasStandaloneRegisteredApp())
362 void GCMClientImpl::StartGCM() {
363 // Taking over the value of account_mappings before passing the ownership of
364 // load result to InitializeMCSClient.
365 std::vector
<AccountMapping
> account_mappings
;
366 account_mappings
.swap(load_result_
->account_mappings
);
367 base::Time last_token_fetch_time
= load_result_
->last_token_fetch_time
;
369 InitializeMCSClient();
371 if (device_checkin_info_
.IsValid()) {
372 SchedulePeriodicCheckin();
373 OnReady(account_mappings
, last_token_fetch_time
);
377 state_
= INITIAL_DEVICE_CHECKIN
;
378 device_checkin_info_
.Reset();
382 void GCMClientImpl::InitializeMCSClient() {
383 std::vector
<GURL
> endpoints
;
384 endpoints
.push_back(gservices_settings_
.GetMCSMainEndpoint());
385 endpoints
.push_back(gservices_settings_
.GetMCSFallbackEndpoint());
386 connection_factory_
= internals_builder_
->BuildConnectionFactory(
388 GetGCMBackoffPolicy(),
390 url_request_context_getter_
->GetURLRequestContext()
391 ->http_transaction_factory()
395 connection_factory_
->SetConnectionListener(this);
396 mcs_client_
= internals_builder_
->BuildMCSClient(
397 chrome_build_info_
.version
,
399 connection_factory_
.get(),
403 mcs_client_
->Initialize(
404 base::Bind(&GCMClientImpl::OnMCSError
, weak_ptr_factory_
.GetWeakPtr()),
405 base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS
,
406 weak_ptr_factory_
.GetWeakPtr()),
407 base::Bind(&GCMClientImpl::OnMessageSentToMCS
,
408 weak_ptr_factory_
.GetWeakPtr()),
409 load_result_
.Pass());
412 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
413 const CheckinInfo
& checkin_info
) {
414 DCHECK(!device_checkin_info_
.IsValid());
416 device_checkin_info_
.android_id
= checkin_info
.android_id
;
417 device_checkin_info_
.secret
= checkin_info
.secret
;
418 // If accounts were not set by now, we can consider them set (to empty list)
419 // to make sure periodic checkins get scheduled after initial checkin.
420 device_checkin_info_
.accounts_set
= true;
421 gcm_store_
->SetDeviceCredentials(
422 checkin_info
.android_id
, checkin_info
.secret
,
423 base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback
,
424 weak_ptr_factory_
.GetWeakPtr()));
426 OnReady(std::vector
<AccountMapping
>(), base::Time());
429 void GCMClientImpl::OnReady(const std::vector
<AccountMapping
>& account_mappings
,
430 const base::Time
& last_token_fetch_time
) {
434 delegate_
->OnGCMReady(account_mappings
, last_token_fetch_time
);
437 void GCMClientImpl::StartMCSLogin() {
438 DCHECK_EQ(READY
, state_
);
439 DCHECK(device_checkin_info_
.IsValid());
440 mcs_client_
->Login(device_checkin_info_
.android_id
,
441 device_checkin_info_
.secret
);
444 void GCMClientImpl::ResetStore() {
445 DCHECK_EQ(LOADING
, state_
);
447 // If already being reset, don't do it again. We want to prevent from
448 // resetting and loading from the store again and again.
449 if (gcm_store_reset_
) {
450 RecordResetStoreErrorToUMA(INFINITE_STORE_RESET
);
451 state_
= UNINITIALIZED
;
454 gcm_store_reset_
= true;
456 // Destroy the GCM store to start over.
457 gcm_store_
->Destroy(base::Bind(&GCMClientImpl::ResetStoreCallback
,
458 weak_ptr_factory_
.GetWeakPtr()));
461 void GCMClientImpl::SetAccountTokens(
462 const std::vector
<AccountTokenInfo
>& account_tokens
) {
463 device_checkin_info_
.account_tokens
.clear();
464 for (std::vector
<AccountTokenInfo
>::const_iterator iter
=
465 account_tokens
.begin();
466 iter
!= account_tokens
.end();
468 device_checkin_info_
.account_tokens
[iter
->email
] = iter
->access_token
;
471 bool accounts_set_before
= device_checkin_info_
.accounts_set
;
472 device_checkin_info_
.accounts_set
= true;
474 DVLOG(1) << "Set account called with: " << account_tokens
.size()
477 if (state_
!= READY
&& state_
!= INITIAL_DEVICE_CHECKIN
)
480 bool account_removed
= false;
481 for (std::set
<std::string
>::iterator iter
=
482 device_checkin_info_
.last_checkin_accounts
.begin();
483 iter
!= device_checkin_info_
.last_checkin_accounts
.end();
485 if (device_checkin_info_
.account_tokens
.find(*iter
) ==
486 device_checkin_info_
.account_tokens
.end()) {
487 account_removed
= true;
491 // Checkin will be forced when any of the accounts was removed during the
492 // current Chrome session or if there has been an account removed between the
493 // restarts of Chrome. If there is a checkin in progress, it will be canceled.
494 // We only force checkin when user signs out. When there is a new account
495 // signed in, the periodic checkin will take care of adding the association in
497 if (account_removed
) {
498 DVLOG(1) << "Detected that account has been removed. Forcing checkin.";
499 checkin_request_
.reset();
501 } else if (!accounts_set_before
) {
502 SchedulePeriodicCheckin();
503 DVLOG(1) << "Accounts set for the first time. Scheduled periodic checkin.";
507 void GCMClientImpl::UpdateAccountMapping(
508 const AccountMapping
& account_mapping
) {
509 gcm_store_
->AddAccountMapping(account_mapping
,
510 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
511 weak_ptr_factory_
.GetWeakPtr()));
514 void GCMClientImpl::RemoveAccountMapping(const std::string
& account_id
) {
515 gcm_store_
->RemoveAccountMapping(
517 base::Bind(&GCMClientImpl::DefaultStoreCallback
,
518 weak_ptr_factory_
.GetWeakPtr()));
521 void GCMClientImpl::SetLastTokenFetchTime(const base::Time
& time
) {
522 gcm_store_
->SetLastTokenFetchTime(
524 base::Bind(&GCMClientImpl::IgnoreWriteResultCallback
,
525 weak_ptr_factory_
.GetWeakPtr()));
528 void GCMClientImpl::UpdateHeartbeatTimer(scoped_ptr
<base::Timer
> timer
) {
530 mcs_client_
->UpdateHeartbeatTimer(timer
.Pass());
533 void GCMClientImpl::StartCheckin() {
534 // Make sure no checkin is in progress.
535 if (checkin_request_
.get())
538 checkin_proto::ChromeBuildProto chrome_build_proto
;
539 ToCheckinProtoVersion(chrome_build_info_
, &chrome_build_proto
);
540 CheckinRequest::RequestInfo
request_info(device_checkin_info_
.android_id
,
541 device_checkin_info_
.secret
,
542 device_checkin_info_
.account_tokens
,
543 gservices_settings_
.digest(),
545 checkin_request_
.reset(
546 new CheckinRequest(gservices_settings_
.GetCheckinURL(),
548 GetGCMBackoffPolicy(),
549 base::Bind(&GCMClientImpl::OnCheckinCompleted
,
550 weak_ptr_factory_
.GetWeakPtr()),
551 url_request_context_getter_
.get(),
553 // Taking a snapshot of the accounts count here, as there might be an asynch
554 // update of the account tokens while checkin is in progress.
555 device_checkin_info_
.SnapshotCheckinAccounts();
556 checkin_request_
->Start();
559 void GCMClientImpl::OnCheckinCompleted(
560 const checkin_proto::AndroidCheckinResponse
& checkin_response
) {
561 checkin_request_
.reset();
563 if (!checkin_response
.has_android_id() ||
564 !checkin_response
.has_security_token()) {
565 // TODO(fgorski): I don't think a retry here will help, we should probably
566 // start over. By checking in with (0, 0).
570 CheckinInfo checkin_info
;
571 checkin_info
.android_id
= checkin_response
.android_id();
572 checkin_info
.secret
= checkin_response
.security_token();
574 if (state_
== INITIAL_DEVICE_CHECKIN
) {
575 OnFirstTimeDeviceCheckinCompleted(checkin_info
);
577 // checkin_info is not expected to change after a periodic checkin as it
578 // would invalidate the registratoin IDs.
579 DCHECK_EQ(READY
, state_
);
580 DCHECK_EQ(device_checkin_info_
.android_id
, checkin_info
.android_id
);
581 DCHECK_EQ(device_checkin_info_
.secret
, checkin_info
.secret
);
584 if (device_checkin_info_
.IsValid()) {
585 // First update G-services settings, as something might have changed.
586 if (gservices_settings_
.UpdateFromCheckinResponse(checkin_response
)) {
587 gcm_store_
->SetGServicesSettings(
588 gservices_settings_
.settings_map(),
589 gservices_settings_
.digest(),
590 base::Bind(&GCMClientImpl::SetGServicesSettingsCallback
,
591 weak_ptr_factory_
.GetWeakPtr()));
594 last_checkin_time_
= clock_
->Now();
595 gcm_store_
->SetLastCheckinInfo(
597 device_checkin_info_
.last_checkin_accounts
,
598 base::Bind(&GCMClientImpl::SetLastCheckinInfoCallback
,
599 weak_ptr_factory_
.GetWeakPtr()));
600 SchedulePeriodicCheckin();
604 void GCMClientImpl::SetGServicesSettingsCallback(bool success
) {
608 void GCMClientImpl::SchedulePeriodicCheckin() {
609 // Make sure no checkin is in progress.
610 if (checkin_request_
.get() || !device_checkin_info_
.accounts_set
)
613 // There should be only one periodic checkin pending at a time. Removing
614 // pending periodic checkin to schedule a new one.
615 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
617 base::TimeDelta time_to_next_checkin
= GetTimeToNextCheckin();
618 if (time_to_next_checkin
< base::TimeDelta())
619 time_to_next_checkin
= base::TimeDelta();
621 base::MessageLoop::current()->PostDelayedTask(
623 base::Bind(&GCMClientImpl::StartCheckin
,
624 periodic_checkin_ptr_factory_
.GetWeakPtr()),
625 time_to_next_checkin
);
628 base::TimeDelta
GCMClientImpl::GetTimeToNextCheckin() const {
629 return last_checkin_time_
+ gservices_settings_
.GetCheckinInterval() -
633 void GCMClientImpl::SetLastCheckinInfoCallback(bool success
) {
634 // TODO(fgorski): This is one of the signals that store needs a rebuild.
638 void GCMClientImpl::SetDeviceCredentialsCallback(bool success
) {
639 // TODO(fgorski): This is one of the signals that store needs a rebuild.
643 void GCMClientImpl::UpdateRegistrationCallback(bool success
) {
644 // TODO(fgorski): This is one of the signals that store needs a rebuild.
648 void GCMClientImpl::DefaultStoreCallback(bool success
) {
652 void GCMClientImpl::IgnoreWriteResultCallback(bool success
) {
653 // TODO(fgorski): Ignoring the write result for now to make sure
654 // sync_intergration_tests are not broken.
657 void GCMClientImpl::ResetStoreCallback(bool success
) {
659 LOG(ERROR
) << "Failed to reset GCM store";
660 RecordResetStoreErrorToUMA(DESTROYING_STORE_FAILED
);
661 state_
= UNINITIALIZED
;
665 state_
= INITIALIZED
;
669 void GCMClientImpl::Stop() {
670 // TODO(fgorski): Perhaps we should make a distinction between a Stop and a
672 DVLOG(1) << "Stopping the GCM Client";
673 weak_ptr_factory_
.InvalidateWeakPtrs();
674 periodic_checkin_ptr_factory_
.InvalidateWeakPtrs();
675 device_checkin_info_
.Reset();
676 connection_factory_
.reset();
677 delegate_
->OnDisconnected();
679 checkin_request_
.reset();
680 // Delete all of the pending registration and unregistration requests.
681 STLDeleteValues(&pending_registration_requests_
);
682 STLDeleteValues(&pending_unregistration_requests_
);
683 state_
= INITIALIZED
;
687 void GCMClientImpl::Register(const std::string
& app_id
,
688 const std::vector
<std::string
>& sender_ids
) {
689 DCHECK_EQ(state_
, READY
);
691 // If the same sender ids is provided, return the cached registration ID
693 RegistrationInfoMap::const_iterator registrations_iter
=
694 registrations_
.find(app_id
);
695 if (registrations_iter
!= registrations_
.end() &&
696 registrations_iter
->second
->sender_ids
== sender_ids
) {
697 delegate_
->OnRegisterFinished(
698 app_id
, registrations_iter
->second
->registration_id
, SUCCESS
);
702 RegistrationRequest::RequestInfo
request_info(
703 device_checkin_info_
.android_id
,
704 device_checkin_info_
.secret
,
707 DCHECK_EQ(0u, pending_registration_requests_
.count(app_id
));
709 RegistrationRequest
* registration_request
=
710 new RegistrationRequest(gservices_settings_
.GetRegistrationURL(),
712 GetGCMBackoffPolicy(),
713 base::Bind(&GCMClientImpl::OnRegisterCompleted
,
714 weak_ptr_factory_
.GetWeakPtr(),
717 kMaxRegistrationRetries
,
718 url_request_context_getter_
,
720 pending_registration_requests_
[app_id
] = registration_request
;
721 registration_request
->Start();
724 void GCMClientImpl::OnRegisterCompleted(
725 const std::string
& app_id
,
726 const std::vector
<std::string
>& sender_ids
,
727 RegistrationRequest::Status status
,
728 const std::string
& registration_id
) {
732 PendingRegistrationRequests::iterator iter
=
733 pending_registration_requests_
.find(app_id
);
734 if (iter
== pending_registration_requests_
.end())
735 result
= UNKNOWN_ERROR
;
736 else if (status
== RegistrationRequest::INVALID_SENDER
)
737 result
= INVALID_PARAMETER
;
738 else if (registration_id
.empty())
739 result
= SERVER_ERROR
;
743 if (result
== SUCCESS
) {
745 linked_ptr
<RegistrationInfo
> registration(new RegistrationInfo
);
746 registration
->sender_ids
= sender_ids
;
747 registration
->registration_id
= registration_id
;
748 registrations_
[app_id
] = registration
;
750 // Save it in the persistent store.
751 gcm_store_
->AddRegistration(
754 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
755 weak_ptr_factory_
.GetWeakPtr()));
758 delegate_
->OnRegisterFinished(
759 app_id
, result
== SUCCESS
? registration_id
: std::string(), result
);
761 if (iter
!= pending_registration_requests_
.end()) {
763 pending_registration_requests_
.erase(iter
);
767 void GCMClientImpl::Unregister(const std::string
& app_id
) {
768 DCHECK_EQ(state_
, READY
);
769 if (pending_unregistration_requests_
.count(app_id
) == 1)
772 // Remove from the cache and persistent store.
773 registrations_
.erase(app_id
);
774 gcm_store_
->RemoveRegistration(
776 base::Bind(&GCMClientImpl::UpdateRegistrationCallback
,
777 weak_ptr_factory_
.GetWeakPtr()));
779 UnregistrationRequest::RequestInfo
request_info(
780 device_checkin_info_
.android_id
,
781 device_checkin_info_
.secret
,
784 UnregistrationRequest
* unregistration_request
= new UnregistrationRequest(
785 gservices_settings_
.GetRegistrationURL(),
787 GetGCMBackoffPolicy(),
788 base::Bind(&GCMClientImpl::OnUnregisterCompleted
,
789 weak_ptr_factory_
.GetWeakPtr(),
791 url_request_context_getter_
,
793 pending_unregistration_requests_
[app_id
] = unregistration_request
;
794 unregistration_request
->Start();
797 void GCMClientImpl::OnUnregisterCompleted(
798 const std::string
& app_id
,
799 UnregistrationRequest::Status status
) {
800 DVLOG(1) << "Unregister completed for app: " << app_id
801 << " with " << (status
? "success." : "failure.");
802 delegate_
->OnUnregisterFinished(
804 status
== UnregistrationRequest::SUCCESS
? SUCCESS
: SERVER_ERROR
);
806 PendingUnregistrationRequests::iterator iter
=
807 pending_unregistration_requests_
.find(app_id
);
808 if (iter
== pending_unregistration_requests_
.end())
812 pending_unregistration_requests_
.erase(iter
);
815 void GCMClientImpl::OnGCMStoreDestroyed(bool success
) {
816 DLOG_IF(ERROR
, !success
) << "GCM store failed to be destroyed!";
817 UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success
);
820 void GCMClientImpl::Send(const std::string
& app_id
,
821 const std::string
& receiver_id
,
822 const OutgoingMessage
& message
) {
823 DCHECK_EQ(state_
, READY
);
825 RecordOutgoingMessageToUMA(message
);
827 mcs_proto::DataMessageStanza stanza
;
828 stanza
.set_ttl(message
.time_to_live
);
829 stanza
.set_sent(clock_
->Now().ToInternalValue() /
830 base::Time::kMicrosecondsPerSecond
);
831 stanza
.set_id(message
.id
);
832 stanza
.set_from(kSendMessageFromValue
);
833 stanza
.set_to(receiver_id
);
834 stanza
.set_category(app_id
);
836 for (MessageData::const_iterator iter
= message
.data
.begin();
837 iter
!= message
.data
.end();
839 mcs_proto::AppData
* app_data
= stanza
.add_app_data();
840 app_data
->set_key(iter
->first
);
841 app_data
->set_value(iter
->second
);
844 MCSMessage
mcs_message(stanza
);
845 DVLOG(1) << "MCS message size: " << mcs_message
.size();
846 mcs_client_
->SendMessage(mcs_message
);
849 std::string
GCMClientImpl::GetStateString() const {
851 case GCMClientImpl::INITIALIZED
:
852 return "INITIALIZED";
853 case GCMClientImpl::UNINITIALIZED
:
854 return "UNINITIALIZED";
855 case GCMClientImpl::LOADING
:
857 case GCMClientImpl::LOADED
:
859 case GCMClientImpl::INITIAL_DEVICE_CHECKIN
:
860 return "INITIAL_DEVICE_CHECKIN";
861 case GCMClientImpl::READY
:
865 return std::string();
869 void GCMClientImpl::SetRecording(bool recording
) {
870 recorder_
.SetRecording(recording
);
873 void GCMClientImpl::ClearActivityLogs() {
877 GCMClient::GCMStatistics
GCMClientImpl::GetStatistics() const {
878 GCMClient::GCMStatistics stats
;
879 stats
.gcm_client_created
= true;
880 stats
.is_recording
= recorder_
.is_recording();
881 stats
.gcm_client_state
= GetStateString();
882 stats
.connection_client_created
= mcs_client_
.get() != NULL
;
883 if (connection_factory_
.get())
884 stats
.connection_state
= connection_factory_
->GetConnectionStateString();
885 if (mcs_client_
.get()) {
886 stats
.send_queue_size
= mcs_client_
->GetSendQueueSize();
887 stats
.resend_queue_size
= mcs_client_
->GetResendQueueSize();
889 if (device_checkin_info_
.android_id
> 0)
890 stats
.android_id
= device_checkin_info_
.android_id
;
891 recorder_
.CollectActivities(&stats
.recorded_activities
);
893 for (RegistrationInfoMap::const_iterator it
= registrations_
.begin();
894 it
!= registrations_
.end(); ++it
) {
895 stats
.registered_app_ids
.push_back(it
->first
);
900 void GCMClientImpl::OnActivityRecorded() {
901 delegate_
->OnActivityRecorded();
904 void GCMClientImpl::OnConnected(const GURL
& current_server
,
905 const net::IPEndPoint
& ip_endpoint
) {
906 // TODO(gcm): expose current server in debug page.
907 delegate_
->OnActivityRecorded();
908 delegate_
->OnConnected(ip_endpoint
);
911 void GCMClientImpl::OnDisconnected() {
912 delegate_
->OnActivityRecorded();
913 delegate_
->OnDisconnected();
916 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage
& message
) {
917 switch (message
.tag()) {
918 case kLoginResponseTag
:
919 DVLOG(1) << "Login response received by GCM Client. Ignoring.";
921 case kDataMessageStanzaTag
:
922 DVLOG(1) << "A downstream message received. Processing...";
923 HandleIncomingMessage(message
);
926 NOTREACHED() << "Message with unexpected tag received by GCMClient";
931 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number
,
932 const std::string
& app_id
,
933 const std::string
& message_id
,
934 MCSClient::MessageSendStatus status
) {
935 DCHECK_EQ(user_serial_number
, kDefaultUserSerialNumber
);
938 // TTL_EXCEEDED is singled out here, because it can happen long time after the
939 // message was sent. That is why it comes as |OnMessageSendError| event rather
940 // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
941 // All other errors will be raised immediately, through asynchronous callback.
942 // It is expected that TTL_EXCEEDED will be issued for a message that was
943 // previously issued |OnSendFinished| with status SUCCESS.
944 // TODO(jianli): Consider adding UMA for this status.
945 if (status
== MCSClient::TTL_EXCEEDED
) {
946 SendErrorDetails send_error_details
;
947 send_error_details
.message_id
= message_id
;
948 send_error_details
.result
= GCMClient::TTL_EXCEEDED
;
949 delegate_
->OnMessageSendError(app_id
, send_error_details
);
950 } else if (status
== MCSClient::SENT
) {
951 delegate_
->OnSendAcknowledged(app_id
, message_id
);
953 delegate_
->OnSendFinished(app_id
, message_id
, ToGCMClientResult(status
));
957 void GCMClientImpl::OnMCSError() {
958 // TODO(fgorski): For now it replaces the initialization method. Long term it
959 // should have an error or status passed in.
962 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage
& message
) {
965 const mcs_proto::DataMessageStanza
& data_message_stanza
=
966 reinterpret_cast<const mcs_proto::DataMessageStanza
&>(
967 message
.GetProtobuf());
968 DCHECK_EQ(data_message_stanza
.device_user_id(), kDefaultUserSerialNumber
);
970 // Copying all the data from the stanza to a MessageData object. When present,
971 // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
973 MessageData message_data
;
974 for (int i
= 0; i
< data_message_stanza
.app_data_size(); ++i
) {
975 std::string key
= data_message_stanza
.app_data(i
).key();
976 message_data
[key
] = data_message_stanza
.app_data(i
).value();
979 MessageType message_type
= DATA_MESSAGE
;
980 MessageData::iterator iter
= message_data
.find(kMessageTypeKey
);
981 if (iter
!= message_data
.end()) {
982 message_type
= DecodeMessageType(iter
->second
);
983 message_data
.erase(iter
);
986 switch (message_type
) {
988 HandleIncomingDataMessage(data_message_stanza
, message_data
);
990 case DELETED_MESSAGES
:
991 recorder_
.RecordDataMessageReceived(data_message_stanza
.category(),
992 data_message_stanza
.from(),
993 data_message_stanza
.ByteSize(),
995 GCMStatsRecorder::DELETED_MESSAGES
);
996 delegate_
->OnMessagesDeleted(data_message_stanza
.category());
999 HandleIncomingSendError(data_message_stanza
, message_data
);
1002 default: // Treat default the same as UNKNOWN.
1003 DVLOG(1) << "Unknown message_type received. Message ignored. "
1004 << "App ID: " << data_message_stanza
.category() << ".";
1009 void GCMClientImpl::HandleIncomingDataMessage(
1010 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1011 MessageData
& message_data
) {
1012 std::string app_id
= data_message_stanza
.category();
1014 // Drop the message when the app is not registered for the sender of the
1016 RegistrationInfoMap::iterator iter
= registrations_
.find(app_id
);
1017 bool not_registered
=
1018 iter
== registrations_
.end() ||
1019 std::find(iter
->second
->sender_ids
.begin(),
1020 iter
->second
->sender_ids
.end(),
1021 data_message_stanza
.from()) == iter
->second
->sender_ids
.end();
1022 recorder_
.RecordDataMessageReceived(app_id
, data_message_stanza
.from(),
1023 data_message_stanza
.ByteSize(), !not_registered
,
1024 GCMStatsRecorder::DATA_MESSAGE
);
1025 if (not_registered
) {
1029 IncomingMessage incoming_message
;
1030 incoming_message
.sender_id
= data_message_stanza
.from();
1031 if (data_message_stanza
.has_token())
1032 incoming_message
.collapse_key
= data_message_stanza
.token();
1033 incoming_message
.data
= message_data
;
1034 delegate_
->OnMessageReceived(app_id
, incoming_message
);
1037 void GCMClientImpl::HandleIncomingSendError(
1038 const mcs_proto::DataMessageStanza
& data_message_stanza
,
1039 MessageData
& message_data
) {
1040 SendErrorDetails send_error_details
;
1041 send_error_details
.additional_data
= message_data
;
1042 send_error_details
.result
= SERVER_ERROR
;
1044 MessageData::iterator iter
=
1045 send_error_details
.additional_data
.find(kSendErrorMessageIdKey
);
1046 if (iter
!= send_error_details
.additional_data
.end()) {
1047 send_error_details
.message_id
= iter
->second
;
1048 send_error_details
.additional_data
.erase(iter
);
1051 recorder_
.RecordIncomingSendError(
1052 data_message_stanza
.category(),
1053 data_message_stanza
.to(),
1054 data_message_stanza
.id());
1055 delegate_
->OnMessageSendError(data_message_stanza
.category(),
1056 send_error_details
);
1059 bool GCMClientImpl::HasStandaloneRegisteredApp() const {
1060 if (registrations_
.empty())
1062 // Note that account mapper is not counted as a standalone app since it is
1063 // automatically started when other app uses GCM.
1064 return registrations_
.size() > 1 ||
1065 !registrations_
.count(kGCMAccountMapperAppId
);