bluez5: telephony: implement error reporting from all methods

This commit is contained in:
George Kiagiadakis 2024-10-01 18:14:29 +03:00 committed by Wim Taymans
parent 159846d226
commit 03ab9dd771
3 changed files with 267 additions and 124 deletions

View file

@ -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 = {

View file

@ -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)

View file

@ -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,