diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 065afc284..981cb33b0 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -1722,6 +1722,33 @@ static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telepho *err = BT_TELEPHONY_ERROR_NONE; } +static void hfp_hf_transport_activate(void *data, enum spa_bt_telephony_error *err) +{ + struct rfcomm *rfcomm = data; + struct impl *backend = rfcomm->backend; + char reply[20]; + + if (spa_list_is_empty(&rfcomm->telephony_ag->call_list)) { + spa_log_debug(backend->log, "no ongoing call"); + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; + } + if (rfcomm->transport->fd > 0) { + spa_log_debug(backend->log, "transport is already active; SCO socket exists"); + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; + } + + rfcomm_send_cmd(rfcomm, "AT+BCC"); + if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { + spa_log_info(backend->log, "Failed to send AT+BCC"); + *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, @@ -1731,7 +1758,8 @@ static const struct spa_bt_telephony_ag_callbacks telephony_ag_callbacks = { .hold_and_answer = hfp_hf_hold_and_answer, .hangup_all = hfp_hf_hangup_all, .create_multiparty = hfp_hf_create_multiparty, - .send_tones = hfp_hf_send_tones + .send_tones = hfp_hf_send_tones, + .transport_activate = hfp_hf_transport_activate, }; static bool rfcomm_hfp_hf(struct rfcomm *rfcomm, char* token) @@ -2661,6 +2689,11 @@ static void sco_listen_event(struct spa_source *source) spa_assert(t->profile & SPA_BT_PROFILE_HEADSET_AUDIO_GATEWAY); + if (rfcomm->telephony_ag && rfcomm->telephony_ag->transport.rejectSCO) { + spa_log_info(backend->log, "rejecting SCO, AudioGatewayTransport1.RejectSCO=true"); + return; + } + if (t->fd >= 0) { spa_log_debug(backend->log, "transport %p: Rejecting, audio already connected", t); return; diff --git a/spa/plugins/bluez5/telephony.c b/spa/plugins/bluez5/telephony.c index e9a77fbd1..7be499276 100644 --- a/spa/plugins/bluez5/telephony.c +++ b/spa/plugins/bluez5/telephony.c @@ -121,6 +121,8 @@ " " \ " " \ " " \ + " " \ + " " \ " " \ " " \ PW_TELEPHONY_AG_COMMON_INTROSPECT_XML \ @@ -188,6 +190,8 @@ struct impl { const char *path; struct spa_list ag_list; + + bool default_reject_sco; }; struct agimpl { @@ -230,6 +234,7 @@ struct callimpl { #define ag_emit_hangup_all(s,e) ag_emit(s,hangup_all,0,e) #define ag_emit_create_multiparty(s,e) ag_emit(s,create_multiparty,0,e) #define ag_emit_send_tones(s,t,e) ag_emit(s,send_tones,0,t,e) +#define ag_emit_transport_activate(s,e) ag_emit(s,transport_activate,0,e) #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) call_emit(s,answer,0,e) @@ -420,6 +425,14 @@ telephony_new(struct spa_log *log, struct spa_dbus *dbus, const struct spa_dict goto fail; } + impl->default_reject_sco = false; + if (info) { + const char *str; + if ((str = spa_dict_lookup(info, "bluez5.telephony.default-reject-sco")) != NULL) { + impl->default_reject_sco = spa_atob(str); + } + } + /* XXX: We should handle spa_dbus reconnecting, but we don't, so ref * XXX: the handle so that we can keep it if spa_dbus unrefs it. */ @@ -555,6 +568,20 @@ dbus_iter_append_ag_transport_properties(DBusMessageIter *i, struct spa_bt_telep changed = true; } + if (all || ag->transport.rejectSCO != agimpl->prev.transport.rejectSCO) { + dbus_message_iter_open_container(&dict, DBUS_TYPE_DICT_ENTRY, NULL, &entry); + const char *name = "RejectSCO"; + dbus_message_iter_append_basic(&entry, DBUS_TYPE_STRING, &name); + dbus_message_iter_open_container(&entry, DBUS_TYPE_VARIANT, + DBUS_TYPE_BOOLEAN_AS_STRING, + &variant); + dbus_message_iter_append_basic(&variant, DBUS_TYPE_BOOLEAN, + &ag->transport.rejectSCO); + 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; } @@ -676,6 +703,17 @@ static DBusMessage *ag_properties_get(struct agimpl *agimpl, DBusMessage *m) transport_state_to_string(agimpl->this.transport.state)); dbus_message_iter_close_container(&i, &v); return r; + } else if (spa_streq(name, "RejectSCO")) { + 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_BOOLEAN_AS_STRING, &v); + dbus_message_iter_append_basic(&v, DBUS_TYPE_BOOLEAN, + &agimpl->this.transport.rejectSCO); + dbus_message_iter_close_container(&i, &v); + return r; } } else { return dbus_message_new_error(m, DBUS_ERROR_UNKNOWN_INTERFACE, @@ -719,6 +757,26 @@ static DBusMessage *ag_properties_get_all(struct agimpl *agimpl, DBusMessage *m) static DBusMessage *ag_properties_set(struct agimpl *agimpl, DBusMessage *m) { + const char *iface, *name; + DBusMessageIter i, variant; + + if (!dbus_message_get_args(m, NULL, + DBUS_TYPE_STRING, &iface, + DBUS_TYPE_STRING, &name, + DBUS_TYPE_INVALID)) + return NULL; + + 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 */ + dbus_message_iter_next(&i); /* skip name */ + dbus_message_iter_recurse(&i, &variant); /* value */ + dbus_message_iter_get_basic(&variant, &agimpl->this.transport.rejectSCO); + return dbus_message_new_method_return(m); + } + } + return dbus_message_new_error(m, DBUS_ERROR_PROPERTY_READ_ONLY, "Property not writable"); } @@ -887,6 +945,17 @@ failed: telephony_error_to_description (err)); } +static DBusMessage *ag_transport_activate(struct agimpl *agimpl, DBusMessage *m) +{ + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (ag_emit_transport_activate(agimpl, &err) && 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)); +} + static DBusHandlerResult ag_handler(DBusConnection *c, DBusMessage *m, void *userdata) { struct agimpl *agimpl = userdata; @@ -937,6 +1006,8 @@ static DBusHandlerResult ag_handler(DBusConnection *c, DBusMessage *m, void *use r = ag_send_tones(agimpl, m); } else if (dbus_message_is_method_call(m, OFONO_VOICE_CALL_MANAGER_IFACE, "GetCalls")) { r = ag_get_managed_objects(agimpl, m, true); + } else if (dbus_message_is_method_call(m, PW_TELEPHONY_AG_TRANSPORT_IFACE, "Activate")) { + r = ag_transport_activate(agimpl, m); } else { return DBUS_HANDLER_RESULT_NOT_YET_HANDLED; } @@ -969,6 +1040,8 @@ telephony_ag_new(struct spa_bt_telephony *telephony, size_t user_data_size) if (user_data_size > 0) agimpl->user_data = SPA_PTROFF(agimpl, sizeof(struct agimpl), void); + agimpl->this.transport.rejectSCO = impl->default_reject_sco; + return &agimpl->this; } diff --git a/spa/plugins/bluez5/telephony.h b/spa/plugins/bluez5/telephony.h index 01acbf5e7..d03ea8346 100644 --- a/spa/plugins/bluez5/telephony.h +++ b/spa/plugins/bluez5/telephony.h @@ -33,6 +33,7 @@ struct spa_bt_telephony { struct spa_bt_telephony_ag_transport { int8_t codec; enum spa_bt_transport_state state; + dbus_bool_t rejectSCO; }; struct spa_bt_telephony_ag { @@ -72,6 +73,8 @@ struct spa_bt_telephony_ag_callbacks { void (*hangup_all)(void *data, enum spa_bt_telephony_error *err); void (*create_multiparty)(void *data, enum spa_bt_telephony_error *err); void (*send_tones)(void *data, const char *tones, enum spa_bt_telephony_error *err); + + void (*transport_activate)(void *data, enum spa_bt_telephony_error *err); }; struct spa_bt_telephony_call_callbacks {