2014-03-17 00:01:08

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v3 0/5] android:Add initial support for GATT Client

This patch set add GATT clien scan, connect/disconnect
functionality.

Some changes in handling discovery has been done as
there are two clients of discovery: HAL and GATT

For now daemon makes sure that device is available before
connecting it. It is because in case device is not available
we would block connect to other devices.

Once white list will be available we can simplify that code.

===
v2: Fixes in discovery handling
v3: Fixes Szymon comments + adding patch for search primary services

Jakub Tyszkowski (2):
android/bluetooth: Add GATT notifications on LE discovery
android/gatt: Use Core profile for LE scan

Lukasz Rymanowski (3):
android/gatt: Add GATT Connect
android/gatt: Add disconnect GATT device
android/gatt: Find primary services

android/Android.mk | 4 +
android/Makefile.am | 3 +
android/bluetooth.c | 107 +++++++-
android/bluetooth.h | 7 +
android/gatt.c | 717 +++++++++++++++++++++++++++++++++++++++++++++++++++-
5 files changed, 821 insertions(+), 17 deletions(-)

--
1.8.4



2014-03-17 09:53:48

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH v0 5/5] android/gatt: Find primary services

Hi Łukasz,

On Monday 17 of March 2014 01:01:13 Lukasz Rymanowski wrote:
> With this patch it is possible to search all primary services.
> ---
> android/gatt.c | 100
> ++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed,
> 99 insertions(+), 1 deletion(-)
>
> diff --git a/android/gatt.c b/android/gatt.c
> index 1081121..4f55d15 100644
> --- a/android/gatt.c
> +++ b/android/gatt.c
> @@ -36,6 +36,7 @@
> #include "ipc-common.h"
> #include "lib/sdp.h"
> #include "lib/uuid.h"
> +

Not related change.

> #include "bluetooth.h"
> #include "gatt.h"
> #include "src/log.h"
> @@ -64,6 +65,7 @@ struct gatt_device {
>
> GAttrib *attrib;
> GIOChannel *att_io;
> + struct queue *services;
>
> guint watch_id;
> };
> @@ -127,6 +129,7 @@ static void destroy_device(void *data)
> struct gatt_device *dev = data;
>
> queue_destroy(dev->clients, NULL);
> + queue_destroy(dev->services, free);
> free(dev);
> }
>
> @@ -205,6 +208,71 @@ failed:
> HAL_OP_GATT_CLIENT_UNREGISTER, status);
> }
>
> +static void primary_cb(uint8_t status, GSList *services, void *user_data)
> +{
> + GSList *l;
> + struct gatt_device *dev = user_data;
> + struct hal_ev_gatt_client_search_complete ev;
> +
> + DBG("Status %d", status);
> +
> + if (status) {
> + error("Discover all primary services failed: %s\n",

No need to add '\n' at the end of error message.

> + att_ecode2str(status));
> + ev.status = HAL_STATUS_FAILED;
> + goto reply;

minor: I'd call this label "done" or "send_notif" as this is not a reply.

> + }
> +
> + if (!services) {
> + info("gatt: no primary serivces found");

typo: serivces -> services

> + ev.status = HAL_STATUS_SUCCESS;
> + goto reply;
> + }
> +
> + for (l = services; l; l = l->next) {
> + uint8_t buf[IPC_MTU];
> + struct hal_ev_gatt_client_search_result *ev_res = (void *)buf;
> + struct gatt_primary *prim = l->data;
> + struct gatt_primary *p = new0(struct gatt_primary, 1);

You should check for null from new0.

> + bt_uuid_t uuid;
> +
> + memset(buf, 0, sizeof(buf));
> +
> + /* Put primary service to our local list */
> + memcpy(p, prim, sizeof(*p));
> + if (!queue_push_tail(dev->services, p)) {
> + error("gatt: Cannot push primary service to the list");
> + free(p);
> + }

continue here?

> +
> + DBG("attr handle = 0x%04x, end grp handle = 0x%04x "
> + "uuid: %s\n", prim->range.start, prim->range.end,
> + prim->uuid);

No '\n' is needed. Also don't break format string.

> + /* Set event data */
> + ev_res->conn_id = dev->conn_id;
> + ev_res->srvc_id.is_primary = 1;
> + ev_res->srvc_id.inst_id = 0;
> +
> + if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
> + error("Can not convert string to uuid");
> + continue;
> + }
> + memcpy(&ev_res->srvc_id.uuid, &uuid.value, sizeof(uuid.value));
> +
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
> + HAL_EV_GATT_CLIENT_SEARCH_RESULT,
> + sizeof(*ev_res), ev_res);

Indentation is broken here.

> + }
> +
> + ev.status = HAL_STATUS_SUCCESS;
> +
> +reply:
> + ev.conn_id = dev->conn_id;
> + ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
> + HAL_EV_GATT_CLIENT_SEARCH_COMPLETE,
> + sizeof(ev), &ev);

Indentation is broken here.

