diff options
Diffstat (limited to 'src/voicecall.c')
-rw-r--r-- | src/voicecall.c | 328 |
1 files changed, 278 insertions, 50 deletions
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 <stdio.h> #include <time.h> #include <errno.h> +#include <stdint.h> #include <glib.h> #include <gdbus.h> @@ -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? */ +} |