gcm_profile_service.cc revision e5d81f57cb97b3b6b7fccc9c5610d21eb81db09d
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_factory.h"
24#include "chrome/common/chrome_constants.h"
25#include "chrome/common/chrome_paths.h"
26#include "chrome/common/chrome_version_info.h"
27#include "chrome/common/pref_names.h"
28#include "components/signin/core/browser/profile_oauth2_token_service.h"
29#include "components/signin/core/browser/signin_manager.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
37namespace gcm {
38
39namespace {
40
41checkin_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
58std::string GetVersion() {
59  chrome::VersionInfo version_info;
60  return version_info.Version();
61}
62
63checkin_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.
85class 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
109GCMProfileService::DelayedTaskController::DelayedTaskController()
110    : ready_(false) {
111}
112
113GCMProfileService::DelayedTaskController::~DelayedTaskController() {
114}
115
116void GCMProfileService::DelayedTaskController::AddTask(base::Closure task) {
117  delayed_tasks_.push_back(task);
118}
119
120void GCMProfileService::DelayedTaskController::SetReady() {
121  ready_ = true;
122  RunTasks();
123}
124
125bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay() const {
126  return ready_;
127}
128
129void 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
137class 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
193GCMProfileService::IOWorker::IOWorker() {
194  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
195}
196
197GCMProfileService::IOWorker::~IOWorker() {
198}
199
200void 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
230void 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
238void 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
254void 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
266void 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
282void 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
296void 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
307void 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
321void GCMProfileService::IOWorker::OnGCMReady() {
322  content::BrowserThread::PostTask(
323      content::BrowserThread::UI,
324      FROM_HERE,
325      base::Bind(&GCMProfileService::GCMClientReady,
326                 service_));
327}
328
329void 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
337void GCMProfileService::IOWorker::Stop() {
338  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
339
340  gcm_client_->Stop();
341}
342
343void 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
352void 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
360void 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
366void 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
375void 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
392std::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
407GCMProfileService::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
422void 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
438GCMProfileService::GCMProfileService(Profile* profile)
439    : profile_(profile),
440      gcm_client_ready_(false),
441      weak_ptr_factory_(this) {
442  DCHECK(!profile->IsOffTheRecord());
443}
444
445GCMProfileService::~GCMProfileService() {
446}
447
448void 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
484void GCMProfileService::Start() {
485  DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
486
487  EnsureLoaded();
488}
489
490void 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
505void 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
515void 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
524void GCMProfileService::RemoveAppHandler(const std::string& app_id) {
525  DCHECK(!app_id.empty());
526
527  app_handlers_.erase(app_id);
528}
529
530void 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
563void 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
585void 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
616void 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
630void 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
666void 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
679GCMClient* GCMProfileService::GetGCMClientForTesting() const {
680  return io_worker_ ? io_worker_->gcm_client_for_testing() : NULL;
681}
682
683std::string GCMProfileService::SignedInUserName() const {
684  return username_;
685}
686
687bool GCMProfileService::IsGCMClientReady() const {
688  return gcm_client_ready_;
689}
690
691void 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
704void 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
718void GCMProfileService::GoogleSigninSucceeded(const std::string& username,
719                                              const std::string& password) {
720  if (GetGCMEnabledState(profile_) == ALWAYS_ENABLED)
721    EnsureLoaded();
722}
723
724void GCMProfileService::GoogleSignedOut(const std::string& username) {
725  CheckOut();
726}
727
728void 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
758void 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
770void 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
785void GCMProfileService::ResetGCMClient() {
786  content::BrowserThread::PostTask(
787      content::BrowserThread::IO,
788      FROM_HERE,
789      base::Bind(&GCMProfileService::IOWorker::Reset, io_worker_));
790}
791
792GCMClient::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
803bool 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
809void 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
826void 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
840void 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
858void 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
869void 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
879void 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
891void 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
901GCMAppHandler* 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
907void 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
915