From 78e255eefb417275e73f8ed96ce08bf7c3a1dff9 Mon Sep 17 00:00:00 2001 From: Dylan Van Assche Date: Tue, 5 Apr 2022 20:39:11 +0200 Subject: [PATCH 04/26] bluetooth: hook up UPower backend Hook up the UPower backend to backend-native to report the host battery level to the HF. --- src/modules/bluetooth/backend-native.c | 100 +++++++++++++++++++++++-- src/modules/bluetooth/bluez5-util.h | 2 + 2 files changed, 95 insertions(+), 7 deletions(-) diff --git a/src/modules/bluetooth/backend-native.c b/src/modules/bluetooth/backend-native.c index d26edc1c9..a490d6efd 100644 --- a/src/modules/bluetooth/backend-native.c +++ b/src/modules/bluetooth/backend-native.c @@ -38,6 +38,7 @@ #include "bluez5-util.h" #include "bt-codec-msbc.h" +#include "upower.h" #define MANDATORY_CALL_INDICATORS \ "(\"call\",(0-1))," \ @@ -49,6 +50,8 @@ struct pa_bluetooth_backend { pa_dbus_connection *connection; pa_bluetooth_discovery *discovery; pa_hook_slot *adapter_uuids_changed_slot; + pa_hook_slot *host_battery_level_changed_slot; + pa_upower_backend *upower; bool enable_shared_profiles; bool enable_hsp_hs; bool enable_hfp_hf; @@ -683,17 +686,27 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf return true; } else if (c->state == 1 && pa_startswith(buf, "AT+CIND=?")) { - /* we declare minimal no indicators */ - rfcomm_write_response(fd, "+CIND: " - /* many indicators can be supported, only call and - * callheld are mandatory, so that's all we reply */ - MANDATORY_CALL_INDICATORS ",", - "(\"service\",(0-1))"); + /* UPower backend available, declare support for more indicators */ + if (discovery->native_backend->upower) { + rfcomm_write_response(fd, "+CIND: " + MANDATORY_CALL_INDICATORS "," + "(\"service\",(0-1))," + "(\"battchg\",(0-5))"); + + /* Minimal indicators supported without any additional backend */ + } else { + rfcomm_write_response(fd, "+CIND: " + MANDATORY_CALL_INDICATORS "," + "(\"service\",(0-1))"); + } c->state = 2; return true; } else if (c->state == 2 && pa_startswith(buf, "AT+CIND?")) { - rfcomm_write_response(fd, "+CIND: 0,0,0,0"); + if (discovery->native_backend->upower) + rfcomm_write_response(fd, "+CIND: 0,0,0,0,%u", pa_upower_get_battery_level(discovery->native_backend->upower)); + else + rfcomm_write_response(fd, "+CIND: 0,0,0,0"); c->state = 3; return true; @@ -803,6 +816,67 @@ static bool hfp_rfcomm_handle(int fd, pa_bluetooth_transport *t, const char *buf return true; } +static int get_rfcomm_fd (pa_bluetooth_discovery *discovery) { + struct pa_bluetooth_transport *t; + struct transport_data *trd = NULL; + pa_bluetooth_profile_t profiles[] = { PA_BLUETOOTH_PROFILE_HSP_HS, PA_BLUETOOTH_PROFILE_HSP_AG, PA_BLUETOOTH_PROFILE_HFP_HF }; + int i; + bool found_transport = false; + void *state = NULL; + + /* Find RFCOMM transport by checking if a HSP or HFP profile transport is available */ + while ((t = pa_hashmap_iterate(discovery->transports, &state, NULL))) { + /* Skip non-connected transports */ + if (!t || t->state == PA_BLUETOOTH_TRANSPORT_STATE_DISCONNECTED) { + pa_log_debug("Profile %d disconnected or unavailable", i); + continue; + } + + /* Break when an RFCOMM capable transport profile is available */ + for (i = 0; i < (sizeof profiles / sizeof *profiles); i++) { + if (t->profile == profiles[i]) { + trd = t->userdata; + found_transport = true; + break; + } + } + + if (found_transport) + break; + } + + /* Skip if RFCOMM channel is not available yet */ + if (!trd) { + pa_log_info("RFCOMM not available yet, skipping notification"); + return -1; + } + + return trd->rfcomm_fd; +} + +static pa_hook_result_t host_battery_level_changed_cb(pa_bluetooth_discovery *y, const pa_upower_backend *u, pa_bluetooth_backend *b) { + int rfcomm_fd; + + pa_assert(y); + pa_assert(u); + pa_assert(b); + + /* Get RFCOMM channel if available */ + rfcomm_fd = get_rfcomm_fd (y); + if (rfcomm_fd < 0) + return PA_HOOK_OK; + + /* Notify HF about AG battery level change over RFCOMM */ + if (b->cmer_indicator_reporting_enabled && (b->cind_enabled_indicators & (1 << CIND_BATT_CHG_INDICATOR))) { + rfcomm_write_response(rfcomm_fd, "+CIEV: %d,%d", CIND_BATT_CHG_INDICATOR, u->battery_level); + pa_log_debug("HG notified of AG's battery level change"); + /* Skip notification if indicator is disabled or event reporting is completely disabled */ + } else + pa_log_debug("Battery level change indicator disabled, skipping notification"); + + return PA_HOOK_OK; +} + static void rfcomm_io_callback(pa_mainloop_api *io, pa_io_event *e, int fd, pa_io_event_flags_t events, void *userdata) { pa_bluetooth_transport *t = userdata; pa_bluetooth_discovery *discovery = t->device->discovery; @@ -1284,6 +1358,10 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED), PA_HOOK_NORMAL, (pa_hook_cb_t) adapter_uuids_changed_cb, backend); + backend->host_battery_level_changed_slot = + pa_hook_connect(pa_bluetooth_discovery_hook(y, PA_BLUETOOTH_HOOK_HOST_BATTERY_LEVEL_CHANGED), PA_HOOK_NORMAL, + (pa_hook_cb_t) host_battery_level_changed_cb, backend); + if (!backend->enable_hsp_hs && !backend->enable_hfp_hf) pa_log_warn("Both HSP HS and HFP HF bluetooth profiles disabled in native backend. Native backend will not register for headset connections."); @@ -1293,6 +1371,8 @@ pa_bluetooth_backend *pa_bluetooth_native_backend_new(pa_core *c, pa_bluetooth_d if (backend->enable_shared_profiles) native_backend_apply_profile_registration_change(backend, true); + backend->upower = pa_upower_backend_new(c, y); + /* All CIND indicators are enabled by default until overriden by AT+BIA */ for (i = 1; i < CIND_INDICATOR_MAX; i++) backend->cind_enabled_indicators |= (1 << i); @@ -1312,12 +1392,18 @@ void pa_bluetooth_native_backend_free(pa_bluetooth_backend *backend) { if (backend->adapter_uuids_changed_slot) pa_hook_slot_free(backend->adapter_uuids_changed_slot); + if (backend->host_battery_level_changed_slot) + pa_hook_slot_free(backend->host_battery_level_changed_slot); + if (backend->enable_shared_profiles) native_backend_apply_profile_registration_change(backend, false); if (backend->enable_hsp_hs) profile_done(backend, PA_BLUETOOTH_PROFILE_HSP_HS); + if (backend->upower) + pa_upower_backend_free(backend->upower); + pa_dbus_connection_unref(backend->connection); pa_xfree(backend); diff --git a/src/modules/bluetooth/bluez5-util.h b/src/modules/bluetooth/bluez5-util.h index f899d9d0c..a24a0b823 100644 --- a/src/modules/bluetooth/bluez5-util.h +++ b/src/modules/bluetooth/bluez5-util.h @@ -63,12 +63,14 @@ typedef struct pa_bluetooth_device pa_bluetooth_device; typedef struct pa_bluetooth_adapter pa_bluetooth_adapter; typedef struct pa_bluetooth_discovery pa_bluetooth_discovery; typedef struct pa_bluetooth_backend pa_bluetooth_backend; +typedef struct pa_upower_backend pa_upower_backend; typedef enum pa_bluetooth_hook { PA_BLUETOOTH_HOOK_ADAPTER_UUIDS_CHANGED, /* Call data: pa_bluetooth_adapter */ PA_BLUETOOTH_HOOK_DEVICE_CONNECTION_CHANGED, /* Call data: pa_bluetooth_device */ PA_BLUETOOTH_HOOK_DEVICE_UNLINK, /* Call data: pa_bluetooth_device */ PA_BLUETOOTH_HOOK_DEVICE_BATTERY_LEVEL_CHANGED, /* Call data: pa_bluetooth_device */ + PA_BLUETOOTH_HOOK_HOST_BATTERY_LEVEL_CHANGED, /* Call data: pa_upower_backend */ PA_BLUETOOTH_HOOK_TRANSPORT_STATE_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_TRANSPORT_SOURCE_VOLUME_CHANGED, /* Call data: pa_bluetooth_transport */ PA_BLUETOOTH_HOOK_TRANSPORT_SINK_VOLUME_CHANGED, /* Call data: pa_bluetooth_transport */ -- 2.35.1