Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / extensions / browser / api / bluetooth_socket / bluetooth_socket_event_dispatcher.cc
blob662b9b7ca678956bfae6d882e67d5e9b12baa6cd
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.
5 #include "extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.h"
7 #include "base/lazy_instance.h"
8 #include "device/bluetooth/bluetooth_device.h"
9 #include "device/bluetooth/bluetooth_socket.h"
10 #include "extensions/browser/api/bluetooth_socket/bluetooth_api_socket.h"
11 #include "extensions/browser/event_router.h"
12 #include "extensions/common/api/bluetooth_socket.h"
13 #include "net/base/io_buffer.h"
15 namespace {
17 namespace bluetooth_socket = extensions::core_api::bluetooth_socket;
18 using extensions::BluetoothApiSocket;
20 int kDefaultBufferSize = 4096;
22 bluetooth_socket::ReceiveError MapReceiveErrorReason(
23 BluetoothApiSocket::ErrorReason value) {
24 switch (value) {
25 case BluetoothApiSocket::kDisconnected:
26 return bluetooth_socket::RECEIVE_ERROR_DISCONNECTED;
27 case BluetoothApiSocket::kNotConnected:
28 // kNotConnected is impossible since a socket has to be connected to be
29 // able to call Receive() on it.
30 // fallthrough
31 case BluetoothApiSocket::kIOPending:
32 // kIOPending is not relevant to apps, as BluetoothSocketEventDispatcher
33 // handles this specific error.
34 // fallthrough
35 default:
36 return bluetooth_socket::RECEIVE_ERROR_SYSTEM_ERROR;
40 bluetooth_socket::AcceptError MapAcceptErrorReason(
41 BluetoothApiSocket::ErrorReason value) {
42 // TODO(keybuk): All values are system error, we may want to seperate these
43 // out to more discrete reasons.
44 switch (value) {
45 case BluetoothApiSocket::kNotListening:
46 // kNotListening is impossible since a socket has to be listening to be
47 // able to call Accept() on it.
48 // fallthrough
49 default:
50 return bluetooth_socket::ACCEPT_ERROR_SYSTEM_ERROR;
54 } // namespace
56 namespace extensions {
57 namespace core_api {
59 using content::BrowserThread;
61 static base::LazyInstance<
62 BrowserContextKeyedAPIFactory<BluetoothSocketEventDispatcher> > g_factory =
63 LAZY_INSTANCE_INITIALIZER;
65 // static
66 BrowserContextKeyedAPIFactory<BluetoothSocketEventDispatcher>*
67 BluetoothSocketEventDispatcher::GetFactoryInstance() {
68 return g_factory.Pointer();
71 // static
72 BluetoothSocketEventDispatcher* BluetoothSocketEventDispatcher::Get(
73 content::BrowserContext* context) {
74 DCHECK_CURRENTLY_ON(BrowserThread::UI);
76 return BrowserContextKeyedAPIFactory<BluetoothSocketEventDispatcher>::Get(
77 context);
80 BluetoothSocketEventDispatcher::BluetoothSocketEventDispatcher(
81 content::BrowserContext* context)
82 : thread_id_(BluetoothApiSocket::kThreadId),
83 browser_context_(context) {
84 ApiResourceManager<BluetoothApiSocket>* manager =
85 ApiResourceManager<BluetoothApiSocket>::Get(browser_context_);
86 DCHECK(manager)
87 << "There is no socket manager. "
88 "If this assertion is failing during a test, then it is likely that "
89 "TestExtensionSystem is failing to provide an instance of "
90 "ApiResourceManager<BluetoothApiSocket>.";
91 sockets_ = manager->data_;
94 BluetoothSocketEventDispatcher::~BluetoothSocketEventDispatcher() {}
96 BluetoothSocketEventDispatcher::SocketParams::SocketParams() {}
98 BluetoothSocketEventDispatcher::SocketParams::~SocketParams() {}
100 void BluetoothSocketEventDispatcher::OnSocketConnect(
101 const std::string& extension_id,
102 int socket_id) {
103 DCHECK(BrowserThread::CurrentlyOn(thread_id_));
105 SocketParams params;
106 params.thread_id = thread_id_;
107 params.browser_context_id = browser_context_;
108 params.extension_id = extension_id;
109 params.sockets = sockets_;
110 params.socket_id = socket_id;
112 StartReceive(params);
115 void BluetoothSocketEventDispatcher::OnSocketListen(
116 const std::string& extension_id,
117 int socket_id) {
118 DCHECK(BrowserThread::CurrentlyOn(thread_id_));
120 SocketParams params;
121 params.thread_id = thread_id_;
122 params.browser_context_id = browser_context_;
123 params.extension_id = extension_id;
124 params.sockets = sockets_;
125 params.socket_id = socket_id;
127 StartAccept(params);
130 void BluetoothSocketEventDispatcher::OnSocketResume(
131 const std::string& extension_id,
132 int socket_id) {
133 DCHECK(BrowserThread::CurrentlyOn(thread_id_));
135 SocketParams params;
136 params.thread_id = thread_id_;
137 params.browser_context_id = browser_context_;
138 params.extension_id = extension_id;
139 params.sockets = sockets_;
140 params.socket_id = socket_id;
142 BluetoothApiSocket* socket =
143 params.sockets->Get(params.extension_id, params.socket_id);
144 if (!socket) {
145 // This can happen if the socket is closed while our callback is active.
146 return;
149 if (socket->IsConnected()) {
150 StartReceive(params);
151 } else {
152 StartAccept(params);
156 // static
157 void BluetoothSocketEventDispatcher::StartReceive(const SocketParams& params) {
158 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
160 BluetoothApiSocket* socket =
161 params.sockets->Get(params.extension_id, params.socket_id);
162 if (!socket) {
163 // This can happen if the socket is closed while our callback is active.
164 return;
166 DCHECK(params.extension_id == socket->owner_extension_id())
167 << "Socket has wrong owner.";
169 // Don't start another read if the socket has been paused.
170 if (socket->paused())
171 return;
173 int buffer_size = socket->buffer_size();
174 if (buffer_size <= 0)
175 buffer_size = kDefaultBufferSize;
176 socket->Receive(
177 buffer_size,
178 base::Bind(
179 &BluetoothSocketEventDispatcher::ReceiveCallback, params),
180 base::Bind(
181 &BluetoothSocketEventDispatcher::ReceiveErrorCallback, params));
184 // static
185 void BluetoothSocketEventDispatcher::ReceiveCallback(
186 const SocketParams& params,
187 int bytes_read,
188 scoped_refptr<net::IOBuffer> io_buffer) {
189 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
191 // Dispatch "onReceive" event.
192 bluetooth_socket::ReceiveInfo receive_info;
193 receive_info.socket_id = params.socket_id;
194 receive_info.data.assign(io_buffer->data(), io_buffer->data() + bytes_read);
195 scoped_ptr<base::ListValue> args =
196 bluetooth_socket::OnReceive::Create(receive_info);
197 scoped_ptr<Event> event(
198 new Event(bluetooth_socket::OnReceive::kEventName, args.Pass()));
199 PostEvent(params, event.Pass());
201 // Post a task to delay the read until the socket is available, as
202 // calling StartReceive at this point would error with ERR_IO_PENDING.
203 BrowserThread::PostTask(
204 params.thread_id,
205 FROM_HERE,
206 base::Bind(&BluetoothSocketEventDispatcher::StartReceive, params));
209 // static
210 void BluetoothSocketEventDispatcher::ReceiveErrorCallback(
211 const SocketParams& params,
212 BluetoothApiSocket::ErrorReason error_reason,
213 const std::string& error) {
214 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
216 if (error_reason == BluetoothApiSocket::kIOPending) {
217 // This happens when resuming a socket which already had an active "read"
218 // callback. We can safely ignore this error, as the application should not
219 // care.
220 return;
223 // Dispatch "onReceiveError" event but don't start another read to avoid
224 // potential infinite reads if we have a persistent network error.
225 bluetooth_socket::ReceiveErrorInfo receive_error_info;
226 receive_error_info.socket_id = params.socket_id;
227 receive_error_info.error_message = error;
228 receive_error_info.error = MapReceiveErrorReason(error_reason);
229 scoped_ptr<base::ListValue> args =
230 bluetooth_socket::OnReceiveError::Create(receive_error_info);
231 scoped_ptr<Event> event(
232 new Event(bluetooth_socket::OnReceiveError::kEventName, args.Pass()));
233 PostEvent(params, event.Pass());
235 // Since we got an error, the socket is now "paused" until the application
236 // "resumes" it.
237 BluetoothApiSocket* socket =
238 params.sockets->Get(params.extension_id, params.socket_id);
239 if (socket) {
240 socket->set_paused(true);
244 // static
245 void BluetoothSocketEventDispatcher::StartAccept(const SocketParams& params) {
246 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
248 BluetoothApiSocket* socket =
249 params.sockets->Get(params.extension_id, params.socket_id);
250 if (!socket) {
251 // This can happen if the socket is closed while our callback is active.
252 return;
254 DCHECK(params.extension_id == socket->owner_extension_id())
255 << "Socket has wrong owner.";
257 // Don't start another accept if the socket has been paused.
258 if (socket->paused())
259 return;
261 socket->Accept(
262 base::Bind(
263 &BluetoothSocketEventDispatcher::AcceptCallback, params),
264 base::Bind(
265 &BluetoothSocketEventDispatcher::AcceptErrorCallback, params));
268 // static
269 void BluetoothSocketEventDispatcher::AcceptCallback(
270 const SocketParams& params,
271 const device::BluetoothDevice* device,
272 scoped_refptr<device::BluetoothSocket> socket) {
273 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
275 BluetoothApiSocket* server_api_socket =
276 params.sockets->Get(params.extension_id, params.socket_id);
277 DCHECK(server_api_socket);
279 BluetoothApiSocket* client_api_socket = new BluetoothApiSocket(
280 params.extension_id,
281 socket,
282 device->GetAddress(),
283 server_api_socket->uuid());
284 int client_socket_id = params.sockets->Add(client_api_socket);
286 // Dispatch "onAccept" event.
287 bluetooth_socket::AcceptInfo accept_info;
288 accept_info.socket_id = params.socket_id;
289 accept_info.client_socket_id = client_socket_id;
290 scoped_ptr<base::ListValue> args =
291 bluetooth_socket::OnAccept::Create(accept_info);
292 scoped_ptr<Event> event(
293 new Event(bluetooth_socket::OnAccept::kEventName, args.Pass()));
294 PostEvent(params, event.Pass());
296 // Post a task to delay the accept until the socket is available, as
297 // calling StartAccept at this point would error with ERR_IO_PENDING.
298 BrowserThread::PostTask(
299 params.thread_id,
300 FROM_HERE,
301 base::Bind(&BluetoothSocketEventDispatcher::StartAccept, params));
304 // static
305 void BluetoothSocketEventDispatcher::AcceptErrorCallback(
306 const SocketParams& params,
307 BluetoothApiSocket::ErrorReason error_reason,
308 const std::string& error) {
309 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
311 if (error_reason == BluetoothApiSocket::kIOPending) {
312 // This happens when resuming a socket which already had an active "accept"
313 // callback. We can safely ignore this error, as the application should not
314 // care.
315 return;
318 // Dispatch "onAcceptError" event but don't start another accept to avoid
319 // potential infinite accepts if we have a persistent network error.
320 bluetooth_socket::AcceptErrorInfo accept_error_info;
321 accept_error_info.socket_id = params.socket_id;
322 accept_error_info.error_message = error;
323 accept_error_info.error = MapAcceptErrorReason(error_reason);
324 scoped_ptr<base::ListValue> args =
325 bluetooth_socket::OnAcceptError::Create(accept_error_info);
326 scoped_ptr<Event> event(
327 new Event(bluetooth_socket::OnAcceptError::kEventName, args.Pass()));
328 PostEvent(params, event.Pass());
330 // Since we got an error, the socket is now "paused" until the application
331 // "resumes" it.
332 BluetoothApiSocket* socket =
333 params.sockets->Get(params.extension_id, params.socket_id);
334 if (socket) {
335 socket->set_paused(true);
339 // static
340 void BluetoothSocketEventDispatcher::PostEvent(const SocketParams& params,
341 scoped_ptr<Event> event) {
342 DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
344 BrowserThread::PostTask(
345 BrowserThread::UI,
346 FROM_HERE,
347 base::Bind(&DispatchEvent,
348 params.browser_context_id,
349 params.extension_id,
350 base::Passed(event.Pass())));
353 // static
354 void BluetoothSocketEventDispatcher::DispatchEvent(
355 void* browser_context_id,
356 const std::string& extension_id,
357 scoped_ptr<Event> event) {
358 DCHECK_CURRENTLY_ON(BrowserThread::UI);
360 content::BrowserContext* context =
361 reinterpret_cast<content::BrowserContext*>(browser_context_id);
362 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
363 return;
365 EventRouter* router = EventRouter::Get(context);
366 if (router)
367 router->DispatchEventToExtension(extension_id, event.Pass());
370 } // namespace core_api
371 } // namespace extensions