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 "chrome/browser/invalidation/gcm_invalidation_bridge.h"
10 #include "chrome/browser/services/gcm/gcm_service.h"
11 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
12 #include "chrome/browser/signin/signin_manager_factory.h"
13 #include "components/signin/core/browser/profile_oauth2_token_service.h"
14 #include "components/signin/core/browser/signin_manager.h"
15 #include "google_apis/gaia/gaia_constants.h"
16 #include "google_apis/gaia/identity_provider.h"
18 namespace invalidation
{
20 // For 3rd party developers SenderId should come from application dashboard when
21 // server side application is registered with Google. Android invalidations use
22 // legacy format where gmail account can be specificed. Below value is copied
24 const char kInvalidationsSenderId
[] = "ipc.invalidation@gmail.com";
25 // In Android world AppId is provided by operating system and should
26 // match package name and hash of application. In desktop world these values
27 // are arbitrary and not verified/enforced by registration service (yet).
28 const char kInvalidationsAppId
[] = "com.google.chrome.invalidations";
30 // Cacheinvalidation specific gcm message keys.
31 const char kContentKey
[] = "content";
32 const char kEchoTokenKey
[] = "echo-token";
35 // Core should be very simple class that implements GCMNetwrokChannelDelegate
36 // and passes all calls to GCMInvalidationBridge. All calls should be serialized
37 // through GCMInvalidationBridge to avoid race conditions.
38 class GCMInvalidationBridge::Core
: public syncer::GCMNetworkChannelDelegate
,
39 public base::NonThreadSafe
{
41 Core(base::WeakPtr
<GCMInvalidationBridge
> bridge
,
42 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner
);
45 // syncer::GCMNetworkChannelDelegate implementation.
46 virtual void Initialize() OVERRIDE
;
47 virtual void RequestToken(RequestTokenCallback callback
) OVERRIDE
;
48 virtual void InvalidateToken(const std::string
& token
) OVERRIDE
;
49 virtual void Register(RegisterCallback callback
) OVERRIDE
;
50 virtual void SetMessageReceiver(MessageCallback callback
) OVERRIDE
;
52 void RequestTokenFinished(RequestTokenCallback callback
,
53 const GoogleServiceAuthError
& error
,
54 const std::string
& token
);
56 void RegisterFinished(RegisterCallback callback
,
57 const std::string
& registration_id
,
58 gcm::GCMClient::Result result
);
60 void OnIncomingMessage(const std::string
& message
,
61 const std::string
& echo_token
);
64 base::WeakPtr
<GCMInvalidationBridge
> bridge_
;
65 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner_
;
67 MessageCallback message_callback_
;
69 base::WeakPtrFactory
<Core
> weak_factory_
;
71 DISALLOW_COPY_AND_ASSIGN(Core
);
74 GCMInvalidationBridge::Core::Core(
75 base::WeakPtr
<GCMInvalidationBridge
> bridge
,
76 scoped_refptr
<base::SingleThreadTaskRunner
> ui_thread_task_runner
)
78 ui_thread_task_runner_(ui_thread_task_runner
),
80 // Core is created on UI thread but all calls happen on IO thread.
84 GCMInvalidationBridge::Core::~Core() {}
86 void GCMInvalidationBridge::Core::Initialize() {
87 DCHECK(CalledOnValidThread());
88 // Pass core WeapPtr and TaskRunner to GCMInvalidationBridge for it to be able
90 ui_thread_task_runner_
->PostTask(
92 base::Bind(&GCMInvalidationBridge::CoreInitializationDone
,
94 weak_factory_
.GetWeakPtr(),
95 base::ThreadTaskRunnerHandle::Get()));
98 void GCMInvalidationBridge::Core::RequestToken(RequestTokenCallback callback
) {
99 DCHECK(CalledOnValidThread());
100 ui_thread_task_runner_
->PostTask(
102 base::Bind(&GCMInvalidationBridge::RequestToken
, bridge_
, callback
));
105 void GCMInvalidationBridge::Core::InvalidateToken(const std::string
& token
) {
106 DCHECK(CalledOnValidThread());
107 ui_thread_task_runner_
->PostTask(
109 base::Bind(&GCMInvalidationBridge::InvalidateToken
, bridge_
, token
));
112 void GCMInvalidationBridge::Core::Register(RegisterCallback callback
) {
113 DCHECK(CalledOnValidThread());
114 ui_thread_task_runner_
->PostTask(
116 base::Bind(&GCMInvalidationBridge::Register
, bridge_
, callback
));
119 void GCMInvalidationBridge::Core::SetMessageReceiver(MessageCallback callback
) {
120 message_callback_
= callback
;
121 ui_thread_task_runner_
->PostTask(
123 base::Bind(&GCMInvalidationBridge::SubscribeForIncomingMessages
,
127 void GCMInvalidationBridge::Core::RequestTokenFinished(
128 RequestTokenCallback callback
,
129 const GoogleServiceAuthError
& error
,
130 const std::string
& token
) {
131 DCHECK(CalledOnValidThread());
132 callback
.Run(error
, token
);
135 void GCMInvalidationBridge::Core::RegisterFinished(
136 RegisterCallback callback
,
137 const std::string
& registration_id
,
138 gcm::GCMClient::Result result
) {
139 DCHECK(CalledOnValidThread());
140 callback
.Run(registration_id
, result
);
143 void GCMInvalidationBridge::Core::OnIncomingMessage(
144 const std::string
& message
,
145 const std::string
& echo_token
) {
146 DCHECK(!message_callback_
.is_null());
147 message_callback_
.Run(message
, echo_token
);
150 GCMInvalidationBridge::GCMInvalidationBridge(
151 gcm::GCMService
* gcm_service
,
152 IdentityProvider
* identity_provider
)
153 : OAuth2TokenService::Consumer("gcm_network_channel"),
154 gcm_service_(gcm_service
),
155 identity_provider_(identity_provider
),
156 subscribed_for_incoming_messages_(false),
157 weak_factory_(this) {}
159 GCMInvalidationBridge::~GCMInvalidationBridge() {
160 if (subscribed_for_incoming_messages_
)
161 gcm_service_
->RemoveAppHandler(kInvalidationsAppId
);
164 scoped_ptr
<syncer::GCMNetworkChannelDelegate
>
165 GCMInvalidationBridge::CreateDelegate() {
166 DCHECK(CalledOnValidThread());
167 scoped_ptr
<syncer::GCMNetworkChannelDelegate
> core(new Core(
168 weak_factory_
.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get()));
172 void GCMInvalidationBridge::CoreInitializationDone(
173 base::WeakPtr
<Core
> core
,
174 scoped_refptr
<base::SingleThreadTaskRunner
> core_thread_task_runner
) {
175 DCHECK(CalledOnValidThread());
177 core_thread_task_runner_
= core_thread_task_runner
;
180 void GCMInvalidationBridge::RequestToken(
181 syncer::GCMNetworkChannelDelegate::RequestTokenCallback callback
) {
182 DCHECK(CalledOnValidThread());
183 if (access_token_request_
!= NULL
) {
184 // Report previous request as cancelled.
185 GoogleServiceAuthError
error(GoogleServiceAuthError::REQUEST_CANCELED
);
186 std::string access_token
;
187 core_thread_task_runner_
->PostTask(
189 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
191 request_token_callback_
,
195 request_token_callback_
= callback
;
196 OAuth2TokenService::ScopeSet scopes
;
197 scopes
.insert(GaiaConstants::kChromeSyncOAuth2Scope
);
198 access_token_request_
= identity_provider_
->GetTokenService()->StartRequest(
199 identity_provider_
->GetActiveAccountId(), scopes
, this);
202 void GCMInvalidationBridge::OnGetTokenSuccess(
203 const OAuth2TokenService::Request
* request
,
204 const std::string
& access_token
,
205 const base::Time
& expiration_time
) {
206 DCHECK(CalledOnValidThread());
207 DCHECK_EQ(access_token_request_
, request
);
208 core_thread_task_runner_
->PostTask(
210 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
212 request_token_callback_
,
213 GoogleServiceAuthError::AuthErrorNone(),
215 request_token_callback_
.Reset();
216 access_token_request_
.reset();
219 void GCMInvalidationBridge::OnGetTokenFailure(
220 const OAuth2TokenService::Request
* request
,
221 const GoogleServiceAuthError
& error
) {
222 DCHECK(CalledOnValidThread());
223 DCHECK_EQ(access_token_request_
, request
);
224 core_thread_task_runner_
->PostTask(
226 base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished
,
228 request_token_callback_
,
231 request_token_callback_
.Reset();
232 access_token_request_
.reset();
235 void GCMInvalidationBridge::InvalidateToken(const std::string
& token
) {
236 DCHECK(CalledOnValidThread());
237 OAuth2TokenService::ScopeSet scopes
;
238 scopes
.insert(GaiaConstants::kChromeSyncOAuth2Scope
);
239 identity_provider_
->GetTokenService()->InvalidateToken(
240 identity_provider_
->GetActiveAccountId(), scopes
, token
);
243 void GCMInvalidationBridge::Register(
244 syncer::GCMNetworkChannelDelegate::RegisterCallback callback
) {
245 DCHECK(CalledOnValidThread());
246 // No-op if GCMClient is disabled.
247 if (gcm_service_
== NULL
)
250 std::vector
<std::string
> sender_ids
;
251 sender_ids
.push_back(kInvalidationsSenderId
);
252 gcm_service_
->Register(kInvalidationsAppId
,
254 base::Bind(&GCMInvalidationBridge::RegisterFinished
,
255 weak_factory_
.GetWeakPtr(),
259 void GCMInvalidationBridge::RegisterFinished(
260 syncer::GCMNetworkChannelDelegate::RegisterCallback callback
,
261 const std::string
& registration_id
,
262 gcm::GCMClient::Result result
) {
263 DCHECK(CalledOnValidThread());
264 core_thread_task_runner_
->PostTask(
266 base::Bind(&GCMInvalidationBridge::Core::RegisterFinished
,
273 void GCMInvalidationBridge::SubscribeForIncomingMessages() {
274 // No-op if GCMClient is disabled.
275 if (gcm_service_
== NULL
)
278 DCHECK(!subscribed_for_incoming_messages_
);
279 gcm_service_
->AddAppHandler(kInvalidationsAppId
, this);
280 subscribed_for_incoming_messages_
= true;
283 void GCMInvalidationBridge::ShutdownHandler() {
287 void GCMInvalidationBridge::OnMessage(
288 const std::string
& app_id
,
289 const gcm::GCMClient::IncomingMessage
& message
) {
290 gcm::GCMClient::MessageData::const_iterator it
;
292 std::string echo_token
;
293 it
= message
.data
.find(kContentKey
);
294 if (it
!= message
.data
.end())
295 content
= it
->second
;
296 it
= message
.data
.find(kEchoTokenKey
);
297 if (it
!= message
.data
.end())
298 echo_token
= it
->second
;
300 core_thread_task_runner_
->PostTask(
302 base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage
,
308 void GCMInvalidationBridge::OnMessagesDeleted(const std::string
& app_id
) {
309 // Cacheinvalidation doesn't use long lived non-collapsable messages with GCM.
310 // Android implementation of cacheinvalidation doesn't handle MessagesDeleted
311 // callback so this should be no-op in desktop version as well.
314 void GCMInvalidationBridge::OnSendError(
315 const std::string
& app_id
,
316 const gcm::GCMClient::SendErrorDetails
& send_error_details
) {
317 // cacheinvalidation doesn't send messages over GCM.
321 } // namespace invalidation