diff options
-rw-r--r-- | cli/mmcli-modem-voice.c | 130 | ||||
-rw-r--r-- | introspection/org.freedesktop.ModemManager1.Modem.Voice.xml | 26 | ||||
-rw-r--r-- | libmm-glib/mm-modem-voice.c | 169 | ||||
-rw-r--r-- | libmm-glib/mm-modem-voice.h | 26 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.c | 214 | ||||
-rw-r--r-- | src/mm-iface-modem-voice.h | 18 |
6 files changed, 575 insertions, 8 deletions
diff --git a/cli/mmcli-modem-voice.c b/cli/mmcli-modem-voice.c index 0eff1f00..87c27bef 100644 --- a/cli/mmcli-modem-voice.c +++ b/cli/mmcli-modem-voice.c @@ -54,6 +54,9 @@ static gboolean hold_and_accept_flag; static gboolean hangup_and_accept_flag; static gboolean hangup_all_flag; static gboolean transfer_flag; +static gboolean call_waiting_enable_flag; +static gboolean call_waiting_disable_flag; +static gboolean call_waiting_query_flag; static GOptionEntry entries[] = { { "voice-list-calls", 0, 0, G_OPTION_ARG_NONE, &list_flag, @@ -84,6 +87,18 @@ static GOptionEntry entries[] = { "Joins active and held calls and disconnects from them", NULL }, + { "voice-enable-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_enable_flag, + "Enables the call waiting network service", + NULL + }, + { "voice-disable-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_disable_flag, + "Disables the call waiting network service", + NULL + }, + { "voice-query-call-waiting", 0, 0, G_OPTION_ARG_NONE, &call_waiting_query_flag, + "Queries the status of the call waiting network service", + NULL + }, { NULL } }; @@ -117,7 +132,10 @@ mmcli_modem_voice_options_enabled (void) hold_and_accept_flag + hangup_and_accept_flag + hangup_all_flag + - transfer_flag); + transfer_flag + + call_waiting_enable_flag + + call_waiting_disable_flag + + call_waiting_query_flag); if (n_actions > 1) { g_printerr ("error: too many Voice actions requested\n"); @@ -200,6 +218,58 @@ output_call_info (MMCall *call) } static void +call_waiting_query_process_reply (const GError *error, + gboolean status) +{ + if (error) { + g_printerr ("error: couldn't query call waiting network service status: '%s'\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("call waiting service is %s\n", status ? "enabled" : "disabled"); +} + +static void +call_waiting_query_ready (MMModemVoice *modem, + GAsyncResult *result, + gpointer nothing) +{ + GError *error = NULL; + gboolean status = FALSE; + + mm_modem_voice_call_waiting_query_finish (modem, result, &status, &error); + call_waiting_query_process_reply (error, status); + + mmcli_async_operation_done (); +} + +static void +call_waiting_setup_process_reply (const GError *error) +{ + if (error) { + g_printerr ("error: couldn't setup call waiting network service: '%s'\n", + error->message); + exit (EXIT_FAILURE); + } + + g_print ("operation successful\n"); +} + +static void +call_waiting_setup_ready (MMModemVoice *modem, + GAsyncResult *result, + gpointer nothing) +{ + GError *error = NULL; + + mm_modem_voice_call_waiting_setup_finish (modem, result, &error); + call_waiting_setup_process_reply (error); + + mmcli_async_operation_done (); +} + +static void transfer_process_reply (const GError *error) { if (error) { @@ -498,6 +568,38 @@ get_modem_ready (GObject *source, return; } + /* Request to enable call waiting? */ + if (call_waiting_enable_flag) { + g_debug ("Asynchronously enabling call waiting..."); + mm_modem_voice_call_waiting_setup (ctx->modem_voice, + TRUE, + ctx->cancellable, + (GAsyncReadyCallback)call_waiting_setup_ready, + NULL); + return; + } + + /* Request to disable call waiting? */ + if (call_waiting_disable_flag) { + g_debug ("Asynchronously enabling call waiting..."); + mm_modem_voice_call_waiting_setup (ctx->modem_voice, + FALSE, + ctx->cancellable, + (GAsyncReadyCallback)call_waiting_setup_ready, + NULL); + return; + } + + /* Request to query call waiting? */ + if (call_waiting_query_flag) { + g_debug ("Asynchronously querying call waiting status..."); + mm_modem_voice_call_waiting_query (ctx->modem_voice, + ctx->cancellable, + (GAsyncReadyCallback)call_waiting_query_ready, + NULL); + return; + } + g_warn_if_reached (); } @@ -626,5 +728,31 @@ mmcli_modem_voice_run_synchronous (GDBusConnection *connection) return; } + /* Request to enable call waiting? */ + if (call_waiting_enable_flag) { + g_debug ("Synchronously enabling call waiting..."); + mm_modem_voice_call_waiting_setup_sync (ctx->modem_voice, TRUE, NULL, &error); + call_waiting_setup_process_reply (error); + return; + } + + /* Request to disable call waiting? */ + if (call_waiting_disable_flag) { + g_debug ("Synchronously disabling call waiting..."); + mm_modem_voice_call_waiting_setup_sync (ctx->modem_voice, FALSE, NULL, &error); + call_waiting_setup_process_reply (error); + return; + } + + /* Request to query call waiting? */ + if (call_waiting_query_flag) { + gboolean status = FALSE; + + g_debug ("Synchronously querying call waiting status..."); + mm_modem_voice_call_waiting_query_sync (ctx->modem_voice, NULL, &status, &error); + call_waiting_query_process_reply (error, status); + return; + } + g_warn_if_reached (); } diff --git a/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml b/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml index 4650b22e..d39081c0 100644 --- a/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml +++ b/introspection/org.freedesktop.ModemManager1.Modem.Voice.xml @@ -124,6 +124,32 @@ <method name="Transfer" /> <!-- + CallWaitingSetup: + + Activates or deactivates the call waiting network service, as per + 3GPP TS 22.083. + + This operation requires communication with the network in order to + complete, so the modem must be successfully registered. + --> + <method name="CallWaitingSetup"> + <arg name="enable" type="b" direction="in" /> + </method> + + <!-- + CallWaitingQuery: + + Queries the status of the call waiting network service, as per + 3GPP TS 22.083. + + This operation requires communication with the network in order to + complete, so the modem must be successfully registered. + --> + <method name="CallWaitingQuery"> + <arg name="status" type="b" direction="out" /> + </method> + + <!-- CallAdded: @path: Object path of the new call. diff --git a/libmm-glib/mm-modem-voice.c b/libmm-glib/mm-modem-voice.c index d01efb14..e23a7306 100644 --- a/libmm-glib/mm-modem-voice.c +++ b/libmm-glib/mm-modem-voice.c @@ -848,6 +848,175 @@ mm_modem_voice_transfer_sync (MMModemVoice *self, /*****************************************************************************/ +/** + * mm_modem_voice_call_waiting_setup_finish: + * @self: A #MMModemVoice. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_call_waiting_setup(). + * @error: Return location for error or %NULL. + * + * Finishes an operation started with mm_modem_voice_call_waiting_setup(). + * + * Returns: %TRUE if @status is set, %FALSE if @error is set. + * Since: 1.12 + */ +gboolean +mm_modem_voice_call_waiting_setup_finish (MMModemVoice *self, + GAsyncResult *res, + GError **error) +{ + g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); + + return mm_gdbus_modem_voice_call_call_waiting_setup_finish (MM_GDBUS_MODEM_VOICE (self), res, error); +} + +/** + * mm_modem_voice_call_waiting_setup: + * @self: A #MMModemVoice. + * @enable: Whether the call waiting service should be enabled. + * @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 enables or disables the call waiting network service. + * + * 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_call_waiting_setup_finish() to get the result of the operation. + * + * See mm_modem_voice_call_waiting_setup_sync() for the synchronous, blocking version of this method. + * + * Since: 1.12 + */ +void +mm_modem_voice_call_waiting_setup (MMModemVoice *self, + gboolean enable, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_VOICE (self)); + + mm_gdbus_modem_voice_call_call_waiting_setup (MM_GDBUS_MODEM_VOICE (self), + enable, + cancellable, + callback, + user_data); +} + +/** + * mm_modem_voice_call_waiting_setup_sync: + * @self: A #MMModemVoice. + * @enable: Whether the call waiting service should be enabled. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @error: Return location for error or %NULL. + * + * Synchronously enables or disables the call waiting network service. + * + * The calling thread is blocked until a reply is received. See mm_modem_voice_call_waiting_setup() + * for the asynchronous version of this method. + * + * Returns: %TRUE if the operation is successful, %FALSE if @error is set. + * Since: 1.12 + */ +gboolean +mm_modem_voice_call_waiting_setup_sync (MMModemVoice *self, + gboolean enable, + GCancellable *cancellable, + GError **error) +{ + g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); + + return mm_gdbus_modem_voice_call_call_waiting_setup_sync (MM_GDBUS_MODEM_VOICE (self), + enable, + cancellable, + error); +} + +/*****************************************************************************/ + +/** + * mm_modem_voice_call_waiting_query_finish: + * @self: A #MMModemVoice. + * @res: The #GAsyncResult obtained from the #GAsyncReadyCallback passed to mm_modem_voice_call_waiting_query(). + * @status: Output location where to store the status. + * @error: Return location for error or %NULL. + * + * Finishes an operation started with mm_modem_voice_call_waiting_query(). + * + * Returns: %TRUE if @status is set, %FALSE if @error is set. + * Since: 1.12 + */ +gboolean +mm_modem_voice_call_waiting_query_finish (MMModemVoice *self, + GAsyncResult *res, + gboolean *status, + GError **error) +{ + g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); + + return mm_gdbus_modem_voice_call_call_waiting_query_finish (MM_GDBUS_MODEM_VOICE (self), status, res, error); +} + +/** + * mm_modem_voice_call_waiting_query: + * @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 queries the status of the call waiting network service. + * + * 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_call_waiting_query_finish() to get the result of the operation. + * + * See mm_modem_voice_call_waiting_query_sync() for the synchronous, blocking version of this method. + * + * Since: 1.12 + */ +void +mm_modem_voice_call_waiting_query (MMModemVoice *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data) +{ + g_return_if_fail (MM_IS_MODEM_VOICE (self)); + + mm_gdbus_modem_voice_call_call_waiting_query (MM_GDBUS_MODEM_VOICE (self), + cancellable, + callback, + user_data); +} + +/** + * mm_modem_voice_call_waiting_query_sync: + * @self: A #MMModemVoice. + * @cancellable: (allow-none): A #GCancellable or %NULL. + * @status: Output location where to store the status. + * @error: Return location for error or %NULL. + * + * Synchronously queries the status of the call waiting network service. + * + * The calling thread is blocked until a reply is received. See mm_modem_voice_call_waiting_query() + * for the asynchronous version of this method. + * + * Returns: %TRUE if @status is set, %FALSE if @error is set. + * Since: 1.12 + */ +gboolean +mm_modem_voice_call_waiting_query_sync (MMModemVoice *self, + GCancellable *cancellable, + gboolean *status, + GError **error) +{ + g_return_val_if_fail (MM_IS_MODEM_VOICE (self), FALSE); + + return mm_gdbus_modem_voice_call_call_waiting_query_sync (MM_GDBUS_MODEM_VOICE (self), + status, + cancellable, + error); +} + +/*****************************************************************************/ + static void mm_modem_voice_init (MMModemVoice *self) { diff --git a/libmm-glib/mm-modem-voice.h b/libmm-glib/mm-modem-voice.h index 45f97353..f43a60a1 100644 --- a/libmm-glib/mm-modem-voice.h +++ b/libmm-glib/mm-modem-voice.h @@ -154,6 +154,32 @@ gboolean mm_modem_voice_transfer_sync (MMModemVoice *self, GCancellable *cancellable, GError **error); +void mm_modem_voice_call_waiting_setup (MMModemVoice *self, + gboolean enable, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_modem_voice_call_waiting_setup_finish (MMModemVoice *self, + GAsyncResult *res, + GError **error); +gboolean mm_modem_voice_call_waiting_setup_sync (MMModemVoice *self, + gboolean enable, + GCancellable *cancellable, + GError **error); + +void mm_modem_voice_call_waiting_query (MMModemVoice *self, + GCancellable *cancellable, + GAsyncReadyCallback callback, + gpointer user_data); +gboolean mm_modem_voice_call_waiting_query_finish (MMModemVoice *self, + GAsyncResult *res, + gboolean *status, + GError **error); +gboolean mm_modem_voice_call_waiting_query_sync (MMModemVoice *self, + GCancellable *cancellable, + gboolean *status, + GError **error); + G_END_DECLS #endif /* _MM_MODEM_VOICE_H_ */ diff --git a/src/mm-iface-modem-voice.c b/src/mm-iface-modem-voice.c index 92f8f65a..1c7af9c0 100644 --- a/src/mm-iface-modem-voice.c +++ b/src/mm-iface-modem-voice.c @@ -1263,6 +1263,204 @@ handle_transfer (MmGdbusModemVoice *skeleton, } /*****************************************************************************/ + +typedef struct { + MmGdbusModemVoice *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemVoice *self; + gboolean enable; +} HandleCallWaitingSetupContext; + +static void +handle_call_waiting_setup_context_free (HandleCallWaitingSetupContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleCallWaitingSetupContext, ctx); +} + +static void +call_waiting_setup_ready (MMIfaceModemVoice *self, + GAsyncResult *res, + HandleCallWaitingSetupContext *ctx) +{ + GError *error = NULL; + + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_call_waiting_setup_context_free (ctx); + return; + } + + mm_gdbus_modem_voice_complete_call_waiting_setup (ctx->skeleton, ctx->invocation); + handle_call_waiting_setup_context_free (ctx); +} + +static void +handle_call_waiting_setup_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleCallWaitingSetupContext *ctx) +{ + MMModemState modem_state = MM_MODEM_STATE_UNKNOWN; + GError *error = NULL; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_call_waiting_setup_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 setup call waiting: device not yet enabled"); + handle_call_waiting_setup_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup || + !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot setup call waiting: unsupported"); + handle_call_waiting_setup_context_free (ctx); + return; + } + + MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_setup (MM_IFACE_MODEM_VOICE (self), + ctx->enable, + (GAsyncReadyCallback)call_waiting_setup_ready, + ctx); +} + +static gboolean +handle_call_waiting_setup (MmGdbusModemVoice *skeleton, + GDBusMethodInvocation *invocation, + gboolean enable, + MMIfaceModemVoice *self) +{ + HandleCallWaitingSetupContext *ctx; + + ctx = g_slice_new0 (HandleCallWaitingSetupContext); + ctx->skeleton = g_object_ref (skeleton); + ctx->invocation = g_object_ref (invocation); + ctx->self = g_object_ref (self); + ctx->enable = enable; + + mm_base_modem_authorize (MM_BASE_MODEM (self), + invocation, + MM_AUTHORIZATION_VOICE, + (GAsyncReadyCallback)handle_call_waiting_setup_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ + +typedef struct { + MmGdbusModemVoice *skeleton; + GDBusMethodInvocation *invocation; + MMIfaceModemVoice *self; + gboolean enable; +} HandleCallWaitingQueryContext; + +static void +handle_call_waiting_query_context_free (HandleCallWaitingQueryContext *ctx) +{ + g_object_unref (ctx->skeleton); + g_object_unref (ctx->invocation); + g_object_unref (ctx->self); + g_slice_free (HandleCallWaitingQueryContext, ctx); +} + +static void +call_waiting_query_ready (MMIfaceModemVoice *self, + GAsyncResult *res, + HandleCallWaitingQueryContext *ctx) +{ + GError *error = NULL; + gboolean status = FALSE; + + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query_finish (self, res, &status, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_call_waiting_query_context_free (ctx); + return; + } + + mm_gdbus_modem_voice_complete_call_waiting_query (ctx->skeleton, ctx->invocation, status); + handle_call_waiting_query_context_free (ctx); +} + +static void +handle_call_waiting_query_auth_ready (MMBaseModem *self, + GAsyncResult *res, + HandleCallWaitingQueryContext *ctx) +{ + MMModemState modem_state = MM_MODEM_STATE_UNKNOWN; + GError *error = NULL; + + if (!mm_base_modem_authorize_finish (self, res, &error)) { + g_dbus_method_invocation_take_error (ctx->invocation, error); + handle_call_waiting_query_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 query call waiting: device not yet enabled"); + handle_call_waiting_query_context_free (ctx); + return; + } + + if (!MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query || + !MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query_finish) { + g_dbus_method_invocation_return_error (ctx->invocation, + MM_CORE_ERROR, + MM_CORE_ERROR_UNSUPPORTED, + "Cannot query call waiting: unsupported"); + handle_call_waiting_query_context_free (ctx); + return; + } + + MM_IFACE_MODEM_VOICE_GET_INTERFACE (self)->call_waiting_query (MM_IFACE_MODEM_VOICE (self), + (GAsyncReadyCallback)call_waiting_query_ready, + ctx); +} + +static gboolean +handle_call_waiting_query (MmGdbusModemVoice *skeleton, + GDBusMethodInvocation *invocation, + MMIfaceModemVoice *self) +{ + HandleCallWaitingQueryContext *ctx; + + ctx = g_slice_new0 (HandleCallWaitingQueryContext); + 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_call_waiting_query_auth_ready, + ctx); + return TRUE; +} + +/*****************************************************************************/ /* Leave one of the calls from the multiparty call */ typedef struct { @@ -2773,13 +2971,15 @@ interface_initialization_step (GTask *task) case INITIALIZATION_STEP_LAST: /* Setup all method handlers */ g_object_connect (ctx->skeleton, - "signal::handle-create-call", G_CALLBACK (handle_create), self, - "signal::handle-delete-call", G_CALLBACK (handle_delete), self, - "signal::handle-list-calls", G_CALLBACK (handle_list), self, - "signal::handle-hangup-and-accept", G_CALLBACK (handle_hangup_and_accept), self, - "signal::handle-hold-and-accept", G_CALLBACK (handle_hold_and_accept), self, - "signal::handle-hangup-all", G_CALLBACK (handle_hangup_all), self, - "signal::handle-transfer", G_CALLBACK (handle_transfer), self, + "signal::handle-create-call", G_CALLBACK (handle_create), self, + "signal::handle-delete-call", G_CALLBACK (handle_delete), self, + "signal::handle-list-calls", G_CALLBACK (handle_list), self, + "signal::handle-hangup-and-accept", G_CALLBACK (handle_hangup_and_accept), self, + "signal::handle-hold-and-accept", G_CALLBACK (handle_hold_and_accept), self, + "signal::handle-hangup-all", G_CALLBACK (handle_hangup_all), self, + "signal::handle-transfer", G_CALLBACK (handle_transfer), self, + "signal::handle-call-waiting-setup", G_CALLBACK (handle_call_waiting_setup), self, + "signal::handle-call-waiting-query", G_CALLBACK (handle_call_waiting_query), self, NULL); /* Finally, export the new interface */ diff --git a/src/mm-iface-modem-voice.h b/src/mm-iface-modem-voice.h index 2f477638..7f793dcc 100644 --- a/src/mm-iface-modem-voice.h +++ b/src/mm-iface-modem-voice.h @@ -175,6 +175,24 @@ struct _MMIfaceModemVoice { gboolean (* transfer_finish) (MMIfaceModemVoice *self, GAsyncResult *res, GError **error); + + /* Call waiting setup */ + void (* call_waiting_setup) (MMIfaceModemVoice *self, + gboolean enable, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* call_waiting_setup_finish) (MMIfaceModemVoice *self, + GAsyncResult *res, + GError **error); + + /* Call waiting query */ + void (* call_waiting_query) (MMIfaceModemVoice *self, + GAsyncReadyCallback callback, + gpointer user_data); + gboolean (* call_waiting_query_finish) (MMIfaceModemVoice *self, + GAsyncResult *res, + gboolean *status, + GError **error); }; GType mm_iface_modem_voice_get_type (void); |