• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /******************************************************************************
2  *
3  *  Copyright 2014 Google, Inc.
4  *
5  *  Licensed under the Apache License, Version 2.0 (the "License");
6  *  you may not use this file except in compliance with the License.
7  *  You may obtain a copy of the License at:
8  *
9  *  http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *  Unless required by applicable law or agreed to in writing, software
12  *  distributed under the License is distributed on an "AS IS" BASIS,
13  *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *  See the License for the specific language governing permissions and
15  *  limitations under the License.
16  *
17  ******************************************************************************/
18 
19 #define LOG_TAG "bt_btif_sock_sco"
20 
21 #include "btif_sock_sco.h"
22 
23 #include <bluetooth/log.h>
24 #include <sys/socket.h>
25 #include <sys/types.h>
26 #include <unistd.h>
27 
28 #include <cstdint>
29 #include <mutex>
30 
31 #include "include/hardware/bt_sock.h"
32 #include "osi/include/allocator.h"
33 #include "osi/include/list.h"
34 #include "osi/include/osi.h"  // INVALID_FD
35 #include "osi/include/socket.h"
36 #include "osi/include/thread.h"
37 #include "stack/include/btm_client_interface.h"
38 #include "stack/include/btm_status.h"
39 #include "types/raw_address.h"
40 
41 // This module provides a socket abstraction for SCO connections to a higher
42 // layer. It returns file descriptors representing two types of sockets:
43 // listening (server) and connected (client) sockets. No SCO data is
44 // transferred across these sockets; instead, they are used to manage SCO
45 // connection lifecycles while the data routing takes place over the I2S bus.
46 //
47 // This code bridges the gap between the BTM layer, which implements SCO
48 // connections, and the Android HAL. It adapts the BTM representation of SCO
49 // connections (integer handles) to a file descriptor representation usable by
50 // Android's LocalSocket implementation.
51 //
52 // Sample flow for an incoming connection:
53 //   btsock_sco_listen()       - listen for incoming connections
54 //   connection_request_cb()   - incoming connection request from remote host
55 //   connect_completed_cb()    - connection successfully established
56 //   socket_read_ready_cb()    - local host closed SCO socket
57 //   disconnect_completed_cb() - connection terminated
58 
59 using namespace bluetooth;
60 
61 typedef struct {
62   uint16_t sco_handle;
63   socket_t* socket;
64   bool connect_completed;
65 } sco_socket_t;
66 
67 static sco_socket_t* sco_socket_establish_locked(bool is_listening, const RawAddress* bd_addr,
68                                                  int* sock_fd);
69 static sco_socket_t* sco_socket_new(void);
70 static void sco_socket_free_locked(sco_socket_t* socket);
71 static sco_socket_t* sco_socket_find_locked(uint16_t sco_handle);
72 static void connection_request_cb(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA* data);
73 static void connect_completed_cb(uint16_t sco_handle);
74 static void disconnect_completed_cb(uint16_t sco_handle);
75 static void socket_read_ready_cb(socket_t* socket, void* context);
76 
77 // |sco_lock| protects all of the static variables below and
78 // calls into the BTM layer.
79 static std::mutex sco_lock;
80 static list_t* sco_sockets;              // Owns a collection of sco_socket_t objects.
81 static sco_socket_t* listen_sco_socket;  // Not owned, do not free.
82 static thread_t* thread;                 // Not owned, do not free.
83 
btsock_sco_init(thread_t * thread_)84 bt_status_t btsock_sco_init(thread_t* thread_) {
85   log::assert_that(thread_ != NULL, "assert failed: thread_ != NULL");
86 
87   sco_sockets = list_new((list_free_cb)sco_socket_free_locked);
88   if (!sco_sockets) {
89     return BT_STATUS_SOCKET_ERROR;
90   }
91 
92   thread = thread_;
93   enh_esco_params_t params = esco_parameters_for_codec(SCO_CODEC_CVSD_D1, true);
94   if (get_btm_client_interface().sco.BTM_SetEScoMode(&params) != tBTM_STATUS::BTM_SUCCESS) {
95     log::warn("Unable to set ESCO parameters");
96   }
97 
98   return BT_STATUS_SUCCESS;
99 }
100 
btsock_sco_cleanup(void)101 bt_status_t btsock_sco_cleanup(void) {
102   list_free(sco_sockets);
103   sco_sockets = NULL;
104   return BT_STATUS_SUCCESS;
105 }
106 
btsock_sco_listen(int * sock_fd,int)107 bt_status_t btsock_sco_listen(int* sock_fd, int /* flags */) {
108   log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL");
109 
110   std::unique_lock<std::mutex> lock(sco_lock);
111 
112   sco_socket_t* sco_socket = sco_socket_establish_locked(true, NULL, sock_fd);
113   if (!sco_socket) {
114     return BT_STATUS_SOCKET_ERROR;
115   }
116 
117   if (get_btm_client_interface().sco.BTM_RegForEScoEvts(
118               sco_socket->sco_handle, connection_request_cb) != tBTM_STATUS::BTM_SUCCESS) {
119     log::warn("Unable to register for ESCO events");
120   }
121   listen_sco_socket = sco_socket;
122 
123   return BT_STATUS_SUCCESS;
124 }
125 
btsock_sco_connect(const RawAddress * bd_addr,int * sock_fd,int)126 bt_status_t btsock_sco_connect(const RawAddress* bd_addr, int* sock_fd, int /* flags */) {
127   log::assert_that(bd_addr != NULL, "assert failed: bd_addr != NULL");
128   log::assert_that(sock_fd != NULL, "assert failed: sock_fd != NULL");
129 
130   std::unique_lock<std::mutex> lock(sco_lock);
131   sco_socket_t* sco_socket = sco_socket_establish_locked(false, bd_addr, sock_fd);
132 
133   return (sco_socket != NULL) ? BT_STATUS_SUCCESS : BT_STATUS_SOCKET_ERROR;
134 }
135 
136 // Must be called with |lock| held.
sco_socket_establish_locked(bool is_listening,const RawAddress * bd_addr,int * sock_fd)137 static sco_socket_t* sco_socket_establish_locked(bool is_listening, const RawAddress* bd_addr,
138                                                  int* sock_fd) {
139   int pair[2] = {INVALID_FD, INVALID_FD};
140   sco_socket_t* sco_socket = NULL;
141   socket_t* socket = NULL;
142   tBTM_STATUS status;
143   enh_esco_params_t params;
144   if (socketpair(AF_LOCAL, SOCK_STREAM, 0, pair) == -1) {
145     log::error("unable to allocate socket pair: {}", strerror(errno));
146     goto error;
147   }
148 
149   sco_socket = sco_socket_new();
150   if (!sco_socket) {
151     log::error("unable to allocate new SCO socket.");
152     goto error;
153   }
154 
155   params = esco_parameters_for_codec(SCO_CODEC_CVSD_D1, true);
156   status = get_btm_client_interface().sco.BTM_CreateSco(
157           bd_addr, !is_listening, params.packet_types, &sco_socket->sco_handle,
158           connect_completed_cb, disconnect_completed_cb);
159   if (status != tBTM_STATUS::BTM_CMD_STARTED) {
160     log::error("unable to create SCO socket: {}", status);
161     goto error;
162   }
163 
164   socket = socket_new_from_fd(pair[1]);
165   if (!socket) {
166     log::error("unable to allocate socket from file descriptor {}.", pair[1]);
167     goto error;
168   }
169 
170   *sock_fd = pair[0];           // Transfer ownership of one end to caller.
171   sco_socket->socket = socket;  // Hang on to the other end.
172   list_append(sco_sockets, sco_socket);
173 
174   socket_register(socket, thread_get_reactor(thread), sco_socket, socket_read_ready_cb, NULL);
175   return sco_socket;
176 
177 error:
178   if (pair[0] != INVALID_FD) {
179     close(pair[0]);
180   }
181   if (pair[1] != INVALID_FD) {
182     close(pair[1]);
183   }
184 
185   sco_socket_free_locked(sco_socket);
186   return NULL;
187 }
188 
sco_socket_new(void)189 static sco_socket_t* sco_socket_new(void) {
190   sco_socket_t* sco_socket = (sco_socket_t*)osi_calloc(sizeof(sco_socket_t));
191   sco_socket->sco_handle = BTM_INVALID_SCO_INDEX;
192   return sco_socket;
193 }
194 
195 // Must be called with |lock| held except during teardown when we know the
196 // socket thread
197 // is no longer alive.
sco_socket_free_locked(sco_socket_t * sco_socket)198 static void sco_socket_free_locked(sco_socket_t* sco_socket) {
199   if (!sco_socket) {
200     return;
201   }
202 
203   if (sco_socket->sco_handle != BTM_INVALID_SCO_INDEX) {
204     if (get_btm_client_interface().sco.BTM_RemoveSco(sco_socket->sco_handle) !=
205         tBTM_STATUS::BTM_SUCCESS) {
206       log::warn("Unable to remove SCO handle:{}", sco_socket->sco_handle);
207     }
208   }
209   socket_free(sco_socket->socket);
210   osi_free(sco_socket);
211 }
212 
213 // Must be called with |lock| held.
sco_socket_find_locked(uint16_t sco_handle)214 static sco_socket_t* sco_socket_find_locked(uint16_t sco_handle) {
215   for (const list_node_t* node = list_begin(sco_sockets); node != list_end(sco_sockets);
216        node = list_next(node)) {
217     sco_socket_t* sco_socket = (sco_socket_t*)list_node(node);
218     if (sco_socket->sco_handle == sco_handle) {
219       return sco_socket;
220     }
221   }
222   return NULL;
223 }
224 
connection_request_cb(tBTM_ESCO_EVT event,tBTM_ESCO_EVT_DATA * data)225 static void connection_request_cb(tBTM_ESCO_EVT event, tBTM_ESCO_EVT_DATA* data) {
226   log::assert_that(data != NULL, "assert failed: data != NULL");
227 
228   // Don't care about change of link parameters, only connection requests.
229   if (event != BTM_ESCO_CONN_REQ_EVT) {
230     return;
231   }
232 
233   std::unique_lock<std::mutex> lock(sco_lock);
234 
235   const tBTM_ESCO_CONN_REQ_EVT_DATA* conn_data = &data->conn_evt;
236   sco_socket_t* sco_socket = sco_socket_find_locked(conn_data->sco_inx);
237   int client_fd = INVALID_FD;
238 
239   uint16_t temp;
240   sco_socket_t* new_sco_socket;
241 
242   if (!sco_socket) {
243     log::error("unable to find sco_socket for handle: {}", conn_data->sco_inx);
244     goto error;
245   }
246 
247   if (sco_socket != listen_sco_socket) {
248     log::error("received connection request on non-listening socket handle: {}",
249                conn_data->sco_inx);
250     goto error;
251   }
252 
253   new_sco_socket = sco_socket_establish_locked(true, NULL, &client_fd);
254   if (!new_sco_socket) {
255     log::error("unable to allocate new sco_socket.");
256     goto error;
257   }
258 
259   // Swap socket->sco_handle and new_socket->sco_handle
260   temp = sco_socket->sco_handle;
261   sco_socket->sco_handle = new_sco_socket->sco_handle;
262   new_sco_socket->sco_handle = temp;
263 
264   sock_connect_signal_t connect_signal;
265   connect_signal.size = sizeof(connect_signal);
266   connect_signal.bd_addr = conn_data->bd_addr;
267   connect_signal.channel = 0;
268   connect_signal.status = 0;
269 
270   if (socket_write_and_transfer_fd(sco_socket->socket, &connect_signal, sizeof(connect_signal),
271                                    client_fd) != sizeof(connect_signal)) {
272     log::error("unable to send new file descriptor to listening socket.");
273     goto error;
274   }
275 
276   if (get_btm_client_interface().sco.BTM_RegForEScoEvts(
277               listen_sco_socket->sco_handle, connection_request_cb) != tBTM_STATUS::BTM_SUCCESS) {
278     log::warn("Unable to register for ESCO events handle:{}", listen_sco_socket->sco_handle);
279   }
280   get_btm_client_interface().sco.BTM_EScoConnRsp(conn_data->sco_inx, HCI_SUCCESS, NULL);
281 
282   return;
283 
284 error:
285   if (client_fd != INVALID_FD) {
286     close(client_fd);
287   }
288   get_btm_client_interface().sco.BTM_EScoConnRsp(conn_data->sco_inx, HCI_ERR_HOST_REJECT_RESOURCES,
289                                                  NULL);
290 }
291 
connect_completed_cb(uint16_t sco_handle)292 static void connect_completed_cb(uint16_t sco_handle) {
293   std::unique_lock<std::mutex> lock(sco_lock);
294 
295   sco_socket_t* sco_socket = sco_socket_find_locked(sco_handle);
296   if (!sco_socket) {
297     log::error("SCO socket not found on connect for handle: {}", sco_handle);
298     return;
299   }
300 
301   // If sco_socket->socket was closed, we should tear down because there is no
302   // app-level
303   // interest in the SCO socket.
304   if (!sco_socket->socket) {
305     if (get_btm_client_interface().sco.BTM_RemoveSco(sco_socket->sco_handle) !=
306         tBTM_STATUS::BTM_SUCCESS) {
307       log::warn("Unable to remove SCO handle:{}", sco_socket->sco_handle);
308     }
309     list_remove(sco_sockets, sco_socket);
310     return;
311   }
312 
313   sco_socket->connect_completed = true;
314 }
315 
disconnect_completed_cb(uint16_t sco_handle)316 static void disconnect_completed_cb(uint16_t sco_handle) {
317   std::unique_lock<std::mutex> lock(sco_lock);
318 
319   sco_socket_t* sco_socket = sco_socket_find_locked(sco_handle);
320   if (!sco_socket) {
321     log::error("SCO socket not found on disconnect for handle: {}", sco_handle);
322     return;
323   }
324 
325   list_remove(sco_sockets, sco_socket);
326 }
327 
socket_read_ready_cb(socket_t *,void * context)328 static void socket_read_ready_cb(socket_t* /* socket */, void* context) {
329   std::unique_lock<std::mutex> lock(sco_lock);
330 
331   sco_socket_t* sco_socket = (sco_socket_t*)context;
332   socket_free(sco_socket->socket);
333   sco_socket->socket = NULL;
334 
335   // Defer the underlying disconnect until the connection completes
336   // since the BTM code doesn't behave correctly when a disconnect
337   // request is issued while a connect is in progress. The fact that
338   // sco_socket->socket == NULL indicates to the connect callback
339   // routine that the socket is no longer desired and should be torn
340   // down.
341   if (sco_socket->connect_completed || sco_socket == listen_sco_socket) {
342     if (get_btm_client_interface().sco.BTM_RemoveSco(sco_socket->sco_handle) ==
343         tBTM_STATUS::BTM_SUCCESS) {
344       list_remove(sco_sockets, sco_socket);
345     }
346     if (sco_socket == listen_sco_socket) {
347       listen_sco_socket = NULL;
348     }
349   }
350 }
351