#include <stdio.h>
#include <time.h>
#include <errno.h>
+#include <stdint.h>
#include <glib.h>
#include <gdbus.h>
const struct ofono_voicecall_driver *driver;
void *driver_data;
struct ofono_atom *atom;
+ struct dial_request *dial_req;
};
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 };
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,
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);
}
}
}
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);
__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;
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)
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;
}
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);
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? */
+}