summaryrefslogtreecommitdiff
path: root/src/modules
diff options
context:
space:
mode:
Diffstat (limited to 'src/modules')
-rw-r--r--src/modules/bluetooth/backend-native.c2
-rw-r--r--src/modules/bluetooth/bluez5-util.c231
-rw-r--r--src/modules/bluetooth/bluez5-util.h6
3 files changed, 236 insertions, 3 deletions
diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c
index 2882d79ac..b81fac732 100644
--- a/src/modules/bluetooth/backend-native.c
+++ b/src/modules/bluetooth/backend-native.c
@@ -769,7 +769,7 @@ static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_i
switch (key) {
case 1:
pa_log_notice("Battery Level: %d0%%", val + 1);
- pa_bluetooth_device_report_battery_level(t->device, (val + 1) * 10);
+ pa_bluetooth_device_report_battery_level(t->device, (val + 1) * 10, "Apple accessory indication");
break;
case 2:
pa_log_notice("Dock Status: %s", val ? "docked" : "undocked");
diff --git a/src/modules/bluetooth/bluez5-util.c b/src/modules/bluetooth/bluez5-util.c
index e55fcf262..61c4f3332 100644
--- a/src/modules/bluetooth/bluez5-util.c
+++ b/src/modules/bluetooth/bluez5-util.c
@@ -50,6 +50,7 @@
#define A2DP_OBJECT_MANAGER_PATH "/MediaEndpoint"
#define A2DP_SOURCE_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSource"
#define A2DP_SINK_ENDPOINT A2DP_OBJECT_MANAGER_PATH "/A2DPSink"
+#define PULSEAUDIO_BASE_PATH "/org/pulseaudio"
#define OBJECT_MANAGER_INTROSPECT_XML \
DBUS_INTROSPECT_1_0_XML_DOCTYPE_DECL_NODE \
@@ -868,10 +869,62 @@ bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d) {
return false;
}
-void pa_bluetooth_device_report_battery_level(pa_bluetooth_device *d, uint8_t level) {
+/* Returns a path containing /org/pulseaudio + /bluez/hciXX */
+static char *adapter_battery_provider_path(pa_bluetooth_adapter *d) {
+ const char *devname = d->path + sizeof("/org") - 1;
+ return pa_sprintf_malloc(PULSEAUDIO_BASE_PATH "%s", devname);
+}
+
+/* Returns a path containing /org/pulseaudio + /bluez/hciXX/dev_XX_XX_XX_XX_XX_XX */
+static char *device_battery_provider_path(pa_bluetooth_device *d) {
+ const char *devname = d->path + sizeof("/org") - 1;
+ return pa_sprintf_malloc(PULSEAUDIO_BASE_PATH "%s", devname);
+}
+
+static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object);
+static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *object, bool only_percentage);
+
+void pa_bluetooth_device_report_battery_level(pa_bluetooth_device *d, uint8_t level, const char *reporting_source) {
+ bool had_battery_provider = d->has_battery_level;
d->has_battery_level = true;
d->battery_level = level;
+ pa_assert_se(d->battery_source = reporting_source);
+
pa_hook_fire(&d->discovery->hooks[PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED], d);
+
+ if (!had_battery_provider) {
+ DBusMessage *m;
+ DBusMessageIter iter;
+ char *provider_path;
+
+ if (!d->adapter->battery_provider_registered) {
+ pa_log_debug("No battery provider registered on adapter of %s", d->path);
+ return;
+ }
+
+ provider_path = adapter_battery_provider_path(d->adapter);
+
+ pa_log_debug("Registering new battery for %s with level %d", d->path, level);
+
+ pa_assert_se(m = dbus_message_new_signal(provider_path, DBUS_INTERFACE_OBJECT_MANAGER, "InterfacesAdded"));
+ dbus_message_iter_init_append(m, &iter);
+ append_battery_provider(d, &iter);
+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
+
+ pa_xfree(provider_path);
+ } else {
+ DBusMessage *m;
+ DBusMessageIter iter;
+ char *battery_path = device_battery_provider_path(d);
+
+ pa_log_debug("Notifying battery Percentage for %s changed %d", battery_path, level);
+
+ pa_assert_se(m = dbus_message_new_signal(battery_path, DBUS_INTERFACE_PROPERTIES, "PropertiesChanged"));
+ dbus_message_iter_init_append(m, &iter);
+ append_battery_provider_properties(d, &iter, true);
+ pa_assert_se(dbus_connection_send(pa_dbus_connection_get(d->discovery->connection), m, NULL));
+ pa_xfree(battery_path);
+ }
}
static int transport_state_from_string(const char* value, pa_bluetooth_transport_state_t *state) {
@@ -1170,6 +1223,179 @@ static void device_set_adapter(pa_bluetooth_device *device, pa_bluetooth_adapter
device_update_valid(device);
}
+static void append_battery_provider_properties(pa_bluetooth_device *d, DBusMessageIter *entry, bool only_percentage) {
+ static const char *interface_name = BLUEZ_BATTERY_PROVIDER_INTERFACE;
+ DBusMessageIter dict;
+
+ pa_assert_se(dbus_message_iter_append_basic(entry, DBUS_TYPE_STRING, &interface_name));
+
+ pa_assert_se(dbus_message_iter_open_container(entry, 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));
+
+ pa_dbus_append_basic_variant_dict_entry(&dict, "Percentage", DBUS_TYPE_BYTE, &d->battery_level);
+
+ if (!only_percentage) {
+ pa_assert(d->battery_source);
+ pa_dbus_append_basic_variant_dict_entry(&dict, "Device", DBUS_TYPE_OBJECT_PATH, &d->path);
+ pa_dbus_append_basic_variant_dict_entry(&dict, "Source", DBUS_TYPE_STRING, &d->battery_source);
+ }
+
+ pa_assert_se(dbus_message_iter_close_container(entry, &dict));
+}
+
+static void append_battery_provider(pa_bluetooth_device *d, DBusMessageIter *object) {
+ char *battery_path = device_battery_provider_path(d);
+ DBusMessageIter array, entry;
+
+ pa_assert_se(dbus_message_iter_append_basic(object, DBUS_TYPE_OBJECT_PATH, &battery_path));
+
+ pa_assert_se(dbus_message_iter_open_container(object, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &array));
+
+ pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &entry));
+ append_battery_provider_properties(d, &entry, false);
+ pa_assert_se(dbus_message_iter_close_container(&array, &entry));
+ pa_assert_se(dbus_message_iter_close_container(object, &array));
+
+ pa_xfree(battery_path);
+}
+
+static DBusHandlerResult battery_provider_handler(DBusConnection *c, DBusMessage *m, void *userdata) {
+ pa_bluetooth_adapter *a = userdata;
+ DBusMessage *r = NULL;
+ const char *path, *interface, *member;
+
+ pa_assert(a);
+
+ path = dbus_message_get_path(m);
+ interface = dbus_message_get_interface(m);
+ member = dbus_message_get_member(m);
+
+ pa_log_debug("%s %s %s", path, interface, member);
+
+ if (dbus_message_is_method_call(m, DBUS_INTERFACE_OBJECT_MANAGER, "GetManagedObjects")) {
+ DBusMessageIter iter, array, object;
+ pa_bluetooth_device *d;
+ void *state;
+
+ pa_assert_se(r = dbus_message_new_method_return(m));
+
+ dbus_message_iter_init_append(r, &iter);
+ pa_assert_se(dbus_message_iter_open_container(&iter, DBUS_TYPE_ARRAY,
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_OBJECT_PATH_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_ARRAY_AS_STRING
+ DBUS_DICT_ENTRY_BEGIN_CHAR_AS_STRING
+ DBUS_TYPE_STRING_AS_STRING
+ DBUS_TYPE_VARIANT_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING
+ DBUS_DICT_ENTRY_END_CHAR_AS_STRING,
+ &array));
+
+ PA_HASHMAP_FOREACH(d, a->discovery->devices, state) {
+
+ if (d->has_battery_level) {
+ pa_log_debug("%s: battery level = %d", d->path, d->battery_level);
+ pa_assert_se(dbus_message_iter_open_container(&array, DBUS_TYPE_DICT_ENTRY, NULL, &object));
+ append_battery_provider(d, &object);
+ pa_assert_se(dbus_message_iter_close_container(&array, &object));
+ }
+ }
+
+ pa_assert_se(dbus_message_iter_close_container(&iter, &array));
+ } else
+ return DBUS_HANDLER_RESULT_NOT_YET_HANDLED;
+
+ pa_assert_se(dbus_connection_send(c, r, NULL));
+ dbus_message_unref(r);
+
+ return DBUS_HANDLER_RESULT_HANDLED;
+}
+
+static void adapter_register_battery_provider(pa_bluetooth_adapter *a) {
+ DBusMessage *m, *r;
+ DBusError error;
+
+ static const DBusObjectPathVTable vtable_profile = {
+ .message_function = battery_provider_handler,
+ };
+
+ char *provider_path = adapter_battery_provider_path(a);
+
+ pa_log_debug("Registering battery provider for %s at %s", a->path, provider_path);
+
+ pa_assert_se(dbus_connection_register_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path, &vtable_profile, a));
+
+ pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "RegisterBatteryProvider"));
+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
+
+ dbus_error_init(&error);
+ if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
+ if (dbus_error_has_name(&error, DBUS_ERROR_UNKNOWN_METHOD))
+ pa_log_notice("Could not find " BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE
+ ".RegisterBatteryProvider(), is bluetoothd started with experimental features enabled (-E flag)?");
+ else
+ pa_log_warn(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".RegisterBatteryProvider() Failed: %s:%s", error.name, error.message);
+ dbus_error_free(&error);
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
+ } else {
+ dbus_message_unref(r);
+ a->battery_provider_registered = true;
+ }
+
+ dbus_message_unref(m);
+ pa_xfree(provider_path);
+}
+
+static void adapter_deregister_battery_provider(pa_bluetooth_adapter *a) {
+ DBusMessage *m, *r;
+ DBusError error;
+ char *provider_path;
+
+ if (!a->battery_provider_registered) {
+ pa_log_debug("No battery provider registered for %s", a->path);
+ return;
+ }
+
+ provider_path = adapter_battery_provider_path(a);
+
+ pa_log_debug("Deregistering battery provider at %s", provider_path);
+
+ pa_assert_se(m = dbus_message_new_method_call(BLUEZ_SERVICE, a->path, BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE, "UnregisterBatteryProvider"));
+ pa_assert_se(dbus_message_append_args(m, DBUS_TYPE_OBJECT_PATH, &provider_path, DBUS_TYPE_INVALID));
+
+ dbus_error_init(&error);
+ if (!(r = dbus_connection_send_with_reply_and_block(pa_dbus_connection_get(a->discovery->connection), m, -1, &error))) {
+ pa_log_error(BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE ".UnregisterBatteryProvider() Failed: %s:%s", error.name, error.message);
+ dbus_error_free(&error);
+ } else {
+ dbus_message_unref(r);
+ a->battery_provider_registered = false;
+ }
+
+ dbus_message_unref(m);
+
+ dbus_connection_unregister_object_path(pa_dbus_connection_get(a->discovery->connection), provider_path);
+
+ pa_xfree(provider_path);
+}
+
static pa_bluetooth_adapter* adapter_create(pa_bluetooth_discovery *y, const char *path) {
pa_bluetooth_adapter *a;
@@ -1192,6 +1418,8 @@ static void adapter_free(pa_bluetooth_adapter *a) {
pa_assert(a);
pa_assert(a->discovery);
+ adapter_deregister_battery_provider(a);
+
PA_HASHMAP_FOREACH(d, a->discovery->devices, state)
if (d->adapter == a)
device_set_adapter(d, NULL);
@@ -1726,6 +1954,7 @@ static void parse_interfaces_and_properties(pa_bluetooth_discovery *y, DBusMessa
return;
register_application(a);
+ adapter_register_battery_provider(a);
} else if (pa_streq(interface, BLUEZ_DEVICE_INTERFACE)) {
if ((d = pa_hashmap_get(y->devices, path))) {
diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h
index ac9c8da10..b07c1eacf 100644
--- a/src/modules/bluetooth/bluez5-util.h
+++ b/src/modules/bluetooth/bluez5-util.h
@@ -27,6 +27,8 @@
#define BLUEZ_SERVICE "org.bluez"
#define BLUEZ_ADAPTER_INTERFACE BLUEZ_SERVICE ".Adapter1"
+#define BLUEZ_BATTERY_PROVIDER_INTERFACE BLUEZ_SERVICE ".BatteryProvider1"
+#define BLUEZ_BATTERY_PROVIDER_MANAGER_INTERFACE BLUEZ_SERVICE ".BatteryProviderManager1"
#define BLUEZ_DEVICE_INTERFACE BLUEZ_SERVICE ".Device1"
#define BLUEZ_MEDIA_ENDPOINT_INTERFACE BLUEZ_SERVICE ".MediaEndpoint1"
#define BLUEZ_MEDIA_INTERFACE BLUEZ_SERVICE ".Media1"
@@ -154,6 +156,7 @@ struct pa_bluetooth_device {
bool has_battery_level;
uint8_t battery_level;
+ const char *battery_source;
};
struct pa_bluetooth_adapter {
@@ -163,6 +166,7 @@ struct pa_bluetooth_adapter {
bool valid;
bool application_registered;
+ bool battery_provider_registered;
};
#ifdef HAVE_BLUEZ_5_OFONO_HEADSET
@@ -201,7 +205,7 @@ void pa_bluetooth_transport_load_a2dp_sink_volume(pa_bluetooth_transport *t);
bool pa_bluetooth_device_any_transport_connected(const pa_bluetooth_device *d);
bool pa_bluetooth_device_switch_codec(pa_bluetooth_device *device, pa_bluetooth_profile_t profile, pa_hashmap *capabilities_hashmap, const pa_a2dp_endpoint_conf *endpoint_conf, void (*codec_switch_cb)(bool, pa_bluetooth_profile_t profile, void *), void *userdata);
-void pa_bluetooth_device_report_battery_level(pa_bluetooth_device *d, uint8_t level);
+void pa_bluetooth_device_report_battery_level(pa_bluetooth_device *d, uint8_t level, const char *reporting_source);
pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_path(pa_bluetooth_discovery *y, const char *path);
pa_bluetooth_device* pa_bluetooth_discovery_get_device_by_address(pa_bluetooth_discovery *y, const char *remote, const char *local);