diff mbox series

[2/5] Implement GetServiceRecords method

Message ID 20200401221434.12185-2-sonnysasaka@chromium.org
State New
Headers show
Series [1/5] D-Bus API changes for managing SDP records | expand

Commit Message

Sonny Sasaka April 1, 2020, 10:14 p.m. UTC
From: Miao Chou <mcchou@chromium.org>

This implements the GetServiceRecords method of org.bluez.Device1 interface.
---
 lib/sdp.h    |  13 +++
 src/device.c | 293 +++++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 306 insertions(+)
diff mbox series

Patch

diff --git a/lib/sdp.h b/lib/sdp.h
index f586eb5eb..c6fdf3d3a 100644
--- a/lib/sdp.h
+++ b/lib/sdp.h
@@ -396,6 +396,19 @@  extern "C" {
 #define SDP_URL_STR16		0x46
 #define SDP_URL_STR32		0x47
 
+/*
+ * These are Chromium specific SDP data types which map the above SDP datatype
+ * but without specifying the sizes of types.
+ */
+#define SDP_VAL_TYPE_NIL	0x00
+#define SDP_VAL_TYPE_UINT	0x01
+#define SDP_VAL_TYPE_INT	0x02
+#define SDP_VAL_TYPE_UUID	0x03
+#define SDP_VAL_TYPE_STRING	0x04
+#define SDP_VAL_TYPE_BOOL	0x05
+#define SDP_VAL_TYPE_SEQUENCE	0x06
+#define SDP_VAL_TYPE_URL	0x07
+
 /*
  * The PDU identifiers of SDP packets between client and server
  */
diff --git a/src/device.c b/src/device.c
index ace9c348c..1b5afc226 100644
--- a/src/device.c
+++ b/src/device.c
@@ -2762,6 +2762,296 @@  static DBusMessage *cancel_pairing(DBusConnection *conn, DBusMessage *msg,
 	return dbus_message_new_method_return(msg);
 }
 
+static dbus_bool_t append_attr_value(sdp_data_t* val,
+					DBusMessageIter *val_entry)
+{
+	DBusMessageIter val_struct;
+	DBusMessageIter val_variant;
+	DBusMessageIter val_seq_array;
+
+	uint8_t value_type;
+	void* value = NULL;
+	uint32_t value_size = (uint32_t) val->unitSize;
+	sdp_data_t *data;
+	char *str = NULL;
+
+	int type;
+	const char *type_sig;
+
+	if (!dbus_message_iter_open_container(val_entry, DBUS_TYPE_STRUCT,
+						NULL, &val_struct))
+		return FALSE;
+
+	switch (val->dtd) {
+	case SDP_DATA_NIL:
+		goto val_nil;
+	case SDP_BOOL:
+		value_type = SDP_VAL_TYPE_BOOL;
+		value = &val->val.uint8;
+		value_size = sizeof(uint8_t);
+		type_sig = DBUS_TYPE_BOOLEAN_AS_STRING;
+		type = DBUS_TYPE_BOOLEAN;
+		break;
+	case SDP_UINT8:
+		value_type = SDP_VAL_TYPE_UINT;
+		value = &val->val.uint8;
+		value_size = sizeof(uint8_t);
+		type_sig = DBUS_TYPE_BYTE_AS_STRING;
+		type = DBUS_TYPE_BYTE;
+		break;
+	case SDP_UINT16:
+		value_type = SDP_VAL_TYPE_UINT;
+		value = &val->val.uint16;
+		value_size = sizeof(uint16_t);
+		type_sig = DBUS_TYPE_UINT16_AS_STRING;
+		type = DBUS_TYPE_UINT16;
+		break;
+	case SDP_UINT32:
+		value_type = SDP_VAL_TYPE_UINT;
+		value = &val->val.uint32;
+		value_size = sizeof(uint32_t);
+		type_sig = DBUS_TYPE_UINT32_AS_STRING;
+		type = DBUS_TYPE_UINT32;
+		break;
+	case SDP_UINT64:
+		value_type = SDP_VAL_TYPE_UINT;
+		value = &val->val.uint64;
+		value_size = sizeof(uint64_t);
+		type_sig = DBUS_TYPE_UINT64_AS_STRING;
+		type = DBUS_TYPE_UINT64;
+		break;
+	case SDP_INT8:
+		value_type = SDP_VAL_TYPE_INT;
+		value = &val->val.int8;
+		value_size = sizeof(int8_t);
+		type_sig = DBUS_TYPE_BYTE_AS_STRING;
+		type = DBUS_TYPE_BYTE;
+		break;
+	case SDP_INT16:
+		value_type = SDP_VAL_TYPE_INT;
+		value = &val->val.int16;
+		value_size = sizeof(int16_t);
+		type_sig = DBUS_TYPE_INT16_AS_STRING;
+		type = DBUS_TYPE_INT16;
+		break;
+	case SDP_INT32:
+		value_type = SDP_VAL_TYPE_INT;
+		value = &val->val.int32;
+		value_size = sizeof(int32_t);
+		type_sig = DBUS_TYPE_INT32_AS_STRING;
+		type = DBUS_TYPE_INT32;
+		break;
+	case SDP_INT64:
+		value_type = SDP_VAL_TYPE_INT;
+		value = &val->val.int64;
+		value_size = sizeof(int64_t);
+		type_sig = DBUS_TYPE_INT64_AS_STRING;
+		type = DBUS_TYPE_INT64;
+		break;
+	case SDP_UUID16:
+	case SDP_UUID32:
+	case SDP_UUID128:
+		// Although a UUID is passed as a string in format
+		// "0000XXXX-0000-1000-8000-00805F9B34FB", |value_size|
+		// should hold the original length of a UUID. The length unit
+		// is byte, so a length can be 2, 4, 16.
+		if (val->dtd == SDP_UUID16)
+			value_size = sizeof(uint16_t);
+		else if (val->dtd == SDP_UUID32)
+			value_size = sizeof(uint32_t);
+		else
+			value_size = sizeof(uint128_t);
+		value_type = SDP_VAL_TYPE_UUID;
+		str = bt_uuid2string(&val->val.uuid);
+		value = &str;
+		type_sig = DBUS_TYPE_STRING_AS_STRING;
+		type = DBUS_TYPE_STRING;
+		break;
+	case SDP_TEXT_STR8:
+	case SDP_TEXT_STR16:
+	case SDP_TEXT_STR32:
+		// TODO(chromium:633929): Distinguish string and data array by
+		// checking if the given string is in UTF-8 format or not. For
+		// now the binary case is skipped by setting |str| as a empty
+		// string.
+		str = val->val.str;
+		if (!dbus_validate_utf8(str, NULL))
+			str = "";
+		value_size = strlen(str);
+		value_type = SDP_VAL_TYPE_STRING;
+		value = &str;
+		type_sig = DBUS_TYPE_STRING_AS_STRING;
+		type = DBUS_TYPE_STRING;
+		break;
+	case SDP_URL_STR8:
+	case SDP_URL_STR16:
+	case SDP_URL_STR32:
+		value_type = SDP_VAL_TYPE_URL;
+		str = val->val.str;
+		value_size = strlen(str);
+		value = &str;
+		type_sig = DBUS_TYPE_STRING_AS_STRING;
+		type = DBUS_TYPE_STRING;
+		break;
+	case SDP_ALT8:
+	case SDP_ALT16:
+	case SDP_ALT32:
+	case SDP_SEQ8:
+	case SDP_SEQ16:
+	case SDP_SEQ32: {
+		uint32_t size = 0;
+
+		value_type = SDP_VAL_TYPE_SEQUENCE;
+
+		// Calculate the number of elements in the sequence.
+		for (data = val->val.dataseq; data; data = data->next)
+			size++;
+
+		if (!dbus_message_iter_append_basic(&val_struct,
+						DBUS_TYPE_BYTE, &value_type))
+			return FALSE;
+		if (!dbus_message_iter_append_basic(&val_struct,
+						DBUS_TYPE_UINT32, &size))
+			return FALSE;
+		if (!dbus_message_iter_open_container(&val_struct,
+				DBUS_TYPE_VARIANT, "a(yuv)", &val_variant))
+			return FALSE;
+		if (!dbus_message_iter_open_container(&val_variant,
+				DBUS_TYPE_ARRAY, "(yuv)", &val_seq_array))
+			return FALSE;
+
+		for (data = val->val.dataseq; data; data = data->next)
+			append_attr_value(data, &val_seq_array);
+
+		if (!dbus_message_iter_close_container(&val_variant,
+							&val_seq_array))
+			return FALSE;
+		if (!dbus_message_iter_close_container(&val_struct,
+							&val_variant))
+			return FALSE;
+		goto done;
+	}
+	default:
+		goto val_nil;
+	}
+
+	if (!dbus_message_iter_append_basic(&val_struct,
+						DBUS_TYPE_BYTE, &value_type))
+		goto failed_to_append;
+	if (!dbus_message_iter_append_basic(&val_struct,
+						DBUS_TYPE_UINT32, &value_size))
+		goto failed_to_append;
+	if (!dbus_message_iter_open_container(&val_struct, DBUS_TYPE_VARIANT,
+						type_sig, &val_variant))
+		goto failed_to_append;
+	if (!dbus_message_iter_append_basic(&val_variant, type, value))
+		goto failed_to_append;
+
+	if (value_type == SDP_VAL_TYPE_UUID)
+		free(str);
+
+	if (!dbus_message_iter_close_container(&val_struct, &val_variant))
+		return FALSE;
+
+val_nil:
+done:
+	if (!dbus_message_iter_close_container(val_entry, &val_struct))
+		return FALSE;
+	return TRUE;
+
+failed_to_append:
+	if (value_type == SDP_VAL_TYPE_UUID)
+		free(str);
+	return FALSE;
+}
+
+static dbus_bool_t append_attr(sdp_data_t *attr, DBusMessageIter *attr_dict)
+{
+	DBusMessageIter attr_entry;
+
+	if (!dbus_message_iter_open_container(attr_dict, DBUS_TYPE_DICT_ENTRY,
+				NULL, &attr_entry))
+		return FALSE;
+
+	if (!dbus_message_iter_append_basic(&attr_entry, DBUS_TYPE_UINT16,
+						&attr->attrId))
+		return FALSE;
+
+	if (!append_attr_value(attr, &attr_entry))
+		return FALSE;
+
+	if (!dbus_message_iter_close_container(attr_dict, &attr_entry))
+		return FALSE;
+
+	return TRUE;
+}
+
+static dbus_bool_t append_record(const sdp_record_t *rec,
+					DBusMessageIter *attr_array)
+{
+	sdp_list_t *seq;
+	DBusMessageIter attr_dict;
+
+	if (!dbus_message_iter_open_container(attr_array, DBUS_TYPE_ARRAY,
+						"{q(yuv)}", &attr_dict))
+		return FALSE;
+
+	for (seq = rec->attrlist; seq; seq = seq->next) {
+		sdp_data_t *attr = (sdp_data_t *) seq->data;
+		if (!append_attr(attr, &attr_dict))
+			return FALSE;
+	}
+
+	if (!dbus_message_iter_close_container(attr_array, &attr_dict))
+		return FALSE;
+
+	return TRUE;
+}
+
+static DBusMessage *get_service_records(DBusConnection *conn,
+					DBusMessage *msg, void *data)
+{
+	struct btd_device *dev = data;
+	sdp_list_t *seq;
+	DBusMessage *reply;
+	DBusMessageIter rec_array;
+	DBusMessageIter attr_array;
+
+	if (!btd_adapter_get_powered(dev->adapter))
+		return btd_error_not_ready(msg);
+
+	if (!btd_device_is_connected(dev))
+		return btd_error_not_connected(msg);
+
+	reply = dbus_message_new_method_return(msg);
+	if (!reply)
+		return btd_error_failed(msg, "Failed to create method reply");
+
+	/* Load records from storage if there is nothing in cache */
+	if (!dev->tmp_records)
+		return btd_error_failed(msg, "SDP record not found");
+
+	dbus_message_iter_init_append(reply, &rec_array);
+
+	if (!dbus_message_iter_open_container(&rec_array, DBUS_TYPE_ARRAY,
+						"a{q(yuv)}", &attr_array))
+		return FALSE;
+
+	for (seq = dev->tmp_records; seq; seq = seq->next) {
+		sdp_record_t *rec = (sdp_record_t *) seq->data;
+		if (!rec)
+			continue;
+		if (!append_record(rec, &attr_array))
+			return btd_error_failed(msg,
+						"SDP record attachment failed");
+	}
+
+	if (!dbus_message_iter_close_container(&rec_array, &attr_array))
+		return FALSE;
+
+	return reply;
+}
+
 static const GDBusMethodTable device_methods[] = {
 	{ GDBUS_ASYNC_METHOD("Disconnect", NULL, NULL, dev_disconnect) },
 	{ GDBUS_ASYNC_METHOD("Connect", NULL, NULL, dev_connect) },
@@ -2771,6 +3061,9 @@  static const GDBusMethodTable device_methods[] = {
 						NULL, disconnect_profile) },
 	{ GDBUS_ASYNC_METHOD("Pair", NULL, NULL, pair_device) },
 	{ GDBUS_METHOD("CancelPairing", NULL, NULL, cancel_pairing) },
+	{ GDBUS_ASYNC_METHOD("GetServiceRecords", NULL,
+			GDBUS_ARGS({"records", "aa{q(yuv)}"}),
+			get_service_records) },
 	{ }
 };