api,voice: new Transfer() method
This method will join all active and held calls into a single multiparty call, and then request the network to terminate the call on the subscriber's end and transfer the control of the call to the parties that are still in the call.
This commit is contained in:
+50
-1
@@ -53,6 +53,7 @@ static gchar *delete_str;
|
||||
static gboolean hold_and_accept_flag;
|
||||
static gboolean hangup_and_accept_flag;
|
||||
static gboolean hangup_all_flag;
|
||||
static gboolean transfer_flag;
|
||||
|
||||
static GOptionEntry entries[] = {
|
||||
{ "voice-list-calls", 0, 0, G_OPTION_ARG_NONE, &list_flag,
|
||||
@@ -79,6 +80,10 @@ static GOptionEntry entries[] = {
|
||||
"Hangs up all ongoing (active, waiting, held) calls",
|
||||
NULL
|
||||
},
|
||||
{ "voice-transfer", 0, 0, G_OPTION_ARG_NONE, &transfer_flag,
|
||||
"Joins active and held calls and disconnects from them",
|
||||
NULL
|
||||
},
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
@@ -111,7 +116,8 @@ mmcli_modem_voice_options_enabled (void)
|
||||
!!delete_str +
|
||||
hold_and_accept_flag +
|
||||
hangup_and_accept_flag +
|
||||
hangup_all_flag);
|
||||
hangup_all_flag +
|
||||
transfer_flag);
|
||||
|
||||
if (n_actions > 1) {
|
||||
g_printerr ("error: too many Voice actions requested\n");
|
||||
@@ -193,6 +199,31 @@ output_call_info (MMCall *call)
|
||||
g_free (extra);
|
||||
}
|
||||
|
||||
static void
|
||||
transfer_process_reply (const GError *error)
|
||||
{
|
||||
if (error) {
|
||||
g_printerr ("error: couldn't hangup all: '%s'\n",
|
||||
error->message);
|
||||
exit (EXIT_FAILURE);
|
||||
}
|
||||
|
||||
g_print ("operation successful\n");
|
||||
}
|
||||
|
||||
static void
|
||||
transfer_ready (MMModemVoice *modem,
|
||||
GAsyncResult *result,
|
||||
gpointer nothing)
|
||||
{
|
||||
GError *error = NULL;
|
||||
|
||||
mm_modem_voice_transfer_finish (modem, result, &error);
|
||||
transfer_process_reply (error);
|
||||
|
||||
mmcli_async_operation_done ();
|
||||
}
|
||||
|
||||
static void
|
||||
hangup_all_process_reply (const GError *error)
|
||||
{
|
||||
@@ -457,6 +488,16 @@ get_modem_ready (GObject *source,
|
||||
return;
|
||||
}
|
||||
|
||||
/* Request to transfer? */
|
||||
if (transfer_flag) {
|
||||
g_debug ("Asynchronously transferring calls...");
|
||||
mm_modem_voice_transfer (ctx->modem_voice,
|
||||
ctx->cancellable,
|
||||
(GAsyncReadyCallback)transfer_ready,
|
||||
NULL);
|
||||
return;
|
||||
}
|
||||
|
||||
g_warn_if_reached ();
|
||||
}
|
||||
|
||||
@@ -577,5 +618,13 @@ mmcli_modem_voice_run_synchronous (GDBusConnection *connection)
|
||||
return;
|
||||
}
|
||||
|
||||
/* Request to transfer? */
|
||||
if (transfer_flag) {
|
||||
g_debug ("Synchronously transferring calls...");
|
||||
mm_modem_voice_transfer_sync (ctx->modem_voice, NULL, &error);
|
||||
transfer_process_reply (error);
|
||||
return;
|
||||
}
|
||||
|
||||
g_warn_if_reached ();
|
||||
}
|
||||
|
||||
@@ -1021,6 +1021,9 @@ mm_modem_voice_hold_and_accept_sync
|
||||
mm_modem_voice_hangup_all
|
||||
mm_modem_voice_hangup_all_finish
|
||||
mm_modem_voice_hangup_all_sync
|
||||
mm_modem_voice_transfer
|
||||
mm_modem_voice_transfer_finish
|
||||
mm_modem_voice_transfer_sync
|
||||
<SUBSECTION Standard>
|
||||
MMModemVoiceClass
|
||||
MMModemVoicePrivate
|
||||
@@ -2853,6 +2856,9 @@ mm_gdbus_modem_voice_call_hold_and_accept_sync
|
||||
mm_gdbus_modem_voice_call_hangup_all
|
||||
mm_gdbus_modem_voice_call_hangup_all_finish
|
||||
mm_gdbus_modem_voice_call_hangup_all_sync
|
||||
mm_gdbus_modem_voice_call_transfer
|
||||
mm_gdbus_modem_voice_call_transfer_finish
|
||||
mm_gdbus_modem_voice_call_transfer_sync
|
||||
<SUBSECTION Private>
|
||||
mm_gdbus_modem_voice_set_calls
|
||||
mm_gdbus_modem_voice_emit_call_added
|
||||
@@ -2863,6 +2869,7 @@ mm_gdbus_modem_voice_complete_list_calls
|
||||
mm_gdbus_modem_voice_complete_hangup_and_accept
|
||||
mm_gdbus_modem_voice_complete_hold_and_accept
|
||||
mm_gdbus_modem_voice_complete_hangup_all
|
||||
mm_gdbus_modem_voice_complete_transfer
|
||||
mm_gdbus_modem_voice_interface_info
|
||||
mm_gdbus_modem_voice_override_properties
|
||||
<SUBSECTION Standard>
|
||||
|
||||
@@ -1386,6 +1386,7 @@ typedef enum { /*< underscore_name=mm_call_state >*/
|
||||
* @MM_CALL_STATE_REASON_REFUSED_OR_BUSY: Remote peer is busy or refused call.
|
||||
* @MM_CALL_STATE_REASON_ERROR: Wrong number or generic network error.
|
||||
* @MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED: Error setting up audio channel.
|
||||
* @MM_CALL_STATE_REASON_TRANSFERRED: Call has been transferred. Since 1.12.
|
||||
*
|
||||
* Reason for the state change in the call.
|
||||
*/
|
||||
@@ -1397,7 +1398,8 @@ typedef enum { /*< underscore_name=mm_call_state_reason >*/
|
||||
MM_CALL_STATE_REASON_TERMINATED = 4,
|
||||
MM_CALL_STATE_REASON_REFUSED_OR_BUSY = 5,
|
||||
MM_CALL_STATE_REASON_ERROR = 6,
|
||||
MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED = 7
|
||||
MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED = 7,
|
||||
MM_CALL_STATE_REASON_TRANSFERRED = 8,
|
||||
} MMCallStateReason;
|
||||
|
||||
/**
|
||||
|
||||
@@ -111,6 +111,17 @@
|
||||
-->
|
||||
<method name="HangupAll" />
|
||||
|
||||
<!--
|
||||
Transfer:
|
||||
|
||||
Join the currently active and held calls together into a single
|
||||
multiparty call, but disconnects from them.
|
||||
|
||||
The affected calls will be considered terminated from the point of
|
||||
view of the subscriber.
|
||||
-->
|
||||
<method name="Transfer" />
|
||||
|
||||
<!--
|
||||
CallAdded:
|
||||
@path: Object path of the new call.
|
||||
|
||||
@@ -769,6 +769,85 @@ mm_modem_voice_hangup_all_sync (MMModemVoice *self,
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
/**
|
||||
* mm_modem_voice_transfer_finish:
|
||||
* @self: A #MMModemVoice.
|
||||
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_transfer().
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Finishes an operation started with mm_modem_voice_transfer().
|
||||
*
|
||||
* Returns: %TRUE if the operation was successful, %FALSE if @error is set.
|
||||
* Since: 1.12
|
||||
*/
|
||||
gboolean
|
||||
mm_modem_voice_transfer_finish (MMModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
|
||||
|
||||
return mm_gdbus_modem_voice_call_transfer_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_modem_voice_transfer:
|
||||
* @self: A #MMModemVoice.
|
||||
* @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.
|
||||
*
|
||||
* Asynchronously joins all active and held calls, and disconnects from them.
|
||||
*
|
||||
* 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_modem_voice_transfer_finish() to get the result of the operation.
|
||||
*
|
||||
* See mm_modem_voice_transfer_sync() for the synchronous, blocking version of this method.
|
||||
*
|
||||
* Since: 1.12
|
||||
*/
|
||||
void
|
||||
mm_modem_voice_transfer (MMModemVoice *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data)
|
||||
{
|
||||
g_return_if_fail (MM_IS_MODEM_VOICE (self));
|
||||
|
||||
mm_gdbus_modem_voice_call_transfer (MM_GDBUS_MODEM_VOICE (self),
|
||||
cancellable,
|
||||
callback,
|
||||
user_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* mm_modem_voice_transfer_sync:
|
||||
* @self: A #MMModemVoice.
|
||||
* @cancellable: (allow-none): A #GCancellable or %NULL.
|
||||
* @error: Return location for error or %NULL.
|
||||
*
|
||||
* Synchronously joins all active and held calls, and disconnects from them.
|
||||
*
|
||||
* The calling thread is blocked until a reply is received. See mm_modem_voice_transfer()
|
||||
* for the asynchronous version of this method.
|
||||
*
|
||||
* Returns: %TRUE if the operation was successful, %FALSE if @error is set.
|
||||
* Since: 1.12
|
||||
*/
|
||||
gboolean
|
||||
mm_modem_voice_transfer_sync (MMModemVoice *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error)
|
||||
{
|
||||
g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
|
||||
|
||||
return mm_gdbus_modem_voice_call_transfer_sync (MM_GDBUS_MODEM_VOICE (self),
|
||||
cancellable,
|
||||
error);
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
static void
|
||||
mm_modem_voice_init (MMModemVoice *self)
|
||||
{
|
||||
|
||||
@@ -143,6 +143,17 @@ gboolean mm_modem_voice_hangup_all_sync (MMModemVoice *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
void mm_modem_voice_transfer (MMModemVoice *self,
|
||||
GCancellable *cancellable,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean mm_modem_voice_transfer_finish (MMModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
gboolean mm_modem_voice_transfer_sync (MMModemVoice *self,
|
||||
GCancellable *cancellable,
|
||||
GError **error);
|
||||
|
||||
G_END_DECLS
|
||||
|
||||
#endif /* _MM_MODEM_VOICE_H_ */
|
||||
|
||||
@@ -981,6 +981,137 @@ handle_hangup_all (MmGdbusModemVoice *skeleton,
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
|
||||
typedef struct {
|
||||
MmGdbusModemVoice *skeleton;
|
||||
GDBusMethodInvocation *invocation;
|
||||
MMIfaceModemVoice *self;
|
||||
GList *calls;
|
||||
} HandleTransferContext;
|
||||
|
||||
static void
|
||||
handle_transfer_context_free (HandleTransferContext *ctx)
|
||||
{
|
||||
g_list_free_full (ctx->calls, g_object_unref);
|
||||
g_object_unref (ctx->skeleton);
|
||||
g_object_unref (ctx->invocation);
|
||||
g_object_unref (ctx->self);
|
||||
g_slice_free (HandleTransferContext, ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
transfer_ready (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
HandleTransferContext *ctx)
|
||||
{
|
||||
GError *error = NULL;
|
||||
GList *l;
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer_finish (self, res, &error)) {
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_transfer_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
for (l = ctx->calls; l; l = g_list_next (l))
|
||||
mm_base_call_change_state (MM_BASE_CALL (l->data), MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_TRANSFERRED);
|
||||
|
||||
mm_gdbus_modem_voice_complete_transfer (ctx->skeleton, ctx->invocation);
|
||||
handle_transfer_context_free (ctx);
|
||||
}
|
||||
|
||||
static void
|
||||
prepare_transfer_foreach (MMBaseCall *call,
|
||||
HandleTransferContext *ctx)
|
||||
{
|
||||
switch (mm_base_call_get_state (call)) {
|
||||
case MM_CALL_STATE_ACTIVE:
|
||||
case MM_CALL_STATE_HELD:
|
||||
ctx->calls = g_list_append (ctx->calls, g_object_ref (call));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
handle_transfer_auth_ready (MMBaseModem *self,
|
||||
GAsyncResult *res,
|
||||
HandleTransferContext *ctx)
|
||||
{
|
||||
MMModemState modem_state = MM_MODEM_STATE_UNKNOWN;
|
||||
GError *error = NULL;
|
||||
MMCallList *list = NULL;
|
||||
|
||||
if (!mm_base_modem_authorize_finish (self, res, &error)) {
|
||||
g_dbus_method_invocation_take_error (ctx->invocation, error);
|
||||
handle_transfer_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_get (self,
|
||||
MM_IFACE_MODEM_STATE, &modem_state,
|
||||
NULL);
|
||||
|
||||
if (modem_state < MM_MODEM_STATE_ENABLED) {
|
||||
g_dbus_method_invocation_return_error (ctx->invocation,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_WRONG_STATE,
|
||||
"Cannot transfer: device not yet enabled");
|
||||
handle_transfer_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer ||
|
||||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer_finish) {
|
||||
g_dbus_method_invocation_return_error (ctx->invocation,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_UNSUPPORTED,
|
||||
"Cannot transfer: unsupported");
|
||||
handle_transfer_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
|
||||
g_object_get (self,
|
||||
MM_IFACE_MODEM_VOICE_CALL_LIST, &list,
|
||||
NULL);
|
||||
if (!list) {
|
||||
g_dbus_method_invocation_return_error (ctx->invocation,
|
||||
MM_CORE_ERROR,
|
||||
MM_CORE_ERROR_WRONG_STATE,
|
||||
"Cannot transfer: missing call list");
|
||||
handle_transfer_context_free (ctx);
|
||||
return;
|
||||
}
|
||||
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_transfer_foreach, ctx);
|
||||
g_object_unref (list);
|
||||
|
||||
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->transfer (MM_IFACE_MODEM_VOICE (self),
|
||||
(GAsyncReadyCallback)transfer_ready,
|
||||
ctx);
|
||||
}
|
||||
|
||||
static gboolean
|
||||
handle_transfer (MmGdbusModemVoice *skeleton,
|
||||
GDBusMethodInvocation *invocation,
|
||||
MMIfaceModemVoice *self)
|
||||
{
|
||||
HandleTransferContext *ctx;
|
||||
|
||||
ctx = g_slice_new0 (HandleTransferContext);
|
||||
ctx->skeleton = g_object_ref (skeleton);
|
||||
ctx->invocation = g_object_ref (invocation);
|
||||
ctx->self = g_object_ref (self);
|
||||
|
||||
mm_base_modem_authorize (MM_BASE_MODEM (self),
|
||||
invocation,
|
||||
MM_AUTHORIZATION_VOICE,
|
||||
(GAsyncReadyCallback)handle_transfer_auth_ready,
|
||||
ctx);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Call list polling logic
|
||||
*
|
||||
@@ -1675,6 +1806,10 @@ interface_initialization_step (GTask *task)
|
||||
"handle-hangup-all",
|
||||
G_CALLBACK (handle_hangup_all),
|
||||
self);
|
||||
g_signal_connect (ctx->skeleton,
|
||||
"handle-transfer",
|
||||
G_CALLBACK (handle_transfer),
|
||||
self);
|
||||
|
||||
/* Finally, export the new interface */
|
||||
mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self),
|
||||
|
||||
@@ -115,6 +115,14 @@ struct _MMIfaceModemVoice {
|
||||
gboolean (* hangup_all_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
|
||||
/* Transfer */
|
||||
void (* transfer) (MMIfaceModemVoice *self,
|
||||
GAsyncReadyCallback callback,
|
||||
gpointer user_data);
|
||||
gboolean (* transfer_finish) (MMIfaceModemVoice *self,
|
||||
GAsyncResult *res,
|
||||
GError **error);
|
||||
};
|
||||
|
||||
GType mm_iface_modem_voice_get_type (void);
|
||||
|
||||
Reference in New Issue
Block a user