From 4a9a985c9dca7aeb650c24e7bd395d58846062b7 Mon Sep 17 00:00:00 2001 From: Andrzej Zaborowski Date: Thu, 26 Aug 2010 13:36:47 +0200 Subject: [PATCH] voicecall: add __ofono_voicecall_dial internal api The callback is only invoked when the call is CONNECTED (becomes active). Cancel ensures that the callback will not be called and does nothing more in this version, it's an open question if we want to release the call. --- src/ofono.h | 19 ++- src/voicecall.c | 328 ++++++++++++++++++++++++++++++++++++++++-------- 2 files changed, 296 insertions(+), 51 deletions(-) diff --git a/src/ofono.h b/src/ofono.h index 4a42f0c4..656d0d71 100644 --- a/src/ofono.h +++ b/src/ofono.h @@ -169,11 +169,28 @@ void __ofono_atom_free(struct ofono_atom *atom); #include #include #include -#include #include #include #include +#include + +enum ofono_voicecall_interaction { + OFONO_VOICECALL_INTERACTION_NONE = 0, + OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD = 1, + OFONO_VOICECALL_INTERACTION_DISCONNECT = 2, +}; + +typedef void (*ofono_voicecall_dial_cb_t)(struct ofono_call *call, void *data); + +ofono_bool_t __ofono_voicecall_busy(struct ofono_voicecall *vc); +int __ofono_voicecall_dial(struct ofono_voicecall *vc, + const char *addr, int addr_type, + const char *message, unsigned char icon_id, + enum ofono_voicecall_interaction interaction, + ofono_voicecall_dial_cb_t cb, void *user_data); +void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc); + #include struct sms; diff --git a/src/voicecall.c b/src/voicecall.c index e62c4347..9199ab6c 100644 --- a/src/voicecall.c +++ b/src/voicecall.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -57,6 +58,7 @@ struct ofono_voicecall { const struct ofono_voicecall_driver *driver; void *driver_data; struct ofono_atom *atom; + struct dial_request *dial_req; }; struct voicecall { @@ -64,6 +66,20 @@ struct voicecall { struct ofono_voicecall *vc; time_t start_time; time_t detect_time; + const char *message; + uint8_t icon_id; + gboolean untracked; +}; + +struct dial_request { + struct ofono_voicecall *vc; + const char *message; + uint8_t icon_id; + enum ofono_voicecall_interaction interaction; + ofono_voicecall_dial_cb_t cb; + void *user_data; + struct voicecall *call; + struct ofono_phone_number ph; }; static const char *default_en_list[] = { "911", "112", NULL }; @@ -235,6 +251,14 @@ static void append_voicecall_properties(struct voicecall *v, mpty = FALSE; ofono_dbus_dict_append(dict, "Multiparty", DBUS_TYPE_BOOLEAN, &mpty); + + if (v->message) + ofono_dbus_dict_append(dict, "Information", DBUS_TYPE_STRING, + &v->message); + + if (v->icon_id) + ofono_dbus_dict_append(dict, "Icon", DBUS_TYPE_BYTE, + &v->icon_id); } static DBusMessage *voicecall_get_properties(DBusConnection *conn, @@ -529,6 +553,32 @@ static void voicecall_set_call_status(struct voicecall *call, OFONO_VOICECALL_INTERFACE, "StartTime", DBUS_TYPE_STRING, ×tr); + + if (call->vc->dial_req && call == call->vc->dial_req->call) { + struct dial_request *req = call->vc->dial_req; + + if (req->cb) + req->cb(call->call, req->user_data); + + /* + * TODO: parse the called number for DTMF tones to be + * sent after call becomes active, and send them. + */ + + call->vc->dial_req = NULL; + g_free(req); + } + } + + if (status == CALL_STATUS_DISCONNECTED && call->vc->dial_req && + call == call->vc->dial_req->call) { + struct dial_request *req = call->vc->dial_req; + + if (req->cb) + req->cb(NULL, req->user_data); + + call->vc->dial_req = NULL; + g_free(req); } } @@ -906,10 +956,9 @@ static ofono_bool_t clir_string_to_clir(const char *clirstr, } static struct ofono_call *synthesize_outgoing_call(struct ofono_voicecall *vc, - DBusMessage *msg) + const char *number) { struct ofono_modem *modem = __ofono_atom_get_modem(vc->atom); - const char *number; struct ofono_call *call; call = g_try_new0(struct ofono_call, 1); @@ -927,10 +976,7 @@ static struct ofono_call *synthesize_outgoing_call(struct ofono_voicecall *vc, __ofono_modem_callid_hold(modem, call->id); - if (dbus_message_get_args(msg, NULL, DBUS_TYPE_STRING, &number, - DBUS_TYPE_INVALID) == FALSE) - number = ""; - else + if (number) string_to_phone_number(number, &call->phone_number); call->direction = CALL_DIRECTION_MOBILE_ORIGINATED; @@ -940,70 +986,80 @@ static struct ofono_call *synthesize_outgoing_call(struct ofono_voicecall *vc, return call; } -static void dial_callback(const struct ofono_error *error, void *data) +static struct voicecall *dial_handle_result(struct ofono_voicecall *vc, + const struct ofono_error *error, + const char *number, + gboolean *need_to_emit) { - struct ofono_voicecall *vc = data; - DBusMessage *reply; GSList *l; struct voicecall *v; struct ofono_call *call; - const char *path; - gboolean need_to_emit = FALSE; + + *need_to_emit = FALSE; if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { DBG("Dial callback returned error: %s", telephony_error_to_str(error)); - reply = __ofono_error_failed(vc->pending); - __ofono_dbus_pending_reply(&vc->pending, reply); - return; + + return NULL; } /* Two things can happen, the call notification arrived before dial * callback or dial callback was first. Handle here */ for (l = vc->call_list; l; l = l->next) { - struct voicecall *v = l->data; + v = l->data; if (v->call->status == CALL_STATUS_DIALING || v->call->status == CALL_STATUS_ALERTING || v->call->status == CALL_STATUS_ACTIVE) - break; + return v; } - if (!l) { - call = synthesize_outgoing_call(vc, vc->pending); + call = synthesize_outgoing_call(vc, number); + if (!call) + return NULL; - if (!call) { - __ofono_dbus_pending_reply(&vc->pending, - __ofono_error_failed(vc->pending)); - return; - } + v = voicecall_create(vc, call); + if (!v) + return NULL; - v = voicecall_create(vc, call); + v->detect_time = time(NULL); - if (!v) { - __ofono_dbus_pending_reply(&vc->pending, - __ofono_error_failed(vc->pending)); - return; - } + DBG("Registering new call: %d", call->id); + voicecall_dbus_register(v); - v->detect_time = time(NULL); + vc->call_list = g_slist_insert_sorted(vc->call_list, v, + call_compare); - DBG("Registering new call: %d", call->id); - voicecall_dbus_register(v); + *need_to_emit = TRUE; - vc->call_list = g_slist_insert_sorted(vc->call_list, v, - call_compare); + return v; +} - need_to_emit = TRUE; - } else { - v = l->data; - call = v->call; - } +static void manager_dial_callback(const struct ofono_error *error, void *data) +{ + struct ofono_voicecall *vc = data; + DBusMessage *reply; + const char *number; + gboolean need_to_emit; + struct voicecall *v; + + if (dbus_message_get_args(vc->pending, NULL, DBUS_TYPE_STRING, &number, + DBUS_TYPE_INVALID) == FALSE) + number = NULL; + + v = dial_handle_result(vc, error, number, &need_to_emit); + + if (v) { + const char *path = voicecall_build_path(vc, v->call); + + reply = dbus_message_new_method_return(vc->pending); + + dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, + DBUS_TYPE_INVALID); + } else + reply = __ofono_error_failed(vc->pending); - reply = dbus_message_new_method_return(vc->pending); - path = voicecall_build_path(vc, call); - dbus_message_append_args(reply, DBUS_TYPE_OBJECT_PATH, &path, - DBUS_TYPE_INVALID); __ofono_dbus_pending_reply(&vc->pending, reply); if (need_to_emit) @@ -1054,7 +1110,7 @@ static DBusMessage *manager_dial(DBusConnection *conn, string_to_phone_number(number, &ph); vc->driver->dial(vc, &ph, clir, OFONO_CUG_OPTION_DEFAULT, - dial_callback, vc); + manager_dial_callback, vc); return NULL; } @@ -1644,12 +1700,14 @@ void ofono_voicecall_disconnected(struct ofono_voicecall *vc, int id, voicecall_set_call_status(call, CALL_STATUS_DISCONNECTED); - if (prev_status == CALL_STATUS_INCOMING || - prev_status == CALL_STATUS_WAITING) - __ofono_history_call_missed(modem, call->call, ts); - else - __ofono_history_call_ended(modem, call->call, - call->detect_time, ts); + if (!call->untracked) { + if (prev_status == CALL_STATUS_INCOMING || + prev_status == CALL_STATUS_WAITING) + __ofono_history_call_missed(modem, call->call, ts); + else + __ofono_history_call_ended(modem, call->call, + call->detect_time, ts); + } voicecalls_emit_call_removed(vc, call); @@ -2095,3 +2153,173 @@ int ofono_voicecall_get_next_callid(struct ofono_voicecall *vc) return __ofono_modem_callid_next(modem); } + +ofono_bool_t __ofono_voicecall_busy(struct ofono_voicecall *vc) +{ + return vc->dial_req || vc->pending || voicecalls_have_active(vc) || + g_slist_length(vc->call_list) >= MAX_VOICE_CALLS || + voicecalls_have_incoming(vc) || + voicecalls_num_connecting(vc) > 0; +} + +static void dial_request_cb(const struct ofono_error *error, void *data) +{ + struct ofono_voicecall *vc = data; + gboolean need_to_emit; + struct voicecall *v; + + v = dial_handle_result(vc, error, + phone_number_to_string(&vc->dial_req->ph), + &need_to_emit); + + if (v) { + v->message = vc->dial_req->message; + v->icon_id = vc->dial_req->icon_id; + + /* + * TS 102 223 Section 6.4.13: The terminal shall not store + * in the UICC the call set-up details (called party number + * and associated parameters) + */ + v->untracked = TRUE; + } + + if (need_to_emit) + voicecalls_emit_call_added(vc, v); + + if (vc->dial_req->cb) { + if (v && v->call->status != CALL_STATUS_ACTIVE) { + vc->dial_req->call = v; + + /* Wait for the call connected message */ + return; + } + + vc->dial_req->cb(v ? v->call : NULL, vc->dial_req->user_data); + } + + g_free(vc->dial_req); + vc->dial_req = NULL; +} + +static int dial_request(struct ofono_voicecall *vc) +{ + if (g_slist_length(vc->call_list) >= MAX_VOICE_CALLS || + voicecalls_have_incoming(vc) || + voicecalls_num_connecting(vc) > 0 || + vc->pending) { + g_free(vc->dial_req); + vc->dial_req = NULL; + + return -EBUSY; + } + + vc->driver->dial(vc, &vc->dial_req->ph, OFONO_CLIR_OPTION_DEFAULT, + OFONO_CUG_OPTION_DEFAULT, dial_request_cb, vc); + + return 0; +} + +static void hold_or_disconnect_cb(const struct ofono_error *error, void *data) +{ + struct ofono_voicecall *vc = data; + ofono_voicecall_dial_cb_t cb = vc->dial_req->cb; + void *user_data = vc->dial_req->user_data; + int err; + + if (!cb) { + g_free(vc->dial_req); + vc->dial_req = NULL; + + return; + } + + if (error->type != OFONO_ERROR_TYPE_NO_ERROR) { + cb(FALSE, user_data); + + g_free(vc->dial_req); + vc->dial_req = NULL; + + return; + } + + err = dial_request(vc); + + if (err < 0) + cb(FALSE, user_data); +} + +int __ofono_voicecall_dial(struct ofono_voicecall *vc, + const char *addr, int addr_type, + const char *message, unsigned char icon_id, + enum ofono_voicecall_interaction interaction, + ofono_voicecall_dial_cb_t cb, void *user_data) +{ + struct dial_request *req; + + if (!valid_phone_number_format(addr)) + return -EINVAL; + + if (!vc->driver->dial) + return -ENOSYS; + + if (vc->dial_req || vc->pending) + return -EBUSY; + + /* + * TODO: if addr starts with "112", possibly translate into the + * technology-specific emergency number. + */ + + req = g_try_new0(struct dial_request, 1); + req->message = message; + req->icon_id = icon_id; + req->interaction = interaction; + req->cb = cb; + req->user_data = user_data; + + /* TODO: parse the tones to dial after call connected */ + req->ph.type = addr_type; + strncpy(req->ph.number, addr, 20); + + if (!voicecalls_have_active(vc)) { + vc->dial_req = req; + + return dial_request(vc); + } + + switch (interaction) { + case OFONO_VOICECALL_INTERACTION_NONE: + return -EBUSY; + + case OFONO_VOICECALL_INTERACTION_PUT_ON_HOLD: + /* Note: dialling automatically puts active calls on hold */ + vc->dial_req = req; + + return dial_request(vc); + + case OFONO_VOICECALL_INTERACTION_DISCONNECT: + if (!vc->driver->release_all_active) { + g_free(req); + + return -ENOSYS; + } + + vc->driver->release_all_active(vc, hold_or_disconnect_cb, vc); + break; + } + + vc->dial_req = req; + + return 0; +} + +void __ofono_voicecall_dial_cancel(struct ofono_voicecall *vc) +{ + if (!vc->dial_req || !vc->dial_req->cb) + return; + + vc->dial_req->cb = NULL; + + /* XXX: If vc->dial_req->call is set, try to release it? */ +}