api,voice: new HangupAll() method

This method will terminate all ongoing calls.
This commit is contained in:
Aleksander Morgado
2019-06-27 14:25:14 +02:00
parent f065105f17
commit efcc4c60fe
7 changed files with 326 additions and 1 deletions

View File

@@ -52,6 +52,7 @@ static gchar *create_str;
static gchar *delete_str;
static gboolean hold_and_accept_flag;
static gboolean hangup_and_accept_flag;
static gboolean hangup_all_flag;
static GOptionEntry entries[] = {
{ "voice-list-calls", 0, 0, G_OPTION_ARG_NONE, &list_flag,
@@ -74,6 +75,10 @@ static GOptionEntry entries[] = {
"Hangs up all active calls and accepts the next waiting or held call",
NULL
},
{ "voice-hangup-all", 0, 0, G_OPTION_ARG_NONE, &hangup_all_flag,
"Hangs up all ongoing (active, waiting, held) calls",
NULL
},
{ NULL }
};
@@ -105,7 +110,8 @@ mmcli_modem_voice_options_enabled (void)
!!create_str +
!!delete_str +
hold_and_accept_flag +
hangup_and_accept_flag);
hangup_and_accept_flag +
hangup_all_flag);
if (n_actions > 1) {
g_printerr ("error: too many Voice actions requested\n");
@@ -187,6 +193,31 @@ output_call_info (MMCall *call)
g_free (extra);
}
static void
hangup_all_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
hangup_all_ready (MMModemVoice *modem,
GAsyncResult *result,
gpointer nothing)
{
GError *error = NULL;
mm_modem_voice_hangup_all_finish (modem, result, &error);
hangup_all_process_reply (error);
mmcli_async_operation_done ();
}
static void
hangup_and_accept_process_reply (const GError *error)
{
@@ -416,6 +447,16 @@ get_modem_ready (GObject *source,
return;
}
/* Request to hangup all? */
if (hangup_all_flag) {
g_debug ("Asynchronously hanging up all calls...");
mm_modem_voice_hangup_all (ctx->modem_voice,
ctx->cancellable,
(GAsyncReadyCallback)hangup_all_ready,
NULL);
return;
}
g_warn_if_reached ();
}
@@ -528,5 +569,13 @@ mmcli_modem_voice_run_synchronous (GDBusConnection *connection)
return;
}
/* Request to hangup all? */
if (hangup_all_flag) {
g_debug ("Synchronously hanging up all calls...");
mm_modem_voice_hangup_all_sync (ctx->modem_voice, NULL, &error);
hangup_all_process_reply (error);
return;
}
g_warn_if_reached ();
}

View File

@@ -1018,6 +1018,9 @@ mm_modem_voice_hangup_and_accept_sync
mm_modem_voice_hold_and_accept
mm_modem_voice_hold_and_accept_finish
mm_modem_voice_hold_and_accept_sync
mm_modem_voice_hangup_all
mm_modem_voice_hangup_all_finish
mm_modem_voice_hangup_all_sync
<SUBSECTION Standard>
MMModemVoiceClass
MMModemVoicePrivate
@@ -2847,6 +2850,9 @@ mm_gdbus_modem_voice_call_hangup_and_accept_sync
mm_gdbus_modem_voice_call_hold_and_accept
mm_gdbus_modem_voice_call_hold_and_accept_finish
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
<SUBSECTION Private>
mm_gdbus_modem_voice_set_calls
mm_gdbus_modem_voice_emit_call_added
@@ -2856,6 +2862,7 @@ mm_gdbus_modem_voice_complete_delete_call
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_interface_info
mm_gdbus_modem_voice_override_properties
<SUBSECTION Standard>

View File

@@ -99,6 +99,18 @@
-->
<method name="HangupAndAccept" />
<!--
HangupAll:
Hangup all active calls.
Depending on how the device implements the action, calls on
hold or in waiting state may also be terminated.
No error is returned if there are no ongoing calls.
-->
<method name="HangupAll" />
<!--
CallAdded:
@path: Object path of the new call.

View File

