• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "components/gcm_driver/gcm_client_impl.h"
6 
7 #include "base/bind.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/strings/string_number_conversions.h"
15 #include "base/strings/stringprintf.h"
16 #include "base/time/default_clock.h"
17 #include "google_apis/gcm/base/encryptor.h"
18 #include "google_apis/gcm/base/mcs_message.h"
19 #include "google_apis/gcm/base/mcs_util.h"
20 #include "google_apis/gcm/engine/checkin_request.h"
21 #include "google_apis/gcm/engine/connection_factory_impl.h"
22 #include "google_apis/gcm/engine/gcm_store_impl.h"
23 #include "google_apis/gcm/monitoring/gcm_stats_recorder.h"
24 #include "google_apis/gcm/protocol/checkin.pb.h"
25 #include "google_apis/gcm/protocol/mcs.pb.h"
26 #include "net/http/http_network_session.h"
27 #include "net/url_request/url_request_context.h"
28 #include "url/gurl.h"
29 
30 namespace gcm {
31 
32 namespace {
33 
34 // Backoff policy. Shared across reconnection logic and checkin/(un)registration
35 // retries.
36 // Note: In order to ensure a minimum of 20 seconds between server errors (for
37 // server reasons), we have a 30s +- 10s (33%) jitter initial backoff.
38 // TODO(zea): consider sharing/synchronizing the scheduling of backoff retries
39 // themselves.
40 const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
41   // Number of initial errors (in sequence) to ignore before applying
42   // exponential back-off rules.
43   0,
44 
45   // Initial delay for exponential back-off in ms.
46   30 * 1000,  // 30 seconds.
47 
48   // Factor by which the waiting time will be multiplied.
49   2,
50 
51   // Fuzzing percentage. ex: 10% will spread requests randomly
52   // between 90%-100% of the calculated time.
53   0.33,  // 33%.
54 
55   // Maximum amount of time we are willing to delay our request in ms.
56   10 * 60 * 1000, // 10 minutes.
57 
58   // Time to keep an entry from being discarded even when it
59   // has no significant state, -1 to never discard.
60   -1,
61 
62   // Don't use initial delay unless the last request was an error.
63   false,
64 };
65 
66 // Indicates a message type of the received message.
67 enum MessageType {
68   UNKNOWN,           // Undetermined type.
69   DATA_MESSAGE,      // Regular data message.
70   DELETED_MESSAGES,  // Messages were deleted on the server.
71   SEND_ERROR,        // Error sending a message.
72 };
73 
74 enum OutgoingMessageTTLCategory {
75   TTL_ZERO,
76   TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE,
77   TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR,
78   TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY,
79   TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK,
80   TTL_MORE_THAN_ONE_WEEK,
81   TTL_MAXIMUM,
82   // NOTE: always keep this entry at the end. Add new TTL category only
83   // immediately above this line. Make sure to update the corresponding
84   // histogram enum accordingly.
85   TTL_CATEGORY_COUNT
86 };
87 
88 const int kMaxRegistrationRetries = 5;
89 const char kMessageTypeDataMessage[] = "gcm";
90 const char kMessageTypeDeletedMessagesKey[] = "deleted_messages";
91 const char kMessageTypeKey[] = "message_type";
92 const char kMessageTypeSendErrorKey[] = "send_error";
93 const char kSendErrorMessageIdKey[] = "google.message_id";
94 const char kSendMessageFromValue[] = "gcm@chrome.com";
95 const int64 kDefaultUserSerialNumber = 0LL;
96 
ToGCMClientResult(MCSClient::MessageSendStatus status)97 GCMClient::Result ToGCMClientResult(MCSClient::MessageSendStatus status) {
98   switch (status) {
99     case MCSClient::QUEUED:
100       return GCMClient::SUCCESS;
101     case MCSClient::QUEUE_SIZE_LIMIT_REACHED:
102       return GCMClient::NETWORK_ERROR;
103     case MCSClient::APP_QUEUE_SIZE_LIMIT_REACHED:
104       return GCMClient::NETWORK_ERROR;
105     case MCSClient::MESSAGE_TOO_LARGE:
106       return GCMClient::INVALID_PARAMETER;
107     case MCSClient::NO_CONNECTION_ON_ZERO_TTL:
108       return GCMClient::NETWORK_ERROR;
109     case MCSClient::TTL_EXCEEDED:
110       return GCMClient::NETWORK_ERROR;
111     case MCSClient::SENT:
112     default:
113       NOTREACHED();
114       break;
115   }
116   return GCMClientImpl::UNKNOWN_ERROR;
117 }
118 
ToCheckinProtoVersion(const GCMClient::ChromeBuildInfo & chrome_build_info,checkin_proto::ChromeBuildProto * android_build_info)119 void ToCheckinProtoVersion(
120     const GCMClient::ChromeBuildInfo& chrome_build_info,
121     checkin_proto::ChromeBuildProto* android_build_info) {
122   checkin_proto::ChromeBuildProto_Platform platform;
123   switch (chrome_build_info.platform) {
124     case GCMClient::PLATFORM_WIN:
125       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
126       break;
127     case GCMClient::PLATFORM_MAC:
128       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
129       break;
130     case GCMClient::PLATFORM_LINUX:
131       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
132       break;
133     case GCMClient::PLATFORM_IOS:
134       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
135       break;
136     case GCMClient::PLATFORM_ANDROID:
137       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_ANDROID;
138       break;
139     case GCMClient::PLATFORM_CROS:
140       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
141       break;
142     case GCMClient::PLATFORM_UNKNOWN:
143       // For unknown platform, return as LINUX.
144       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
145       break;
146     default:
147       NOTREACHED();
148       platform = checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
149       break;
150   }
151   android_build_info->set_platform(platform);
152 
153   checkin_proto::ChromeBuildProto_Channel channel;
154   switch (chrome_build_info.channel) {
155     case GCMClient::CHANNEL_STABLE:
156       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
157       break;
158     case GCMClient::CHANNEL_BETA:
159       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
160       break;
161     case GCMClient::CHANNEL_DEV:
162       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
163       break;
164     case GCMClient::CHANNEL_CANARY:
165       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
166       break;
167     case GCMClient::CHANNEL_UNKNOWN:
168       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
169       break;
170     default:
171       NOTREACHED();
172       channel = checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
173       break;
174   }
175   android_build_info->set_channel(channel);
176 
177   android_build_info->set_chrome_version(chrome_build_info.version);
178 }
179 
DecodeMessageType(const std::string & value)180 MessageType DecodeMessageType(const std::string& value) {
181   if (kMessageTypeDeletedMessagesKey == value)
182     return DELETED_MESSAGES;
183   if (kMessageTypeSendErrorKey == value)
184     return SEND_ERROR;
185   if (kMessageTypeDataMessage == value)
186     return DATA_MESSAGE;
187   return UNKNOWN;
188 }
189 
RecordOutgoingMessageToUMA(const gcm::GCMClient::OutgoingMessage & message)190 void RecordOutgoingMessageToUMA(
191     const gcm::GCMClient::OutgoingMessage& message) {
192   OutgoingMessageTTLCategory ttl_category;
193   if (message.time_to_live == 0)
194     ttl_category = TTL_ZERO;
195   else if (message.time_to_live <= 60 )
196     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_MINUTE;
197   else if (message.time_to_live <= 60 * 60)
198     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_HOUR;
199   else if (message.time_to_live <= 24 * 60 * 60)
200     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_DAY;
201   else if (message.time_to_live <= 7 * 24 * 60 * 60)
202     ttl_category = TTL_LESS_THAN_OR_EQUAL_TO_ONE_WEEK;
203   else if (message.time_to_live < gcm::GCMClient::OutgoingMessage::kMaximumTTL)
204     ttl_category = TTL_MORE_THAN_ONE_WEEK;
205   else
206     ttl_category = TTL_MAXIMUM;
207 
208   UMA_HISTOGRAM_ENUMERATION("GCM.GCMOutgoingMessageTTLCategory",
209                             ttl_category,
210                             TTL_CATEGORY_COUNT);
211 }
212 
213 }  // namespace
214 
GCMInternalsBuilder()215 GCMInternalsBuilder::GCMInternalsBuilder() {}
~GCMInternalsBuilder()216 GCMInternalsBuilder::~GCMInternalsBuilder() {}
217 
BuildClock()218 scoped_ptr<base::Clock> GCMInternalsBuilder::BuildClock() {
219   return make_scoped_ptr<base::Clock>(new base::DefaultClock());
220 }
221 
BuildMCSClient(const std::string & version,base::Clock * clock,ConnectionFactory * connection_factory,GCMStore * gcm_store,GCMStatsRecorder * recorder)222 scoped_ptr<MCSClient> GCMInternalsBuilder::BuildMCSClient(
223     const std::string& version,
224     base::Clock* clock,
225     ConnectionFactory* connection_factory,
226     GCMStore* gcm_store,
227     GCMStatsRecorder* recorder) {
228   return make_scoped_ptr<MCSClient>(
229       new MCSClient(version,
230                     clock,
231                     connection_factory,
232                     gcm_store,
233                     recorder));
234 }
235 
BuildConnectionFactory(const std::vector<GURL> & endpoints,const net::BackoffEntry::Policy & backoff_policy,scoped_refptr<net::HttpNetworkSession> network_session,net::NetLog * net_log,GCMStatsRecorder * recorder)236 scoped_ptr<ConnectionFactory> GCMInternalsBuilder::BuildConnectionFactory(
237       const std::vector<GURL>& endpoints,
238       const net::BackoffEntry::Policy& backoff_policy,
239       scoped_refptr<net::HttpNetworkSession> network_session,
240       net::NetLog* net_log,
241       GCMStatsRecorder* recorder) {
242   return make_scoped_ptr<ConnectionFactory>(
243       new ConnectionFactoryImpl(endpoints,
244                                 backoff_policy,
245                                 network_session,
246                                 net_log,
247                                 recorder));
248 }
249 
GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)250 GCMClientImpl::GCMClientImpl(scoped_ptr<GCMInternalsBuilder> internals_builder)
251     : internals_builder_(internals_builder.Pass()),
252       state_(UNINITIALIZED),
253       delegate_(NULL),
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) {
261 }
262 
~GCMClientImpl()263 GCMClientImpl::~GCMClientImpl() {
264 }
265 
Initialize(const ChromeBuildInfo & chrome_build_info,const base::FilePath & path,const scoped_refptr<base::SequencedTaskRunner> & blocking_task_runner,const scoped_refptr<net::URLRequestContextGetter> & url_request_context_getter,scoped_ptr<Encryptor> encryptor,GCMClient::Delegate * delegate)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);
276   DCHECK(delegate);
277 
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);
284 
285   chrome_build_info_ = chrome_build_info;
286 
287   gcm_store_.reset(
288       new GCMStoreImpl(path, blocking_task_runner, encryptor.Pass()));
289 
290   delegate_ = delegate;
291 
292   recorder_.SetDelegate(this);
293 
294   state_ = INITIALIZED;
295 }
296 
Start()297 void GCMClientImpl::Start() {
298   DCHECK_EQ(INITIALIZED, state_);
299 
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()));
303   state_ = LOADING;
304 }
305 
OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result)306 void GCMClientImpl::OnLoadCompleted(scoped_ptr<GCMStore::LoadResult> result) {
307   DCHECK_EQ(LOADING, state_);
308 
309   if (!result->success) {
310     ResetState();
311     return;
312   }
313 
314   registrations_ = result->registrations;
315   device_checkin_info_.android_id = result->device_android_id;
316   device_checkin_info_.secret = result->device_security_token;
317   last_checkin_time_ = result->last_checkin_time;
318   gservices_settings_.UpdateFromLoadResult(*result);
319   InitializeMCSClient(result.Pass());
320 
321   if (device_checkin_info_.IsValid()) {
322     SchedulePeriodicCheckin();
323     OnReady();
324     return;
325   }
326 
327   state_ = INITIAL_DEVICE_CHECKIN;
328   device_checkin_info_.Reset();
329   StartCheckin();
330 }
331 
InitializeMCSClient(scoped_ptr<GCMStore::LoadResult> result)332 void GCMClientImpl::InitializeMCSClient(
333     scoped_ptr<GCMStore::LoadResult> result) {
334   std::vector<GURL> endpoints;
335   endpoints.push_back(gservices_settings_.GetMCSMainEndpoint());
336   endpoints.push_back(gservices_settings_.GetMCSFallbackEndpoint());
337   connection_factory_ = internals_builder_->BuildConnectionFactory(
338       endpoints,
339       kDefaultBackoffPolicy,
340       network_session_,
341       net_log_.net_log(),
342       &recorder_);
343   connection_factory_->SetConnectionListener(this);
344   mcs_client_ = internals_builder_->BuildMCSClient(
345       chrome_build_info_.version,
346       clock_.get(),
347       connection_factory_.get(),
348       gcm_store_.get(),
349       &recorder_).Pass();
350 
351   mcs_client_->Initialize(
352       base::Bind(&GCMClientImpl::OnMCSError, weak_ptr_factory_.GetWeakPtr()),
353       base::Bind(&GCMClientImpl::OnMessageReceivedFromMCS,
354                  weak_ptr_factory_.GetWeakPtr()),
355       base::Bind(&GCMClientImpl::OnMessageSentToMCS,
356                  weak_ptr_factory_.GetWeakPtr()),
357       result.Pass());
358 }
359 
OnFirstTimeDeviceCheckinCompleted(const CheckinInfo & checkin_info)360 void GCMClientImpl::OnFirstTimeDeviceCheckinCompleted(
361     const CheckinInfo& checkin_info) {
362   DCHECK(!device_checkin_info_.IsValid());
363 
364   device_checkin_info_.android_id = checkin_info.android_id;
365   device_checkin_info_.secret = checkin_info.secret;
366   gcm_store_->SetDeviceCredentials(
367       checkin_info.android_id, checkin_info.secret,
368       base::Bind(&GCMClientImpl::SetDeviceCredentialsCallback,
369                  weak_ptr_factory_.GetWeakPtr()));
370 
371   OnReady();
372 }
373 
OnReady()374 void GCMClientImpl::OnReady() {
375   state_ = READY;
376   StartMCSLogin();
377 
378   delegate_->OnGCMReady();
379 }
380 
StartMCSLogin()381 void GCMClientImpl::StartMCSLogin() {
382   DCHECK_EQ(READY, state_);
383   DCHECK(device_checkin_info_.IsValid());
384   mcs_client_->Login(device_checkin_info_.android_id,
385                      device_checkin_info_.secret);
386 }
387 
ResetState()388 void GCMClientImpl::ResetState() {
389   state_ = UNINITIALIZED;
390   // TODO(fgorski): reset all of the necessart objects and start over.
391 }
392 
StartCheckin()393 void GCMClientImpl::StartCheckin() {
394   // Make sure no checkin is in progress.
395   if (checkin_request_.get())
396     return;
397 
398   checkin_proto::ChromeBuildProto chrome_build_proto;
399   ToCheckinProtoVersion(chrome_build_info_, &chrome_build_proto);
400   CheckinRequest::RequestInfo request_info(device_checkin_info_.android_id,
401                                            device_checkin_info_.secret,
402                                            gservices_settings_.digest(),
403                                            chrome_build_proto);
404   checkin_request_.reset(
405       new CheckinRequest(gservices_settings_.GetCheckinURL(),
406                          request_info,
407                          kDefaultBackoffPolicy,
408                          base::Bind(&GCMClientImpl::OnCheckinCompleted,
409                                     weak_ptr_factory_.GetWeakPtr()),
410                          url_request_context_getter_,
411                          &recorder_));
412   checkin_request_->Start();
413 }
414 
OnCheckinCompleted(const checkin_proto::AndroidCheckinResponse & checkin_response)415 void GCMClientImpl::OnCheckinCompleted(
416     const checkin_proto::AndroidCheckinResponse& checkin_response) {
417   checkin_request_.reset();
418 
419   if (!checkin_response.has_android_id() ||
420       !checkin_response.has_security_token()) {
421     // TODO(fgorski): I don't think a retry here will help, we should probably
422     // start over. By checking in with (0, 0).
423     return;
424   }
425 
426   CheckinInfo checkin_info;
427   checkin_info.android_id = checkin_response.android_id();
428   checkin_info.secret = checkin_response.security_token();
429 
430   if (state_ == INITIAL_DEVICE_CHECKIN) {
431     OnFirstTimeDeviceCheckinCompleted(checkin_info);
432   } else {
433     // checkin_info is not expected to change after a periodic checkin as it
434     // would invalidate the registratoin IDs.
435     DCHECK_EQ(READY, state_);
436     DCHECK_EQ(device_checkin_info_.android_id, checkin_info.android_id);
437     DCHECK_EQ(device_checkin_info_.secret, checkin_info.secret);
438   }
439 
440   if (device_checkin_info_.IsValid()) {
441     // First update G-services settings, as something might have changed.
442     if (gservices_settings_.UpdateFromCheckinResponse(checkin_response)) {
443       gcm_store_->SetGServicesSettings(
444           gservices_settings_.settings_map(),
445           gservices_settings_.digest(),
446           base::Bind(&GCMClientImpl::SetGServicesSettingsCallback,
447                      weak_ptr_factory_.GetWeakPtr()));
448     }
449 
450     last_checkin_time_ = clock_->Now();
451     gcm_store_->SetLastCheckinTime(
452         last_checkin_time_,
453         base::Bind(&GCMClientImpl::SetLastCheckinTimeCallback,
454                    weak_ptr_factory_.GetWeakPtr()));
455     SchedulePeriodicCheckin();
456   }
457 }
458 
SetGServicesSettingsCallback(bool success)459 void GCMClientImpl::SetGServicesSettingsCallback(bool success) {
460   DCHECK(success);
461 }
462 
SchedulePeriodicCheckin()463 void GCMClientImpl::SchedulePeriodicCheckin() {
464   // Make sure no checkin is in progress.
465   if (checkin_request_.get())
466     return;
467 
468   // There should be only one periodic checkin pending at a time. Removing
469   // pending periodic checkin to schedule a new one.
470   periodic_checkin_ptr_factory_.InvalidateWeakPtrs();
471 
472   base::TimeDelta time_to_next_checkin = GetTimeToNextCheckin();
473   if (time_to_next_checkin < base::TimeDelta())
474     time_to_next_checkin = base::TimeDelta();
475 
476   base::MessageLoop::current()->PostDelayedTask(
477       FROM_HERE,
478       base::Bind(&GCMClientImpl::StartCheckin,
479                  periodic_checkin_ptr_factory_.GetWeakPtr()),
480       time_to_next_checkin);
481 }
482 
GetTimeToNextCheckin() const483 base::TimeDelta GCMClientImpl::GetTimeToNextCheckin() const {
484   return last_checkin_time_ + gservices_settings_.GetCheckinInterval() -
485          clock_->Now();
486 }
487 
SetLastCheckinTimeCallback(bool success)488 void GCMClientImpl::SetLastCheckinTimeCallback(bool success) {
489   // TODO(fgorski): This is one of the signals that store needs a rebuild.
490   DCHECK(success);
491 }
492 
SetDeviceCredentialsCallback(bool success)493 void GCMClientImpl::SetDeviceCredentialsCallback(bool success) {
494   // TODO(fgorski): This is one of the signals that store needs a rebuild.
495   DCHECK(success);
496 }
497 
UpdateRegistrationCallback(bool success)498 void GCMClientImpl::UpdateRegistrationCallback(bool success) {
499   // TODO(fgorski): This is one of the signals that store needs a rebuild.
500   DCHECK(success);
501 }
502 
Stop()503 void GCMClientImpl::Stop() {
504   weak_ptr_factory_.InvalidateWeakPtrs();
505   device_checkin_info_.Reset();
506   connection_factory_.reset();
507   delegate_->OnDisconnected();
508   mcs_client_.reset();
509   checkin_request_.reset();
510   pending_registration_requests_.clear();
511   state_ = INITIALIZED;
512   gcm_store_->Close();
513 }
514 
CheckOut()515 void GCMClientImpl::CheckOut() {
516   Stop();
517   gcm_store_->Destroy(base::Bind(&GCMClientImpl::OnGCMStoreDestroyed,
518                                  weak_ptr_factory_.GetWeakPtr()));
519 }
520 
Register(const std::string & app_id,const std::vector<std::string> & sender_ids)521 void GCMClientImpl::Register(const std::string& app_id,
522                              const std::vector<std::string>& sender_ids) {
523   DCHECK_EQ(state_, READY);
524 
525   // If the same sender ids is provided, return the cached registration ID
526   // directly.
527   RegistrationInfoMap::const_iterator registrations_iter =
528       registrations_.find(app_id);
529   if (registrations_iter != registrations_.end() &&
530       registrations_iter->second->sender_ids == sender_ids) {
531     delegate_->OnRegisterFinished(
532         app_id, registrations_iter->second->registration_id, SUCCESS);
533     return;
534   }
535 
536   RegistrationRequest::RequestInfo request_info(
537       device_checkin_info_.android_id,
538       device_checkin_info_.secret,
539       app_id,
540       sender_ids);
541   DCHECK_EQ(0u, pending_registration_requests_.count(app_id));
542 
543   RegistrationRequest* registration_request =
544       new RegistrationRequest(gservices_settings_.GetRegistrationURL(),
545                               request_info,
546                               kDefaultBackoffPolicy,
547                               base::Bind(&GCMClientImpl::OnRegisterCompleted,
548                                          weak_ptr_factory_.GetWeakPtr(),
549                                          app_id,
550                                          sender_ids),
551                               kMaxRegistrationRetries,
552                               url_request_context_getter_,
553                               &recorder_);
554   pending_registration_requests_[app_id] = registration_request;
555   registration_request->Start();
556 }
557 
OnRegisterCompleted(const std::string & app_id,const std::vector<std::string> & sender_ids,RegistrationRequest::Status status,const std::string & registration_id)558 void GCMClientImpl::OnRegisterCompleted(
559     const std::string& app_id,
560     const std::vector<std::string>& sender_ids,
561     RegistrationRequest::Status status,
562     const std::string& registration_id) {
563   DCHECK(delegate_);
564 
565   Result result;
566   PendingRegistrationRequests::iterator iter =
567       pending_registration_requests_.find(app_id);
568   if (iter == pending_registration_requests_.end())
569     result = UNKNOWN_ERROR;
570   else if (status == RegistrationRequest::INVALID_SENDER)
571     result = INVALID_PARAMETER;
572   else if (registration_id.empty())
573     result = SERVER_ERROR;
574   else
575     result = SUCCESS;
576 
577   if (result == SUCCESS) {
578     // Cache it.
579     linked_ptr<RegistrationInfo> registration(new RegistrationInfo);
580     registration->sender_ids = sender_ids;
581     registration->registration_id = registration_id;
582     registrations_[app_id] = registration;
583 
584     // Save it in the persistent store.
585     gcm_store_->AddRegistration(
586         app_id,
587         registration,
588         base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
589                    weak_ptr_factory_.GetWeakPtr()));
590   }
591 
592   delegate_->OnRegisterFinished(
593       app_id, result == SUCCESS ? registration_id : std::string(), result);
594 
595   if (iter != pending_registration_requests_.end()) {
596     delete iter->second;
597     pending_registration_requests_.erase(iter);
598   }
599 }
600 
Unregister(const std::string & app_id)601 void GCMClientImpl::Unregister(const std::string& app_id) {
602   DCHECK_EQ(state_, READY);
603   if (pending_unregistration_requests_.count(app_id) == 1)
604     return;
605 
606   // Remove from the cache and persistent store.
607   registrations_.erase(app_id);
608   gcm_store_->RemoveRegistration(
609       app_id,
610       base::Bind(&GCMClientImpl::UpdateRegistrationCallback,
611                  weak_ptr_factory_.GetWeakPtr()));
612 
613   UnregistrationRequest::RequestInfo request_info(
614       device_checkin_info_.android_id,
615       device_checkin_info_.secret,
616       app_id);
617 
618   UnregistrationRequest* unregistration_request = new UnregistrationRequest(
619       gservices_settings_.GetRegistrationURL(),
620       request_info,
621       kDefaultBackoffPolicy,
622       base::Bind(&GCMClientImpl::OnUnregisterCompleted,
623                  weak_ptr_factory_.GetWeakPtr(),
624                  app_id),
625       url_request_context_getter_,
626       &recorder_);
627   pending_unregistration_requests_[app_id] = unregistration_request;
628   unregistration_request->Start();
629 }
630 
OnUnregisterCompleted(const std::string & app_id,UnregistrationRequest::Status status)631 void GCMClientImpl::OnUnregisterCompleted(
632     const std::string& app_id,
633     UnregistrationRequest::Status status) {
634   DVLOG(1) << "Unregister completed for app: " << app_id
635            << " with " << (status ? "success." : "failure.");
636   delegate_->OnUnregisterFinished(
637       app_id,
638       status == UnregistrationRequest::SUCCESS ? SUCCESS : SERVER_ERROR);
639 
640   PendingUnregistrationRequests::iterator iter =
641       pending_unregistration_requests_.find(app_id);
642   if (iter == pending_unregistration_requests_.end())
643     return;
644 
645   delete iter->second;
646   pending_unregistration_requests_.erase(iter);
647 }
648 
OnGCMStoreDestroyed(bool success)649 void GCMClientImpl::OnGCMStoreDestroyed(bool success) {
650   DLOG_IF(ERROR, !success) << "GCM store failed to be destroyed!";
651   UMA_HISTOGRAM_BOOLEAN("GCM.StoreDestroySucceeded", success);
652 }
653 
Send(const std::string & app_id,const std::string & receiver_id,const OutgoingMessage & message)654 void GCMClientImpl::Send(const std::string& app_id,
655                          const std::string& receiver_id,
656                          const OutgoingMessage& message) {
657   DCHECK_EQ(state_, READY);
658 
659   RecordOutgoingMessageToUMA(message);
660 
661   mcs_proto::DataMessageStanza stanza;
662   stanza.set_ttl(message.time_to_live);
663   stanza.set_sent(clock_->Now().ToInternalValue() /
664                   base::Time::kMicrosecondsPerSecond);
665   stanza.set_id(message.id);
666   stanza.set_from(kSendMessageFromValue);
667   stanza.set_to(receiver_id);
668   stanza.set_category(app_id);
669 
670   for (MessageData::const_iterator iter = message.data.begin();
671        iter != message.data.end();
672        ++iter) {
673     mcs_proto::AppData* app_data = stanza.add_app_data();
674     app_data->set_key(iter->first);
675     app_data->set_value(iter->second);
676   }
677 
678   MCSMessage mcs_message(stanza);
679   DVLOG(1) << "MCS message size: " << mcs_message.size();
680   mcs_client_->SendMessage(mcs_message);
681 }
682 
GetStateString() const683 std::string GCMClientImpl::GetStateString() const {
684   switch(state_) {
685     case GCMClientImpl::INITIALIZED:
686       return "INITIALIZED";
687     case GCMClientImpl::UNINITIALIZED:
688       return "UNINITIALIZED";
689     case GCMClientImpl::LOADING:
690       return "LOADING";
691     case GCMClientImpl::INITIAL_DEVICE_CHECKIN:
692       return "INITIAL_DEVICE_CHECKIN";
693     case GCMClientImpl::READY:
694       return "READY";
695     default:
696       NOTREACHED();
697       return std::string();
698   }
699 }
700 
SetRecording(bool recording)701 void GCMClientImpl::SetRecording(bool recording) {
702   recorder_.SetRecording(recording);
703 }
704 
ClearActivityLogs()705 void GCMClientImpl::ClearActivityLogs() {
706   recorder_.Clear();
707 }
708 
GetStatistics() const709 GCMClient::GCMStatistics GCMClientImpl::GetStatistics() const {
710   GCMClient::GCMStatistics stats;
711   stats.gcm_client_created = true;
712   stats.is_recording = recorder_.is_recording();
713   stats.gcm_client_state = GetStateString();
714   stats.connection_client_created = mcs_client_.get() != NULL;
715   if (connection_factory_.get())
716     stats.connection_state = connection_factory_->GetConnectionStateString();
717   if (mcs_client_.get()) {
718     stats.send_queue_size = mcs_client_->GetSendQueueSize();
719     stats.resend_queue_size = mcs_client_->GetResendQueueSize();
720   }
721   if (device_checkin_info_.android_id > 0)
722     stats.android_id = device_checkin_info_.android_id;
723   recorder_.CollectActivities(&stats.recorded_activities);
724 
725   for (RegistrationInfoMap::const_iterator it = registrations_.begin();
726        it != registrations_.end(); ++it) {
727     stats.registered_app_ids.push_back(it->first);
728   }
729   return stats;
730 }
731 
OnActivityRecorded()732 void GCMClientImpl::OnActivityRecorded() {
733   delegate_->OnActivityRecorded();
734 }
735 
OnConnected(const GURL & current_server,const net::IPEndPoint & ip_endpoint)736 void GCMClientImpl::OnConnected(const GURL& current_server,
737                                 const net::IPEndPoint& ip_endpoint) {
738   // TODO(gcm): expose current server in debug page.
739   delegate_->OnActivityRecorded();
740   delegate_->OnConnected(ip_endpoint);
741 }
742 
OnDisconnected()743 void GCMClientImpl::OnDisconnected() {
744   delegate_->OnActivityRecorded();
745   delegate_->OnDisconnected();
746 }
747 
OnMessageReceivedFromMCS(const gcm::MCSMessage & message)748 void GCMClientImpl::OnMessageReceivedFromMCS(const gcm::MCSMessage& message) {
749   switch (message.tag()) {
750     case kLoginResponseTag:
751       DVLOG(1) << "Login response received by GCM Client. Ignoring.";
752       return;
753     case kDataMessageStanzaTag:
754       DVLOG(1) << "A downstream message received. Processing...";
755       HandleIncomingMessage(message);
756       return;
757     default:
758       NOTREACHED() << "Message with unexpected tag received by GCMClient";
759       return;
760   }
761 }
762 
OnMessageSentToMCS(int64 user_serial_number,const std::string & app_id,const std::string & message_id,MCSClient::MessageSendStatus status)763 void GCMClientImpl::OnMessageSentToMCS(int64 user_serial_number,
764                                        const std::string& app_id,
765                                        const std::string& message_id,
766                                        MCSClient::MessageSendStatus status) {
767   DCHECK_EQ(user_serial_number, kDefaultUserSerialNumber);
768   DCHECK(delegate_);
769 
770   // TTL_EXCEEDED is singled out here, because it can happen long time after the
771   // message was sent. That is why it comes as |OnMessageSendError| event rather
772   // than |OnSendFinished|. SendErrorDetails.additional_data is left empty.
773   // All other errors will be raised immediately, through asynchronous callback.
774   // It is expected that TTL_EXCEEDED will be issued for a message that was
775   // previously issued |OnSendFinished| with status SUCCESS.
776   // For now, we do not report that the message has been sent and acked
777   // successfully.
778   // TODO(jianli): Consider adding UMA for this status.
779   if (status == MCSClient::TTL_EXCEEDED) {
780     SendErrorDetails send_error_details;
781     send_error_details.message_id = message_id;
782     send_error_details.result = GCMClient::TTL_EXCEEDED;
783     delegate_->OnMessageSendError(app_id, send_error_details);
784   } else if (status != MCSClient::SENT) {
785     delegate_->OnSendFinished(app_id, message_id, ToGCMClientResult(status));
786   }
787 }
788 
OnMCSError()789 void GCMClientImpl::OnMCSError() {
790   // TODO(fgorski): For now it replaces the initialization method. Long term it
791   // should have an error or status passed in.
792 }
793 
HandleIncomingMessage(const gcm::MCSMessage & message)794 void GCMClientImpl::HandleIncomingMessage(const gcm::MCSMessage& message) {
795   DCHECK(delegate_);
796 
797   const mcs_proto::DataMessageStanza& data_message_stanza =
798       reinterpret_cast<const mcs_proto::DataMessageStanza&>(
799           message.GetProtobuf());
800   DCHECK_EQ(data_message_stanza.device_user_id(), kDefaultUserSerialNumber);
801 
802   // Copying all the data from the stanza to a MessageData object. When present,
803   // keys like kMessageTypeKey or kSendErrorMessageIdKey will be filtered out
804   // later.
805   MessageData message_data;
806   for (int i = 0; i < data_message_stanza.app_data_size(); ++i) {
807     std::string key = data_message_stanza.app_data(i).key();
808     message_data[key] = data_message_stanza.app_data(i).value();
809   }
810 
811   MessageType message_type = DATA_MESSAGE;
812   MessageData::iterator iter = message_data.find(kMessageTypeKey);
813   if (iter != message_data.end()) {
814     message_type = DecodeMessageType(iter->second);
815     message_data.erase(iter);
816   }
817 
818   switch (message_type) {
819     case DATA_MESSAGE:
820       HandleIncomingDataMessage(data_message_stanza, message_data);
821       break;
822     case DELETED_MESSAGES:
823       recorder_.RecordDataMessageReceived(data_message_stanza.category(),
824                                           data_message_stanza.from(),
825                                           data_message_stanza.ByteSize(),
826                                           true,
827                                           GCMStatsRecorder::DELETED_MESSAGES);
828       delegate_->OnMessagesDeleted(data_message_stanza.category());
829       break;
830     case SEND_ERROR:
831       HandleIncomingSendError(data_message_stanza, message_data);
832       break;
833     case UNKNOWN:
834     default:  // Treat default the same as UNKNOWN.
835       DVLOG(1) << "Unknown message_type received. Message ignored. "
836                << "App ID: " << data_message_stanza.category() << ".";
837       break;
838   }
839 }
840 
HandleIncomingDataMessage(const mcs_proto::DataMessageStanza & data_message_stanza,MessageData & message_data)841 void GCMClientImpl::HandleIncomingDataMessage(
842     const mcs_proto::DataMessageStanza& data_message_stanza,
843     MessageData& message_data) {
844   std::string app_id = data_message_stanza.category();
845 
846   // Drop the message when the app is not registered for the sender of the
847   // message.
848   RegistrationInfoMap::iterator iter = registrations_.find(app_id);
849   bool not_registered =
850       iter == registrations_.end() ||
851       std::find(iter->second->sender_ids.begin(),
852                 iter->second->sender_ids.end(),
853                 data_message_stanza.from()) == iter->second->sender_ids.end();
854   recorder_.RecordDataMessageReceived(app_id, data_message_stanza.from(),
855       data_message_stanza.ByteSize(), !not_registered,
856       GCMStatsRecorder::DATA_MESSAGE);
857   if (not_registered) {
858     return;
859   }
860 
861   IncomingMessage incoming_message;
862   incoming_message.sender_id = data_message_stanza.from();
863   if (data_message_stanza.has_token())
864     incoming_message.collapse_key = data_message_stanza.token();
865   incoming_message.data = message_data;
866   delegate_->OnMessageReceived(app_id, incoming_message);
867 }
868 
HandleIncomingSendError(const mcs_proto::DataMessageStanza & data_message_stanza,MessageData & message_data)869 void GCMClientImpl::HandleIncomingSendError(
870     const mcs_proto::DataMessageStanza& data_message_stanza,
871     MessageData& message_data) {
872   SendErrorDetails send_error_details;
873   send_error_details.additional_data = message_data;
874   send_error_details.result = SERVER_ERROR;
875 
876   MessageData::iterator iter =
877       send_error_details.additional_data.find(kSendErrorMessageIdKey);
878   if (iter != send_error_details.additional_data.end()) {
879     send_error_details.message_id = iter->second;
880     send_error_details.additional_data.erase(iter);
881   }
882 
883   recorder_.RecordIncomingSendError(
884       data_message_stanza.category(),
885       data_message_stanza.to(),
886       data_message_stanza.id());
887   delegate_->OnMessageSendError(data_message_stanza.category(),
888                                 send_error_details);
889 }
890 
891 }  // namespace gcm
892