Diff between 02c6fdc7d217560fd598393281ea23fa830503ea and f20cfda54ca87d9839eff2af88047bf1b70cc544

Changed Files

File Additions Deletions Status
android/gatt.c +208 -34 modified

Full Patch

diff --git a/android/gatt.c b/android/gatt.c
index 67a1330..7f03af5 100644
--- a/android/gatt.c
+++ b/android/gatt.c
@@ -156,6 +156,8 @@ struct gatt_device {
 
 	int ref;
 	int conn_cnt;
+
+	struct queue *pending_requests;
 };
 
 struct app_connection {
@@ -753,6 +755,25 @@ static void disconnect_notify_by_device(void *data, void *user_data)
 		send_app_connect_notify(conn, GATT_FAILURE);
 }
 
+#define READ_INIT -3
+#define READ_PENDING -2
+#define READ_FAILED -1
+
+struct pending_request {
+	uint16_t handle;
+	int length;
+	uint8_t *value;
+	uint16_t offset;
+};
+
+static void destroy_pending_request(void *data)
+{
+	struct pending_request *entry = data;
+
+	free(entry->value);
+	free(entry);
+}
+
 static void destroy_device(void *data)
 {
 	struct gatt_device *dev = data;
@@ -761,6 +782,7 @@ static void destroy_device(void *data)
 		return;
 
 	queue_destroy(dev->services, destroy_service);
+	queue_destroy(dev->pending_requests, destroy_pending_request);
 
 	free(dev);
 }
@@ -1143,6 +1165,13 @@ static struct gatt_device *create_device(const bdaddr_t *addr)
 		return NULL;
 	}
 
+	dev->pending_requests = queue_new();
+	if (!dev->pending_requests) {
+		error("gatt: Failed to allocate memory for client");
+		destroy_device(dev);
+		return NULL;
+	}
+
 	if (!queue_push_head(gatt_devices, dev)) {
 		error("gatt: Cannot push device to queue");
 		destroy_device(dev);
@@ -3387,6 +3416,118 @@ failed:
 				HAL_OP_GATT_SERVER_ADD_INC_SERVICE, status);
 }
 
