Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / extensions / browser / api / bluetooth_socket / bluetooth_socket_event_dispatcher.cc
blobf040ef2c0ad5a986d53a8d460a1040cbb250d475
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::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 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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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_CURRENTLY_ON(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(new Event(events::BLUETOOTH_SOCKET_ON_RECEIVE,
198 bluetooth_socket::OnReceive::kEventName,
199 args.Pass()));
200 PostEvent(params, event.Pass());
202 // Post a task to delay the read until the socket is available, as
203 // calling StartReceive at this point would error with ERR_IO_PENDING.
204 BrowserThread::PostTask(
205 params.thread_id,
206 FROM_HERE,
207 base::Bind(&BluetoothSocketEventDispatcher::StartReceive, params));
210 // static
211 void BluetoothSocketEventDispatcher::ReceiveErrorCallback(
212 const SocketParams& params,
213 BluetoothApiSocket::ErrorReason error_reason,
214 const std::string& error) {
215 DCHECK_CURRENTLY_ON(params.thread_id);
217 if (error_reason == BluetoothApiSocket::kIOPending) {
218 // This happens when resuming a socket which already had an active "read"
219 // callback. We can safely ignore this error, as the application should not
220 // care.
221 return;
224 // Dispatch "onReceiveError" event but don't start another read to avoid
225 // potential infinite reads if we have a persistent network error.
226 bluetooth_socket::ReceiveErrorInfo receive_error_info;
227 receive_error_info.socket_id = params.socket_id;
228 receive_error_info.error_message = error;
229 receive_error_info.error = MapReceiveErrorReason(error_reason);
230 scoped_ptr<base::ListValue> args =
231 bluetooth_socket::OnReceiveError::Create(receive_error_info);
232 scoped_ptr<Event> event(
233 new Event(events::BLUETOOTH_SOCKET_ON_RECEIVE_ERROR,
234 bluetooth_socket::OnReceiveError::kEventName, args.Pass()));
235 PostEvent(params, event.Pass());
237 // Since we got an error, the socket is now "paused" until the application
238 // "resumes" it.
239 BluetoothApiSocket* socket =
240 params.sockets->Get(params.extension_id, params.socket_id);
241 if (socket) {
242 socket->set_paused(true);
246 // static
247 void BluetoothSocketEventDispatcher::StartAccept(const SocketParams& params) {
248 DCHECK_CURRENTLY_ON(params.thread_id);
250 BluetoothApiSocket* socket =
251 params.sockets->Get(params.extension_id, params.socket_id);
252 if (!socket) {
253 // This can happen if the socket is closed while our callback is active.
254 return;
256 DCHECK(params.extension_id == socket->owner_extension_id())
257 << "Socket has wrong owner.";
259 // Don't start another accept if the socket has been paused.
260 if (socket->paused())
261 return;
263 socket->Accept(
264 base::Bind(
265 &BluetoothSocketEventDispatcher::AcceptCallback, params),
266 base::Bind(
267 &BluetoothSocketEventDispatcher::AcceptErrorCallback, params));
270 // static
271 void BluetoothSocketEventDispatcher::AcceptCallback(
272 const SocketParams& params,
273 const device::BluetoothDevice* device,
274 scoped_refptr<device::BluetoothSocket> socket) {
275 DCHECK_CURRENTLY_ON(params.thread_id);
277 BluetoothApiSocket* server_api_socket =
278 params.sockets->Get(params.extension_id, params.socket_id);
279 DCHECK(server_api_socket);
281 BluetoothApiSocket* client_api_socket = new BluetoothApiSocket(
282 params.extension_id,
283 socket,
284 device->GetAddress(),
285 server_api_socket->uuid());
286 int client_socket_id = params.sockets->Add(client_api_socket);
288 // Dispatch "onAccept" event.
289 bluetooth_socket::AcceptInfo accept_info;
290 accept_info.socket_id = params.socket_id;
291 accept_info.client_socket_id = client_socket_id;
292 scoped_ptr<base::ListValue> args =
293 bluetooth_socket::OnAccept::Create(accept_info);
294 scoped_ptr<Event> event(new Event(events::BLUETOOTH_SOCKET_ON_ACCEPT,
295 bluetooth_socket::OnAccept::kEventName,
296 args.Pass()));
297 PostEvent(params, event.Pass());
299 // Post a task to delay the accept until the socket is available, as
300 // calling StartAccept at this point would error with ERR_IO_PENDING.
301 BrowserThread::PostTask(
302 params.thread_id,
303 FROM_HERE,
304 base::Bind(&BluetoothSocketEventDispatcher::StartAccept, params));
307 // static
308 void BluetoothSocketEventDispatcher::AcceptErrorCallback(
309 const SocketParams& params,
310 BluetoothApiSocket::ErrorReason error_reason,
311 const std::string& error) {
312 DCHECK_CURRENTLY_ON(params.thread_id);
314 if (error_reason == BluetoothApiSocket::kIOPending) {
315 // This happens when resuming a socket which already had an active "accept"
316 // callback. We can safely ignore this error, as the application should not
317 // care.
318 return;
321 // Dispatch "onAcceptError" event but don't start another accept to avoid
322 // potential infinite accepts if we have a persistent network error.
323 bluetooth_socket::AcceptErrorInfo accept_error_info;
324 accept_error_info.socket_id = params.socket_id;
325 accept_error_info.error_message = error;
326 accept_error_info.error = MapAcceptErrorReason(error_reason);
327 scoped_ptr<base::ListValue> args =
328 bluetooth_socket::OnAcceptError::Create(accept_error_info);
329 scoped_ptr<Event> event(new Event(events::BLUETOOTH_SOCKET_ON_ACCEPT_ERROR,
330 bluetooth_socket::OnAcceptError::kEventName,
331 args.Pass()));
332 PostEvent(params, event.Pass());
334 // Since we got an error, the socket is now "paused" until the application
335 // "resumes" it.
336 BluetoothApiSocket* socket =
337 params.sockets->Get(params.extension_id, params.socket_id);
338 if (socket) {
339 socket->set_paused(true);
343 // static
344 void BluetoothSocketEventDispatcher::PostEvent(const SocketParams& params,
345 scoped_ptr<Event> event) {
346 DCHECK_CURRENTLY_ON(params.thread_id);
348 BrowserThread::PostTask(
349 BrowserThread::UI,
350 FROM_HERE,
351 base::Bind(&DispatchEvent,
352 params.browser_context_id,
353 params.extension_id,
354 base::Passed(event.Pass())));
357 // static
358 void BluetoothSocketEventDispatcher::DispatchEvent(
359 void* browser_context_id,
360 const std::string& extension_id,
361 scoped_ptr<Event> event) {
362 DCHECK_CURRENTLY_ON(BrowserThread::UI);
364 content::BrowserContext* context =
365 reinterpret_cast<content::BrowserContext*>(browser_context_id);
366 if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
367 return;
369 EventRouter* router = EventRouter::Get(context);
370 if (router)
371 router->DispatchEventToExtension(extension_id, event.Pass());
374 } // namespace api
375 } // namespace extensions