/* * modem/oface.c - Abstract oFono D-Bus interface class * * Handle GetProperties, SetProperty and PropertyChanged signal * * Copyright (C) 2010 Nokia Corporation * @author Pekka Pessi * * This work is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This work 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this work; if not, write to the Free Software * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ #include "config.h" #define MODEM_DEBUG_FLAG MODEM_LOG_MODEM #include "debug.h" #include "modem/oface.h" #include "modem/request-private.h" #include "modem/errors.h" #include "modem/ofono.h" #include #include "signals-marshal.h" #include #include #include #include #include #include #include /* ------------------------------------------------------------------------ */ G_DEFINE_TYPE (ModemOface, modem_oface, G_TYPE_OBJECT); /* Properties */ enum { PROP_NONE, PROP_INTERFACE, PROP_OBJECT_PATH, LAST_PROPERTY }; /* Signals */ enum { SIGNAL_CONNECTED, N_SIGNALS }; static guint signals[N_SIGNALS]; /* private data */ struct _ModemOfacePrivate { DBusGProxy *proxy; struct { GQueue queue[1]; GError *error; } connecting; unsigned dispose_has_run:1, connected:1, signals:1, disconnected:1, :0; }; /* ------------------------------------------------------------------------ */ /* Local functions */ static void modem_oface_set_object_path (ModemOface *self, char const *path); static void on_property_changed (DBusGProxy *, char const *, GValue const *, gpointer); /* ------------------------------------------------------------------------ */ static void modem_oface_init (ModemOface *self) { DEBUG ("enter"); self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self, MODEM_TYPE_OFACE, ModemOfacePrivate); g_queue_init (self->priv->connecting.queue); } static void modem_oface_get_property (GObject *object, guint property_id, GValue *value, GParamSpec *pspec) { ModemOface *self = MODEM_OFACE (object); ModemOfacePrivate *priv = self->priv; switch (property_id) { case PROP_INTERFACE: g_value_set_string (value, dbus_g_proxy_get_interface (priv->proxy)); break; case PROP_OBJECT_PATH: g_value_set_string (value, dbus_g_proxy_get_path (priv->proxy)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_oface_set_property (GObject *object, guint property_id, const GValue *value, GParamSpec *pspec) { ModemOface *self = MODEM_OFACE (object); switch (property_id) { case PROP_OBJECT_PATH: modem_oface_set_object_path (self, g_value_get_string (value)); break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); break; } } static void modem_oface_constructed (GObject *object) { if (G_OBJECT_CLASS (modem_oface_parent_class)->constructed) G_OBJECT_CLASS (modem_oface_parent_class)->constructed (object); if (MODEM_OFACE (object)->priv->proxy == NULL) g_warning("object created without dbus-proxy"); } static void modem_oface_dispose (GObject *object) { ModemOface *self = MODEM_OFACE (object); ModemOfacePrivate *priv = self->priv; DEBUG ("enter"); if (priv->dispose_has_run) return; priv->dispose_has_run = TRUE; if (!priv->disconnected) modem_oface_disconnect (self); g_clear_error (&priv->connecting.error); g_object_run_dispose (G_OBJECT (priv->proxy)); if (G_OBJECT_CLASS (modem_oface_parent_class)->dispose) G_OBJECT_CLASS (modem_oface_parent_class)->dispose (object); } static void modem_oface_finalize (GObject *object) { ModemOface *self = MODEM_OFACE (object); DEBUG ("enter"); /* Free any data held directly by the object here */ g_object_unref (self->priv->proxy); g_clear_error (&self->priv->connecting.error); G_OBJECT_CLASS (modem_oface_parent_class)->finalize (object); } static void modem_oface_class_init (ModemOfaceClass *klass) { GObjectClass *object_class = G_OBJECT_CLASS (klass); DEBUG ("enter"); object_class->get_property = modem_oface_get_property; object_class->set_property = modem_oface_set_property; object_class->dispose = modem_oface_dispose; object_class->constructed = modem_oface_constructed; object_class->finalize = modem_oface_finalize; g_type_class_add_private (klass, sizeof (ModemOfacePrivate)); /* Properties */ g_object_class_install_property (object_class, PROP_INTERFACE, g_param_spec_string ("interface", "Interface name", "D-Bus interface name", ".", /* default value */ G_PARAM_READABLE | G_PARAM_STATIC_STRINGS)); g_object_class_install_property (object_class, PROP_OBJECT_PATH, g_param_spec_string ("object-path", "Modem object path", "D-Bus object path used to identify the modem", "/", /* default value */ G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS)); /* Signals to emit */ signals[SIGNAL_CONNECTED] = g_signal_new ("connected", G_OBJECT_CLASS_TYPE (klass), G_SIGNAL_RUN_LAST | G_SIGNAL_DETAILED, 0, NULL, NULL, g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); } /* ------------------------------------------------------------------------- */ /* ModemOface factory */ static DBusGConnection * modem_oface_get_bus (void) { static DBusGConnection *bus = NULL; if (G_UNLIKELY (bus == NULL)) bus = dbus_g_bus_get (DBUS_BUS_SYSTEM, NULL); return bus; } static void modem_oface_set_object_path (ModemOface *self, char const *object_path) { DBusGConnection *bus = modem_oface_get_bus (); char const *interface = MODEM_OFACE_GET_CLASS (self)->ofono_interface; self->priv->proxy = dbus_g_proxy_new_for_name (bus, OFONO_BUS_NAME, object_path, interface); } static GHashTable *modem_oface_types; char const * modem_oface_get_interface_name_by_type (GType type) { ModemOfaceClass *klass; char const *interface = NULL; g_return_val_if_fail (G_TYPE_IS_OBJECT (type), NULL); klass = g_type_class_peek_static (type); if (klass) { g_return_val_if_fail (MODEM_IS_OFACE_CLASS (klass), NULL); interface = klass->ofono_interface; } else { klass = g_type_class_ref (type); if (MODEM_IS_OFACE_CLASS (klass)) interface = klass->ofono_interface; else (void) MODEM_OFACE_CLASS (klass); g_type_class_unref (klass); } return interface; } GType modem_oface_get_type_by_interface_name (char const *interface) { gpointer type = g_hash_table_lookup (modem_oface_types, interface); if (type != NULL) return (GType) type; return G_TYPE_INVALID; } void modem_oface_register_type (GType type) { static gsize once = 0; gpointer interface; if (g_once_init_enter (&once)) { modem_oface_types = g_hash_table_new_full (g_str_hash, g_str_equal, NULL, NULL); g_once_init_leave (&once, 1); } interface = (gpointer) modem_oface_get_interface_name_by_type (type); g_return_if_fail (interface); g_hash_table_insert (modem_oface_types, interface, (gpointer)type); } ModemOface * modem_oface_new (char const *interface, char const *object_path) { GType type = modem_oface_get_type_by_interface_name (interface); if (type != G_TYPE_INVALID) { return g_object_new (type, "object-path", object_path, NULL); } else { return NULL; } } /* ------------------------------------------------------------------------- */ /* Connecting API */ static gboolean modem_oface_set_connected (ModemOface *, gboolean connected); gboolean modem_oface_connect (ModemOface *self) { g_return_val_if_fail (MODEM_IS_OFACE (self), FALSE); g_return_val_if_fail (self->priv->connected == FALSE, FALSE); g_return_val_if_fail (self->priv->disconnected == FALSE, FALSE); g_return_val_if_fail (self->priv->dispose_has_run == FALSE, FALSE); ModemOfacePrivate *priv = self->priv; void (*implement_connect) (ModemOface *); DEBUG ("enter"); g_clear_error (&priv->connecting.error); implement_connect = MODEM_OFACE_GET_CLASS (self)->connect; if (implement_connect) { implement_connect (self); } if (g_queue_is_empty (priv->connecting.queue)) { modem_oface_set_connected (self, priv->connecting.error == NULL); } DEBUG ("leave%s", priv->connected ? " already connected" : ! g_queue_is_empty (priv->connecting.queue) ? " while connecting" : ""); return priv->connected || ! g_queue_is_empty (priv->connecting.queue); } static gboolean modem_oface_set_connected (ModemOface *self, gboolean connected) { g_return_val_if_fail (MODEM_IS_OFACE (self), FALSE); g_return_val_if_fail (self->priv->disconnected == FALSE, FALSE); g_return_val_if_fail (self->priv->dispose_has_run == FALSE, FALSE); ModemOfacePrivate *priv = self->priv; if (priv->connected || priv->disconnected || priv->dispose_has_run) return FALSE; priv->connected = connected != FALSE; if (connected && MODEM_OFACE_GET_CLASS (self)->connected) { MODEM_OFACE_GET_CLASS (self)->connected (self); } g_signal_emit (self, signals[SIGNAL_CONNECTED], 0, connected != FALSE); return TRUE; } void modem_oface_add_connect_request (ModemOface *self, ModemRequest *request) { DEBUG("enter"); g_queue_push_tail (self->priv->connecting.queue, request); } void modem_oface_check_connected (ModemOface *self, ModemRequest *request, GError const *error) { ModemOfacePrivate *priv = self->priv; if (!g_queue_find (priv->connecting.queue, request)) return; g_queue_remove (priv->connecting.queue, request); if (error) modem_oface_set_connecting_error (self, error); if (g_queue_is_empty (priv->connecting.queue)) { modem_oface_set_connected (self, priv->connecting.error == NULL); } } void modem_oface_set_connecting_error (ModemOface *self, GError const *error) { ModemOfacePrivate *priv = self->priv; if (!priv->connecting.error || priv->connecting.error->domain == DBUS_GERROR) { g_clear_error (&priv->connecting.error); g_set_error (&priv->connecting.error, error->domain, error->code, "%s", error->message); } } /** Disconnect from call service */ void modem_oface_disconnect (ModemOface *self) { DEBUG ("(%p): enter%s", self, self->priv->disconnected ? " (already done)" : ""); ModemOfacePrivate *priv = self->priv; void (*implement_disconnect)(ModemOface *); unsigned was_connected = priv->connected; if (priv->disconnected) return; priv->connected = FALSE; priv->disconnected = TRUE; implement_disconnect = MODEM_OFACE_GET_CLASS (self)->disconnect; if (implement_disconnect) { implement_disconnect (self); } while (!g_queue_is_empty (priv->connecting.queue)) { modem_request_cancel (g_queue_pop_head (priv->connecting.queue)); } if (was_connected) g_signal_emit (self, signals[SIGNAL_CONNECTED], 0, FALSE); } static void reply_to_connect_properties (ModemOface *self, ModemRequest *request, GHashTable *properties, GError const *error, gpointer dummy) { if (properties) { modem_oface_update_properties (self, properties); } /* XXX/PP: GetProperties might return error randomly */ /* XXX/PP: we should retry on error */ modem_oface_check_connected (self, request, error); } void modem_oface_connect_properties (ModemOface *self, gboolean get_all) { ModemOfacePrivate *priv = self->priv; if (!priv->signals) { priv->signals = TRUE; dbus_g_proxy_add_signal (priv->proxy, "PropertyChanged", G_TYPE_STRING, G_TYPE_VALUE, G_TYPE_INVALID); dbus_g_proxy_connect_signal (priv->proxy, "PropertyChanged", G_CALLBACK (on_property_changed), self, NULL); } if (!get_all) return; modem_oface_add_connect_request (self, modem_oface_request_properties (self, reply_to_connect_properties, NULL)); } void modem_oface_disconnect_properties (ModemOface *self) { ModemOfacePrivate *priv = self->priv; if (priv->signals) { priv->signals = FALSE; dbus_g_proxy_disconnect_signal (priv->proxy, "PropertyChanged", G_CALLBACK (on_property_changed), self); } } gboolean modem_oface_is_connected (ModemOface const *self) { return MODEM_IS_OFACE (self) && self->priv->connected; } gboolean modem_oface_is_connecting (ModemOface const *self) { return MODEM_IS_OFACE (self) && !g_queue_is_empty (self->priv->connecting.queue); } static void reply_to_get_properties (DBusGProxy *proxy, DBusGProxyCall *call, gpointer _request) { GHashTable *properties = NULL; ModemRequest *request = _request; gpointer object = modem_request_object (request); ModemOfacePropertiesReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call, &error, MODEM_TYPE_DBUS_DICT, &properties, G_TYPE_INVALID)) { modem_error_fix (&error); } if (callback) callback (object, request, properties, error, user_data); if (error) g_error_free (error); if (properties) g_hash_table_unref (properties); } ModemRequest * modem_oface_request_properties (ModemOface *self, ModemOfacePropertiesReply *callback, gpointer user_data) { DEBUG("enter"); g_return_val_if_fail (MODEM_IS_OFACE(self), NULL); return modem_request_begin (self, self->priv->proxy, "GetProperties", reply_to_get_properties, G_CALLBACK (callback), user_data, G_TYPE_INVALID); } static void on_property_changed (DBusGProxy *proxy, char const *property, GValue const *value, gpointer _self) { ModemOface *self = MODEM_OFACE (_self); char const *(*property_mapper)(char const *ofono_property); char const *gname; property_mapper = MODEM_OFACE_GET_CLASS (self)->property_mapper; if (!property_mapper) return; gname = property_mapper (property); if (DEBUGGING) { char *s = g_strdup_value_contents (value); DEBUG("%s = %s (as %s)", property, s, gname); g_free(s); } if (!gname) return; g_object_set_property (G_OBJECT (self), gname, value); } void modem_oface_update_properties (ModemOface *self, GHashTable *properties) { GHashTableIter iter[1]; char *name; GValue *value; DEBUG ("enter"); for (g_hash_table_iter_init (iter, properties); g_hash_table_iter_next (iter, (gpointer)&name, (gpointer)&value);) { on_property_changed (NULL, name, value, self); } } static void reply_to_set_property (DBusGProxy *proxy, DBusGProxyCall *call, void *_request) { ModemRequest *request = _request; ModemOface *self = modem_request_object (request); ModemOfaceVoidReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; dbus_g_proxy_end_call (proxy, call, &error, G_TYPE_INVALID); if (callback) callback (self, request, error, user_data); g_clear_error (&error); } ModemRequest * modem_oface_set_property_req (ModemOface *self, char const *property, GValue *value, ModemOfaceVoidReply *callback, gpointer user_data) { ModemOfacePrivate *priv = self->priv; if (DEBUGGING) { char *s = g_strdup_value_contents (value); DEBUG ("%s.%s (%s, %s)", dbus_g_proxy_get_interface (priv->proxy), "SetProperty", property, s); g_free (s); } return modem_request_begin (self, priv->proxy, "SetProperty", reply_to_set_property, G_CALLBACK (callback), user_data, G_TYPE_STRING, property, G_TYPE_VALUE, value, G_TYPE_INVALID); } /* ------------------------------------------------------------------------- */ /* Managed */ static void reply_to_get_managed (DBusGProxy *proxy, DBusGProxyCall *call, gpointer _request) { GPtrArray *array = NULL; ModemRequest *request = _request; gpointer object = modem_request_object (request); ModemOfaceManagedReply *callback = modem_request_callback (request); gpointer user_data = modem_request_user_data (request); GError *error = NULL; if (!dbus_g_proxy_end_call (proxy, call, &error, MODEM_TYPE_DBUS_MANAGED_ARRAY, &array, G_TYPE_INVALID)) modem_error_fix (&error); if (callback) callback (object, request, array, error, user_data); if (error) g_error_free (error); if (array) g_ptr_array_free (array, TRUE); } ModemRequest * modem_oface_request_managed (ModemOface *oface, char const *method, ModemOfaceManagedReply *callback, gpointer userdata) { ModemOfacePrivate *priv = oface->priv; return modem_request_begin (oface, priv->proxy, method, reply_to_get_managed, G_CALLBACK (callback), userdata, G_TYPE_INVALID); } /** Returns pointer to DBusGProxy. * * If you want to have a new reference, use g_object_get_property (). */ DBusGProxy * modem_oface_dbus_proxy (ModemOface *self) { return MODEM_OFACE (self)->priv->proxy; } /** Returns object path for the oface object. * * If you want to have a newly copied string, use g_object_get_property (). */ char const * modem_oface_object_path (ModemOface *self) { return dbus_g_proxy_get_path (MODEM_OFACE (self)->priv->proxy); } char const * modem_oface_interface (ModemOface *self) { return dbus_g_proxy_get_interface (MODEM_OFACE (self)->priv->proxy); }