bluez5: create device handle before profile negotiation started so that profile handler can retrieve per-device settings

This commit is contained in:
Huang-Huang Bao 2021-03-14 14:06:50 +08:00 committed by Wim Taymans
parent e7cecaaea6
commit af8272fe08
5 changed files with 140 additions and 80 deletions

View file

@ -136,6 +136,10 @@ struct spa_bt_a2dp_codec_switch {
size_t num_paths;
};
#define BT_DEVICE_DISCONNECTED 0
#define BT_DEVICE_CONNECTED 1
#define BT_DEVICE_INIT -1
/*
* SCO socket connect may fail with ECONNABORTED if it is done too soon after
* previous close. To avoid this in cases where nodes are toggled between
@ -640,8 +644,6 @@ static struct spa_bt_device *device_create(struct spa_bt_monitor *monitor, const
static int device_stop_timer(struct spa_bt_device *device);
static int device_remove(struct spa_bt_monitor *monitor, struct spa_bt_device *device);
static void a2dp_codec_switch_free(struct spa_bt_a2dp_codec_switch *sw);
static void device_free(struct spa_bt_device *device)
@ -662,7 +664,10 @@ static void device_free(struct spa_bt_device *device)
battery_remove(device);
device_stop_timer(device);
device_remove(monitor, device);
if (device->added) {
spa_device_emit_object_info(&monitor->hooks, device->id, NULL);
}
spa_list_for_each_safe(ep, tep, &device->remote_endpoint_list, device_link) {
if (ep->device == device) {
@ -691,15 +696,38 @@ static void device_free(struct spa_bt_device *device)
free(device);
}
static int device_add(struct spa_bt_monitor *monitor, struct spa_bt_device *device)
static int device_connected(struct spa_bt_monitor *monitor, struct spa_bt_device *device, int status)
{
struct spa_device_object_info info;
char dev[32], name[128], class[16];
struct spa_dict_item items[20];
uint32_t n_items = 0;
bool connection_changed, init = status == BT_DEVICE_INIT;
if (device->connected_profiles == 0 || device->added)
status = init ? false : status;
connection_changed = status ^ device->connected;
device->connected = status;
if (init) {
device->added = true;
} else if (!device->added || !connection_changed) {
return 0;
}
if ((device->connected_profiles != 0) ^ device->connected) {
spa_log_error(monitor->log,
"unexpected call, connected_profiles:%08x connected:%d",
device->connected_profiles, device->connected);
return -EINVAL;
}
if (!init) {
spa_bt_device_emit_connected(device, device->connected);
if (!device->connected) {
battery_remove(device);
spa_bt_device_release_transports(device);
}
}
info = SPA_DEVICE_OBJECT_INFO_INIT();
info.type = SPA_TYPE_INTERFACE_Device;
@ -724,29 +752,20 @@ static int device_add(struct spa_bt_monitor *monitor, struct spa_bt_device *devi
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_DEVICE, dev);
snprintf(class, sizeof(class), "0x%06x", device->bluetooth_class);
items[n_items++] = SPA_DICT_ITEM_INIT(SPA_KEY_API_BLUEZ5_CLASS, class);
items[n_items++] = SPA_DICT_ITEM_INIT(
SPA_KEY_API_BLUEZ5_CONNECTION,
device->connected ? "connected": "disconnected");
info.props = &SPA_DICT_INIT(items, n_items);
device->added = true;
spa_device_emit_object_info(&monitor->hooks, device->id, &info);
return 0;
}
static int device_remove(struct spa_bt_monitor *monitor, struct spa_bt_device *device)
{
if (!device->added)
return 0;
battery_remove(device);
device->added = false;
spa_device_emit_object_info(&monitor->hooks, device->id, NULL);
if(!init && !device->connected) {
spa_device_emit_object_info(&monitor->hooks, device->id, NULL);
}
return 0;
}
#define DEVICE_PROFILE_TIMEOUT_SEC 3
static void device_timer_event(struct spa_source *source)
@ -761,7 +780,7 @@ static void device_timer_event(struct spa_source *source)
spa_log_debug(monitor->log, "device %p: timeout %08x %08x",
device, device->profiles, device->connected_profiles);
device_add(device->monitor, device);
device_connected(device->monitor, device, BT_DEVICE_CONNECTED);
}
static int device_start_timer(struct spa_bt_device *device)
@ -821,13 +840,11 @@ int spa_bt_device_check_profiles(struct spa_bt_device *device, bool force)
device, device->profiles, connected_profiles, device->added);
if (connected_profiles == 0 && spa_list_is_empty(&device->codec_switch_list)) {
if (device->added) {
device_stop_timer(device);
device_remove(monitor, device);
}
device_stop_timer(device);
device_connected(monitor, device, BT_DEVICE_DISCONNECTED);
} else if (force || (device->profiles & connected_profiles) == device->profiles) {
device_stop_timer(device);
device_add(monitor, device);
device_connected(monitor, device, BT_DEVICE_CONNECTED);
} else {
device_start_timer(device);
}
@ -841,14 +858,11 @@ static void device_set_connected(struct spa_bt_device *device, int connected)
if (device->connected && !connected)
device->connected_profiles = 0;
device->connected = connected;
if (connected)
spa_bt_device_check_profiles(device, false);
else {
device_stop_timer(device);
if (device->added)
device_remove(monitor, device);
device_connected(monitor, device, connected);
}
}
@ -1158,6 +1172,8 @@ static int remote_endpoint_update_props(struct spa_bt_remote_endpoint *remote_en
else if (strcmp(key, "Device") == 0) {
struct spa_bt_device *device;
device = spa_bt_device_find(monitor, value);
if (device == NULL)
goto next;
spa_log_debug(monitor->log, "remote_endpoint %p: device -> %p", remote_endpoint, device);
if (remote_endpoint->device != device) {
@ -2737,16 +2753,25 @@ static void interface_added(struct spa_bt_monitor *monitor,
else if (strcmp(interface_name, BLUEZ_DEVICE_INTERFACE) == 0) {
struct spa_bt_device *d;
d = spa_bt_device_find(monitor, object_path);
spa_assert(spa_bt_device_find(monitor, object_path) == NULL);
d = device_create(monitor, object_path);
if (d == NULL) {
d = device_create(monitor, object_path);
if (d == NULL) {
spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m",
object_path);
return;
}
spa_log_warn(monitor->log, "can't create Bluetooth device %s: %m",
object_path);
return;
}
device_update_props(d, props_iter, NULL);
/* We only care about audio devices. */
if (d->profiles == 0) {
device_free(d);
return;
}
/* Trigger bluez device creation before bluez profile negotiation started so that
* profile connection handlers can receive per-device settings during profile negotiation. */
device_connected(monitor, d, BT_DEVICE_INIT);
}
else if (strcmp(interface_name, BLUEZ_MEDIA_ENDPOINT_INTERFACE) == 0) {
struct spa_bt_remote_endpoint *ep;

View file

@ -410,8 +410,20 @@ static void profiles_changed(void *userdata, uint32_t prev_profiles, uint32_t pr
emit_info(this, false);
}
static void set_initial_profile(struct impl *this);
static void device_connected(void *userdata, bool connected) {
struct impl *this = userdata;
spa_log_debug(this->log, "connected: %d", connected);
if (connected ^ (this->profile != 0))
set_initial_profile(this);
}
static const struct spa_bt_device_events bt_dev_events = {
SPA_VERSION_BT_DEVICE_EVENTS,
.connected = device_connected,
.codec_switched = codec_switched,
.profiles_changed = profiles_changed,
};
@ -540,6 +552,11 @@ static void set_initial_profile(struct impl *this)
struct spa_bt_transport *t;
int i;
if (this->supported_codecs)
free(this->supported_codecs);
this->supported_codecs = spa_bt_device_get_supported_a2dp_codecs(
this->bt_dev, &this->supported_codec_count);
/* Prefer A2DP, then HFP, then null */
for (i = SPA_BT_PROFILE_A2DP_SINK; i <= SPA_BT_PROFILE_A2DP_SOURCE; i <<= 1) {
@ -1248,8 +1265,6 @@ impl_init(const struct spa_handle_factory *factory,
this->info.params = this->params;
this->info.n_params = 4;
this->supported_codecs = spa_bt_device_get_supported_a2dp_codecs(this->bt_dev, &this->supported_codec_count);
spa_bt_device_add_listener(this->bt_dev, &this->bt_dev_listener, &bt_dev_events, this);
set_initial_profile(this);

View file

@ -375,6 +375,9 @@ struct spa_bt_device_events {
#define SPA_VERSION_BT_DEVICE_EVENTS 0
uint32_t version;
/** Device connection status */
void (*connected) (void *data, bool connected);
/** Codec switching completed */
void (*codec_switched) (void *data, int status);
@ -430,6 +433,7 @@ int spa_bt_device_report_battery_level(struct spa_bt_device *device, uint8_t per
#define spa_bt_device_emit(d,m,v,...) spa_hook_list_call(&(d)->listener_list, \
struct spa_bt_device_events, \
m, v, ##__VA_ARGS__)
#define spa_bt_device_emit_connected(d,...) spa_bt_device_emit(d, connected, 0, __VA_ARGS__)
#define spa_bt_device_emit_codec_switched(d,...) spa_bt_device_emit(d, codec_switched, 0, __VA_ARGS__)
#define spa_bt_device_emit_profiles_changed(d,...) spa_bt_device_emit(d, profiles_changed, 0, __VA_ARGS__)
#define spa_bt_device_add_listener(d,listener,events,data) \