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
16namespace invalidation {
17namespace {
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.
22const 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).
26const char kInvalidationsAppId[] = "com.google.chrome.invalidations";
27
28// Cacheinvalidation specific gcm message keys.
29const char kContentKey[] = "content";
30const 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.
36class 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
75GCMInvalidationBridge::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
85GCMInvalidationBridge::Core::~Core() {}
86
87void 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
100void 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
107void 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
114void 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
121void 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
129void 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
137void 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
145void 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
152void GCMInvalidationBridge::Core::OnConnectionStateChanged(bool online) {
153  if (!connection_state_callback_.is_null()) {
154    connection_state_callback_.Run(online);
155  }
156}
157
158GCMInvalidationBridge::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
167GCMInvalidationBridge::~GCMInvalidationBridge() {
168  if (subscribed_for_incoming_messages_) {
169    gcm_driver_->RemoveAppHandler(kInvalidationsAppId);
170    gcm_driver_->RemoveConnectionObserver(this);
171  }
172}
173
174scoped_ptr<syncer::GCMNetworkChannelDelegate>
175GCMInvalidationBridge::CreateDelegate() {
176  DCHECK(CalledOnValidThread());
177  scoped_ptr<syncer::GCMNetworkChannelDelegate> core(new Core(
178      weak_factory_.GetWeakPtr(), base::ThreadTaskRunnerHandle::Get()));
179  return core.Pass();
180}
181
182void GCMInvalidationBridge::CoreInitializationDone(
183    base::WeakPtr<Core> core,
184    scoped_refptr<base::SingleThreadTaskRunner> core_thread_task_runner) {
185  DCHECK(CalledOnValidThread());
186  core_ = core;
187  core_thread_task_runner_ = core_thread_task_runner;
188}
189
190void 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(
198        FROM_HERE,
199        base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
200                   core_,
201                   request_token_callback_,
202                   error,
203                   access_token));
204  }
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);
210}
211
212void 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(
219      FROM_HERE,
220      base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
221                 core_,
222                 request_token_callback_,
223                 GoogleServiceAuthError::AuthErrorNone(),
224                 access_token));
225  request_token_callback_.Reset();
226  access_token_request_.reset();
227}
228
229void 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(
235      FROM_HERE,
236      base::Bind(&GCMInvalidationBridge::Core::RequestTokenFinished,
237                 core_,
238                 request_token_callback_,
239                 error,
240                 std::string()));
241  request_token_callback_.Reset();
242  access_token_request_.reset();
243}
244
245void 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);
251}
252
253void GCMInvalidationBridge::Register(
254    syncer::GCMNetworkChannelDelegate::RegisterCallback callback) {
255  DCHECK(CalledOnValidThread());
256  // No-op if GCMClient is disabled.
257  if (gcm_driver_ == NULL)
258    return;
259
260  std::vector<std::string> sender_ids;
261  sender_ids.push_back(kInvalidationsSenderId);
262  gcm_driver_->Register(kInvalidationsAppId,
263                         sender_ids,
264                         base::Bind(&GCMInvalidationBridge::RegisterFinished,
265                                    weak_factory_.GetWeakPtr(),
266                                    callback));
267}
268
269void 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(
275      FROM_HERE,
276      base::Bind(&GCMInvalidationBridge::Core::RegisterFinished,
277                 core_,
278                 callback,
279                 registration_id,
280                 result));
281}
282
283void GCMInvalidationBridge::SubscribeForIncomingMessages() {
284  // No-op if GCMClient is disabled.
285  if (gcm_driver_ == NULL)
286    return;
287
288  DCHECK(!subscribed_for_incoming_messages_);
289  gcm_driver_->AddAppHandler(kInvalidationsAppId, this);
290  gcm_driver_->AddConnectionObserver(this);
291  core_thread_task_runner_->PostTask(
292      FROM_HERE,
293      base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged,
294                 core_,
295                 gcm_driver_->IsConnected()));
296
297  subscribed_for_incoming_messages_ = true;
298}
299
300void GCMInvalidationBridge::ShutdownHandler() {
301  // Nothing to do.
302}
303
304void GCMInvalidationBridge::OnMessage(
305    const std::string& app_id,
306    const gcm::GCMClient::IncomingMessage& message) {
307  gcm::GCMClient::MessageData::const_iterator it;
308  std::string content;
309  std::string echo_token;
310  it = message.data.find(kContentKey);
311  if (it != message.data.end())
312    content = it->second;
313  it = message.data.find(kEchoTokenKey);
314  if (it != message.data.end())
315    echo_token = it->second;
316
317  core_thread_task_runner_->PostTask(
318      FROM_HERE,
319      base::Bind(&GCMInvalidationBridge::Core::OnIncomingMessage,
320                 core_,
321                 content,
322                 echo_token));
323}
324
325void GCMInvalidationBridge::OnMessagesDeleted(const std::string& app_id) {
326  // Cacheinvalidation doesn't use long lived non-collapsable messages with GCM.
327  // Android implementation of cacheinvalidation doesn't handle MessagesDeleted
328  // callback so this should be no-op in desktop version as well.
329}
330
331void GCMInvalidationBridge::OnSendError(
332    const std::string& app_id,
333    const gcm::GCMClient::SendErrorDetails& send_error_details) {
334  // cacheinvalidation doesn't send messages over GCM.
335  NOTREACHED();
336}
337
338void GCMInvalidationBridge::OnSendAcknowledged(
339    const std::string& app_id,
340    const std::string& message_id) {
341  // cacheinvalidation doesn't send messages over GCM.
342  NOTREACHED();
343}
344
345void GCMInvalidationBridge::OnConnected(const net::IPEndPoint& ip_endpoint) {
346  core_thread_task_runner_->PostTask(
347      FROM_HERE,
348      base::Bind(
349          &GCMInvalidationBridge::Core::OnConnectionStateChanged, core_, true));
350}
351
352void GCMInvalidationBridge::OnDisconnected() {
353  core_thread_task_runner_->PostTask(
354      FROM_HERE,
355      base::Bind(&GCMInvalidationBridge::Core::OnConnectionStateChanged,
356                 core_,
357                 false));
358}
359
360
361}  // namespace invalidation
362