> +}
> +
> static void connection_cleanup(struct gatt_device *device)
> {
> if (device->watch_id) {
> @@ -635,6 +703,14 @@ static void handle_client_connect(const void *buf,
> uint16_t len) goto reply;
> }
>
> + dev->services = queue_new();
> + if (!dev->services) {
> + error("gatt: Cannot create services queue");
> + queue_destroy(dev->clients, NULL);
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> /* Update client list of device */
> if (!queue_push_tail(dev->clients, INT_TO_PTR(cmd->client_if))) {
> error("gatt: Cannot push client on the client queue!?");
> @@ -755,10 +831,32 @@ static void handle_client_refresh(const void *buf,
> uint16_t len)
>
> static void handle_client_search_service(const void *buf, uint16_t len)
> {
> + const struct hal_cmd_gatt_client_search_service *cmd = buf;
> + struct gatt_device *dev;
> + uint8_t status;
> +
> DBG("");
>
> + dev = queue_find(conn_list, match_dev_by_conn_id,
> + INT_TO_PTR(cmd->conn_id));
> + if (!dev) {
> + error("gatt: dev with conn_id=%d not found", cmd->conn_id);
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + /*TODO: Handle filter uuid */
> +
> + if (!gatt_discover_primary(dev->attrib, NULL, primary_cb, dev)) {
> + status = HAL_STATUS_FAILED;
> + goto reply;
> + }
> +
> + status = HAL_STATUS_SUCCESS;
> +
> +reply:
> ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
> - HAL_OP_GATT_CLIENT_SEARCH_SERVICE, HAL_STATUS_FAILED);
> + HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
> }
>
> static void handle_client_get_included_service(const void *buf, uint16_t
> len)

--
BR
Szymon Janc

2014-03-17 09:42:58

by Szymon Janc

[permalink] [raw]
Subject: Re: [PATCH v3 0/5] android:Add initial support for GATT Client

Hi Łukasz,

On Monday 17 of March 2014 01:01:08 Lukasz Rymanowski wrote:
> This patch set add GATT clien scan, connect/disconnect
> functionality.
>
> Some changes in handling discovery has been done as
> there are two clients of discovery: HAL and GATT
>
> For now daemon makes sure that device is available before
> connecting it. It is because in case device is not available
> we would block connect to other devices.
>
> Once white list will be available we can simplify that code.
>
> ===
> v2: Fixes in discovery handling
> v3: Fixes Szymon comments + adding patch for search primary services
>
> Jakub Tyszkowski (2):
> android/bluetooth: Add GATT notifications on LE discovery
> android/gatt: Use Core profile for LE scan
>
> Lukasz Rymanowski (3):
> android/gatt: Add GATT Connect
> android/gatt: Add disconnect GATT device
> android/gatt: Find primary services
>
> android/Android.mk | 4 +
> android/Makefile.am | 3 +
> android/bluetooth.c | 107 +++++++-
> android/bluetooth.h | 7 +
> android/gatt.c | 717
> +++++++++++++++++++++++++++++++++++++++++++++++++++- 5 files changed, 821
> insertions(+), 17 deletions(-)

Patches 1-4 are now pushed, thanks.

--
BR
Szymon Janc

2014-03-17 00:01:11

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v3 3/5] android/gatt: Add GATT Connect

This patch introduce connect LE device functionality.

There is gatt_device representing remote le device. Each gatt device
has a list own list of clients as it is possible that more apps
would like to use same remote device.

Possible connect scenarios:

1. There is no ACL connection to device:
Then new dev is put on conn_wait_queue and le scan is enabled.
Once device is found we do connect it.

Once device is connected then device is moved form conn_wait_queue to
conn_list and success event is sent to client(s) with conn_id

2. Device is already connected:
Then we update client list, reply with success and do send connect event.

3. For unregisterd clients or uknown conn_id, failed response is sent.
---
android/Android.mk | 4 +
android/Makefile.am | 3 +
android/gatt.c | 494 ++++++++++++++++++++++++++++++++++++++++++++++++++--
3 files changed, 483 insertions(+), 18 deletions(-)

diff --git a/android/Android.mk b/android/Android.mk
index 34e21ea..0e0932d 100644
--- a/android/Android.mk
+++ b/android/Android.mk
@@ -60,9 +60,13 @@ LOCAL_SRC_FILES := \
bluez/lib/sdp.c \
bluez/lib/bluetooth.c \
bluez/lib/hci.c \
+ bluez/lib/uuid.c \
bluez/btio/btio.c \
bluez/src/sdp-client.c \
bluez/profiles/network/bnep.c \
+ bluez/attrib/gattrib.c \
+ bluez/attrib/gatt.c \
+ bluez/attrib/att.c

LOCAL_C_INCLUDES := \
$(call include-path-for, glib) \
diff --git a/android/Makefile.am b/android/Makefile.am
index ff15682..3c503da 100644
--- a/android/Makefile.am
+++ b/android/Makefile.am
@@ -43,6 +43,9 @@ android_bluetoothd_SOURCES = android/main.c \
android/handsfree.h android/handsfree.c \
android/gatt.h android/gatt.c \
android/health.h android/health.c \
+ attrib/att.c attrib/att.h \
+ attrib/gatt.c attrib/gatt.h \
+ attrib/gattrib.c attrib/gattrib.h \
btio/btio.h btio/btio.c \
src/sdp-client.h src/sdp-client.c \
profiles/network/bnep.h profiles/network/bnep.c
diff --git a/android/gatt.c b/android/gatt.c
index d5abbda..f2a1b07 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -29,10 +29,13 @@
#include <stdlib.h>
#include <stdint.h>
#include <glib.h>
+#include <errno.h>
+#include <sys/socket.h>

#include "ipc.h"
#include "ipc-common.h"
#include "lib/sdp.h"
+#include "lib/uuid.h"
#include "bluetooth.h"
#include "gatt.h"
#include "src/log.h"
@@ -40,16 +43,40 @@
#include "utils.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"
+#include "attrib/gattrib.h"
+#include "attrib/att.h"
+#include "attrib/gatt.h"
+#include "btio/btio.h"

struct gatt_client {
int32_t id;
uint8_t uuid[16];
};

+struct gatt_device {
+ bdaddr_t bdaddr;
+ uint8_t bdaddr_type;
+
+ struct queue *clients;
+
+ bool connect_ready;
+ int32_t conn_id;
+
+ GAttrib *attrib;
+ GIOChannel *att_io;
+
+ guint watch_id;
+};
+
static struct ipc *hal_ipc = NULL;
static bdaddr_t adapter_addr;
+
static struct queue *gatt_clients = NULL;
static struct queue *scan_clients = NULL;
+static struct queue *conn_list = NULL; /* Connected devices */
+static struct queue *conn_wait_queue = NULL; /* Devs waiting to connect */
+
+static void bt_le_discovery_stop_cb(void);

static bool match_client_by_uuid(const void *data, const void *user_data)
{
@@ -72,29 +99,27 @@ static bool match_by_value(const void *data, const void *user_data)
return data == user_data;
}

-static void le_device_found_handler(bdaddr_t *addr, uint8_t addr_type, int rssi,
- uint16_t eir_len, const void *eir)
+static bool match_dev_by_bdaddr(const void *data, const void *user_data)
{
- uint8_t buf[IPC_MTU];
- struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
- char bda[18];
+ const struct gatt_device *dev = data;
+ const bdaddr_t *addr = user_data;

- if (queue_isempty(scan_clients))
- return;
+ return !bacmp(&dev->bdaddr, addr);
+}

- ba2str(addr, bda);
- DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi,
- eir ? true : false);
+static bool match_dev_connect_ready(const void *data, const void *user_data)
+{
+ const struct gatt_device *dev = data;

- bdaddr2android(addr, ev->bda);
- ev->rssi = rssi;
- ev->len = eir_len;
+ return dev->connect_ready;
+}

- memcpy(ev->adv_data, eir, ev->len);
+static void destroy_device(void *data)
+{
+ struct gatt_device *dev = data;

- ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_EV_GATT_CLIENT_SCAN_RESULT,
- sizeof(ev) + ev->len, ev);
+ queue_destroy(dev->clients, NULL);
+ free(dev);
}

