diff options
author | Aleksander Morgado <aleksander@aleksander.es> | 2019-07-01 15:21:18 +0200 |
---|---|---|
committer | Aleksander Morgado <aleksander@aleksander.es> | 2019-07-11 23:20:59 +0200 |
commit | 222874299eb16cba8e71ff2b4152d364615f2235 (patch) | |
tree | 123c38497e8da4187d1b90a2a0afd67bbb930967 | |
parent | 81f1483eecb26ff16576e28a7058b37f095584c1 (diff) |
api,call: new Deflect() method
This method allows deflecting an incoming or waiting call to a
different number.
-rw-r--r-- | cli/mmcli-call.c | 65 | ||||
-rw-r--r-- | docs/reference/libmm-glib/libmm-glib-sections.txt | 7 | ||||
-rw-r--r-- | include/ModemManager-enums.h | 2 | ||||
-rw-r--r-- | introspection/org.freedesktop.ModemManager1.Call.xml | 15 | ||||
-rw-r--r-- | libmm-glib/mm-call.c | 85 | ||||
-rw-r--r-- | libmm-glib/mm-call.h | 15 | ||||
-rw-r--r-- | src/mm-base-call.c | 111 | ||||
-rw-r--r-- | src/mm-base-call.h | 9 |
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, |