OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "jingle/notifier/listener/push_client.h" | 5 #include "jingle/notifier/listener/push_client.h" |
6 | 6 |
| 7 #include <cstddef> |
| 8 |
7 #include "base/bind.h" | 9 #include "base/bind.h" |
8 #include "base/compiler_specific.h" | 10 #include "base/single_thread_task_runner.h" |
9 #include "base/location.h" | 11 #include "jingle/notifier/listener/non_blocking_push_client.h" |
10 #include "base/logging.h" | 12 #include "jingle/notifier/listener/xmpp_push_client.h" |
11 #include "base/memory/scoped_ptr.h" | |
12 #include "base/observer_list_threadsafe.h" | |
13 #include "jingle/notifier/base/notifier_options_util.h" | |
14 #include "jingle/notifier/communicator/login.h" | |
15 #include "jingle/notifier/listener/push_notifications_listen_task.h" | |
16 #include "jingle/notifier/listener/push_notifications_send_update_task.h" | |
17 #include "jingle/notifier/listener/push_notifications_subscribe_task.h" | |
18 #include "talk/xmpp/xmppclientsettings.h" | |
19 | 13 |
20 namespace notifier { | 14 namespace notifier { |
21 | 15 |
22 PushClient::Observer::~Observer() {} | 16 PushClient::~PushClient() {} |
23 | 17 |
24 // All member functions except for the constructor, destructor, and | 18 namespace { |
25 // {Add,Remove}Observer() must be called on the IO thread (as taken from | |
26 // |notifier_options|). | |
27 class PushClient::Core | |
28 : public base::RefCountedThreadSafe<PushClient::Core>, | |
29 public LoginDelegate, | |
30 public PushNotificationsListenTaskDelegate, | |
31 public PushNotificationsSubscribeTaskDelegate { | |
32 public: | |
33 // Called on the parent thread. | |
34 explicit Core(const NotifierOptions& notifier_options); | |
35 | 19 |
36 // Must be called before being destroyed. | 20 scoped_ptr<PushClient> CreateXmppPushClient( |
37 void DestroyOnIOThread(); | 21 const NotifierOptions& notifier_options) { |
38 | 22 return scoped_ptr<PushClient>(new XmppPushClient(notifier_options)); |
39 // Login::Delegate implementation. | |
40 virtual void OnConnect( | |
41 base::WeakPtr<buzz::XmppTaskParentInterface> base_task) OVERRIDE; | |
42 virtual void OnDisconnect(); | |
43 | |
44 // PushNotificationsListenTaskDelegate implementation. | |
45 virtual void OnNotificationReceived( | |
46 const Notification& notification) OVERRIDE; | |
47 | |
48 // PushNotificationsSubscribeTaskDelegate implementation. | |
49 virtual void OnSubscribed() OVERRIDE; | |
50 virtual void OnSubscriptionError() OVERRIDE; | |
51 | |
52 // Called on the parent thread. | |
53 void AddObserver(Observer* observer); | |
54 void RemoveObserver(Observer* observer); | |
55 | |
56 void UpdateSubscriptions(const SubscriptionList& subscriptions); | |
57 void UpdateCredentials(const std::string& email, const std::string& token); | |
58 void SendNotification(const Notification& data); | |
59 | |
60 // Any notifications sent after this is called will be reflected, | |
61 // i.e. will be treated as an incoming notification also. | |
62 void ReflectSentNotificationsForTest(); | |
63 | |
64 private: | |
65 friend class base::RefCountedThreadSafe<PushClient::Core>; | |
66 | |
67 // Called on either the parent thread or the I/O thread. | |
68 virtual ~Core(); | |
69 | |
70 const NotifierOptions notifier_options_; | |
71 const scoped_refptr<base::MessageLoopProxy> parent_message_loop_proxy_; | |
72 const scoped_refptr<base::MessageLoopProxy> io_message_loop_proxy_; | |
73 const scoped_refptr<ObserverListThreadSafe<Observer> > observers_; | |
74 | |
75 // XMPP connection settings. | |
76 SubscriptionList subscriptions_; | |
77 buzz::XmppClientSettings xmpp_settings_; | |
78 | |
79 // Must be created/used/destroyed only on the IO thread. | |
80 scoped_ptr<notifier::Login> login_; | |
81 | |
82 // The XMPP connection. | |
83 base::WeakPtr<buzz::XmppTaskParentInterface> base_task_; | |
84 | |
85 std::vector<Notification> pending_notifications_to_send_; | |
86 | |
87 bool reflect_sent_notifications_for_test_; | |
88 | |
89 DISALLOW_COPY_AND_ASSIGN(Core); | |
90 }; | |
91 | |
92 PushClient::Core::Core(const NotifierOptions& notifier_options) | |
93 : notifier_options_(notifier_options), | |
94 parent_message_loop_proxy_(base::MessageLoopProxy::current()), | |
95 io_message_loop_proxy_( | |
96 notifier_options_.request_context_getter->GetIOMessageLoopProxy()), | |
97 observers_(new ObserverListThreadSafe<Observer>()), | |
98 reflect_sent_notifications_for_test_(false) {} | |
99 | |
100 PushClient::Core::~Core() { | |
101 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread() || | |
102 io_message_loop_proxy_->BelongsToCurrentThread()); | |
103 DCHECK(!login_.get()); | |
104 DCHECK(!base_task_.get()); | |
105 observers_->AssertEmpty(); | |
106 } | 23 } |
107 | 24 |
108 void PushClient::Core::DestroyOnIOThread() { | 25 } // namespace |
109 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
110 login_.reset(); | |
111 base_task_.reset(); | |
112 } | |
113 | 26 |
114 void PushClient::Core::OnConnect( | 27 scoped_ptr<PushClient> PushClient::CreateDefault( |
115 base::WeakPtr<buzz::XmppTaskParentInterface> base_task) { | 28 const NotifierOptions& notifier_options) { |
116 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | 29 return scoped_ptr<PushClient>(new NonBlockingPushClient( |
117 base_task_ = base_task; | 30 notifier_options.request_context_getter->GetIOMessageLoopProxy(), |
118 | 31 base::Bind(&CreateXmppPushClient, notifier_options))); |
119 if (!base_task_.get()) { | |
120 NOTREACHED(); | |
121 return; | |
122 } | |
123 | |
124 // Listen for notifications. | |
125 { | |
126 // Owned by |base_task_|. | |
127 PushNotificationsListenTask* listener = | |
128 new PushNotificationsListenTask(base_task_, this); | |
129 listener->Start(); | |
130 } | |
131 | |
132 // Send subscriptions. | |
133 { | |
134 // Owned by |base_task_|. | |
135 PushNotificationsSubscribeTask* subscribe_task = | |
136 new PushNotificationsSubscribeTask(base_task_, subscriptions_, this); | |
137 subscribe_task->Start(); | |
138 } | |
139 | |
140 std::vector<Notification> notifications_to_send; | |
141 notifications_to_send.swap(pending_notifications_to_send_); | |
142 for (std::vector<Notification>::const_iterator it = | |
143 notifications_to_send.begin(); | |
144 it != notifications_to_send.end(); ++it) { | |
145 DVLOG(1) << "Push: Sending pending notification " << it->ToString(); | |
146 SendNotification(*it); | |
147 } | |
148 } | |
149 | |
150 void PushClient::Core::OnDisconnect() { | |
151 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
152 base_task_.reset(); | |
153 observers_->Notify(&Observer::OnNotificationStateChange, false); | |
154 } | |
155 | |
156 void PushClient::Core::OnNotificationReceived( | |
157 const Notification& notification) { | |
158 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
159 observers_->Notify(&Observer::OnIncomingNotification, notification); | |
160 } | |
161 | |
162 void PushClient::Core::OnSubscribed() { | |
163 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
164 observers_->Notify(&Observer::OnNotificationStateChange, true); | |
165 } | |
166 | |
167 void PushClient::Core::OnSubscriptionError() { | |
168 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
169 observers_->Notify(&Observer::OnNotificationStateChange, false); | |
170 } | |
171 | |
172 void PushClient::Core::AddObserver(Observer* observer) { | |
173 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
174 observers_->AddObserver(observer); | |
175 } | |
176 | |
177 void PushClient::Core::RemoveObserver(Observer* observer) { | |
178 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
179 observers_->RemoveObserver(observer); | |
180 } | |
181 | |
182 void PushClient::Core::UpdateSubscriptions( | |
183 const SubscriptionList& subscriptions) { | |
184 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
185 subscriptions_ = subscriptions; | |
186 } | |
187 | |
188 void PushClient::Core::UpdateCredentials( | |
189 const std::string& email, const std::string& token) { | |
190 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
191 DVLOG(1) << "Push: Updating credentials for " << email; | |
192 xmpp_settings_ = MakeXmppClientSettings(notifier_options_, email, token); | |
193 if (login_.get()) { | |
194 login_->UpdateXmppSettings(xmpp_settings_); | |
195 } else { | |
196 DVLOG(1) << "Push: Starting XMPP connection"; | |
197 base_task_.reset(); | |
198 login_.reset(new notifier::Login(this, | |
199 xmpp_settings_, | |
200 notifier_options_.request_context_getter, | |
201 GetServerList(notifier_options_), | |
202 notifier_options_.try_ssltcp_first, | |
203 notifier_options_.auth_mechanism)); | |
204 login_->StartConnection(); | |
205 } | |
206 } | |
207 | |
208 void PushClient::Core::SendNotification(const Notification& notification) { | |
209 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
210 if (!base_task_.get()) { | |
211 DVLOG(1) << "Push: Cannot send notification " | |
212 << notification.ToString() << "; sending later"; | |
213 pending_notifications_to_send_.push_back(notification); | |
214 return; | |
215 } | |
216 // Owned by |base_task_|. | |
217 PushNotificationsSendUpdateTask* task = | |
218 new PushNotificationsSendUpdateTask(base_task_, notification); | |
219 task->Start(); | |
220 | |
221 if (reflect_sent_notifications_for_test_) { | |
222 OnNotificationReceived(notification); | |
223 } | |
224 } | |
225 | |
226 void PushClient::Core::ReflectSentNotificationsForTest() { | |
227 DCHECK(io_message_loop_proxy_->BelongsToCurrentThread()); | |
228 reflect_sent_notifications_for_test_ = true; | |
229 } | |
230 | |
231 PushClient::PushClient(const NotifierOptions& notifier_options) | |
232 : core_(new Core(notifier_options)), | |
233 parent_message_loop_proxy_(base::MessageLoopProxy::current()), | |
234 io_message_loop_proxy_( | |
235 notifier_options.request_context_getter->GetIOMessageLoopProxy()) { | |
236 } | |
237 | |
238 PushClient::~PushClient() { | |
239 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
240 io_message_loop_proxy_->PostTask( | |
241 FROM_HERE, | |
242 base::Bind(&PushClient::Core::DestroyOnIOThread, core_.get())); | |
243 } | |
244 | |
245 void PushClient::AddObserver(Observer* observer) { | |
246 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
247 core_->AddObserver(observer); | |
248 } | |
249 | |
250 void PushClient::RemoveObserver(Observer* observer) { | |
251 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
252 core_->RemoveObserver(observer); | |
253 } | |
254 | |
255 void PushClient::UpdateSubscriptions(const SubscriptionList& subscriptions) { | |
256 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
257 io_message_loop_proxy_->PostTask( | |
258 FROM_HERE, | |
259 base::Bind(&PushClient::Core::UpdateSubscriptions, | |
260 core_.get(), subscriptions)); | |
261 } | |
262 | |
263 void PushClient::UpdateCredentials( | |
264 const std::string& email, const std::string& token) { | |
265 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
266 io_message_loop_proxy_->PostTask( | |
267 FROM_HERE, | |
268 base::Bind(&PushClient::Core::UpdateCredentials, | |
269 core_.get(), email, token)); | |
270 } | |
271 | |
272 void PushClient::SendNotification(const Notification& notification) { | |
273 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
274 io_message_loop_proxy_->PostTask( | |
275 FROM_HERE, | |
276 base::Bind(&PushClient::Core::SendNotification, core_.get(), | |
277 notification)); | |
278 } | |
279 | |
280 void PushClient::SimulateOnNotificationReceivedForTest( | |
281 const Notification& notification) { | |
282 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
283 io_message_loop_proxy_->PostTask( | |
284 FROM_HERE, | |
285 base::Bind(&PushClient::Core::OnNotificationReceived, | |
286 core_.get(), notification)); | |
287 } | |
288 | |
289 void PushClient::SimulateConnectAndSubscribeForTest( | |
290 base::WeakPtr<buzz::XmppTaskParentInterface> base_task) { | |
291 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
292 io_message_loop_proxy_->PostTask( | |
293 FROM_HERE, | |
294 base::Bind(&PushClient::Core::OnConnect, core_.get(), base_task)); | |
295 io_message_loop_proxy_->PostTask( | |
296 FROM_HERE, | |
297 base::Bind(&PushClient::Core::OnSubscribed, core_.get())); | |
298 } | |
299 | |
300 void PushClient::SimulateDisconnectForTest() { | |
301 DCHECK(parent_message_loop_proxy_->BelongsToCurrentThread()); | |
302 io_message_loop_proxy_->PostTask( | |
303 FROM_HERE, | |
304 base::Bind(&PushClient::Core::OnDisconnect, core_.get())); | |
305 } | |
306 | |
307 void PushClient::SimulateSubscriptionErrorForTest() { | |
308 io_message_loop_proxy_->PostTask( | |
309 FROM_HERE, | |
310 base::Bind(&PushClient::Core::OnSubscriptionError, core_.get())); | |
311 } | |
312 | |
313 void PushClient::ReflectSentNotificationsForTest() { | |
314 io_message_loop_proxy_->PostTask( | |
315 FROM_HERE, | |
316 base::Bind(&PushClient::Core::ReflectSentNotificationsForTest, | |
317 core_.get())); | |
318 } | 32 } |
319 | 33 |
320 } // namespace notifier | 34 } // namespace notifier |
OLD | NEW |