From 03ab9dd771da67349d878753fafad5dc3e30102c Mon Sep 17 00:00:00 2001 From: George Kiagiadakis Date: Tue, 1 Oct 2024 18:14:29 +0300 Subject: [PATCH] bluez5: telephony: implement error reporting from all methods --- spa/plugins/bluez5/backend-native.c | 145 +++++++++++------- spa/plugins/bluez5/telephony.c | 218 ++++++++++++++++++++-------- spa/plugins/bluez5/telephony.h | 28 ++-- 3 files changed, 267 insertions(+), 124 deletions(-) diff --git a/spa/plugins/bluez5/backend-native.c b/spa/plugins/bluez5/backend-native.c index 6423866b4..af2d29581 100644 --- a/spa/plugins/bluez5/backend-native.c +++ b/spa/plugins/bluez5/backend-native.c @@ -1311,25 +1311,30 @@ static bool hfp_hf_wait_for_reply(struct rfcomm *rfcomm, char *buf, size_t len) return reply_found; } -static int hfp_hf_answer(void *data) { +static void hfp_hf_answer(void *data, enum spa_bt_telephony_error *err) +{ struct rfcomm_call_data *call_data = telephony_call_get_user_data(data); struct rfcomm *rfcomm = call_data->rfcomm; struct impl *backend = rfcomm->backend; char reply[20]; - if (call_data->call->state != CALL_STATE_INCOMING) - return -1; + if (call_data->call->state != CALL_STATE_INCOMING) { + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; + } rfcomm_send_cmd(rfcomm, "ATA"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to answer call"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } -static int hfp_hf_hangup(void *data) { +static void hfp_hf_hangup(void *data, enum spa_bt_telephony_error *err) +{ struct rfcomm_call_data *call_data = telephony_call_get_user_data(data); struct rfcomm *rfcomm = call_data->rfcomm; struct impl *backend = rfcomm->backend; @@ -1341,15 +1346,17 @@ static int hfp_hf_hangup(void *data) { rfcomm_send_cmd(rfcomm, "AT+CHLD=0"); } else { spa_log_info(backend->log, "Call not incoming, waiting or active: skip hangup"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to hangup call"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } static const struct spa_bt_telephony_call_events telephony_call_events = { @@ -1365,6 +1372,8 @@ static struct spa_bt_telephony_call *hfp_hf_add_call(struct rfcomm *rfcomm, stru struct rfcomm_call_data *data; call = telephony_call_new(ag, sizeof(*data)); + if (!call) + return NULL; call->state = state; if (number) call->line_identification = strdup(number); @@ -1377,7 +1386,7 @@ static struct spa_bt_telephony_call *hfp_hf_add_call(struct rfcomm *rfcomm, stru return call; } -static int hfp_hf_dial(void *data, const char *number) +static void hfp_hf_dial(void *data, const char *number, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1388,17 +1397,14 @@ static int hfp_hf_dial(void *data, const char *number) if (hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) && spa_strstartswith(reply, "OK")) { struct spa_bt_telephony_call *call; call = hfp_hf_add_call(rfcomm, rfcomm->telephony_ag, CALL_STATE_DIALING, number); - if (!call) - return -1; + *err = call ? BT_TELEPHONY_ERROR_NONE : BT_TELEPHONY_ERROR_FAILED; } else { spa_log_info(backend->log, "Failed to dial: \"%s\"", number); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; } - - return 0; } -static int hfp_hf_swap_calls(void *data) +static void hfp_hf_swap_calls(void *data, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1407,13 +1413,16 @@ static int hfp_hf_swap_calls(void *data) bool found_held = false; char reply[20]; - if (!rfcomm->chld_supported) - return -1; + if (!rfcomm->chld_supported) { + *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + return; + } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_WAITING) { spa_log_debug(backend->log, "call waiting before swapping"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } else if (call->state == CALL_STATE_ACTIVE) found_active = true; else if (call->state == CALL_STATE_HELD) @@ -1422,13 +1431,15 @@ static int hfp_hf_swap_calls(void *data) if (!found_active || !found_held) { spa_log_debug(backend->log, "no active and held calls"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } rfcomm_send_cmd(rfcomm, "AT+CHLD=2"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to swap calls"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { @@ -1445,10 +1456,10 @@ static int hfp_hf_swap_calls(void *data) telephony_call_notify_updated_props(call); } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } -static int hfp_hf_release_and_answer(void *data) +static void hfp_hf_release_and_answer(void *data, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1457,8 +1468,10 @@ static int hfp_hf_release_and_answer(void *data) bool found_waiting = false; char reply[20]; - if (!rfcomm->chld_supported) - return -1; + if (!rfcomm->chld_supported) { + *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + return; + } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_ACTIVE) @@ -1469,13 +1482,15 @@ static int hfp_hf_release_and_answer(void *data) if (!found_active || !found_waiting) { spa_log_debug(backend->log, "no active and waiting calls"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to release and answer calls"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) { @@ -1489,10 +1504,10 @@ static int hfp_hf_release_and_answer(void *data) } } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } -static int hfp_hf_release_and_swap(void *data) +static void hfp_hf_release_and_swap(void *data, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1501,13 +1516,16 @@ static int hfp_hf_release_and_swap(void *data) bool found_held = false; char reply[20]; - if (!rfcomm->chld_supported) - return -1; + if (!rfcomm->chld_supported) { + *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + return; + } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_WAITING) { spa_log_debug(backend->log, "call waiting before release and swap"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } else if (call->state == CALL_STATE_ACTIVE) found_active = true; else if (call->state == CALL_STATE_HELD) @@ -1516,13 +1534,15 @@ static int hfp_hf_release_and_swap(void *data) if (!found_active || !found_held) { spa_log_debug(backend->log, "no active and held calls"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } rfcomm_send_cmd(rfcomm, "AT+CHLD=1"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to release and swap calls"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } spa_list_for_each_safe(call, tcall, &rfcomm->telephony_ag->call_list, link) { @@ -1536,10 +1556,10 @@ static int hfp_hf_release_and_swap(void *data) } } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } -static int hfp_hf_hold_and_answer(void *data) +static void hfp_hf_hold_and_answer(void *data, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1548,8 +1568,10 @@ static int hfp_hf_hold_and_answer(void *data) bool found_waiting = false; char reply[20]; - if (!rfcomm->chld_supported) - return -1; + if (!rfcomm->chld_supported) { + *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + return; + } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_ACTIVE) @@ -1560,13 +1582,15 @@ static int hfp_hf_hold_and_answer(void *data) if (!found_active || !found_waiting) { spa_log_debug(backend->log, "no active and waiting calls"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } rfcomm_send_cmd(rfcomm, "AT+CHLD=2"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to hold and answer calls"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { @@ -1583,22 +1607,23 @@ static int hfp_hf_hold_and_answer(void *data) telephony_call_notify_updated_props(call); } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } -static int hfp_hf_hangup_all(void *data) +static void hfp_hf_hangup_all(void *data, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; char reply[20]; - int ret = 0; + + *err = BT_TELEPHONY_ERROR_NONE; /* Hangup held calls */ if (rfcomm->chld_supported) { rfcomm_send_cmd(rfcomm, "AT+CHLD=0"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to hangup held calls"); - ret = -1; + *err = BT_TELEPHONY_ERROR_FAILED; } } @@ -1606,13 +1631,11 @@ static int hfp_hf_hangup_all(void *data) rfcomm_send_cmd(rfcomm, "AT+CHUP"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to hangup active calls"); - ret = -1; + *err = BT_TELEPHONY_ERROR_FAILED; } - - return ret; } -static int hfp_hf_create_multiparty(void *data) +static void hfp_hf_create_multiparty(void *data, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1621,10 +1644,16 @@ static int hfp_hf_create_multiparty(void *data) bool found_held = false; char reply[20]; + if (!rfcomm->chld_supported) { + *err = BT_TELEPHONY_ERROR_NOT_SUPPORTED; + return; + } + spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { if (call->state == CALL_STATE_WAITING) { spa_log_debug(backend->log, "call waiting before creating multiparty"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } else if (call->state == CALL_STATE_ACTIVE) found_active = true; else if (call->state == CALL_STATE_HELD) @@ -1633,13 +1662,15 @@ static int hfp_hf_create_multiparty(void *data) if (!found_active || !found_held) { spa_log_debug(backend->log, "no active and held calls"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } rfcomm_send_cmd(rfcomm, "AT+CHLD=3"); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to create multiparty"); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } spa_list_for_each(call, &rfcomm->telephony_ag->call_list, link) { @@ -1657,10 +1688,10 @@ static int hfp_hf_create_multiparty(void *data) telephony_call_notify_updated_props(call); } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } -static int hfp_hf_send_tones(void *data, const char *tones) +static void hfp_hf_send_tones(void *data, const char *tones, enum spa_bt_telephony_error *err) { struct rfcomm *rfcomm = data; struct impl *backend = rfcomm->backend; @@ -1677,16 +1708,18 @@ static int hfp_hf_send_tones(void *data, const char *tones) if (!found) { spa_log_debug(backend->log, "no active call"); - return -1; + *err = BT_TELEPHONY_ERROR_INVALID_STATE; + return; } rfcomm_send_cmd(rfcomm, "AT+VTS=%s", tones); if (!hfp_hf_wait_for_reply(rfcomm, reply, sizeof(reply)) || !spa_strstartswith(reply, "OK")) { spa_log_info(backend->log, "Failed to send tones: %s", tones); - return -1; + *err = BT_TELEPHONY_ERROR_FAILED; + return; } - return 0; + *err = BT_TELEPHONY_ERROR_NONE; } static const struct spa_bt_telephony_ag_events telephony_ag_events = { diff --git a/spa/plugins/bluez5/telephony.c b/spa/plugins/bluez5/telephony.c index b68fe621e..1dcffbd7c 100644 --- a/spa/plugins/bluez5/telephony.c +++ b/spa/plugins/bluez5/telephony.c @@ -211,22 +211,59 @@ struct callimpl { } prev; }; -#define ag_emit(ag,m,v,...) spa_hook_list_call(&ag->listener_list, struct spa_bt_telephony_ag_events, m, v, ##__VA_ARGS__) -#define ag_emit_dial(s,n) ag_emit(s,dial,0,n) -#define ag_emit_swap_calls(s) ag_emit(s,swap_calls,0) -#define ag_emit_release_and_answer(s) ag_emit(s,release_and_answer,0) -#define ag_emit_release_and_swap(s) ag_emit(s,release_and_swap,0) -#define ag_emit_hold_and_answer(s) ag_emit(s,hold_and_answer,0) -#define ag_emit_hangup_all(s) ag_emit(s,hangup_all,0) -#define ag_emit_create_multiparty(s) ag_emit(s,create_multiparty,0) -#define ag_emit_send_tones(s,t) ag_emit(s,send_tones,0,t) +#define ag_emit(ag,m,v,...) spa_hook_list_call_once(&ag->listener_list, struct spa_bt_telephony_ag_events, m, v, ##__VA_ARGS__) +#define ag_emit_dial(s,n,e) ag_emit(s,dial,0,n,e) +#define ag_emit_swap_calls(s,e) ag_emit(s,swap_calls,0,e) +#define ag_emit_release_and_answer(s,e) ag_emit(s,release_and_answer,0,e) +#define ag_emit_release_and_swap(s,e) ag_emit(s,release_and_swap,0,e) +#define ag_emit_hold_and_answer(s,e) ag_emit(s,hold_and_answer,0,e) +#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 call_emit(c,m,v,...) spa_hook_list_call(&c->listener_list, struct spa_bt_telephony_call_events, m, v, ##__VA_ARGS__) -#define call_emit_answer(s) call_emit(s,answer,0) -#define call_emit_hangup(s) call_emit(s,hangup,0) +#define call_emit(c,m,v,...) spa_hook_list_call_once(&c->listener_list, struct spa_bt_telephony_call_events, m, v, ##__VA_ARGS__) +#define call_emit_answer(s,e) call_emit(s,answer,0,e) +#define call_emit_hangup(s,e) call_emit(s,hangup,0,e) static void dbus_iter_append_call_properties(DBusMessageIter *i, struct spa_bt_telephony_call *call, bool all); +#define PW_TELEPHONY_ERROR_FAILED "org.freedesktop.PipeWire.Telephony.Error.Failed" +#define PW_TELEPHONY_ERROR_NOT_SUPPORTED "org.freedesktop.PipeWire.Telephony.Error.NotSupported" +#define PW_TELEPHONY_ERROR_INVALID_FORMAT "org.freedesktop.PipeWire.Telephony.Error.InvalidFormat" +#define PW_TELEPHONY_ERROR_INVALID_STATE "org.freedesktop.PipeWire.Telephony.Error.InvalidState" + +static const char *telephony_error_to_dbus (enum spa_bt_telephony_error err) +{ + switch (err) { + case BT_TELEPHONY_ERROR_FAILED: + return PW_TELEPHONY_ERROR_FAILED; + case BT_TELEPHONY_ERROR_NOT_SUPPORTED: + return PW_TELEPHONY_ERROR_NOT_SUPPORTED; + case BT_TELEPHONY_ERROR_INVALID_FORMAT: + return PW_TELEPHONY_ERROR_INVALID_FORMAT; + case BT_TELEPHONY_ERROR_INVALID_STATE: + return PW_TELEPHONY_ERROR_INVALID_STATE; + default: + return ""; + } +} + +static const char *telephony_error_to_description (enum spa_bt_telephony_error err) +{ + switch (err) { + case BT_TELEPHONY_ERROR_FAILED: + return "Method call failed"; + case BT_TELEPHONY_ERROR_NOT_SUPPORTED: + return "Method is not supported on this Audio Gateway"; + case BT_TELEPHONY_ERROR_INVALID_FORMAT: + return "Invalid phone number or tones"; + case BT_TELEPHONY_ERROR_INVALID_STATE: + return "The current state does not allow this method call"; + default: + return ""; + } +} + #define find_free_object_id(list, obj_type, link) \ ({ \ int id = 0; \ @@ -472,10 +509,41 @@ static DBusMessage *ag_get_managed_objects(struct agimpl *agimpl, DBusMessage *m return spa_steal_ptr(r); } +static bool validate_phone_number(const char *number) +{ + const char *c; + int count = 0; + + if (!number) + return false; + for (c = number; *c != '\0'; c++) { + if (!(*c >= '0' && *c <= '9') && !(*c >= 'A' && *c <= 'D') && + *c != '#' && *c != '*' && *c != '+' && *c != ',' ) + return false; + count++; + } + if (count < 1 || count > 80) + return false; + return true; +} + +static bool validate_tones(const char *tones) +{ + const char *c; + if (!tones) + return false; + for (c = tones; *c != '\0'; c++) { + if (!(*c >= '0' && *c <= '9') && !(*c >= 'A' && *c <= 'D') && + *c != '#' && *c != '*') + return false; + } + return true; +} + static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) { - const char *number = NULL, *c; - int count = 0; + const char *number = NULL; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; spa_autoptr(DBusMessage) r = NULL; if (!dbus_message_get_args(m, NULL, @@ -483,23 +551,22 @@ static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) DBUS_TYPE_INVALID)) return NULL; - /* validate number */ - if (!number) - goto invalid_argument; - for (c = number; *c != '\0'; c++) { - if (!(*c >= '0' && *c <= '9') && !(*c >= 'A' && *c <= 'D') && - *c != '#' && *c != '*' && *c != '+' && *c != ',' ) - goto invalid_argument; - count++; + if (!validate_phone_number(number)) { + err = BT_TELEPHONY_ERROR_INVALID_FORMAT; + goto failed; } - if (count < 1 || count > 80) - goto invalid_argument; agimpl->dial_in_progress = true; - ag_emit_dial(agimpl, number); + if (!ag_emit_dial(agimpl, number, &err)) { + agimpl->dial_in_progress = false; + goto failed; + } agimpl->dial_in_progress = false; if (!agimpl->dial_return || !agimpl->dial_return->path) + err = BT_TELEPHONY_ERROR_FAILED; + + if (err != BT_TELEPHONY_ERROR_NONE) goto failed; if ((r = dbus_message_new_method_return(m)) == NULL) @@ -512,43 +579,64 @@ static DBusMessage *ag_dial(struct agimpl *agimpl, DBusMessage *m) return spa_steal_ptr(r); -invalid_argument: - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "Dial number is not a valid phone number"); - failed: - return dbus_message_new_error(m, DBUS_ERROR_FAILED, - "Dial did not create a new Call object"); + return dbus_message_new_error(m, telephony_error_to_dbus (err), + telephony_error_to_description (err)); } static DBusMessage *ag_swap_calls(struct agimpl *agimpl, DBusMessage *m) { - ag_emit_swap_calls(agimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (ag_emit_swap_calls(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 DBusMessage *ag_release_and_answer(struct agimpl *agimpl, DBusMessage *m) { - ag_emit_release_and_answer(agimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (ag_emit_release_and_answer(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 DBusMessage *ag_release_and_swap(struct agimpl *agimpl, DBusMessage *m) { - ag_emit_release_and_swap(agimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (ag_emit_release_and_swap(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 DBusMessage *ag_hold_and_answer(struct agimpl *agimpl, DBusMessage *m) { - ag_emit_hold_and_answer(agimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (ag_emit_hold_and_answer(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 DBusMessage *ag_hangup_all(struct agimpl *agimpl, DBusMessage *m) { - ag_emit_hangup_all(agimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (ag_emit_hangup_all(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 DBusMessage *ag_create_multiparty(struct agimpl *agimpl, DBusMessage *m) @@ -556,8 +644,10 @@ static DBusMessage *ag_create_multiparty(struct agimpl *agimpl, DBusMessage *m) struct callimpl *callimpl; spa_autoptr(DBusMessage) r = NULL; DBusMessageIter i, oi; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; - ag_emit_create_multiparty(agimpl); + if (!ag_emit_create_multiparty(agimpl, &err) || err != BT_TELEPHONY_ERROR_NONE) + goto failed; if ((r = dbus_message_new_method_return(m)) == NULL) return NULL; @@ -572,31 +662,33 @@ static DBusMessage *ag_create_multiparty(struct agimpl *agimpl, DBusMessage *m) } dbus_message_iter_close_container(&i, &oi); return spa_steal_ptr(r); + +failed: + return dbus_message_new_error(m, telephony_error_to_dbus (err), + telephony_error_to_description (err)); } static DBusMessage *ag_send_tones(struct agimpl *agimpl, DBusMessage *m) { - const char *tones = NULL, *c; + const char *tones = NULL; + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; if (!dbus_message_get_args(m, NULL, DBUS_TYPE_STRING, &tones, DBUS_TYPE_INVALID)) return NULL; - if (!tones) - goto invalid_argument; - for (c = tones; *c != '\0'; c++) { - if (!(*c >= '0' && *c <= '9') && !(*c >= 'A' && *c <= 'D') && - *c != '#' && *c != '*') - goto invalid_argument; + if (!validate_tones(tones)) { + err = BT_TELEPHONY_ERROR_INVALID_FORMAT; + goto failed; } - ag_emit_send_tones(agimpl, tones); - return dbus_message_new_method_return(m); + if (ag_emit_send_tones(agimpl, tones, &err) && err == BT_TELEPHONY_ERROR_NONE) + return dbus_message_new_method_return(m); -invalid_argument: - return dbus_message_new_error(m, DBUS_ERROR_INVALID_ARGS, - "SendTones argument is not a valid DTMF tones string"); +failed: + 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) @@ -1097,14 +1189,24 @@ static DBusMessage *call_properties_set(struct callimpl *callimpl, DBusMessage * static DBusMessage *call_answer(struct callimpl *callimpl, DBusMessage *m) { - call_emit_answer(callimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (call_emit_answer(callimpl, &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 DBusMessage *call_hangup(struct callimpl *callimpl, DBusMessage *m) { - call_emit_hangup(callimpl); - return dbus_message_new_method_return(m); + enum spa_bt_telephony_error err = BT_TELEPHONY_ERROR_FAILED; + + if (call_emit_hangup(callimpl, &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 call_handler(DBusConnection *c, DBusMessage *m, void *userdata) diff --git a/spa/plugins/bluez5/telephony.h b/spa/plugins/bluez5/telephony.h index 613167e08..8ffa605ad 100644 --- a/spa/plugins/bluez5/telephony.h +++ b/spa/plugins/bluez5/telephony.h @@ -7,6 +7,14 @@ #include "defs.h" +enum spa_bt_telephony_error { + BT_TELEPHONY_ERROR_NONE = 0, + BT_TELEPHONY_ERROR_FAILED, + BT_TELEPHONY_ERROR_NOT_SUPPORTED, + BT_TELEPHONY_ERROR_INVALID_FORMAT, + BT_TELEPHONY_ERROR_INVALID_STATE, +}; + enum spa_bt_telephony_call_state { CALL_STATE_ACTIVE, CALL_STATE_HELD, @@ -42,22 +50,22 @@ struct spa_bt_telephony_ag_events { #define SPA_VERSION_BT_TELEPHONY_AG_EVENTS 0 uint32_t version; - int (*dial)(void *data, const char *number); - int (*swap_calls)(void *data); - int (*release_and_answer)(void *data); - int (*release_and_swap)(void *data); - int (*hold_and_answer)(void *data); - int (*hangup_all)(void *data); - int (*create_multiparty)(void *data); - int (*send_tones)(void *data, const char *tones); + void (*dial)(void *data, const char *number, enum spa_bt_telephony_error *err); + void (*swap_calls)(void *data, enum spa_bt_telephony_error *err); + void (*release_and_answer)(void *data, enum spa_bt_telephony_error *err); + void (*release_and_swap)(void *data, enum spa_bt_telephony_error *err); + void (*hold_and_answer)(void *data, enum spa_bt_telephony_error *err); + 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); }; struct spa_bt_telephony_call_events { #define SPA_VERSION_BT_TELEPHONY_CALL_EVENTS 0 uint32_t version; - int (*answer)(void *data); - int (*hangup)(void *data); + void (*answer)(void *data, enum spa_bt_telephony_error *err); + void (*hangup)(void *data, enum spa_bt_telephony_error *err); }; struct spa_bt_telephony *telephony_new(struct spa_log *log, struct spa_dbus *dbus,