mirror of
				https://gitlab.freedesktop.org/pipewire/pipewire.git
				synced 2025-11-03 09:01:54 -05:00 
			
		
		
		
	bluez5-dbus: move battery provider functions, fix ghost batteries
This commit is contained in:
		
							parent
							
								
									48cc5915fb
								
							
						
					
					
						commit
						5c9028a94d
					
				
					 3 changed files with 286 additions and 275 deletions
				
			
		| 
						 | 
					@ -47,16 +47,6 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles"
 | 
					#define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
 | 
					 | 
				
			||||||
#define DBUS_SIGNAL_INTERFACES_ADDED "InterfacesAdded"
 | 
					 | 
				
			||||||
#define DBUS_SIGNAL_INTERFACES_REMOVED "InterfacesRemoved"
 | 
					 | 
				
			||||||
#define DBUS_SIGNAL_PROPERTIES_CHANGED "PropertiesChanged"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define BLUEZ_INTERFACE_BATTERY_PROVIDER "org.bluez.BatteryProvider1"
 | 
					 | 
				
			||||||
#define BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER "org.bluez.BatteryProviderManager1"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#define PIPEWIRE_BATTERY_PROVIDER "/org/freedesktop/pipewire/battery"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct spa_bt_backend {
 | 
					struct spa_bt_backend {
 | 
				
			||||||
	struct spa_bt_monitor *monitor;
 | 
						struct spa_bt_monitor *monitor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -72,8 +62,6 @@ struct spa_bt_backend {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	struct spa_list rfcomm_list;
 | 
						struct spa_list rfcomm_list;
 | 
				
			||||||
	unsigned int msbc_support_enabled_in_config:1;
 | 
						unsigned int msbc_support_enabled_in_config:1;
 | 
				
			||||||
	unsigned int has_battery_provider;
 | 
					 | 
				
			||||||
	unsigned int battery_provider_unavailable;
 | 
					 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct transport_data {
 | 
					struct transport_data {
 | 
				
			||||||
| 
						 | 
					@ -166,205 +154,6 @@ finish:
 | 
				
			||||||
	return t;
 | 
						return t;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Working with BlueZ Battery Provider. Developed using https://github.com/dgreid/adhd/commit/655b58f as example of DBus calls.
 | 
					 | 
				
			||||||
static char *battery_name(struct spa_bt_device *d)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	char *path = malloc(strlen(PIPEWIRE_BATTERY_PROVIDER) + strlen(d->path) + 1);
 | 
					 | 
				
			||||||
	sprintf(path, PIPEWIRE_BATTERY_PROVIDER"%s", d->path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	// /org/freedesktop/pipewire/battery/org/bluez/hci0/dev_XX_XX_XX_XX_XX_XX
 | 
					 | 
				
			||||||
	return path;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void write_battery(DBusMessageIter *iter, struct spa_bt_device *device)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	DBusMessageIter dict, entry, variant;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
 | 
					 | 
				
			||||||
					 &entry);
 | 
					 | 
				
			||||||
	const char *prop_percentage = "Percentage";
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &prop_percentage);
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
 | 
					 | 
				
			||||||
					 DBUS_TYPE_BYTE_AS_STRING, &variant);
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &device->battery);
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&entry, &variant);
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&dict, &entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
 | 
					 | 
				
			||||||
	const char *prop_device = "Device";
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &prop_device);
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
 | 
					 | 
				
			||||||
					 DBUS_TYPE_OBJECT_PATH_AS_STRING,
 | 
					 | 
				
			||||||
					 &variant);
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&variant, DBUS_TYPE_OBJECT_PATH, &device->path);
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&entry, &variant);
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&dict, &entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(iter, &dict);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void update_battery(struct spa_bt_device *device)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	const char *name = battery_name(device);
 | 
					 | 
				
			||||||
	spa_log_debug(device->monitor->log, NAME": updating battery: %s", name);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DBusMessage *msg;
 | 
					 | 
				
			||||||
	DBusMessageIter iter;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	msg = dbus_message_new_signal(name,
 | 
					 | 
				
			||||||
				      DBUS_INTERFACE_PROPERTIES,
 | 
					 | 
				
			||||||
				      DBUS_SIGNAL_PROPERTIES_CHANGED);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_init_append(msg, &iter);
 | 
					 | 
				
			||||||
	const char *interface = BLUEZ_INTERFACE_BATTERY_PROVIDER;
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
 | 
					 | 
				
			||||||
				       &interface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	write_battery(&iter, device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!dbus_connection_send(device->monitor->conn, msg, NULL))
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, NAME": Error updating battery");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_unref(msg);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void create_battery(struct spa_bt_device *device) {
 | 
					 | 
				
			||||||
	DBusMessage *msg;
 | 
					 | 
				
			||||||
	DBusMessageIter iter, entry, dict;
 | 
					 | 
				
			||||||
	msg = dbus_message_new_signal(PIPEWIRE_BATTERY_PROVIDER,
 | 
					 | 
				
			||||||
				      DBUS_INTERFACE_OBJECT_MANAGER,
 | 
					 | 
				
			||||||
				      DBUS_SIGNAL_INTERFACES_ADDED);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_init_append(msg, &iter);
 | 
					 | 
				
			||||||
	const char *bat_name = battery_name(device);
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 | 
					 | 
				
			||||||
				       &bat_name);
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sa{sv}}", &dict);
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
 | 
					 | 
				
			||||||
	const char *interface = BLUEZ_INTERFACE_BATTERY_PROVIDER;
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
 | 
					 | 
				
			||||||
				       &interface);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	write_battery(&entry, device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&dict, &entry);
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&iter, &dict);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!dbus_connection_send(device->monitor->conn, msg, NULL)) {
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->backend_native->log, NAME": Failed to create virtual battery for %s", device->address);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_unref(msg);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spa_log_debug(device->monitor->backend_native->log, NAME": Created virtual battery for %s", device->address);
 | 
					 | 
				
			||||||
	device->has_battery = true;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void remove_battery(struct spa_bt_device *d) {
 | 
					 | 
				
			||||||
	if (!d->monitor->backend_native->has_battery_provider) return;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spa_log_debug(d->monitor->backend_native->log, NAME": Removing battery provider: %s", battery_name(d));
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DBusMessage *m = dbus_message_new_signal(PIPEWIRE_BATTERY_PROVIDER,
 | 
					 | 
				
			||||||
				      DBUS_INTERFACE_OBJECT_MANAGER,
 | 
					 | 
				
			||||||
				      DBUS_SIGNAL_INTERFACES_REMOVED);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	DBusMessageIter i, entry;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_init_append(m, &i);
 | 
					 | 
				
			||||||
	const char *bat_name = battery_name(d);
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH,
 | 
					 | 
				
			||||||
				       &bat_name);
 | 
					 | 
				
			||||||
	dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
 | 
					 | 
				
			||||||
					 DBUS_TYPE_STRING_AS_STRING, &entry);
 | 
					 | 
				
			||||||
	const char *interface = BLUEZ_INTERFACE_BATTERY_PROVIDER;
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
 | 
					 | 
				
			||||||
				       &interface);
 | 
					 | 
				
			||||||
	dbus_message_iter_close_container(&i, &entry);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!dbus_connection_send(d->monitor->conn, m, NULL)) {
 | 
					 | 
				
			||||||
		spa_log_error(d->monitor->backend_native->log, NAME": sending " DBUS_SIGNAL_INTERFACES_REMOVED " failed");
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_unref(m);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	d->has_battery = false;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void on_battery_provider_registered(DBusPendingCall *pending_call,
 | 
					 | 
				
			||||||
				       void *data)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	DBusMessage *reply;
 | 
					 | 
				
			||||||
	struct spa_bt_device *device = data;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	reply = dbus_pending_call_steal_reply(pending_call);
 | 
					 | 
				
			||||||
	dbus_pending_call_unref(pending_call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, NAME": Failed to register battery provider. Error: %s", dbus_message_get_error_name(reply));
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, NAME": BlueZ battery provider is not available, won't retry to register it");
 | 
					 | 
				
			||||||
		device->monitor->backend_native->battery_provider_unavailable = true;
 | 
					 | 
				
			||||||
		dbus_message_unref(reply);
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spa_log_debug(device->monitor->log, NAME": Registered battery provider");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	device->monitor->backend_native->has_battery_provider = true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!device->has_battery)
 | 
					 | 
				
			||||||
		create_battery(device);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_unref(reply);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void register_battery_provider(DBusConnection *conn, struct spa_bt_device *device)
 | 
					 | 
				
			||||||
{
 | 
					 | 
				
			||||||
	DBusMessage *method_call;
 | 
					 | 
				
			||||||
	DBusMessageIter message_iter;
 | 
					 | 
				
			||||||
	DBusPendingCall *pending_call;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	method_call = dbus_message_new_method_call(
 | 
					 | 
				
			||||||
		BLUEZ_SERVICE, device->adapter_path,
 | 
					 | 
				
			||||||
		BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER,
 | 
					 | 
				
			||||||
		"RegisterBatteryProvider");
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!method_call) {
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, NAME": Failed to register battery provider");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_iter_init_append(method_call, &message_iter);
 | 
					 | 
				
			||||||
	const char *object_path = PIPEWIRE_BATTERY_PROVIDER;
 | 
					 | 
				
			||||||
	dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
 | 
					 | 
				
			||||||
				       &object_path);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!dbus_connection_send_with_reply(conn, method_call, &pending_call,
 | 
					 | 
				
			||||||
					     DBUS_TIMEOUT_USE_DEFAULT)) {
 | 
					 | 
				
			||||||
		dbus_message_unref(method_call);
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, NAME": Failed to register battery provider");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	dbus_message_unref(method_call);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!pending_call) {
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, NAME": Failed to register battery provider");
 | 
					 | 
				
			||||||
		return;
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if (!dbus_pending_call_set_notify(
 | 
					 | 
				
			||||||
		    pending_call, on_battery_provider_registered,
 | 
					 | 
				
			||||||
		    device, NULL)) {
 | 
					 | 
				
			||||||
		spa_log_error(device->monitor->log, "Failed to register battery provider");
 | 
					 | 
				
			||||||
		dbus_pending_call_cancel(pending_call);
 | 
					 | 
				
			||||||
		dbus_pending_call_unref(pending_call);
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
static void rfcomm_free(struct rfcomm *rfcomm)
 | 
					static void rfcomm_free(struct rfcomm *rfcomm)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	spa_list_remove(&rfcomm->link);
 | 
						spa_list_remove(&rfcomm->link);
 | 
				
			||||||
| 
						 | 
					@ -692,25 +481,7 @@ static bool rfcomm_hfp_ag(struct spa_source *source, char* buf)
 | 
				
			||||||
				spa_log_debug(backend->log, NAME": battery level: %d%%", level);
 | 
									spa_log_debug(backend->log, NAME": battery level: %d%%", level);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
				// TODO: report without Battery Provider (using props)
 | 
									// TODO: report without Battery Provider (using props)
 | 
				
			||||||
 | 
									spa_bt_device_report_battery_level(rfcomm->device, level);
 | 
				
			||||||
				// BlueZ likely is running without battery provider support, don't try to report battery
 | 
					 | 
				
			||||||
				if (backend->battery_provider_unavailable) return true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				// If everything is initialized and battery level has not changed we don't need to send anything to BlueZ
 | 
					 | 
				
			||||||
				if (backend->has_battery_provider && rfcomm->device->has_battery && rfcomm->device->battery == level) return true;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				rfcomm->device->battery = level;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
				if (!backend->has_battery_provider) {
 | 
					 | 
				
			||||||
					// No provider: register it, create battery in hook
 | 
					 | 
				
			||||||
					register_battery_provider(backend->conn, rfcomm->device);
 | 
					 | 
				
			||||||
				} else if (!rfcomm->device->has_battery) {
 | 
					 | 
				
			||||||
					// Have provider but no battery: create battery, it will already contain percentage
 | 
					 | 
				
			||||||
					create_battery(rfcomm->device);
 | 
					 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					// Just update existing battery percentage
 | 
					 | 
				
			||||||
					update_battery(rfcomm->device);
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else if (strncmp(buf, "AT+APLSIRI?", 11) == 0) {
 | 
						} else if (strncmp(buf, "AT+APLSIRI?", 11) == 0) {
 | 
				
			||||||
| 
						 | 
					@ -1373,8 +1144,6 @@ static DBusHandlerResult profile_request_disconnection(DBusConnection *conn, DBu
 | 
				
			||||||
		return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
							return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	remove_battery(d);
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	spa_list_for_each_safe(rfcomm, rfcomm_tmp, &backend->rfcomm_list, link) {
 | 
						spa_list_for_each_safe(rfcomm, rfcomm_tmp, &backend->rfcomm_list, link) {
 | 
				
			||||||
		if (rfcomm->device == d && rfcomm->profile == profile) {
 | 
							if (rfcomm->device == d && rfcomm->profile == profile) {
 | 
				
			||||||
			if (rfcomm->source.loop)
 | 
								if (rfcomm->source.loop)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -53,6 +53,46 @@
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define NAME "bluez5-monitor"
 | 
					#define NAME "bluez5-monitor"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					struct spa_bt_monitor {
 | 
				
			||||||
 | 
						struct spa_handle handle;
 | 
				
			||||||
 | 
						struct spa_device device;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_log *log;
 | 
				
			||||||
 | 
						struct spa_loop *main_loop;
 | 
				
			||||||
 | 
						struct spa_system *main_system;
 | 
				
			||||||
 | 
						struct spa_dbus *dbus;
 | 
				
			||||||
 | 
						struct spa_dbus_connection *dbus_connection;
 | 
				
			||||||
 | 
						DBusConnection *conn;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_hook_list hooks;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						uint32_t count;
 | 
				
			||||||
 | 
						uint32_t id;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						/*
 | 
				
			||||||
 | 
						 * Lists of BlueZ objects, kept up-to-date by following DBus events
 | 
				
			||||||
 | 
						 * initiated by BlueZ. Object lifetime is also determined by that.
 | 
				
			||||||
 | 
						 */
 | 
				
			||||||
 | 
						struct spa_list adapter_list;
 | 
				
			||||||
 | 
						struct spa_list device_list;
 | 
				
			||||||
 | 
						struct spa_list remote_endpoint_list;
 | 
				
			||||||
 | 
						struct spa_list transport_list;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned int filters_added:1;
 | 
				
			||||||
 | 
						unsigned int objects_listed:1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_bt_backend *backend_native;
 | 
				
			||||||
 | 
						struct spa_bt_backend *backend_ofono;
 | 
				
			||||||
 | 
						struct spa_bt_backend *backend_hsphfpd;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						struct spa_dict enabled_codecs;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						unsigned int enable_sbc_xq:1;
 | 
				
			||||||
 | 
						unsigned int backend_native_registered:1;
 | 
				
			||||||
 | 
						unsigned int backend_ofono_registered:1;
 | 
				
			||||||
 | 
						unsigned int backend_hsphfpd_registered:1;
 | 
				
			||||||
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* Stream endpoints owned by BlueZ for each device */
 | 
					/* Stream endpoints owned by BlueZ for each device */
 | 
				
			||||||
struct spa_bt_remote_endpoint {
 | 
					struct spa_bt_remote_endpoint {
 | 
				
			||||||
	struct spa_list link;
 | 
						struct spa_list link;
 | 
				
			||||||
| 
						 | 
					@ -109,6 +149,211 @@ struct spa_bt_a2dp_codec_switch {
 | 
				
			||||||
static int spa_bt_transport_stop_release_timer(struct spa_bt_transport *transport);
 | 
					static int spa_bt_transport_stop_release_timer(struct spa_bt_transport *transport);
 | 
				
			||||||
static int spa_bt_transport_start_release_timer(struct spa_bt_transport *transport);
 | 
					static int spa_bt_transport_start_release_timer(struct spa_bt_transport *transport);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Working with BlueZ Battery Provider.
 | 
				
			||||||
 | 
					// Developed using https://github.com/dgreid/adhd/commit/655b58f as an example of DBus calls.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Name of battery, formatted as /org/freedesktop/pipewire/battery/org/bluez/hciX/dev_XX_XX_XX_XX_XX_XX
 | 
				
			||||||
 | 
					static char *battery_get_name(struct spa_bt_device *device)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						char *path = malloc(strlen(PIPEWIRE_BATTERY_PROVIDER) + strlen(device->path) + 1);
 | 
				
			||||||
 | 
						sprintf(path, PIPEWIRE_BATTERY_PROVIDER "%s", device->path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return path;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Unregister virtual battery of device
 | 
				
			||||||
 | 
					static void battery_remove(struct spa_bt_device *device) {
 | 
				
			||||||
 | 
						if (!device->adapter->has_battery_provider) return;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_debug(device->monitor->log, NAME": Removing virtual battery: %s", battery_get_name(device));
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusMessage *m = dbus_message_new_signal(PIPEWIRE_BATTERY_PROVIDER,
 | 
				
			||||||
 | 
									      DBUS_INTERFACE_OBJECT_MANAGER,
 | 
				
			||||||
 | 
									      DBUS_SIGNAL_INTERFACES_REMOVED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusMessageIter i, entry;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_init_append(m, &i);
 | 
				
			||||||
 | 
						const char *bat_name = battery_get_name(device);
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&i, DBUS_TYPE_OBJECT_PATH,
 | 
				
			||||||
 | 
									       &bat_name);
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY,
 | 
				
			||||||
 | 
										 DBUS_TYPE_STRING_AS_STRING, &entry);
 | 
				
			||||||
 | 
						const char *interface = BLUEZ_INTERFACE_BATTERY_PROVIDER;
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
 | 
				
			||||||
 | 
									       &interface);
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&i, &entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_connection_send(device->monitor->conn, m, NULL)) {
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": sending " DBUS_SIGNAL_INTERFACES_REMOVED " failed");
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_unref(m);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device->has_battery = false;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create properties for Battery Provider request
 | 
				
			||||||
 | 
					static void battery_write_properties(DBusMessageIter *iter, struct spa_bt_device *device)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DBusMessageIter dict, entry, variant;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(iter, DBUS_TYPE_ARRAY, "{sv}", &dict);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL,
 | 
				
			||||||
 | 
										 &entry);
 | 
				
			||||||
 | 
						const char *prop_percentage = "Percentage";
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &prop_percentage);
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
 | 
				
			||||||
 | 
										 DBUS_TYPE_BYTE_AS_STRING, &variant);
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &device->battery);
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&entry, &variant);
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&dict, &entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
 | 
				
			||||||
 | 
						const char *prop_device = "Device";
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &prop_device);
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT,
 | 
				
			||||||
 | 
										 DBUS_TYPE_OBJECT_PATH_AS_STRING,
 | 
				
			||||||
 | 
										 &variant);
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&variant, DBUS_TYPE_OBJECT_PATH, &device->path);
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&entry, &variant);
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&dict, &entry);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(iter, &dict);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Send current percentage to BlueZ
 | 
				
			||||||
 | 
					static void battery_update(struct spa_bt_device *device)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *name = battery_get_name(device);
 | 
				
			||||||
 | 
						spa_log_debug(device->monitor->log, NAME": updating battery: %s", name);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						DBusMessage *msg;
 | 
				
			||||||
 | 
						DBusMessageIter iter;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						msg = dbus_message_new_signal(name,
 | 
				
			||||||
 | 
									      DBUS_INTERFACE_PROPERTIES,
 | 
				
			||||||
 | 
									      DBUS_SIGNAL_PROPERTIES_CHANGED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_init_append(msg, &iter);
 | 
				
			||||||
 | 
						const char *interface = BLUEZ_INTERFACE_BATTERY_PROVIDER;
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&iter, DBUS_TYPE_STRING,
 | 
				
			||||||
 | 
									       &interface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						battery_write_properties(&iter, device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_connection_send(device->monitor->conn, msg, NULL))
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": Error updating battery");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_unref(msg);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Create ney virtual battery with value stored in current device object
 | 
				
			||||||
 | 
					static void battery_create(struct spa_bt_device *device) {
 | 
				
			||||||
 | 
						DBusMessage *msg;
 | 
				
			||||||
 | 
						DBusMessageIter iter, entry, dict;
 | 
				
			||||||
 | 
						msg = dbus_message_new_signal(PIPEWIRE_BATTERY_PROVIDER,
 | 
				
			||||||
 | 
									      DBUS_INTERFACE_OBJECT_MANAGER,
 | 
				
			||||||
 | 
									      DBUS_SIGNAL_INTERFACES_ADDED);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_init_append(msg, &iter);
 | 
				
			||||||
 | 
						const char *bat_name = battery_get_name(device);
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&iter, DBUS_TYPE_OBJECT_PATH,
 | 
				
			||||||
 | 
									       &bat_name);
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY, "{sa{sv}}", &dict);
 | 
				
			||||||
 | 
						dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry);
 | 
				
			||||||
 | 
						const char *interface = BLUEZ_INTERFACE_BATTERY_PROVIDER;
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING,
 | 
				
			||||||
 | 
									       &interface);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						battery_write_properties(&entry, device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&dict, &entry);
 | 
				
			||||||
 | 
						dbus_message_iter_close_container(&iter, &dict);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_connection_send(device->monitor->conn, msg, NULL)) {
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": Failed to create virtual battery for %s", device->address);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_unref(msg);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_debug(device->monitor->log, NAME": Created virtual battery for %s", device->address);
 | 
				
			||||||
 | 
						device->has_battery = true;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					static void on_battery_provider_registered(DBusPendingCall *pending_call,
 | 
				
			||||||
 | 
									       void *data)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DBusMessage *reply;
 | 
				
			||||||
 | 
						struct spa_bt_device *device = data;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						reply = dbus_pending_call_steal_reply(pending_call);
 | 
				
			||||||
 | 
						dbus_pending_call_unref(pending_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (dbus_message_get_type(reply) == DBUS_MESSAGE_TYPE_ERROR) {
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": Failed to register battery provider. Error: %s", dbus_message_get_error_name(reply));
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": BlueZ Battery Provider is not available, won't retry to register it. Make sure you are running BlueZ 5.56+ with experimental features to use Battery Provider.");
 | 
				
			||||||
 | 
							device->adapter->battery_provider_unavailable = true;
 | 
				
			||||||
 | 
							dbus_message_unref(reply);
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						spa_log_debug(device->monitor->log, NAME": Registered Battery Provider");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device->adapter->has_battery_provider = true;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!device->has_battery)
 | 
				
			||||||
 | 
							battery_create(device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_unref(reply);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Register Battery Provider for adapter and then create virtual battery for device
 | 
				
			||||||
 | 
					static void register_battery_provider(struct spa_bt_device *device)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						DBusMessage *method_call;
 | 
				
			||||||
 | 
						DBusMessageIter message_iter;
 | 
				
			||||||
 | 
						DBusPendingCall *pending_call;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						method_call = dbus_message_new_method_call(
 | 
				
			||||||
 | 
							BLUEZ_SERVICE, device->adapter_path,
 | 
				
			||||||
 | 
							BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER,
 | 
				
			||||||
 | 
							"RegisterBatteryProvider");
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!method_call) {
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": Failed to register battery provider");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_iter_init_append(method_call, &message_iter);
 | 
				
			||||||
 | 
						const char *object_path = PIPEWIRE_BATTERY_PROVIDER;
 | 
				
			||||||
 | 
						dbus_message_iter_append_basic(&message_iter, DBUS_TYPE_OBJECT_PATH,
 | 
				
			||||||
 | 
									       &object_path);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_connection_send_with_reply(device->monitor->conn, method_call, &pending_call,
 | 
				
			||||||
 | 
										     DBUS_TIMEOUT_USE_DEFAULT)) {
 | 
				
			||||||
 | 
							dbus_message_unref(method_call);
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": Failed to register battery provider");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						dbus_message_unref(method_call);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!pending_call) {
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, NAME": Failed to register battery provider");
 | 
				
			||||||
 | 
							return;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!dbus_pending_call_set_notify(
 | 
				
			||||||
 | 
							    pending_call, on_battery_provider_registered,
 | 
				
			||||||
 | 
							    device, NULL)) {
 | 
				
			||||||
 | 
							spa_log_error(device->monitor->log, "Failed to register battery provider");
 | 
				
			||||||
 | 
							dbus_pending_call_cancel(pending_call);
 | 
				
			||||||
 | 
							dbus_pending_call_unref(pending_call);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
static inline void add_dict(struct spa_pod_builder *builder, const char *key, const char *val)
 | 
					static inline void add_dict(struct spa_pod_builder *builder, const char *key, const char *val)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
| 
						 | 
					@ -406,6 +651,7 @@ static void device_free(struct spa_bt_device *device)
 | 
				
			||||||
	struct spa_bt_monitor *monitor = device->monitor;
 | 
						struct spa_bt_monitor *monitor = device->monitor;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	spa_log_debug(monitor->log, "%p", device);
 | 
						spa_log_debug(monitor->log, "%p", device);
 | 
				
			||||||
 | 
						battery_remove(device);
 | 
				
			||||||
	device_stop_timer(device);
 | 
						device_stop_timer(device);
 | 
				
			||||||
	device_remove(monitor, device);
 | 
						device_remove(monitor, device);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -3160,3 +3406,28 @@ const struct spa_handle_factory spa_bluez5_dbus_factory = {
 | 
				
			||||||
	impl_init,
 | 
						impl_init,
 | 
				
			||||||
	impl_enum_interface_info,
 | 
						impl_enum_interface_info,
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// Report battery percentage to BlueZ using experimental (BlueZ 5.56) Battery Provider API. No-op if no changes occured.
 | 
				
			||||||
 | 
					int spa_bt_device_report_battery_level(struct spa_bt_device *device, uint8_t percentage)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						// BlueZ likely is running without battery provider support, don't try to report battery
 | 
				
			||||||
 | 
						if (device->adapter->battery_provider_unavailable) return 0;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						// If everything is initialized and battery level has not changed we don't need to send anything to BlueZ
 | 
				
			||||||
 | 
						if (device->adapter->has_battery_provider && device->has_battery && device->battery == percentage) return 1;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						device->battery = percentage;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if (!device->adapter->has_battery_provider) {
 | 
				
			||||||
 | 
							// No provider: register it, create battery when registered
 | 
				
			||||||
 | 
							register_battery_provider(device);
 | 
				
			||||||
 | 
						} else if (!device->has_battery) {
 | 
				
			||||||
 | 
							// Have provider but no battery: create battery with correct percentage
 | 
				
			||||||
 | 
							battery_create(device);
 | 
				
			||||||
 | 
						} else {
 | 
				
			||||||
 | 
							// Just update existing battery percentage
 | 
				
			||||||
 | 
							battery_update(device);
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -48,6 +48,17 @@ extern "C" {
 | 
				
			||||||
#define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
 | 
					#define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
 | 
				
			||||||
#define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
 | 
					#define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
 | 
				
			||||||
#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
 | 
					#define BLUEZ_MEDIA_TRANSPORT_INTERFACE BLUEZ_SERVICE ".MediaTransport1"
 | 
				
			||||||
 | 
					#define BLUEZ_INTERFACE_BATTERY_PROVIDER BLUEZ_SERVICE ".BatteryProvider1"
 | 
				
			||||||
 | 
					#define BLUEZ_INTERFACE_BATTERY_PROVIDER_MANAGER BLUEZ_SERVICE ".BatteryProviderManager1"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define DBUS_INTERFACE_OBJECT_MANAGER "org.freedesktop.DBus.ObjectManager"
 | 
				
			||||||
 | 
					#define DBUS_SIGNAL_INTERFACES_ADDED "InterfacesAdded"
 | 
				
			||||||
 | 
					#define DBUS_SIGNAL_INTERFACES_REMOVED "InterfacesRemoved"
 | 
				
			||||||
 | 
					#define DBUS_SIGNAL_PROPERTIES_CHANGED "PropertiesChanged"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define PIPEWIRE_BATTERY_PROVIDER "/org/freedesktop/pipewire/battery"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					#define SPA_BT_HFP_HF_IPHONEACCEV_KEY_BATTERY	1
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define MIN_LATENCY	512
 | 
					#define MIN_LATENCY	512
 | 
				
			||||||
#define MAX_LATENCY	1024
 | 
					#define MAX_LATENCY	1024
 | 
				
			||||||
| 
						 | 
					@ -279,6 +290,8 @@ struct spa_bt_adapter {
 | 
				
			||||||
	int powered;
 | 
						int powered;
 | 
				
			||||||
	unsigned int endpoints_registered:1;
 | 
						unsigned int endpoints_registered:1;
 | 
				
			||||||
	unsigned int application_registered:1;
 | 
						unsigned int application_registered:1;
 | 
				
			||||||
 | 
						unsigned int has_battery_provider;
 | 
				
			||||||
 | 
						unsigned int battery_provider_unavailable;
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
enum spa_bt_form_factor {
 | 
					enum spa_bt_form_factor {
 | 
				
			||||||
| 
						 | 
					@ -369,46 +382,6 @@ struct spa_bt_device_events {
 | 
				
			||||||
	void (*profiles_changed) (void *data, uint32_t prev_profiles, uint32_t prev_connected);
 | 
						void (*profiles_changed) (void *data, uint32_t prev_profiles, uint32_t prev_connected);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
struct spa_bt_monitor {
 | 
					 | 
				
			||||||
	struct spa_handle handle;
 | 
					 | 
				
			||||||
	struct spa_device device;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct spa_log *log;
 | 
					 | 
				
			||||||
	struct spa_loop *main_loop;
 | 
					 | 
				
			||||||
	struct spa_system *main_system;
 | 
					 | 
				
			||||||
	struct spa_dbus *dbus;
 | 
					 | 
				
			||||||
	struct spa_dbus_connection *dbus_connection;
 | 
					 | 
				
			||||||
	DBusConnection *conn;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct spa_hook_list hooks;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	uint32_t count;
 | 
					 | 
				
			||||||
	uint32_t id;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	/*
 | 
					 | 
				
			||||||
	 * Lists of BlueZ objects, kept up-to-date by following DBus events
 | 
					 | 
				
			||||||
	 * initiated by BlueZ. Object lifetime is also determined by that.
 | 
					 | 
				
			||||||
	 */
 | 
					 | 
				
			||||||
	struct spa_list adapter_list;
 | 
					 | 
				
			||||||
	struct spa_list device_list;
 | 
					 | 
				
			||||||
	struct spa_list remote_endpoint_list;
 | 
					 | 
				
			||||||
	struct spa_list transport_list;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unsigned int filters_added:1;
 | 
					 | 
				
			||||||
	unsigned int objects_listed:1;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct spa_bt_backend *backend_native;
 | 
					 | 
				
			||||||
	struct spa_bt_backend *backend_ofono;
 | 
					 | 
				
			||||||
	struct spa_bt_backend *backend_hsphfpd;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	struct spa_dict enabled_codecs;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	unsigned int enable_sbc_xq:1;
 | 
					 | 
				
			||||||
	unsigned int backend_native_registered:1;
 | 
					 | 
				
			||||||
	unsigned int backend_ofono_registered:1;
 | 
					 | 
				
			||||||
	unsigned int backend_hsphfpd_registered:1;
 | 
					 | 
				
			||||||
};
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct spa_bt_device {
 | 
					struct spa_bt_device {
 | 
				
			||||||
	struct spa_list link;
 | 
						struct spa_list link;
 | 
				
			||||||
	struct spa_bt_monitor *monitor;
 | 
						struct spa_bt_monitor *monitor;
 | 
				
			||||||
| 
						 | 
					@ -450,6 +423,7 @@ int spa_bt_device_ensure_a2dp_codec(struct spa_bt_device *device, const struct a
 | 
				
			||||||
bool spa_bt_device_supports_a2dp_codec(struct spa_bt_device *device, const struct a2dp_codec *codec);
 | 
					bool spa_bt_device_supports_a2dp_codec(struct spa_bt_device *device, const struct a2dp_codec *codec);
 | 
				
			||||||
const struct a2dp_codec **spa_bt_device_get_supported_a2dp_codecs(struct spa_bt_device *device, size_t *count);
 | 
					const struct a2dp_codec **spa_bt_device_get_supported_a2dp_codecs(struct spa_bt_device *device, size_t *count);
 | 
				
			||||||
int spa_bt_device_release_transports(struct spa_bt_device *device);
 | 
					int spa_bt_device_release_transports(struct spa_bt_device *device);
 | 
				
			||||||
 | 
					int spa_bt_device_report_battery_level(struct spa_bt_device *device, uint8_t percentage);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#define spa_bt_device_emit(d,m,v,...)			spa_hook_list_call(&(d)->listener_list, \
 | 
					#define spa_bt_device_emit(d,m,v,...)			spa_hook_list_call(&(d)->listener_list, \
 | 
				
			||||||
								struct spa_bt_device_events,	\
 | 
													struct spa_bt_device_events,	\
 | 
				
			||||||
| 
						 | 
					@ -491,9 +465,6 @@ struct spa_bt_transport_implementation {
 | 
				
			||||||
	int (*destroy) (void *data);
 | 
						int (*destroy) (void *data);
 | 
				
			||||||
};
 | 
					};
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Key for getting battery state
 | 
					 | 
				
			||||||
#define SPA_BT_HFP_HF_IPHONEACCEV_KEY_BATTERY	1
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
struct spa_bt_transport {
 | 
					struct spa_bt_transport {
 | 
				
			||||||
	struct spa_list link;
 | 
						struct spa_list link;
 | 
				
			||||||
	struct spa_bt_monitor *monitor;
 | 
						struct spa_bt_monitor *monitor;
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue