Upstream version 5.34.92.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 "base/base64.h"
8 #include "base/logging.h"
9 #include "base/prefs/pref_service.h"
10 #include "base/strings/string_number_conversions.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/extensions/extension_service.h"
13 #include "chrome/browser/extensions/extension_system.h"
14 #include "chrome/browser/extensions/state_store.h"
15 #include "chrome/browser/services/gcm/gcm_client_factory.h"
16 #include "chrome/browser/services/gcm/gcm_event_router.h"
17 #include "chrome/browser/signin/signin_manager.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/common/chrome_version_info.h"
20 #include "chrome/common/pref_names.h"
21 #include "components/user_prefs/pref_registry_syncable.h"
22 #include "components/webdata/encryptor/encryptor.h"
23 #include "content/public/browser/browser_thread.h"
24 #include "content/public/browser/notification_details.h"
25 #include "content/public/browser/notification_source.h"
26 #include "extensions/common/extension.h"
27
28 using extensions::Extension;
29
30 namespace gcm {
31
32 const char kRegistrationKey[] = "gcm.registration";
33 const char kSendersKey[] = "senders";
34 const char kRegistrationIDKey[] = "reg_id";
35
36 // Helper class to save tasks to run until we're ready to execute them.
37 class GCMProfileService::DelayedTaskController {
38  public:
39   DelayedTaskController();
40   ~DelayedTaskController();
41
42   // Adds an app to the tracking list. It will be first marked as not ready.
43   // Tasks will be queued for delay execution until the app is marked as ready.
44   void AddApp(const std::string& app_id);
45
46   // Removes the app from the tracking list.
47   void RemoveApp(const std::string& app_id);
48
49   // Adds a task that will be invoked once we're ready.
50   void AddTask(const std::string& app_id, base::Closure task);
51
52   // Marks that GCMClient is ready.
53   void SetGCMClientReady();
54
55   // Marks that the app is ready to have operations performed.
56   void SetAppReady(const std::string& app_id);
57
58   // Returns true if it is ready to perform operations for an app.
59   bool CanRunTaskWithoutDelay(const std::string& app_id) const;
60
61   // Returns true if the app has been tracked for readiness.
62   bool IsAppTracked(const std::string& app_id) const;
63
64  private:
65   struct AppTaskQueue {
66     AppTaskQueue();
67     ~AppTaskQueue();
68
69     // The flag that indicates if GCMProfileService completes loading the
70     // persistent data for the app.
71     bool ready;
72
73     // Tasks to be invoked upon ready.
74     std::vector<base::Closure> tasks;
75   };
76
77   void RunTasks(AppTaskQueue* task_queue);
78
79   // Flag that indicates that GCMClient is ready.
80   bool gcm_client_ready_;
81
82   // Map from app_id to callbacks.
83   typedef std::map<std::string, AppTaskQueue*> DelayedTaskMap;
84   DelayedTaskMap delayed_task_map_;
85 };
86
87 GCMProfileService::DelayedTaskController::AppTaskQueue::AppTaskQueue()
88     : ready(false) {
89 }
90
91 GCMProfileService::DelayedTaskController::AppTaskQueue::~AppTaskQueue() {
92 }
93
94 GCMProfileService::DelayedTaskController::DelayedTaskController()
95     : gcm_client_ready_(false) {
96 }
97
98 GCMProfileService::DelayedTaskController::~DelayedTaskController() {
99   for (DelayedTaskMap::const_iterator iter = delayed_task_map_.begin();
100        iter != delayed_task_map_.end(); ++iter) {
101     delete iter->second;
102   }
103 }
104
105 void GCMProfileService::DelayedTaskController::AddApp(
106     const std::string& app_id) {
107   DCHECK(delayed_task_map_.find(app_id) == delayed_task_map_.end());
108   delayed_task_map_[app_id] = new AppTaskQueue;
109 }
110
111 void GCMProfileService::DelayedTaskController::RemoveApp(
112     const std::string& app_id) {
113   delayed_task_map_.erase(app_id);
114 }
115
116 void GCMProfileService::DelayedTaskController::AddTask(
117     const std::string& app_id, base::Closure task) {
118   DelayedTaskMap::const_iterator iter = delayed_task_map_.find(app_id);
119   DCHECK(iter != delayed_task_map_.end());
120   iter->second->tasks.push_back(task);
121 }
122
123 void GCMProfileService::DelayedTaskController::SetGCMClientReady() {
124   gcm_client_ready_ = true;
125
126   for (DelayedTaskMap::iterator iter = delayed_task_map_.begin();
127        iter != delayed_task_map_.end();) {
128     if (iter->second->ready) {
129       AppTaskQueue* task_queue = iter->second;
130       RunTasks(task_queue);
131       delete task_queue;
132       delayed_task_map_.erase(iter++);
133     } else {
134       ++iter;
135     }
136   }
137 }
138
139 void GCMProfileService::DelayedTaskController::SetAppReady(
140     const std::string& app_id) {
141   DelayedTaskMap::iterator iter = delayed_task_map_.find(app_id);
142   DCHECK(iter != delayed_task_map_.end());
143
144   AppTaskQueue* task_queue = iter->second;
145   DCHECK(task_queue);
146   task_queue->ready = true;
147
148   if (gcm_client_ready_) {
149     RunTasks(task_queue);
150     delete task_queue;
151     delayed_task_map_.erase(iter);
152   }
153 }
154
155 bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay(
156     const std::string& app_id) const {
157   if (!gcm_client_ready_)
158     return false;
159   DelayedTaskMap::const_iterator iter = delayed_task_map_.find(app_id);
160   if (iter == delayed_task_map_.end())
161     return true;
162   return iter->second->ready;
163 }
164
165 bool GCMProfileService::DelayedTaskController::IsAppTracked(
166     const std::string& app_id) const {
167   return delayed_task_map_.find(app_id) != delayed_task_map_.end();
168 }
169
170 void GCMProfileService::DelayedTaskController::RunTasks(
171     AppTaskQueue* task_queue) {
172   DCHECK(gcm_client_ready_ && task_queue->ready);
173
174   for (size_t i = 0; i < task_queue->tasks.size(); ++i)
175     task_queue->tasks[i].Run();
176   task_queue->tasks.clear();
177 }
178
179 class GCMProfileService::IOWorker
180     : public GCMClient::Delegate,
181       public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{
182  public:
183   // Called on UI thread.
184   explicit IOWorker(const base::WeakPtr<GCMProfileService>& service);
185
186   // Overridden from GCMClient::Delegate:
187   // Called on IO thread.
188   virtual void OnCheckInFinished(const GCMClient::CheckinInfo& checkin_info,
189                                  GCMClient::Result result) OVERRIDE;
190   virtual void OnRegisterFinished(const std::string& app_id,
191                                   const std::string& registration_id,
192                                   GCMClient::Result result) OVERRIDE;
193   virtual void OnSendFinished(const std::string& app_id,
194                               const std::string& message_id,
195                               GCMClient::Result result) OVERRIDE;
196   virtual void OnMessageReceived(
197       const std::string& app_id,
198       const GCMClient::IncomingMessage& message) OVERRIDE;
199   virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
200   virtual void OnMessageSendError(const std::string& app_id,
201                                   const std::string& message_id,
202                                   GCMClient::Result result) OVERRIDE;
203   virtual GCMClient::CheckinInfo GetCheckinInfo() const OVERRIDE;
204   virtual void OnLoadingCompleted() OVERRIDE;
205
206   // Called on IO thread.
207   void Initialize();
208   void SetUser(const std::string& username);
209   void RemoveUser();
210   void CheckIn();
211   void SetCheckinInfo(const GCMClient::CheckinInfo& checkin_info);
212   void CheckOut();
213   void Register(const std::string& app_id,
214                 const std::vector<std::string>& sender_ids,
215                 const std::string& cert);
216   void Unregister(const std::string& app_id);
217   void Send(const std::string& app_id,
218             const std::string& receiver_id,
219             const GCMClient::OutgoingMessage& message);
220
221  private:
222   friend class base::RefCountedThreadSafe<IOWorker>;
223   virtual ~IOWorker();
224
225   const base::WeakPtr<GCMProfileService> service_;
226
227   // Not owned.
228   GCMClient* gcm_client_;
229
230   // The username (email address) of the signed-in user.
231   std::string username_;
232
233   // The checkin info obtained from the server for the signed in user associated
234   // with the profile.
235   GCMClient::CheckinInfo checkin_info_;
236 };
237
238 GCMProfileService::IOWorker::IOWorker(
239     const base::WeakPtr<GCMProfileService>& service)
240     : service_(service),
241       gcm_client_(NULL) {
242   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
243 }
244
245 GCMProfileService::IOWorker::~IOWorker() {
246 }
247
248 void GCMProfileService::IOWorker::Initialize() {
249   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
250
251   gcm_client_ = GCMClientFactory::GetClient();
252
253   content::BrowserThread::PostTask(
254       content::BrowserThread::UI,
255       FROM_HERE,
256       base::Bind(&GCMProfileService::CheckGCMClientLoadingFinished,
257                  service_,
258                  gcm_client_->IsLoading()));
259 }
260
261 void GCMProfileService::IOWorker::OnCheckInFinished(
262     const GCMClient::CheckinInfo& checkin_info,
263     GCMClient::Result result) {
264   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
265
266   checkin_info_ = checkin_info;
267
268   content::BrowserThread::PostTask(
269       content::BrowserThread::UI,
270       FROM_HERE,
271       base::Bind(&GCMProfileService::CheckInFinished,
272                  service_,
273                  checkin_info_,
274                  result));
275 }
276
277 void GCMProfileService::IOWorker::OnRegisterFinished(
278     const std::string& app_id,
279     const std::string& registration_id,
280     GCMClient::Result result) {
281   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
282
283   content::BrowserThread::PostTask(
284       content::BrowserThread::UI,
285       FROM_HERE,
286       base::Bind(&GCMProfileService::RegisterFinished,
287                  service_,
288                  app_id,
289                  registration_id,
290                  result));
291 }
292
293 void GCMProfileService::IOWorker::OnSendFinished(
294     const std::string& app_id,
295     const std::string& message_id,
296     GCMClient::Result result) {
297   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
298
299   content::BrowserThread::PostTask(
300       content::BrowserThread::UI,
301       FROM_HERE,
302       base::Bind(&GCMProfileService::SendFinished,
303                  service_,
304                  app_id,
305                  message_id,
306                  result));
307 }
308
309 void GCMProfileService::IOWorker::OnMessageReceived(
310     const std::string& app_id,
311     const GCMClient::IncomingMessage& message) {
312   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
313
314   content::BrowserThread::PostTask(
315       content::BrowserThread::UI,
316       FROM_HERE,
317       base::Bind(&GCMProfileService::MessageReceived,
318                  service_,
319                  app_id,
320                  message));
321 }
322
323 void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
324   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
325
326   content::BrowserThread::PostTask(
327       content::BrowserThread::UI,
328       FROM_HERE,
329       base::Bind(&GCMProfileService::MessagesDeleted,
330                  service_,
331                  app_id));
332 }
333
334 void GCMProfileService::IOWorker::OnMessageSendError(
335     const std::string& app_id,
336     const std::string& message_id,
337     GCMClient::Result result) {
338   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
339
340   content::BrowserThread::PostTask(
341       content::BrowserThread::UI,
342       FROM_HERE,
343       base::Bind(&GCMProfileService::MessageSendError,
344                  service_,
345                  app_id,
346                  message_id,
347                  result));
348 }
349
350 GCMClient::CheckinInfo GCMProfileService::IOWorker::GetCheckinInfo() const {
351   return checkin_info_;
352 }
353
354 void GCMProfileService::IOWorker::OnLoadingCompleted() {
355   content::BrowserThread::PostTask(
356       content::BrowserThread::UI,
357       FROM_HERE,
358       base::Bind(&GCMProfileService::GCMClientLoadingFinished,
359                  service_));
360 }
361
362 void GCMProfileService::IOWorker::SetUser(const std::string& username) {
363   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
364   DCHECK(username_.empty() && !username.empty());
365
366   username_ = username;
367   gcm_client_->SetUserDelegate(username_, this);
368 }
369
370 void GCMProfileService::IOWorker::RemoveUser() {
371   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
372
373   if (username_.empty())
374     return;
375   gcm_client_->SetUserDelegate(username_, NULL);
376   username_.clear();
377 }
378
379 void GCMProfileService::IOWorker::CheckIn() {
380   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
381
382   gcm_client_->CheckIn(username_);
383 }
384
385 void GCMProfileService::IOWorker::SetCheckinInfo(
386     const GCMClient::CheckinInfo& checkin_info) {
387   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
388
389   checkin_info_ = checkin_info;
390 }
391
392 void GCMProfileService::IOWorker::CheckOut() {
393   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
394
395   checkin_info_.Reset();
396   RemoveUser();
397 }
398
399 void GCMProfileService::IOWorker::Register(
400     const std::string& app_id,
401     const std::vector<std::string>& sender_ids,
402     const std::string& cert) {
403   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
404   DCHECK(!username_.empty() && checkin_info_.IsValid());
405
406   gcm_client_->Register(username_, app_id, cert, sender_ids);
407 }
408
409 void GCMProfileService::IOWorker::Unregister(const std::string& app_id) {
410   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
411   DCHECK(!username_.empty() && checkin_info_.IsValid());
412
413   gcm_client_->Unregister(username_, app_id);
414 }
415
416 void GCMProfileService::IOWorker::Send(
417     const std::string& app_id,
418     const std::string& receiver_id,
419     const GCMClient::OutgoingMessage& message) {
420   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
421   DCHECK(!username_.empty() && checkin_info_.IsValid());
422
423   gcm_client_->Send(username_, app_id, receiver_id, message);
424 }
425
426 GCMProfileService::RegistrationInfo::RegistrationInfo() {
427 }
428
429 GCMProfileService::RegistrationInfo::~RegistrationInfo() {
430 }
431
432 bool GCMProfileService::RegistrationInfo::IsValid() const {
433   return !sender_ids.empty() && !registration_id.empty();
434 }
435
436 bool GCMProfileService::enable_gcm_for_testing_ = false;
437
438 // static
439 bool GCMProfileService::IsGCMEnabled(Profile* profile) {
440   // GCM is not enabled in incognito mode.
441   if (profile->IsOffTheRecord())
442     return false;
443
444   if (enable_gcm_for_testing_)
445     return true;
446
447   // GCM support is only enabled for Canary/Dev builds.
448   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
449   return channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
450          channel == chrome::VersionInfo::CHANNEL_CANARY ||
451          channel == chrome::VersionInfo::CHANNEL_DEV;
452 }
453
454 // static
455 void GCMProfileService::RegisterProfilePrefs(
456     user_prefs::PrefRegistrySyncable* registry) {
457   registry->RegisterUint64Pref(
458       prefs::kGCMUserAccountID,
459       0,
460       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
461   registry->RegisterStringPref(
462       prefs::kGCMUserToken,
463       "",
464       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
465 }
466
467 GCMProfileService::GCMProfileService(Profile* profile)
468     : profile_(profile),
469       testing_delegate_(NULL),
470       weak_ptr_factory_(this) {
471   DCHECK(!profile->IsOffTheRecord());
472 }
473
474 GCMProfileService::~GCMProfileService() {
475   if (username_.empty())
476     return;
477   content::BrowserThread::PostTask(
478       content::BrowserThread::IO,
479       FROM_HERE,
480       base::Bind(&GCMProfileService::IOWorker::RemoveUser,
481                  io_worker_));
482 }
483
484 void GCMProfileService::Initialize() {
485   delayed_task_controller_.reset(new DelayedTaskController);
486
487   // This has to be done first since CheckIn depends on it.
488   io_worker_ = new IOWorker(weak_ptr_factory_.GetWeakPtr());
489
490   // This initializes GCMClient and also does the check to find out if GCMClient
491   // has finished the loading.
492   content::BrowserThread::PostTask(
493       content::BrowserThread::IO,
494       FROM_HERE,
495       base::Bind(&GCMProfileService::IOWorker::Initialize, io_worker_));
496
497   // In case that the profile has been signed in before GCMProfileService is
498   // created.
499   SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
500   if (manager)
501     AddUser(manager->GetAuthenticatedUsername());
502
503   registrar_.Add(this,
504                  chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
505                  content::Source<Profile>(profile_));
506   registrar_.Add(this,
507                  chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
508                  content::Source<Profile>(profile_));
509   // TODO(jianli): move extension specific logic out of GCMProfileService.
510   registrar_.Add(this,
511                  chrome::NOTIFICATION_EXTENSION_LOADED,
512                  content::Source<Profile>(profile_));
513   registrar_.Add(this,
514                  chrome:: NOTIFICATION_EXTENSION_UNINSTALLED,
515                  content::Source<Profile>(profile_));
516 }
517
518 void GCMProfileService::Register(const std::string& app_id,
519                                  const std::vector<std::string>& sender_ids,
520                                  const std::string& cert,
521                                  RegisterCallback callback) {
522   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
523   DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null());
524
525   // If the profile was not signed in, bail out.
526   if (username_.empty()) {
527     callback.Run(std::string(), GCMClient::NOT_SIGNED_IN);
528     return;
529   }
530
531   // If previous register operation is still in progress, bail out.
532   if (register_callbacks_.find(app_id) != register_callbacks_.end()) {
533     callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
534     return;
535   }
536
537   // Delay the register operation until the loading is done.
538   if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) {
539     delayed_task_controller_->AddTask(
540         app_id,
541         base::Bind(&GCMProfileService::DoRegister,
542                    weak_ptr_factory_.GetWeakPtr(),
543                    app_id,
544                    sender_ids,
545                    cert,
546                    callback));
547     return;
548   }
549
550   DoRegister(app_id, sender_ids, cert, callback);
551 }
552
553 void GCMProfileService::DoRegister(const std::string& app_id,
554                                    const std::vector<std::string>& sender_ids,
555                                    const std::string& cert,
556                                    RegisterCallback callback) {
557   // Normalize the sender IDs by making them sorted.
558   std::vector<std::string> normalized_sender_ids = sender_ids;
559   std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
560
561   // If the same sender ids is provided, return the cached registration ID
562   // directly.
563   RegistrationInfoMap::const_iterator registration_info_iter =
564       registration_info_map_.find(app_id);
565   if (registration_info_iter != registration_info_map_.end() &&
566       registration_info_iter->second.sender_ids == normalized_sender_ids) {
567     callback.Run(registration_info_iter->second.registration_id,
568                  GCMClient::SUCCESS);
569     return;
570   }
571
572   // Cache the sender IDs. The registration ID will be filled when the
573   // registration completes.
574   RegistrationInfo registration_info;
575   registration_info.sender_ids = normalized_sender_ids;
576   registration_info_map_[app_id] = registration_info;
577
578   register_callbacks_[app_id] = callback;
579
580   content::BrowserThread::PostTask(
581       content::BrowserThread::IO,
582       FROM_HERE,
583       base::Bind(&GCMProfileService::IOWorker::Register,
584                  io_worker_,
585                  app_id,
586                  normalized_sender_ids,
587                  cert));
588 }
589
590 void GCMProfileService::Send(const std::string& app_id,
591                              const std::string& receiver_id,
592                              const GCMClient::OutgoingMessage& message,
593                              SendCallback callback) {
594   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
595   DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null());
596
597   // If the profile was not signed in, bail out.
598   if (username_.empty()) {
599     callback.Run(std::string(), GCMClient::NOT_SIGNED_IN);
600     return;
601   }
602
603   // If the message with send ID is still in progress, bail out.
604   std::pair<std::string, std::string> key(app_id, message.id);
605   if (send_callbacks_.find(key) != send_callbacks_.end()) {
606     callback.Run(message.id, GCMClient::INVALID_PARAMETER);
607     return;
608   }
609   send_callbacks_[key] = callback;
610
611   // Delay the send operation until all the loadings are done.
612   if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) {
613     delayed_task_controller_->AddTask(
614         app_id,
615         base::Bind(&GCMProfileService::DoSend,
616                    weak_ptr_factory_.GetWeakPtr(),
617                    app_id,
618                    receiver_id,
619                    message));
620     return;
621   }
622
623   DoSend(app_id, receiver_id, message);
624 }
625
626 void GCMProfileService::DoSend(const std::string& app_id,
627                                const std::string& receiver_id,
628                                const GCMClient::OutgoingMessage& message) {
629   content::BrowserThread::PostTask(
630       content::BrowserThread::IO,
631       FROM_HERE,
632       base::Bind(&GCMProfileService::IOWorker::Send,
633                  io_worker_,
634                  app_id,
635                  receiver_id,
636                  message));
637 }
638
639 void GCMProfileService::Observe(int type,
640                                 const content::NotificationSource& source,
641                                 const content::NotificationDetails& details) {
642   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
643
644   switch (type) {
645     case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: {
646       const GoogleServiceSigninSuccessDetails* signin_details =
647           content::Details<GoogleServiceSigninSuccessDetails>(details).ptr();
648       AddUser(signin_details->username);
649       break;
650     }
651     case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
652       username_.clear();
653       RemoveUser();
654       break;
655     case chrome::NOTIFICATION_EXTENSION_LOADED: {
656       extensions::Extension* extension =
657           content::Details<extensions::Extension>(details).ptr();
658       // No need to load the persisted registration info if the extension does
659       // not have the GCM permission.
660       if (extension->HasAPIPermission(extensions::APIPermission::kGcm))
661         ReadRegistrationInfo(extension->id());
662       break;
663     }
664     case chrome:: NOTIFICATION_EXTENSION_UNINSTALLED: {
665       extensions::Extension* extension =
666           content::Details<extensions::Extension>(details).ptr();
667       Unregister(extension->id());
668       break;
669     }
670     default:
671       NOTREACHED();
672   }
673 }
674
675 void GCMProfileService::AddUser(const std::string& username) {
676   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
677
678   // If re-signin occurs due to password change, there is no need to do
679   // check-in again.
680   if (username_ == username || username.empty())
681     return;
682   username_ = username;
683
684   content::BrowserThread::PostTask(
685       content::BrowserThread::IO,
686       FROM_HERE,
687       base::Bind(&GCMProfileService::IOWorker::SetUser,
688                  io_worker_,
689                  username_));
690
691   // Try to read persisted check-in info from the profile's prefs store.
692   PrefService* pref_service = profile_->GetPrefs();
693   uint64 android_id = pref_service->GetUint64(prefs::kGCMUserAccountID);
694   std::string base64_token = pref_service->GetString(prefs::kGCMUserToken);
695   std::string encrypted_secret;
696   base::Base64Decode(base::StringPiece(base64_token), &encrypted_secret);
697   if (android_id && !encrypted_secret.empty()) {
698     std::string decrypted_secret;
699     Encryptor::DecryptString(encrypted_secret, &decrypted_secret);
700     uint64 secret = 0;
701     if (base::StringToUint64(decrypted_secret, &secret) && secret) {
702       GCMClient::CheckinInfo checkin_info;
703       checkin_info.android_id = android_id;
704       checkin_info.secret = secret;
705       content::BrowserThread::PostTask(
706           content::BrowserThread::IO,
707           FROM_HERE,
708           base::Bind(&GCMProfileService::IOWorker::SetCheckinInfo,
709                      io_worker_,
710                      checkin_info));
711
712       if (testing_delegate_)
713         testing_delegate_->CheckInFinished(checkin_info, GCMClient::SUCCESS);
714
715       return;
716     }
717   }
718
719   content::BrowserThread::PostTask(
720       content::BrowserThread::IO,
721       FROM_HERE,
722       base::Bind(&GCMProfileService::IOWorker::CheckIn, io_worker_));
723 }
724
725 void GCMProfileService::RemoveUser() {
726   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
727
728   PrefService* pref_service = profile_->GetPrefs();
729   pref_service->ClearPref(prefs::kGCMUserAccountID);
730   pref_service->ClearPref(prefs::kGCMUserToken);
731
732   content::BrowserThread::PostTask(
733       content::BrowserThread::IO,
734       FROM_HERE,
735       base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
736 }
737
738 void GCMProfileService::Unregister(const std::string& app_id) {
739   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
740
741   // This is unlikely to happen because the app will not be uninstalled before
742   // the asynchronous extension function completes.
743   DCHECK(register_callbacks_.find(app_id) == register_callbacks_.end());
744
745   // Remove the cached registration info. If not found, there is no need to
746   // ask the server to unregister it.
747   RegistrationInfoMap::iterator registration_info_iter =
748       registration_info_map_.find(app_id);
749   if (registration_info_iter == registration_info_map_.end())
750     return;
751   registration_info_map_.erase(registration_info_iter);
752
753   // Remove the persisted registration info.
754   DeleteRegistrationInfo(app_id);
755
756   // No need to track the app any more.
757   delayed_task_controller_->RemoveApp(app_id);
758
759   // Ask the server to unregister it. There could be a small chance that the
760   // unregister request fails. If this occurs, it does not bring any harm since
761   // we simply reject the messages/events received from the server.
762   content::BrowserThread::PostTask(
763       content::BrowserThread::IO,
764       FROM_HERE,
765       base::Bind(&GCMProfileService::IOWorker::Unregister,
766                  io_worker_,
767                  app_id));
768 }
769
770 void GCMProfileService::CheckInFinished(
771     const GCMClient::CheckinInfo& checkin_info, GCMClient::Result result) {
772   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
773
774   // Save the check-in info into the profile's prefs store.
775   PrefService* pref_service = profile_->GetPrefs();
776   pref_service->SetUint64(prefs::kGCMUserAccountID, checkin_info.android_id);
777
778   // Encrypt the secret for persisting purpose.
779   std::string encrypted_secret;
780   Encryptor::EncryptString(base::Uint64ToString(checkin_info.secret),
781                            &encrypted_secret);
782
783   // |encrypted_secret| might contain binary data and our prefs store only
784   // works for the text.
785   std::string base64_token;
786   base::Base64Encode(encrypted_secret, &base64_token);
787   pref_service->SetString(prefs::kGCMUserToken, base64_token);
788
789   if (testing_delegate_)
790     testing_delegate_->CheckInFinished(checkin_info, result);
791 }
792
793 void GCMProfileService::RegisterFinished(const std::string& app_id,
794                                          const std::string& registration_id,
795                                          GCMClient::Result result) {
796   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
797
798   std::map<std::string, RegisterCallback>::iterator callback_iter =
799       register_callbacks_.find(app_id);
800   if (callback_iter == register_callbacks_.end()) {
801     // The callback could have been removed when the app is uninstalled.
802     return;
803   }
804
805   // Cache the registration ID if the registration succeeds. Otherwise,
806   // removed the cached info.
807   RegistrationInfoMap::iterator registration_info_iter =
808       registration_info_map_.find(app_id);
809   // This is unlikely to happen because the app will not be uninstalled before
810   // the asynchronous extension function completes.
811   DCHECK(registration_info_iter != registration_info_map_.end());
812   if (result == GCMClient::SUCCESS) {
813     registration_info_iter->second.registration_id = registration_id;
814     WriteRegistrationInfo(app_id);
815   } else {
816     registration_info_map_.erase(registration_info_iter);
817   }
818
819   RegisterCallback callback = callback_iter->second;
820   register_callbacks_.erase(callback_iter);
821   callback.Run(registration_id, result);
822 }
823
824 void GCMProfileService::SendFinished(const std::string& app_id,
825                                      const std::string& message_id,
826                                      GCMClient::Result result) {
827   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
828
829   std::map<std::pair<std::string, std::string>, SendCallback>::iterator
830       callback_iter = send_callbacks_.find(
831           std::pair<std::string, std::string>(app_id, message_id));
832   if (callback_iter == send_callbacks_.end()) {
833     // The callback could have been removed when the app is uninstalled.
834     return;
835   }
836
837   SendCallback callback = callback_iter->second;
838   send_callbacks_.erase(callback_iter);
839   callback.Run(message_id, result);
840 }
841
842 void GCMProfileService::MessageReceived(const std::string& app_id,
843                                         GCMClient::IncomingMessage message) {
844   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
845
846   // Drop the event if signed out.
847   if (username_.empty())
848     return;
849
850   GetEventRouter(app_id)->OnMessage(app_id, message);
851 }
852
853 void GCMProfileService::MessagesDeleted(const std::string& app_id) {
854   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
855
856   // Drop the event if signed out.
857   if (username_.empty())
858     return;
859
860   GetEventRouter(app_id)->OnMessagesDeleted(app_id);
861 }
862
863 void GCMProfileService::MessageSendError(const std::string& app_id,
864                                          const std::string& message_id,
865                                          GCMClient::Result result) {
866   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
867
868   // Drop the event if signed out.
869   if (username_.empty())
870     return;
871
872   GetEventRouter(app_id)->OnSendError(app_id, message_id, result);
873 }
874
875 void GCMProfileService::CheckGCMClientLoadingFinished(bool is_loading) {
876   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
877
878   if (!is_loading)
879     delayed_task_controller_->SetGCMClientReady();
880 }
881
882 void GCMProfileService::GCMClientLoadingFinished() {
883   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
884
885   delayed_task_controller_->SetGCMClientReady();
886 }
887
888 GCMEventRouter* GCMProfileService::GetEventRouter(const std::string& app_id) {
889   if (testing_delegate_ && testing_delegate_->GetEventRouter())
890     return testing_delegate_->GetEventRouter();
891   // TODO(fgorski): check and create the event router for JS routing.
892   return js_event_router_.get();
893 }
894
895 void GCMProfileService::DeleteRegistrationInfo(const std::string& app_id) {
896   extensions::StateStore* storage =
897       extensions::ExtensionSystem::Get(profile_)->state_store();
898   DCHECK(storage);
899
900   storage->RemoveExtensionValue(app_id, kRegistrationKey);
901 }
902
903 void GCMProfileService::WriteRegistrationInfo(const std::string& app_id) {
904   extensions::StateStore* storage =
905       extensions::ExtensionSystem::Get(profile_)->state_store();
906   DCHECK(storage);
907
908   RegistrationInfoMap::const_iterator registration_info_iter =
909       registration_info_map_.find(app_id);
910   if (registration_info_iter == registration_info_map_.end())
911     return;
912   const RegistrationInfo& registration_info = registration_info_iter->second;
913
914   scoped_ptr<base::ListValue> senders_list(new base::ListValue());
915   for (std::vector<std::string>::const_iterator senders_iter =
916            registration_info.sender_ids.begin();
917        senders_iter != registration_info.sender_ids.end();
918        ++senders_iter) {
919     senders_list->AppendString(*senders_iter);
920   }
921
922   scoped_ptr<base::DictionaryValue> registration_info_dict(
923       new base::DictionaryValue());
924   registration_info_dict->Set(kSendersKey, senders_list.release());
925   registration_info_dict->SetString(kRegistrationIDKey,
926                                     registration_info.registration_id);
927
928   storage->SetExtensionValue(
929       app_id, kRegistrationKey, registration_info_dict.PassAs<base::Value>());
930 }
931
932 void GCMProfileService::ReadRegistrationInfo(const std::string& app_id) {
933   // This function can be called more than once when the app is allowed in
934   // incognito and the extension service reloads the app.
935   if (delayed_task_controller_->IsAppTracked(app_id))
936     return;
937
938   delayed_task_controller_->AddApp(app_id);
939
940   extensions::StateStore* storage =
941       extensions::ExtensionSystem::Get(profile_)->state_store();
942   DCHECK(storage);
943   storage->GetExtensionValue(
944       app_id,
945       kRegistrationKey,
946       base::Bind(
947           &GCMProfileService::ReadRegistrationInfoFinished,
948           weak_ptr_factory_.GetWeakPtr(),
949           app_id));
950 }
951
952 void GCMProfileService::ReadRegistrationInfoFinished(
953     const std::string& app_id,
954     scoped_ptr<base::Value> value) {
955   RegistrationInfo registration_info;
956   if (value &&
957      !ParsePersistedRegistrationInfo(value.Pass(), &registration_info)) {
958     // Delete the persisted data if it is corrupted.
959     DeleteRegistrationInfo(app_id);
960   }
961
962   if (registration_info.IsValid())
963     registration_info_map_[app_id] = registration_info;
964
965   delayed_task_controller_->SetAppReady(app_id);
966 }
967
968 bool GCMProfileService::ParsePersistedRegistrationInfo(
969     scoped_ptr<base::Value> value,
970     RegistrationInfo* registration_info) {
971   base::DictionaryValue* dict = NULL;
972   if (!value.get() || !value->GetAsDictionary(&dict))
973     return false;
974
975   if (!dict->GetString(kRegistrationIDKey, &registration_info->registration_id))
976     return false;
977
978   const base::ListValue* senders_list = NULL;
979   if (!dict->GetList(kSendersKey, &senders_list) || !senders_list->GetSize())
980     return false;
981   for (size_t i = 0; i < senders_list->GetSize(); ++i) {
982     std::string sender;
983     if (!senders_list->GetString(i, &sender))
984       return false;
985     registration_info->sender_ids.push_back(sender);
986   }
987
988   return true;
989 }
990
991 // static
992 const char* GCMProfileService::GetPersistentRegisterKeyForTesting() {
993   return kRegistrationKey;
994 }
995
996 }  // namespace gcm