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.
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"
16 namespace invalidation
{
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
22 const 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).
26 const char kInvalidationsAppId
[] = "com.google.chrome.invalidations";
28 // Cacheinvalidation specific gcm message keys.
29 const char kContentKey
[] = "content";
30 const char kEchoTokenKey
[] = "echo-token";
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
{
39 Core(base::WeakPtr
<GCMInvalidationBridge
> bridge
,
40 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner
);
43 // syncer::GCMNetworkChannelDelegate implementation.
44 void Initialize(ConnectionStateCallback callback
) override
;
45 void RequestToken(RequestTokenCallback callback
) override
;
46 void InvalidateToken(const std::string
& token
) override
;
47 void Register(RegisterCallback callback
) override
;
48 void SetMessageReceiver(MessageCallback callback
) override
;
50 void RequestTokenFinished(RequestTokenCallback callback
,
51 const GoogleServiceAuthError
& error
,
52 const std::string
& token
);
54 void RegisterFinished(RegisterCallback callback
,
55 const std::string
& registration_id
,
56 gcm::GCMClient::Result result
);
58 void OnIncomingMessage(const std::string
& message
,
59 const std::string
& echo_token
);
61 void OnConnectionStateChanged(bool online
);
64 base::WeakPtr
<GCMInvalidationBridge
> bridge_
;
65 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner_
;
67 MessageCallback message_callback_
;
68 ConnectionStateCallback connection_state_callback_
;
70 base::WeakPtrFactory
<Core
> weak_factory_
;
72 DISALLOW_COPY_AND_ASSIGN(Core
);
75 GCMInvalidationBridge::Core::Core(
76 base::WeakPtr
<GCMInvalidationBridge
> bridge
,
77 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner
)
79 ui_thread_task_runner_(ui_thread_task_runner
),
81 // Core is created on UI thread but all calls happen on IO thread.
85 GCMInvalidationBridge::Core::~Core() {}
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
92 ui_thread_task_runner_
->PostTask(
94 base::Bind(&GCMInvalidationBridge::CoreInitializationDone
,
96 weak_factory_
.GetWeakPtr(),
97 base::ThreadTaskRunnerHandle::Get()));
100 void GCMInvalidationBridge::Core::RequestToken(RequestTokenCallback callback
) {
101 DCHECK(CalledOnValidThread());
102 ui_thread_task_runner_
->PostTask(
104 base::Bind(&GCMInvalidationBridge::RequestToken
, bridge_
, callback
));
107 void GCMInvalidationBridge::Core::InvalidateToken(const std::string
& token
) {
108 DCHECK(CalledOnValidThread());
109 ui_thread_task_runner_
->PostTask(
111 base::Bind(&GCMInvalidationBridge::InvalidateToken
, bridge_
, token
));
114 void GCMInvalidationBridge::Core::Register(RegisterCallback callback
) {
115 DCHECK(CalledOnValidThread());
116 ui_thread_task_runner_
->PostTask(
118 base::Bind(&GCMInvalidationBridge::Register
, bridge_
, callback
));
121 void GCMInvalidationBridge::Core::SetMessageReceiver(MessageCallback callback
) {
122 message_callback_
= callback
;
123 ui_thread_task_runner_
->PostTask(
125 base::Bind(&GCMInvalidationBridge::SubscribeForIncomingMessages
,
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
);
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
);
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
);
152 void GCMInvalidationBridge::Core::OnConnectionStateChanged(bool online
) {
153 if (!connection_state_callback_
.is_null()) {
154 connection_state_callback_
.Run(online
);
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) {}
167 GCMInvalidationBridge::~GCMInvalidationBridge() {
168 if (subscribed_for_incoming_messages_
) {
169 gcm_driver_
->RemoveAppHandler(kInvalidationsAppId
);
170 gcm_driver_
->RemoveConnectionObserver(this);
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()));
182 void GCMInvalidationBridge::CoreInitializationDone(
183 base::WeakPtr
<Core
> core
,
184 scoped_refptr
<base::SingleThreadTaskRunner
> core_thread_task_runner
) {
185 DCHECK(CalledOnValidThread());
187 core_thread_task_runner_
= core_thread_task_runner
;
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(
199 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
201 request_token_callback_
,
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);
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(
220 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
222 request_token_callback_
,
223 GoogleServiceAuthError::AuthErrorNone(),
225 request_token_callback_
.Reset();
226 access_token_request_
.reset();
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(
236 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
238 request_token_callback_
,
241 request_token_callback_
.Reset();
242 access_token_request_
.reset();
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
);
253 void GCMInvalidationBridge::Register(
254 syncer::GCMNetworkChannelDelegate::RegisterCallback callback
) {
255 DCHECK(CalledOnValidThread());
256 // No-op if GCMClient is disabled.
257 if (gcm_driver_
== NULL
)
260 std::vector
<std::string
> sender_ids
;
261 sender_ids
.push_back(kInvalidationsSenderId
);
262 gcm_driver_
->Register(kInvalidationsAppId
,
264 base::Bind(&GCMInvalidationBridge::RegisterFinished
,
265 weak_factory_
.GetWeakPtr(),
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(
276 base::Bind(&GCMInvalidationBridge::Core::RegisterFinished
,
283 void GCMInvalidationBridge::Unregister() {
284 DCHECK(CalledOnValidThread());
285 // No-op if GCMClient is disabled.
286 if (gcm_driver_
== NULL
)
289 gcm_driver_
->Unregister(
291 base::Bind(&GCMInvalidationBridge::UnregisterFinishedNoOp
));
295 void GCMInvalidationBridge::UnregisterFinishedNoOp(
296 gcm::GCMClient::Result result
) {
300 void GCMInvalidationBridge::SubscribeForIncomingMessages() {
301 // No-op if GCMClient is disabled.
302 if (gcm_driver_
== NULL
)
305 DCHECK(!subscribed_for_incoming_messages_
);
306 gcm_driver_
->AddAppHandler(kInvalidationsAppId
, this);
307 gcm_driver_
->AddConnectionObserver(this);
308 core_thread_task_runner_
->PostTask(
310 base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged
,
312 gcm_driver_
->IsConnected()));
314 subscribed_for_incoming_messages_
= true;
317 void GCMInvalidationBridge::ShutdownHandler() {
321 void GCMInvalidationBridge::OnMessage(
322 const std::string
& app_id
,
323 const gcm::GCMClient::IncomingMessage
& message
) {
324 gcm::GCMClient::MessageData::const_iterator it
;
326 std::string echo_token
;
327 it
= message
.data
.find(kContentKey
);
328 if (it
!= message
.data
.end())
329 content
= it
->second
;
330 it
= message
.data
.find(kEchoTokenKey
);
331 if (it
!= message
.data
.end())
332 echo_token
= it
->second
;
334 core_thread_task_runner_
->PostTask(
336 base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage
,
342 void GCMInvalidationBridge::OnMessagesDeleted(const std::string
& app_id
) {
343 // Cacheinvalidation doesn't use long lived non-collapsable messages with GCM.
344 // Android implementation of cacheinvalidation doesn't handle MessagesDeleted
345 // callback so this should be no-op in desktop version as well.
348 void GCMInvalidationBridge::OnSendError(
349 const std::string
& app_id
,
350 const gcm::GCMClient::SendErrorDetails
& send_error_details
) {
351 // cacheinvalidation doesn't send messages over GCM.
355 void GCMInvalidationBridge::OnSendAcknowledged(
356 const std::string
& app_id
,
357 const std::string
& message_id
) {
358 // cacheinvalidation doesn't send messages over GCM.
362 void GCMInvalidationBridge::OnConnected(const net::IPEndPoint
& ip_endpoint
) {
363 core_thread_task_runner_
->PostTask(
366 &GCMInvalidationBridge::Core::OnConnectionStateChanged
, core_
, true));
369 void GCMInvalidationBridge::OnDisconnected() {
370 core_thread_task_runner_
->PostTask(
372 base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged
,
377 } // namespace invalidation