+static void send_dev_pending_response(struct gatt_device *device,
+								uint8_t opcode)
+{
+	uint8_t rsp[ATT_DEFAULT_LE_MTU];
+	struct pending_request *val;
+	uint16_t len = 0;
+	uint8_t error = 0;
+
+	switch (opcode) {
+	case ATT_OP_READ_BLOB_REQ:
+		val = queue_pop_head(device->pending_requests);
+		if (!val || val->length < 0) {
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+			goto done;
+		}
+
+		len = enc_read_blob_resp(val->value, val->length, val->offset,
+							rsp, sizeof(rsp));
+		destroy_pending_request(val);
+		break;
+	case ATT_OP_READ_REQ:
+		val = queue_pop_head(device->pending_requests);
+		if (!val || val->length < 0) {
+			error = ATT_ECODE_ATTR_NOT_FOUND;
+			goto done;
+		}
+
+		len = enc_read_resp(val->value, val->length, rsp, sizeof(rsp));
+		destroy_pending_request(val);
+		break;
+	default:
+		break;
+	}
+
+done:
+	if (!len)
+		len = enc_error_resp(opcode, 0x0000, error, rsp,
+							ATT_DEFAULT_LE_MTU);
+
+	g_attrib_send(device->attrib, 0, rsp, len, NULL, NULL, NULL);
+
+	queue_remove_all(device->pending_requests, NULL, NULL,
+						destroy_pending_request);
+}
+
+struct request_processing_data {
+	uint8_t opcode;
+	struct gatt_device *device;
+};
+
+static bool match_pending_dev_request(const void *data, const void *user_data)
+{
+	const struct pending_request *pending_request = data;
+
+	return pending_request->length == READ_PENDING;
+}
+
+static bool match_dev_request_by_handle(const void *data, const void *user_data)
+{
+	const struct pending_request *handle_data = data;
+	uint16_t handle = PTR_TO_UINT(user_data);
+
+	return handle_data->handle == handle;
+}
+
+static void read_requested_attributes(void *data, void *user_data)
+{
+	struct pending_request *resp_data = data;
+	struct request_processing_data *process_data = user_data;
+	uint8_t *value;
+	int value_len;
+
+	if (!gatt_db_read(gatt_db, resp_data->handle,
+						resp_data->offset,
+						process_data->opcode,
+						&process_data->device->bdaddr,
+						&value, &value_len))
+		resp_data->length = READ_FAILED;
+
+	/* We have value here already if no callback will be called */
+	if (value_len >= 0) {
+		resp_data->value = malloc0(value_len);
+		if (!resp_data->value) {
+			/* If data cannot be copied, act like when read fails */
+			resp_data->length = READ_FAILED;
+			return;
+		}
+
+		memcpy(resp_data->value, value, value_len);
+		resp_data->length = value_len;
+	} else {
+		resp_data->length = READ_PENDING;
+	}
+}
+
+static void process_dev_pending_requests(struct gatt_device *device,
+							uint8_t att_opcode)
+{
+	struct request_processing_data process_data;
+
+	process_data.device = device;
+	process_data.opcode = att_opcode;
+
+	/* Process pending requests and prepare response */
+	queue_foreach(device->pending_requests, read_requested_attributes,
+								&process_data);
+
+	if (!queue_find(device->pending_requests,
+					match_pending_dev_request, NULL))
+		send_dev_pending_response(device, att_opcode);
+}
+
 static void send_gatt_response(uint8_t opcode, uint16_t handle,
 					uint16_t offset, uint8_t status,
 					uint16_t len, const uint8_t *data,
@@ -4096,7 +4237,8 @@ static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
 {
 	uint16_t handle;
 	uint16_t len;
-	uint16_t offset = 0;
+	uint16_t offset;
+	struct pending_request *data;
 
 	DBG("");
 
@@ -4110,15 +4252,26 @@ static uint8_t read_request(const uint8_t *cmd, uint16_t cmd_len,
 		len = dec_read_req(cmd, cmd_len, &handle);
 		if (!len)
 			return ATT_ECODE_INVALID_PDU;
+		offset = 0;
 		break;
 	default:
 		error("gatt: Unexpected read type 0x%02x", cmd[0]);
 		return ATT_ECODE_REQ_NOT_SUPP;
 	}
 
-	if (!gatt_db_read(gatt_db, handle, offset, cmd[0], &dev->bdaddr, NULL,
-									NULL))
-		return ATT_ECODE_UNLIKELY;
+	data = new0(struct pending_request, 1);
+	if (!data)
+		return ATT_ECODE_INSUFF_RESOURCES;
+
+	data->offset = offset;
+	data->handle = handle;
+	data->length = READ_INIT;
+	if (!queue_push_tail(dev->pending_requests, data)) {
+		free(data);
+		return ATT_ECODE_INSUFF_RESOURCES;
+	}
+
+	process_dev_pending_requests(dev, cmd[0]);
 
 	return 0;
 }
@@ -4319,9 +4472,6 @@ static void att_handler(const uint8_t *ipdu, uint16_t len, gpointer user_data)
 	case ATT_OP_READ_REQ:
 	case ATT_OP_READ_BLOB_REQ:
 		status = read_request(ipdu, len, dev);
-		if (!status)
-			/* Response will be sent in callback */
-			return;
 		break;
 	case ATT_OP_MTU_REQ:
 		status = mtu_att_handle(ipdu, len, opdu, sizeof(opdu), dev,
@@ -4364,7 +4514,8 @@ done:
 		length = enc_error_resp(ipdu[0], 0x0000, status, opdu,
 							ATT_DEFAULT_LE_MTU);
 
-	g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
+	if (length)
+		g_attrib_send(dev->attrib, 0, opdu, length, NULL, NULL, NULL);
 }
 
 static void create_listen_connections(void *data, void *user_data)
@@ -4465,8 +4616,7 @@ static struct gap_srvc_handles gap_srvc_data;
 static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
 					bdaddr_t *bdaddr, void *user_data)
 {
-	uint8_t pdu[ATT_DEFAULT_LE_MTU];
-	uint16_t len;
+	struct pending_request *entry;
 	struct gatt_device *dev;
 
 	DBG("");
@@ -4477,33 +4627,46 @@ static void gap_read_cb(uint16_t handle, uint16_t offset, uint8_t att_opcode,
 		return;
 	}
 
+	entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+							UINT_TO_PTR(handle));
+	if (!entry)
+		return;
+
 	if (handle == gap_srvc_data.dev_name) {
 		const char *name = bt_get_adapter_name();
 
-		len = enc_read_resp((uint8_t *)name, strlen(name),
-							pdu, sizeof(pdu));
-		goto done;
-	}
+		entry->value = malloc0(strlen(name));
+		if (!entry->value) {
+			queue_remove(dev->pending_requests, entry);
+			free(entry);
+			return;
+		}
 
