mirror of
https://gitlab.gnome.org/GNOME/calls.git
synced 2024-12-12 15:47:35 +00:00
eda460ac15
And adapt to changes It's easier to track changes if we are using GListModel than a GList. Also, eventually we shall have multiple providers, and we can use GtkFlattenListModel to merge multiple origins without manually managing pointers ourself. Since several widgets has APIs to handle GListModels, it will also help us create widgets from GListModels easily.
480 lines
11 KiB
C
480 lines
11 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
|
|
*
|
|
*/
|
|
|
|
#include "calls-ofono-provider.h"
|
|
#include "calls-provider.h"
|
|
#include "calls-ofono-origin.h"
|
|
#include "calls-message-source.h"
|
|
#include "util.h"
|
|
|
|
#include <libgdbofono/gdbo-manager.h>
|
|
#include <libgdbofono/gdbo-modem.h>
|
|
|
|
#include <glib/gi18n.h>
|
|
#include <libpeas/peas.h>
|
|
|
|
|
|
struct _CallsOfonoProvider
|
|
{
|
|
CallsProvider parent_instance;
|
|
|
|
/** D-Bus connection */
|
|
GDBusConnection *connection;
|
|
/** D-Bus proxy for the oFono Manager object */
|
|
GDBOManager *manager;
|
|
/** Map of D-Bus object paths to a struct CallsModemData */
|
|
GHashTable *modems;
|
|
/* A list of CallsOrigins */
|
|
GListStore *origins;
|
|
};
|
|
|
|
|
|
static void calls_ofono_provider_message_source_interface_init (CallsMessageSourceInterface *iface);
|
|
|
|
|
|
G_DEFINE_DYNAMIC_TYPE_EXTENDED
|
|
(CallsOfonoProvider, calls_ofono_provider, G_TYPE_OBJECT, 0,
|
|
G_IMPLEMENT_INTERFACE_DYNAMIC (CALLS_TYPE_MESSAGE_SOURCE,
|
|
calls_ofono_provider_message_source_interface_init))
|
|
|
|
|
|
gboolean
|
|
ofono_find_origin_index (CallsOfonoProvider *self,
|
|
const char *path,
|
|
guint *index)
|
|
{
|
|
GListModel *model;
|
|
guint n_items;
|
|
|
|
g_assert (CALLS_IS_OFONO_PROVIDER (self));
|
|
|
|
model = G_LIST_MODEL (self->origins);
|
|
n_items = g_list_model_get_n_items (model);
|
|
|
|
for (guint i = 0; i < n_items; i++)
|
|
{
|
|
g_autoptr(CallsOfonoOrigin) origin = NULL;
|
|
|
|
origin = g_list_model_get_item (model, i);
|
|
|
|
if (calls_ofono_origin_matches (origin, path))
|
|
{
|
|
if (index)
|
|
*index = i;
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
static gboolean
|
|
object_array_includes (GVariantIter *iter,
|
|
const gchar *needle)
|
|
{
|
|
const gchar *str;
|
|
gboolean found = FALSE;
|
|
while (g_variant_iter_loop (iter, "&s", &str))
|
|
{
|
|
if (g_strcmp0 (str, needle) == 0)
|
|
{
|
|
found = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
g_variant_iter_free (iter);
|
|
|
|
return found;
|
|
}
|
|
|
|
|
|
static void
|
|
modem_check_ifaces (CallsOfonoProvider *self,
|
|
GDBOModem *modem,
|
|
const gchar *modem_name,
|
|
GVariant *ifaces)
|
|
{
|
|
gboolean voice;
|
|
GVariantIter *iter = NULL;
|
|
const gchar *path;
|
|
guint index;
|
|
gboolean has_origin;
|
|
|
|
g_variant_get (ifaces, "as", &iter);
|
|
|
|
voice = object_array_includes
|
|
(iter, "org.ofono.VoiceCallManager");
|
|
|
|
path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (modem));
|
|
|
|
has_origin = ofono_find_origin_index (self, path, &index);
|
|
if (voice && !has_origin)
|
|
{
|
|
g_autoptr(CallsOfonoOrigin) origin = NULL;
|
|
|
|
g_debug ("Adding oFono Origin with path `%s'", path);
|
|
|
|
origin = calls_ofono_origin_new (modem);
|
|
g_list_store_append (self->origins, origin);
|
|
}
|
|
else if (!voice && has_origin)
|
|
{
|
|
g_list_store_remove (self->origins, index);
|
|
}
|
|
}
|
|
|
|
|
|
static void
|
|
modem_property_changed_cb (GDBOModem *modem,
|
|
const gchar *name,
|
|
GVariant *value,
|
|
CallsOfonoProvider *self)
|
|
{
|
|
gchar *modem_name;
|
|
|
|
g_debug ("Modem property `%s' changed", name);
|
|
|
|
if (g_strcmp0 (name, "Interfaces") != 0)
|
|
{
|
|
return;
|
|
}
|
|
|
|
modem_name = g_object_get_data (G_OBJECT (modem),
|
|
"calls-modem-name");
|
|
|
|
modem_check_ifaces (self, modem, modem_name, value);
|
|
}
|
|
|
|
|
|
struct CallsModemProxyNewData
|
|
{
|
|
CallsOfonoProvider *self;
|
|
gchar *name;
|
|
GVariant *ifaces;
|
|
};
|
|
|
|
|
|
static void
|
|
modem_proxy_new_cb (GDBusConnection *connection,
|
|
GAsyncResult *res,
|
|
struct CallsModemProxyNewData *data)
|
|
{
|
|
GDBOModem *modem;
|
|
GError *error = NULL;
|
|
const gchar *path;
|
|
|
|
modem = gdbo_modem_proxy_new_finish (res, &error);
|
|
if (!modem)
|
|
{
|
|
g_variant_unref (data->ifaces);
|
|
g_free (data->name);
|
|
g_free (data);
|
|
g_error ("Error creating oFono Modem proxy: %s",
|
|
error->message);
|
|
return;
|
|
}
|
|
|
|
g_signal_connect (modem, "property-changed",
|
|
G_CALLBACK (modem_property_changed_cb),
|
|
data->self);
|
|
|
|
|
|
/* We want to store the oFono modem's Name property so we can pass it
|
|
to our Origin when we create it */
|
|
g_object_set_data_full (G_OBJECT (modem), "calls-modem-name",
|
|
data->name, g_free);
|
|
|
|
path = g_dbus_proxy_get_object_path (G_DBUS_PROXY (modem));
|
|
|
|
g_hash_table_insert (data->self->modems, g_strdup(path), modem);
|
|
|
|
|
|
if (data->ifaces)
|
|
{
|
|
modem_check_ifaces (data->self, modem,
|
|
data->name, data->ifaces);
|
|
g_variant_unref (data->ifaces);
|
|
}
|
|
|
|
g_free (data);
|
|
|
|
g_debug ("Modem `%s' added", path);
|
|
}
|
|
|
|
|
|
static gchar *
|
|
modem_properties_get_name (GVariant *properties)
|
|
{
|
|
gchar *name = NULL;
|
|
gboolean ok;
|
|
|
|
#define try(prop) \
|
|
ok = g_variant_lookup (properties, prop, "s", &name); \
|
|
if (ok) { \
|
|
return name; \
|
|
}
|
|
|
|
try ("Name");
|
|
try ("Model");
|
|
try ("Manufacturer");
|
|
try ("Serial");
|
|
try ("SystemPath");
|
|
|
|
#undef try
|
|
|
|
return NULL;
|
|
}
|
|
|
|
|
|
static void
|
|
modem_added_cb (GDBOManager *manager,
|
|
const gchar *path,
|
|
GVariant *properties,
|
|
CallsOfonoProvider *self)
|
|
{
|
|
struct CallsModemProxyNewData *data;
|
|
|
|
g_debug ("Adding modem `%s'", path);
|
|
|
|
if (g_hash_table_lookup (self->modems, path))
|
|
{
|
|
g_warning ("Modem `%s' already exists", path);
|
|
return;
|
|
}
|
|
|
|
data = g_new0 (struct CallsModemProxyNewData, 1);
|
|
data->self = self;
|
|
data->name = modem_properties_get_name (properties);
|
|
|
|
data->ifaces = g_variant_lookup_value
|
|
(properties, "Interfaces", G_VARIANT_TYPE_ARRAY);
|
|
if (data->ifaces)
|
|
{
|
|
g_variant_ref (data->ifaces);
|
|
}
|
|
|
|
gdbo_modem_proxy_new
|
|
(self->connection,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
g_dbus_proxy_get_name (G_DBUS_PROXY (manager)),
|
|
path,
|
|
NULL,
|
|
(GAsyncReadyCallback) modem_proxy_new_cb,
|
|
data);
|
|
|
|
g_debug ("Modem `%s' addition in progress", path);
|
|
}
|
|
|
|
|
|
static void
|
|
modem_removed_cb (GDBOManager *manager,
|
|
const gchar *path,
|
|
CallsOfonoProvider *self)
|
|
{
|
|
guint index;
|
|
|
|
g_debug ("Removing modem `%s'", path);
|
|
|
|
if (ofono_find_origin_index (self, path, &index))
|
|
g_list_store_remove (self->origins, index);
|
|
|
|
g_hash_table_remove (self->modems, path);
|
|
|
|
g_debug ("Modem `%s' removed", path);
|
|
}
|
|
|
|
|
|
static void
|
|
get_modems_cb (GDBOManager *manager,
|
|
GAsyncResult *res,
|
|
CallsOfonoProvider *self)
|
|
{
|
|
gboolean ok;
|
|
GVariant *modems;
|
|
GVariantIter *modems_iter = NULL;
|
|
GError *error = NULL;
|
|
const gchar *path;
|
|
GVariant *properties;
|
|
|
|
ok = gdbo_manager_call_get_modems_finish (manager, &modems,
|
|
res, &error);
|
|
if (!ok)
|
|
{
|
|
g_warning ("Error getting modems from oFono Manager: %s",
|
|
error->message);
|
|
CALLS_ERROR (self, error);
|
|
return;
|
|
}
|
|
|
|
{
|
|
char *text = g_variant_print (modems, TRUE);
|
|
g_debug ("Received modems from oFono Manager: %s", text);
|
|
g_free (text);
|
|
}
|
|
|
|
g_variant_get (modems, "a(oa{sv})", &modems_iter);
|
|
while (g_variant_iter_loop (modems_iter, "(&o@a{sv})",
|
|
&path, &properties))
|
|
{
|
|
g_debug ("Got modem object path `%s'", path);
|
|
modem_added_cb (manager, path, properties, self);
|
|
}
|
|
g_variant_iter_free (modems_iter);
|
|
|
|
g_variant_unref (modems);
|
|
}
|
|
|
|
static const char *
|
|
calls_ofono_provider_get_name (CallsProvider *provider)
|
|
{
|
|
return "Ofono";
|
|
}
|
|
|
|
static const char *
|
|
calls_ofono_provider_get_status (CallsProvider *provider)
|
|
{
|
|
return "";
|
|
}
|
|
|
|
static GListModel *
|
|
calls_ofono_provider_get_origins (CallsProvider *provider)
|
|
{
|
|
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (provider);
|
|
|
|
return G_LIST_MODEL (self->origins);
|
|
}
|
|
|
|
|
|
static void
|
|
constructed (GObject *object)
|
|
{
|
|
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (object);
|
|
GError *error = NULL;
|
|
|
|
self->connection = g_bus_get_sync (G_BUS_TYPE_SYSTEM, NULL, &error);
|
|
if (!self->connection)
|
|
{
|
|
g_error ("Error creating D-Bus connection: %s",
|
|
error->message);
|
|
}
|
|
|
|
self->manager = gdbo_manager_proxy_new_sync
|
|
(self->connection,
|
|
G_DBUS_PROXY_FLAGS_NONE,
|
|
"org.ofono",
|
|
"/",
|
|
NULL,
|
|
&error);
|
|
if (!self->manager)
|
|
{
|
|
g_error ("Error creating ModemManager object manager proxy: %s",
|
|
error->message);
|
|
}
|
|
|
|
g_signal_connect (self->manager, "modem-added",
|
|
G_CALLBACK (modem_added_cb), self);
|
|
g_signal_connect (self->manager, "modem-removed",
|
|
G_CALLBACK (modem_removed_cb), self);
|
|
|
|
gdbo_manager_call_get_modems
|
|
(self->manager,
|
|
NULL,
|
|
(GAsyncReadyCallback) get_modems_cb,
|
|
self);
|
|
|
|
G_OBJECT_CLASS (calls_ofono_provider_parent_class)->constructed (object);
|
|
}
|
|
|
|
|
|
static void
|
|
dispose (GObject *object)
|
|
{
|
|
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (object);
|
|
|
|
g_clear_object (&self->manager);
|
|
g_clear_object (&self->connection);
|
|
|
|
G_OBJECT_CLASS (calls_ofono_provider_parent_class)->dispose (object);
|
|
}
|
|
|
|
|
|
static void
|
|
finalize (GObject *object)
|
|
{
|
|
CallsOfonoProvider *self = CALLS_OFONO_PROVIDER (object);
|
|
|
|
g_object_unref (self->origins);
|
|
g_hash_table_unref (self->modems);
|
|
|
|
G_OBJECT_CLASS (calls_ofono_provider_parent_class)->finalize (object);
|
|
}
|
|
|
|
|
|
static void
|
|
calls_ofono_provider_class_init (CallsOfonoProviderClass *klass)
|
|
{
|
|
GObjectClass *object_class = G_OBJECT_CLASS (klass);
|
|
CallsProviderClass *provider_class = CALLS_PROVIDER_CLASS (klass);
|
|
|
|
object_class->constructed = constructed;
|
|
object_class->dispose = dispose;
|
|
object_class->finalize = finalize;
|
|
|
|
provider_class->get_name = calls_ofono_provider_get_name;
|
|
provider_class->get_status = calls_ofono_provider_get_status;
|
|
provider_class->get_origins = calls_ofono_provider_get_origins;
|
|
}
|
|
|
|
|
|
static void
|
|
calls_ofono_provider_class_finalize (CallsOfonoProviderClass *klass)
|
|
{
|
|
}
|
|
|
|
|
|
static void
|
|
calls_ofono_provider_message_source_interface_init (CallsMessageSourceInterface *iface)
|
|
{
|
|
}
|
|
|
|
|
|
static void
|
|
calls_ofono_provider_init (CallsOfonoProvider *self)
|
|
{
|
|
self->modems = g_hash_table_new_full (g_str_hash, g_str_equal,
|
|
g_free, g_object_unref);
|
|
self->origins = g_list_store_new (CALLS_TYPE_OFONO_ORIGIN);
|
|
}
|
|
|
|
|
|
G_MODULE_EXPORT void
|
|
peas_register_types (PeasObjectModule *module)
|
|
{
|
|
calls_ofono_provider_register_type (G_TYPE_MODULE (module));
|
|
|
|
peas_object_module_register_extension_type (module,
|
|
CALLS_TYPE_PROVIDER,
|
|
CALLS_TYPE_OFONO_PROVIDER);
|
|
}
|