Upstream version 6.35.121.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / services / gcm / gcm_profile_service.cc
1 // Copyright (c) 2013 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 "chrome/browser/services/gcm/gcm_profile_service.h"
6
7 #include <algorithm>
8 #include <string>
9 #include <vector>
10
11 #include "base/base64.h"
12 #include "base/files/file_path.h"
13 #include "base/logging.h"
14 #include "base/path_service.h"
15 #include "base/prefs/pref_service.h"
16 #include "base/strings/string_number_conversions.h"
17 #include "base/threading/sequenced_worker_pool.h"
18 #include "chrome/browser/chrome_notification_types.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/services/gcm/gcm_app_handler.h"
21 #include "chrome/browser/services/gcm/gcm_client_factory.h"
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23 #include "chrome/browser/signin/signin_manager.h"
24 #include "chrome/browser/signin/signin_manager_factory.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/chrome_paths.h"
27 #include "chrome/common/chrome_version_info.h"
28 #include "chrome/common/pref_names.h"
29 #include "components/signin/core/browser/profile_oauth2_token_service.h"
30 #include "components/user_prefs/pref_registry_syncable.h"
31 #include "content/public/browser/browser_thread.h"
32 #include "content/public/browser/notification_details.h"
33 #include "content/public/browser/notification_source.h"
34 #include "google_apis/gcm/protocol/android_checkin.pb.h"
35 #include "net/url_request/url_request_context_getter.h"
36
37 namespace gcm {
38
39 namespace {
40
41 checkin_proto::ChromeBuildProto_Platform GetPlatform() {
42 #if defined(OS_WIN)
43   return checkin_proto::ChromeBuildProto_Platform_PLATFORM_WIN;
44 #elif defined(OS_MACOSX)
45   return checkin_proto::ChromeBuildProto_Platform_PLATFORM_MAC;
46 #elif defined(OS_IOS)
47   return checkin_proto::ChromeBuildProto_Platform_PLATFORM_IOS;
48 #elif defined(OS_CHROMEOS)
49   return checkin_proto::ChromeBuildProto_Platform_PLATFORM_CROS;
50 #elif defined(OS_LINUX)
51   return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
52 #else
53   // For all other platforms, return as LINUX.
54   return checkin_proto::ChromeBuildProto_Platform_PLATFORM_LINUX;
55 #endif
56 }
57
58 std::string GetVersion() {
59   chrome::VersionInfo version_info;
60   return version_info.Version();
61 }
62
63 checkin_proto::ChromeBuildProto_Channel GetChannel() {
64   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
65   switch (channel) {
66     case chrome::VersionInfo::CHANNEL_UNKNOWN:
67       return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
68     case chrome::VersionInfo::CHANNEL_CANARY:
69       return checkin_proto::ChromeBuildProto_Channel_CHANNEL_CANARY;
70     case chrome::VersionInfo::CHANNEL_DEV:
71       return checkin_proto::ChromeBuildProto_Channel_CHANNEL_DEV;
72     case chrome::VersionInfo::CHANNEL_BETA:
73       return checkin_proto::ChromeBuildProto_Channel_CHANNEL_BETA;
74     case chrome::VersionInfo::CHANNEL_STABLE:
75       return checkin_proto::ChromeBuildProto_Channel_CHANNEL_STABLE;
76     default:
77       NOTREACHED();
78       return checkin_proto::ChromeBuildProto_Channel_CHANNEL_UNKNOWN;
79   };
80 }
81
82 }  // namespace
83
84 // Helper class to save tasks to run until we're ready to execute them.
85 class GCMProfileService::DelayedTaskController {
86  public:
87   DelayedTaskController();
88   ~DelayedTaskController();
89
90   // Adds a task that will be invoked once we're ready.
91   void AddTask(base::Closure task);
92
93   // Sets ready status. It is ready only when check-in is completed and
94   // the GCMClient is fully initialized.
95   void SetReady();
96
97   // Returns true if it is ready to perform tasks.
98   bool CanRunTaskWithoutDelay() const;
99
100  private:
101   void RunTasks();
102
103   // Flag that indicates that GCM is ready.
104   bool ready_;
105
106   std::vector<base::Closure> delayed_tasks_;
107 };
108
109 GCMProfileService::DelayedTaskController::DelayedTaskController()
110     : ready_(false) {
111 }
112
113 GCMProfileService::DelayedTaskController::~DelayedTaskController() {
114 }
115
116 void GCMProfileService::DelayedTaskController::AddTask(base::Closure task) {
117   delayed_tasks_.push_back(task);
118 }
119
120 void GCMProfileService::DelayedTaskController::SetReady() {
121   ready_ = true;
122   RunTasks();
123 }
124
125 bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay() const {
126   return ready_;
127 }
128
129 void GCMProfileService::DelayedTaskController::RunTasks() {
130   DCHECK(ready_);
131
132   for (size_t i = 0; i < delayed_tasks_.size(); ++i)
133     delayed_tasks_[i].Run();
134   delayed_tasks_.clear();
135 }
136
137 class GCMProfileService::IOWorker
138     : public GCMClient::Delegate,
139       public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{
140  public:
141   // Called on UI thread.
142   IOWorker();
143
144   // Overridden from GCMClient::Delegate:
145   // Called on IO thread.
146   virtual void OnRegisterFinished(const std::string& app_id,
147                                   const std::string& registration_id,
148                                   GCMClient::Result result) OVERRIDE;
149   virtual void OnUnregisterFinished(const std::string& app_id,
150                                     GCMClient::Result result) OVERRIDE;
151   virtual void OnSendFinished(const std::string& app_id,
152                               const std::string& message_id,
153                               GCMClient::Result result) OVERRIDE;
154   virtual void OnMessageReceived(
155       const std::string& app_id,
156       const GCMClient::IncomingMessage& message) OVERRIDE;
157   virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
158   virtual void OnMessageSendError(
159       const std::string& app_id,
160       const GCMClient::SendErrorDetails& send_error_details) OVERRIDE;
161   virtual void OnGCMReady() OVERRIDE;
162
163   // Called on IO thread.
164   void Initialize(scoped_ptr<GCMClientFactory> gcm_client_factory,
165                   const base::FilePath& store_path,
166                   const std::vector<std::string>& account_ids,
167                   const scoped_refptr<net::URLRequestContextGetter>&
168                       url_request_context_getter);
169   void Reset();
170   void Load(const base::WeakPtr<GCMProfileService>& service);
171   void Stop();
172   void CheckOut();
173   void Register(const std::string& app_id,
174                 const std::vector<std::string>& sender_ids);
175   void Unregister(const std::string& app_id);
176   void Send(const std::string& app_id,
177             const std::string& receiver_id,
178             const GCMClient::OutgoingMessage& message);
179   void RequestGCMStatistics();
180
181   // For testing purpose. Can be called from UI thread. Use with care.
182   GCMClient* gcm_client_for_testing() const { return gcm_client_.get(); }
183
184  private:
185   friend class base::RefCountedThreadSafe<IOWorker>;
186   virtual ~IOWorker();
187
188   base::WeakPtr<GCMProfileService> service_;
189
190   scoped_ptr<GCMClient> gcm_client_;
191 };
192
193 GCMProfileService::IOWorker::IOWorker() {
194   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
195 }
196
197 GCMProfileService::IOWorker::~IOWorker() {
198 }
199
200 void GCMProfileService::IOWorker::Initialize(
201     scoped_ptr<GCMClientFactory> gcm_client_factory,
202     const base::FilePath& store_path,
203     const std::vector<std::string>& account_ids,
204     const scoped_refptr<net::URLRequestContextGetter>&
205         url_request_context_getter) {
206   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
207
208   gcm_client_ = gcm_client_factory->BuildInstance().Pass();
209
210   checkin_proto::ChromeBuildProto chrome_build_proto;
211   chrome_build_proto.set_platform(GetPlatform());
212   chrome_build_proto.set_chrome_version(GetVersion());
213   chrome_build_proto.set_channel(GetChannel());
214
215   scoped_refptr<base::SequencedWorkerPool> worker_pool(
216       content::BrowserThread::GetBlockingPool());
217   scoped_refptr<base::SequencedTaskRunner> blocking_task_runner(
218       worker_pool->GetSequencedTaskRunnerWithShutdownBehavior(
219           worker_pool->GetSequenceToken(),
220           base::SequencedWorkerPool::SKIP_ON_SHUTDOWN));
221
222   gcm_client_->Initialize(chrome_build_proto,
223                           store_path,
224                           account_ids,
225                           blocking_task_runner,
226                           url_request_context_getter,
227                           this);
228 }
229
230 void GCMProfileService::IOWorker::Reset() {
231   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
232
233   // GCMClient instance must be destroyed from the same thread where it was
234   // created.
235   gcm_client_.reset();
236 }
237
238 void GCMProfileService::IOWorker::OnRegisterFinished(
239     const std::string& app_id,
240     const std::string& registration_id,
241     GCMClient::Result result) {
242   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
243
244   content::BrowserThread::PostTask(
245       content::BrowserThread::UI,
246       FROM_HERE,
247       base::Bind(&GCMProfileService::RegisterFinished,
248                  service_,
249                  app_id,
250                  registration_id,
251                  result));
252 }
253
254 void GCMProfileService::IOWorker::OnUnregisterFinished(
255     const std::string& app_id,
256     GCMClient::Result result) {
257   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
258
259   content::BrowserThread::PostTask(
260       content::BrowserThread::UI,
261       FROM_HERE,
262       base::Bind(
263           &GCMProfileService::UnregisterFinished, service_, app_id, result));
264 }
265
266 void GCMProfileService::IOWorker::OnSendFinished(
267     const std::string& app_id,
268     const std::string& message_id,
269     GCMClient::Result result) {
270   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
271
272   content::BrowserThread::PostTask(
273       content::BrowserThread::UI,
274       FROM_HERE,
275       base::Bind(&GCMProfileService::SendFinished,
276                  service_,
277                  app_id,
278                  message_id,
279                  result));
280 }
281
282 void GCMProfileService::IOWorker::OnMessageReceived(
283     const std::string& app_id,
284     const GCMClient::IncomingMessage& message) {
285   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
286
287   content::BrowserThread::PostTask(
288       content::BrowserThread::UI,
289       FROM_HERE,
290       base::Bind(&GCMProfileService::MessageReceived,
291                  service_,
292                  app_id,
293                  message));
294 }
295
296 void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
297   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
298
299   content::BrowserThread::PostTask(
300       content::BrowserThread::UI,
301       FROM_HERE,
302       base::Bind(&GCMProfileService::MessagesDeleted,
303                  service_,
304                  app_id));
305 }
306
307 void GCMProfileService::IOWorker::OnMessageSendError(
308     const std::string& app_id,
309     const GCMClient::SendErrorDetails& send_error_details) {
310   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
311
312   content::BrowserThread::PostTask(
313       content::BrowserThread::UI,
314       FROM_HERE,
315       base::Bind(&GCMProfileService::MessageSendError,
316                  service_,
317                  app_id,
318                  send_error_details));
319 }
320
321 void GCMProfileService::IOWorker::OnGCMReady() {
322   content::BrowserThread::PostTask(
323       content::BrowserThread::UI,
324       FROM_HERE,
325       base::Bind(&GCMProfileService::GCMClientReady,
326                  service_));
327 }
328
329 void GCMProfileService::IOWorker::Load(
330     const base::WeakPtr<GCMProfileService>& service) {
331   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
332
333   service_ = service;
334   gcm_client_->Load();
335 }
336
337 void GCMProfileService::IOWorker::Stop() {
338   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
339
340   gcm_client_->Stop();
341 }
342
343 void GCMProfileService::IOWorker::CheckOut() {
344   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
345
346   gcm_client_->CheckOut();
347
348   // Note that we still need to keep GCMClient instance alive since the profile
349   // might be signed in again.
350 }
351
352 void GCMProfileService::IOWorker::Register(
353     const std::string& app_id,
354     const std::vector<std::string>& sender_ids) {
355   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
356
357   gcm_client_->Register(app_id, sender_ids);
358 }
359
360 void GCMProfileService::IOWorker::Unregister(const std::string& app_id) {
361   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
362
363   gcm_client_->Unregister(app_id);
364 }
365
366 void GCMProfileService::IOWorker::Send(
367     const std::string& app_id,
368     const std::string& receiver_id,
369     const GCMClient::OutgoingMessage& message) {
370   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
371
372   gcm_client_->Send(app_id, receiver_id, message);
373 }
374
375 void GCMProfileService::IOWorker::RequestGCMStatistics() {
376   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
377   gcm::GCMClient::GCMStatistics stats;
378
379   if (gcm_client_.get()) {
380     stats.gcm_client_created = true;
381     stats = gcm_client_->GetStatistics();
382   }
383
384   content::BrowserThread::PostTask(
385       content::BrowserThread::UI,
386       FROM_HERE,
387       base::Bind(&GCMProfileService::RequestGCMStatisticsFinished,
388                  service_,
389                  stats));
390 }
391
392 std::string GCMProfileService::GetGCMEnabledStateString(GCMEnabledState state) {
393   switch (state) {
394     case GCMProfileService::ALWAYS_ENABLED:
395       return "ALWAYS_ENABLED";
396     case GCMProfileService::ENABLED_FOR_APPS:
397       return "ENABLED_FOR_APPS";
398     case GCMProfileService::ALWAYS_DISABLED:
399       return "ALWAYS_DISABLED";
400     default:
401       NOTREACHED();
402       return std::string();
403   }
404 }
405
406 // static
407 GCMProfileService::GCMEnabledState GCMProfileService::GetGCMEnabledState(
408     Profile* profile) {
409   const base::Value* gcm_enabled_value =
410       profile->GetPrefs()->GetUserPrefValue(prefs::kGCMChannelEnabled);
411   if (!gcm_enabled_value)
412     return ENABLED_FOR_APPS;
413
414   bool gcm_enabled = false;
415   if (!gcm_enabled_value->GetAsBoolean(&gcm_enabled))
416     return ENABLED_FOR_APPS;
417
418   return gcm_enabled ? ALWAYS_ENABLED : ALWAYS_DISABLED;
419 }
420
421 // static
422 void GCMProfileService::RegisterProfilePrefs(
423     user_prefs::PrefRegistrySyncable* registry) {
424   // GCM support is only enabled by default for Canary/Dev/Custom builds.
425   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
426   bool on_by_default = false;
427   if (channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
428       channel == chrome::VersionInfo::CHANNEL_CANARY ||
429       channel == chrome::VersionInfo::CHANNEL_DEV) {
430     on_by_default = true;
431   }
432   registry->RegisterBooleanPref(
433       prefs::kGCMChannelEnabled,
434       on_by_default,
435       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
436 }
437
438 GCMProfileService::GCMProfileService(Profile* profile)
439     : profile_(profile),
440       gcm_client_ready_(false),
441       weak_ptr_factory_(this) {
442   DCHECK(!profile->IsOffTheRecord());
443 }
444
445 GCMProfileService::~GCMProfileService() {
446 }
447
448 void GCMProfileService::Initialize(
449     scoped_ptr<GCMClientFactory> gcm_client_factory) {
450   registrar_.Add(this,
451                  chrome::NOTIFICATION_PROFILE_DESTROYED,
452                  content::Source<Profile>(profile_));
453
454   SigninManagerFactory::GetForProfile(profile_)->AddObserver(this);
455
456   // Get the list of available accounts.
457   std::vector<std::string> account_ids;
458 #if !defined(OS_ANDROID)
459   account_ids =
460       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->GetAccounts();
461 #endif
462
463   // Create and initialize the GCMClient. Note that this does not initiate the
464   // GCM check-in.
465   io_worker_ = new IOWorker();
466   scoped_refptr<net::URLRequestContextGetter> url_request_context_getter =
467       profile_->GetRequestContext();
468   content::BrowserThread::PostTask(
469       content::BrowserThread::IO,
470       FROM_HERE,
471       base::Bind(&GCMProfileService::IOWorker::Initialize,
472                  io_worker_,
473                  base::Passed(&gcm_client_factory),
474                  profile_->GetPath().Append(chrome::kGCMStoreDirname),
475                  account_ids,
476                  url_request_context_getter));
477
478   // Load from the GCM store and initiate the GCM check-in if the rollout signal
479   // indicates yes.
480   if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED)
481     EnsureLoaded();
482 }
483
484 void GCMProfileService::Start() {
485   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
486
487   EnsureLoaded();
488 }
489
490 void GCMProfileService::Stop() {
491   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
492
493   // No need to stop GCM service if not started yet.
494   if (username_.empty())
495     return;
496
497   RemoveCachedData();
498
499   content::BrowserThread::PostTask(
500       content::BrowserThread::IO,
501       FROM_HERE,
502       base::Bind(&GCMProfileService::IOWorker::Stop, io_worker_));
503 }
504
505 void GCMProfileService::Shutdown() {
506   for (GCMAppHandlerMap::const_iterator iter = app_handlers_.begin();
507        iter != app_handlers_.end(); ++iter) {
508     iter->second->ShutdownHandler();
509   }
510   app_handlers_.clear();
511
512   SigninManagerFactory::GetForProfile(profile_)->RemoveObserver(this);
513 }
514
515 void GCMProfileService::AddAppHandler(const std::string& app_id,
516                                       GCMAppHandler* handler) {
517   DCHECK(!app_id.empty());
518   DCHECK(handler);
519   DCHECK(app_handlers_.find(app_id) == app_handlers_.end());
520
521   app_handlers_[app_id] = handler;
522 }
523
524 void GCMProfileService::RemoveAppHandler(const std::string& app_id) {
525   DCHECK(!app_id.empty());
526
527   app_handlers_.erase(app_id);
528 }
529
530 void GCMProfileService::Register(const std::string& app_id,
531                                  const std::vector<std::string>& sender_ids,
532                                  RegisterCallback callback) {
533   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
534   DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null());
535
536   GCMClient::Result result = EnsureAppReady(app_id);
537   if (result != GCMClient::SUCCESS) {
538     callback.Run(std::string(), result);
539     return;
540   }
541
542   // If previous un/register operation is still in progress, bail out.
543   if (IsAsyncOperationPending(app_id)) {
544     callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
545     return;
546   }
547
548   register_callbacks_[app_id] = callback;
549
550   // Delay the register operation until GCMClient is ready.
551   if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
552     delayed_task_controller_->AddTask(
553         base::Bind(&GCMProfileService::DoRegister,
554                    weak_ptr_factory_.GetWeakPtr(),
555                    app_id,
556                    sender_ids));
557     return;
558   }
559
560   DoRegister(app_id, sender_ids);
561 }
562
563 void GCMProfileService::DoRegister(const std::string& app_id,
564                                    const std::vector<std::string>& sender_ids) {
565   std::map<std::string, RegisterCallback>::iterator callback_iter =
566       register_callbacks_.find(app_id);
567   if (callback_iter == register_callbacks_.end()) {
568     // The callback could have been removed when the app is uninstalled.
569     return;
570   }
571
572   // Normalize the sender IDs by making them sorted.
573   std::vector<std::string> normalized_sender_ids = sender_ids;
574   std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
575
576   content::BrowserThread::PostTask(
577       content::BrowserThread::IO,
578       FROM_HERE,
579       base::Bind(&GCMProfileService::IOWorker::Register,
580                  io_worker_,
581                  app_id,
582                  normalized_sender_ids));
583 }
584
585 void GCMProfileService::Unregister(const std::string& app_id,
586                                    UnregisterCallback callback) {
587   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
588   DCHECK(!app_id.empty() && !callback.is_null());
589
590   GCMClient::Result result = EnsureAppReady(app_id);
591   if (result != GCMClient::SUCCESS) {
592     callback.Run(result);
593     return;
594   }
595
596   // If previous un/register operation is still in progress, bail out.
597   if (IsAsyncOperationPending(app_id)) {
598     callback.Run(GCMClient::ASYNC_OPERATION_PENDING);
599     return;
600   }
601
602   unregister_callbacks_[app_id] = callback;
603
604   // Delay the unregister operation until GCMClient is ready.
605   if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
606     delayed_task_controller_->AddTask(
607         base::Bind(&GCMProfileService::DoUnregister,
608                    weak_ptr_factory_.GetWeakPtr(),
609                    app_id));
610     return;
611   }
612
613   DoUnregister(app_id);
614 }
615
616 void GCMProfileService::DoUnregister(const std::string& app_id) {
617   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
618
619   // Ask the server to unregister it. There could be a small chance that the
620   // unregister request fails. If this occurs, it does not bring any harm since
621   // we simply reject the messages/events received from the server.
622   content::BrowserThread::PostTask(
623       content::BrowserThread::IO,
624       FROM_HERE,
625       base::Bind(&GCMProfileService::IOWorker::Unregister,
626                  io_worker_,
627                  app_id));
628 }
629
630 void GCMProfileService::Send(const std::string& app_id,
631                              const std::string& receiver_id,
632                              const GCMClient::OutgoingMessage& message,
633                              SendCallback callback) {
634   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
635   DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null());
636
637   GCMClient::Result result = EnsureAppReady(app_id);
638   if (result != GCMClient::SUCCESS) {
639     callback.Run(std::string(), result);
640     return;
641   }
642
643   // If the message with send ID is still in progress, bail out.
644   std::pair<std::string, std::string> key(app_id, message.id);
645   if (send_callbacks_.find(key) != send_callbacks_.end()) {
646     callback.Run(message.id, GCMClient::INVALID_PARAMETER);
647     return;
648   }
649
650   send_callbacks_[key] = callback;
651
652   // Delay the send operation until all GCMClient is ready.
653   if (!delayed_task_controller_->CanRunTaskWithoutDelay()) {
654     delayed_task_controller_->AddTask(
655         base::Bind(&GCMProfileService::DoSend,
656                    weak_ptr_factory_.GetWeakPtr(),
657                    app_id,
658                    receiver_id,
659                    message));
660     return;
661   }
662
663   DoSend(app_id, receiver_id, message);
664 }
665
666 void GCMProfileService::DoSend(const std::string& app_id,
667                                const std::string& receiver_id,
668                                const GCMClient::OutgoingMessage& message) {
669   content::BrowserThread::PostTask(
670       content::BrowserThread::IO,
671       FROM_HERE,
672       base::Bind(&GCMProfileService::IOWorker::Send,
673                  io_worker_,
674                  app_id,
675                  receiver_id,
676                  message));
677 }
678
679 GCMClient* GCMProfileService::GetGCMClientForTesting() const {
680   return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL;
681 }
682
683 std::string GCMProfileService::SignedInUserName() const {
684   return username_;
685 }
686
687 bool GCMProfileService::IsGCMClientReady() const {
688   return gcm_client_ready_;
689 }
690
691 void GCMProfileService::RequestGCMStatistics(
692     RequestGCMStatisticsCallback callback) {
693   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
694   DCHECK(!callback.is_null());
695
696   request_gcm_statistics_callback_ = callback;
697   content::BrowserThread::PostTask(
698       content::BrowserThread::IO,
699       FROM_HERE,
700       base::Bind(&GCMProfileService::IOWorker::RequestGCMStatistics,
701                  io_worker_));
702 }
703
704 void GCMProfileService::Observe(int type,
705                                 const content::NotificationSource& source,
706                                 const content::NotificationDetails& details) {
707   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
708
709   switch (type) {
710     case chrome::NOTIFICATION_PROFILE_DESTROYED:
711       ResetGCMClient();
712       break;
713     default:
714       NOTREACHED();
715   }
716 }
717
718 void GCMProfileService::GoogleSigninSucceeded(const std::string& username,
719                                               const std::string& password) {
720   if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED)
721     EnsureLoaded();
722 }
723
724 void GCMProfileService::GoogleSignedOut(const std::string& username) {
725   CheckOut();
726 }
727
728 void GCMProfileService::EnsureLoaded() {
729   SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
730   if (!manager)
731     return;
732   std::string username = manager->GetAuthenticatedUsername();
733   if (username.empty())
734     return;
735
736   // CheckIn could be called more than once when:
737   // 1) The password changes.
738   // 2) Register/send function calls it to ensure CheckIn is done.
739   if (username_ == username)
740     return;
741   username_ = username;
742
743   DCHECK(!delayed_task_controller_);
744   delayed_task_controller_.reset(new DelayedTaskController);
745
746   // This will load the data from the gcm store and trigger the check-in if
747   // the persisted check-in info is not found.
748   // Note that we need to pass weak pointer again since the existing weak
749   // pointer in IOWorker might have been invalidated when check-out occurs.
750   content::BrowserThread::PostTask(
751       content::BrowserThread::IO,
752       FROM_HERE,
753       base::Bind(&GCMProfileService::IOWorker::Load,
754                  io_worker_,
755                  weak_ptr_factory_.GetWeakPtr()));
756 }
757
758 void GCMProfileService::RemoveCachedData() {
759   // Remove all the queued tasks since they no longer make sense after
760   // GCM service is stopped.
761   weak_ptr_factory_.InvalidateWeakPtrs();
762
763   username_.clear();
764   gcm_client_ready_ = false;
765   delayed_task_controller_.reset();
766   register_callbacks_.clear();
767   send_callbacks_.clear();
768 }
769
770 void GCMProfileService::CheckOut() {
771   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
772
773   // We still proceed with the check-out logic even if the check-in is not
774   // initiated in the current session. This will make sure that all the
775   // persisted data written previously will get purged.
776
777   RemoveCachedData();
778
779   content::BrowserThread::PostTask(
780       content::BrowserThread::IO,
781       FROM_HERE,
782       base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
783 }
784
785 void GCMProfileService::ResetGCMClient() {
786   content::BrowserThread::PostTask(
787       content::BrowserThread::IO,
788       FROM_HERE,
789       base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_));
790 }
791
792 GCMClient::Result GCMProfileService::EnsureAppReady(const std::string& app_id) {
793   // Ensure that check-in has been done.
794   EnsureLoaded();
795
796   // If the profile was not signed in, bail out.
797   if (username_.empty())
798     return GCMClient::NOT_SIGNED_IN;
799
800   return GCMClient::SUCCESS;
801 }
802
803 bool GCMProfileService::IsAsyncOperationPending(
804     const std::string& app_id) const {
805   return register_callbacks_.find(app_id) != register_callbacks_.end() ||
806          unregister_callbacks_.find(app_id) != unregister_callbacks_.end();
807 }
808
809 void GCMProfileService::RegisterFinished(const std::string& app_id,
810                                          const std::string& registration_id,
811                                          GCMClient::Result result) {
812   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
813
814   std::map<std::string, RegisterCallback>::iterator callback_iter =
815       register_callbacks_.find(app_id);
816   if (callback_iter == register_callbacks_.end()) {
817     // The callback could have been removed when the app is uninstalled.
818     return;
819   }
820
821   RegisterCallback callback = callback_iter->second;
822   register_callbacks_.erase(callback_iter);
823   callback.Run(registration_id, result);
824 }
825
826 void GCMProfileService::UnregisterFinished(const std::string& app_id,
827                                            GCMClient::Result result) {
828   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
829
830   std::map<std::string, UnregisterCallback>::iterator callback_iter =
831       unregister_callbacks_.find(app_id);
832   if (callback_iter == unregister_callbacks_.end())
833     return;
834
835   UnregisterCallback callback = callback_iter->second;
836   unregister_callbacks_.erase(callback_iter);
837   callback.Run(result);
838 }
839
840 void GCMProfileService::SendFinished(const std::string& app_id,
841                                      const std::string& message_id,
842                                      GCMClient::Result result) {
843   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
844
845   std::map<std::pair<std::string, std::string>, SendCallback>::iterator
846       callback_iter = send_callbacks_.find(
847           std::pair<std::string, std::string>(app_id, message_id));
848   if (callback_iter == send_callbacks_.end()) {
849     // The callback could have been removed when the app is uninstalled.
850     return;
851   }
852
853   SendCallback callback = callback_iter->second;
854   send_callbacks_.erase(callback_iter);
855   callback.Run(message_id, result);
856 }
857
858 void GCMProfileService::MessageReceived(const std::string& app_id,
859                                         GCMClient::IncomingMessage message) {
860   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
861
862   // Drop the event if signed out.
863   if (username_.empty())
864     return;
865
866   GetAppHandler(app_id)->OnMessage(app_id, message);
867 }
868
869 void GCMProfileService::MessagesDeleted(const std::string& app_id) {
870   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
871
872   // Drop the event if signed out.
873   if (username_.empty())
874     return;
875
876   GetAppHandler(app_id)->OnMessagesDeleted(app_id);
877 }
878
879 void GCMProfileService::MessageSendError(
880     const std::string& app_id,
881     const GCMClient::SendErrorDetails& send_error_details) {
882   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
883
884   // Drop the event if signed out.
885   if (username_.empty())
886     return;
887
888   GetAppHandler(app_id)->OnSendError(app_id, send_error_details);
889 }
890
891 void GCMProfileService::GCMClientReady() {
892   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
893
894   if (gcm_client_ready_)
895     return;
896   gcm_client_ready_ = true;
897
898   delayed_task_controller_->SetReady();
899 }
900
901 GCMAppHandler* GCMProfileService::GetAppHandler(const std::string& app_id) {
902   std::map<std::string, GCMAppHandler*>::const_iterator iter =
903       app_handlers_.find(app_id);
904   return iter == app_handlers_.end() ? &default_app_handler_ : iter->second;
905 }
906
907 void GCMProfileService::RequestGCMStatisticsFinished(
908     GCMClient::GCMStatistics stats) {
909   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
910
911   request_gcm_statistics_callback_.Run(stats);
912 }
913
914 }  // namespace gcm