diff options
-rw-r--r-- | cli/mmcli-modem-voice.c | 49 | ||||
-rw-r--r-- | docs/reference/libmm-glib/libmm-glib-sections.txt | 7 | ||||
-rw-r--r-- | introspection/org.freedesktop.ModemManager1.Modem.Voice.xml | 17 | ||||
-rw-r--r-- | libmm-glib/mm-modem-voice.c | 79 | ||||
-rw-r--r-- | libmm-glib/mm-modem-voice.h | 11 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.c | 146 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.h | 8 |
7 files changed, 317 insertions, 0 deletions
diff --git a/cli/mmcli-modem-voice.c b/cli/mmcli-modem-voice.c index f1d24e16..e1f1426a 100644 --- a/cli/mmcli-modem-voice.c +++ b/cli/mmcli-modem-voice.c @@ -50,6 +50,7 @@ static Context *ctx; static gboolean list_flag; static gchar *create_str; static gchar *delete_str; +static gboolean hold_and_accept_flag; static gboolean hangup_and_accept_flag; static GOptionEntry entries[] = { @@ -65,6 +66,10 @@ static GOptionEntry entries[] = { "Delete a call from a given modem", "[PATH|INDEX]" }, + { "voice-hold-and-accept", 0, 0, G_OPTION_ARG_NONE, &hold_and_accept_flag, + "Places all active calls in hold and accepts the next waiting or held call", + NULL + }, { "voice-hangup-and-accept", 0, 0, G_OPTION_ARG_NONE, &hangup_and_accept_flag, "Hangs up all active calls and accepts the next waiting or held call", NULL @@ -99,6 +104,7 @@ mmcli_modem_voice_options_enabled (void) n_actions = (list_flag + !!create_str + !!delete_str + + hold_and_accept_flag + hangup_and_accept_flag); if (n_actions > 1) { @@ -207,6 +213,31 @@ hangup_and_accept_ready (MMModemVoice *modem, } static void +hold_and_accept_process_reply (const GError *error) +{ + if (error) { + g_printerr ("error: couldn't hold and accept: '%s'\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("operation successful\n"); +} + +static void +hold_and_accept_ready (MMModemVoice *modem, + GAsyncResult *result, + gpointer nothing) +{ + GError *error = NULL; + + mm_modem_voice_hold_and_accept_finish (modem, result, &error); + hold_and_accept_process_reply (error); + + mmcli_async_operation_done (); +} + +static void list_process_reply (GList *result, const GError *error) { @@ -365,6 +396,16 @@ get_modem_ready (GObject *source, return; } + /* Request to hold and accept? */ + if (hold_and_accept_flag) { + g_debug ("Asynchronously holding and accepting next call..."); + mm_modem_voice_hold_and_accept (ctx->modem_voice, + ctx->cancellable, + (GAsyncReadyCallback)hold_and_accept_ready, + NULL); + return; + } + /* Request to hangup and accept? */ if (hangup_and_accept_flag) { g_debug ("Asynchronously hanging up and accepting next call..."); @@ -471,6 +512,14 @@ mmcli_modem_voice_run_synchronous (GDBusConnection *connection) return; } + /* Request to hold and accept? */ + if (hold_and_accept_flag) { + g_debug ("Synchronously holding and accepting call..."); + mm_modem_voice_hold_and_accept_sync (ctx->modem_voice, NULL, &error); + hold_and_accept_process_reply (error); + return; + } + /* Request to hangup and accept? */ if (hangup_and_accept_flag) { g_debug ("Synchronously hanging up and accepting call..."); diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt index 65ba689d..a2087f38 100644 --- a/docs/reference/libmm-glib/libmm-glib-sections.txt +++ b/docs/reference/libmm-glib/libmm-glib-sections.txt @@ -1015,6 +1015,9 @@ mm_modem_voice_list_calls_sync mm_modem_voice_hangup_and_accept mm_modem_voice_hangup_and_accept_finish 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 <SUBSECTION Standard> MMModemVoiceClass MMModemVoicePrivate @@ -2841,6 +2844,9 @@ mm_gdbus_modem_voice_call_list_calls_sync mm_gdbus_modem_voice_call_hangup_and_accept mm_gdbus_modem_voice_call_hangup_and_accept_finish 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 <SUBSECTION Private> mm_gdbus_modem_voice_set_calls mm_gdbus_modem_voice_emit_call_added @@ -2849,6 +2855,7 @@ mm_gdbus_modem_voice_complete_create_call 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_interface_info mm_gdbus_modem_voice_override_properties <SUBSECTION Standard> diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml b/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml index 37b70260..b6c16f13 100644 --- a/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml +++ b/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml @@ -64,6 +64,23 @@ </method> <!-- + HoldAndAccept: + + Place all active calls on hold, if any, and accept the next + call. + + Waiting calls have preference over held calls, so the next + call being active will be any waiting call, or otherwise, + any held call. + + The user should monitor the state of all available ongoing + calls to be reported of which one becomes active. + + No error is returned if there are no waiting or held calls. + --> + <method name="HoldAndAccept" /> + + <!-- HangupAndAccept: Hangup all active calls, if any, and accept the next call. diff --git a/libmm-glib/mm-modem-voice.c b/libmm-glib/mm-modem-voice.c index 19846602..8d86f718 100644 --- a/libmm-glib/mm-modem-voice.c +++ b/libmm-glib/mm-modem-voice.c @@ -533,6 +533,85 @@ mm_modem_voice_delete_call_sync (MMModemVoice *self, /*****************************************************************************/ /** + * mm_modem_voice_hold_and_accept_finish: + * @self: A #MMModemVoice. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_hold_and_accept(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with mm_modem_voice_hold_and_accept(). + * + * Returns: %TRUE if the operation was successful, %FALSE if @error is set. + * Since: 1.12 + */ +gboolean +mm_modem_voice_hold_and_accept_finish (MMModemVoice *self, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); + + return mm_gdbus_modem_voice_call_hold_and_accept_finish (MM_GDBUS_MODEM_VOICE (self), res, error); +} + +/** + * mm_modem_voice_hold_and_accept: + * @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 puts all active calls on hold and accepts the next waiting or held 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_modem_voice_hold_and_accept_finish() to get the result of the operation. + * + * See mm_modem_voice_hold_and_accept_sync() for the synchronous, blocking version of this method. + * + * Since: 1.12 + */ +void +mm_modem_voice_hold_and_accept (MMModemVoice *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_VOICE (self)); + + mm_gdbus_modem_voice_call_hold_and_accept (MM_GDBUS_MODEM_VOICE (self), + cancellable, + callback, + user_data); +} + +/** + * mm_modem_voice_hold_and_accept_sync: + * @self: A #MMModemVoice. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously puts all active calls on hold and accepts the next waiting or held call. + * + * The calling thread is blocked until a reply is received. See mm_modem_voice_hold_and_accept() + * 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_hold_and_accept_sync (MMModemVoice *self, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); + + return mm_gdbus_modem_voice_call_hold_and_accept_sync (MM_GDBUS_MODEM_VOICE (self), + cancellable, + error); +} + +/*****************************************************************************/ + +/** * mm_modem_voice_hangup_and_accept_finish: * @self: A #MMModemVoice. * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_hangup_and_accept(). diff --git a/libmm-glib/mm-modem-voice.h b/libmm-glib/mm-modem-voice.h index 18e13e04..ebc2493f 100644 --- a/libmm-glib/mm-modem-voice.h +++ b/libmm-glib/mm-modem-voice.h @@ -110,6 +110,17 @@ gboolean mm_modem_voice_delete_call_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); +void mm_modem_voice_hold_and_accept (MMModemVoice *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_modem_voice_hold_and_accept_finish (MMModemVoice *self, + GAsyncResult *res, + GError **error); +gboolean mm_modem_voice_hold_and_accept_sync (MMModemVoice *self, + GCancellable *cancellable, + GError **error); + void mm_modem_voice_hangup_and_accept (MMModemVoice *self, GCancellable *cancellable, GAsyncReadyCallback callback, diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c index 7d5afa4b..c3e0f677 100644 --- a/src/mm-iface-modem-voice.c +++ b/src/mm-iface-modem-voice.c @@ -550,6 +550,148 @@ typedef struct { MMIfaceModemVoice *self; GList *active_calls; MMBaseCall *next_call; +} HandleHoldAndAcceptContext; + +static void +handle_hold_and_accept_context_free (HandleHoldAndAcceptContext *ctx) +{ + g_list_free_full (ctx->active_calls, g_object_unref); + g_clear_object (&ctx->next_call); + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleHoldAndAcceptContext, ctx); +} + +static void +hold_and_accept_ready (MMIfaceModemVoice *self, + GAsyncResult *res, + HandleHoldAndAcceptContext *ctx) +{ + GError *error = NULL; + GList *l; + + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_hold_and_accept_context_free (ctx); + return; + } + + for (l = ctx->active_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); + if (ctx->next_call) + mm_base_call_change_state (ctx->next_call, MM_CALL_STATE_ACTIVE, MM_CALL_STATE_REASON_ACCEPTED); + + mm_gdbus_modem_voice_complete_hold_and_accept (ctx->skeleton, ctx->invocation); + handle_hold_and_accept_context_free (ctx); +} + +static void +prepare_hold_and_accept_foreach (MMBaseCall *call, + HandleHoldAndAcceptContext *ctx) +{ + switch (mm_base_call_get_state (call)) { + case MM_CALL_STATE_ACTIVE: + ctx->active_calls = g_list_append (ctx->active_calls, g_object_ref (call)); + break; + case MM_CALL_STATE_WAITING: + g_clear_object (&ctx->next_call); + ctx->next_call = g_object_ref (call); + break; + case MM_CALL_STATE_HELD: + if (!ctx->next_call) + ctx->next_call = g_object_ref (call); + break; + default: + break; + } +} + +static void +handle_hold_and_accept_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleHoldAndAcceptContext *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_hold_and_accept_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 hold and accept: device not yet enabled"); + handle_hold_and_accept_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept || + !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot hold and accept: unsupported"); + handle_hold_and_accept_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 hold and accept: missing call list"); + handle_hold_and_accept_context_free (ctx); + return; + } + mm_call_list_foreach (list, (MMCallListForeachFunc)prepare_hold_and_accept_foreach, ctx); + g_object_unref (list); + + MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->hold_and_accept (MM_IFACE_MODEM_VOICE (self), + (GAsyncReadyCallback)hold_and_accept_ready, + ctx); +} + +static gboolean +handle_hold_and_accept (MmGdbusModemVoice *skeleton, + GDBusMethodInvocation *invocation, + MMIfaceModemVoice *self) +{ + HandleHoldAndAcceptContext *ctx; + + ctx = g_slice_new0 (HandleHoldAndAcceptContext); + 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_hold_and_accept_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ + +typedef struct { + MmGdbusModemVoice *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemVoice *self; + GList *active_calls; + MMBaseCall *next_call; } HandleHangupAndAcceptContext; static void @@ -1370,6 +1512,10 @@ interface_initialization_step (GTask *task) "handle-hangup-and-accept", G_CALLBACK (handle_hangup_and_accept), self); + g_signal_connect (ctx->skeleton, + "handle-hold-and-accept", + G_CALLBACK (handle_hold_and_accept), + self); /* Finally, export the new interface */ mm_gdbus_object_skeleton_set_modem_voice (MM_GDBUS_OBJECT_SKELETON (self), diff --git a/src/mm-iface-modem-voice.h b/src/mm-iface-modem-voice.h index f65b63e0..d5395741 100644 --- a/src/mm-iface-modem-voice.h +++ b/src/mm-iface-modem-voice.h @@ -92,6 +92,14 @@ struct _MMIfaceModemVoice { MMCallDirection direction, const gchar *number); + /* Hold and accept */ + void (* hold_and_accept) (MMIfaceModemVoice *self, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* hold_and_accept_finish) (MMIfaceModemVoice *self, + GAsyncResult *res, + GError **error); + /* Hangup and accept */ void (* hangup_and_accept) (MMIfaceModemVoice *self, GAsyncReadyCallback callback, |