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