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 }
    171 
    172 scoped_ptr<syncer::GCMNetworkChannelDelegate>
    173 GCMInvalidationBridge::CreateDelegate() {
    174   DCHECK(CalledOnValidThread());
    175   scoped_ptr<syncer::GCMNetworkChannelDelegate> core(new Core(
    176       weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get()));
    177   return core.Pass();
    178 }
    179 
    180 void GCMInvalidationBridge::CoreInitializationDone(
    181     base::WeakPtr<Core> core,
    182     scoped_refptr<base::SingleThreadTaskRunner> core_thread_task_runner) {
    183   DCHECK(CalledOnValidThread());
    184   core_ = core;
    185   core_thread_task_runner_ = core_thread_task_runner;
    186 }
    187 
    188 void GCMInvalidationBridge::RequestToken(
    189     syncer::GCMNetworkChannelDelegate::RequestTokenCallback callback) {
    190   DCHECK(CalledOnValidThread());
    191   if (access_token_request_ != NULL) {
    192     // Report previous request as cancelled.
    193     GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
    194     std::string access_token;
    195     core_thread_task_runner_->PostTask(
    196         FROM_HERE,
    197         base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
    198                    core_,
    199                    request_token_callback_,
    200                    error,
    201                    access_token));
    202   }
    203   request_token_callback_ = callback;
    204   OAuth2TokenService::ScopeSet scopes;
    205   scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
    206   access_token_request_ = identity_provider_->GetTokenService()->StartRequest(
    207       identity_provider_->GetActiveAccountId(), scopes, this);
    208 }
    209 
    210 void GCMInvalidationBridge::OnGetTokenSuccess(
    211     const OAuth2TokenService::Request* request,
    212     const std::string& access_token,
    213     const base::Time& expiration_time) {
    214   DCHECK(CalledOnValidThread());
    215   DCHECK_EQ(access_token_request_, request);
    216   core_thread_task_runner_->PostTask(
    217       FROM_HERE,
    218       base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
    219                  core_,
    220                  request_token_callback_,
    221                  GoogleServiceAuthError::AuthErrorNone(),
    222                  access_token));
    223   request_token_callback_.Reset();
    224   access_token_request_.reset();
    225 }
    226 
    227 void GCMInvalidationBridge::OnGetTokenFailure(
    228     const OAuth2TokenService::Request* request,
    229     const GoogleServiceAuthError& error) {
    230   DCHECK(CalledOnValidThread());
    231   DCHECK_EQ(access_token_request_, request);
    232   core_thread_task_runner_->PostTask(
    233       FROM_HERE,
    234       base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
    235                  core_,
    236                  request_token_callback_,
    237                  error,
    238                  std::string()));
    239   request_token_callback_.Reset();
    240   access_token_request_.reset();
    241 }
    242 
    243 void GCMInvalidationBridge::InvalidateToken(const std::string& token) {
    244   DCHECK(CalledOnValidThread());
    245   OAuth2TokenService::ScopeSet scopes;
    246   scopes.insert(GaiaConstants::kChromeSyncOAuth2Scope);
    247   identity_provider_->GetTokenService()->InvalidateToken(
    248       identity_provider_->GetActiveAccountId(), scopes, token);
    249 }
    250 
    251 void GCMInvalidationBridge::Register(
    252     syncer::GCMNetworkChannelDelegate::RegisterCallback callback) {
    253   DCHECK(CalledOnValidThread());
    254   // No-op if GCMClient is disabled.
    255   if (gcm_driver_ == NULL)
    256     return;
    257 
    258   std::vector<std::string> sender_ids;
    259   sender_ids.push_back(kInvalidationsSenderId);
    260   gcm_driver_->Register(kInvalidationsAppId,
    261                          sender_ids,
    262                          base::Bind(&GCMInvalidationBridge::RegisterFinished,
    263                                     weak_factory_.GetWeakPtr(),
    264                                     callback));
    265 }
    266 
    267 void GCMInvalidationBridge::RegisterFinished(
    268     syncer::GCMNetworkChannelDelegate::RegisterCallback callback,
    269     const std::string& registration_id,
    270     gcm::GCMClient::Result result) {
    271   DCHECK(CalledOnValidThread());
    272   core_thread_task_runner_->PostTask(
    273       FROM_HERE,
    274       base::Bind(&GCMInvalidationBridge::Core::RegisterFinished,
    275                  core_,
    276                  callback,
    277                  registration_id,
    278                  result));
    279 }
    280 
    281 void GCMInvalidationBridge::SubscribeForIncomingMessages() {
    282   // No-op if GCMClient is disabled.
    283   if (gcm_driver_ == NULL)
    284     return;
    285 
    286   DCHECK(!subscribed_for_incoming_messages_);
    287   gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
    288   core_thread_task_runner_->PostTask(
    289       FROM_HERE,
    290       base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged,
    291                  core_,
    292                  gcm_driver_->IsConnected()));
    293 
    294   subscribed_for_incoming_messages_ = true;
    295 }
    296 
    297 void GCMInvalidationBridge::ShutdownHandler() {
    298   // Nothing to do.
    299 }
    300 
    301 void GCMInvalidationBridge::OnMessage(
    302     const std::string& app_id,
    303     const gcm::GCMClient::IncomingMessage& message) {
    304   gcm::GCMClient::MessageData::const_iterator it;
    305   std::string content;
    306   std::string echo_token;
    307   it = message.data.find(kContentKey);
    308   if (it != message.data.end())
    309     content = it->second;
    310   it = message.data.find(kEchoTokenKey);
    311   if (it != message.data.end())
    312     echo_token = it->second;
    313 
    314   core_thread_task_runner_->PostTask(
    315       FROM_HERE,
    316       base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage,
    317                  core_,
    318                  content,
    319                  echo_token));
    320 }
    321 
    322 void GCMInvalidationBridge::OnMessagesDeleted(const std::string& app_id) {
    323   // Cacheinvalidation doesn't use long lived non-collapsable messages with GCM.
    324   // Android implementation of cacheinvalidation doesn't handle MessagesDeleted
    325   // callback so this should be no-op in desktop version as well.
    326 }
    327 
    328 void GCMInvalidationBridge::OnSendError(
    329     const std::string& app_id,
    330     const gcm::GCMClient::SendErrorDetails& send_error_details) {
    331   // cacheinvalidation doesn't send messages over GCM.
    332   NOTREACHED();
    333 }
    334 
    335 void GCMInvalidationBridge::OnConnected(const net::IPEndPoint& ip_endpoint) {
    336   core_thread_task_runner_->PostTask(
    337       FROM_HERE,
    338       base::Bind(
    339           &GCMInvalidationBridge::Core::OnConnectionStateChanged, core_, true));
    340 }
    341 
    342 void GCMInvalidationBridge::OnDisconnected() {
    343   core_thread_task_runner_->PostTask(
    344       FROM_HERE,
    345       base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged,
    346                  core_,
    347                  false));
    348 }
    349 
    350 
    351 }  // namespace invalidation
    352