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