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