static void handle_client_register(const void *buf, uint16_t len)
@@ -172,6 +197,264 @@ failed:
HAL_OP_GATT_CLIENT_UNREGISTER, status);
}

+static void connection_cleanup(struct gatt_device *device)
+{
+ if (device->watch_id) {
+ g_source_remove(device->watch_id);
+ device->watch_id = 0;
+ }
+
+ if (device->att_io) {
+ g_io_channel_shutdown(device->att_io, FALSE, NULL);
+ g_io_channel_unref(device->att_io);
+ device->att_io = NULL;
+ }
+
+ if (device->attrib) {
+ GAttrib *attrib = device->attrib;
+ device->attrib = NULL;
+ g_attrib_cancel_all(attrib);
+ g_attrib_unref(attrib);
+ }
+}
+
+static void send_disconnect_notify(int32_t id, struct gatt_device *dev,
+ uint8_t status)
+{
+ struct hal_ev_gatt_client_disconnect ev;
+
+ ev.client_if = id;
+ ev.conn_id = dev->conn_id;
+ ev.status = status;
+ bdaddr2android(&dev->bdaddr, &ev.bda);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_DISCONNECT, sizeof(ev), &ev);
+}
+
+static void disconnect_notify(void *data, void *user_data)
+{
+ struct gatt_device *dev = user_data;
+ int32_t id = PTR_TO_INT(data);
+
+ send_disconnect_notify(id, dev, HAL_STATUS_SUCCESS);
+}
+
+static bool is_device_wating_for_connect(const bdaddr_t *addr,
+ uint8_t addr_type)
+{
+ struct gatt_device *dev;
+
+ DBG("");
+
+ dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, (void *)addr);
+ if (!dev)
+ return false;
+
+ dev->bdaddr_type = addr_type;
+
+ /* Mark that this device is ready for connect.
+ * Need it because will continue with connect after scan is stopped
+ */
+ dev->connect_ready = true;
+
+ return true;
+}
+
+static void le_device_found_handler(bdaddr_t *addr, uint8_t addr_type,
+ int rssi, uint16_t eir_len,
+ const void *eir)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+ char bda[18];
+
+ if (queue_isempty(scan_clients))
+ goto connect;
+
+ ba2str(addr, bda);
+ DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi, !!eir);
+
+ bdaddr2android(addr, ev->bda);
+ ev->rssi = rssi;
+ ev->len = eir_len;
+
+ memcpy(ev->adv_data, eir, ev->len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SCAN_RESULT,
+ sizeof(ev) + ev->len, ev);
+
+connect:
+ if (!is_device_wating_for_connect(addr, addr_type))
+ return;
+
+ /* We are ok to perform connect now. Stop discovery
+ * and once it is stopped continue with creating ACL
+ */
+ bt_le_discovery_stop(bt_le_discovery_stop_cb);
+}
+
+static gboolean disconnected_cb(GIOChannel *io, GIOCondition cond,
+ gpointer user_data)
+{
+ bdaddr_t *addr = user_data;
+ struct gatt_device *dev;
+ int sock, err = 0;
+ socklen_t len;
+
+ dev = queue_remove_if(conn_list, match_dev_by_bdaddr, addr);
+
+ sock = g_io_channel_unix_get_fd(io);
+ len = sizeof(err);
+ if (getsockopt(sock, SOL_SOCKET, SO_ERROR, &err, &len) < 0)
+ goto done;
+
+ DBG("%s (%d)", strerror(err), err);
+
+ /* Keep scanning/re-connection active if disconnection reason
+ * is connection timeout, remote user terminated connection or local
+ * initiated disconnection.
+ */
+ if (err == ETIMEDOUT || err == ECONNRESET || err == ECONNABORTED) {
+ if (!queue_push_tail(conn_wait_queue, dev)) {
+ error("gatt: Cannot push data");
+ } else {
+ bt_le_discovery_start(le_device_found_handler);
+ return FALSE;
+ }
+ }
+
+done:
+ connection_cleanup(dev);
+
+ queue_foreach(dev->clients, disconnect_notify, dev);
+ destroy_device(dev);
+
+ return FALSE;
+}
+
+static void send_client_connect_notify(void *data, void *user_data)
+{
+ struct hal_ev_gatt_client_connect *ev = user_data;
+ int32_t id = PTR_TO_INT(data);
+
+ ev->client_if = id;
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_CONNECT, sizeof(*ev), ev);
+
+}
+
+static void connect_cb(GIOChannel *io, GError *gerr, gpointer user_data)
+{
+ bdaddr_t *addr = user_data;
+ struct gatt_device *dev;
+ struct hal_ev_gatt_client_connect ev;
+ GAttrib *attrib;
+ static uint32_t conn_id = 0;
+ uint8_t status;
+
+ /* Take device from conn waiting queue */
+ dev = queue_remove_if(conn_wait_queue, match_dev_by_bdaddr, addr);
+ if (!dev) {
+ error("gatt: Device not on the connect wait queue!?");
+ g_io_channel_shutdown(io, TRUE, NULL);
+ return;
+ }
+
+ g_io_channel_unref(dev->att_io);
+ dev->att_io = NULL;
+
+ /* Set address and client id in the event */
+ bdaddr2android(&dev->bdaddr, &ev.bda);
+
+ if (gerr) {
+ error("gatt: connection failed %s", gerr->message);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ attrib = g_attrib_new(io);
+ if (!attrib) {
+ error("gatt: unable to create new GAttrib instance");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ dev->attrib = attrib;
+ dev->watch_id = g_io_add_watch(io, G_IO_HUP | G_IO_ERR | G_IO_NVAL,
+ disconnected_cb, dev);
+ dev->conn_id = ++conn_id;
+
+ /* Move gatt device from connect queue to conn_list */
+ if (!queue_push_tail(conn_list, dev)) {
+ error("gatt: Cannot push dev on conn_list");
+ connection_cleanup(dev);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+
+reply:
+ ev.conn_id = dev ? dev->conn_id : 0;
+ ev.status = status;
+
+ queue_foreach(dev->clients, send_client_connect_notify, &ev);
+
+ /* If connection did not succeed, destroy device */
+ if (status)
+ destroy_device(dev);
+}
+
+static int connect_le(struct gatt_device *dev)
+{
+ BtIOSecLevel sec_level;
+ GIOChannel *io;
+ GError *gerr = NULL;
+ char addr[18];
+
+ ba2str(&dev->bdaddr, addr);
+
+ /* There is one connection attempt going on */
+ if (dev->att_io) {
+ info("gatt: connection to dev %s is ongoing", addr);
+ return -EALREADY;
+ }
+
+ DBG("Connection attempt to: %s", addr);
+
+ /*TODO: If we are bonded then we should use higier sec level */
+ sec_level = BT_IO_SEC_LOW;
+
+ /*
+ * This connection will help us catch any PDUs that comes before
+ * pairing finishes
+ */
+ io = bt_io_connect(connect_cb, dev, NULL, &gerr,
+ BT_IO_OPT_SOURCE_BDADDR,
+ &adapter_addr,
+ BT_IO_OPT_SOURCE_TYPE, BDADDR_LE_PUBLIC,
+ BT_IO_OPT_DEST_BDADDR, &dev->bdaddr,
+ BT_IO_OPT_DEST_TYPE, dev->bdaddr_type,
+ BT_IO_OPT_CID, ATT_CID,
+ BT_IO_OPT_SEC_LEVEL, sec_level,
+ BT_IO_OPT_INVALID);
+ if (!io) {
+ error("gatt: Failed bt_io_connect(%s): %s", addr,
+ gerr->message);
+ g_error_free(gerr);
+ return -EIO;
+ }
+
+ /* Keep this, so we can cancel the connection */
+ dev->att_io = io;
+
+ return 0;
+}
+
static void handle_client_scan(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_scan *cmd = buf;
@@ -229,12 +512,168 @@ reply:
status);
}

+static int connect_next_dev(void)
+{
+ struct gatt_device *dev;
+
+ DBG("");
+
+ if (queue_isempty(conn_wait_queue))
+ return 0;
+
+ /* Discovery has been stopped because there is connection waiting */
+ dev = queue_find(conn_wait_queue, match_dev_connect_ready, NULL);
+ if (!dev)
+ /* Lets try again. */
+ return -1;
+
+ dev->connect_ready = false;
+
+ return connect_le(dev);
+}
+
+static void bt_le_discovery_stop_cb(void)
+{
+ DBG("");
+
+ /* Check now if there is any device ready to connect*/
+ if (connect_next_dev() < 0)
+ bt_le_discovery_start(le_device_found_handler);
+}
+
+static struct gatt_device *find_device(bdaddr_t *addr)
+{
+ struct gatt_device *dev;
+
+ dev = queue_find(conn_list, match_dev_by_bdaddr, addr);
+ if (dev)
+ return dev;
+
+ dev = queue_find(conn_wait_queue, match_dev_by_bdaddr, addr);
+ if (dev)
+ return dev;
+
+ return NULL;
+}
+
static void handle_client_connect(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_connect *cmd = buf;
+ struct gatt_device *dev = NULL;
+ void *l;
+ bdaddr_t addr;
+ uint8_t status;
+ bool send_notify = false;
+
DBG("");

+ /* Check if client is registered */
+ l = queue_find(gatt_clients, match_client_by_id,
+ INT_TO_PTR(cmd->client_if));
+ if (!l) {
+ error("gatt: Client id %d not found", cmd->client_if);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ android2bdaddr(&cmd->bdaddr, &addr);
+
+ /* We do support many clients for one device connection so lets check
+ * If device is connected or in connecting state just update list of
+ * clients
+ */
+ dev = find_device(&addr);
+ if (dev) {
+ /* Remeber to send dummy notification event if we area
+ * connected
+ */
+ if (dev->conn_id)
+ send_notify = true;
+
+ if (queue_find(dev->clients, match_by_value,
+ INT_TO_PTR(cmd->client_if))) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Store another client */
+ if (!queue_push_tail(dev->clients,
+ INT_TO_PTR(cmd->client_if))) {
+ error("gatt: Cannot push client on gatt device list");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* Lets create new gatt device and put it on conn_wait_queue.
+ * Once it is connected we move it to conn_list
+ */
+ dev = new0(struct gatt_device, 1);
+ if (!dev) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ memcpy(&dev->bdaddr, &addr, sizeof(bdaddr_t));
+
+ /* Create queue to keep list of clients for given device*/
+ dev->clients = queue_new();
+ if (!dev->clients) {
+ error("gatt: Cannot create client queue");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ /* Update client list of device */
+ if (!queue_push_tail(dev->clients, INT_TO_PTR(cmd->client_if))) {
+ error("gatt: Cannot push client on the client queue!?");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ /* Start le scan if not started */
+ if (queue_isempty(scan_clients)) {
+ if (!bt_le_discovery_start(le_device_found_handler)) {
+ error("gatt: Could not start scan");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+ }
+
+ if (!queue_push_tail(conn_wait_queue, dev)) {
+ error("gatt: Cannot push device on conn_wait_queue");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_CONNECT,
- HAL_STATUS_FAILED);
+ status);
+
+ /* If there is an error here we should make sure dev is out.*/
+ if ((status != HAL_STATUS_SUCCESS) && dev) {
+ destroy_device(dev);
+ return;
+ }
+
+ /* Send dummy notification since ACL is already up*/
+ if (send_notify) {
+ struct hal_ev_gatt_client_connect ev;
+
+ ev.conn_id = dev->conn_id;
+ ev.status = HAL_STATUS_SUCCESS;
+ ev.client_if = cmd->client_if;
+ bdaddr2android(&addr, &ev.bda);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_CONNECT,
+ sizeof(ev), &ev);
+ }
}

static void handle_client_disconnect(const void *buf, uint16_t len)
@@ -610,6 +1049,18 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)

hal_ipc = ipc;

+ conn_list = queue_new();
+ if (!conn_list) {
+ error("gatt: Can not create conn queue");
+ return false;
+ }
+
+ conn_wait_queue = queue_new();
+ if (!conn_wait_queue) {
+ error("gatt: Can not create conn queue");
+ return false;
+ }
+
ipc_register(hal_ipc, HAL_SERVICE_ID_GATT, cmd_handlers,
G_N_ELEMENTS(cmd_handlers));

@@ -637,4 +1088,11 @@ void bt_gatt_unregister(void)

ipc_unregister(hal_ipc, HAL_SERVICE_ID_GATT);
hal_ipc = NULL;
+
+ queue_destroy(conn_list, destroy_device);
+ conn_list = NULL;
+
+ queue_destroy(conn_wait_queue, destroy_device);
+ conn_wait_queue = NULL;
+
}
--
1.8.4


2014-03-17 00:01:12

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v3 4/5] android/gatt: Add disconnect GATT device

Disconnect scenarios:

1. If there is more then one client for a given gatt_device then
client id is removed from dev->clients, success response is sent
together with success disconnect event.

2. If there is only one client for a given remote device then we
do what is decribed above plus clean of gattrib stuff

3. In case client_if or conn_id is incorrect, response failed is sent
---
android/gatt.c | 55 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 54 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index f2a1b07..1081121 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -114,6 +114,14 @@ static bool match_dev_connect_ready(const void *data, const void *user_data)
return dev->connect_ready;
}

+static bool match_dev_by_conn_id(const void *data, const void *user_data)
+{
+ const struct gatt_device *dev = data;
+ const int32_t conn_id = PTR_TO_INT(user_data);
+
+ return dev->conn_id == conn_id;
+}
+
static void destroy_device(void *data)
{
struct gatt_device *dev = data;
@@ -678,10 +686,55 @@ reply:

static void handle_client_disconnect(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_disconnect *cmd = buf;
+ struct gatt_device *dev;
+ uint8_t status;
+ char addr[18];
+
DBG("");

+ ba2str((bdaddr_t *)&cmd->bdaddr, addr);
+
+ dev = queue_find(conn_list, match_dev_by_conn_id,
+ INT_TO_PTR(cmd->conn_id));
+ if (!dev) {
+ error("gatt: dev %s with conn_id=%d not found",
+ addr, cmd->conn_id);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ /*Check if client owns this connection */
+ if (!queue_remove_if(dev->clients, match_by_value,
+ INT_TO_PTR(cmd->client_if))) {
+ error("gatt: cannot remove conn_id=%d", cmd->client_if);
+ status = HAL_STATUS_FAILED;
+ } else {
+ status = HAL_STATUS_SUCCESS;
+ }
+
+reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_DISCONNECT, HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_DISCONNECT, status);
+
+ if (status == HAL_STATUS_FAILED)
+ return;
+
+ /* Just send disconnect event. If there is more clients on this
+ * device then this is what we shall to do.
+ * If this is last client, this is still OK to do because on connect
+ * request we do le scan and wait until remote device start
+ * advertisement */
+ send_disconnect_notify(cmd->client_if, dev, HAL_STATUS_SUCCESS);
+
+ /* If there is more clients just return */
+ if (!queue_isempty(dev->clients))
+ return;
+
+ /* If this is last client do more cleaning */
+ connection_cleanup(dev);
+ dev = queue_remove_if(conn_list, match_dev_by_bdaddr, &dev->bdaddr);
+ destroy_device(dev);
}

static void handle_client_listen(const void *buf, uint16_t len)
--
1.8.4


2014-03-17 00:01:13

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v0 5/5] android/gatt: Find primary services

With this patch it is possible to search all primary services.
---
android/gatt.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
1 file changed, 99 insertions(+), 1 deletion(-)

diff --git a/android/gatt.c b/android/gatt.c
index 1081121..4f55d15 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -36,6 +36,7 @@
#include "ipc-common.h"
#include "lib/sdp.h"
#include "lib/uuid.h"
+
#include "bluetooth.h"
#include "gatt.h"
#include "src/log.h"
@@ -64,6 +65,7 @@ struct gatt_device {

GAttrib *attrib;
GIOChannel *att_io;
+ struct queue *services;

guint watch_id;
};
@@ -127,6 +129,7 @@ static void destroy_device(void *data)
struct gatt_device *dev = data;

queue_destroy(dev->clients, NULL);
+ queue_destroy(dev->services, free);
free(dev);
}

@@ -205,6 +208,71 @@ failed:
HAL_OP_GATT_CLIENT_UNREGISTER, status);
}

