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 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
;
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
);
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()));
180 void GCMInvalidationBridge::CoreInitializationDone(
181 base::WeakPtr
<Core
> core
,
182 scoped_refptr
<base::SingleThreadTaskRunner
> core_thread_task_runner
) {
183 DCHECK(CalledOnValidThread());
185 core_thread_task_runner_
= core_thread_task_runner
;
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(
197 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
199 request_token_callback_
,
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);
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(
218 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
220 request_token_callback_
,
221 GoogleServiceAuthError::AuthErrorNone(),
223 request_token_callback_
.Reset();
224 access_token_request_
.reset();
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(
234 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
236 request_token_callback_
,
239 request_token_callback_
.Reset();
240 access_token_request_
.reset();
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
);
251 void GCMInvalidationBridge::Register(
252 syncer::GCMNetworkChannelDelegate::RegisterCallback callback
) {
253 DCHECK(CalledOnValidThread());
254 // No-op if GCMClient is disabled.
255 if (gcm_driver_
== NULL
)
258 std::vector
<std::string
> sender_ids
;
259 sender_ids
.push_back(kInvalidationsSenderId
);
260 gcm_driver_
->Register(kInvalidationsAppId
,
262 base::Bind(&GCMInvalidationBridge::RegisterFinished
,
263 weak_factory_
.GetWeakPtr(),
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(
274 base::Bind(&GCMInvalidationBridge::Core::RegisterFinished
,
281 void GCMInvalidationBridge::SubscribeForIncomingMessages() {
282 // No-op if GCMClient is disabled.
283 if (gcm_driver_
== NULL
)
286 DCHECK(!subscribed_for_incoming_messages_
);
287 gcm_driver_
->AddAppHandler(kInvalidationsAppId
, this);
288 core_thread_task_runner_
->PostTask(
290 base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged
,
292 gcm_driver_
->IsConnected()));
294 subscribed_for_incoming_messages_
= true;
297 void GCMInvalidationBridge::ShutdownHandler() {
301 void GCMInvalidationBridge::OnMessage(
302 const std::string
& app_id
,
303 const gcm::GCMClient::IncomingMessage
& message
) {
304 gcm::GCMClient::MessageData::const_iterator it
;
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
;
314 core_thread_task_runner_
->PostTask(
316 base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage
,
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.
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.
335 void GCMInvalidationBridge::OnConnected(const net::IPEndPoint
& ip_endpoint
) {
336 core_thread_task_runner_
->PostTask(
339 &GCMInvalidationBridge::Core::OnConnectionStateChanged
, core_
, true));
342 void GCMInvalidationBridge::OnDisconnected() {
343 core_thread_task_runner_
->PostTask(
345 base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged
,
351 } // namespace invalidation