api,call: new JoinMultiparty() and LeaveMultiparty() methods
This commit is contained in:
@@ -461,6 +461,157 @@ handle_deflect (MMBaseCall *self,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Join multiparty call (DBus call handling) */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *self;
|
||||
MMBaseModem *modem;
|
||||
GDBusMethodInvocation *invocation;
|
||||
} HandleJoinMultipartyContext;
|
||||
|
||||
static void
|
||||
handle_join_multiparty_context_free (HandleJoinMultipartyContext *ctx)
|
||||
{
|
||||
g_object_unref (ctx->invocation);
|
||||
g_object_unref (ctx->modem);
|
||||
g_object_unref (ctx->self);
|
||||
g_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_voice_join_multiparty_ready (MMIfaceModemVoice *modem,
|
||||
GAsyncResult *res,
|
||||
HandleJoinMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_iface_modem_voice_join_multiparty_finish (modem, res, &error))
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
else
|
||||
mm_gdbus_call_complete_join_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation);
|
||||
handle_join_multiparty_context_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_join_multiparty_auth_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
HandleJoinMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_join_multiparty_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This action is provided in the Call API, but implemented in the Modem.Voice interface
|
||||
* logic, because the action affects not only one call object, but all call objects that
|
||||
* are part of the multiparty call. */
|
||||
mm_iface_modem_voice_join_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem),
|
||||
ctx->self,
|
||||
(GAsyncReadyCallback)modem_voice_join_multiparty_ready,
|
||||
ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_join_multiparty (MMBaseCall *self,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
HandleJoinMultipartyContext *ctx;
|
||||
|
||||
ctx = g_new0 (HandleJoinMultipartyContext, 1);
|
||||
ctx->self = g_object_ref (self);
|
||||
ctx->invocation = g_object_ref (invocation);
|
||||
g_object_get (self,
|
||||
MM_BASE_CALL_MODEM, &ctx->modem,
|
||||
NULL);
|
||||
|
||||
mm_base_modem_authorize (ctx->modem,
|
||||
invocation,
|
||||
MM_AUTHORIZATION_VOICE,
|
||||
(GAsyncReadyCallback)handle_join_multiparty_auth_ready,
|
||||
ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Leave multiparty call (DBus call handling) */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *self;
|
||||
MMBaseModem *modem;
|
||||
GDBusMethodInvocation *invocation;
|
||||
} HandleLeaveMultipartyContext;
|
||||
|
||||
static void
|
||||
handle_leave_multiparty_context_free (HandleLeaveMultipartyContext *ctx)
|
||||
{
|
||||
g_object_unref (ctx->invocation);
|
||||
g_object_unref (ctx->modem);
|
||||
g_object_unref (ctx->self);
|
||||
g_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
modem_voice_leave_multiparty_ready (MMIfaceModemVoice *modem,
|
||||
GAsyncResult *res,
|
||||
HandleLeaveMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_iface_modem_voice_leave_multiparty_finish (modem, res, &error))
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
else
|
||||
mm_gdbus_call_complete_leave_multiparty (MM_GDBUS_CALL (ctx->self), ctx->invocation);
|
||||
|
||||
handle_leave_multiparty_context_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
handle_leave_multiparty_auth_ready (MMBaseModem *modem,
|
||||
GAsyncResult *res,
|
||||
HandleLeaveMultipartyContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
if (!mm_base_modem_authorize_finish (modem, res, &error)) {
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_leave_multiparty_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
/* This action is provided in the Call API, but implemented in the Modem.Voice interface
|
||||
* logic, because the action affects not only one call object, but all call objects that
|
||||
* are part of the multiparty call. */
|
||||
mm_iface_modem_voice_leave_multiparty (MM_IFACE_MODEM_VOICE (ctx->modem),
|
||||
ctx->self,
|
||||
(GAsyncReadyCallback)modem_voice_leave_multiparty_ready,
|
||||
ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_leave_multiparty (MMBaseCall *self,
|
||||
GDBusMethodInvocation *invocation)
|
||||
{
|
||||
HandleLeaveMultipartyContext *ctx;
|
||||
|
||||
ctx = g_new0 (HandleLeaveMultipartyContext, 1);
|
||||
ctx->self = g_object_ref (self);
|
||||
ctx->invocation = g_object_ref (invocation);
|
||||
g_object_get (self,
|
||||
MM_BASE_CALL_MODEM, &ctx->modem,
|
||||
NULL);
|
||||
|
||||
mm_base_modem_authorize (ctx->modem,
|
||||
invocation,
|
||||
MM_AUTHORIZATION_VOICE,
|
||||
(GAsyncReadyCallback)handle_leave_multiparty_auth_ready,
|
||||
ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Hangup call (DBus call handling) */
|
||||
|
||||
@@ -709,6 +860,14 @@ call_dbus_export (MMBaseCall *self)
|
||||
"handle-deflect",
|
||||
G_CALLBACK (handle_deflect),
|
||||
NULL);
|
||||
g_signal_connect (self,
|
||||
"handle-join-multiparty",
|
||||
G_CALLBACK (handle_join_multiparty),
|
||||
NULL);
|
||||
g_signal_connect (self,
|
||||
"handle-leave-multiparty",
|
||||
G_CALLBACK (handle_leave_multiparty),
|
||||
NULL);
|
||||
g_signal_connect (self,
|
||||
"handle-hangup",
|
||||
G_CALLBACK (handle_hangup),
|
||||
|
||||
@@ -1261,6 +1261,288 @@ handle_transfer (MmGdbusModemVoice *skeleton,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Leave one of the calls from the multiparty call */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *call;
|
||||
GList *other_calls;
|
||||
} LeaveMultipartyContext;
|
||||
|
||||
static void
|
||||
leave_multiparty_context_free (LeaveMultipartyContext *ctx)
|
||||
{
|
||||
g_list_free_full (ctx->other_calls, g_object_unref);
|
||||
g_object_unref (ctx->call);
|
||||
g_slice_free (LeaveMultipartyContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_leave_multiparty_foreach (MMBaseCall *call,
|
||||
LeaveMultipartyContext *ctx)
|
||||
{
|
||||
/* ignore call that is leaving */
|
||||
if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0))
|
||||
return;
|
||||
|
||||
/* ignore non-multiparty calls */
|
||||
if (!mm_base_call_get_multiparty (call))
|
||||
return;
|
||||
|
||||
/* ignore calls not currently ongoing */
|
||||
switch (mm_base_call_get_state (call)) {
|
||||
case MM_CALL_STATE_ACTIVE:
|
||||
case MM_CALL_STATE_HELD:
|
||||
ctx->other_calls = g_list_append (ctx->other_calls, g_object_ref (call));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
leave_multiparty_ready (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
LeaveMultipartyContext *ctx;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish (self, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
/* If there is only one remaining call that was part of the multiparty, consider that
|
||||
* one also no longer part of any multiparty, and put it on hold right away */
|
||||
if (g_list_length (ctx->other_calls) == 1) {
|
||||
mm_base_call_set_multiparty (MM_BASE_CALL (ctx->other_calls->data), FALSE);
|
||||
mm_base_call_change_state (MM_BASE_CALL (ctx->other_calls->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
}
|
||||
/* If there are still more than one calls in the multiparty, just change state of all
|
||||
* of them. */
|
||||
else {
|
||||
GList *l;
|
||||
|
||||
for (l = ctx->other_calls; l; l = g_list_next (l))
|
||||
mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_HELD, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
}
|
||||
|
||||
/* The call that left would now be active */
|
||||
mm_base_call_set_multiparty (ctx->call, FALSE);
|
||||
mm_base_call_change_state (ctx->call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
LeaveMultipartyContext *ctx;
|
||||
MMCallList *list = NULL;
|
||||
MMCallState call_state;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* validate multiparty status */
|
||||
if (!mm_base_call_get_multiparty (call)) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"this call is not part of a multiparty call");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
/* validate call state */
|
||||
call_state = mm_base_call_get_state (call);
|
||||
if ((call_state != MM_CALL_STATE_ACTIVE) && (call_state != MM_CALL_STATE_HELD)) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"invalid call state (%s): must be either active or held",
|
||||
mm_call_state_get_string (call_state));
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty ||
|
||||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty_finish) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Cannot leave multiparty: unsupported");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_get (self,
|
||||
MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
|
||||
NULL);
|
||||
if (!list) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"Cannot leave multiparty: missing call list");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = g_slice_new0 (LeaveMultipartyContext);
|
||||
ctx->call = g_object_ref (call);
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) leave_multiparty_context_free);
|
||||
|
||||
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_leave_multiparty_foreach, ctx);
|
||||
g_object_unref (list);
|
||||
|
||||
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->leave_multiparty (self,
|
||||
call,
|
||||
(GAsyncReadyCallback)leave_multiparty_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Join calls into a multiparty call */
|
||||
|
||||
typedef struct {
|
||||
MMBaseCall *call;
|
||||
GList *all_calls;
|
||||
gboolean added;
|
||||
} JoinMultipartyContext;
|
||||
|
||||
static void
|
||||
join_multiparty_context_free (JoinMultipartyContext *ctx)
|
||||
{
|
||||
g_list_free_full (ctx->all_calls, g_object_unref);
|
||||
g_object_unref (ctx->call);
|
||||
g_slice_free (JoinMultipartyContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_join_multiparty_foreach (MMBaseCall *call,
|
||||
JoinMultipartyContext *ctx)
|
||||
{
|
||||
/* always add call that is being added */
|
||||
if ((call == ctx->call) || (g_strcmp0 (mm_base_call_get_path (call), mm_base_call_get_path (ctx->call)) == 0))
|
||||
ctx->added = TRUE;
|
||||
|
||||
/* ignore calls not currently ongoing */
|
||||
switch (mm_base_call_get_state (call)) {
|
||||
case MM_CALL_STATE_ACTIVE:
|
||||
case MM_CALL_STATE_HELD:
|
||||
ctx->all_calls = g_list_append (ctx->all_calls, g_object_ref (call));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
gboolean
|
||||
mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
return g_task_propagate_boolean (G_TASK (res), error);
|
||||
}
|
||||
|
||||
static void
|
||||
join_multiparty_ready (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GTask *task)
|
||||
{
|
||||
GError *error = NULL;
|
||||
JoinMultipartyContext *ctx;
|
||||
GList *l;
|
||||
|
||||
ctx = g_task_get_task_data (task);
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish (self, res, &error)) {
|
||||
g_task_return_error (task, error);
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = ctx->all_calls; l; l = g_list_next (l)) {
|
||||
mm_base_call_set_multiparty (MM_BASE_CALL (l->data), TRUE);
|
||||
mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_UNKNOWN);
|
||||
}
|
||||
|
||||
g_task_return_boolean (task, TRUE);
|
||||
g_object_unref (task);
|
||||
}
|
||||
|
||||
void
|
||||
mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
GTask *task;
|
||||
JoinMultipartyContext *ctx;
|
||||
MMCallList *list = NULL;
|
||||
MMCallState call_state;
|
||||
|
||||
task = g_task_new (self, NULL, callback, user_data);
|
||||
|
||||
/* validate multiparty status */
|
||||
if (mm_base_call_get_multiparty (call)) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"this call is already part of a multiparty call");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
/* validate call state */
|
||||
call_state = mm_base_call_get_state (call);
|
||||
if (call_state != MM_CALL_STATE_HELD) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"invalid call state (%s): must be held",
|
||||
mm_call_state_get_string (call_state));
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty ||
|
||||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty_finish) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Cannot join multiparty: unsupported");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_get (self,
|
||||
MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
|
||||
NULL);
|
||||
if (!list) {
|
||||
g_task_return_new_error (task, MM_CORE_ERROR, MM_CORE_ERROR_WRONG_STATE,
|
||||
"Cannot join multiparty: missing call list");
|
||||
g_object_unref (task);
|
||||
return;
|
||||
}
|
||||
|
||||
ctx = g_slice_new0 (JoinMultipartyContext);
|
||||
ctx->call = g_object_ref (call);
|
||||
g_task_set_task_data (task, ctx, (GDestroyNotify) join_multiparty_context_free);
|
||||
|
||||
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_join_multiparty_foreach, ctx);
|
||||
g_object_unref (list);
|
||||
|
||||
/* our logic makes sure that we would be adding the incoming call into the multiparty call */
|
||||
g_assert (ctx->added);
|
||||
|
||||
/* NOTE: we do not give the call we want to join, because the join operation acts on all
|
||||
* active/held calls. */
|
||||
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->join_multiparty (self,
|
||||
(GAsyncReadyCallback)join_multiparty_ready,
|
||||
task);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* In-call setup operation
|
||||
*
|
||||
|
||||
@@ -150,6 +150,23 @@ struct _MMIfaceModemVoice {
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Join multiparty */
|
||||
void (* join_multiparty) (MMIfaceModemVoice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* join_multiparty_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Leave multiparty */
|
||||
void (* leave_multiparty) (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* leave_multiparty_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Transfer */
|
||||
void (* transfer) (MMIfaceModemVoice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
@@ -207,4 +224,25 @@ void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self,
|
||||
guint index,
|
||||
const gchar *dtmf);
|
||||
|
||||
/* Join/Leave multiparty calls
|
||||
*
|
||||
* These actions are provided in the Call API, but implemented in the
|
||||
* modem Voice interface because they really affect multiple calls at
|
||||
* the same time.
|
||||
*/
|
||||
void mm_iface_modem_voice_join_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_iface_modem_voice_join_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
void mm_iface_modem_voice_leave_multiparty (MMIfaceModemVoice *self,
|
||||
MMBaseCall *call,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_iface_modem_voice_leave_multiparty_finish (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
#endif /* MM_IFACE_MODEM_VOICE_H */
|
||||
|
||||
Reference in New Issue
Block a user