aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/mmcli-modem-voice.c49
-rw-r--r--docs/reference/libmm-glib/libmm-glib-sections.txt7
-rw-r--r--introspection/org.freedesktop.ModemManager1.Modem.Voice.xml17
-rw-r--r--libmm-glib/mm-modem-voice.c79
-rw-r--r--libmm-glib/mm-modem-voice.h11
-rw-r--r--src/mm-iface-modem-voice.c146
-rw-r--r--src/mm-iface-modem-voice.h8
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,