1
0
Fork 0
mirror of https://gitlab.gnome.org/GNOME/calls.git synced 2024-11-16 21:46:33 +00:00
Purism-Calls/plugins/provider/mm/calls-mm-origin.c
Evangelos Ribeiro Tzaras 205f691570 mm-origin: Disable call waiting
As Calls cannot deal with call waiting we should disable it entirely.
This works around issues where call audio get's completely broken once
the waiting call is disconnected on the BM818 modem shipped with the
Librem 5.

See https://source.puri.sm/Librem5/OS-issues/-/issues/311
2023-01-31 08:55:38 +00:00

1007 lines
27 KiB
C

/*
* Copyright (C) 2018 Purism SPC
*
* This file is part of Calls.
*
* Calls 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 3 of the License, or
* (at your option) any later version.
*
* Calls 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 Calls. If not, see <http://www.gnu.org/licenses/>.
*
* Author: Bob Ham <bob.ham@puri.sm>
*
* SPDX-License-Identifier: GPL-3.0-or-later
*
*/
#define G_LOG_DOMAIN "CallsMMOrigin"
#include "calls-mm-origin.h"
#include "calls-origin.h"
#include "calls-ussd.h"
#include "calls-mm-call.h"
#include "calls-message-source.h"
#include "calls-util.h"
#include "itu-e212-iso.h"
#include <glib/gi18n.h>
struct _CallsMMOrigin {
GObject parent_instance;
MMObject *mm_obj;
MMModemVoice *voice;
MMModem3gppUssd *ussd;
MMSim *sim;
/* XXX: These should be used only for pointer comparison,
* The content should never be used as it might be
* pointing to a freed location */
char *last_ussd_request;
char *last_ussd_response;
gulong ussd_handle_id;
char *id;
char *name;
GHashTable *calls;
char *country_code;
};
static void calls_mm_origin_message_source_interface_init (CallsOriginInterface *iface);
static void calls_mm_origin_origin_interface_init (CallsOriginInterface *iface);
static void calls_mm_origin_ussd_interface_init (CallsUssdInterface *iface);
G_DEFINE_TYPE_WITH_CODE (CallsMMOrigin, calls_mm_origin, G_TYPE_OBJECT,
G_IMPLEMENT_INTERFACE (CALLS_TYPE_MESSAGE_SOURCE,
calls_mm_origin_message_source_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_USSD,
calls_mm_origin_ussd_interface_init)
G_IMPLEMENT_INTERFACE (CALLS_TYPE_ORIGIN,
calls_mm_origin_origin_interface_init))
enum {
PROP_0,
PROP_ID,
PROP_NAME,
PROP_CALLS,
PROP_MODEM,
PROP_COUNTRY_CODE,
PROP_LAST_PROP,
};
static GParamSpec *props[PROP_LAST_PROP];
static void
ussd_initiate_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GTask) task = user_data;
MMModem3gppUssd *ussd = MM_MODEM_3GPP_USSD (object);
CallsMMOrigin *self = CALLS_MM_ORIGIN (user_data);
char *response = NULL;
GError *error = NULL;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (MM_IS_MODEM_3GPP_USSD (ussd));
g_assert (CALLS_IS_MM_ORIGIN (self));
response = mm_modem_3gpp_ussd_initiate_finish (ussd, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_pointer (task, response, g_free);
}
static void
ussd_reinitiate_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GTask) task = user_data;
CallsUssd *ussd = CALLS_USSD (object);
CallsMMOrigin *self = CALLS_MM_ORIGIN (user_data);
GCancellable *cancellable;
GError *error = NULL;
const char *command;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_USSD (ussd));
g_assert (CALLS_IS_MM_ORIGIN (self));
calls_ussd_cancel_finish (ussd, result, &error);
cancellable = g_task_get_cancellable (task);
command = g_task_get_task_data (task);
if (error)
g_task_return_error (task, error);
else
mm_modem_3gpp_ussd_initiate (self->ussd, command, cancellable,
ussd_initiate_cb, g_steal_pointer (&task));
}
static void
ussd_respond_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GTask) task = user_data;
MMModem3gppUssd *ussd = MM_MODEM_3GPP_USSD (object);
CallsMMOrigin *self;
char *response = NULL;
GError *error = NULL;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_MM_ORIGIN (self));
g_assert (MM_IS_MODEM_3GPP_USSD (ussd));
response = mm_modem_3gpp_ussd_respond_finish (ussd, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_pointer (task, response, g_free);
}
static void
ussd_cancel_cb (GObject *object,
GAsyncResult *result,
gpointer user_data)
{
g_autoptr (GTask) task = user_data;
MMModem3gppUssd *ussd = MM_MODEM_3GPP_USSD (object);
CallsMMOrigin *self;
GError *error = NULL;
gboolean response;
g_assert (G_IS_TASK (task));
self = g_task_get_source_object (task);
g_assert (CALLS_IS_MM_ORIGIN (self));
g_assert (MM_IS_MODEM_3GPP_USSD (ussd));
response = mm_modem_3gpp_ussd_cancel_finish (ussd, result, &error);
if (error)
g_task_return_error (task, error);
else
g_task_return_boolean (task, response);
}
static CallsUssdState
calls_mm_ussd_get_state (CallsUssd *ussd)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
if (!self->ussd)
return CALLS_USSD_STATE_UNKNOWN;
return (CallsUssdState) mm_modem_3gpp_ussd_get_state (self->ussd);
}
static void
calls_mm_ussd_initiate_async (CallsUssd *ussd,
const char *command,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
g_autoptr (GTask) task = NULL;
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
CallsUssdState state;
g_return_if_fail (CALLS_IS_USSD (ussd));
task = g_task_new (self, cancellable, callback, user_data);
if (!self->ussd) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_NOT_FOUND,
"No USSD interface found");
return;
}
if (STR_IS_NULL_OR_EMPTY (command)) {
g_task_return_new_error (task, G_IO_ERROR, G_IO_ERROR_FAILED,
"USSD command empty");
return;
}
state = calls_ussd_get_state (CALLS_USSD (self));
g_task_set_task_data (task, g_strdup (command), g_free);
if (state == CALLS_USSD_STATE_ACTIVE ||
state == CALLS_USSD_STATE_USER_RESPONSE)
calls_ussd_cancel_async (CALLS_USSD (self), cancellable,
ussd_reinitiate_cb, g_steal_pointer (&task));
else
mm_modem_3gpp_ussd_initiate (self->ussd, command, cancellable,
ussd_initiate_cb, g_steal_pointer (&task));
}
static char *
calls_mm_ussd_initiate_finish (CallsUssd *ussd,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_USSD (ussd), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
calls_mm_ussd_respond_async (CallsUssd *ussd,
const char *response,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
GTask *task;
g_return_if_fail (CALLS_IS_USSD (ussd));
task = g_task_new (self, cancellable, callback, user_data);
mm_modem_3gpp_ussd_respond (self->ussd, response, cancellable,
ussd_respond_cb, task);
}
static char *
calls_mm_ussd_respond_finish (CallsUssd *ussd,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_USSD (ussd), NULL);
g_return_val_if_fail (G_IS_TASK (result), NULL);
return g_task_propagate_pointer (G_TASK (result), error);
}
static void
calls_mm_ussd_cancel_async (CallsUssd *ussd,
GCancellable *cancellable,
GAsyncReadyCallback callback,
gpointer user_data)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (ussd);
GTask *task;
g_return_if_fail (CALLS_IS_USSD (ussd));
task = g_task_new (self, cancellable, callback, user_data);
mm_modem_3gpp_ussd_cancel (self->ussd, cancellable,
ussd_cancel_cb, task);
}
static gboolean
calls_mm_ussd_cancel_finish (CallsUssd *ussd,
GAsyncResult *result,
GError **error)
{
g_return_val_if_fail (CALLS_IS_USSD (ussd), FALSE);
g_return_val_if_fail (G_IS_TASK (result), FALSE);
return g_task_propagate_boolean (G_TASK (result), error);
}
static void
dial_cb (MMModemVoice *voice,
GAsyncResult *res,
CallsMMOrigin *self)
{
g_autoptr (GError) error = NULL;
MMCall *call;
call = mm_modem_voice_create_call_finish (voice, res, &error);
if (!call) {
g_warning ("Error dialing number on ModemManager modem `%s': %s",
self->name, error->message);
CALLS_ERROR (self, error);
}
}
static void
dial (CallsOrigin *origin,
const char *number)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (origin);
g_autoptr (MMCallProperties) call_props = NULL;
g_assert (self->voice != NULL);
call_props = mm_call_properties_new ();
mm_call_properties_set_number (call_props, number);
mm_modem_voice_create_call (self->voice,
call_props,
NULL,
(GAsyncReadyCallback) dial_cb,
self);
}
static gboolean
supports_protocol (CallsOrigin *origin,
const char *protocol)
{
g_assert (protocol);
g_assert (CALLS_IS_MM_ORIGIN (origin));
return g_strcmp0 (protocol, "tel") == 0;
}
static void
remove_calls (CallsMMOrigin *self, const char *reason)
{
GList *paths;
gpointer call;
paths = g_hash_table_get_keys (self->calls);
for (GList *node = paths; node != NULL; node = node->next) {
g_hash_table_steal_extended (self->calls, node->data, NULL, &call);
g_signal_emit_by_name (self, "call-removed",
CALLS_CALL (call), reason);
g_object_unref (call);
}
g_list_free_full (paths, g_free);
}
struct CallsMMOriginDeleteCallData {
CallsMMOrigin *self;
char *path;
};
static void
delete_call_cb (MMModemVoice *voice,
GAsyncResult *res,
struct CallsMMOriginDeleteCallData *data)
{
g_autoptr (GError) error = NULL;
gboolean ok;
ok = mm_modem_voice_delete_call_finish (voice, res, &error);
if (!ok) {
g_warning ("Error deleting call `%s' on MMModemVoice `%s': %s",
data->path, data->self->name, error->message);
CALLS_ERROR (data->self, error);
}
g_free (data->path);
g_object_unref (data->self);
g_free (data);
}
static void
delete_call (CallsMMOrigin *self,
CallsMMCall *call)
{
const char *path;
struct CallsMMOriginDeleteCallData *data;
path = calls_mm_call_get_object_path (call);
data = g_new0 (struct CallsMMOriginDeleteCallData, 1);
data->self = g_object_ref (self);
data->path = g_strdup (path);
mm_modem_voice_delete_call (self->voice,
path,
NULL,
(GAsyncReadyCallback) delete_call_cb,
data);
}
static void
call_state_changed_cb (CallsCall *call,
GParamSpec *pspec,
CallsMMOrigin *self)
{
g_assert (CALLS_IS_MM_ORIGIN (self));
g_assert (CALLS_IS_MM_CALL (call));
if (calls_call_get_state (call) != CALLS_CALL_STATE_DISCONNECTED)
return;
delete_call (self, CALLS_MM_CALL (call));
}
static void
add_call (CallsMMOrigin *self,
MMCall *mm_call)
{
CallsMMCall *call;
char *path;
call = calls_mm_call_new (mm_call);
g_signal_connect (call, "notify::state",
G_CALLBACK (call_state_changed_cb),
self);
path = mm_call_dup_path (mm_call);
g_hash_table_insert (self->calls, path, call);
g_signal_emit_by_name (CALLS_ORIGIN (self), "call-added",
CALLS_CALL (call));
if (mm_call_get_state (mm_call) == MM_CALL_STATE_TERMINATED) {
/* Delete any remnant disconnected call */
delete_call (self, call);
}
g_debug ("Call `%s' added", path);
/* FIXME: Hang up the call, since accepting a secondary call does not currently work.
* CallsMMCall[28822]: WARNING: Error accepting ModemManager call to `+4916XXXXXXXX': GDBus.Error:org.freedesktop.ModemManager1.Error.Core.Failed: This call was not ringing, cannot accept
*/
if (g_hash_table_size (self->calls) > 1)
calls_call_hang_up (CALLS_CALL (call));
}
struct CallsMMOriginCallAddedData {
CallsMMOrigin *self;
char *path;
};
static void
call_added_list_calls_cb (MMModemVoice *voice,
GAsyncResult *res,
struct CallsMMOriginCallAddedData *data)
{
g_autoptr (GError) error = NULL;
GList *calls;
calls = mm_modem_voice_list_calls_finish (voice, res, &error);
if (!calls) {
if (error) {
g_warning ("Error listing calls on MMModemVoice `%s'"
" after call-added signal: %s",
data->self->name, error->message);
CALLS_ERROR (data->self, error);
} else {
g_warning ("No calls on MMModemVoice `%s'"
" after call-added signal",
data->self->name);
}
} else {
GList *node;
MMCall *call;
gboolean found = FALSE;
for (node = calls; node; node = node->next) {
call = MM_CALL (node->data);
if (g_strcmp0 (mm_call_get_path (call), data->path) == 0) {
add_call (data->self, call);
found = TRUE;
}
}
if (!found) {
g_warning ("Could not find new call `%s' in call list"
" on MMModemVoice `%s' after call-added signal",
data->path, data->self->name);
}
g_list_free_full (calls, g_object_unref);
}
g_free (data->path);
g_object_unref (data->self);
g_free (data);
}
static void
call_added_cb (MMModemVoice *voice,
char *path,
CallsMMOrigin *self)
{
struct CallsMMOriginCallAddedData *data;
if (g_hash_table_contains (self->calls, path)) {
g_warning ("Received call-added signal for"
" existing call object path `%s'", path);
return;
}
data = g_new0 (struct CallsMMOriginCallAddedData, 1);
data->self = g_object_ref (self);
data->path = g_strdup (path);
mm_modem_voice_list_calls (voice,
NULL,
(GAsyncReadyCallback) call_added_list_calls_cb,
data);
}
static void
call_deleted_cb (MMModemVoice *voice,
const char *path,
gpointer user_data)
{
g_autoptr (CallsMMOrigin) self = NULL;
gpointer call;
gpointer key;
const char *mm_reason;
g_assert (CALLS_IS_MM_ORIGIN (user_data));
self = CALLS_MM_ORIGIN (user_data);
g_debug ("Removing call `%s'", path);
g_hash_table_steal_extended (self->calls, path, &key, &call);
g_free (key);
if (!call) {
g_warning ("Could not find removed call `%s'", path);
return;
}
mm_reason = calls_mm_call_get_disconnect_reason (CALLS_MM_CALL (call));
g_signal_emit_by_name (self,
"call-removed",
call,
mm_reason ?: "Call removed");
g_object_unref (call);
g_debug ("Removed call `%s'", path);
}
static void
list_calls_cb (MMModemVoice *voice,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) error = NULL;
g_autoptr (CallsMMOrigin) self = NULL;
GList *calls;
g_assert (CALLS_IS_MM_ORIGIN (user_data));
self = CALLS_MM_ORIGIN (user_data);
calls = mm_modem_voice_list_calls_finish (voice, res, &error);
if (!calls) {
if (error) {
g_warning ("Error listing calls on MMModemVoice `%s': %s",
self->name, error->message);
CALLS_ERROR (self, error);
}
return;
}
for (GList *node = calls; node; node = node->next) {
add_call (self, MM_CALL (node->data));
}
g_list_free_full (calls, g_object_unref);
}
static void
set_property (GObject *object,
guint property_id,
const GValue *value,
GParamSpec *pspec)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
switch (property_id) {
case PROP_ID:
self->id = g_value_dup_string (value);
break;
case PROP_MODEM:
g_set_object (&self->mm_obj, g_value_get_object (value));
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static void
get_property (GObject *object,
guint property_id,
GValue *value,
GParamSpec *pspec)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
switch (property_id) {
case PROP_ID:
g_value_set_string (value, self->id);
break;
case PROP_NAME:
g_value_set_string (value, self->name);
break;
case PROP_CALLS:
g_value_set_pointer (value, g_hash_table_get_values (self->calls));
break;
case PROP_COUNTRY_CODE:
g_value_set_string (value, self->country_code);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
break;
}
}
static char *
modem_get_name (MMModem *modem)
{
char *name = NULL;
const char * const *numbers = NULL;
numbers = mm_modem_get_own_numbers (modem);
if (numbers && g_strv_length ((char **) numbers) > 0) {
name = g_strdup (numbers[0]);
return name;
}
#define try(prop) \
name = mm_modem_dup_##prop (modem); \
if (name) { \
return name; \
}
try (model);
try (manufacturer);
try (device);
try (primary_port);
try (device_identifier);
try (plugin);
#undef try
return NULL;
}
static void
ussd_properties_changed_cb (CallsMMOrigin *self,
GVariant *properties)
{
const char *response;
GVariant *value;
CallsUssdState state;
g_assert (CALLS_IS_MM_ORIGIN (self));
state = calls_ussd_get_state (CALLS_USSD (self));
value = g_variant_lookup_value (properties, "State", NULL);
if (value)
g_signal_emit_by_name (self, "ussd-state-changed");
g_clear_pointer (&value, g_variant_unref);
/* XXX: We check for user state only because the NetworkRequest
* dbus property change isn't regularly emitted */
if (state == CALLS_USSD_STATE_USER_RESPONSE ||
(value = g_variant_lookup_value (properties, "NetworkRequest", NULL))) {
response = mm_modem_3gpp_ussd_get_network_request (self->ussd);
if (!STR_IS_NULL_OR_EMPTY (response) && response != self->last_ussd_request)
g_signal_emit_by_name (self, "ussd-added", response);
if (!STR_IS_NULL_OR_EMPTY (response))
self->last_ussd_request = (char *) response;
g_clear_pointer (&value, g_variant_unref);
}
if (state != CALLS_USSD_STATE_USER_RESPONSE &&
(value = g_variant_lookup_value (properties, "NetworkNotification", NULL))) {
response = mm_modem_3gpp_ussd_get_network_notification (self->ussd);
if (!STR_IS_NULL_OR_EMPTY (response) && response != self->last_ussd_response)
g_signal_emit_by_name (self, "ussd-added", response);
if (!STR_IS_NULL_OR_EMPTY (response))
self->last_ussd_response = (char *) response;
g_clear_pointer (&value, g_variant_unref);
}
}
static void
call_mm_ussd_changed_cb (CallsMMOrigin *self)
{
g_assert (CALLS_IS_MM_ORIGIN (self));
if (self->ussd_handle_id)
g_signal_handler_disconnect (self, self->ussd_handle_id);
self->ussd_handle_id = 0;
g_clear_object (&self->ussd);
self->ussd = mm_object_get_modem_3gpp_ussd (self->mm_obj);
/* XXX: We hook to dbus properties changed because the regular signal emission is inconsistent */
if (self->ussd)
self->ussd_handle_id = g_signal_connect_object (self->ussd, "g-properties-changed",
G_CALLBACK (ussd_properties_changed_cb), self,
G_CONNECT_SWAPPED);
}
static void
get_sim_ready_cb (MMModem *modem,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (CallsMMOrigin) self = NULL;
const char *code;
g_assert (CALLS_IS_MM_ORIGIN (user_data));
self = CALLS_MM_ORIGIN (user_data);
self->sim = mm_modem_get_sim_finish (modem, res, NULL);
code = get_country_iso_for_mcc (mm_sim_get_imsi (self->sim));
if (code) {
if (g_strcmp0 (self->country_code, code) == 0)
return;
g_debug ("Setting the country code to `%s'", code);
self->country_code = g_strdup (code);
g_object_notify_by_pspec (G_OBJECT (self), props[PROP_COUNTRY_CODE]);
}
}
static void
call_waiting_setup_cb (GObject *obj,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) error = NULL;
g_autoptr (CallsMMOrigin) self = NULL;
MMModemVoice *voice = MM_MODEM_VOICE (obj);
g_assert (CALLS_IS_MM_ORIGIN (user_data));
self = CALLS_MM_ORIGIN (user_data);
g_assert (voice == self->voice);
if (!mm_modem_voice_call_waiting_setup_finish (voice, res, &error)) {
g_warning ("Could not disable call waiting: %s", error->message);
return;
}
g_info ("Disabled call waiting on modem '%s'", self->name);
}
static void
call_waiting_query_cb (GObject *obj,
GAsyncResult *res,
gpointer user_data)
{
g_autoptr (GError) error = NULL;
g_autoptr (CallsMMOrigin) self = NULL;
MMModemVoice *voice = MM_MODEM_VOICE (obj);
gboolean waiting;
g_assert (CALLS_IS_MM_ORIGIN (user_data));
self = CALLS_MM_ORIGIN (user_data);
g_assert (voice == self->voice);
if (!mm_modem_voice_call_waiting_query_finish (voice, res, &waiting, &error)) {
g_warning ("Could not query call waiting status: %s", error->message);
return;
}
g_debug ("Call waiting is %sabled", waiting ? "en" : "dis");
if (waiting) {
g_info ("Disabling call waiting: Not implemented");
mm_modem_voice_call_waiting_setup (voice,
FALSE,
NULL,
call_waiting_setup_cb,
g_steal_pointer (&self));
}
}
static void
constructed (GObject *object)
{
g_autoptr (MMModem) modem = NULL;
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
G_OBJECT_CLASS (calls_mm_origin_parent_class)->constructed (object);
modem = mm_object_get_modem (self->mm_obj);
self->name = modem_get_name (modem);
mm_modem_get_sim (modem,
NULL,
(GAsyncReadyCallback) get_sim_ready_cb,
g_object_ref (self));
g_signal_connect_object (self->mm_obj, "notify::modem3gpp-ussd",
G_CALLBACK (call_mm_ussd_changed_cb), self,
G_CONNECT_SWAPPED);
call_mm_ussd_changed_cb (self);
self->voice = mm_object_get_modem_voice (self->mm_obj);
g_assert (self->voice != NULL);
mm_modem_voice_call_waiting_query (self->voice,
NULL,
call_waiting_query_cb,
g_object_ref (self));
g_signal_connect (self->voice, "call-added",
G_CALLBACK (call_added_cb), self);
g_signal_connect (self->voice, "call-deleted",
G_CALLBACK (call_deleted_cb), self);
mm_modem_voice_list_calls (self->voice,
NULL,
(GAsyncReadyCallback) list_calls_cb,
g_object_ref (self));
}
static void
dispose (GObject *object)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
remove_calls (self, NULL);
g_clear_object (&self->voice);
g_clear_object (&self->mm_obj);
g_clear_object (&self->ussd);
g_clear_object (&self->sim);
g_clear_pointer (&self->country_code, g_free);
g_clear_pointer (&self->id, g_free);
G_OBJECT_CLASS (calls_mm_origin_parent_class)->dispose (object);
}
static void
finalize (GObject *object)
{
CallsMMOrigin *self = CALLS_MM_ORIGIN (object);
g_hash_table_unref (self->calls);
g_free (self->name);
G_OBJECT_CLASS (calls_mm_origin_parent_class)->finalize (object);
}
static void
calls_mm_origin_class_init (CallsMMOriginClass *klass)
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
object_class->get_property = get_property;
object_class->set_property = set_property;
object_class->constructed = constructed;
object_class->dispose = dispose;
object_class->finalize = finalize;
props[PROP_MODEM] =
g_param_spec_object ("mm-object",
"Modem Object",
"A libmm-glib proxy object for the modem",
MM_TYPE_OBJECT,
G_PARAM_WRITABLE | G_PARAM_CONSTRUCT_ONLY);
g_object_class_install_property (object_class, PROP_MODEM, props[PROP_MODEM]);
#define IMPLEMENTS(ID, NAME) \
g_object_class_override_property (object_class, ID, NAME); \
props[ID] = g_object_class_find_property(object_class, NAME);
IMPLEMENTS (PROP_ID, "id");
IMPLEMENTS (PROP_NAME, "name");
IMPLEMENTS (PROP_CALLS, "calls");
IMPLEMENTS (PROP_COUNTRY_CODE, "country-code");
#undef IMPLEMENTS
}
static void
calls_mm_origin_message_source_interface_init (CallsOriginInterface *iface)
{
}
static void
calls_mm_origin_ussd_interface_init (CallsUssdInterface *iface)
{
iface->get_state = calls_mm_ussd_get_state;
iface->initiate_async = calls_mm_ussd_initiate_async;
iface->initiate_finish = calls_mm_ussd_initiate_finish;
iface->respond_async = calls_mm_ussd_respond_async;
iface->respond_finish = calls_mm_ussd_respond_finish;
iface->cancel_async = calls_mm_ussd_cancel_async;
iface->cancel_finish = calls_mm_ussd_cancel_finish;
}
static void
calls_mm_origin_origin_interface_init (CallsOriginInterface *iface)
{
iface->dial = dial;
iface->supports_protocol = supports_protocol;
}
static void
calls_mm_origin_init (CallsMMOrigin *self)
{
self->calls = g_hash_table_new_full (g_str_hash, g_str_equal,
g_free, g_object_unref);
}
CallsMMOrigin *
calls_mm_origin_new (MMObject *mm_obj,
const char *id)
{
return g_object_new (CALLS_TYPE_MM_ORIGIN,
"mm-object", mm_obj,
"id", id,
NULL);
}
gboolean
calls_mm_origin_matches (CallsMMOrigin *self,
MMObject *mm_obj)
{
g_return_val_if_fail (CALLS_IS_MM_ORIGIN (self), FALSE);
g_return_val_if_fail (MM_IS_OBJECT (mm_obj), FALSE);
if (self->mm_obj)
return g_strcmp0 (mm_object_get_path (mm_obj),
mm_object_get_path (self->mm_obj)) == 0;
return FALSE;
}