• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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 
Core(base::WeakPtr<GCMInvalidationBridge> bridge,scoped_refptr<base::SingleThreadTaskRunner> ui_thread_task_runner)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 
~Core()85 GCMInvalidationBridge::Core::~Core() {}
86 
Initialize(ConnectionStateCallback callback)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 
RequestToken(RequestTokenCallback callback)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 
InvalidateToken(const std::string & token)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 
Register(RegisterCallback callback)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 
SetMessageReceiver(MessageCallback callback)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 
RequestTokenFinished(RequestTokenCallback callback,const GoogleServiceAuthError & error,const std::string & token)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 
RegisterFinished(RegisterCallback callback,const std::string & registration_id,gcm::GCMClient::Result result)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 
OnIncomingMessage(const std::string & message,const std::string & echo_token)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 
OnConnectionStateChanged(bool online)152 void GCMInvalidationBridge::Core::OnConnectionStateChanged(bool online) {
153   if (!connection_state_callback_.is_null()) {
154     connection_state_callback_.Run(online);
155   }
156 }
157 
GCMInvalidationBridge(gcm::GCMDriver * gcm_driver,IdentityProvider * identity_provider)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 
~GCMInvalidationBridge()167 GCMInvalidationBridge::~GCMInvalidationBridge() {
168   if (subscribed_for_incoming_messages_)
169     gcm_driver_->RemoveAppHandler(kInvalidationsAppId);
170 }
171 
172 scoped_ptr<syncer::GCMNetworkChannelDelegate>
CreateDelegate()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 
CoreInitializationDone(base::WeakPtr<Core> core,scoped_refptr<base::SingleThreadTaskRunner> core_thread_task_runner)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 
RequestToken(syncer::GCMNetworkChannelDelegate::RequestTokenCallback callback)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 
OnGetTokenSuccess(const OAuth2TokenService::Request * request,const std::string & access_token,const base::Time & expiration_time)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 
OnGetTokenFailure(const OAuth2TokenService::Request * request,const GoogleServiceAuthError & error)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 
InvalidateToken(const std::string & token)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 
Register(syncer::GCMNetworkChannelDelegate::RegisterCallback callback)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 
RegisterFinished(syncer::GCMNetworkChannelDelegate::RegisterCallback callback,const std::string & registration_id,gcm::GCMClient::Result result)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 
SubscribeForIncomingMessages()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 
ShutdownHandler()297 void GCMInvalidationBridge::ShutdownHandler() {
298   // Nothing to do.
299 }
300 
OnMessage(const std::string & app_id,const gcm::GCMClient::IncomingMessage & message)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 
OnMessagesDeleted(const std::string & app_id)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 
OnSendError(const std::string & app_id,const gcm::GCMClient::SendErrorDetails & send_error_details)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 
OnConnected(const net::IPEndPoint & ip_endpoint)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 
OnDisconnected()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