aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--cli/mmcli-call.c65
-rw-r--r--docs/reference/libmm-glib/libmm-glib-sections.txt7
-rw-r--r--include/ModemManager-enums.h2
-rw-r--r--introspection/org.freedesktop.ModemManager1.Call.xml15
-rw-r--r--libmm-glib/mm-call.c85
-rw-r--r--libmm-glib/mm-call.h15
-rw-r--r--src/mm-base-call.c111
-rw-r--r--src/mm-base-call.h9
8 files changed, 304 insertions, 5 deletions
diff --git a/cli/mmcli-call.c b/cli/mmcli-call.c
index a871dbc0..83a0c017 100644
--- a/cli/mmcli-call.c
+++ b/cli/mmcli-call.c
@@ -46,11 +46,12 @@ typedef struct {
static Context *ctx;
/* Options */
-static gboolean info_flag; /* set when no action found */
-static gboolean start_flag;
-static gboolean accept_flag;
-static gboolean hangup_flag;
-static gchar *dtmf_request;
+static gboolean info_flag; /* set when no action found */
+static gboolean start_flag;
+static gboolean accept_flag;
+static gchar *deflect_str;
+static gboolean hangup_flag;
+static gchar *dtmf_request;
static GOptionEntry entries[] = {
{ "start", 0, 0, G_OPTION_ARG_NONE, &start_flag,
@@ -61,6 +62,10 @@ static GOptionEntry entries[] = {
"Accept the incoming call",
NULL,
},
+ { "deflect", 0, 0, G_OPTION_ARG_STRING, &deflect_str,
+ "Deflect the incoming call",
+ "[NUMBER]",
+ },
{ "hangup", 0, 0, G_OPTION_ARG_NONE, &hangup_flag,
"Hang up the call",
NULL,
@@ -99,6 +104,7 @@ mmcli_call_options_enabled (void)
n_actions = (start_flag +
accept_flag +
+ !!deflect_str +
hangup_flag +
!!dtmf_request);
@@ -228,6 +234,33 @@ accept_ready (MMCall *call,
}
static void
+deflect_process_reply (gboolean result,
+ const GError *error)
+{
+ if (!result) {
+ g_printerr ("error: couldn't deflect the call: '%s'\n",
+ error ? error->message : "unknown error");
+ exit (EXIT_FAILURE);
+ }
+
+ g_print ("successfully deflected the call\n");
+}
+
+static void
+deflect_ready (MMCall *call,
+ GAsyncResult *result,
+ gpointer nothing)
+{
+ gboolean operation_result;
+ GError *error = NULL;
+
+ operation_result = mm_call_deflect_finish (call, result, &error);
+ deflect_process_reply (operation_result, error);
+
+ mmcli_async_operation_done ();
+}
+
+static void
hangup_process_reply (gboolean result,
const GError *error)
{
@@ -313,6 +346,16 @@ get_call_ready (GObject *source,
return;
}
+ /* Requesting to deflect the call? */
+ if (deflect_str) {
+ mm_call_deflect (ctx->call,
+ deflect_str,
+ ctx->cancellable,
+ (GAsyncReadyCallback)deflect_ready,
+ NULL);
+ return;
+ }
+
/* Requesting to hangup the call? */
if (hangup_flag) {
mm_call_hangup (ctx->call,
@@ -398,6 +441,18 @@ mmcli_call_run_synchronous (GDBusConnection *connection)
return;
}
+ /* Requesting to deflect the call? */
+ if (deflect_str) {
+ gboolean operation_result;
+
+ operation_result = mm_call_deflect_sync (ctx->call,
+ deflect_str,
+ NULL,
+ &error);
+ deflect_process_reply (operation_result, error);
+ return;
+ }
+
/* Requesting to hangup the call? */
if (hangup_flag) {
gboolean operation_result;
diff --git a/docs/reference/libmm-glib/libmm-glib-sections.txt b/docs/reference/libmm-glib/libmm-glib-sections.txt
index b1727913..5daaff5c 100644
--- a/docs/reference/libmm-glib/libmm-glib-sections.txt
+++ b/docs/reference/libmm-glib/libmm-glib-sections.txt
@@ -1345,6 +1345,9 @@ mm_call_hangup_sync
mm_call_send_dtmf
mm_call_send_dtmf_finish
mm_call_send_dtmf_sync
+mm_call_deflect
+mm_call_deflect_finish
+mm_call_deflect_sync
<SUBSECTION Standard>
MMCallClass
MMCallPrivate
@@ -3256,6 +3259,9 @@ mm_gdbus_call_call_hangup_sync
mm_gdbus_call_call_send_dtmf
mm_gdbus_call_call_send_dtmf_finish
mm_gdbus_call_call_send_dtmf_sync
+mm_gdbus_call_call_deflect
+mm_gdbus_call_call_deflect_finish
+mm_gdbus_call_call_deflect_sync
<SUBSECTION Private>
mm_gdbus_call_set_direction
mm_gdbus_call_set_number
@@ -3267,6 +3273,7 @@ mm_gdbus_call_complete_accept
mm_gdbus_call_complete_hangup
mm_gdbus_call_complete_send_dtmf
mm_gdbus_call_complete_start
+mm_gdbus_call_complete_deflect
mm_gdbus_call_interface_info
mm_gdbus_call_override_properties
mm_gdbus_call_emit_dtmf_received
diff --git a/include/ModemManager-enums.h b/include/ModemManager-enums.h
index 15d701e2..f84eafc2 100644
--- a/include/ModemManager-enums.h
+++ b/include/ModemManager-enums.h
@@ -1387,6 +1387,7 @@ typedef enum { /*< underscore_name=mm_call_state >*/
* @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.
+ * @MM_CALL_STATE_REASON_DEFLECTED: Call has been deflected to a new number. Since 1.12.
*
* Reason for the state change in the call.
*/
@@ -1400,6 +1401,7 @@ typedef enum { /*< underscore_name=mm_call_state_reason >*/
MM_CALL_STATE_REASON_ERROR = 6,
MM_CALL_STATE_REASON_AUDIO_SETUP_FAILED = 7,
MM_CALL_STATE_REASON_TRANSFERRED = 8,
+ MM_CALL_STATE_REASON_DEFLECTED = 9,
} MMCallStateReason;
/**
diff --git a/introspection/org.freedesktop.ModemManager1.Call.xml b/introspection/org.freedesktop.ModemManager1.Call.xml
index f53f5b7b..0f704f2f 100644
--- a/introspection/org.freedesktop.ModemManager1.Call.xml
+++ b/introspection/org.freedesktop.ModemManager1.Call.xml
@@ -38,6 +38,21 @@
<method name="Accept" />
<!--
+ Deflect:
+ @number: new number where the call will be deflected.
+
+ Deflect an incoming or waiting call to a new number. This call will be
+ considered terminated once the deflection is performed.
+
+ Applicable only if state is <link linkend="MM-CALL-STATE-RINGING-IN:CAPS"><constant>MM_CALL_STATE_RINGING_IN</constant></link> or
+ <link linkend="MM-CALL-STATE-WAITING:CAPS"><constant>MM_CALL_STATE_WAITING</constant></link> and direction is
+ <link linkend="MM-CALL-DIRECTION-INCOMING:CAPS"><constant>MM_CALL_DIRECTION_INCOMING</constant></link>.
+ -->
+ <method name="Deflect">
+ <arg name="number" type="s" />
+ </method>
+
+ <!--
Hangup:
Hangup the active call.
diff --git a/libmm-glib/mm-call.c b/libmm-glib/mm-call.c
index af1d8f12..7800249d 100644
--- a/libmm-glib/mm-call.c
+++ b/libmm-glib/mm-call.c
@@ -492,6 +492,91 @@ mm_call_accept_sync (MMCall *self,
/*****************************************************************************/
/**
+ * mm_call_deflect_finish:
+ * @self: A #MMCall.
+ * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_call_deflect().
+ * @error: Return location for error or %NULL.
+ *
+ * Finishes an operation started with mm_call_deflect().
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ */
+gboolean
+mm_call_deflect_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_deflect_finish (MM_GDBUS_CALL (self), res, error);
+}
+
+/**
+ * mm_call_deflect:
+ * @self: A #MMCall.
+ * @number: new number where the call will be deflected.
+ * @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 requests to deflect the incoming call.
+ *
+ * This call will be considered terminated once the deflection is performed.
+ *
+ * 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_deflect_finish() to get the result of the operation.
+ *
+ * See mm_call_deflect_sync() for the synchronous, blocking version of this method.
+ */
+void
+mm_call_deflect (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ g_return_if_fail (MM_IS_CALL (self));
+
+ mm_gdbus_call_call_deflect (MM_GDBUS_CALL (self),
+ number,
+ cancellable,
+ callback,
+ user_data);
+}
+
+/**
+ * mm_call_deflect_sync:
+ * @self: A #MMCall.
+ * @number: new number where the call will be deflected.
+ * @cancellable: (allow-none): A #GCancellable or %NULL.
+ * @error: Return location for error or %NULL.
+ *
+ * Synchronously requests to deflect the incoming call.
+ *
+ * This call will be considered terminated once the deflection is performed.
+ *
+ * The calling thread is blocked until an incoming call is ready.
+ * See mm_call_deflect() for the asynchronous version of this method.
+ *
+ * Returns: %TRUE if the operation succeeded, %FALSE if @error is set.
+ */
+gboolean
+mm_call_deflect_sync (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GError **error)
+{
+ g_return_val_if_fail (MM_IS_CALL (self), FALSE);
+
+ return mm_gdbus_call_call_deflect_sync (MM_GDBUS_CALL (self),
+ number,
+ cancellable,
+ error);
+}
+
+ /*****************************************************************************/
+
+/**
* mm_call_hangup_finish:
* @self: A #MMCall.
* @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_call_hangup().
diff --git a/libmm-glib/mm-call.h b/libmm-glib/mm-call.h
index e6d89574..9344367b 100644
--- a/libmm-glib/mm-call.h
+++ b/libmm-glib/mm-call.h
@@ -110,6 +110,21 @@ gboolean mm_call_accept_sync (MMCall *self,
GCancellable *cancellable,
GError **error);
+void mm_call_deflect (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+
+gboolean mm_call_deflect_finish (MMCall *self,
+ GAsyncResult *res,
+ GError **error);
+
+gboolean mm_call_deflect_sync (MMCall *self,
+ const gchar *number,
+ GCancellable *cancellable,
+ GError **error);
+
void mm_call_hangup (MMCall *self,
GCancellable *cancellable,
diff --git a/src/mm-base-call.c b/src/mm-base-call.c
index e208b007..5b83b4e5 100644
--- a/src/mm-base-call.c
+++ b/src/mm-base-call.c
@@ -511,7 +511,114 @@ handle_accept (MMBaseCall *self,
}
/*****************************************************************************/
+/* Deflect call (DBus call handling) */
+typedef struct {
+ MMBaseCall *self;
+ MMBaseModem *modem;
+ GDBusMethodInvocation *invocation;
+ gchar *number;
+} HandleDeflectContext;
+
+static void
+handle_deflect_context_free (HandleDeflectContext *ctx)
+{
+ g_free (ctx->number);
+ g_object_unref (ctx->invocation);
+ g_object_unref (ctx->modem);
+ g_object_unref (ctx->self);
+ g_slice_free (HandleDeflectContext, ctx);
+}
+
+static void
+handle_deflect_ready (MMBaseCall *self,
+ GAsyncResult *res,
+ HandleDeflectContext *ctx)
+{
+ GError *error = NULL;
+
+ if (!MM_BASE_CALL_GET_CLASS (self)->deflect_finish (self, res, &error)) {
+ mm_base_call_change_state (self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_ERROR);
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ mm_info ("call is deflected to '%s'", ctx->number);
+ mm_base_call_change_state (ctx->self, MM_CALL_STATE_TERMINATED, MM_CALL_STATE_REASON_DEFLECTED);
+ mm_gdbus_call_complete_deflect (MM_GDBUS_CALL (ctx->self), ctx->invocation);
+ handle_deflect_context_free (ctx);
+}
+
+static void
+handle_deflect_auth_ready (MMBaseModem *modem,
+ GAsyncResult *res,
+ HandleDeflectContext *ctx)
+{
+ MMCallState state;
+ GError *error = NULL;
+
+ if (!mm_base_modem_authorize_finish (modem, res, &error)) {
+ g_dbus_method_invocation_take_error (ctx->invocation, error);
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ state = mm_gdbus_call_get_state (MM_GDBUS_CALL (ctx->self));
+
+ /* We can only deflect incoming call in ringing or waiting state */
+ if (state != MM_CALL_STATE_RINGING_IN && state != MM_CALL_STATE_WAITING) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_FAILED,
+ "This call was not ringing/waiting, cannot deflect");
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ mm_info ("user request to deflect call");
+
+ /* Check if we do support doing it */
+ if (!MM_BASE_CALL_GET_CLASS (ctx->self)->deflect ||
+ !MM_BASE_CALL_GET_CLASS (ctx->self)->deflect_finish) {
+ g_dbus_method_invocation_return_error (ctx->invocation,
+ MM_CORE_ERROR,
+ MM_CORE_ERROR_UNSUPPORTED,
+ "Deflecting call is not supported by this modem");
+ handle_deflect_context_free (ctx);
+ return;
+ }
+
+ MM_BASE_CALL_GET_CLASS (ctx->self)->deflect (ctx->self,
+ ctx->number,
+ (GAsyncReadyCallback)handle_deflect_ready,
+ ctx);
+}
+
+static gboolean
+handle_deflect (MMBaseCall *self,
+ GDBusMethodInvocation *invocation,
+ const gchar *number)
+{
+ HandleDeflectContext *ctx;
+
+ ctx = g_slice_new0 (HandleDeflectContext);
+ ctx->self = g_object_ref (self);
+ ctx->invocation = g_object_ref (invocation);
+ ctx->number = g_strdup (number);
+ g_object_get (self,
+ MM_BASE_CALL_MODEM, &ctx->modem,
+ NULL);
+
+ mm_base_modem_authorize (ctx->modem,
+ invocation,
+ MM_AUTHORIZATION_VOICE,
+ (GAsyncReadyCallback)handle_deflect_auth_ready,
+ ctx);
+ return TRUE;
+}
+
+/*****************************************************************************/
/* Hangup call (DBus call handling) */
typedef struct {
@@ -756,6 +863,10 @@ call_dbus_export (MMBaseCall *self)
G_CALLBACK (handle_accept),
NULL);
g_signal_connect (self,
+ "handle-deflect",
+ G_CALLBACK (handle_deflect),
+ NULL);
+ g_signal_connect (self,
"handle-hangup",
G_CALLBACK (handle_hangup),
NULL);
diff --git a/src/mm-base-call.h b/src/mm-base-call.h
index 5dddb6bc..e1a64e1e 100644
--- a/src/mm-base-call.h
+++ b/src/mm-base-call.h
@@ -67,6 +67,15 @@ struct _MMBaseCallClass {
GAsyncResult *res,
GError **error);
+ /* Deflect the call */
+ void (* deflect) (MMBaseCall *self,
+ const gchar *number,
+ GAsyncReadyCallback callback,
+ gpointer user_data);
+ gboolean (* deflect_finish) (MMBaseCall *self,
+ GAsyncResult *res,
+ GError **error);
+
/* Hangup the call */
void (* hangup) (MMBaseCall *self,
GAsyncReadyCallback callback,