diff --git a/Makefile b/Makefile index c9eebef..bf7562b 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,8 @@ SRC = \ qti_ims.c \ qti_plugin.c \ qti_slot.c \ - qti_radio_ext.c + qti_radio_ext.c \ + qti_ims_call.c # # Directories diff --git a/src/qti_ims.c b/src/qti_ims.c index b25655d..04952f1 100644 --- a/src/qti_ims.c +++ b/src/qti_ims.c @@ -48,7 +48,8 @@ #include #define DBG(fmt, ...) \ - gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, fmt, ##__VA_ARGS__) + gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, "ims:"fmt, ##__VA_ARGS__) + typedef GObjectClass QtiImsClass; typedef struct qti_ims { @@ -118,6 +119,7 @@ void qti_ims_result_request_complete( QtiRadioExt* radio_ext, int result, + GBinderReader* reader, void* user_data) { QtiImsResultRequest* req = user_data; @@ -169,6 +171,41 @@ qti_ims_reg_status_changed( } } + +static +void +qti_ims_reg_status_response( + QtiRadioExt* radio_ext, + int result, + GBinderReader* reader, + void* user_data) +{ + DBG("qti_ims_reg_status_response"); + QtiImsResultRequest* req = user_data; + + QTI_RADIO_REG_STATE state = QTI_RADIO_REG_STATE_INVALID; + GBinderReader reader_copy; + + gbinder_reader_copy(&reader_copy, reader); + const QtiRadioRegInfo* info = qti_radio_ext_read_ims_reg_status_info(radio_ext, &reader_copy); + + if (info) { + state = info->state; + } + + const char *uri = info->uri.data.str ? info->uri.data.str : ""; + const char *error_msg = info->error_message.data.str ? info->error_message.data.str : ""; + DBG("%s: QtiRadioRegInfo response state:%d radiotech:%d" + " error_code:%d\n" + " uri:%s error_msg:%s", + info->state, + info->radio_tech, + info->error_code, + uri, error_msg); + + qti_ims_reg_status_changed(radio_ext, state, req->ext); +} + /*==========================================================================* * BinderExtImsInterface *==========================================================================*/ @@ -250,6 +287,7 @@ qti_ims_iface_init( BinderExtImsInterface* iface) { iface->version = BINDER_EXT_IMS_INTERFACE_VERSION; + iface->flags = BINDER_EXT_IMS_INTERFACE_FLAG_VOICE_SUPPORT; iface->get_state = qti_ims_get_state; iface->set_registration = qti_ims_set_registration; iface->cancel = qti_ims_cancel; @@ -273,11 +311,18 @@ qti_ims_new( */ self->slot = g_strdup(slot); self->radio_ext = qti_radio_ext_ref(radio_ext); - self->ims_state = BINDER_EXT_IMS_STATE_NOT_REGISTERED; + self->ims_state = BINDER_EXT_IMS_STATE_UNKNOWN; if (self->radio_ext) { qti_radio_ext_add_ims_reg_status_handler(self->radio_ext, qti_ims_reg_status_changed, self); + + // get updated state + //QtiImsResultRequest* req = qti_ims_result_request_new(self, + // complete, destroy, user_data); + //qti_radio_ext_get_ims_reg_state(self->radio_ext, + // qti_ims_reg_status_response, + // qti_ims_result_request_destroy, NULL); } return BINDER_EXT_IMS(self); diff --git a/src/qti_ims_call.c b/src/qti_ims_call.c new file mode 100644 index 0000000..d5f8dae --- /dev/null +++ b/src/qti_ims_call.c @@ -0,0 +1,491 @@ +/* + * oFono - Open Source Telephony - binder based adaptation QTI plugin + * + * Copyright (C) 2022 Jolla Ltd. + * Copyright (C) 2024 TheKit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include + +#include "qti_ims_call.h" +#include "qti_radio_ext.h" +#include "qti_radio_ext_types.h" + +#include +#include + +#include + +#include +#include +#include +#include + +#define DBG(fmt, ...) \ + gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, "ims:"fmt, ##__VA_ARGS__) + +typedef GObjectClass QtiImsCallClass; +typedef struct qti_ims_call { + GObject parent; + GUtilIdlePool* pool; + QtiRadioExt* radio_ext; + GPtrArray* calls; + GHashTable* id_map; +} QtiImsCall; + +static +void +qti_ims_call_iface_init( + BinderExtCallInterface* iface); + +GType qti_ims_call_get_type() G_GNUC_INTERNAL; +G_DEFINE_TYPE_WITH_CODE(QtiImsCall, qti_ims_call, G_TYPE_OBJECT, +G_IMPLEMENT_INTERFACE(BINDER_EXT_TYPE_CALL, qti_ims_call_iface_init)) + +#define THIS_TYPE qti_ims_call_get_type() +#define THIS(obj) G_TYPE_CHECK_INSTANCE_CAST(obj, THIS_TYPE, QtiImsCall) +#define PARENT_CLASS qti_ims_call_parent_class + +#define ID_KEY(id) GUINT_TO_POINTER(id) +#define ID_VALUE(id) GUINT_TO_POINTER(id) + +typedef struct qti_ims_call_result_request { + int ref_count; + guint id; + guint id_mapped; + guint param; + BinderExtCall* ext; + BinderExtCallResultFunc complete; + GDestroyNotify destroy; + void* user_data; +} QtiImsCallResultRequest; + +enum qti_ims_call_signal { + SIGNAL_CALL_STATE_CHANGED, + SIGNAL_CALL_END, + SIGNAL_CALL_RING, + SIGNAL_CALL_SUPP_SVC_NOTIFY, + SIGNAL_COUNT +}; + +#define SIGNAL_CALL_STATE_CHANGED_NAME "qti-ims-call-state-changed" +#define SIGNAL_CALL_END_NAME "qti-ims-call-end" +#define SIGNAL_CALL_RING_NAME "qti-ims-call-ring" +#define SIGNAL_CALL_SUPP_SVC_NOTIFY_NAME "qti-ims-call-supp-svc-notify" + +static guint qti_ims_call_signals[SIGNAL_COUNT] = { 0 }; + +static +QtiImsCallResultRequest* +qti_ims_call_result_request_new( + BinderExtCall* ext, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + QtiImsCallResultRequest* req = + g_slice_new0(QtiImsCallResultRequest); + + req->ref_count = 1; + req->ext = binder_ext_call_ref(ext); + req->complete = complete; + req->destroy = destroy; + req->user_data = user_data; + return req; +} + +static +void +qti_ims_call_result_request_free( + QtiImsCallResultRequest* req) +{ + BinderExtCall* ext = req->ext; + + if (req->destroy) { + req->destroy(req->user_data); + } + if (req->id_mapped) { + g_hash_table_remove(THIS(ext)->id_map, ID_KEY(req->id_mapped)); + } + binder_ext_call_unref(ext); + gutil_slice_free(req); +} + +static +gboolean +qti_ims_call_result_request_unref( + QtiImsCallResultRequest* req) +{ + if (!--(req->ref_count)) { + qti_ims_call_result_request_free(req); + return TRUE; + } else { + return FALSE; + } +} + +static +void +qti_ims_call_result_request_destroy( + gpointer req) +{ + qti_ims_call_result_request_unref(req); +} + +/*==========================================================================* + * BinderExtCallInterface + *==========================================================================*/ + +// find call by id +static +BinderExtCallInfo* +qti_ims_call_info_find( + QtiImsCall* self, + guint call_id) +{ + for (int i = 0; i < self->calls->len; i++) { + BinderExtCallInfo* info = + (BinderExtCallInfo*) g_ptr_array_index(self->calls, i); + if (info->call_id == call_id) { + return info; + } + } + return NULL; +} + +static +void +qti_ims_call_handle_call_info( + QtiRadioExt* radio, + GPtrArray* updated_calls, + void* user_data) +{ + QtiImsCall* self = THIS(user_data); + + // loop over the updated calls + for (int i = 0; i < updated_calls->len; i++) { + BinderExtCallInfo* info = g_ptr_array_index(updated_calls, i); + BinderExtCallInfo* call = qti_ims_call_info_find(self, info->call_id); + + if (call->state == BINDER_EXT_CALL_STATE_END) { + g_signal_emit(THIS(user_data), + qti_ims_call_signals[SIGNAL_CALL_END], 0, call->call_id, ""); + + if (call) + g_ptr_array_remove(self->calls, call); + + continue; + } else if (call) { + // update the existing call + call->state = info->state; + } else { + // add a new call + g_ptr_array_add(self->calls, g_memdup(info, sizeof(BinderExtCallInfo))); + } + } + + g_signal_emit(THIS(user_data), + qti_ims_call_signals[SIGNAL_CALL_STATE_CHANGED], 0); +} + + +static +const BinderExtCallInfo* const* +qti_ims_call_get_calls( + BinderExtCall* ext) +{ + static const BinderExtCallInfo* none = NULL; + QtiImsCall* self = THIS(ext); + + return self->calls->len ? (const BinderExtCallInfo**)self->calls->pdata : &none; +} + +static +void +qti_ims_call_result_response( + QtiRadioExt* radio, + int result, + GBinderReader* reader, + void* user_data) +{ + QtiImsCallResultRequest* req = user_data; + BinderExtCallResultFunc complete = req->complete; + + if (complete) { + complete(req->ext, result ? BINDER_EXT_CALL_RESULT_ERROR : + BINDER_EXT_CALL_RESULT_OK, req->user_data); + } + + DBG("qti_ims_call_result_response %d", result); + + qti_ims_call_result_request_unref(req); +} + +static +guint +qti_ims_call_dial( + BinderExtCall* ext, + const char* number, + BINDER_EXT_TOA toa, + BINDER_EXT_CALL_CLIR clir, + BINDER_EXT_CALL_DIAL_FLAGS flags, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + QtiImsCall* self = THIS(ext); + + QtiImsCallResultRequest* req = qti_ims_call_result_request_new(ext, + complete, destroy, user_data); + guint id = qti_radio_ext_dial(self->radio_ext, number, toa, clir, flags, + qti_ims_call_result_response, qti_ims_call_result_request_destroy, req); + + if (id) { + req->id = id; + req->id_mapped = id; + g_hash_table_insert(self->id_map, ID_KEY(id), ID_VALUE(id)); + } else { + qti_ims_call_result_request_free(req); + } + + DBG("Dialing return %d", id); + + return id; +} + +static +guint +qti_ims_call_answer( + BinderExtCall* ext, + BINDER_EXT_CALL_ANSWER_FLAGS flags, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + DBG("answer is not implemented yet"); + return 0; +} + +static +guint +qti_ims_call_swap( + BinderExtCall* ext, + BINDER_EXT_CALL_SWAP_FLAGS swap_flags, + BINDER_EXT_CALL_ANSWER_FLAGS answer_flags, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + DBG("swap is not implemented yet"); + return 0; +} + +static +guint +qti_ims_call_hangup( + BinderExtCall* ext, + guint call_id, + BINDER_EXT_CALL_HANGUP_REASON reason, + BINDER_EXT_CALL_HANGUP_FLAGS flags, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + DBG("hangup is not implemented yet"); + return 0; +} + +static +guint +qti_ims_call_conference( + BinderExtCall* ext, + BINDER_EXT_CALL_CONFERENCE_FLAGS flags, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + DBG("conference is not implemented yet"); + return 0; +} + +static +guint +qti_ims_call_send_dtmf( + BinderExtCall* ext, + const char* tones, + BinderExtCallResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + DBG("send_dtmf is not implemented yet"); + return 0; +} + +static +void +qti_ims_call_cancel( + BinderExtCall* ext, + guint id) +{ + QtiImsCall* self = THIS(ext); + const guint mapped = GPOINTER_TO_UINT(g_hash_table_lookup(self->id_map, + ID_KEY(id))); + + qti_radio_ext_cancel(self->radio_ext, mapped ? mapped : id); +} + +static +gulong +qti_ims_call_add_calls_changed_handler( + BinderExtCall* ext, + BinderExtCallFunc cb, + void* user_data) +{ + return G_LIKELY(cb) ? g_signal_connect(THIS(ext), + SIGNAL_CALL_STATE_CHANGED_NAME, G_CALLBACK(cb), user_data) : 0; +} + +static +gulong +qti_ims_call_add_disconnect_handler( + BinderExtCall* ext, + BinderExtCallDisconnectFunc cb, + void* user_data) +{ + return G_LIKELY(cb) ? g_signal_connect(THIS(ext), + SIGNAL_CALL_END_NAME, G_CALLBACK(cb), user_data) : 0; +} + +static +gulong +qti_ims_call_add_ring_handler( + BinderExtCall* ext, + BinderExtCallFunc cb, + void* user_data) +{ + return G_LIKELY(cb) ? g_signal_connect(THIS(ext), + SIGNAL_CALL_RING_NAME, G_CALLBACK(cb), user_data) : 0; +} + +static +gulong +qti_ims_call_add_ssn_handler( + BinderExtCall* ext, + BinderExtCallSuppSvcNotifyFunc cb, + void* user_data) +{ + return G_LIKELY(cb) ? g_signal_connect(THIS(ext), + SIGNAL_CALL_SUPP_SVC_NOTIFY_NAME, G_CALLBACK(cb), user_data) : 0; +} + +void +qti_ims_call_iface_init( + BinderExtCallInterface* iface) +{ + iface->flags |= BINDER_EXT_CALL_INTERFACE_FLAG_IMS_SUPPORT | + BINDER_EXT_CALL_INTERFACE_FLAG_IMS_REQUIRED; + iface->version = BINDER_EXT_CALL_INTERFACE_VERSION; + iface->get_calls = qti_ims_call_get_calls; + iface->dial = qti_ims_call_dial; + iface->answer = qti_ims_call_answer; + iface->swap = qti_ims_call_swap; + iface->conference = qti_ims_call_conference; + iface->send_dtmf = qti_ims_call_send_dtmf; + iface->hangup = qti_ims_call_hangup; + iface->cancel = qti_ims_call_cancel; + iface->add_calls_changed_handler = + qti_ims_call_add_calls_changed_handler; + iface->add_disconnect_handler = qti_ims_call_add_disconnect_handler; + iface->add_ring_handler = qti_ims_call_add_ring_handler; + iface->add_ssn_handler = qti_ims_call_add_ssn_handler; +} + +/*==========================================================================* + * API + *==========================================================================*/ + +BinderExtCall* +qti_ims_call_new( + QtiRadioExt* radio_ext) +{ + if (G_LIKELY(radio_ext)) { + QtiImsCall* self = g_object_new(THIS_TYPE, NULL); + + self->radio_ext = qti_radio_ext_ref(radio_ext); + self->calls = g_ptr_array_new_with_free_func(g_free); + + qti_radio_ext_add_call_state_handler(radio_ext, + qti_ims_call_handle_call_info, self); + + return BINDER_EXT_CALL(self); + } + return NULL; +} + +/*==========================================================================* + * Internals + *==========================================================================*/ + +static +void +qti_ims_call_finalize( + GObject* object) +{ + QtiImsCall* self = THIS(object); + + qti_radio_ext_unref(self->radio_ext); + gutil_idle_pool_destroy(self->pool); + gutil_ptrv_free((void**)self->calls); + g_hash_table_unref(self->id_map); + G_OBJECT_CLASS(PARENT_CLASS)->finalize(object); +} + +static +void +qti_ims_call_init( + QtiImsCall* self) +{ + self->pool = gutil_idle_pool_new(); + self->id_map = g_hash_table_new(g_direct_hash, g_direct_equal); +} + +static +void +qti_ims_call_class_init( + QtiImsCallClass* klass) +{ + GType type = G_OBJECT_CLASS_TYPE(klass); + + G_OBJECT_CLASS(klass)->finalize = qti_ims_call_finalize; + qti_ims_call_signals[SIGNAL_CALL_STATE_CHANGED] = + g_signal_new(SIGNAL_CALL_STATE_CHANGED_NAME, type, + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 0); + qti_ims_call_signals[SIGNAL_CALL_END] = + g_signal_new(SIGNAL_CALL_END_NAME, type, + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, + 2, G_TYPE_INT, G_TYPE_INT); + qti_ims_call_signals[SIGNAL_CALL_RING] = + g_signal_new(SIGNAL_CALL_RING_NAME, type, G_SIGNAL_RUN_FIRST, 0, + NULL, NULL, NULL, G_TYPE_NONE, 0); + qti_ims_call_signals[SIGNAL_CALL_SUPP_SVC_NOTIFY] = + g_signal_new(SIGNAL_CALL_SUPP_SVC_NOTIFY_NAME, type, + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, + 1, G_TYPE_POINTER); +} + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/qti_ims_call.h b/src/qti_ims_call.h new file mode 100644 index 0000000..5b50468 --- /dev/null +++ b/src/qti_ims_call.h @@ -0,0 +1,36 @@ +/* + * oFono - Open Source Telephony - binder based adaptation QTI plugin + * + * Copyright (C) 2024 TheKit + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef QTI_IMS_CALL_H +#define QTI_IMS_CALL_H + +#include + +typedef struct qti_radio_ext QtiRadioExt; + +BinderExtCall* +qti_ims_call_new( + QtiRadioExt* ims_radio) + G_GNUC_INTERNAL; + +#endif /* QTI_IMS_CALL_H */ + +/* + * Local Variables: + * mode: C + * c-basic-offset: 4 + * indent-tabs-mode: nil + * End: + */ diff --git a/src/qti_radio_ext.c b/src/qti_radio_ext.c index 3dc9959..2f970a1 100644 --- a/src/qti_radio_ext.c +++ b/src/qti_radio_ext.c @@ -21,6 +21,7 @@ #include #include +#include #include #include @@ -30,7 +31,7 @@ #include #define DBG(fmt, ...) \ - gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, fmt, ##__VA_ARGS__) + gutil_log(GLOG_MODULE_CURRENT, GLOG_LEVEL_ALWAYS, "ims:"fmt, ##__VA_ARGS__) #define QTI_RADIO_CALL_TIMEOUT (3*1000) /* ms */ @@ -68,6 +69,7 @@ typedef void (*QtiRadioExtRequestHandlerFunc)( typedef void (*QtiRadioExtResultFunc)( QtiRadioExt* radio, int result, + GBinderReader* reader, void* user_data); struct qti_radio_ext_request { @@ -88,10 +90,12 @@ typedef struct qti_radio_ext_result_request { enum qti_radio_ext_signal { SIGNAL_IMS_REG_STATUS_CHANGED, + SIGNAL_EXT_CALL_STATE_CHANGED, SIGNAL_COUNT }; -#define SIGNAL_IMS_REG_STATUS_CHANGED_NAME "qti-radio-ext-ims-reg-status-changed" +#define SIGNAL_IMS_REG_STATUS_CHANGED_NAME "qti-radio-ext-ims-reg-status-changed" +#define SIGNAL_EXT_CALL_STATE_CHANGED_NAME "qti-radio-ext-call-state-changed" static guint qti_radio_ext_signals[SIGNAL_COUNT] = { 0 }; @@ -246,7 +250,6 @@ qti_radio_ext_dump_request( gutil_log_dump(log, level, " ", data, size); } -static const QtiRadioRegInfo* qti_radio_ext_read_ims_reg_status_info( QtiRadioExt* self, @@ -291,6 +294,103 @@ qti_radio_ext_handle_ims_reg_status_report( 0, info->state); } +static +BINDER_EXT_CALL_STATE +qti_ims_call_radio_state_to_state( + QTI_RADIO_CALL_STATE state) +{ + switch (state) { + case QTI_RADIO_CALL_STATE_INCOMING: + return BINDER_EXT_CALL_STATE_INCOMING; + case QTI_RADIO_CALL_STATE_ALERTING: + return BINDER_EXT_CALL_STATE_ALERTING; + case QTI_RADIO_CALL_STATE_HOLDING: + return BINDER_EXT_CALL_STATE_HOLDING; + case QTI_RADIO_CALL_STATE_WAITING: + return BINDER_EXT_CALL_STATE_WAITING; + case QTI_RADIO_CALL_STATE_ACTIVE: + return BINDER_EXT_CALL_STATE_ACTIVE; + case QTI_RADIO_CALL_STATE_END: + return BINDER_EXT_CALL_STATE_END; + case QTI_RADIO_CALL_STATE_DIALING: + return BINDER_EXT_CALL_STATE_DIALING; + default: + return BINDER_EXT_CALL_STATE_INVALID; + } +} + +static +GPtrArray* +qti_radio_ext_read_call_state_info( + QtiRadioCallInfo* call_info_array, + gsize count) +{ + // array of QtiRadioCallInfo + GPtrArray* call_ext_info_array = g_ptr_array_new_with_free_func(g_free); + + for (gsize i = 0; i < count; i++) { + QtiRadioCallInfo* call_info = &call_info_array[i]; + + const gsize total = G_ALIGN8(sizeof(BinderExtCallInfo)) + + G_ALIGN8(call_info->number.len + 1) + G_ALIGN8(call_info->name.len + 1); + BinderExtCallInfo* dest = g_malloc0(total); + + char* ptr_number = ((char*)dest) + G_ALIGN8(sizeof(BinderExtCallInfo)); + char* ptr_name = ptr_number + G_ALIGN8(call_info->number.len + 1); + + dest->call_id = call_info->index; + dest->state = qti_ims_call_radio_state_to_state(call_info->state); + dest->type = BINDER_EXT_CALL_TYPE_VOICE; + dest->flags = BINDER_EXT_CALL_FLAG_IMS | BINDER_EXT_CALL_FLAG_INCOMING; + + dest->number = ptr_number; + dest->name = ptr_name; + + memcpy(ptr_name, call_info->name.data.str, call_info->name.len); + ptr_name += G_ALIGN8(call_info->name.len + 1); + + memcpy(ptr_number, call_info->number.data.str, call_info->number.len); + ptr_number += G_ALIGN8(call_info->number.len + 1); + + g_ptr_array_add(call_ext_info_array, dest); + + // print call_info + const char* number = call_info->number.data.str ? call_info->number.data.str : ""; + const char* name = call_info->name.data.str ? call_info->name.data.str : ""; + DBG("callInfoIndication state:%d index:%d name:%s number:%s", + call_info->state, call_info->index, name, number); + + } + + return call_ext_info_array; +} + +static +void +qti_radio_ext_handle_call_state_indication( + QtiRadioExt* self, + const GBinderReader* args) +{ + /* callInfoIndication(vec callList) */ + QtiRadioCallInfo* call_infos; + GBinderReader reader; + gsize count; + + gbinder_reader_copy(&reader, args); + call_infos = gbinder_reader_read_hidl_type_vec(&reader, QtiRadioCallInfo, &count); + + if (call_infos) { + GPtrArray* call_info_ptr = qti_radio_ext_read_call_state_info(call_infos, count); + + g_signal_emit(self, qti_radio_ext_signals[SIGNAL_EXT_CALL_STATE_CHANGED], + 0, call_info_ptr); + + g_free(call_infos); + } else { + DBG("%s: failed to parse call state data", self->slot); + } +} + static GBinderLocalReply* @@ -315,6 +415,9 @@ qti_radio_ext_indication( case QTI_RADIO_IND_REG_STATE_INDICATION: qti_radio_ext_handle_ims_reg_status_report(self, &args); return NULL; + case QTI_RADIO_IND_CALL_STATE_INDICATION: + qti_radio_ext_handle_call_state_indication(self, &args); + return NULL; } } @@ -332,6 +435,16 @@ qti_radio_ext_add_ims_reg_status_handler( SIGNAL_IMS_REG_STATUS_CHANGED_NAME, G_CALLBACK(handler), user_data) : 0; } +gulong +qti_radio_ext_add_call_state_handler( + QtiRadioExt* self, + QtiRadioExtCallStateFunc handler, + void* user_data) +{ + return (G_LIKELY(self) && G_LIKELY(handler)) ? g_signal_connect(self, + SIGNAL_EXT_CALL_STATE_CHANGED_NAME, G_CALLBACK(handler), user_data) : 0; +} + static GBinderLocalReply* qti_radio_ext_response( @@ -391,7 +504,7 @@ qti_radio_ext_result_response( result = -1; } if (result_req->complete) { - result_req->complete(self, result, req->user_data); + result_req->complete(self, result, args, req->user_data); } } @@ -487,6 +600,17 @@ qti_radio_ext_call( GBINDER_TX_FLAG_ONEWAY, req, reply, destroy, user_data); } +void +qti_radio_ext_cancel( + QtiRadioExt* self, + guint id) +{ + if (G_LIKELY(self) && G_LIKELY(id)) { + g_hash_table_remove(self->requests, KEY(id)); + } +} + + static gulong qti_radio_ext_submit_request( @@ -542,6 +666,7 @@ qti_radio_ext_result_request_submit( return 0; } + static QtiRadioExt* qti_radio_ext_create( @@ -674,6 +799,269 @@ qti_radio_ext_set_reg_state( reg_state); } +/* From ofono-binder-plugin's binder-util.c */ +static +void +binder_copy_hidl_string( + GBinderWriter* writer, + GBinderHidlString* dest, + const char* src) +{ + gssize len = src ? strlen(src) : 0; + dest->owns_buffer = TRUE; + if (len > 0) { + /* GBinderWriter takes ownership of the string contents */ + dest->len = (guint32) len; + dest->data.str = gbinder_writer_memdup(writer, src, len + 1); + } else { + /* Replace NULL strings with empty strings */ + dest->data.str = ""; + dest->len = 0; + } +} + +static +void +binder_append_hidl_string_with_parent( + GBinderWriter* writer, + const GBinderHidlString* str, + guint32 index, + guint32 offset) +{ + GBinderParent parent; + + parent.index = index; + parent.offset = offset; + + /* Strings are NULL-terminated, hence len + 1 */ + gbinder_writer_append_buffer_object_with_parent(writer, str->data.str, + str->len + 1, &parent); +} + +#define binder_append_hidl_string_data(writer,ptr,field,index) \ + binder_append_hidl_string_with_parent(writer, &ptr->field, index, \ + ((guint8*)(&ptr->field) - (guint8*)ptr)) + +static +void +binder_append_hidl_vec_with_parent( + GBinderWriter* writer, + const GBinderHidlVec* vec, + guint32 index, + guint32 offset) +{ + GBinderParent parent; + + parent.index = index; + parent.offset = offset; + + gbinder_writer_append_buffer_object_with_parent(writer, vec->data.ptr, + vec->count, &parent); +} + +#define binder_append_hidl_vec_data(writer,ptr,field,index) \ + binder_append_hidl_vec_with_parent(writer, &ptr->field, index, \ + ((guint8*)(&ptr->field) - (guint8*)ptr)) + +#define CLIR_DEFAULT 0 // "use subscription default value" +#define CLIR_INVOCATION 1 // (restrict CLI presentation) +#define CLIR_SUPPRESSION 2 // (allow CLI presentation) + + +static const GBinderWriterField qti_radio_dial_request_f[] = { + GBINDER_WRITER_FIELD_HIDL_STRING + (QtiRadioDialRequest, address), + GBINDER_WRITER_FIELD_HIDL_VEC_BYTE + (QtiRadioDialRequest, call_details.extras), + GBINDER_WRITER_FIELD_HIDL_VEC_BYTE + (QtiRadioDialRequest, call_details.local_ability), + GBINDER_WRITER_FIELD_HIDL_VEC_BYTE + (QtiRadioDialRequest, call_details.peer_ability), + GBINDER_WRITER_FIELD_HIDL_STRING + (QtiRadioDialRequest, call_details.sip_alternate_uri), + GBINDER_WRITER_FIELD_END() +}; +static const GBinderWriterType qti_radio_dial_request_t = { + GBINDER_WRITER_STRUCT_NAME_AND_SIZE(QtiRadioDialRequest), + qti_radio_dial_request_f +}; + + +static +void +qti_radio_ext_dial_args( + GBinderWriter* args, + va_list va) +{ + QtiRadioDialRequest* dial_request_writer; + + const char* number = va_arg(va, const char*); + gint32 clir = va_arg(va, gint32); + + dial_request_writer = gbinder_writer_new0(args, QtiRadioDialRequest); + + GBinderHidlVec* empty_vec1 = gbinder_writer_new0(args, GBinderHidlVec); + GBinderHidlVec* empty_vec2 = gbinder_writer_new0(args, GBinderHidlVec); + GBinderHidlVec* empty_vec3 = gbinder_writer_new0(args, GBinderHidlVec); + + dial_request_writer->clir_mode = clir; + switch (clir) + { + case CLIR_SUPPRESSION: + dial_request_writer->presentation = QTI_RADIO_IP_PRESENTATION_NUM_RESTRICTED; + break; + default: + dial_request_writer->presentation = QTI_RADIO_IP_PRESENTATION_NUM_ALLOWED; + break; + } + dial_request_writer->call_details.call_type = QTI_RADIO_CALL_TYPE_VOICE; + dial_request_writer->call_details.call_domain = QTI_RADIO_CALL_DOMAIN_CS; + dial_request_writer->call_details.extras_length = 0; + dial_request_writer->call_details.extras.count = 0; + dial_request_writer->call_details.extras.data.ptr = empty_vec1; + dial_request_writer->call_details.extras.owns_buffer = TRUE; + dial_request_writer->call_details.local_ability.count = 0; + dial_request_writer->call_details.local_ability.data.ptr = empty_vec2; + dial_request_writer->call_details.local_ability.owns_buffer = TRUE; + dial_request_writer->call_details.peer_ability.count = 0; + dial_request_writer->call_details.peer_ability.data.ptr = empty_vec3; + dial_request_writer->call_details.peer_ability.owns_buffer = TRUE; + + dial_request_writer->call_details.call_substate = 0; // none + dial_request_writer->call_details.media_id = -1; // unknown + dial_request_writer->call_details.cause_code = 0; // none + dial_request_writer->call_details.rtt_mode = 0; + + binder_copy_hidl_string(args, &dial_request_writer->address, number); + binder_copy_hidl_string(args, &dial_request_writer->call_details.sip_alternate_uri, NULL); + + gbinder_writer_append_struct(&args, dial_request_writer, + &qti_radio_dial_request_t, NULL); + + DBG("Dialing in args New %s", number); +} + + /* +// dail +static +void +qti_radio_ext_dial_args( + GBinderWriter* args, + va_list va) +{ + GBinderParent parent; + QtiRadioDialRequest* dial_request_writer; + + const char* number = va_arg(va, const char*); + gint32 clir = va_arg(va, gint32); + + dial_request_writer = gbinder_writer_new0(args, QtiRadioDialRequest); + + GBinderHidlVec* empty_vec1 = gbinder_writer_new0(args, GBinderHidlVec); + GBinderHidlVec* empty_vec2 = gbinder_writer_new0(args, GBinderHidlVec); + GBinderHidlVec* empty_vec3 = gbinder_writer_new0(args, GBinderHidlVec); + + dial_request_writer->clir_mode = clir; + switch (clir) + { + case CLIR_SUPPRESSION: + dial_request_writer->presentation = QTI_RADIO_IP_PRESENTATION_NUM_RESTRICTED; + break; + default: + dial_request_writer->presentation = QTI_RADIO_IP_PRESENTATION_NUM_ALLOWED; + break; + } + dial_request_writer->call_details.call_type = QTI_RADIO_CALL_TYPE_VOICE; + dial_request_writer->call_details.call_domain = QTI_RADIO_CALL_DOMAIN_CS; + dial_request_writer->call_details.extras_length = 0; + dial_request_writer->call_details.extras.count = 0; + dial_request_writer->call_details.extras.data.ptr = empty_vec1; + dial_request_writer->call_details.extras.owns_buffer = TRUE; + dial_request_writer->call_details.local_ability.count = 0; + dial_request_writer->call_details.local_ability.data.ptr = empty_vec2; + dial_request_writer->call_details.local_ability.owns_buffer = TRUE; + dial_request_writer->call_details.peer_ability.count = 0; + dial_request_writer->call_details.peer_ability.data.ptr = empty_vec3; + dial_request_writer->call_details.peer_ability.owns_buffer = TRUE; + + dial_request_writer->call_details.call_substate = 0; // none + dial_request_writer->call_details.media_id = -1; // unknown + dial_request_writer->call_details.cause_code = 0; // none + dial_request_writer->call_details.rtt_mode = 0; + + binder_copy_hidl_string(args, &dial_request_writer->address, number); + binder_copy_hidl_string(args, &dial_request_writer->call_details.sip_alternate_uri, NULL); + + /* Write the parent structure + parent.index = gbinder_writer_append_buffer_object(args, dial_request_writer, + sizeof(*dial_request_writer)); + + /* Write the string data + binder_append_hidl_string_data(args, dial_request_writer, address, parent.index); + + // find right index after + guint32 index = G_STRUCT_OFFSET(QtiRadioDialRequest, call_details.call_type); + binder_append_hidl_string_data(args, dial_request_writer, call_details.sip_alternate_uri, parent.index); + binder_append_hidl_vec_data(args, dial_request_writer, call_details.extras, parent.index); + + /* UUS information is empty but we still need to write a buffer + //parent.offset = G_STRUCT_OFFSET(QtiRadioDialRequest, call_details.extras.data.ptr); + //gbinder_writer_append_buffer_object_with_parent(args, NULL, 0, &parent); + + //parent.offset = G_STRUCT_OFFSET(QtiRadioDialRequest, call_details.local_ability.data.ptr); + //gbinder_writer_append_buffer_object_with_parent(args, NULL, 0, &parent); + + //parent.offset = G_STRUCT_OFFSET(QtiRadioDialRequest, call_details.peer_ability.data.ptr); + //gbinder_writer_append_buffer_object_with_parent(args, NULL, 0, &parent); + + DBG("Dialing in args YAY %s", number); +} +*/ + +guint +qti_radio_ext_dial( + QtiRadioExt* self, + const char* number, + BINDER_EXT_TOA toa, + BINDER_EXT_CALL_CLIR clir, + BINDER_EXT_CALL_DIAL_FLAGS flags, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + return qti_radio_ext_result_request_submit(self, + QTI_RADIO_REQ_DAIL, + QTI_RADIO_RESP_DAIL, + qti_radio_ext_dial_args, + complete, destroy, user_data, + number, clir); +} + +// GET_IMS_REG_STATE + +static +void +qti_radio_ext_get_ims_reg_state_args( + GBinderWriter* args, + va_list va) +{ + // empty +} + +guint +qti_radio_ext_get_ims_reg_state( + QtiRadioExt* self, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data) +{ + return qti_radio_ext_result_request_submit(self, + QTI_RADIO_REQ_GET_IMS_REG_STATE, + QTI_RADIO_RESP_GET_IMS_REG_STATE, + qti_radio_ext_get_ims_reg_state_args, + complete, destroy, user_data); +} + /*==========================================================================* * Internals *==========================================================================*/ @@ -709,6 +1097,10 @@ qti_radio_ext_class_init( g_signal_new(SIGNAL_IMS_REG_STATUS_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass), G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, 1, G_TYPE_UINT); + qti_radio_ext_signals[SIGNAL_EXT_CALL_STATE_CHANGED] = + g_signal_new(SIGNAL_EXT_CALL_STATE_CHANGED_NAME, G_OBJECT_CLASS_TYPE(klass), + G_SIGNAL_RUN_FIRST, 0, NULL, NULL, NULL, G_TYPE_NONE, + 1, G_TYPE_PTR_ARRAY); } /* diff --git a/src/qti_radio_ext.h b/src/qti_radio_ext.h index 8335f8d..e282ca3 100644 --- a/src/qti_radio_ext.h +++ b/src/qti_radio_ext.h @@ -18,15 +18,18 @@ #include #include +#include #include "qti_radio_ext_types.h" +#define BINDER_EXT_CALL_STATE_END (BINDER_EXT_CALL_STATE_INVALID - 1) typedef struct qti_radio_ext QtiRadioExt; typedef void (*QtiRadioExtResultFunc)( QtiRadioExt* radio, int result, + GBinderReader* reader, void* user_data); typedef void (*QtiRadioExtImsRegStatusFunc)( @@ -34,6 +37,11 @@ typedef void (*QtiRadioExtImsRegStatusFunc)( guint32 status, void* user_data); +typedef void (*QtiRadioExtCallStateFunc)( + QtiRadioExt* radio, + GPtrArray* updated_calls, + void* user_data); + QtiRadioExt* qti_radio_ext_new( const char* dev, @@ -47,6 +55,16 @@ void qti_radio_ext_unref( QtiRadioExt* self); +void +qti_radio_ext_cancel( + QtiRadioExt* self, + guint id); + +const QtiRadioRegInfo* +qti_radio_ext_read_ims_reg_status_info( + QtiRadioExt* self, + GBinderReader* reader); + guint qti_radio_ext_set_reg_state( QtiRadioExt* self, @@ -55,12 +73,35 @@ qti_radio_ext_set_reg_state( GDestroyNotify destroy, void* user_data); +guint +qti_radio_ext_dial( + QtiRadioExt* self, + const char* number, + BINDER_EXT_TOA toa, + BINDER_EXT_CALL_CLIR clir, + BINDER_EXT_CALL_DIAL_FLAGS flags, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data); + +guint +qti_radio_ext_get_ims_reg_state( + QtiRadioExt* self, + QtiRadioExtResultFunc complete, + GDestroyNotify destroy, + void* user_data); + gulong qti_radio_ext_add_ims_reg_status_handler( QtiRadioExt* self, QtiRadioExtImsRegStatusFunc handler, void* user_data); +gulong +qti_radio_ext_add_call_state_handler( + QtiRadioExt* self, + QtiRadioExtCallStateFunc handler, + void* user_data); #endif /* QTI_RADIO_EXT_H */ diff --git a/src/qti_radio_ext_types.h b/src/qti_radio_ext_types.h index 6bdccb6..0217e23 100644 --- a/src/qti_radio_ext_types.h +++ b/src/qti_radio_ext_types.h @@ -85,6 +85,103 @@ typedef enum qti_radio_service_status { QTI_RADIO_SERVICE_STATUS_INVALID = 2, } QTI_RADIO_SERVICE_STATUS; +/* +enum CallState : int32_t { + CALL_ACTIVE, + CALL_HOLDING, + CALL_DIALING, + CALL_ALERTING, + CALL_INCOMING, + CALL_WAITING, + CALL_END, + CALL_STATE_INVALID, +}; +*/ + +typedef enum qti_radio_call_state { + QTI_RADIO_CALL_STATE_ACTIVE = 0, + QTI_RADIO_CALL_STATE_HOLDING = 1, + QTI_RADIO_CALL_STATE_DIALING = 2, + QTI_RADIO_CALL_STATE_ALERTING = 3, + QTI_RADIO_CALL_STATE_INCOMING = 4, + QTI_RADIO_CALL_STATE_WAITING = 5, + QTI_RADIO_CALL_STATE_END = 6, + QTI_RADIO_CALL_STATE_INVALID = 7, +} QTI_RADIO_CALL_STATE; + +/* +enum IpPresentation : int32_t { + IP_PRESENTATION_NUM_ALLOWED, + IP_PRESENTATION_NUM_RESTRICTED, + IP_PRESENTATION_NUM_DEFAULT, + IP_PRESENTATION_INVALID, +}; + +*/ + +typedef enum qti_radio_ip_presentation { + QTI_RADIO_IP_PRESENTATION_NUM_ALLOWED = 0, + QTI_RADIO_IP_PRESENTATION_NUM_RESTRICTED = 1, + QTI_RADIO_IP_PRESENTATION_NUM_DEFAULT = 2, + QTI_RADIO_IP_PRESENTATION_INVALID = 3, +} QTI_RADIO_IP_PRESENTATION; + +/* +enum CallType : int32_t { + CALL_TYPE_VOICE, + CALL_TYPE_VT_TX, + CALL_TYPE_VT_RX, + CALL_TYPE_VT, + CALL_TYPE_VT_NODIR, + CALL_TYPE_CS_VS_TX, + CALL_TYPE_CS_VS_RX, + CALL_TYPE_PS_VS_TX, + CALL_TYPE_PS_VS_RX, + CALL_TYPE_UNKNOWN, + CALL_TYPE_SMS, + CALL_TYPE_UT, + CALL_TYPE_INVALID, +}; + +*/ + +typedef enum qti_radio_call_type { + QTI_RADIO_CALL_TYPE_VOICE = 0, + QTI_RADIO_CALL_TYPE_VT_TX = 1, + QTI_RADIO_CALL_TYPE_VT_RX = 2, + QTI_RADIO_CALL_TYPE_VT = 3, + QTI_RADIO_CALL_TYPE_VT_NODIR = 4, + QTI_RADIO_CALL_TYPE_CS_VS_TX = 5, + QTI_RADIO_CALL_TYPE_CS_VS_RX = 6, + QTI_RADIO_CALL_TYPE_PS_VS_TX = 7, + QTI_RADIO_CALL_TYPE_PS_VS_RX = 8, + QTI_RADIO_CALL_TYPE_UNKNOWN = 9, + QTI_RADIO_CALL_TYPE_SMS = 10, + QTI_RADIO_CALL_TYPE_UT = 11, + QTI_RADIO_CALL_TYPE_INVALID = 12, +} QTI_RADIO_CALL_TYPE; + +/* + +enum CallDomain : int32_t { + CALL_DOMAIN_UNKNOWN, + CALL_DOMAIN_CS, + CALL_DOMAIN_PS, + CALL_DOMAIN_AUTOMATIC, + CALL_DOMAIN_NOT_SET, + CALL_DOMAIN_INVALID, +}; +*/ + +typedef enum qti_radio_call_domain { + QTI_RADIO_CALL_DOMAIN_UNKNOWN = 0, + QTI_RADIO_CALL_DOMAIN_CS = 1, + QTI_RADIO_CALL_DOMAIN_PS = 2, + QTI_RADIO_CALL_DOMAIN_AUTOMATIC = 3, + QTI_RADIO_CALL_DOMAIN_NOT_SET = 4, + QTI_RADIO_CALL_DOMAIN_INVALID = 5, +} QTI_RADIO_CALL_DOMAIN; + /* struct RegistrationInfo { @@ -104,6 +201,141 @@ typedef struct qti_radio_reg_info { GBinderHidlString uri RADIO_ALIGNED(8); } QtiRadioRegInfo; +/* +struct CallInfo { + CallState state; + uint32_t index; + uint32_t toa; + bool hasIsMpty; + bool isMpty; + bool hasIsMT; + bool isMT; + uint32_t als; + bool hasIsVoice; + bool isVoice; + bool hasIsVoicePrivacy; + bool isVoicePrivacy; + string number; + uint32_t numberPresentation; + string name; + uint32_t namePresentation; + bool hasCallDetails; +}; +*/ + +typedef struct qti_radio_call_info { + QTI_RADIO_CALL_STATE state RADIO_ALIGNED(4); + guint32 index RADIO_ALIGNED(4); + guint32 toa RADIO_ALIGNED(4); + gboolean has_is_mpty RADIO_ALIGNED(4); + gboolean is_mpty RADIO_ALIGNED(4); + gboolean has_is_mt RADIO_ALIGNED(4); + gboolean is_mt RADIO_ALIGNED(4); + guint32 als RADIO_ALIGNED(4); + gboolean has_is_voice RADIO_ALIGNED(4); + gboolean is_voice RADIO_ALIGNED(4); + gboolean has_is_voice_privacy RADIO_ALIGNED(4); + gboolean is_voice_privacy RADIO_ALIGNED(4); + GBinderHidlString number RADIO_ALIGNED(8); + guint32 number_presentation RADIO_ALIGNED(4); + GBinderHidlString name RADIO_ALIGNED(8); + guint32 name_presentation RADIO_ALIGNED(4); + gboolean has_call_details RADIO_ALIGNED(4); +} QtiRadioCallInfo; + +/* + +struct ServiceStatusInfo { + bool hasIsValid; + bool isValid; + ServiceType type; + CallType callType; + StatusType status; + vec userdata; + uint32_t restrictCause; + vec accTechStatus; + RttMode rttMode; +}; + + */ + +typedef struct qti_radio_service_status_info { + gboolean has_is_valid RADIO_ALIGNED(4); + gboolean is_valid RADIO_ALIGNED(4); + guint32 type RADIO_ALIGNED(4); + guint32 call_type RADIO_ALIGNED(4); + guint32 status RADIO_ALIGNED(4); + GBinderHidlVec userdata RADIO_ALIGNED(8); + guint32 restrict_cause RADIO_ALIGNED(4); + GBinderHidlVec acc_tech_status RADIO_ALIGNED(8); + guint32 rtt_mode RADIO_ALIGNED(4); +} QtiRadioServiceStatusInfo; + +/* +struct CallDetails { + CallType callType; + CallDomain callDomain; + uint32_t extrasLength; + vec extras; + + vec localAbility; + vec peerAbility; + uint32_t callSubstate; + uint32_t mediaId; + uint32_t causeCode; + RttMode rttMode; + string sipAlternateUri; +}; +*/ + +typedef struct qti_radio_call_details { + guint32 call_type RADIO_ALIGNED(4); + guint32 call_domain RADIO_ALIGNED(4); + guint32 extras_length RADIO_ALIGNED(4); + + GBinderHidlVec extras RADIO_ALIGNED(8); + GBinderHidlVec local_ability RADIO_ALIGNED(8); + GBinderHidlVec peer_ability RADIO_ALIGNED(8); + + guint32 call_substate RADIO_ALIGNED(4); + guint32 media_id RADIO_ALIGNED(4); + guint32 cause_code RADIO_ALIGNED(4); + guint32 rtt_mode RADIO_ALIGNED(4); + GBinderHidlString sip_alternate_uri RADIO_ALIGNED(8); +} RADIO_ALIGNED(8) QtiRadioCallDetails; + +/* +struct DialRequest { + string address; + uint32_t clirMode; + IpPresentation presentation; + bool hasCallDetails; + CallDetails callDetails; + bool hasIsConferenceUri; + bool isConferenceUri; + bool hasIsCallPull; + bool isCallPull; + bool hasIsEncrypted; + bool isEncrypted; +}; + +*/ + +typedef struct qti_radio_dial_request { + GBinderHidlString address RADIO_ALIGNED(8); + guint32 clir_mode RADIO_ALIGNED(4); + guint32 presentation RADIO_ALIGNED(4); + guint8 has_call_details RADIO_ALIGNED(1); + QtiRadioCallDetails call_details RADIO_ALIGNED(8); + guint8 has_is_conference_uri RADIO_ALIGNED(1); + guint8 is_conference_uri RADIO_ALIGNED(1); + guint8 has_is_call_pull RADIO_ALIGNED(1); + guint8 is_call_pull RADIO_ALIGNED(1); + guint8 has_is_encrypted RADIO_ALIGNED(1); + guint8 is_encrypted RADIO_ALIGNED(1); +} RADIO_ALIGNED(8) QtiRadioDialRequest; + + /* c(req, resp, callName, CALL_NAME) */ #define QTI_RADIO_EXT_IMS_CALL_1_0(c) \ c(2, 1, dail, DAIL) \ diff --git a/src/qti_slot.c b/src/qti_slot.c index dcc1976..303547c 100644 --- a/src/qti_slot.c +++ b/src/qti_slot.c @@ -38,6 +38,7 @@ #include "qti_slot.h" #include "qti_ims.h" #include "qti_radio_ext.h" +#include "qti_ims_call.h" #include @@ -48,6 +49,7 @@ typedef struct qti_slot { BinderExtSlot parent; BinderExtIms* ims; QtiRadioExt* radio_ext; + BinderExtCall* call; } QtiSlot; GType qti_slot_get_type() G_GNUC_INTERNAL; @@ -82,6 +84,8 @@ qti_slot_get_interface( if (iface == BINDER_EXT_TYPE_IMS) { return self->ims; + } else if (iface == BINDER_EXT_TYPE_CALL) { + return self->call; } else { return BINDER_EXT_SLOT_CLASS(PARENT_CLASS)->get_interface(slot, iface); } @@ -112,6 +116,7 @@ qti_slot_new( self->radio_ext = qti_radio_ext_new(radio->dev, radio_slot); if (self->radio_ext) { self->ims = qti_ims_new(radio_slot, self->radio_ext); + self->call = qti_ims_call_new(self->radio_ext); } return slot;