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 16namespace invalidation { 17namespace { 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. 22const char kInvalidationsSenderId[] = "ipc.invalidation@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). 26const char kInvalidationsAppId[] = "com.google.chrome.invalidations"; 27 28// Cacheinvalidation specific gcm message keys. 29const char kContentKey[] = "content"; 30const 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. 36class 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 75GCMInvalidationBridge::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 85GCMInvalidationBridge::Core::~Core() {} 86 87void 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 100void 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 107void 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 114void 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 121void 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 129void 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 137void 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 145void 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 152void GCMInvalidationBridge::Core::OnConnectionStateChanged(bool online) { 153 if (!connection_state_callback_.is_null()) { 154 connection_state_callback_.Run(online); 155 } 156} 157 158GCMInvalidationBridge::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 167GCMInvalidationBridge::~GCMInvalidationBridge() { 168 if (subscribed_for_incoming_messages_) { 169 gcm_driver_->RemoveAppHandler(kInvalidationsAppId); 170 gcm_driver_->RemoveConnectionObserver(this); 171 } 172} 173 174scoped_ptr<syncer::GCMNetworkChannelDelegate> 175GCMInvalidationBridge::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 182void 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 190void 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 212void 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 229void 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 245void 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 253void 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 269void 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 283void 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 300void GCMInvalidationBridge::ShutdownHandler() { 301 // Nothing to do. 302} 303 304void 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 325void 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 331void 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 338void 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 345void 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 352void 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