+static void primary_cb(uint8_t status, GSList *services, void *user_data)
+{
+ GSList *l;
+ struct gatt_device *dev = user_data;
+ struct hal_ev_gatt_client_search_complete ev;
+
+ DBG("Status %d", status);
+
+ if (status) {
+ error("Discover all primary services failed: %s\n",
+ att_ecode2str(status));
+ ev.status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ if (!services) {
+ info("gatt: no primary serivces found");
+ ev.status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ for (l = services; l; l = l->next) {
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_search_result *ev_res = (void *)buf;
+ struct gatt_primary *prim = l->data;
+ struct gatt_primary *p = new0(struct gatt_primary, 1);
+ bt_uuid_t uuid;
+
+ memset(buf, 0, sizeof(buf));
+
+ /* Put primary service to our local list */
+ memcpy(p, prim, sizeof(*p));
+ if (!queue_push_tail(dev->services, p)) {
+ error("gatt: Cannot push primary service to the list");
+ free(p);
+ }
+
+ DBG("attr handle = 0x%04x, end grp handle = 0x%04x "
+ "uuid: %s\n", prim->range.start, prim->range.end,
+ prim->uuid);
+ /* Set event data */
+ ev_res->conn_id = dev->conn_id;
+ ev_res->srvc_id.is_primary = 1;
+ ev_res->srvc_id.inst_id = 0;
+
+ if (bt_string_to_uuid(&uuid, prim->uuid) < 0) {
+ error("Can not convert string to uuid");
+ continue;
+ }
+ memcpy(&ev_res->srvc_id.uuid, &uuid.value, sizeof(uuid.value));
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT ,
+ HAL_EV_GATT_CLIENT_SEARCH_RESULT,
+ sizeof(*ev_res), ev_res);
+ }
+
+ ev.status = HAL_STATUS_SUCCESS;
+
+reply:
+ ev.conn_id = dev->conn_id;
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SEARCH_COMPLETE,
+ sizeof(ev), &ev);
+}
+
static void connection_cleanup(struct gatt_device *device)
{
if (device->watch_id) {
@@ -635,6 +703,14 @@ static void handle_client_connect(const void *buf, uint16_t len)
goto reply;
}

+ dev->services = queue_new();
+ if (!dev->services) {
+ error("gatt: Cannot create services queue");
+ queue_destroy(dev->clients, NULL);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
/* Update client list of device */
if (!queue_push_tail(dev->clients, INT_TO_PTR(cmd->client_if))) {
error("gatt: Cannot push client on the client queue!?");
@@ -755,10 +831,32 @@ static void handle_client_refresh(const void *buf, uint16_t len)

static void handle_client_search_service(const void *buf, uint16_t len)
{
+ const struct hal_cmd_gatt_client_search_service *cmd = buf;
+ struct gatt_device *dev;
+ uint8_t status;
+
DBG("");

+ dev = queue_find(conn_list, match_dev_by_conn_id,
+ INT_TO_PTR(cmd->conn_id));
+ if (!dev) {
+ error("gatt: dev with conn_id=%d not found", cmd->conn_id);
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ /*TODO: Handle filter uuid */
+
+ if (!gatt_discover_primary(dev->attrib, NULL, primary_cb, dev)) {
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT,
- HAL_OP_GATT_CLIENT_SEARCH_SERVICE, HAL_STATUS_FAILED);
+ HAL_OP_GATT_CLIENT_SEARCH_SERVICE, status);
}

static void handle_client_get_included_service(const void *buf, uint16_t len)
--
1.8.4


2014-03-17 00:01:10

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v3 2/5] android/gatt: Use Core profile for LE scan

From: Jakub Tyszkowski <[email protected]>

This makes gatt capable of triggering LE scan using functionality
exposed by Core API. GATT registers its own callbacks for discovering
events.
---
android/gatt.c | 102 +++++++++++++++++++++++++++++++++++++++++++++++++++++++--
1 file changed, 99 insertions(+), 3 deletions(-)

diff --git a/android/gatt.c b/android/gatt.c
index b566470..d5abbda 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -32,10 +32,12 @@

#include "ipc.h"
#include "ipc-common.h"
-#include "lib/bluetooth.h"
+#include "lib/sdp.h"
+#include "bluetooth.h"
#include "gatt.h"
#include "src/log.h"
#include "hal-msg.h"
+#include "utils.h"
#include "src/shared/util.h"
#include "src/shared/queue.h"

@@ -47,6 +49,7 @@ struct gatt_client {
static struct ipc *hal_ipc = NULL;
static bdaddr_t adapter_addr;
static struct queue *gatt_clients = NULL;
+static struct queue *scan_clients = NULL;

static bool match_client_by_uuid(const void *data, const void *user_data)
{
@@ -64,6 +67,36 @@ static bool match_client_by_id(const void *data, const void *user_data)
return client->id == exp_id;
}

+static bool match_by_value(const void *data, const void *user_data)
+{
+ return data == user_data;
+}
+
+static void le_device_found_handler(bdaddr_t *addr, uint8_t addr_type, int rssi,
+ uint16_t eir_len, const void *eir)
+{
+ uint8_t buf[IPC_MTU];
+ struct hal_ev_gatt_client_scan_result *ev = (void *) buf;
+ char bda[18];
+
+ if (queue_isempty(scan_clients))
+ return;
+
+ ba2str(addr, bda);
+ DBG("LE Device found: %s, rssi: %d, adv_data: %d", bda, rssi,
+ eir ? true : false);
+
+ bdaddr2android(addr, ev->bda);
+ ev->rssi = rssi;
+ ev->len = eir_len;
+
+ memcpy(ev->adv_data, eir, ev->len);
+
+ ipc_send_notif(hal_ipc, HAL_SERVICE_ID_GATT,
+ HAL_EV_GATT_CLIENT_SCAN_RESULT,
+ sizeof(ev) + ev->len, ev);
+}
+
static void handle_client_register(const void *buf, uint16_t len)
{
const struct hal_cmd_gatt_client_register *cmd = buf;
@@ -124,6 +157,13 @@ static void handle_client_unregister(const void *buf, uint16_t len)
goto failed;
}

+ queue_remove_if(scan_clients, match_by_value,
+ INT_TO_PTR(cmd->client_if));
+
+ /* If there is no client interesting in scan, just stop it */
+ if (queue_isempty(scan_clients))
+ bt_le_discovery_stop(NULL);
+
free(cl);
status = HAL_STATUS_SUCCESS;

@@ -134,10 +174,59 @@ failed:

static void handle_client_scan(const void *buf, uint16_t len)
{
- DBG("");
+ const struct hal_cmd_gatt_client_scan *cmd = buf;
+ uint8_t status;
+ void *registered;
+ void *l;
+
+ DBG("new state %d", cmd->start);

+ registered = queue_find(gatt_clients, match_client_by_id,
+ INT_TO_PTR(cmd->client_if));
+ /* Turn off scan */
+ if (!cmd->start) {
+ if (registered)
+ queue_remove_if(scan_clients, match_by_value,
+ INT_TO_PTR(cmd->client_if));
+
+ if (queue_isempty(scan_clients)) {
+ DBG("Stopping LE SCAN");
+ bt_le_discovery_stop(NULL);
+ }
+
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ /* If device already do scan, reply with success and avoid to add it
+ * again to the list
+ */
+ l = queue_find(scan_clients, match_by_value,
+ INT_TO_PTR(cmd->client_if));
+ if (l) {
+ status = HAL_STATUS_SUCCESS;
+ goto reply;
+ }
+
+ if (!bt_le_discovery_start(le_device_found_handler)) {
+ error("gatt: LE scan switch failed");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ /* Add scan client to the list if its registered */
+ if (registered && !queue_push_tail(scan_clients,
+ INT_TO_PTR(cmd->client_if))) {
+ error("gatt: Cannot push scan client");
+ status = HAL_STATUS_FAILED;
+ goto reply;
+ }
+
+ status = HAL_STATUS_SUCCESS;
+
+reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_GATT, HAL_OP_GATT_CLIENT_SCAN,
- HAL_STATUS_FAILED);
+ status);
}

static void handle_client_connect(const void *buf, uint16_t len)
@@ -530,6 +619,13 @@ bool bt_gatt_register(struct ipc *ipc, const bdaddr_t *addr)
return false;
}

+ scan_clients = queue_new();
+ if (!scan_clients) {
+ error("gatt: Cannot allocate scan_clients");
+ queue_destroy(gatt_clients, NULL);
+ return false;
+ }
+
return true;
}

--
1.8.4


2014-03-17 00:01:09

by Lukasz Rymanowski

[permalink] [raw]
Subject: [PATCH v3 1/5] android/bluetooth: Add GATT notifications on LE discovery

From: Jakub Tyszkowski <[email protected]>

This patch introduce API which GATT can use to start/stop discovery
and register for required events.

This is because GATT needs to get from GAP notifications about
founded devices and also notification when discovery has been stopped.

GATT will need it explicity when GATT client calls scan, and also in
case of connect device, as before le connect is sent we do scan first
to make sure that device is available.

For now on adapter have two variables tracing discovery.
1. cur_discovery_type which show type of ongoing discovery type.
2. exp_discovery_type which shows type of next discovery session.

We need this because of scenarion when GATT is interesting in scan and
in the same time HAL wants to do scanning.
---
android/bluetooth.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++------
android/bluetooth.h | 7 ++++
2 files changed, 103 insertions(+), 11 deletions(-)

diff --git a/android/bluetooth.c b/android/bluetooth.c
index 3e2e547..a537507 100644
--- a/android/bluetooth.c
+++ b/android/bluetooth.c
@@ -80,10 +80,13 @@
#define BASELEN_REMOTE_DEV_PROP (sizeof(struct hal_ev_remote_device_props) \
+ sizeof(struct hal_property))

