diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 8d5354d31..906e5268d 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -48,6 +48,8 @@ SPA_LOG_TOPIC_DEFINE_STATIC(log_topic, "spa.bluez5.native"); #define PROP_KEY_ROLES "bluez5.roles" #define PROP_KEY_HEADSET_ROLES "bluez5.headset-roles" #define PROP_KEY_HFP_DISABLE_NREC "bluez5.hfp-hf.disable-nrec" +#define PROP_KEY_HFP_DEFAULT_MIC_VOL "bluez5.hfp-hf.default-mic-volume" +#define PROP_KEY_HFP_DEFAULT_SPEAKER_VOL "bluez5.hfp-hf.default-speaker-volume" #define HFP_CODEC_SWITCH_INITIAL_TIMEOUT_MSEC 5000 #define HFP_CODEC_SWITCH_TIMEOUT_MSEC 20000 @@ -100,6 +102,8 @@ struct impl { #define DEFAULT_ENABLED_PROFILES (SPA_BT_PROFILE_HFP_HF | SPA_BT_PROFILE_HFP_AG) enum spa_bt_profile enabled_profiles; bool hfp_disable_nrec; + int hfp_default_mic_volume; + int hfp_default_speaker_volume; struct spa_source sco; @@ -460,7 +464,7 @@ static void rfcomm_send_error(const struct rfcomm *rfcomm, enum cmee_error error rfcomm_send_reply(rfcomm, "ERROR"); } -static bool rfcomm_volume_enabled(struct rfcomm *rfcomm) +static bool rfcomm_hw_volume_enabled(struct rfcomm *rfcomm) { return rfcomm->device != NULL && (rfcomm->device->hw_volume_profiles & rfcomm->profile); @@ -470,9 +474,6 @@ static void rfcomm_emit_volume_changed(struct rfcomm *rfcomm, int id, int hw_vol { struct spa_bt_transport_volume *t_volume; - if (!rfcomm_volume_enabled(rfcomm)) - return; - if ((id == SPA_BT_VOLUME_ID_RX || id == SPA_BT_VOLUME_ID_TX) && hw_volume >= 0) { rfcomm->volumes[id].active = true; rfcomm->volumes[id].hw_volume = hw_volume; @@ -480,17 +481,24 @@ static void rfcomm_emit_volume_changed(struct rfcomm *rfcomm, int id, int hw_vol spa_log_debug(rfcomm->backend->log, "volume changed %d", hw_volume); - if (rfcomm->transport == NULL || !rfcomm->has_volume) - return; + if (rfcomm_hw_volume_enabled(rfcomm)) { + if (rfcomm->transport == NULL || !rfcomm->has_volume) + return; - for (int i = 0; i < SPA_BT_VOLUME_ID_TERM ; ++i) { - t_volume = &rfcomm->transport->volumes[i]; - t_volume->active = rfcomm->volumes[i].active; - t_volume->volume = (float) - spa_bt_volume_hw_to_linear(rfcomm->volumes[i].hw_volume, t_volume->hw_volume_max); + for (int i = 0; i < SPA_BT_VOLUME_ID_TERM ; ++i) { + t_volume = &rfcomm->transport->volumes[i]; + t_volume->active = rfcomm->volumes[i].active; + t_volume->volume = (float) + spa_bt_volume_hw_to_linear(rfcomm->volumes[i].hw_volume, t_volume->hw_volume_max); + } + + spa_bt_transport_emit_volume_changed(rfcomm->transport); } - spa_bt_transport_emit_volume_changed(rfcomm->transport); + if (rfcomm->telephony_ag) { + rfcomm->telephony_ag->volume[id] = hw_volume; + telephony_ag_notify_updated_props(rfcomm->telephony_ag); + } } #ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE @@ -534,18 +542,21 @@ static bool rfcomm_send_volume_cmd(struct rfcomm *rfcomm, int id) { struct spa_bt_transport_volume *t_volume; const char *format; - int hw_volume; + int hw_volume = rfcomm->volumes[id].hw_volume; - if (!rfcomm_volume_enabled(rfcomm)) - return false; + if (rfcomm_hw_volume_enabled(rfcomm)) { + t_volume = rfcomm->transport ? &rfcomm->transport->volumes[id] : NULL; - t_volume = rfcomm->transport ? &rfcomm->transport->volumes[id] : NULL; + if (t_volume && t_volume->active) { + hw_volume = spa_bt_volume_linear_to_hw(t_volume->volume, t_volume->hw_volume_max); + rfcomm->volumes[id].hw_volume = hw_volume; + } + } - if (!(t_volume && t_volume->active)) - return false; - - hw_volume = spa_bt_volume_linear_to_hw(t_volume->volume, t_volume->hw_volume_max); - rfcomm->volumes[id].hw_volume = hw_volume; + if (rfcomm->telephony_ag) { + rfcomm->telephony_ag->volume[id] = hw_volume; + telephony_ag_notify_updated_props(rfcomm->telephony_ag); + } if (id == SPA_BT_VOLUME_ID_TX) format = "AT+VGM"; @@ -1898,6 +1909,70 @@ static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *e *err = BT_TELEPHONY_ERROR_NONE; } +static void hfp_hf_set_speaker_volume(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error) +{ + struct rfcomm *rfcomm = data; + struct impl *backend = rfcomm->backend; + struct spa_bt_transport_volume *t_volume; + char reply[20]; + bool res; + + rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = volume; + if (rfcomm_hw_volume_enabled(rfcomm)) { + t_volume = rfcomm->transport ? &rfcomm->transport->volumes[SPA_BT_VOLUME_ID_RX] : NULL; + + if (t_volume && t_volume->active) { + t_volume->volume = (float) spa_bt_volume_hw_to_linear(volume, t_volume->hw_volume_max); + spa_bt_transport_emit_volume_changed(rfcomm->transport); + } + } + + rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_RX); + res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); + if (!res || !spa_strstartswith(reply, "OK")) { + spa_log_info(backend->log, "Failed to send AT+VGS"); + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; + return; + } + + *err = BT_TELEPHONY_ERROR_NONE; +} + +static void hfp_hf_set_microphone_volume(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error) +{ + struct rfcomm *rfcomm = data; + struct impl *backend = rfcomm->backend; + struct spa_bt_transport_volume *t_volume; + char reply[20]; + bool res; + + rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = volume; + if (rfcomm_hw_volume_enabled(rfcomm)) { + t_volume = rfcomm->transport ? &rfcomm->transport->volumes[SPA_BT_VOLUME_ID_TX] : NULL; + + if (t_volume && t_volume->active) { + t_volume->volume = (float) spa_bt_volume_hw_to_linear(volume, t_volume->hw_volume_max); + spa_bt_transport_emit_volume_changed(rfcomm->transport); + } + } + + rfcomm_send_volume_cmd(rfcomm, SPA_BT_VOLUME_ID_TX); + res = hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)); + if (!res || !spa_strstartswith(reply, "OK")) { + spa_log_info(backend->log, "Failed to send AT+VGM"); + if (res) + hfp_hf_get_error_from_reply(reply, err, cme_error); + else + *err = BT_TELEPHONY_ERROR_FAILED; + return; + } + + *err = BT_TELEPHONY_ERROR_NONE; +} + static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = { SPA_VERSION_BT_TELEPHONY_AG_CALLBACKS, .dial = hfp_hf_dial, @@ -1909,6 +1984,8 @@ static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = { .create_multiparty = hfp_hf_create_multiparty, .send_tones = hfp_hf_send_tones, .transport_activate = hfp_hf_transport_activate, + .set_speaker_volume = hfp_hf_set_speaker_volume, + .set_microphone_volume = hfp_hf_set_microphone_volume, }; static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) @@ -2342,6 +2419,8 @@ static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) rfcomm->telephony_ag = telephony_ag_new(backend->telephony, 0); rfcomm->telephony_ag->address = strdup(rfcomm->device->address); + rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_RX] = rfcomm->volumes[SPA_BT_VOLUME_ID_RX].hw_volume = backend->hfp_default_speaker_volume; + rfcomm->telephony_ag->volume[SPA_BT_VOLUME_ID_TX] = rfcomm->volumes[SPA_BT_VOLUME_ID_TX].hw_volume = backend->hfp_default_mic_volume; telephony_ag_set_callbacks(rfcomm->telephony_ag, &telephony_ag_callbacks, rfcomm); if (rfcomm->transport) { @@ -2979,7 +3058,7 @@ static int rfcomm_ag_set_volume(struct spa_bt_transport *t, int id) const char *format; int value; - if (!rfcomm_volume_enabled(rfcomm) + if (!rfcomm_hw_volume_enabled(rfcomm) || !(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) || !(rfcomm->has_volume && rfcomm->volumes[id].active)) return -ENOTSUP; @@ -3013,7 +3092,7 @@ static int sco_set_volume_cb(void *data, int id, float volume) struct rfcomm *rfcomm = td->rfcomm; int value; - if (!rfcomm_volume_enabled(rfcomm) + if (!rfcomm_hw_volume_enabled(rfcomm) || !(rfcomm->profile & SPA_BT_PROFILE_HEADSET_HEAD_UNIT) || !(rfcomm->has_volume && rfcomm->volumes[id].active)) return -ENOTSUP; @@ -3350,7 +3429,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag if (rfcomm_new_transport(rfcomm, HFP_AUDIO_CODEC_CVSD) < 0) goto fail_need_memory; - rfcomm->has_volume = rfcomm_volume_enabled(rfcomm); + rfcomm->has_volume = rfcomm_hw_volume_enabled(rfcomm); if (profile == SPA_BT_PROFILE_HSP_AG) { rfcomm->hs_state = hsp_hs_init1; @@ -3383,10 +3462,8 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag rfcomm->codec_negotiation_supported = false; } - if (rfcomm_volume_enabled(rfcomm)) { - rfcomm->has_volume = true; - hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL; - } + rfcomm->has_volume = true; + hf_features |= SPA_BT_HFP_HF_FEATURE_REMOTE_VOLUME_CONTROL; /* send command to AG with the features supported by Hands-Free */ rfcomm_send_cmd(rfcomm, "AT+BRSF=%u", hf_features); @@ -3394,7 +3471,7 @@ static DBusHandlerResult profile_new_connection(DBusConnection *conn, DBusMessag rfcomm->hf_state = hfp_hf_brsf; } - if (rfcomm_volume_enabled(rfcomm) && (profile == SPA_BT_PROFILE_HFP_HF || profile == SPA_BT_PROFILE_HSP_HS)) { + if (rfcomm_hw_volume_enabled(rfcomm) && (profile == SPA_BT_PROFILE_HFP_HF || profile == SPA_BT_PROFILE_HSP_HS)) { uint32_t device_features; if (spa_bt_quirks_get_features(backend->quirks, d->adapter, d, &device_features) == 0) { rfcomm->broken_mic_hw_volume = !(device_features & SPA_BT_FEATURE_HW_VOLUME_MIC); @@ -3944,6 +4021,29 @@ static void parse_hfp_disable_nrec(struct impl *backend, const struct spa_dict * backend->hfp_disable_nrec = false; } +static void parse_hfp_default_volumes(struct impl *backend, const struct spa_dict *info) +{ + const char *str; + int vol = -1; + + if ((str = spa_dict_lookup(info, PROP_KEY_HFP_DEFAULT_MIC_VOL)) != NULL) + spa_atoi32(str, &vol, 10); + + if (vol >= 0 && vol <= 15) + backend->hfp_default_mic_volume = vol; + else + backend->hfp_default_mic_volume = SPA_BT_VOLUME_HS_MAX; + + vol = -1; + if ((str = spa_dict_lookup(info, PROP_KEY_HFP_DEFAULT_SPEAKER_VOL)) != NULL) + spa_atoi32(str, &vol, 10); + + if (vol >= 0 && vol <= 15) + backend->hfp_default_speaker_volume = vol; + else + backend->hfp_default_speaker_volume = SPA_BT_VOLUME_HS_MAX; +} + static const struct spa_bt_backend_implementation backend_impl = { SPA_VERSION_BT_BACKEND_IMPLEMENTATION, .free = backend_native_free, @@ -4002,6 +4102,7 @@ struct spa_bt_backend *backend_native_new(struct spa_bt_monitor *monitor, goto fail; parse_hfp_disable_nrec(backend, info); + parse_hfp_default_volumes(backend, info); #ifdef HAVE_BLUEZ_5_BACKEND_HSP_NATIVE if (!dbus_connection_register_object_path(backend->conn, diff --git a/spa/plugins/bluez5/telephony.c b/spa/plugins/bluez5/telephony.c index c2f9d6741..f5887053d 100644 --- a/spa/plugins/bluez5/telephony.c +++ b/spa/plugins/bluez5/telephony.c @@ -117,6 +117,8 @@ " " \ " " \ " " \ + " " \ + " " \ " " \ " " \ " " \ @@ -205,6 +207,7 @@ struct agimpl { struct callimpl *dial_return; struct { + int volume[SPA_BT_VOLUME_ID_TERM]; struct spa_bt_telephony_ag_transport transport; } prev; }; @@ -235,6 +238,8 @@ struct callimpl { #define ag_emit_create_multiparty(s,e,cme) ag_emit(s,create_multiparty,0,e,cme) #define ag_emit_send_tones(s,t,e,cme) ag_emit(s,send_tones,0,t,e,cme) #define ag_emit_transport_activate(s,e,cme) ag_emit(s,transport_activate,0,e,cme) +#define ag_emit_set_speaker_volume(s,v,e,cme) ag_emit(s,set_speaker_volume,0,v,e,cme) +#define ag_emit_set_microphone_volume(s,v,e,cme) ag_emit(s,set_microphone_volume,0,v,e,cme) #define call_emit(c,m,v,...) spa_callbacks_call(&c->callbacks, struct spa_bt_telephony_call_callbacks, m, v, ##__VA_ARGS__) #define call_emit_answer(s,e,cme) call_emit(s,answer,0,e,cme) @@ -516,6 +521,14 @@ void telephony_free(struct spa_bt_telephony *telephony) free(impl); } +static void telephony_ag_commit_properties(struct spa_bt_telephony_ag *ag) +{ + struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); + for (int i = 0; i < SPA_BT_VOLUME_ID_TERM; ++i) { + agimpl->prev.volume[i] = ag->volume[i]; + } +} + static void telephony_ag_transport_commit_properties(struct spa_bt_telephony_ag *ag) { struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); @@ -538,6 +551,7 @@ static const char * const * transport_state_to_string(int state) static bool dbus_iter_append_ag_properties(DBusMessageIter *i, struct spa_bt_telephony_ag *ag, bool all) { + struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); DBusMessageIter dict, entry, variant; bool changed = false; @@ -558,6 +572,32 @@ dbus_iter_append_ag_properties(DBusMessageIter *i, struct spa_bt_telephony_ag *a changed = true; } + if (all || ag->volume[SPA_BT_VOLUME_ID_RX] != agimpl->prev.volume[SPA_BT_VOLUME_ID_RX]) { + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); + const char *name = "SpeakerVolume"; + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_BYTE_AS_STRING, + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &ag->volume[SPA_BT_VOLUME_ID_RX]); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(&dict, &entry); + changed = true; + } + + if (all || ag->volume[SPA_BT_VOLUME_ID_TX] != agimpl->prev.volume[SPA_BT_VOLUME_ID_TX]) { + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); + const char *name = "MicrophoneVolume"; + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_BYTE_AS_STRING, + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BYTE, &ag->volume[SPA_BT_VOLUME_ID_TX]); + dbus_message_iter_close_container(&entry, &variant); + dbus_message_iter_close_container(&dict, &entry); + changed = true; + } + dbus_message_iter_close_container(i, &dict); return changed; } @@ -709,6 +749,28 @@ static DBusMessage *ag_properties_get(struct agimpl *agimpl, DBusMessage *m) &agimpl->this.address); dbus_message_iter_close_container(&i, &v); return r; + } else if (spa_streq(name, "SpeakerVolume")) { + r = dbus_message_new_method_return(m); + if (r == NULL) + return NULL; + dbus_message_iter_init_append(r, &i); + dbus_message_iter_open_container(&i, DBUS_TYPE_VARIANT, + DBUS_TYPE_BYTE_AS_STRING, &v); + dbus_message_iter_append_basic(&v, DBUS_TYPE_BYTE, + &agimpl->this.volume[SPA_BT_VOLUME_ID_RX]); + dbus_message_iter_close_container(&i, &v); + return r; + } else if (spa_streq(name, "MicrophoneVolume")) { + r = dbus_message_new_method_return(m); + if (r == NULL) + return NULL; + dbus_message_iter_init_append(r, &i); + dbus_message_iter_open_container(&i, DBUS_TYPE_VARIANT, + DBUS_TYPE_BYTE_AS_STRING, &v); + dbus_message_iter_append_basic(&v, DBUS_TYPE_BYTE, + &agimpl->this.volume[SPA_BT_VOLUME_ID_TX]); + dbus_message_iter_close_container(&i, &v); + return r; } } else if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) { if (spa_streq(name, "Codec")) { @@ -796,7 +858,38 @@ static DBusMessage *ag_properties_set(struct agimpl *agimpl, DBusMessage *m) DBUS_TYPE_INVALID)) return NULL; - if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) { + if (spa_streq(iface, PW_TELEPHONY_AG_IFACE)) { + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + uint8_t cme_error; + + if (spa_streq(name, "SpeakerVolume")) { + dbus_message_iter_init(m, &i); + dbus_message_iter_next(&i); /* skip iface */ + dbus_message_iter_next(&i); /* skip name */ + dbus_message_iter_recurse(&i, &variant); /* value */ + dbus_message_iter_get_basic(&variant, &agimpl->this.volume[SPA_BT_VOLUME_ID_RX]); + + if (ag_emit_set_speaker_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_RX], &err, &cme_error) && + err == BT_TELEPHONY_ERROR_NONE) + return dbus_message_new_method_return(m); + + return dbus_message_new_error(m, telephony_error_to_dbus (err), + telephony_error_to_description (err, cme_error)); + } else if (spa_streq(name, "MicrophoneVolume")) { + dbus_message_iter_init(m, &i); + dbus_message_iter_next(&i); /* skip iface */ + dbus_message_iter_next(&i); /* skip name */ + dbus_message_iter_recurse(&i, &variant); /* value */ + dbus_message_iter_get_basic(&variant, &agimpl->this.volume[SPA_BT_VOLUME_ID_TX]); + + if (ag_emit_set_microphone_volume(agimpl, agimpl->this.volume[SPA_BT_VOLUME_ID_TX], &err, &cme_error) && + err == BT_TELEPHONY_ERROR_NONE) + return dbus_message_new_method_return(m); + + return dbus_message_new_error(m, telephony_error_to_dbus (err), + telephony_error_to_description (err, cme_error)); + } + } else if (spa_streq(iface, PW_TELEPHONY_AG_TRANSPORT_IFACE)) { if (spa_streq(name, "RejectSCO")) { dbus_message_iter_init(m, &i); dbus_message_iter_next(&i); /* skip iface */ @@ -1219,7 +1312,38 @@ void telephony_ag_unregister(struct spa_bt_telephony_ag *ag) agimpl->path = NULL; } -/* send message to notify about property changes */ +/* send message to notify about volume property changes */ +void telephony_ag_notify_updated_props(struct spa_bt_telephony_ag *ag) +{ + struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); + struct impl *impl = SPA_CONTAINER_OF(agimpl->this.telephony, struct impl, this); + + spa_autoptr(DBusMessage) msg = NULL; + const char *interface = PW_TELEPHONY_AG_IFACE; + DBusMessageIter i, a; + + msg = dbus_message_new_signal(agimpl->path, + DBUS_INTERFACE_PROPERTIES, + "PropertiesChanged"); + + dbus_message_iter_init_append(msg, &i); + dbus_message_iter_append_basic(&i, DBUS_TYPE_STRING, &interface); + + if (!dbus_iter_append_ag_properties(&i, ag, false)) + return; + + dbus_message_iter_open_container(&i, DBUS_TYPE_ARRAY, + DBUS_TYPE_STRING_AS_STRING, &a); + dbus_message_iter_close_container(&i, &a); + + if (!dbus_connection_send(impl->conn, msg, NULL)){ + spa_log_warn(impl->log, "sending PropertiesChanged failed"); + } + + telephony_ag_commit_properties(ag); +} + +/* send message to notify about transport property changes */ void telephony_ag_transport_notify_updated_props(struct spa_bt_telephony_ag *ag) { struct agimpl *agimpl = SPA_CONTAINER_OF(ag, struct agimpl, this); diff --git a/spa/plugins/bluez5/telephony.h b/spa/plugins/bluez5/telephony.h index 8ba85ec47..13961b7db 100644 --- a/spa/plugins/bluez5/telephony.h +++ b/spa/plugins/bluez5/telephony.h @@ -45,6 +45,7 @@ struct spa_bt_telephony_ag { /* D-Bus properties */ char *address; + int volume[SPA_BT_VOLUME_ID_TERM]; struct spa_bt_telephony_ag_transport transport; }; @@ -76,6 +77,9 @@ struct spa_bt_telephony_ag_callbacks { void (*send_tones)(void *data, const char *tones, enum spa_bt_telephony_error *err, uint8_t *cme_error); void (*transport_activate)(void *data, enum spa_bt_telephony_error *err, uint8_t *cme_error); + + void (*set_speaker_volume)(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error); + void (*set_microphone_volume)(void *data, uint8_t volume, enum spa_bt_telephony_error *err, uint8_t *cme_error); }; struct spa_bt_telephony_call_callbacks { @@ -103,6 +107,7 @@ void telephony_ag_set_callbacks(struct spa_bt_telephony_ag *ag, const struct spa_bt_telephony_ag_callbacks *cbs, void *data); +void telephony_ag_notify_updated_props(struct spa_bt_telephony_ag *ag); void telephony_ag_transport_notify_updated_props(struct spa_bt_telephony_ag *ag); /* register/unregister AudioGateway object on the bus */