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.
5 #include "chrome/browser/services/gcm/gcm_profile_service.h"
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"
28 using extensions::Extension;
32 const char kRegistrationKey[] = "gcm.registration";
33 const char kSendersKey[] = "senders";
34 const char kRegistrationIDKey[] = "reg_id";
36 // Helper class to save tasks to run until we're ready to execute them.
37 class GCMProfileService::DelayedTaskController {
39 DelayedTaskController();
40 ~DelayedTaskController();
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);
46 // Removes the app from the tracking list.
47 void RemoveApp(const std::string& app_id);
49 // Adds a task that will be invoked once we're ready.
50 void AddTask(const std::string& app_id, base::Closure task);
52 // Marks that GCMClient is ready.
53 void SetGCMClientReady();
55 // Marks that the app is ready to have operations performed.
56 void SetAppReady(const std::string& app_id);
58 // Returns true if it is ready to perform operations for an app.
59 bool CanRunTaskWithoutDelay(const std::string& app_id) const;
61 // Returns true if the app has been tracked for readiness.
62 bool IsAppTracked(const std::string& app_id) const;
69 // The flag that indicates if GCMProfileService completes loading the
70 // persistent data for the app.
73 // Tasks to be invoked upon ready.
74 std::vector<base::Closure> tasks;
77 void RunTasks(AppTaskQueue* task_queue);
79 // Flag that indicates that GCMClient is ready.
80 bool gcm_client_ready_;
82 // Map from app_id to callbacks.
83 typedef std::map<std::string, AppTaskQueue*> DelayedTaskMap;
84 DelayedTaskMap delayed_task_map_;
87 GCMProfileService::DelayedTaskController::AppTaskQueue::AppTaskQueue()
91 GCMProfileService::DelayedTaskController::AppTaskQueue::~AppTaskQueue() {
94 GCMProfileService::DelayedTaskController::DelayedTaskController()
95 : gcm_client_ready_(false) {
98 GCMProfileService::DelayedTaskController::~DelayedTaskController() {
99 for (DelayedTaskMap::const_iterator iter = delayed_task_map_.begin();
100 iter != delayed_task_map_.end(); ++iter) {
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;
111 void GCMProfileService::DelayedTaskController::RemoveApp(
112 const std::string& app_id) {
113 delayed_task_map_.erase(app_id);
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);
123 void GCMProfileService::DelayedTaskController::SetGCMClientReady() {
124 gcm_client_ready_ = true;
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);
132 delayed_task_map_.erase(iter++);
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());
144 AppTaskQueue* task_queue = iter->second;
146 task_queue->ready = true;
148 if (gcm_client_ready_) {
149 RunTasks(task_queue);
151 delayed_task_map_.erase(iter);
155 bool GCMProfileService::DelayedTaskController::CanRunTaskWithoutDelay(
156 const std::string& app_id) const {
157 if (!gcm_client_ready_)
159 DelayedTaskMap::const_iterator iter = delayed_task_map_.find(app_id);
160 if (iter == delayed_task_map_.end())
162 return iter->second->ready;
165 bool GCMProfileService::DelayedTaskController::IsAppTracked(
166 const std::string& app_id) const {
167 return delayed_task_map_.find(app_id) != delayed_task_map_.end();
170 void GCMProfileService::DelayedTaskController::RunTasks(
171 AppTaskQueue* task_queue) {
172 DCHECK(gcm_client_ready_ && task_queue->ready);
174 for (size_t i = 0; i < task_queue->tasks.size(); ++i)
175 task_queue->tasks[i].Run();
176 task_queue->tasks.clear();
179 class GCMProfileService::IOWorker
180 : public GCMClient::Delegate,
181 public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{
183 // Called on UI thread.
184 explicit IOWorker(const base::WeakPtr<GCMProfileService>& service);
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;
206 // Called on IO thread.
208 void SetUser(const std::string& username);
211 void SetCheckinInfo(const GCMClient::CheckinInfo& checkin_info);
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);
222 friend class base::RefCountedThreadSafe<IOWorker>;
225 const base::WeakPtr<GCMProfileService> service_;
228 GCMClient* gcm_client_;
230 // The username (email address) of the signed-in user.
231 std::string username_;
233 // The checkin info obtained from the server for the signed in user associated
235 GCMClient::CheckinInfo checkin_info_;
238 GCMProfileService::IOWorker::IOWorker(
239 const base::WeakPtr<GCMProfileService>& service)
242 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
245 GCMProfileService::IOWorker::~IOWorker() {
248 void GCMProfileService::IOWorker::Initialize() {
249 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
251 gcm_client_ = GCMClientFactory::GetClient();
253 content::BrowserThread::PostTask(
254 content::BrowserThread::UI,
256 base::Bind(&GCMProfileService::CheckGCMClientLoadingFinished,
258 gcm_client_->IsLoading()));
261 void GCMProfileService::IOWorker::OnCheckInFinished(
262 const GCMClient::CheckinInfo& checkin_info,
263 GCMClient::Result result) {
264 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
266 checkin_info_ = checkin_info;
268 content::BrowserThread::PostTask(
269 content::BrowserThread::UI,
271 base::Bind(&GCMProfileService::CheckInFinished,
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));
283 content::BrowserThread::PostTask(
284 content::BrowserThread::UI,
286 base::Bind(&GCMProfileService::RegisterFinished,
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));
299 content::BrowserThread::PostTask(
300 content::BrowserThread::UI,
302 base::Bind(&GCMProfileService::SendFinished,
309 void GCMProfileService::IOWorker::OnMessageReceived(
310 const std::string& app_id,
311 const GCMClient::IncomingMessage& message) {
312 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
314 content::BrowserThread::PostTask(
315 content::BrowserThread::UI,
317 base::Bind(&GCMProfileService::MessageReceived,
323 void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
324 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
326 content::BrowserThread::PostTask(
327 content::BrowserThread::UI,
329 base::Bind(&GCMProfileService::MessagesDeleted,
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));
340 content::BrowserThread::PostTask(
341 content::BrowserThread::UI,
343 base::Bind(&GCMProfileService::MessageSendError,
350 GCMClient::CheckinInfo GCMProfileService::IOWorker::GetCheckinInfo() const {
351 return checkin_info_;
354 void GCMProfileService::IOWorker::OnLoadingCompleted() {
355 content::BrowserThread::PostTask(
356 content::BrowserThread::UI,
358 base::Bind(&GCMProfileService::GCMClientLoadingFinished,
362 void GCMProfileService::IOWorker::SetUser(const std::string& username) {
363 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
364 DCHECK(username_.empty() && !username.empty());
366 username_ = username;
367 gcm_client_->SetUserDelegate(username_, this);
370 void GCMProfileService::IOWorker::RemoveUser() {
371 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
373 if (username_.empty())
375 gcm_client_->SetUserDelegate(username_, NULL);
379 void GCMProfileService::IOWorker::CheckIn() {
380 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
382 gcm_client_->CheckIn(username_);
385 void GCMProfileService::IOWorker::SetCheckinInfo(
386 const GCMClient::CheckinInfo& checkin_info) {
387 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
389 checkin_info_ = checkin_info;
392 void GCMProfileService::IOWorker::CheckOut() {
393 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
395 checkin_info_.Reset();
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());
406 gcm_client_->Register(username_, app_id, cert, sender_ids);
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());
413 gcm_client_->Unregister(username_, app_id);
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());
423 gcm_client_->Send(username_, app_id, receiver_id, message);
426 GCMProfileService::RegistrationInfo::RegistrationInfo() {
429 GCMProfileService::RegistrationInfo::~RegistrationInfo() {
432 bool GCMProfileService::RegistrationInfo::IsValid() const {
433 return !sender_ids.empty() && !registration_id.empty();
436 bool GCMProfileService::enable_gcm_for_testing_ = false;
439 bool GCMProfileService::IsGCMEnabled(Profile* profile) {
440 // GCM is not enabled in incognito mode.
441 if (profile->IsOffTheRecord())
444 if (enable_gcm_for_testing_)
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;
455 void GCMProfileService::RegisterProfilePrefs(
456 user_prefs::PrefRegistrySyncable* registry) {
457 registry->RegisterUint64Pref(
458 prefs::kGCMUserAccountID,
460 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
461 registry->RegisterStringPref(
462 prefs::kGCMUserToken,
464 user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
467 GCMProfileService::GCMProfileService(Profile* profile)
469 testing_delegate_(NULL),
470 weak_ptr_factory_(this) {
471 DCHECK(!profile->IsOffTheRecord());
474 GCMProfileService::~GCMProfileService() {
475 if (username_.empty())
477 content::BrowserThread::PostTask(
478 content::BrowserThread::IO,
480 base::Bind(&GCMProfileService::IOWorker::RemoveUser,
484 void GCMProfileService::Initialize() {
485 delayed_task_controller_.reset(new DelayedTaskController);
487 // This has to be done first since CheckIn depends on it.
488 io_worker_ = new IOWorker(weak_ptr_factory_.GetWeakPtr());
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,
495 base::Bind(&GCMProfileService::IOWorker::Initialize, io_worker_));
497 // In case that the profile has been signed in before GCMProfileService is
499 SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
501 AddUser(manager->GetAuthenticatedUsername());
504 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
505 content::Source<Profile>(profile_));
507 chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
508 content::Source<Profile>(profile_));
509 // TODO(jianli): move extension specific logic out of GCMProfileService.
511 chrome::NOTIFICATION_EXTENSION_LOADED,
512 content::Source<Profile>(profile_));
514 chrome:: NOTIFICATION_EXTENSION_UNINSTALLED,
515 content::Source<Profile>(profile_));
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());
525 // If the profile was not signed in, bail out.
526 if (username_.empty()) {
527 callback.Run(std::string(), GCMClient::NOT_SIGNED_IN);
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);
537 // Delay the register operation until the loading is done.
538 if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) {
539 delayed_task_controller_->AddTask(
541 base::Bind(&GCMProfileService::DoRegister,
542 weak_ptr_factory_.GetWeakPtr(),
550 DoRegister(app_id, sender_ids, cert, callback);
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());
561 // If the same sender ids is provided, return the cached registration ID
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,
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;
578 register_callbacks_[app_id] = callback;
580 content::BrowserThread::PostTask(
581 content::BrowserThread::IO,
583 base::Bind(&GCMProfileService::IOWorker::Register,
586 normalized_sender_ids,
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());
597 // If the profile was not signed in, bail out.
598 if (username_.empty()) {
599 callback.Run(std::string(), GCMClient::NOT_SIGNED_IN);
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);
609 send_callbacks_[key] = callback;
611 // Delay the send operation until all the loadings are done.
612 if (!delayed_task_controller_->CanRunTaskWithoutDelay(app_id)) {
613 delayed_task_controller_->AddTask(
615 base::Bind(&GCMProfileService::DoSend,
616 weak_ptr_factory_.GetWeakPtr(),
623 DoSend(app_id, receiver_id, message);
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,
632 base::Bind(&GCMProfileService::IOWorker::Send,
639 void GCMProfileService::Observe(int type,
640 const content::NotificationSource& source,
641 const content::NotificationDetails& details) {
642 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
645 case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: {
646 const GoogleServiceSigninSuccessDetails* signin_details =
647 content::Details<GoogleServiceSigninSuccessDetails>(details).ptr();
648 AddUser(signin_details->username);
651 case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
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());
664 case chrome:: NOTIFICATION_EXTENSION_UNINSTALLED: {
665 extensions::Extension* extension =
666 content::Details<extensions::Extension>(details).ptr();
667 Unregister(extension->id());
675 void GCMProfileService::AddUser(const std::string& username) {
676 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
678 // If re-signin occurs due to password change, there is no need to do
680 if (username_ == username || username.empty())
682 username_ = username;
684 content::BrowserThread::PostTask(
685 content::BrowserThread::IO,
687 base::Bind(&GCMProfileService::IOWorker::SetUser,
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);
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,
708 base::Bind(&GCMProfileService::IOWorker::SetCheckinInfo,
712 if (testing_delegate_)
713 testing_delegate_->CheckInFinished(checkin_info, GCMClient::SUCCESS);
719 content::BrowserThread::PostTask(
720 content::BrowserThread::IO,
722 base::Bind(&GCMProfileService::IOWorker::CheckIn, io_worker_));
725 void GCMProfileService::RemoveUser() {
726 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
728 PrefService* pref_service = profile_->GetPrefs();
729 pref_service->ClearPref(prefs::kGCMUserAccountID);
730 pref_service->ClearPref(prefs::kGCMUserToken);
732 content::BrowserThread::PostTask(
733 content::BrowserThread::IO,
735 base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
738 void GCMProfileService::Unregister(const std::string& app_id) {
739 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
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());
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())
751 registration_info_map_.erase(registration_info_iter);
753 // Remove the persisted registration info.
754 DeleteRegistrationInfo(app_id);
756 // No need to track the app any more.
757 delayed_task_controller_->RemoveApp(app_id);
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,
765 base::Bind(&GCMProfileService::IOWorker::Unregister,
770 void GCMProfileService::CheckInFinished(
771 const GCMClient::CheckinInfo& checkin_info, GCMClient::Result result) {
772 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
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);
778 // Encrypt the secret for persisting purpose.
779 std::string encrypted_secret;
780 Encryptor::EncryptString(base::Uint64ToString(checkin_info.secret),
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);
789 if (testing_delegate_)
790 testing_delegate_->CheckInFinished(checkin_info, result);
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));
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.
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);
816 registration_info_map_.erase(registration_info_iter);
819 RegisterCallback callback = callback_iter->second;
820 register_callbacks_.erase(callback_iter);
821 callback.Run(registration_id, result);
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));
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.
837 SendCallback callback = callback_iter->second;
838 send_callbacks_.erase(callback_iter);
839 callback.Run(message_id, result);
842 void GCMProfileService::MessageReceived(const std::string& app_id,
843 GCMClient::IncomingMessage message) {
844 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
846 // Drop the event if signed out.
847 if (username_.empty())
850 GetEventRouter(app_id)->OnMessage(app_id, message);
853 void GCMProfileService::MessagesDeleted(const std::string& app_id) {
854 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
856 // Drop the event if signed out.
857 if (username_.empty())
860 GetEventRouter(app_id)->OnMessagesDeleted(app_id);
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));
868 // Drop the event if signed out.
869 if (username_.empty())
872 GetEventRouter(app_id)->OnSendError(app_id, message_id, result);
875 void GCMProfileService::CheckGCMClientLoadingFinished(bool is_loading) {
876 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
879 delayed_task_controller_->SetGCMClientReady();
882 void GCMProfileService::GCMClientLoadingFinished() {
883 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
885 delayed_task_controller_->SetGCMClientReady();
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();
895 void GCMProfileService::DeleteRegistrationInfo(const std::string& app_id) {
896 extensions::StateStore* storage =
897 extensions::ExtensionSystem::Get(profile_)->state_store();
900 storage->RemoveExtensionValue(app_id, kRegistrationKey);
903 void GCMProfileService::WriteRegistrationInfo(const std::string& app_id) {
904 extensions::StateStore* storage =
905 extensions::ExtensionSystem::Get(profile_)->state_store();
908 RegistrationInfoMap::const_iterator registration_info_iter =
909 registration_info_map_.find(app_id);
910 if (registration_info_iter == registration_info_map_.end())
912 const RegistrationInfo& registration_info = registration_info_iter->second;
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();
919 senders_list->AppendString(*senders_iter);
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);
928 storage->SetExtensionValue(
929 app_id, kRegistrationKey, registration_info_dict.PassAs<base::Value>());
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))
938 delayed_task_controller_->AddApp(app_id);
940 extensions::StateStore* storage =
941 extensions::ExtensionSystem::Get(profile_)->state_store();
943 storage->GetExtensionValue(
947 &GCMProfileService::ReadRegistrationInfoFinished,
948 weak_ptr_factory_.GetWeakPtr(),
952 void GCMProfileService::ReadRegistrationInfoFinished(
953 const std::string& app_id,
954 scoped_ptr<base::Value> value) {
955 RegistrationInfo registration_info;
957 !ParsePersistedRegistrationInfo(value.Pass(), ®istration_info)) {
958 // Delete the persisted data if it is corrupted.
959 DeleteRegistrationInfo(app_id);
962 if (registration_info.IsValid())
963 registration_info_map_[app_id] = registration_info;
965 delayed_task_controller_->SetAppReady(app_id);
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))
975 if (!dict->GetString(kRegistrationIDKey, ®istration_info->registration_id))
978 const base::ListValue* senders_list = NULL;
979 if (!dict->GetList(kSendersKey, &senders_list) || !senders_list->GetSize())
981 for (size_t i = 0; i < senders_list->GetSize(); ++i) {
983 if (!senders_list->GetString(i, &sender))
985 registration_info->sender_ids.push_back(sender);
992 const char* GCMProfileService::GetPersistentRegisterKeyForTesting() {
993 return kRegistrationKey;