@@ -690,6 +690,85 @@ mm_modem_voice_hangup_and_accept_sync (MMModemVoice *self,
/*****************************************************************************/
/**
* mm_modem_voice_hangup_all_finish:
* @self: A #MMModemVoice.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_hangup_all().
* @error: Return location for error or %NULL.
*
* Finishes an operation started with mm_modem_voice_hangup_all().
*
* Returns: %TRUE if the operation was successful, %FALSE if @error is set.
* Since: 1.12
*/
gboolean
mm_modem_voice_hangup_all_finish (MMModemVoice *self,
GAsyncResult *res,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
return mm_gdbus_modem_voice_call_hangup_all_finish (MM_GDBUS_MODEM_VOICE (self), res, error);
}
/**
* mm_modem_voice_hangup_all:
* @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 hangs up all ongoing (active, waiting, held) calls.
*
* 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_hangup_all_finish() to get the result of the operation.
*
* See mm_modem_voice_hangup_all_sync() for the synchronous, blocking version of this method.
*
* Since: 1.12
*/
void
mm_modem_voice_hangup_all (MMModemVoice *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_return_if_fail (MM_IS_MODEM_VOICE (self));
mm_gdbus_modem_voice_call_hangup_all (MM_GDBUS_MODEM_VOICE (self),
cancellable,
callback,
user_data);
}
/**
* mm_modem_voice_hangup_all_sync:
* @self: A #MMModemVoice.
* @cancellable: (allow-none): A #GCancellable or %NULL.
* @error: Return location for error or %NULL.
*
* Synchronously hangs up all ongoing (active, waiting, held) calls.
*
* The calling thread is blocked until a reply is received. See mm_modem_voice_hangup_all()
* 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_hangup_all_sync (MMModemVoice *self,
GCancellable *cancellable,
GError **error)
{
g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE);
return mm_gdbus_modem_voice_call_hangup_all_sync (MM_GDBUS_MODEM_VOICE (self),
cancellable,
error);
}
/*****************************************************************************/
static void
mm_modem_voice_init (MMModemVoice *self)
{

View File

@@ -132,6 +132,17 @@ gboolean mm_modem_voice_hangup_and_accept_sync (MMModemVoice *self,
GCancellable *cancellable,
GError **error);
void mm_modem_voice_hangup_all (MMModemVoice *self,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean mm_modem_voice_hangup_all_finish (MMModemVoice *self,
GAsyncResult *res,
GError **error);
gboolean mm_modem_voice_hangup_all_sync (MMModemVoice *self,
GCancellable *cancellable,
GError **error);
G_END_DECLS
#endif /* _MM_MODEM_VOICE_H_ */

View File

@@ -826,6 +826,161 @@ handle_hangup_and_accept (MmGdbusModemVoice *skeleton,
return TRUE;
}
/*****************************************************************************/
typedef struct {
MmGdbusModemVoice *skeleton;
GDBusMethodInvocation *invocation;
MMIfaceModemVoice *self;
GList *calls;
} HandleHangupAllContext;
static void
handle_hangup_all_context_free (HandleHangupAllContext *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 (HandleHangupAllContext, ctx);
}
static void
hangup_all_ready (MMIfaceModemVoice *self,
GAsyncResult *res,
HandleHangupAllContext *ctx)
{
GError *error = NULL;
GList *l;
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all_finish (self, res, &error)) {
g_dbus_method_invocation_take_error (ctx->invocation, error);
handle_hangup_all_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_TERMINATED);
mm_gdbus_modem_voice_complete_hangup_all (ctx->skeleton, ctx->invocation);
handle_hangup_all_context_free (ctx);
}
static void
prepare_hangup_all_foreach (MMBaseCall *call,
HandleHangupAllContext *ctx)
{
/* The implementation of this operation will usually be done with +CHUP, and we
* know that +CHUP is implemented in different ways by different manufacturers.
*
* The 3GPP TS27.007 spec for +CHUP states that the "Execution command causes
* the TA to hangup the current call of the MT." This sentence leaves a bit of open
* interpretation to the implementors, because a current call can be considered only
* the active ones, or otherwise any call (active, held or waiting).
*
* And so, the u-blox TOBY-L4 takes one interpretation and "In case of multiple
* calls, all active calls will be released, while waiting and held calls are not".
*
* And the Cinterion PLS-8 takes a different interpretation and cancels all calls,
* including the waiting and held ones.
*
* In this logic, we're going to terminate exclusively the ACTIVE calls only, and we
* will leave the possible termination of waiting/held calls to be reported via
* call state updates, e.g. +CLCC polling or other plugin-specific method. In the
* case of the Cinterion PLS-8, we'll detect the termination of the waiting and
* held calls via ^SLCC URCs.
*/
switch (mm_base_call_get_state (call)) {
case MM_CALL_STATE_DIALING:
case MM_CALL_STATE_RINGING_OUT:
case MM_CALL_STATE_RINGING_IN:
case MM_CALL_STATE_ACTIVE:
ctx->calls = g_list_append (ctx->calls, g_object_ref (call));
break;
case MM_CALL_STATE_WAITING:
case MM_CALL_STATE_HELD:
default:
break;
}
}
static void
handle_hangup_all_auth_ready (MMBaseModem *self,
GAsyncResult *res,
HandleHangupAllContext *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_hangup_all_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 hangup all: device not yet enabled");
handle_hangup_all_context_free (ctx);
return;
}
if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all ||
!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all_finish) {
g_dbus_method_invocation_return_error (ctx->invocation,
MM_CORE_ERROR,
MM_CORE_ERROR_UNSUPPORTED,
"Cannot hangup all: unsupported");
handle_hangup_all_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 hangup all: missing call list");
handle_hangup_all_context_free (ctx);
return;
}
mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hangup_all_foreach, ctx);
g_object_unref (list);
MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hangup_all (MM_IFACE_MODEM_VOICE (self),
(GAsyncReadyCallback)hangup_all_ready,
ctx);
}
static gboolean
handle_hangup_all (MmGdbusModemVoice *skeleton,
GDBusMethodInvocation *invocation,
MMIfaceModemVoice *self)
{
HandleHangupAllContext *ctx;
ctx = g_slice_new0 (HandleHangupAllContext);
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_hangup_all_auth_ready,
ctx);
return TRUE;
}
/*****************************************************************************/
/* Call list polling logic
*
@@ -1516,6 +1671,10 @@ interface_initialization_step (GTask *task)
"handle-hold-and-accept",
G_CALLBACK (handle_hold_and_accept),
self);
g_signal_connect (ctx->skeleton,
"handle-hangup-all",
G_CALLBACK (handle_hangup_all),
self);
/* Finally, export the new interface */
mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self),

View File

@@ -107,6 +107,14 @@ struct _MMIfaceModemVoice {
gboolean (* hangup_and_accept_finish) (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
/* Hangup all */
void (* hangup_all) (MMIfaceModemVoice *self,
GAsyncReadyCallback callback,
gpointer user_data);
gboolean (* hangup_all_finish) (MMIfaceModemVoice *self,
GAsyncResult *res,
GError **error);
};
GType mm_iface_modem_voice_get_type (void);