btsocket: Implement listen and accept

This CL adds a function to deal with incoming BLE connection,
which is needed by GATT test server.

BUG=chromium:449666
TEST=cros deploy $HOST dev-python/btsocket
manually run function on tester, run BlueZ btgatt-client on DUT
verify that returned socket is able to receive and send messages

Change-Id: Ia29ff6073108bc0e9c3474703165e87972f6adf0
diff --git a/btsocket/btsocket.py b/btsocket/btsocket.py
index 37889d2..1cec234 100644
--- a/btsocket/btsocket.py
+++ b/btsocket/btsocket.py
@@ -85,3 +85,26 @@
         """
         return _btsocket.recvmsg(self, buffers, ancbufsize, flags)
 
+
+def create_le_gatt_server_socket():
+    """Create a socket for incoming BLE connection.
+
+    The function creates an LE socket, then does bind, listen and accept.
+    The accept call blocks until it receives an incoming connection.
+
+    Note, that resulting socket will be standard Python socket,
+    not BluetoothSocket. It is already connected, ready for recv() and
+    send() calls, and there is no need for further setup.
+
+    Before calling this function, the machine must be set up (with appropriate
+    HCI commands) to be advertising, be powered, and have LE turned on.
+
+    @return tuple of (sock, source_address),
+      @sock is standard Python socket object, which is able to receive and send.
+      @source_address is string containing BDADDR of the connected remote.
+
+    """
+    file_descriptor, source_address = _btsocket.listen_and_accept()
+    sock = socket.fromfd(file_descriptor, constants.AF_BLUETOOTH,
+                         socket.SOCK_SEQPACKET, constants.BTPROTO_L2CAP)
+    return sock, source_address
diff --git a/src/bluetooth.h b/src/bluetooth.h
index 48323f0..695885a 100644
--- a/src/bluetooth.h
+++ b/src/bluetooth.h
@@ -42,6 +42,8 @@
 #define PF_BLUETOOTH	AF_BLUETOOTH
 #endif
 
+#define ATT_CID	4
+
 #define BTPROTO_L2CAP	0
 #define BTPROTO_HCI	1
 #define BTPROTO_SCO	2
diff --git a/src/btsocket.c b/src/btsocket.c
index 7b3c579..791eeda 100644
--- a/src/btsocket.c
+++ b/src/btsocket.c
@@ -404,6 +404,74 @@
   return retval;
 }
 
+static PyObject *_btsocket_listen_and_accept() {
+  int sk, nsk, result;
+  struct sockaddr_l2 srcaddr, addr;
+  socklen_t optlen;
+  struct bt_security btsec;
+  bdaddr_t *ba;
+  char str[18];
+
+  Py_BEGIN_ALLOW_THREADS;
+  sk = socket(AF_BLUETOOTH, SOCK_SEQPACKET, BTPROTO_L2CAP);
+  Py_END_ALLOW_THREADS;
+  if (sk < 0)
+    goto fail;
+
+  /* Set up source address */
+  memset(&srcaddr, 0, sizeof(srcaddr));
+  srcaddr.l2_family = AF_BLUETOOTH;
+  srcaddr.l2_cid = htobs(ATT_CID);
+  srcaddr.l2_bdaddr_type = BDADDR_LE_PUBLIC;
+
+  Py_BEGIN_ALLOW_THREADS;
+  result = bind(sk, (struct sockaddr *) &srcaddr, sizeof(srcaddr));
+  Py_END_ALLOW_THREADS;
+
+  if (result < 0)
+    goto fail;
+
+  /* Set the security level */
+  memset(&btsec, 0, sizeof(btsec));
+  btsec.level = BT_SECURITY_LOW;
+
+  Py_BEGIN_ALLOW_THREADS;
+  result = setsockopt(sk, SOL_BLUETOOTH, BT_SECURITY, &btsec, sizeof(btsec));
+  Py_END_ALLOW_THREADS;
+
+  if (result != 0)
+    goto fail;
+
+  Py_BEGIN_ALLOW_THREADS;
+  result = listen(sk, 10);
+  Py_END_ALLOW_THREADS;
+
+  if (result < 0)
+    goto fail;
+
+  memset(&addr, 0, sizeof(addr));
+  optlen = sizeof(addr);
+
+  Py_BEGIN_ALLOW_THREADS;
+  nsk = accept(sk, (struct sockaddr *) &addr, &optlen);
+  Py_END_ALLOW_THREADS;
+
+  if (nsk < 0)
+    goto fail;
+
+  ba = &addr.l2_bdaddr;
+  sprintf(str, "%2.2X:%2.2X:%2.2X:%2.2X:%2.2X:%2.2X",
+          ba->b[5], ba->b[4], ba->b[3], ba->b[2], ba->b[1], ba->b[0]);
+
+  close(sk);
+  return Py_BuildValue("(i,s)", nsk, str);
+
+fail:
+  if (sk >= 0)
+    close(sk);
+
+  return PyErr_SetFromErrno(_btsocket_error);
+}
 
 static PyMethodDef _btsocket_methods[] = {
   { "bind", _btsocket_bind, METH_VARARGS,
@@ -412,6 +480,8 @@
     "Connect a Bluetooth socket to a remote address" },
   { "recvmsg", _btsocket_recvmsg, METH_VARARGS,
     "Receive normal and ancillary data from a Bluetooth socket" },
+  { "listen_and_accept", _btsocket_listen_and_accept, METH_NOARGS,
+    "Create a socket for incoming BLE connection" },
 
   { NULL, NULL, 0, NULL }
 };