@@ -48,6 +48,7 @@
#include "bluetooth/sdp_lib.h"
#include "lib/uuid.h"
#include "lib/mgmt.h"
+#include "lib/sdp.h"
#include "gdbus/gdbus.h"
@@ -3424,6 +3425,415 @@ static DBusMessage *connect_device(DBusConnection *conn,
return NULL;
}
+static int parse_int_uint_variant(DBusMessageIter *iter, bool unsign,
+ uint32_t size, void **val, uint8_t *dtd)
+{
+ int dbus_type = DBUS_TYPE_INVALID;
+ char arg[4];
+
+ switch(size) {
+ case 1:
+ if (unsign) {
+ *val = malloc(sizeof(uint8_t));
+ *dtd = SDP_UINT8;
+ } else {
+ *val = malloc(sizeof(int8_t));
+ *dtd = SDP_INT8;
+ }
+ dbus_type = DBUS_TYPE_BYTE;
+ break;
+ case 2:
+ if (unsign) {
+ *val = malloc(sizeof(uint16_t));
+ *dtd = SDP_UINT16;
+ dbus_type = DBUS_TYPE_UINT16;
+ } else {
+ *val = malloc(sizeof(int16_t));
+ *dtd = SDP_INT16;
+ dbus_type = DBUS_TYPE_INT16;
+ }
+ break;
+ case 4:
+ if (unsign) {
+ *val = malloc(sizeof(uint32_t));
+ *dtd = SDP_UINT32;
+ dbus_type = DBUS_TYPE_UINT32;
+ } else {
+ *val = malloc(sizeof(int32_t));
+ *dtd = SDP_INT32;
+ dbus_type = DBUS_TYPE_INT32;
+ }
+ break;
+ case 8:
+ if (unsign) {
+ *val = malloc(sizeof(uint64_t));
+ *dtd = SDP_UINT64;
+ dbus_type = DBUS_TYPE_UINT64;
+ } else {
+ *val = malloc(sizeof(int64_t));
+ *dtd = SDP_INT64;
+ dbus_type = DBUS_TYPE_INT64;
+ }
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ if (!*val)
+ return -1;
+
+ if (dbus_message_iter_get_arg_type(iter) != dbus_type) {
+ free(*val);
+ return -EINVAL;
+ }
+ dbus_message_iter_get_basic(iter, arg);
+
+ memcpy(*val, arg, size);
+
+ return 0;
+}
+
+static int parse_str_variant(DBusMessageIter *iter, void **val, int *str_len)
+{
+ const char *str;
+
+ if (dbus_message_iter_get_arg_type(iter) != DBUS_TYPE_STRING)
+ return -EINVAL;
+ dbus_message_iter_get_basic(iter, &str);
+
+ if (!str)
+ return -1;
+
+ *str_len = strlen(str) + 1;
+
+ *val = calloc(*str_len, sizeof(char));
+ if (!val)
+ return -1;
+
+ strncpy(*val, str, *str_len - 1);
+
+ return 0;
+}
+
+static int parse_attr_value(DBusMessageIter *val_struct,
+ sdp_data_t **attr, sdp_record_t* rec)
+{
+ DBusMessageIter iter, val_variant;
+ uint8_t val_type = SDP_VAL_TYPE_NIL;
+ uint32_t val_size = 0;
+
+ uint8_t dtd;
+ void *val = NULL;
+ int str_len = 0;
+ int ret = 0;
+
+ *attr = NULL;
+
+ if (dbus_message_iter_get_arg_type(val_struct) != DBUS_TYPE_STRUCT)
+ return -EINVAL;
+ dbus_message_iter_recurse(val_struct, &iter);
+
+ // Extract attribute value type.
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_BYTE)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&iter, &val_type);
+ if (!dbus_message_iter_next(&iter))
+ return -ENODATA;
+
+ // Extract attribute value size.
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_UINT32)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&iter, &val_size);
+ if (!dbus_message_iter_next(&iter))
+ return -ENODATA;
+
+ // Extract attribute value based on its type.
+ if (dbus_message_iter_get_arg_type(&iter) != DBUS_TYPE_VARIANT)
+ return -EINVAL;
+ dbus_message_iter_recurse(&iter, &val_variant);
+
+ switch(val_type) {
+ case SDP_VAL_TYPE_NIL:
+ dtd = SDP_DATA_NIL;
+ break;
+ case SDP_VAL_TYPE_UINT: {
+ ret = parse_int_uint_variant(&val_variant, true, val_size, &val,
+ &dtd);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ case SDP_VAL_TYPE_INT: {
+ ret = parse_int_uint_variant(&val_variant, false, val_size, &val,
+ &dtd);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ case SDP_VAL_TYPE_UUID: {
+ // The UUID string is in "%08x-%04x-%04x-%04x-%08x%04x" format.
+ // Take Bluetooth base UUID for instance, the base UUID string
+ // is "00000000-0000-1000-8000-00805F9B34FB". Extra action may
+ // be needed to change the UUID type of a newly-created |uuid|
+ // based on the |val_size| field.
+ uuid_t *uuid = NULL;
+ const char *uuid_str;
+ void *arg = NULL;
+
+ if (dbus_message_iter_get_arg_type(&val_variant) !=
+ DBUS_TYPE_STRING)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&val_variant, &uuid_str);
+
+ if (!uuid_str)
+ return -1;
+
+ uuid = (uuid_t *)malloc(sizeof(uuid_t));
+ if (!uuid)
+ return -1;
+
+ if (bt_string2uuid(uuid, uuid_str) < 0) {
+ free(uuid);
+ return -1;
+ }
+
+ switch(val_size) {
+ case 2:
+ dtd = SDP_UUID16;
+ if (!sdp_uuid128_to_uuid(uuid)) {
+ free(uuid);
+ return -1;
+ }
+ arg = &uuid->value.uuid16;
+ break;
+ case 4:
+ dtd = SDP_UUID32;
+ if (!sdp_uuid128_to_uuid(uuid)) {
+ free(uuid);
+ return -1;
+ }
+ arg = &uuid->value.uuid32;
+ break;
+ case 16:
+ dtd = SDP_UUID128;
+ arg = &uuid->value.uuid128;
+ break;
+ default:
+ free(uuid);
+ return -EINVAL;
+ }
+
+ val = malloc(val_size);
+ memcpy(val, arg, val_size);
+
+ // Insert the UUID into |rec|'s search pattern. The ownership
+ // of |uuid| will NOT be transferred, so |uuid| should be freed
+ // after use.
+ sdp_pattern_add_uuid(rec, uuid);
+
+ free(uuid);
+ break;
+ }
+ case SDP_VAL_TYPE_STRING: {
+ dtd = SDP_TEXT_STR8;
+ ret = parse_str_variant(&val_variant, &val, &str_len);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ case SDP_VAL_TYPE_BOOL: {
+ dbus_bool_t *arg = NULL;
+
+ dtd = SDP_BOOL;
+
+ if (dbus_message_iter_get_arg_type(&val_variant) !=
+ DBUS_TYPE_BOOLEAN)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&val_variant, arg);
+
+ if (!arg)
+ return -1;
+
+ val = malloc(sizeof(bool));
+ if (!val)
+ return -1;
+
+ memcpy(val, arg, sizeof(bool));
+ break;
+ }
+ case SDP_VAL_TYPE_URL: {
+ dtd = SDP_URL_STR8;
+ ret = parse_str_variant(&val_variant, &val, &str_len);
+ if (ret < 0)
+ return ret;
+ break;
+ }
+ case SDP_VAL_TYPE_SEQUENCE: {
+ sdp_data_t *seq = NULL;
+ DBusMessageIter seq_iter;
+
+ dtd = SDP_SEQ32;
+
+ if (dbus_message_iter_get_arg_type(&val_variant) !=
+ DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(&val_variant, &seq_iter);
+
+ // Recursively extract the value from each element of the
+ // sequence.
+ while (dbus_message_iter_get_arg_type(&seq_iter) ==
+ DBUS_TYPE_STRUCT) {
+ sdp_data_t *data;
+
+ ret = parse_attr_value(&seq_iter, &data, rec);
+ if (!data || ret < 0) {
+ if (data)
+ sdp_data_free(data);
+ if (seq)
+ sdp_data_free(seq);
+
+ return ret;
+ }
+ seq = sdp_seq_append(seq, data);
+
+ dbus_message_iter_next(&seq_iter);
+ }
+
+ val = seq;
+ break;
+ }
+ default:
+ return -EINVAL;
+ }
+
+ *attr = sdp_data_alloc_with_length(dtd, val, str_len);
+ if (!*attr) {
+ ret = -1;
+ goto error_alloc_attr;
+ }
+
+ return 0;
+
+error_alloc_attr:
+ if (!val)
+ return ret;
+
+ if (val_type == SDP_VAL_TYPE_SEQUENCE)
+ sdp_data_free((sdp_data_t *)val);
+ else
+ free(val);
+
+ return ret;
+}
+
+static int parse_record(DBusMessageIter *rec_array,
+ sdp_record_t *rec)
+{
+ DBusMessageIter attr_dict;
+ sdp_data_t *attr = NULL;
+ int ret = 0;
+
+ if (dbus_message_iter_get_arg_type(rec_array) != DBUS_TYPE_ARRAY)
+ return -EINVAL;
+
+ dbus_message_iter_recurse(rec_array, &attr_dict);
+
+ while(dbus_message_iter_get_arg_type(&attr_dict) ==
+ DBUS_TYPE_DICT_ENTRY) {
+ DBusMessageIter entry;
+ uint16_t attr_id;
+
+ dbus_message_iter_recurse(&attr_dict, &entry);
+
+ // Extract attribute ID.
+ if (dbus_message_iter_get_arg_type(&entry) != DBUS_TYPE_UINT16)
+ return -EINVAL;
+ dbus_message_iter_get_basic(&entry, &attr_id);
+ if (!dbus_message_iter_next(&entry))
+ return -ENODATA;
+
+ // Extract attribute value structure.
+ ret = parse_attr_value(&entry, &attr, rec);
+ if (ret < 0)
+ goto error_parse_attr;
+
+ // Add the new attribute to the record.
+ if (sdp_attr_add(rec, attr_id, attr) < 0) {
+ ret = -1;
+ goto error_parse_attr;
+ }
+
+ dbus_message_iter_next(&attr_dict);
+ }
+
+ return 0;
+
+error_parse_attr:
+ if (attr)
+ sdp_data_free(attr);
+
+ // Release the UUID pattern list.
+ sdp_list_free(rec->pattern, free);
+
+ return ret;
+}
+
+static DBusMessage *create_service_record(DBusConnection *conn,
+ DBusMessage *msg, void *user_data)
+{
+ struct btd_adapter *adapter = user_data;
+ DBusMessage *reply;
+ sdp_record_t *rec;
+ DBusMessageIter rec_array, iter;
+ int ret = 0;
+
+ if (!(adapter->current_settings & MGMT_SETTING_POWERED))
+ return btd_error_not_ready(msg);
+
+ if(!dbus_message_iter_init(msg, &rec_array))
+ return btd_error_failed(msg, "Failed to read record argument");
+
+ reply = dbus_message_new_method_return(msg);
+ if (!reply)
+ return btd_error_failed(msg, "Failed to create method reply");
+
+ rec = sdp_record_alloc();
+ if (!rec)
+ goto failed_to_alloc;
+
+ ret = parse_record(&rec_array, rec);
+ if (ret < 0) {
+ sdp_record_free(rec);
+ if (ret == -EINVAL || ret == -ENODATA)
+ return btd_error_invalid_args(msg);
+ else
+ return btd_error_failed(msg,
+ "Failed to parse record argument");
+ }
+
+ ret = adapter_service_add(adapter, rec);
+ if (ret < 0) {
+ sdp_record_free(rec);
+ if (ret == -EEXIST)
+ return btd_error_already_exists(msg);
+ else
+ return btd_error_failed(msg, "Failed to add record");
+ }
+
+ dbus_message_iter_init_append(reply, &iter);
+ if (!dbus_message_iter_append_basic(
+ &iter, DBUS_TYPE_UINT32, &rec->handle)) {
+ sdp_record_free(rec);
+ return btd_error_failed(msg, "Failed to append record handle");
+ }
+
+ return reply;
+
+failed_to_alloc:
+ return btd_error_failed(msg, "Failed to allocate SDP record");
+}
+
static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_ASYNC_METHOD("StartDiscovery", NULL, NULL, start_discovery) },
{ GDBUS_METHOD("SetDiscoveryFilter",
@@ -3438,6 +3848,10 @@ static const GDBusMethodTable adapter_methods[] = {
{ GDBUS_EXPERIMENTAL_ASYNC_METHOD("ConnectDevice",
GDBUS_ARGS({ "properties", "a{sv}" }), NULL,
connect_device) },
+ { GDBUS_METHOD("CreateServiceRecord",
+ GDBUS_ARGS({"record", "a{q(yuv)}"}),
+ GDBUS_ARGS({"handle", "u"}),
+ create_service_record)},
{ }
};
From: Miao Chou <mcchou@chromium.org> This implements the CreateServiceRecord method of org.bluez.Adapter1 interface. --- src/adapter.c | 414 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 414 insertions(+)