+#define SCAN_TYPE_NONE 0
#define SCAN_TYPE_BREDR (1 << BDADDR_BREDR)
#define SCAN_TYPE_LE ((1 << BDADDR_LE_PUBLIC) | (1 << BDADDR_LE_RANDOM))
#define SCAN_TYPE_DUAL (SCAN_TYPE_BREDR | SCAN_TYPE_LE)

+#define BDADDR_LE (BDADDR_LE_RANDOM | BDADDR_LE_PUBLIC)
+
struct device {
bdaddr_t bdaddr;
uint8_t bdaddr_type;
@@ -122,7 +125,8 @@ static struct {

uint32_t current_settings;

- bool discovering;
+ uint8_t cur_discovery_type;
+ uint8_t exp_discovery_type;
uint32_t discoverable_timeout;

GSList *uuids;
@@ -131,7 +135,8 @@ static struct {
.dev_class = 0,
.name = NULL,
.current_settings = 0,
- .discovering = false,
+ .cur_discovery_type = 0,
+ .exp_discovery_type = 0,
.discoverable_timeout = DEFAULT_DISCOVERABLE_TIMEOUT,
.uuids = NULL,
};
@@ -149,6 +154,9 @@ static struct mgmt *mgmt_if = NULL;
static GSList *bonded_devices = NULL;
static GSList *cached_devices = NULL;

+static bt_le_device_found gatt_device_found_cb = NULL;
+static bt_le_discovery_stopped gatt_discovery_stopped_cb = NULL;
+
/* This list contains addresses which are asked for records */
static GSList *browse_reqs;

@@ -509,7 +517,6 @@ static void settings_changed(uint32_t settings)
if (changed_mask & MGMT_SETTING_POWERED)
powered_changed();

-
scan_mode_mask = MGMT_SETTING_CONNECTABLE |
MGMT_SETTING_DISCOVERABLE;

@@ -1036,6 +1043,7 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,
{
const struct mgmt_ev_discovering *ev = param;
struct hal_ev_discovery_state_changed cp;
+ bool is_discovering = adapter.cur_discovery_type;

if (length < sizeof(*ev)) {
error("Too small discovering event");
@@ -1045,14 +1053,14 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,
DBG("hci%u type %u discovering %u", index, ev->type,
ev->discovering);

- if (adapter.discovering == !!ev->discovering)
+ if (is_discovering == !!ev->discovering)
return;

- adapter.discovering = !!ev->discovering;
+ adapter.cur_discovery_type = ev->discovering ? ev->type : 0;

DBG("new discovering state %u", ev->discovering);

- if (adapter.discovering) {
+ if (adapter.cur_discovery_type != SCAN_TYPE_NONE) {
cp.state = HAL_DISCOVERY_STATE_STARTED;
} else {
g_slist_foreach(bonded_devices, clear_device_found, NULL);
@@ -1062,6 +1070,27 @@ static void mgmt_discovering_event(uint16_t index, uint16_t length,

ipc_send_notif(hal_ipc, HAL_SERVICE_ID_BLUETOOTH,
HAL_EV_DISCOVERY_STATE_CHANGED, sizeof(cp), &cp);
+
+ if (gatt_discovery_stopped_cb &&
+ (adapter.cur_discovery_type == SCAN_TYPE_NONE)) {
+ /* One shot notification about discovery stopped send to gatt*/
+ gatt_discovery_stopped_cb();
+ gatt_discovery_stopped_cb = NULL;
+ }
+
+ /* If discovery is ON or there is no expected next discovery session
+ * then just return
+ */
+ if ((adapter.cur_discovery_type != SCAN_TYPE_NONE) ||
+ (adapter.exp_discovery_type == SCAN_TYPE_NONE))
+ return;
+
+ start_discovery(adapter.exp_discovery_type);
+
+ /* Maintain expected discovery type if there is gatt client
+ * registered
+ */
+ adapter.exp_discovery_type = gatt_device_found_cb ? SCAN_TYPE_LE : 0;
}

static void confirm_device_name_cb(uint8_t status, uint16_t length,
@@ -1143,7 +1172,7 @@ static void update_new_device(struct device *dev, int8_t rssi,

memset(buf, 0, sizeof(buf));

- if (adapter.discovering)
+ if (adapter.cur_discovery_type)
dev->found = true;

size = sizeof(*ev);
@@ -1251,6 +1280,11 @@ static void update_found_device(const bdaddr_t *bdaddr, uint8_t bdaddr_type,
update_device(dev, rssi, &eir);
}

+ /* Notify Gatt if its registered for LE events */
+ if (gatt_device_found_cb && (dev->bdaddr_type & BDADDR_LE))
+ gatt_device_found_cb(&dev->bdaddr, dev->bdaddr_type,
+ dev->rssi, sizeof(eir), &eir);
+
eir_data_free(&eir);

if (dev->bond_state != HAL_BOND_STATE_BONDED)
@@ -2511,6 +2545,38 @@ static bool stop_discovery(uint8_t type)
return false;
}

+bool bt_le_discovery_stop(bt_le_discovery_stopped cb)
+{
+ if (!adapter.cur_discovery_type) {
+ if (cb)
+ cb();
+ return true;
+ }
+
+ gatt_discovery_stopped_cb = cb;
+ /* Remove device found callback */
+ gatt_device_found_cb = NULL;
+ adapter.exp_discovery_type &= ~SCAN_TYPE_LE;
+
+ return stop_discovery(adapter.cur_discovery_type);
+}
+
+bool bt_le_discovery_start(bt_le_device_found cb)
+{
+ if (!(adapter.current_settings & MGMT_SETTING_POWERED))
+ return false;
+
+ gatt_device_found_cb = cb;
+
+ adapter.exp_discovery_type |= SCAN_TYPE_LE;
+
+ /* If core is discovering, don't bother */
+ if (adapter.cur_discovery_type)
+ return true;
+
+ return start_discovery(adapter.exp_discovery_type);
+}
+
static uint8_t set_adapter_scan_mode(const void *buf, uint16_t len)
{
const uint8_t *mode = buf;
@@ -3180,7 +3246,8 @@ static void handle_start_discovery_cmd(const void *buf, uint16_t len)
{
uint8_t status;

- if (adapter.discovering) {
+ /* Check if there is discovery with BREDR type */
+ if (adapter.cur_discovery_type & SCAN_TYPE_BREDR) {
status = HAL_STATUS_SUCCESS;
goto reply;
}
@@ -3190,12 +3257,27 @@ static void handle_start_discovery_cmd(const void *buf, uint16_t len)
goto reply;
}

- if (!start_discovery(SCAN_TYPE_DUAL)) {
+ adapter.exp_discovery_type |= SCAN_TYPE_DUAL;
+
+ /* If there is no discovery ongoing, try to start discovery */
+ if (!adapter.cur_discovery_type) {
+ if (!start_discovery(adapter.exp_discovery_type))
+ status = HAL_STATUS_FAILED;
+ else
+ status = HAL_STATUS_SUCCESS;
+
+ goto reply;
+ }
+
+ /* Stop discovery here. Once it is stop we will restart it
+ * with exp_discovery_settings */
+ if (!stop_discovery(adapter.cur_discovery_type)) {
status = HAL_STATUS_FAILED;
goto reply;
}

status = HAL_STATUS_SUCCESS;
+
reply:
ipc_send_rsp(hal_ipc, HAL_SERVICE_ID_BLUETOOTH, HAL_OP_START_DISCOVERY,
status);
@@ -3205,7 +3287,7 @@ static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
{
uint8_t status;

- if (!adapter.discovering) {
+ if (!adapter.cur_discovery_type) {
status = HAL_STATUS_SUCCESS;
goto reply;
}
@@ -3215,7 +3297,10 @@ static void handle_cancel_discovery_cmd(const void *buf, uint16_t len)
goto reply;
}

- if (!stop_discovery(SCAN_TYPE_DUAL)) {
+ /* Take into account that gatt might want to keep discover */
+ adapter.exp_discovery_type = gatt_device_found_cb ? SCAN_TYPE_LE : 0;
+
+ if (!stop_discovery(adapter.cur_discovery_type)) {
status = HAL_STATUS_FAILED;
goto reply;
}
diff --git a/android/bluetooth.h b/android/bluetooth.h
index f436178..a03305d 100644
--- a/android/bluetooth.h
+++ b/android/bluetooth.h
@@ -34,3 +34,10 @@ void bt_bluetooth_unregister(void);

int bt_adapter_add_record(sdp_record_t *rec, uint8_t svc_hint);
void bt_adapter_remove_record(uint32_t handle);
+
+typedef void (*bt_le_device_found)(bdaddr_t *addr, uint8_t addr_type, int rssi,
+ uint16_t eir_len, const void *eir);
+bool bt_le_discovery_start(bt_le_device_found cb);
+
+typedef void (*bt_le_discovery_stopped)(void);
+bool bt_le_discovery_stop(bt_le_discovery_stopped cb);
--
1.8.4