Files
ModemManager/cli/mmcli-modem-voice.c
T
Aleksander Morgado d56d1b2656 api,voice: new HangupAndAccept() method
This method will hangup the currently active call and right away
accept the next available call.

The user of the API does not need to specify explicitly which is the
next call to accept, because that is decided automatically:
  * If there is any waiting call, it will accept it right away.
  * If there is no waiting call but there is a held call, it will make
    the held call active again.
2019-07-11 23:20:59 +02:00

484 lines
14 KiB
C

/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* mmcli -- Control modem status & access information from the command line
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* Copyright (C) 2012 Google, Inc.
* Copyright (C) 2015 Riccardo Vangelisti <riccardo.vangelisti@sadel.it>
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <locale.h>
#include <string.h>
#include <glib.h>
#include <gio/gio.h>
#define _LIBMM_INSIDE_MMCLI
#include <libmm-glib.h>
#include "mmcli.h"
#include "mmcli-common.h"
#include "mmcli-output.h"
/* Context */
typedef struct {
GDBusConnection *connection;
MMManager *manager;
GCancellable *cancellable;
MMObject *object;
MMModemVoice *modem_voice;
} Context;
static Context *ctx;
/* Options */
static gboolean list_flag;
static gchar *create_str;
static gchar *delete_str;
static gboolean hangup_and_accept_flag;
static GOptionEntry entries[] = {
{ "voice-list-calls", 0, 0, G_OPTION_ARG_NONE, &list_flag,
"List calls available in a given modem",
NULL
},
{ "voice-create-call", 0, 0, G_OPTION_ARG_STRING, &create_str,
"Create a new call in a given modem",
"[\"key=value,...\"]"
},
{ "voice-delete-call", 0, 0, G_OPTION_ARG_STRING, &delete_str,
"Delete a call from a given modem",
"[PATH|INDEX]"
},
{ "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
},
{ NULL }
};
GOptionGroup *
mmcli_modem_voice_get_option_group (void)
{
GOptionGroup *group;
group = g_option_group_new ("voice",
"Voice options",
"Show Voice options",
NULL,
NULL);
g_option_group_add_entries (group, entries);
return group;
}
gboolean
mmcli_modem_voice_options_enabled (void)
{
static guint n_actions = 0;
static gboolean checked = FALSE;
if (checked)
return !!n_actions;
n_actions = (list_flag +
!!create_str +
!!delete_str +
hangup_and_accept_flag);
if (n_actions > 1) {
g_printerr ("error: too many Voice actions requested\n");
exit (EXIT_FAILURE);
}
checked = TRUE;
return !!n_actions;
}
static void
context_free (Context *ctx)
{
if (!ctx)
return;
if (ctx->cancellable)
g_object_unref (ctx->cancellable);
if (ctx->modem_voice)
g_object_unref (ctx->modem_voice);
if (ctx->object)
g_object_unref (ctx->object);
if (ctx->manager)
g_object_unref (ctx->manager);
if (ctx->connection)
g_object_unref (ctx->connection);
g_free (ctx);
}
static void
ensure_modem_voice (void)
{
if (mm_modem_get_state (mm_object_peek_modem (ctx->object)) < MM_MODEM_STATE_ENABLED) {
g_printerr ("error: modem not enabled yet\n");
exit (EXIT_FAILURE);
}
if (!ctx->modem_voice) {
g_printerr ("error: modem has no voice capabilities\n");
exit (EXIT_FAILURE);
}
/* Success */
}
void
mmcli_modem_voice_shutdown (void)
{
context_free (ctx);
}
static MMCallProperties *
build_call_properties_from_input (const gchar *properties_string)
{
GError *error = NULL;
MMCallProperties *properties;
properties = mm_call_properties_new_from_string (properties_string, &error);
if (!properties) {
g_printerr ("error: cannot parse properties string: '%s'\n", error->message);
exit (EXIT_FAILURE);
}
return properties;
}
static void
output_call_info (MMCall *call)
{
gchar *extra;
extra = g_strdup_printf ("%s (%s)",
mm_call_direction_get_string (mm_call_get_direction (call)),
mm_call_state_get_string (mm_call_get_state (call)));
mmcli_output_listitem (MMC_F_CALL_LIST_DBUS_PATH,
" ",
mm_call_get_path (call),
extra);
g_free (extra);
}
static void
hangup_and_accept_process_reply (const GError *error)
{
if (error) {
g_printerr ("error: couldn't hangup and accept: '%s'\n",
error->message);
exit (EXIT_FAILURE);
}
g_print ("operation successful\n");
}
static void
hangup_and_accept_ready (MMModemVoice *modem,
GAsyncResult *result,
gpointer nothing)
{
GError *error = NULL;
mm_modem_voice_hangup_and_accept_finish (modem, result, &error);
hangup_and_accept_process_reply (error);
mmcli_async_operation_done ();
}
static void
list_process_reply (GList *result,
const GError *error)
{
GList *l;
if (error) {
g_printerr ("error: couldn't list call: '%s'\n",
error->message);
exit (EXIT_FAILURE);
}
for (l = result; l; l = g_list_next (l))
output_call_info (MM_CALL (l->data));
mmcli_output_list_dump (MMC_F_CALL_LIST_DBUS_PATH);
}
static void
list_ready (MMModemVoice *modem,
GAsyncResult *result,
gpointer nothing)
{
GList *operation_result;
GError *error = NULL;
operation_result = mm_modem_voice_list_calls_finish (modem, result, &error);
list_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void
create_process_reply (MMCall *call,
const GError *error)
{
if (!call) {
g_printerr ("error: couldn't create new call: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
g_print ("Successfully created new call: %s\n", mm_call_get_path (call));
g_object_unref (call);
}
static void
create_ready (MMModemVoice *modem,
GAsyncResult *result,
gpointer nothing)
{
MMCall *call;
GError *error = NULL;
call = mm_modem_voice_create_call_finish (modem, result, &error);
create_process_reply (call, error);
mmcli_async_operation_done ();
}
static void
delete_process_reply (gboolean result,
const GError *error)
{
if (!result) {
g_printerr ("error: couldn't delete call: '%s'\n",
error ? error->message : "unknown error");
exit (EXIT_FAILURE);
}
g_print ("successfully deleted call from modem\n");
}
static void
delete_ready (MMModemVoice *modem,
GAsyncResult *result,
gpointer nothing)
{
gboolean operation_result;
GError *error = NULL;
operation_result = mm_modem_voice_delete_call_finish (modem, result, &error);
delete_process_reply (operation_result, error);
mmcli_async_operation_done ();
}
static void
get_call_to_delete_ready (GDBusConnection *connection,
GAsyncResult *res)
{
MMCall *call;
MMObject *obj = NULL;
call = mmcli_get_call_finish (res, NULL, &obj);
if (!g_str_equal (mm_object_get_path (obj), mm_modem_voice_get_path (ctx->modem_voice))) {
g_printerr ("error: call '%s' not owned by modem '%s'",
mm_call_get_path (call),
mm_modem_voice_get_path (ctx->modem_voice));
exit (EXIT_FAILURE);
}
mm_modem_voice_delete_call (ctx->modem_voice,
mm_call_get_path (call),
ctx->cancellable,
(GAsyncReadyCallback)delete_ready,
NULL);
g_object_unref (call);
g_object_unref (obj);
}
static void
get_modem_ready (GObject *source,
GAsyncResult *result,
gpointer none)
{
ctx->object = mmcli_get_modem_finish (result, &ctx->manager);
ctx->modem_voice = mm_object_get_modem_voice (ctx->object);
/* Setup operation timeout */
if (ctx->modem_voice)
mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_voice));
ensure_modem_voice ();
/* Request to list call? */
if (list_flag) {
g_debug ("Asynchronously listing calls in modem...");
mm_modem_voice_list_calls (ctx->modem_voice,
ctx->cancellable,
(GAsyncReadyCallback)list_ready,
NULL);
return;
}
/* Request to create a new call? */
if (create_str) {
MMCallProperties *properties;
properties = build_call_properties_from_input (create_str);
g_debug ("Asynchronously creating new call in modem...");
mm_modem_voice_create_call (ctx->modem_voice,
properties,
ctx->cancellable,
(GAsyncReadyCallback)create_ready,
NULL);
g_object_unref (properties);
return;
}
/* Request to delete a given call? */
if (delete_str) {
mmcli_get_call (ctx->connection,
delete_str,
ctx->cancellable,
(GAsyncReadyCallback)get_call_to_delete_ready,
NULL);
return;
}
/* Request to hangup and accept? */
if (hangup_and_accept_flag) {
g_debug ("Asynchronously hanging up and accepting next call...");
mm_modem_voice_hangup_and_accept (ctx->modem_voice,
ctx->cancellable,
(GAsyncReadyCallback)hangup_and_accept_ready,
NULL);
return;
}
g_warn_if_reached ();
}
void
mmcli_modem_voice_run_asynchronous (GDBusConnection *connection,
GCancellable *cancellable)
{
/* Initialize context */
ctx = g_new0 (Context, 1);
if (cancellable)
ctx->cancellable = g_object_ref (cancellable);
ctx->connection = g_object_ref (connection);
/* Get proper modem */
mmcli_get_modem (connection,
mmcli_get_common_modem_string (),
cancellable,
(GAsyncReadyCallback)get_modem_ready,
NULL);
}
void
mmcli_modem_voice_run_synchronous (GDBusConnection *connection)
{
GError *error = NULL;
/* Initialize context */
ctx = g_new0 (Context, 1);
ctx->object = mmcli_get_modem_sync (connection,
mmcli_get_common_modem_string (),
&ctx->manager);
ctx->modem_voice = mm_object_get_modem_voice (ctx->object);
/* Setup operation timeout */
if (ctx->modem_voice)
mmcli_force_operation_timeout (G_DBUS_PROXY (ctx->modem_voice));
ensure_modem_voice ();
/* Request to list the call? */
if (list_flag) {
GList *result;
g_debug ("Synchronously listing call...");
result = mm_modem_voice_list_calls_sync (ctx->modem_voice, NULL, &error);
list_process_reply (result, error);
return;
}
/* Request to create a new call? */
if (create_str) {
MMCall *call;
GError *error = NULL;
MMCallProperties *properties;
properties = build_call_properties_from_input (create_str);
g_debug ("Synchronously creating new call in modem...");
call = mm_modem_voice_create_call_sync (ctx->modem_voice,
properties,
NULL,
&error);
g_object_unref (properties);
create_process_reply (call, error);
return;
}
/* Request to delete a given call? */
if (delete_str) {
gboolean result;
MMCall *call;
MMObject *obj = NULL;
call = mmcli_get_call_sync (connection,
delete_str,
NULL,
&obj);
if (!g_str_equal (mm_object_get_path (obj), mm_modem_voice_get_path (ctx->modem_voice))) {
g_printerr ("error: call '%s' not owned by modem '%s'",
mm_call_get_path (call),
mm_modem_voice_get_path (ctx->modem_voice));
exit (EXIT_FAILURE);
}
result = mm_modem_voice_delete_call_sync (ctx->modem_voice,
mm_call_get_path (call),
NULL,
&error);
g_object_unref (call);
g_object_unref (obj);
delete_process_reply (result, error);
return;
}
/* Request to hangup and accept? */
if (hangup_and_accept_flag) {
g_debug ("Synchronously hanging up and accepting call...");
mm_modem_voice_hangup_and_accept_sync (ctx->modem_voice, NULL, &error);
hangup_and_accept_process_reply (error);
return;
}
g_warn_if_reached ();
}