Home | History | Annotate | Download | only in gcm
      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_event_router.h"
     16 #include "chrome/browser/signin/signin_manager.h"
     17 #include "chrome/browser/signin/signin_manager_factory.h"
     18 #include "chrome/common/chrome_version_info.h"
     19 #include "chrome/common/pref_names.h"
     20 #include "components/user_prefs/pref_registry_syncable.h"
     21 #include "components/webdata/encryptor/encryptor.h"
     22 #include "content/public/browser/browser_thread.h"
     23 #include "content/public/browser/notification_details.h"
     24 #include "content/public/browser/notification_source.h"
     25 #include "extensions/common/extension.h"
     26 
     27 using extensions::Extension;
     28 
     29 namespace gcm {
     30 
     31 const char kRegistrationKey[] = "gcm.registration";
     32 const char kSendersKey[] = "senders";
     33 const char kRegistrationIDKey[] = "reg_id";
     34 
     35 class GCMProfileService::IOWorker
     36     : public GCMClient::Delegate,
     37       public base::RefCountedThreadSafe<GCMProfileService::IOWorker>{
     38  public:
     39   explicit IOWorker(const base::WeakPtr<GCMProfileService>& service);
     40 
     41   // Overridden from GCMClient::Delegate:
     42   // Called from IO thread.
     43   virtual void OnCheckInFinished(const GCMClient::CheckInInfo& checkin_info,
     44                                  GCMClient::Result result) OVERRIDE;
     45   virtual void OnRegisterFinished(const std::string& app_id,
     46                                   const std::string& registration_id,
     47                                   GCMClient::Result result) OVERRIDE;
     48   virtual void OnSendFinished(const std::string& app_id,
     49                               const std::string& message_id,
     50                               GCMClient::Result result) OVERRIDE;
     51   virtual void OnMessageReceived(
     52       const std::string& app_id,
     53       const GCMClient::IncomingMessage& message) OVERRIDE;
     54   virtual void OnMessagesDeleted(const std::string& app_id) OVERRIDE;
     55   virtual void OnMessageSendError(const std::string& app_id,
     56                                   const std::string& message_id,
     57                                   GCMClient::Result result) OVERRIDE;
     58   virtual GCMClient::CheckInInfo GetCheckInInfo() const OVERRIDE;
     59   virtual void OnLoadingCompleted() OVERRIDE;
     60   virtual base::TaskRunner* GetFileTaskRunner() OVERRIDE;
     61 
     62   void CheckIn(const std::string& username);
     63   void SetCheckInInfo(GCMClient::CheckInInfo checkin_info);
     64   void CheckOut();
     65   void Register(const std::string& username,
     66                 const std::string& app_id,
     67                 const std::vector<std::string>& sender_ids,
     68                 const std::string& cert);
     69   void Unregister(const std::string& username, const std::string& app_id);
     70   void Send(const std::string& username,
     71             const std::string& app_id,
     72             const std::string& receiver_id,
     73             const GCMClient::OutgoingMessage& message);
     74 
     75  private:
     76   friend class base::RefCountedThreadSafe<IOWorker>;
     77   virtual ~IOWorker();
     78 
     79   const base::WeakPtr<GCMProfileService> service_;
     80 
     81   // The checkin info obtained from the server for the signed in user associated
     82   // with the profile.
     83   GCMClient::CheckInInfo checkin_info_;
     84 };
     85 
     86 GCMProfileService::IOWorker::IOWorker(
     87     const base::WeakPtr<GCMProfileService>& service)
     88     : service_(service) {
     89 }
     90 
     91 GCMProfileService::IOWorker::~IOWorker() {
     92 }
     93 
     94 void GCMProfileService::IOWorker::OnCheckInFinished(
     95     const GCMClient::CheckInInfo& checkin_info,
     96     GCMClient::Result result) {
     97   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
     98 
     99   checkin_info_ = checkin_info;
    100 
    101   content::BrowserThread::PostTask(
    102       content::BrowserThread::UI,
    103       FROM_HERE,
    104       base::Bind(&GCMProfileService::CheckInFinished,
    105                  service_,
    106                  checkin_info_,
    107                  result));
    108 }
    109 
    110 void GCMProfileService::IOWorker::OnRegisterFinished(
    111     const std::string& app_id,
    112     const std::string& registration_id,
    113     GCMClient::Result result) {
    114   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    115 
    116   content::BrowserThread::PostTask(
    117       content::BrowserThread::UI,
    118       FROM_HERE,
    119       base::Bind(&GCMProfileService::RegisterFinished,
    120                  service_,
    121                  app_id,
    122                  registration_id,
    123                  result));
    124 }
    125 
    126 void GCMProfileService::IOWorker::OnSendFinished(
    127     const std::string& app_id,
    128     const std::string& message_id,
    129     GCMClient::Result result) {
    130   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    131 
    132   content::BrowserThread::PostTask(
    133       content::BrowserThread::UI,
    134       FROM_HERE,
    135       base::Bind(&GCMProfileService::SendFinished,
    136                  service_,
    137                  app_id,
    138                  message_id,
    139                  result));
    140 }
    141 
    142 void GCMProfileService::IOWorker::OnMessageReceived(
    143     const std::string& app_id,
    144     const GCMClient::IncomingMessage& message) {
    145   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    146 
    147   content::BrowserThread::PostTask(
    148       content::BrowserThread::UI,
    149       FROM_HERE,
    150       base::Bind(&GCMProfileService::MessageReceived,
    151                  service_,
    152                  app_id,
    153                  message));
    154 }
    155 
    156 void GCMProfileService::IOWorker::OnMessagesDeleted(const std::string& app_id) {
    157   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    158 
    159   content::BrowserThread::PostTask(
    160       content::BrowserThread::UI,
    161       FROM_HERE,
    162       base::Bind(&GCMProfileService::MessagesDeleted,
    163                  service_,
    164                  app_id));
    165 }
    166 
    167 void GCMProfileService::IOWorker::OnMessageSendError(
    168     const std::string& app_id,
    169     const std::string& message_id,
    170     GCMClient::Result result) {
    171   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    172 
    173   content::BrowserThread::PostTask(
    174       content::BrowserThread::UI,
    175       FROM_HERE,
    176       base::Bind(&GCMProfileService::MessageSendError,
    177                  service_,
    178                  app_id,
    179                  message_id,
    180                  result));
    181 }
    182 
    183 GCMClient::CheckInInfo GCMProfileService::IOWorker::GetCheckInInfo() const {
    184   return checkin_info_;
    185 }
    186 
    187 void GCMProfileService::IOWorker::OnLoadingCompleted() {
    188   // TODO(jianli): to be implemented.
    189 }
    190 
    191 base::TaskRunner* GCMProfileService::IOWorker::GetFileTaskRunner() {
    192   // TODO(jianli): to be implemented.
    193   return NULL;
    194 }
    195 
    196 void GCMProfileService::IOWorker::CheckIn(const std::string& username) {
    197   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    198 
    199   GCMClient::Get()->CheckIn(username, this);
    200 }
    201 
    202 void GCMProfileService::IOWorker::SetCheckInInfo(
    203     GCMClient::CheckInInfo checkin_info) {
    204   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    205 
    206   checkin_info_ = checkin_info;
    207 }
    208 
    209 void GCMProfileService::IOWorker::CheckOut() {
    210   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    211 
    212   checkin_info_.Reset();
    213 }
    214 
    215 void GCMProfileService::IOWorker::Register(
    216     const std::string& username,
    217     const std::string& app_id,
    218     const std::vector<std::string>& sender_ids,
    219     const std::string& cert) {
    220   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    221   DCHECK(checkin_info_.IsValid());
    222 
    223   GCMClient::Get()->Register(username, app_id, cert, sender_ids);
    224 }
    225 
    226 void GCMProfileService::IOWorker::Unregister(const std::string& username,
    227                                              const std::string& app_id) {
    228   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    229   DCHECK(checkin_info_.IsValid());
    230 
    231   GCMClient::Get()->Unregister(username, app_id);
    232 }
    233 
    234 void GCMProfileService::IOWorker::Send(
    235     const std::string& username,
    236     const std::string& app_id,
    237     const std::string& receiver_id,
    238     const GCMClient::OutgoingMessage& message) {
    239   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
    240   DCHECK(checkin_info_.IsValid());
    241 
    242   GCMClient::Get()->Send(username, app_id, receiver_id, message);
    243 }
    244 
    245 GCMProfileService::RegistrationInfo::RegistrationInfo() {
    246 }
    247 
    248 GCMProfileService::RegistrationInfo::~RegistrationInfo() {
    249 }
    250 
    251 bool GCMProfileService::enable_gcm_for_testing_ = false;
    252 
    253 // static
    254 bool GCMProfileService::IsGCMEnabled() {
    255   if (enable_gcm_for_testing_)
    256     return true;
    257 
    258   // GCM support is only enabled for Canary/Dev builds.
    259   chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
    260   return channel == chrome::VersionInfo::CHANNEL_UNKNOWN ||
    261          channel == chrome::VersionInfo::CHANNEL_CANARY ||
    262          channel == chrome::VersionInfo::CHANNEL_DEV;
    263 }
    264 
    265 // static
    266 void GCMProfileService::RegisterProfilePrefs(
    267     user_prefs::PrefRegistrySyncable* registry) {
    268   registry->RegisterUint64Pref(
    269       prefs::kGCMUserAccountID,
    270       0,
    271       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    272   registry->RegisterStringPref(
    273       prefs::kGCMUserToken,
    274       "",
    275       user_prefs::PrefRegistrySyncable::UNSYNCABLE_PREF);
    276 }
    277 
    278 GCMProfileService::GCMProfileService(Profile* profile)
    279     : profile_(profile),
    280       testing_delegate_(NULL),
    281       weak_ptr_factory_(this) {
    282   Init();
    283 }
    284 
    285 GCMProfileService::GCMProfileService(Profile* profile,
    286                                      TestingDelegate* testing_delegate)
    287     : profile_(profile),
    288       testing_delegate_(testing_delegate),
    289       weak_ptr_factory_(this) {
    290   Init();
    291 }
    292 
    293 GCMProfileService::~GCMProfileService() {
    294 }
    295 
    296 void GCMProfileService::Init() {
    297   // This has to be done first since CheckIn depends on it.
    298   io_worker_ = new IOWorker(weak_ptr_factory_.GetWeakPtr());
    299 
    300   // In case that the profile has been signed in before GCMProfileService is
    301   // created.
    302   SigninManagerBase* manager = SigninManagerFactory::GetForProfile(profile_);
    303   if (manager)
    304     username_ = manager->GetAuthenticatedUsername();
    305   if (!username_.empty())
    306     AddUser();
    307 
    308   registrar_.Add(this,
    309                  chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
    310                  content::Source<Profile>(profile_));
    311   registrar_.Add(this,
    312                  chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
    313                  content::Source<Profile>(profile_));
    314   // TODO(jianli): move extension specific logic out of GCMProfileService.
    315   registrar_.Add(this,
    316                  chrome::NOTIFICATION_EXTENSION_LOADED,
    317                  content::Source<Profile>(profile_));
    318   registrar_.Add(this,
    319                  chrome:: NOTIFICATION_EXTENSION_UNINSTALLED,
    320                  content::Source<Profile>(profile_));
    321 }
    322 
    323 void GCMProfileService::Register(const std::string& app_id,
    324                                  const std::vector<std::string>& sender_ids,
    325                                  const std::string& cert,
    326                                  RegisterCallback callback) {
    327   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    328   DCHECK(!app_id.empty() && !sender_ids.empty() && !callback.is_null());
    329 
    330   // If previous register operation is still in progress, bail out.
    331   if (register_callbacks_.find(app_id) != register_callbacks_.end()) {
    332     callback.Run(std::string(), GCMClient::ASYNC_OPERATION_PENDING);
    333     return;
    334   }
    335 
    336   // Normalize the sender IDs by making them sorted.
    337   std::vector<std::string> normalized_sender_ids = sender_ids;
    338   std::sort(normalized_sender_ids.begin(), normalized_sender_ids.end());
    339 
    340   // If the same sender ids is provided, return the cached registration ID
    341   // directly.
    342   RegistrationInfoMap::const_iterator registration_info_iter =
    343       registration_info_map_.find(app_id);
    344   if (registration_info_iter != registration_info_map_.end() &&
    345       registration_info_iter->second.sender_ids == normalized_sender_ids) {
    346     callback.Run(registration_info_iter->second.registration_id,
    347                  GCMClient::SUCCESS);
    348     return;
    349   }
    350 
    351   // Cache the sender IDs. The registration ID will be filled when the
    352   // registration completes.
    353   RegistrationInfo registration_info;
    354   registration_info.sender_ids = normalized_sender_ids;
    355   registration_info_map_[app_id] = registration_info;
    356 
    357   register_callbacks_[app_id] = callback;
    358 
    359   content::BrowserThread::PostTask(
    360       content::BrowserThread::IO,
    361       FROM_HERE,
    362       base::Bind(&GCMProfileService::IOWorker::Register,
    363                  io_worker_,
    364                  username_,
    365                  app_id,
    366                  normalized_sender_ids,
    367                  cert));
    368 }
    369 
    370 void GCMProfileService::Send(const std::string& app_id,
    371                              const std::string& receiver_id,
    372                              const GCMClient::OutgoingMessage& message,
    373                              SendCallback callback) {
    374   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    375   DCHECK(!app_id.empty() && !receiver_id.empty() && !callback.is_null());
    376 
    377   std::pair<std::string, std::string> key(app_id, message.id);
    378   if (send_callbacks_.find(key) != send_callbacks_.end()) {
    379     callback.Run(message.id, GCMClient::INVALID_PARAMETER);
    380     return;
    381   }
    382   send_callbacks_[key] = callback;
    383 
    384   content::BrowserThread::PostTask(
    385       content::BrowserThread::IO,
    386       FROM_HERE,
    387       base::Bind(&GCMProfileService::IOWorker::Send,
    388                  io_worker_,
    389                  username_,
    390                  app_id,
    391                  receiver_id,
    392                  message));
    393 }
    394 
    395 void GCMProfileService::Observe(int type,
    396                                 const content::NotificationSource& source,
    397                                 const content::NotificationDetails& details) {
    398   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    399 
    400   switch (type) {
    401     case chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL: {
    402       const GoogleServiceSigninSuccessDetails* signin_details =
    403           content::Details<GoogleServiceSigninSuccessDetails>(details).ptr();
    404       // If re-signin occurs due to password change, there is no need to do
    405       // check-in again.
    406       if (username_ != signin_details->username) {
    407         username_ = signin_details->username;
    408         DCHECK(!username_.empty());
    409         AddUser();
    410       }
    411       break;
    412     }
    413     case chrome::NOTIFICATION_GOOGLE_SIGNED_OUT:
    414       username_.clear();
    415       RemoveUser();
    416       break;
    417     case chrome::NOTIFICATION_EXTENSION_LOADED: {
    418       extensions::Extension* extension =
    419           content::Details<extensions::Extension>(details).ptr();
    420       // No need to load the persisted registration info if the extension does
    421       // not have the GCM permission.
    422       if (extension->HasAPIPermission(extensions::APIPermission::kGcm))
    423         ReadRegistrationInfo(extension->id());
    424       break;
    425     }
    426     case chrome:: NOTIFICATION_EXTENSION_UNINSTALLED: {
    427       extensions::Extension* extension =
    428           content::Details<extensions::Extension>(details).ptr();
    429       Unregister(extension->id());
    430       break;
    431     }
    432     default:
    433       NOTREACHED();
    434   }
    435 }
    436 
    437 void GCMProfileService::AddUser() {
    438   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    439 
    440   // Try to read persisted check-in info from the profile's prefs store.
    441   PrefService* pref_service = profile_->GetPrefs();
    442   uint64 android_id = pref_service->GetUint64(prefs::kGCMUserAccountID);
    443   std::string base64_token = pref_service->GetString(prefs::kGCMUserToken);
    444   std::string encrypted_secret;
    445   base::Base64Decode(base::StringPiece(base64_token), &encrypted_secret);
    446   if (android_id && !encrypted_secret.empty()) {
    447     std::string decrypted_secret;
    448     Encryptor::DecryptString(encrypted_secret, &decrypted_secret);
    449     uint64 secret = 0;
    450     if (base::StringToUint64(decrypted_secret, &secret) && secret) {
    451       GCMClient::CheckInInfo checkin_info;
    452       checkin_info.android_id = android_id;
    453       checkin_info.secret = secret;
    454       content::BrowserThread::PostTask(
    455           content::BrowserThread::IO,
    456           FROM_HERE,
    457           base::Bind(&GCMProfileService::IOWorker::SetCheckInInfo,
    458                      io_worker_,
    459                      checkin_info));
    460 
    461       if (testing_delegate_)
    462         testing_delegate_->CheckInFinished(checkin_info, GCMClient::SUCCESS);
    463 
    464       return;
    465     }
    466   }
    467 
    468   content::BrowserThread::PostTask(
    469       content::BrowserThread::IO,
    470       FROM_HERE,
    471       base::Bind(&GCMProfileService::IOWorker::CheckIn,
    472                  io_worker_,
    473                  username_));
    474 }
    475 
    476 void GCMProfileService::RemoveUser() {
    477   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    478 
    479   PrefService* pref_service = profile_->GetPrefs();
    480   pref_service->ClearPref(prefs::kGCMUserAccountID);
    481   pref_service->ClearPref(prefs::kGCMUserToken);
    482 
    483   content::BrowserThread::PostTask(
    484       content::BrowserThread::IO,
    485       FROM_HERE,
    486       base::Bind(&GCMProfileService::IOWorker::CheckOut, io_worker_));
    487 }
    488 
    489 void GCMProfileService::Unregister(const std::string& app_id) {
    490   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    491 
    492   // This is unlikely to happen because the app will not be uninstalled before
    493   // the asynchronous extension function completes.
    494   DCHECK(register_callbacks_.find(app_id) == register_callbacks_.end());
    495 
    496   // Remove the cached registration info. If not found, there is no need to
    497   // ask the server to unregister it.
    498   RegistrationInfoMap::iterator registration_info_iter =
    499       registration_info_map_.find(app_id);
    500   if (registration_info_iter == registration_info_map_.end())
    501     return;
    502   registration_info_map_.erase(registration_info_iter);
    503 
    504   // Remove the persisted registration info.
    505   DeleteRegistrationInfo(app_id);
    506 
    507   // Ask the server to unregister it. There could be a small chance that the
    508   // unregister request fails. If this occurs, it does not bring any harm since
    509   // we simply reject the messages/events received from the server.
    510   content::BrowserThread::PostTask(
    511       content::BrowserThread::IO,
    512       FROM_HERE,
    513       base::Bind(&GCMProfileService::IOWorker::Unregister,
    514                  io_worker_,
    515                  username_,
    516                  app_id));
    517 }
    518 
    519 void GCMProfileService::CheckInFinished(GCMClient::CheckInInfo checkin_info,
    520                                         GCMClient::Result result) {
    521   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    522 
    523   // Save the check-in info into the profile's prefs store.
    524   PrefService* pref_service = profile_->GetPrefs();
    525   pref_service->SetUint64(prefs::kGCMUserAccountID, checkin_info.android_id);
    526 
    527   // Encrypt the secret for persisting purpose.
    528   std::string encrypted_secret;
    529   Encryptor::EncryptString(base::Uint64ToString(checkin_info.secret),
    530                            &encrypted_secret);
    531 
    532   // |encrypted_secret| might contain binary data and our prefs store only
    533   // works for the text.
    534   std::string base64_token;
    535   base::Base64Encode(encrypted_secret, &base64_token);
    536   pref_service->SetString(prefs::kGCMUserToken, base64_token);
    537 
    538   if (testing_delegate_)
    539     testing_delegate_->CheckInFinished(checkin_info, result);
    540 }
    541 
    542 void GCMProfileService::RegisterFinished(std::string app_id,
    543                                          std::string registration_id,
    544                                          GCMClient::Result result) {
    545   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    546 
    547   std::map<std::string, RegisterCallback>::iterator callback_iter =
    548       register_callbacks_.find(app_id);
    549   if (callback_iter == register_callbacks_.end()) {
    550     // The callback could have been removed when the app is uninstalled.
    551     return;
    552   }
    553 
    554   // Cache the registration ID if the registration succeeds. Otherwise,
    555   // removed the cached info.
    556   RegistrationInfoMap::iterator registration_info_iter =
    557       registration_info_map_.find(app_id);
    558   // This is unlikely to happen because the app will not be uninstalled before
    559   // the asynchronous extension function completes.
    560   DCHECK(registration_info_iter != registration_info_map_.end());
    561   if (result == GCMClient::SUCCESS) {
    562     registration_info_iter->second.registration_id = registration_id;
    563     WriteRegistrationInfo(app_id);
    564   } else {
    565     registration_info_map_.erase(registration_info_iter);
    566   }
    567 
    568   RegisterCallback callback = callback_iter->second;
    569   register_callbacks_.erase(callback_iter);
    570   callback.Run(registration_id, result);
    571 }
    572 
    573 void GCMProfileService::SendFinished(std::string app_id,
    574                                      std::string message_id,
    575                                      GCMClient::Result result) {
    576   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    577 
    578   std::map<std::pair<std::string, std::string>, SendCallback>::iterator
    579       callback_iter = send_callbacks_.find(
    580           std::pair<std::string, std::string>(app_id, message_id));
    581   if (callback_iter == send_callbacks_.end()) {
    582     // The callback could have been removed when the app is uninstalled.
    583     return;
    584   }
    585 
    586   SendCallback callback = callback_iter->second;
    587   send_callbacks_.erase(callback_iter);
    588   callback.Run(message_id, result);
    589 }
    590 
    591 void GCMProfileService::MessageReceived(std::string app_id,
    592                                         GCMClient::IncomingMessage message) {
    593   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    594 
    595   GetEventRouter(app_id)->OnMessage(app_id, message);
    596 }
    597 
    598 void GCMProfileService::MessagesDeleted(std::string app_id) {
    599   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    600 
    601   GetEventRouter(app_id)->OnMessagesDeleted(app_id);
    602 }
    603 
    604 void GCMProfileService::MessageSendError(std::string app_id,
    605                                          std::string message_id,
    606                                          GCMClient::Result result) {
    607   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI));
    608 
    609   GetEventRouter(app_id)->OnSendError(app_id, message_id, result);
    610 }
    611 
    612 GCMEventRouter* GCMProfileService::GetEventRouter(const std::string& app_id) {
    613   if (testing_delegate_ && testing_delegate_->GetEventRouter())
    614     return testing_delegate_->GetEventRouter();
    615   // TODO(fgorski): check and create the event router for JS routing.
    616   return js_event_router_.get();
    617 }
    618 
    619 void GCMProfileService::DeleteRegistrationInfo(const std::string& app_id) {
    620   extensions::StateStore* storage =
    621       extensions::ExtensionSystem::Get(profile_)->state_store();
    622   if (!storage)
    623     return;
    624 
    625   storage->RemoveExtensionValue(app_id, kRegistrationKey);
    626 }
    627 
    628 void GCMProfileService::WriteRegistrationInfo(const std::string& app_id) {
    629   extensions::StateStore* storage =
    630       extensions::ExtensionSystem::Get(profile_)->state_store();
    631   if (!storage)
    632     return;
    633 
    634   RegistrationInfoMap::const_iterator registration_info_iter =
    635       registration_info_map_.find(app_id);
    636   if (registration_info_iter == registration_info_map_.end())
    637     return;
    638   const RegistrationInfo& registration_info = registration_info_iter->second;
    639 
    640   scoped_ptr<base::ListValue> senders_list(new base::ListValue());
    641   for (std::vector<std::string>::const_iterator senders_iter =
    642            registration_info.sender_ids.begin();
    643        senders_iter != registration_info.sender_ids.end();
    644        ++senders_iter) {
    645     senders_list->AppendString(*senders_iter);
    646   }
    647 
    648   scoped_ptr<base::DictionaryValue> registration_info_dict(
    649       new base::DictionaryValue());
    650   registration_info_dict->Set(kSendersKey, senders_list.release());
    651   registration_info_dict->SetString(kRegistrationIDKey,
    652                                     registration_info.registration_id);
    653 
    654   storage->SetExtensionValue(
    655       app_id, kRegistrationKey, registration_info_dict.PassAs<base::Value>());
    656 }
    657 
    658 void GCMProfileService::ReadRegistrationInfo(const std::string& app_id) {
    659   extensions::StateStore* storage =
    660       extensions::ExtensionSystem::Get(profile_)->state_store();
    661   if (!storage)
    662     return;
    663 
    664   storage->GetExtensionValue(
    665       app_id,
    666       kRegistrationKey,
    667       base::Bind(
    668           &GCMProfileService::ReadRegistrationInfoFinished,
    669           weak_ptr_factory_.GetWeakPtr(),
    670           app_id));
    671 }
    672 
    673 void GCMProfileService::ReadRegistrationInfoFinished(
    674     std::string app_id,
    675     scoped_ptr<base::Value> value) {
    676   RegistrationInfo registration_info;
    677   if (!ParsePersistedRegistrationInfo(value.Pass(), &registration_info)) {
    678     // Delete the persisted data if it is corrupted.
    679     DeleteRegistrationInfo(app_id);
    680     return;
    681   }
    682 
    683   registration_info_map_[app_id] = registration_info;
    684 
    685   // TODO(jianli): The waiting would be removed once we support delaying running
    686   // register operation until the persistent loading completes.
    687   if (testing_delegate_)
    688     testing_delegate_->LoadingFromPersistentStoreFinished();
    689 }
    690 
    691 bool GCMProfileService::ParsePersistedRegistrationInfo(
    692     scoped_ptr<base::Value> value,
    693     RegistrationInfo* registration_info) {
    694   base::DictionaryValue* dict = NULL;
    695   if (!value.get() || !value->GetAsDictionary(&dict))
    696     return false;
    697 
    698   if (!dict->GetString(kRegistrationIDKey, &registration_info->registration_id))
    699     return false;
    700 
    701   const base::ListValue* senders_list = NULL;
    702   if (!dict->GetList(kSendersKey, &senders_list) || !senders_list->GetSize())
    703     return false;
    704   for (size_t i = 0; i < senders_list->GetSize(); ++i) {
    705     std::string sender;
    706     if (!senders_list->GetString(i, &sender))
    707       return false;
    708     registration_info->sender_ids.push_back(sender);
    709   }
    710 
    711   return true;
    712 }
    713 
    714 // static
    715 const char* GCMProfileService::GetPersistentRegisterKeyForTesting() {
    716   return kRegistrationKey;
    717 }
    718 
    719 }  // namespace gcm
    720