Home | History | Annotate | Download | only in bluetooth_socket
      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 "extensions/browser/api/bluetooth_socket/bluetooth_socket_event_dispatcher.h"
      6 
      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"
     14 
     15 namespace {
     16 
     17 namespace bluetooth_socket = extensions::core_api::bluetooth_socket;
     18 using extensions::BluetoothApiSocket;
     19 
     20 int kDefaultBufferSize = 4096;
     21 
     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;
     37   }
     38 }
     39 
     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;
     51   }
     52 }
     53 
     54 }  // namespace
     55 
     56 namespace extensions {
     57 namespace core_api {
     58 
     59 using content::BrowserThread;
     60 
     61 static base::LazyInstance<
     62     BrowserContextKeyedAPIFactory<BluetoothSocketEventDispatcher> > g_factory =
     63     LAZY_INSTANCE_INITIALIZER;
     64 
     65 // static
     66 BrowserContextKeyedAPIFactory<BluetoothSocketEventDispatcher>*
     67 BluetoothSocketEventDispatcher::GetFactoryInstance() {
     68   return g_factory.Pointer();
     69 }
     70 
     71 // static
     72 BluetoothSocketEventDispatcher* BluetoothSocketEventDispatcher::Get(
     73     content::BrowserContext* context) {
     74   DCHECK_CURRENTLY_ON(BrowserThread::UI);
     75 
     76   return BrowserContextKeyedAPIFactory<BluetoothSocketEventDispatcher>::Get(
     77       context);
     78 }
     79 
     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_;
     92 }
     93 
     94 BluetoothSocketEventDispatcher::~BluetoothSocketEventDispatcher() {}
     95 
     96 BluetoothSocketEventDispatcher::SocketParams::SocketParams() {}
     97 
     98 BluetoothSocketEventDispatcher::SocketParams::~SocketParams() {}
     99 
    100 void BluetoothSocketEventDispatcher::OnSocketConnect(
    101     const std::string& extension_id,
    102     int socket_id) {
    103   DCHECK(BrowserThread::CurrentlyOn(thread_id_));
    104 
    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;
    111 
    112   StartReceive(params);
    113 }
    114 
    115 void BluetoothSocketEventDispatcher::OnSocketListen(
    116     const std::string& extension_id,
    117     int socket_id) {
    118   DCHECK(BrowserThread::CurrentlyOn(thread_id_));
    119 
    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;
    126 
    127   StartAccept(params);
    128 }
    129 
    130 void BluetoothSocketEventDispatcher::OnSocketResume(
    131     const std::string& extension_id,
    132     int socket_id) {
    133   DCHECK(BrowserThread::CurrentlyOn(thread_id_));
    134 
    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;
    141 
    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;
    147   }
    148 
    149   if (socket->IsConnected()) {
    150     StartReceive(params);
    151   } else {
    152     StartAccept(params);
    153   }
    154 }
    155 
    156 // static
    157 void BluetoothSocketEventDispatcher::StartReceive(const SocketParams& params) {
    158   DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
    159 
    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;
    165   }
    166   DCHECK(params.extension_id == socket->owner_extension_id())
    167       << "Socket has wrong owner.";
    168 
    169   // Don't start another read if the socket has been paused.
    170   if (socket->paused())
    171     return;
    172 
    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));
    182 }
    183 
    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));
    190 
    191   // Dispatch "onReceive" event.
    192   bluetooth_socket::ReceiveInfo receive_info;
    193   receive_info.socket_id = params.socket_id;
    194   receive_info.data = std::string(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());
    200 
    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));
    207 }
    208 
    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));
    215 
    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;
    221   }
    222 
    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());
    234 
    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);
    241   }
    242 }
    243 
    244 // static
    245 void BluetoothSocketEventDispatcher::StartAccept(const SocketParams& params) {
    246   DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
    247 
    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;
    253   }
    254   DCHECK(params.extension_id == socket->owner_extension_id())
    255       << "Socket has wrong owner.";
    256 
    257   // Don't start another accept if the socket has been paused.
    258   if (socket->paused())
    259     return;
    260 
    261   socket->Accept(
    262       base::Bind(
    263           &BluetoothSocketEventDispatcher::AcceptCallback, params),
    264       base::Bind(
    265           &BluetoothSocketEventDispatcher::AcceptErrorCallback, params));
    266 }
    267 
    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));
    274 
    275   BluetoothApiSocket* server_api_socket =
    276       params.sockets->Get(params.extension_id, params.socket_id);
    277   DCHECK(server_api_socket);
    278 
    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);
    285 
    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());
    295 
    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));
    302 }
    303 
    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));
    310 
    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;
    316   }
    317 
    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());
    329 
    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);
    336   }
    337 }
    338 
    339 // static
    340 void BluetoothSocketEventDispatcher::PostEvent(const SocketParams& params,
    341                                                scoped_ptr<Event> event) {
    342   DCHECK(BrowserThread::CurrentlyOn(params.thread_id));
    343 
    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())));
    351 }
    352 
    353 // static
    354 void BluetoothSocketEventDispatcher::DispatchEvent(
    355     void* browser_context_id,
    356     const std::string& extension_id,
    357     scoped_ptr<Event> event) {
    358   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    359 
    360   content::BrowserContext* context =
    361       reinterpret_cast<content::BrowserContext*>(browser_context_id);
    362   if (!extensions::ExtensionsBrowserClient::Get()->IsValidContext(context))
    363     return;
    364 
    365   EventRouter* router = EventRouter::Get(context);
    366   if (router)
    367     router->DispatchEventToExtension(extension_id, event.Pass());
    368 }
    369 
    370 }  // namespace core_api
    371 }  // namespace extensions
    372