api,call: new JoinMultiparty() and LeaveMultiparty() methods

This commit is contained in:
Aleksander Morgado
2019-07-02 17:53:25 +02:00
parent 6f23519239
commit 511b0ff244
8 changed files with 801 additions and 0 deletions

View File

@@ -50,6 +50,8 @@ static gboolean info_flag; /* set when no action found */
static gboolean start_flag; static gboolean start_flag;
static gboolean accept_flag; static gboolean accept_flag;
static gchar *deflect_str; static gchar *deflect_str;
static gboolean join_multiparty_flag;
static gboolean leave_multiparty_flag;
static gboolean hangup_flag; static gboolean hangup_flag;
static gchar *dtmf_request; static gchar *dtmf_request;
@@ -66,6 +68,14 @@ static GOptionEntry entries[] = {
"Deflect the incoming call", "Deflect the incoming call",
"[NUMBER]", "[NUMBER]",
}, },
{ "join-multiparty", 0, 0, G_OPTION_ARG_NONE, &join_multiparty_flag,
"Join multiparty call",
NULL,
},
{ "leave-multiparty", 0, 0, G_OPTION_ARG_NONE, &leave_multiparty_flag,
"Leave multiparty call",
NULL,
},
{ "hangup", 0, 0, G_OPTION_ARG_NONE, &hangup_flag, { "hangup", 0, 0, G_OPTION_ARG_NONE, &hangup_flag,
"Hang up the call", "Hang up the call",
NULL, NULL,
@@ -105,6 +115,8 @@ mmcli_call_options_enabled (void)
n_actions = (start_flag + n_actions = (start_flag +
accept_flag + accept_flag +
!!deflect_str + !!deflect_str +
join_multiparty_flag +
leave_multiparty_flag +
hangup_flag + hangup_flag +
!!dtmf_request); !!dtmf_request);
@@ -261,6 +273,60 @@ deflect_ready (MMCall *call,
mmcli_async_operation_done (); mmcli_async_operation_done ();
} }
static void
join_multiparty_process_reply (gboolean result,
const GError *error)
{
if (!result) {
g_printerr ("error: couldn't join multiparty call: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
g_print ("successfully joined multiparty call\n");
}
static void
join_multiparty_ready (MMCall *call,
GAsyncResult *result,
gpointer nothing)
{
gboolean operation_result;
GError *error = NULL;
operation_result = mm_call_join_multiparty_finish (call, result, &error);
join_multiparty_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void
leave_multiparty_process_reply (gboolean result,
const GError *error)
{
if (!result) {
g_printerr ("error: couldn't leave multiparty call: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
g_print ("successfully left multiparty call\n");
}
static void
leave_multiparty_ready (MMCall *call,
GAsyncResult *result,
gpointer nothing)
{
gboolean operation_result;
GError *error = NULL;
operation_result = mm_call_leave_multiparty_finish (call, result, &error);
leave_multiparty_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void static void
hangup_process_reply (gboolean result, hangup_process_reply (gboolean result,
const GError *error) const GError *error)
@@ -357,6 +423,24 @@ get_call_ready (GObject *source,
return; return;
} }
/* Requesting to join multiparty call? */
if (join_multiparty_flag) {
mm_call_join_multiparty (ctx->call,
ctx->cancellable,
(GAsyncReadyCallback)join_multiparty_ready,
NULL);
return;
}
/* Requesting to leave multiparty call? */
if (leave_multiparty_flag) {
mm_call_leave_multiparty (ctx->call,
ctx->cancellable,
(GAsyncReadyCallback)leave_multiparty_ready,
NULL);
return;
}
/* Requesting to hangup the call? */ /* Requesting to hangup the call? */
if (hangup_flag) { if (hangup_flag) {
mm_call_hangup (ctx->call, mm_call_hangup (ctx->call,
@@ -454,6 +538,28 @@ mmcli_call_run_synchronous (GDBusConnection *connection)
return; return;
} }
/* Requesting to join multiparty call? */
if (join_multiparty_flag) {
gboolean operation_result;
operation_result = mm_call_join_multiparty_sync (ctx->call,
NULL,
&error);
join_multiparty_process_reply (operation_result, error);
return;
}
/* Requesting to leave multiparty call? */
if (leave_multiparty_flag) {
gboolean operation_result;
operation_result = mm_call_leave_multiparty_sync (ctx->call,
NULL,
&error);
leave_multiparty_process_reply (operation_result, error);
return;
}
/* Requesting to hangup the call? */ /* Requesting to hangup the call? */
if (hangup_flag) { if (hangup_flag) {
gboolean operation_result; gboolean operation_result;

View File

@@ -1349,6 +1349,12 @@ mm_call_send_dtmf_sync
mm_call_deflect mm_call_deflect
mm_call_deflect_finish mm_call_deflect_finish
mm_call_deflect_sync mm_call_deflect_sync
mm_call_join_multiparty
mm_call_join_multiparty_finish
mm_call_join_multiparty_sync
mm_call_leave_multiparty
mm_call_leave_multiparty_finish
mm_call_leave_multiparty_sync
<SUBSECTION Standard> <SUBSECTION Standard>
MMCallClass MMCallClass
MMCallPrivate MMCallPrivate
@@ -3264,6 +3270,12 @@ mm_gdbus_call_call_send_dtmf_sync
mm_gdbus_call_call_deflect mm_gdbus_call_call_deflect
mm_gdbus_call_call_deflect_finish mm_gdbus_call_call_deflect_finish
mm_gdbus_call_call_deflect_sync mm_gdbus_call_call_deflect_sync
mm_gdbus_call_call_join_multiparty
mm_gdbus_call_call_join_multiparty_finish
mm_gdbus_call_call_join_multiparty_sync
mm_gdbus_call_call_leave_multiparty
mm_gdbus_call_call_leave_multiparty_finish
mm_gdbus_call_call_leave_multiparty_sync
<SUBSECTION Private> <SUBSECTION Private>
mm_gdbus_call_set_direction mm_gdbus_call_set_direction
mm_gdbus_call_set_number mm_gdbus_call_set_number
@@ -3277,6 +3289,8 @@ mm_gdbus_call_complete_hangup
mm_gdbus_call_complete_send_dtmf mm_gdbus_call_complete_send_dtmf
mm_gdbus_call_complete_start mm_gdbus_call_complete_start
mm_gdbus_call_complete_deflect mm_gdbus_call_complete_deflect
mm_gdbus_call_complete_join_multiparty
mm_gdbus_call_complete_leave_multiparty
mm_gdbus_call_interface_info mm_gdbus_call_interface_info
mm_gdbus_call_override_properties mm_gdbus_call_override_properties
mm_gdbus_call_emit_dtmf_received mm_gdbus_call_emit_dtmf_received

View File

@@ -52,6 +52,33 @@
<arg name="number" type="s" /> <arg name="number" type="s" />
</method> </method>
<!--
JoinMultiparty:
Join the currently held call into a single multiparty call with another
already active call.
The calls will be flagged with the
'<link linkend="gdbus-property-org-freedesktop-ModemManager1-Call.Multiparty">Multiparty</link>'
property while they are part of the multiparty call.
Applicable only if state is <link linkend="MM-CALL-STATE-HELD:CAPS"><constant>MM_CALL_STATE_HELD</constant></link>.
-->
<method name="JoinMultiparty" />
<!--
LeaveMultiparty:
If this call is part of an ongoing multiparty call, detach it from the multiparty call,
put the multiparty call on hold, and activate this one alone. This operation makes this
call private again between both ends of the call.
Applicable only if state is <link linkend="MM-CALL-STATE-ACTIVE:CAPS"><constant>MM_CALL_STATE_ACTIVE</constant></link> or
<link linkend="MM-CALL-STATE-HELD:CAPS"><constant>MM_CALL_STATE_HELD</constant></link> and
the call is a multiparty call.
-->
<method name="LeaveMultiparty"/>
<!-- <!--
Hangup: Hangup:

View File

@@ -592,6 +592,156 @@ mm_call_deflect_sync (MMCall *self,
error); error);
} }
/*****************************************************************************/
/**
* mm_call_join_multiparty_finish:
* @self: A #MMCall.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_call_join_multiparty().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_call_join_multiparty().
*
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
*/
gboolean
mm_call_join_multiparty_finish (MMCall *self,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
return mm_gdbus_call_call_join_multiparty_finish (MM_GDBUS_CALL (self), res, error);
}
/**
* mm_call_join_multiparty:
* @self: A #MMCall.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
* @user_data: User data to pass to @callback.
*
* Synchronously requests to join this call into a multiparty call.
*
* When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
* You can then call mm_call_join_multiparty_finish() to get the result of the operation.
*
* See mm_call_join_multiparty_sync() for the synchronous, blocking version of this method.
*/
void
mm_call_join_multiparty (MMCall *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_CALL (self));
mm_gdbus_call_call_join_multiparty (MM_GDBUS_CALL (self),
cancellable,
callback,
user_data);
}
/**
* mm_call_join_multiparty_sync:
* @self: A #MMCall.g
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests to join this call into a multiparty call.
*
* The calling thread is blocked until an incoming call is ready.
* See mm_call_join_multiparty() for the asynchronous version of this method.
*
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
*/
gboolean
mm_call_join_multiparty_sync (MMCall *self,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
return mm_gdbus_call_call_join_multiparty_sync (MM_GDBUS_CALL (self),
cancellable,
error);
}
/**
* mm_call_leave_multiparty_finish:
* @self: A #MMCall.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_call_leave_multiparty().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_call_leave_multiparty().
*
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
*/
gboolean
mm_call_leave_multiparty_finish (MMCall *self,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
return mm_gdbus_call_call_leave_multiparty_finish (MM_GDBUS_CALL (self), res, error);
}
/**
* mm_call_leave_multiparty:
* @self: A #MMCall.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @callback: A #GAsyncReadyCallback to call when the request is satisfied or %NULL.
* @user_data: User data to pass to @callback.
*
* Synchronously requests to make this call private again by leaving the
* multiparty call.
*
* When the operation is finished, @callback will be invoked in the <link linkend="g-main-context-push-thread-default">thread-default main loop</link> of the thread you are calling this method from.
* You can then call mm_call_leave_multiparty_finish() to get the result of the operation.
*
* See mm_call_leave_multiparty_sync() for the synchronous, blocking version of this method.
*/
void
mm_call_leave_multiparty (MMCall *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_CALL (self));
mm_gdbus_call_call_leave_multiparty (MM_GDBUS_CALL (self),
cancellable,
callback,
user_data);
}
/**
* mm_call_leave_multiparty_sync:
* @self: A #MMCall.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously requests to make this call private again by leaving the
* multiparty call.
*
* The calling thread is blocked until an incoming call is ready.
* See mm_call_leave_multiparty() for the asynchronous version of this method.
*
* Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
*/
gboolean
mm_call_leave_multiparty_sync (MMCall *self,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_CALL (self), FALSE);
return mm_gdbus_call_call_leave_multiparty_sync (MM_GDBUS_CALL (self),
cancellable,
error);
}
/*****************************************************************************/ /*****************************************************************************/
/** /**

View File

@@ -127,6 +127,31 @@ gboolean mm_call_deflect_sync (MMCall *self,
GCancellable *cancellable, GCancellable *cancellable,
GError **error); GError **error);
void mm_call_join_multiparty (MMCall *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_call_join_multiparty_finish (MMCall *self,
GAsyncResult *res,
GError **error);
gboolean mm_call_join_multiparty_sync (MMCall *self,
GCancellable *cancellable,
GError **error);
void mm_call_leave_multiparty (MMCall *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_call_leave_multiparty_finish (MMCall *self,
GAsyncResult *res,
GError **error);
gboolean mm_call_leave_multiparty_sync (MMCall *self,
GCancellable *cancellable,
GError **error);
void mm_call_hangup (MMCall *self, void mm_call_hangup (MMCall *self,
GCancellable *cancellable, GCancellable *cancellable,

View File

@@ -461,6 +461,157 @@ handle_deflect (MMBaseCall *self,
return TRUE; 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) */ /* Hangup call (DBus call handling) */
@@ -709,6 +860,14 @@ call_dbus_export (MMBaseCall *self)
"handle-deflect", "handle-deflect",
G_CALLBACK (handle_deflect), G_CALLBACK (handle_deflect),
NULL); 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, g_signal_connect (self,
"handle-hangup", "handle-hangup",
G_CALLBACK (handle_hangup), G_CALLBACK (handle_hangup),

View File

@@ -1261,6 +1261,288 @@ handle_transfer (MmGdbusModemVoice *skeleton,
return TRUE; 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 /* In-call setup operation
* *

View File

@@ -150,6 +150,23 @@ struct _MMIfaceModemVoice {
GAsyncResult *res, GAsyncResult *res,
GError **error); 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 */ /* Transfer */
void (* transfer) (MMIfaceModemVoice *self, void (* transfer) (MMIfaceModemVoice *self,
GAsyncReadyCallback callback, GAsyncReadyCallback callback,
@@ -207,4 +224,25 @@ void mm_iface_modem_voice_received_dtmf (MMIfaceModemVoice *self,
guint index, guint index,
const gchar *dtmf); 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 */ #endif /* MM_IFACE_MODEM_VOICE_H */