Home | History | Annotate | Download | only in invalidation
      1 // Copyright 2014 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 "base/bind.h"
      6 #include "base/location.h"
      7 #include "base/single_thread_task_runner.h"
      8 #include "base/thread_task_runner_handle.h"
      9 #include "components/gcm_driver/gcm_driver.h"
     10 #include "components/invalidation/gcm_invalidation_bridge.h"
     11 #include "components/signin/core/browser/profile_oauth2_token_service.h"
     12 #include "components/signin/core/browser/signin_manager.h"
     13 #include "google_apis/gaia/gaia_constants.h"
     14 #include "google_apis/gaia/identity_provider.h"
     15 
     16 namespace invalidation {
     17 namespace {
     18 // For 3rd party developers SenderId should come from application dashboard when
     19 // server side application is registered with Google. Android invalidations use
     20 // legacy format where gmail account can be specificed. Below value is copied
     21 // from Android.
     22 const char kInvalidationsSenderId[] = "ipc.invalidation (at) gmail.com";
     23 // In Android world AppId is provided by operating system and should
     24 // match package name and hash of application. In desktop world these values
     25 // are arbitrary and not verified/enforced by registration service (yet).
     26 const char kInvalidationsAppId[] = "com.google.chrome.invalidations";
     27 
     28 // Cacheinvalidation specific gcm message keys.
     29 const char kContentKey[] = "content";
     30 const char kEchoTokenKey[] = "echo-token";
     31 }  // namespace
     32 
     33 // Core should be very simple class that implements GCMNetwrokChannelDelegate
     34 // and passes all calls to GCMInvalidationBridge. All calls should be serialized
     35 // through GCMInvalidationBridge to avoid race conditions.
     36 class GCMInvalidationBridge::Core : public syncer::GCMNetworkChannelDelegate,
     37                                     public base::NonThreadSafe {
     38  public:
     39   Core(base::WeakPtr<GCMInvalidationBridge> bridge,
     40        scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner);
     41   virtual ~Core();
     42 
     43   // syncer::GCMNetworkChannelDelegate implementation.
     44   virtual void Initialize(ConnectionStateCallback callback) OVERRIDE;
     45   virtual void RequestToken(RequestTokenCallback callback) OVERRIDE;
     46   virtual void InvalidateToken(const std::string& token) OVERRIDE;
     47   virtual void Register(RegisterCallback callback) OVERRIDE;
     48   virtual void SetMessageReceiver(MessageCallback callback) OVERRIDE;
     49 
     50   void RequestTokenFinished(RequestTokenCallback callback,
     51                             const GoogleServiceAuthError& error,
     52                             const std::string& token);
     53 
     54   void RegisterFinished(RegisterCallback callback,
     55                         const std::string& registration_id,
     56                         gcm::GCMClient::Result result);
     57 
     58   void OnIncomingMessage(const std::string& message,
     59                          const std::string& echo_token);
     60 
     61   void OnConnectionStateChanged(bool online);
     62 
     63  private:
     64   base::WeakPtr<GCMInvalidationBridge> bridge_;
     65   scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner_;
     66 
     67   MessageCallback message_callback_;
     68   ConnectionStateCallback connection_state_callback_;
     69 
     70   base::WeakPtrFactory<Core> weak_factory_;
     71 
     72   DISALLOW_COPY_AND_ASSIGN(Core);
     73 };
     74 
     75 GCMInvalidationBridge::Core::Core(
     76     base::WeakPtr<GCMInvalidationBridge> bridge,
     77     scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)
     78     : bridge_(bridge),
     79       ui_thread_task_runner_(ui_thread_task_runner),
     80       weak_factory_(this) {
     81   // Core is created on UI thread but all calls happen on IO thread.
     82   DetachFromThread();
     83 }
     84 
     85 GCMInvalidationBridge::Core::~Core() {}
     86 
     87 void GCMInvalidationBridge::Core::Initialize(ConnectionStateCallback callback) {
     88   DCHECK(CalledOnValidThread());
     89   connection_state_callback_ = callback;
     90   // Pass core WeapPtr and TaskRunner to GCMInvalidationBridge for it to be able
     91   // to post back.
     92   ui_thread_task_runner_->PostTask(
     93       FROM_HERE,
     94       base::Bind(&GCMInvalidationBridge::CoreInitializationDone,
     95                  bridge_,
     96                  weak_factory_.GetWeakPtr(),
     97                  base::ThreadTaskRunnerHandle::Get()));
     98 }
     99 
    100 void GCMInvalidationBridge::Core::RequestToken(RequestTokenCallback callback) {
    101   DCHECK(CalledOnValidThread());
    102   ui_thread_task_runner_->PostTask(
    103       FROM_HERE,
    104       base::Bind(&GCMInvalidationBridge::RequestToken, bridge_, callback));
    105 }
    106 
    107 void GCMInvalidationBridge::Core::InvalidateToken(const std::string& token) {
    108   DCHECK(CalledOnValidThread());
    109   ui_thread_task_runner_->PostTask(
    110       FROM_HERE,
    111       base::Bind(&GCMInvalidationBridge::InvalidateToken, bridge_, token));
    112 }
    113 
    114 void GCMInvalidationBridge::Core::Register(RegisterCallback callback) {
    115   DCHECK(CalledOnValidThread());
    116   ui_thread_task_runner_->PostTask(
    117       FROM_HERE,
    118       base::Bind(&GCMInvalidationBridge::Register, bridge_, callback));
    119 }
    120 
    121 void GCMInvalidationBridge::Core::SetMessageReceiver(MessageCallback callback) {
    122   message_callback_ = callback;
    123   ui_thread_task_runner_->PostTask(
    124       FROM_HERE,
    125       base::Bind(&GCMInvalidationBridge::SubscribeForIncomingMessages,
    126                  bridge_));
    127 }
    128 
    129 void GCMInvalidationBridge::Core::RequestTokenFinished(
    130     RequestTokenCallback callback,
    131     const GoogleServiceAuthError& error,
    132     const std::string& token) {
    133   DCHECK(CalledOnValidThread());
    134   callback.Run(error, token);
    135 }
    136 
    137 void GCMInvalidationBridge::Core::RegisterFinished(
    138     RegisterCallback callback,
    139     const std::string& registration_id,
    140     gcm::GCMClient::Result result) {
    141   DCHECK(CalledOnValidThread());
    142   callback.Run(registration_id, result);
    143 }
    144 
    145 void GCMInvalidationBridge::Core::OnIncomingMessage(
    146     const std::string& message,
    147     const std::string& echo_token) {
    148   DCHECK(!message_callback_.is_null());
    149   message_callback_.Run(message, echo_token);
    150 }
    151 
    152 void GCMInvalidationBridge::Core::OnConnectionStateChanged(bool online) {
    153   if (!connection_state_callback_.is_null()) {
    154     connection_state_callback_.Run(online);
    155   }
    156 }
    157 
    158 GCMInvalidationBridge::GCMInvalidationBridge(
    159     gcm::GCMDriver* gcm_driver,
    160     IdentityProvider* identity_provider)
    161     : OAuth2TokenService::Consumer("gcm_network_channel"),
    162       gcm_driver_(gcm_driver),
    163       identity_provider_(identity_provider),
    164       subscribed_for_incoming_messages_(false),
    165       weak_factory_(this) {}
    166 
    167 GCMInvalidationBridge::~GCMInvalidationBridge() {
    168   if (subscribed_for_incoming_messages_) {
    169     gcm_driver_->RemoveAppHandler(kInvalidationsAppId);
    170     gcm_driver_->RemoveConnectionObserver(this);
    171   }
    172 }
    173 
    174 scoped_ptr<syncer::GCMNetworkChannelDelegate>
    175 GCMInvalidationBridge::CreateDelegate() {
    176   DCHECK(CalledOnValidThread());
    177   scoped_ptr<syncer::GCMNetworkChannelDelegate> core(new Core(
    178       weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get()));
    179   return core.Pass();
    180 }
    181 
    182 void GCMInvalidationBridge::CoreInitializationDone(
    183     base::WeakPtr<Core> core,
    184     scoped_refptr<base::SingleThreadTaskRunner> core_thread_task_runner) {
    185   DCHECK(CalledOnValidThread());
    186   core_ = core;
    187   core_thread_task_runner_ = core_thread_task_runner;
    188 }
    189 
    190 void GCMInvalidationBridge::RequestToken(
    191     syncer::GCMNetworkChannelDelegate::RequestTokenCallback callback) {
    192   DCHECK(CalledOnValidThread());
    193   if (access_token_request_ != NULL) {
    194     // Report previous request as cancelled.
    195     GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
    196     std::string access_token;
    197     core_thread_task_runner_->PostTask(
    198         FROM_HERE,
    199         base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
    200                    core_,
    201                    request_token_callback_,
    202                    error,
    203                    access_token));
    204   }
    205   request_token_callback_ = callback;
    206   OAuth2TokenService::ScopeSet scopes;
    207   scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
    208   access_token_request_ = identity_provider_->GetTokenService()->StartRequest(
    209       identity_provider_->GetActiveAccountId(), scopes, this);
    210 }
    211 
    212 void GCMInvalidationBridge::OnGetTokenSuccess(
    213     const OAuth2TokenService::Request* request,
    214     const std::string& access_token,
    215     const base::Time& expiration_time) {
    216   DCHECK(CalledOnValidThread());
    217   DCHECK_EQ(access_token_request_, request);
    218   core_thread_task_runner_->PostTask(
    219       FROM_HERE,
    220       base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
    221                  core_,
    222                  request_token_callback_,
    223                  GoogleServiceAuthError::AuthErrorNone(),
    224                  access_token));
    225   request_token_callback_.Reset();
    226   access_token_request_.reset();
    227 }
    228 
    229 void GCMInvalidationBridge::OnGetTokenFailure(
    230     const OAuth2TokenService::Request* request,
    231     const GoogleServiceAuthError& error) {
    232   DCHECK(CalledOnValidThread());
    233   DCHECK_EQ(access_token_request_, request);
    234   core_thread_task_runner_->PostTask(
    235       FROM_HERE,
    236       base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
    237                  core_,
    238                  request_token_callback_,
    239                  error,
    240                  std::string()));
    241   request_token_callback_.Reset();
    242   access_token_request_.reset();
    243 }
    244 
    245 void GCMInvalidationBridge::InvalidateToken(const std::string& token) {
    246   DCHECK(CalledOnValidThread());
    247   OAuth2TokenService::ScopeSet scopes;
    248   scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
    249   identity_provider_->GetTokenService()->InvalidateToken(
    250       identity_provider_->GetActiveAccountId(), scopes, token);
    251 }
    252 
    253 void GCMInvalidationBridge::Register(
    254     syncer::GCMNetworkChannelDelegate::RegisterCallback callback) {
    255   DCHECK(CalledOnValidThread());
    256   // No-op if GCMClient is disabled.
    257   if (gcm_driver_ == NULL)
    258     return;
    259 
    260   std::vector<std::string> sender_ids;
    261   sender_ids.push_back(kInvalidationsSenderId);
    262   gcm_driver_->Register(kInvalidationsAppId,
    263                          sender_ids,
    264                          base::Bind(&GCMInvalidationBridge::RegisterFinished,
    265                                     weak_factory_.GetWeakPtr(),
    266                                     callback));
    267 }
    268 
    269 void GCMInvalidationBridge::RegisterFinished(
    270     syncer::GCMNetworkChannelDelegate::RegisterCallback callback,
    271     const std::string& registration_id,
    272     gcm::GCMClient::Result result) {
    273   DCHECK(CalledOnValidThread());
    274   core_thread_task_runner_->PostTask(
    275       FROM_HERE,
    276       base::Bind(&GCMInvalidationBridge::Core::RegisterFinished,
    277                  core_,
    278                  callback,
    279                  registration_id,
    280                  result));
    281 }
    282 
    283 void GCMInvalidationBridge::SubscribeForIncomingMessages() {
    284   // No-op if GCMClient is disabled.
    285   if (gcm_driver_ == NULL)
    286     return;
    287 
    288   DCHECK(!subscribed_for_incoming_messages_);
    289   gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
    290   gcm_driver_->AddConnectionObserver(this);
    291   core_thread_task_runner_->PostTask(
    292       FROM_HERE,
    293       base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged,
    294                  core_,
    295                  gcm_driver_->IsConnected()));
    296 
    297   subscribed_for_incoming_messages_ = true;
    298 }
    299 
    300 void GCMInvalidationBridge::ShutdownHandler() {
    301   // Nothing to do.
    302 }
    303 
    304 void GCMInvalidationBridge::OnMessage(
    305     const std::string& app_id,
    306     const gcm::GCMClient::IncomingMessage& message) {
    307   gcm::GCMClient::MessageData::const_iterator it;
    308   std::string content;
    309   std::string echo_token;
    310   it = message.data.find(kContentKey);
    311   if (it != message.data.end())
    312     content = it->second;
    313   it = message.data.find(kEchoTokenKey);
    314   if (it != message.data.end())
    315     echo_token = it->second;
    316 
    317   core_thread_task_runner_->PostTask(
    318       FROM_HERE,
    319       base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage,
    320                  core_,
    321                  content,
    322                  echo_token));
    323 }
    324 
    325 void GCMInvalidationBridge::OnMessagesDeleted(const std::string& app_id) {
    326   // Cacheinvalidation doesn't use long lived non-collapsable messages with GCM.
    327   // Android implementation of cacheinvalidation doesn't handle MessagesDeleted
    328   // callback so this should be no-op in desktop version as well.
    329 }
    330 
    331 void GCMInvalidationBridge::OnSendError(
    332     const std::string& app_id,
    333     const gcm::GCMClient::SendErrorDetails& send_error_details) {
    334   // cacheinvalidation doesn't send messages over GCM.
    335   NOTREACHED();
    336 }
    337 
    338 void GCMInvalidationBridge::OnSendAcknowledged(
    339     const std::string& app_id,
    340     const std::string& message_id) {
    341   // cacheinvalidation doesn't send messages over GCM.
    342   NOTREACHED();
    343 }
    344 
    345 void GCMInvalidationBridge::OnConnected(const net::IPEndPoint& ip_endpoint) {
    346   core_thread_task_runner_->PostTask(
    347       FROM_HERE,
    348       base::Bind(
    349           &GCMInvalidationBridge::Core::OnConnectionStateChanged, core_, true));
    350 }
    351 
    352 void GCMInvalidationBridge::OnDisconnected() {
    353   core_thread_task_runner_->PostTask(
    354       FROM_HERE,
    355       base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged,
    356                  core_,
    357                  false));
    358 }
    359 
    360 
    361 }  // namespace invalidation
    362