-	if (handle == gap_srvc_data.appear) {
-		uint8_t val[2];
-		put_le16(APPEARANCE_GENERIC_PHONE, val);
-		len = enc_read_resp(val, sizeof(val), pdu, sizeof(pdu));
-		goto done;
-	}
+		entry->length = strlen(name);
+		memcpy(entry->value, bt_get_adapter_name(), entry->length);
+	} else if (handle == gap_srvc_data.appear) {
+		entry->value = malloc0(2);
+		if (!entry->value) {
+			queue_remove(dev->pending_requests, entry);
+			free(entry);
+			return;
+		}
 
-	if (handle == gap_srvc_data.priv) {
-		uint8_t val = PERIPHERAL_PRIVACY_DISABLE;
-		len = enc_read_resp(&val, sizeof(val), pdu, sizeof(pdu));
-		goto done;
-	}
+		put_le16(APPEARANCE_GENERIC_PHONE, entry->value);
+		entry->length = sizeof(uint8_t) * 2;
+	} else if (handle == gap_srvc_data.priv) {
+		entry->value = malloc0(1);
+		if (!entry->value) {
+			queue_remove(dev->pending_requests, entry);
+			free(entry);
+			return;
+		}
 
-	error("gatt: Unknown handle 0x%02x", handle);
-	len = enc_error_resp(ATT_OP_READ_REQ, handle,
-					ATT_ECODE_UNLIKELY, pdu, sizeof(pdu));
+		*entry->value = PERIPHERAL_PRIVACY_DISABLE;
+		entry->length = sizeof(uint8_t);
+	}
 
-done:
-	g_attrib_send(dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+	entry->offset = offset;
 }
 
 static void register_gap_service(void)
@@ -4567,10 +4730,9 @@ static void device_info_read_cb(uint16_t handle, uint16_t offset,
 					uint8_t att_opcode, bdaddr_t *bdaddr,
 					void *user_data)
 {
-	uint8_t pdu[ATT_DEFAULT_LE_MTU];
+	struct pending_request *entry;
 	struct gatt_device *dev;
 	char *buf = user_data;
-	uint16_t len;
 
 	dev = find_device_by_addr(bdaddr);
 	if (!dev) {
@@ -4578,9 +4740,21 @@ static void device_info_read_cb(uint16_t handle, uint16_t offset,
 		return;
 	}
 
-	len = enc_read_resp((uint8_t *) buf, strlen(buf), pdu, sizeof(pdu));
+	entry = queue_find(dev->pending_requests, match_dev_request_by_handle,
+							UINT_TO_PTR(handle));
+	if (!entry)
+		return;
+
+	entry->value = malloc0(strlen(buf));
+	if (!entry->value) {
+		queue_remove(dev->pending_requests, entry);
+		free(entry);
+		return;
+	}
 
-	g_attrib_send(dev->attrib, 0, pdu, len, NULL, NULL, NULL);
+	entry->length = strlen(buf);
+	memcpy(entry->value, buf, entry->length);
+	entry->offset = offset;
 }
 
 static void register_device_info_service(void)