static gboolean process_auth_queue(gpointer user_data);
-static void dev_info_free(void *data)
-{
- struct remote_dev_info *dev = data;
-
- g_free(dev->name);
- g_free(dev->alias);
- g_slist_free_full(dev->services, g_free);
- g_strfreev(dev->uuids);
- g_free(dev);
-}
-
int btd_adapter_set_class(struct btd_adapter *adapter, uint8_t major,
uint8_t minor)
{
return mode;
}
-static GSList *remove_bredr(GSList *all)
-{
- GSList *l, *le;
-
- for (l = all, le = NULL; l; l = l->next) {
- struct remote_dev_info *dev = l->data;
- if (dev->bdaddr_type == BDADDR_BREDR) {
- dev_info_free(dev);
- continue;
- }
-
- le = g_slist_append(le, dev);
- }
-
- g_slist_free(all);
-
- return le;
-}
-
/* Called when a session gets removed or the adapter is stopped */
static void stop_discovery(struct btd_adapter *adapter)
{
- adapter->found_devices = remove_bredr(adapter->found_devices);
-
if (adapter->oor_devices) {
g_slist_free(adapter->oor_devices);
adapter->oor_devices = NULL;
GList *l;
adapter->devices = g_slist_remove(adapter->devices, device);
+ adapter->found_devices = g_slist_remove(adapter->found_devices,
+ device);
+
adapter->connections = g_slist_remove(adapter->connections, device);
l = adapter->auths->head;
if (adapter->disc_sessions)
goto done;
- g_slist_free_full(adapter->found_devices, dev_info_free);
+ g_slist_free(adapter->found_devices);
adapter->found_devices = NULL;
g_slist_free(adapter->oor_devices);
device = l->data;
- if (device_is_temporary(device) || device_is_busy(device))
- return g_dbus_create_error(msg,
- ERROR_INTERFACE ".DoesNotExist",
- "Device creation in progress");
-
device_set_temporary(device, TRUE);
if (!device_is_connected(device)) {
static void emit_device_disappeared(gpointer data, gpointer user_data)
{
- struct remote_dev_info *dev = data;
+ struct btd_device *dev = data;
struct btd_adapter *adapter = user_data;
char address[18];
const char *paddr = address;
- ba2str(&dev->bdaddr, address);
+ ba2str(device_get_address(dev), address);
g_dbus_emit_signal(btd_get_dbus_connection(), adapter->path,
- ADAPTER_INTERFACE, "DeviceDisappeared",
- DBUS_TYPE_STRING, &paddr,
- DBUS_TYPE_INVALID);
+ ADAPTER_INTERFACE, "DeviceDisappeared",
+ DBUS_TYPE_STRING, &paddr,
+ DBUS_TYPE_INVALID);
- adapter->found_devices = g_slist_remove(adapter->found_devices, dev);
+ if (device_is_temporary(dev))
+ adapter_remove_device(adapter, dev, TRUE);
}
void btd_adapter_get_mode(struct btd_adapter *adapter, uint8_t *mode,
sdp_list_free(adapter->services, NULL);
- g_slist_free_full(adapter->found_devices, dev_info_free);
+ g_slist_free(adapter->found_devices);
g_slist_free(adapter->oor_devices);
gboolean discovering)
{
guint connect_list_len;
+ GSList *l;
adapter->discovering = discovering;
return;
g_slist_foreach(adapter->oor_devices, emit_device_disappeared, adapter);
- g_slist_free_full(adapter->oor_devices, dev_info_free);
- adapter->oor_devices = g_slist_copy(adapter->found_devices);
+ g_slist_free(adapter->oor_devices);
+
+ for (l = adapter->found_devices; l != NULL; l = g_slist_next(l)) {
+ struct btd_device *dev = l->data;
+ device_set_rssi(dev, 0);
+ }
+
+ adapter->oor_devices = adapter->found_devices;
+ adapter->found_devices = NULL;
if (adapter->discov_suspended)
return;
mgmt_stop_discovery(adapter->dev_id);
}
-static int found_device_cmp(gconstpointer a, gconstpointer b)
-{
- const struct remote_dev_info *d = a;
- const bdaddr_t *bdaddr = b;
-
- if (bacmp(bdaddr, BDADDR_ANY) == 0)
- return 0;
-
- return bacmp(&d->bdaddr, bdaddr);
-}
-
-struct remote_dev_info *adapter_search_found_devices(struct btd_adapter *adapter,
- const bdaddr_t *bdaddr)
-{
- GSList *l;
-
- l = g_slist_find_custom(adapter->found_devices, bdaddr,
- found_device_cmp);
- if (l)
- return l->data;
-
- return NULL;
-}
-
-static int dev_rssi_cmp(struct remote_dev_info *d1, struct remote_dev_info *d2)
-{
- int rssi1, rssi2;
-
- rssi1 = d1->rssi < 0 ? -d1->rssi : d1->rssi;
- rssi2 = d2->rssi < 0 ? -d2->rssi : d2->rssi;
-
- return rssi1 - rssi2;
-}
-
-static void append_dict_valist(DBusMessageIter *iter,
- const char *first_key,
- va_list var_args)
-{
- DBusMessageIter dict;
- const char *key;
- int type;
- int n_elements;
- void *val;
-
- dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
- DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
- DBUS_TYPE_STRING_AS_STRING DBUS_TYPE_VARIANT_AS_STRING
- DBUS_DICT_ENTRY_END_CHAR_AS_STRING, &dict);
-
- key = first_key;
- while (key) {
- type = va_arg(var_args, int);
- val = va_arg(var_args, void *);
- if (type == DBUS_TYPE_ARRAY) {
- n_elements = va_arg(var_args, int);
- if (n_elements > 0)
- dict_append_array(&dict, key, DBUS_TYPE_STRING,
- val, n_elements);
- } else
- dict_append_entry(&dict, key, type, val);
- key = va_arg(var_args, char *);
- }
-
- dbus_message_iter_close_container(iter, &dict);
-}
-
-static void emit_device_found(const char *path, const char *address,
- const char *first_key, ...)
-{
- DBusMessage *signal;
- DBusMessageIter iter;
- va_list var_args;
-
- signal = dbus_message_new_signal(path, ADAPTER_INTERFACE,
- "DeviceFound");
- if (!signal) {
- error("Unable to allocate new %s.DeviceFound signal",
- ADAPTER_INTERFACE);
- return;
- }
- dbus_message_iter_init_append(signal, &iter);
- dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING, &address);
-
- va_start(var_args, first_key);
- append_dict_valist(&iter, first_key, var_args);
- va_end(var_args);
-
- g_dbus_send_message(btd_get_dbus_connection(), signal);
-}
-
-static char **strlist2array(GSList *list)
-{
- unsigned int i, n;
- char **array;
-
- if (list == NULL)
- return NULL;
-
- n = g_slist_length(list);
- array = g_new0(char *, n + 1);
-
- for (i = 0; list; list = list->next, i++)
- array[i] = g_strdup((const gchar *) list->data);
-
- return array;
-}
-
-void adapter_emit_device_found(struct btd_adapter *adapter,
- struct remote_dev_info *dev)
-{
- struct btd_device *device;
- char peer_addr[18];
- const char *icon, *paddr = peer_addr;
- dbus_bool_t paired = FALSE, trusted = FALSE;
- dbus_int16_t rssi = dev->rssi;
- dbus_bool_t legacy = dev->legacy;
- char *alias;
- size_t uuid_count;
-
- ba2str(&dev->bdaddr, peer_addr);
-
- device = adapter_find_device(adapter, paddr);
- if (device) {
- paired = device_is_paired(device);
- trusted = device_is_trusted(device);
- }
-
- /* The uuids string array is updated only if necessary */
- uuid_count = g_slist_length(dev->services);
- if (dev->services && dev->uuid_count != uuid_count) {
- g_strfreev(dev->uuids);
- dev->uuids = strlist2array(dev->services);
- dev->uuid_count = uuid_count;
- }
-
- if (!dev->alias) {
- if (!dev->name) {
- alias = g_strdup(peer_addr);
- g_strdelimit(alias, ":", '-');
- } else
- alias = g_strdup(dev->name);
- } else
- alias = g_strdup(dev->alias);
-
- if (dev->bdaddr_type != BDADDR_BREDR) {
- uint16_t app;
-
- /* Avoid emitting DeviceFound() signal if device is not
- * discoverable */
- if (!(dev->flags & (EIR_LIM_DISC | EIR_GEN_DISC))) {
- g_free(alias);
- return;
- }
-
- if (read_remote_appearance(&adapter->bdaddr, &dev->bdaddr,
- dev->bdaddr_type, &app) == 0)
- icon = gap_appearance_to_icon(app);
- else {
- app = 0;
- icon = NULL;
- }
-
- emit_device_found(adapter->path, paddr,
- "Address", DBUS_TYPE_STRING, &paddr,
- "Appearance", DBUS_TYPE_UINT16, &app,
- "Icon", DBUS_TYPE_STRING, &icon,
- "RSSI", DBUS_TYPE_INT16, &rssi,
- "Name", DBUS_TYPE_STRING, &dev->name,
- "Alias", DBUS_TYPE_STRING, &alias,
- "LegacyPairing", DBUS_TYPE_BOOLEAN, &legacy,
- "Paired", DBUS_TYPE_BOOLEAN, &paired,
- "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
- NULL);
- } else {
- icon = class_to_icon(dev->class);
-
- emit_device_found(adapter->path, paddr,
- "Address", DBUS_TYPE_STRING, &paddr,
- "Class", DBUS_TYPE_UINT32, &dev->class,
- "Icon", DBUS_TYPE_STRING, &icon,
- "RSSI", DBUS_TYPE_INT16, &rssi,
- "Name", DBUS_TYPE_STRING, &dev->name,
- "Alias", DBUS_TYPE_STRING, &alias,
- "LegacyPairing", DBUS_TYPE_BOOLEAN, &legacy,
- "Paired", DBUS_TYPE_BOOLEAN, &paired,
- "Trusted", DBUS_TYPE_BOOLEAN, &trusted,
- "UUIDs", DBUS_TYPE_ARRAY, &dev->uuids, uuid_count,
- NULL);
- }
-
- g_free(alias);
-}
-
-static struct remote_dev_info *found_device_new(const bdaddr_t *bdaddr,
- uint8_t bdaddr_type, const char *name,
- const char *alias, uint32_t class,
- bool legacy, int flags)
-{
- struct remote_dev_info *dev;
-
- dev = g_new0(struct remote_dev_info, 1);
- bacpy(&dev->bdaddr, bdaddr);
- dev->bdaddr_type = bdaddr_type;
- dev->name = g_strdup(name);
- dev->alias = g_strdup(alias);
- dev->class = class;
- dev->legacy = legacy;
- if (flags >= 0)
- dev->flags = flags;
-
- return dev;
-}
-
-static void remove_same_uuid(gpointer data, gpointer user_data)
-{
- struct remote_dev_info *dev = user_data;
- GSList *l;
-
- for (l = dev->services; l; l = l->next) {
- char *current_uuid = l->data;
- char *new_uuid = data;
-
- if (strcmp(current_uuid, new_uuid) == 0) {
- g_free(current_uuid);
- dev->services = g_slist_delete_link(dev->services, l);
- break;
- }
- }
-}
-
-static void dev_prepend_uuid(gpointer data, gpointer user_data)
-{
- struct remote_dev_info *dev = user_data;
- char *new_uuid = data;
-
- dev->services = g_slist_prepend(dev->services, g_strdup(new_uuid));
-}
-
-static char *read_stored_data(const bdaddr_t *local, const bdaddr_t *peer,
- uint8_t peer_type, const char *file)
-{
- char local_addr[18], key[20], filename[PATH_MAX + 1], *str;
-
- ba2str(local, local_addr);
-
- create_name(filename, PATH_MAX, STORAGEDIR, local_addr, file);
-
- ba2str(peer, key);
- sprintf(&key[17], "#%hhu", peer_type);
-
- str = textfile_get(filename, key);
- if (str != NULL)
- return str;
-
- /* Try old format (address only) */
- key[17] = '\0';
-
- return textfile_get(filename, key);
-}
-
static gboolean clean_connecting_state(GIOChannel *io, GIOCondition cond,
gpointer user_data)
{
bool confirm_name, bool legacy,
uint8_t *data, uint8_t data_len)
{
- struct remote_dev_info *dev;
+ struct btd_device *dev;
struct eir_data eir_data;
- char *alias, *name;
- gboolean name_known;
+ char addr[18];
int err;
GSList *l;
write_device_name(&adapter->bdaddr, bdaddr, bdaddr_type,
eir_data.name);
- dev = adapter_search_found_devices(adapter, bdaddr);
- if (dev && dev->bdaddr_type == BDADDR_BREDR) {
- adapter->oor_devices = g_slist_remove(adapter->oor_devices,
- dev);
-
- /* If an existing device had no name but the newly received EIR
- * data has (complete or not), we want to present it to the
- * user. */
- if (dev->name == NULL && eir_data.name != NULL) {
- dev->name = g_strdup(eir_data.name);
- goto done;
- }
-
- if (dev->rssi != rssi || dev->legacy != legacy)
- goto done;
-
+ /* Avoid creating LE device if it's not discoverable */
+ if (bdaddr_type != BDADDR_BREDR &&
+ !(eir_data.flags & (EIR_LIM_DISC | EIR_GEN_DISC))) {
eir_data_free(&eir_data);
-
return;
}
- /* New device in the discovery session */
-
- name = read_stored_data(&adapter->bdaddr, bdaddr, bdaddr_type, "names");
+ ba2str(bdaddr, addr);
- if (bdaddr_type == BDADDR_BREDR && !name && main_opts.name_resolv &&
- adapter_has_discov_sessions(adapter))
- name_known = FALSE;
+ l = g_slist_find_custom(adapter->devices, bdaddr,
+ (GCompareFunc) device_bdaddr_cmp);
+ if (l)
+ dev = l->data;
else
- name_known = TRUE;
+ dev = adapter_create_device(adapter, addr, bdaddr_type);
- if (confirm_name)
- mgmt_confirm_name(adapter->dev_id, bdaddr, bdaddr_type,
- name_known);
+ device_set_legacy(dev, legacy);
+ device_set_rssi(dev, rssi);
- alias = read_stored_data(&adapter->bdaddr, bdaddr, bdaddr_type,
- "aliases");
+ if (eir_data.name)
+ device_set_name(dev, eir_data.name);
- dev = found_device_new(bdaddr, bdaddr_type, name, alias,
- eir_data.class, legacy, eir_data.flags);
- free(name);
- free(alias);
+ device_add_eir_uuids(dev, eir_data.services);
- adapter->found_devices = g_slist_prepend(adapter->found_devices, dev);
+ eir_data_free(&eir_data);
- if (bdaddr_type == BDADDR_LE_PUBLIC ||
- bdaddr_type == BDADDR_LE_RANDOM) {
- struct btd_device *device;
+ if (device_is_bredr(dev))
+ adapter->oor_devices = g_slist_remove(adapter->oor_devices,
+ dev);
- l = g_slist_find_custom(adapter->connect_list, bdaddr,
- (GCompareFunc) device_bdaddr_cmp);
- if (!l)
- goto done;
+ if (g_slist_find(adapter->found_devices, dev))
+ return;
- device = l->data;
- adapter_connect_list_remove(adapter, device);
+ if (confirm_name)
+ mgmt_confirm_name(adapter->dev_id, bdaddr, bdaddr_type,
+ device_name_known(dev));
- g_idle_add(connect_pending_cb, btd_device_ref(device));
+ adapter->found_devices = g_slist_prepend(adapter->found_devices, dev);
+
+ if (device_is_le(dev) && g_slist_find(adapter->connect_list, dev)) {
+ adapter_connect_list_remove(adapter, dev);
+ g_idle_add(connect_pending_cb, btd_device_ref(dev));
stop_discovery(adapter);
adapter->waiting_to_connect++;
}
-
-done:
- dev->rssi = rssi;
- dev->legacy = legacy;
-
- adapter->found_devices = g_slist_sort(adapter->found_devices,
- (GCompareFunc) dev_rssi_cmp);
-
- g_slist_foreach(eir_data.services, remove_same_uuid, dev);
- g_slist_foreach(eir_data.services, dev_prepend_uuid, dev);
-
- adapter_emit_device_found(adapter, dev);
-
- eir_data_free(&eir_data);
}
void adapter_mode_changed(struct btd_adapter *adapter, uint8_t scan_mode)
bdaddr_t bdaddr;
uint8_t bdaddr_type;
gchar *path;
+ bool svc_resolved;
+ GSList *eir_uuids;
char name[MAX_NAME_LENGTH + 1];
char *alias;
uint16_t vendor_src;
return TRUE;
}
+static gboolean dev_property_exists_name(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct btd_device *dev = data;
+
+ return device_name_known(dev);
+}
+
static gboolean dev_property_get_alias(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY,
DBUS_TYPE_STRING_AS_STRING, &entry);
- for (l = device->uuids; l != NULL; l = l->next)
+ if (!device->svc_resolved)
+ l = device->eir_uuids;
+ else
+ l = device->uuids;
+
+ for (; l != NULL; l = l->next)
dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
&l->data);
return TRUE;
}
+static gboolean dev_property_exists_uuids(const GDBusPropertyTable *property,
+ void *data)
+{
+ struct btd_device *dev = data;
+
+ if (dev->svc_resolved)
+ return dev->uuids ? TRUE : FALSE;
+ else
+ return dev->eir_uuids ? TRUE : FALSE;
+}
+
static gboolean dev_property_get_services(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
{
return TRUE;
}
+static gboolean dev_property_exists_services(const GDBusPropertyTable *prop,
+ void *data)
+{
+ struct btd_device *dev = data;
+
+ return dev->services ? TRUE : FALSE;
+}
static gboolean dev_property_get_adapter(const GDBusPropertyTable *property,
DBusMessageIter *iter, void *data)
dev->connect = NULL;
}
+void device_add_eir_uuids(struct btd_device *dev, GSList *uuids)
+{
+ GSList *l;
+ bool added = false;
+
+ if (dev->svc_resolved)
+ return;
+
+ for (l = uuids; l != NULL; l = l->next) {
+ const char *str = l->data;
+ if (g_slist_find_custom(dev->eir_uuids, str, bt_uuid_strcmp))
+ continue;
+ added = true;
+ dev->eir_uuids = g_slist_append(dev->eir_uuids, g_strdup(str));
+ }
+
+ if (added)
+ g_dbus_emit_property_changed(btd_get_dbus_connection(),
+ dev->path, DEVICE_INTERFACE,
+ "UUIDs");
+}
+
+static int device_resolve_svc(struct btd_device *dev, DBusMessage *msg)
+{
+ if (device_is_bredr(dev))
+ return device_browse_sdp(dev, msg, NULL, FALSE);
+ else
+ return device_browse_primary(dev, msg, FALSE);
+}
+
static DBusMessage *dev_connect(DBusConnection *conn, DBusMessage *msg,
void *user_data)
{
if (dev->profiles_connected)
return btd_error_already_connected(msg);
- if (dev->pending || dev->connect)
+ if (dev->pending || dev->connect || dev->browse)
return btd_error_in_progress(msg);
+ if (!dev->svc_resolved) {
+ err = device_resolve_svc(dev, msg);
+ if (err < 0)
+ return btd_error_failed(msg, strerror(-err));
+ return NULL;
+ }
+
for (l = dev->profiles; l != NULL; l = g_slist_next(l)) {
p = l->data;
return NULL;
}
+static void device_svc_resolved(struct btd_device *dev, int err)
+{
+ DBusMessage *reply;
+ DBusConnection *conn = btd_get_dbus_connection();
+ struct browse_req *req = dev->browse;
+
+ dev->svc_resolved = true;
+ dev->browse = NULL;
+
+ g_slist_free_full(dev->eir_uuids, g_free);
+ dev->eir_uuids = NULL;
+
+ if (!req || !req->msg)
+ return;
+
+ if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+ "DiscoverServices")) {
+ discover_services_reply(req, err, dev->tmp_records);
+ } else if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+ "Pair")) {
+ reply = dbus_message_new_method_return(req->msg);
+ g_dbus_send_message(conn, reply);
+ } else if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
+ "Connect")) {
+ if (err) {
+ reply = btd_error_failed(req->msg, strerror(-err));
+ g_dbus_send_message(conn, reply);
+ return;
+ }
+
+ reply = dev_connect(conn, req->msg, dev);
+ if (reply)
+ g_dbus_send_message(conn, reply);
+ else
+ req->msg = NULL;
+ }
+}
+
static uint8_t parse_io_capability(const char *capability)
{
if (g_str_equal(capability, ""))
static const GDBusPropertyTable device_properties[] = {
{ "Address", "s", dev_property_get_address },
- { "Name", "s", dev_property_get_name },
+ { "Name", "s", dev_property_get_name, NULL, dev_property_exists_name },
{ "Alias", "s", dev_property_get_alias, dev_property_set_alias },
{ "Class", "u", dev_property_get_class, NULL,
dev_property_exists_class },
{ "LegacyPairing", "b", dev_property_get_legacy },
{ "RSSI", "n", dev_property_get_rssi, NULL, dev_property_exists_rssi },
{ "Connected", "b", dev_property_get_connected },
- { "UUIDs", "as", dev_property_get_uuids },
- { "Services", "ao", dev_property_get_services },
+ { "UUIDs", "as", dev_property_get_uuids, NULL,
+ dev_property_exists_uuids },
+ { "Services", "ao", dev_property_get_services, NULL,
+ dev_property_exists_services },
{ "Adapter", "o", dev_property_get_adapter },
{ }
};
if (strncmp(name, device->name, MAX_NAME_LENGTH) == 0)
return;
+ DBG("%s %s", device->path, name);
+
strncpy(device->name, name, MAX_NAME_LENGTH);
g_dbus_emit_property_changed(btd_get_dbus_connection(), device->path,
g_free(str);
}
-static void create_device_reply(struct btd_device *device, struct browse_req *req)
-{
- DBusMessage *reply;
-
- reply = dbus_message_new_method_return(req->msg);
- if (!reply)
- return;
-
- dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &device->path,
- DBUS_TYPE_INVALID);
-
- g_dbus_send_message(btd_get_dbus_connection(), reply);
-}
-
GSList *device_services_from_record(struct btd_device *device, GSList *profiles)
{
GSList *l, *prim_list = NULL;
uuids_changed(req->device);
send_reply:
- if (!req->msg)
- goto cleanup;
-
- if (dbus_message_is_method_call(req->msg, DEVICE_INTERFACE,
- "DiscoverServices"))
- discover_services_reply(req, err, device->tmp_records);
- else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
- "CreatePairedDevice"))
- create_device_reply(device, req);
- else if (dbus_message_is_method_call(req->msg, ADAPTER_INTERFACE,
- "CreateDevice")) {
- if (err < 0) {
- DBusMessage *reply;
- reply = btd_error_failed(req->msg, strerror(-err));
- g_dbus_send_message(btd_get_dbus_connection(), reply);
- goto cleanup;
- }
-
- create_device_reply(device, req);
- device_set_temporary(device, FALSE);
- }
+ device_svc_resolved(device, err);
-cleanup:
if (!device->temporary)
store_profiles(device);
- device->browse = NULL;
browse_request_free(req);
}
attio_cleanup(device);
uuids_changed(device);
- if (req->msg)
- create_device_reply(device, req);
+
+ device_svc_resolved(device, 0);
store_services(device);
- device->browse = NULL;
browse_request_free(req);
}