treewide: Inital call support (unstable)

This commit is contained in:
Marius Gripsgard
2024-10-10 16:03:58 +02:00
parent 68ca473945
commit 63be8813a1
8 changed files with 1250 additions and 7 deletions

View File

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

View File

@@ -48,7 +48,8 @@
#include <gbinder.h>
#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);

491
src/qti_ims_call.c Normal file
View File

@@ -0,0 +1,491 @@
/*
* oFono - Open Source Telephony - binder based adaptation QTI plugin
*
* Copyright (C) 2022 Jolla Ltd.
* Copyright (C) 2024 TheKit <thekit@disroot.org>
*
* 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 <glib-object.h>
#include "qti_ims_call.h"
#include "qti_radio_ext.h"
#include "qti_radio_ext_types.h"
#include <binder_ext_call_impl.h>
#include <gbinder.h>
#include <ofono/log.h>
#include <gutil_idlepool.h>
#include <gutil_macros.h>
#include <gutil_misc.h>
#include <gutil_log.h>
#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:
*/

36
src/qti_ims_call.h Normal file
View File

@@ -0,0 +1,36 @@
/*
* oFono - Open Source Telephony - binder based adaptation QTI plugin
*
* Copyright (C) 2024 TheKit <thekit@disroot.org>
*
* 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 <binder_ext_call.h>
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:
*/

View File

@@ -21,6 +21,7 @@
#include <radio_types.h>
#include <binder_ext_ims_impl.h>
#include <binder_ext_call_impl.h>
#include <ofono/log.h>
#include <gbinder.h>
@@ -30,7 +31,7 @@
#include <gutil_macros.h>
#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<CallInfo> 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);
}
/*

View File

@@ -18,15 +18,18 @@
#include <radio_types.h>
#include <binder_ext_ims_impl.h>
#include <binder_ext_call_impl.h>
#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 */

View File

@@ -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<uint8_t> userdata;
uint32_t restrictCause;
vec<StatusForAccessTech> 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<string> extras;
vec<ServiceStatusInfo> localAbility;
vec<ServiceStatusInfo> 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) \

View File

@@ -38,6 +38,7 @@
#include "qti_slot.h"
#include "qti_ims.h"
#include "qti_radio_ext.h"
#include "qti_ims_call.h"
#include <binder_ext_slot_impl.h>
@@ -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;