diff options
author | Pekka Pessi <Pekka.Pessi@nokia.com> | 2011-01-27 16:40:38 +0200 |
---|---|---|
committer | Pekka Pessi <Pekka.Pessi@nokia.com> | 2011-02-01 19:13:17 +0200 |
commit | 75a0a159b88125d2ce3f46da80b2acb3497c77be (patch) | |
tree | 53e5c074c2192c05fe979ae2bed2ea2f7ec5cc31 | |
parent | 5f4bc6b9e3fa75a43552fe3fcd382db64118266f (diff) |
Add ring-conference-manager
-rw-r--r-- | src/Makefile.am | 1 | ||||
-rw-r--r-- | src/ring-conference-channel.c | 87 | ||||
-rw-r--r-- | src/ring-conference-channel.h | 13 | ||||
-rw-r--r-- | src/ring-conference-manager.c | 741 | ||||
-rw-r--r-- | src/ring-conference-manager.h | 79 | ||||
-rw-r--r-- | src/ring-connection.c | 46 |
6 files changed, 891 insertions, 76 deletions
diff --git a/src/Makefile.am b/src/Makefile.am index 68df9c1..7a65dae 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -52,6 +52,7 @@ libtpring_la_SOURCES = \ ring-media-channel.h ring-media-channel.c \ ring-call-channel.h ring-call-channel.c \ ring-member-channel.h ring-member-channel.c \ + ring-conference-manager.h ring-conference-manager.c \ ring-conference-channel.h ring-conference-channel.c \ ring-param-spec.h ring-param-spec.c \ ring-emergency-service.h ring-emergency-service.c \ diff --git a/src/ring-conference-channel.c b/src/ring-conference-channel.c index 5989653..ca22fba 100644 --- a/src/ring-conference-channel.c +++ b/src/ring-conference-channel.c @@ -1765,29 +1765,34 @@ static void reply_to_modem_call_request_conference(ModemCallService *_service, ModemRequest *request, GError *error, gpointer _channel); static gboolean -ring_conference_channel_create_streams(RingConferenceChannel *_self, - guint handle, - gboolean audio, - gboolean video, - GError **error) +ring_conference_channel_create_streams (RingConferenceChannel *_self, + guint handle, + gboolean audio, + gboolean video, + GError **error) { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; ModemCallService *service; + TpBaseConnection *baseconn; (void)audio; (void)video; - if (priv->streams_created) { - DEBUG("Already associated with a conference"); - return TRUE; - } + if (priv->streams_created) + { + DEBUG("Already associated with a conference"); + return TRUE; + } + priv->streams_created = 1; - if (!ring_connection_validate_initial_members( - RING_CONNECTION(tp_base_channel_get_connection(TP_BASE_CHANNEL(self))), - priv->initial_members, - error)) - return FALSE; + baseconn = tp_base_channel_get_connection (TP_BASE_CHANNEL (self)); + + if (!ring_connection_validate_initial_members (RING_CONNECTION (baseconn), + priv->initial_members, error)) + { + return FALSE; + } service = ring_conference_channel_get_call_service (self); @@ -1799,32 +1804,23 @@ ring_conference_channel_create_streams(RingConferenceChannel *_self, } void -ring_conference_channel_initial_audio(RingConferenceChannel *self, - RingMediaManager *manager, - gpointer channelrequest) +ring_conference_channel_initial_audio (RingConferenceChannel *self) { RingConferenceChannelPrivate *priv = self->priv; ModemCallService *service; ModemRequest *request; - DEBUG("%s(%p, %p, %p) called", __func__, self, manager, channelrequest); + DEBUG ("(%p)", self); priv->streams_created = 1; service = ring_conference_channel_get_call_service (self), request = modem_call_request_conference (service, - reply_to_modem_call_request_conference, - self); + reply_to_modem_call_request_conference, + self); ring_conference_channel_queue_request (self, request); - - modem_request_add_qdatas( - request, - g_type_qname(RING_TYPE_MEDIA_MANAGER), g_object_ref(manager), g_object_unref, - /* XXX - GDestroyNotify for channelrequest */ - g_quark_from_static_string("RingChannelRequest"), channelrequest, NULL, - NULL); } static void @@ -1835,41 +1831,24 @@ reply_to_modem_call_request_conference(ModemCallService *_service, { RingConferenceChannel *self = RING_CONFERENCE_CHANNEL(_self); RingConferenceChannelPrivate *priv = self->priv; - gpointer channelrequest; - GError error0[1] = {{ - TP_ERRORS, TP_ERROR_NOT_AVAILABLE, "Conference channel already exists" - }}; ring_conference_channel_dequeue_request (self, request); g_assert(priv->conf_created == FALSE); + if (error) { - DEBUG("Call.CreateMultiparty with channel %s (%p) failed: " GERROR_MSG_FMT, - self->nick, self, GERROR_MSG_CODE(error)); - } - else - { - priv->conf_created = TRUE; - DEBUG("Call.CreateMultiparty with channel %s (%p) returned", - self->nick, self); - } + DEBUG("Call.CreateMultiparty with channel %s (%p) failed: " + GERROR_MSG_FMT, self->nick, self, GERROR_MSG_CODE(error)); + ring_conference_channel_release(_self, 0, 0, error); + ring_conference_channel_close_impl(_self, FALSE, FALSE); + return; + } - channelrequest = modem_request_steal_data(request, "RingChannelRequest"); - if (channelrequest) { - RingMediaManager *manager = modem_request_get_data(request, "RingMediaManager"); - - ring_media_manager_emit_new_channel(manager, - channelrequest, self, error ? error0 : NULL); - } - else if (error) { - ring_conference_channel_release(_self, 0, 0, error); - ring_conference_channel_close_impl(_self, FALSE, FALSE); - } - else { - ring_conference_channel_emit_initial(_self); - } + priv->conf_created = TRUE; + DEBUG("VoiceCallManager.CreateMultiparty with channel %s (%p) returned", + self->nick, self); } /* ---------------------------------------------------------------------- */ diff --git a/src/ring-conference-channel.h b/src/ring-conference-channel.h index 56897ee..dab4c17 100644 --- a/src/ring-conference-channel.h +++ b/src/ring-conference-channel.h @@ -88,16 +88,11 @@ void ring_conference_channel_emit_channel_removed( void ring_conference_channel_emit_initial(RingConferenceChannel *channel); -/* ---------------------------------------------------------------------- */ -/* "initial-members" */ +gboolean ring_conference_channel_check_initial_members ( + RingConferenceChannel const *, + RingInitialMembers const *); -gboolean ring_conference_channel_check_initial_members( - RingConferenceChannel const *channel, - RingInitialMembers const *initial); - -void ring_conference_channel_initial_audio(RingConferenceChannel *self, - RingMediaManager *manager, - gpointer request); +void ring_conference_channel_initial_audio (RingConferenceChannel *self); G_END_DECLS diff --git a/src/ring-conference-manager.c b/src/ring-conference-manager.c new file mode 100644 index 0000000..b4257e0 --- /dev/null +++ b/src/ring-conference-manager.c @@ -0,0 +1,741 @@ +/* + * ring-conference-manager.c - Manager for conference channels + * + * Copyright (C) 2007-2011 Nokia Corporation + * @author Pekka Pessi <first.surname@nokia.com> + * @author Lassi Syrjala <first.surname@nokia.com> + * @author Kai Vehmanen <first.surname@nokia.com> + * + * 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 + */ + +/* + * Based on telepathy-glib/examples/cm/echo/factory.c with notice: + * + * """ + * Copyright (C) 2007 Collabora Ltd. <http://www.collabora.co.uk/> + * Copyright (C) 2007 Nokia Corporation + * + * Copying and distribution of this file, with or without modification, + * are permitted in any medium without royalty provided the copyright + * notice and this notice are preserved. + * """ + */ + +#include "config.h" + +#define DEBUG_FLAG RING_DEBUG_CONNECTION +#include "ring-debug.h" + +#include "ring-conference-manager.h" +#include "ring-conference-channel.h" +#include "ring-connection.h" +#include "ring-param-spec.h" +#include "ring-util.h" + +#include "sms-glib/deliver.h" + +#include "ring-extensions/ring-extensions.h" + +#include "modem/call.h" +#include "modem/tones.h" + +#include <telepathy-glib/base-connection.h> +#include <telepathy-glib/dbus.h> +#include <telepathy-glib/errors.h> +#include <telepathy-glib/gtypes.h> +#include <telepathy-glib/interfaces.h> + +#include <dbus/dbus-glib.h> + +#include <string.h> + +static void manager_iface_init (gpointer, gpointer); + +G_DEFINE_TYPE_WITH_CODE (RingConferenceManager, ring_conference_manager, + G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER, manager_iface_init)); + +struct _RingConferenceManagerPrivate +{ + RingConnection *connection; + ModemCallService *call_service; + + GHashTable *channels; + + unsigned dispose_has_run:1, :0; + + struct { + gulong status_changed; + } signals; +}; + +enum +{ + PROP_NONE, + PROP_CONNECTION, + PROP_CALL_SERVICE, + N_PROPS +}; + +typedef enum + { + METHOD_COMPATIBLE, + METHOD_CREATE, + METHOD_ENSURE + } RequestotronMethod; + +/* ---------------------------------------------------------------------- */ + +static void conference_manager_emit_new_channel (RingConferenceManager *, + gpointer request, gpointer channel, GError *error); + +static gboolean conference_requestotron (RingConferenceManager *, + gpointer, + GHashTable *); + +static void set_call_service (RingConferenceManager *, ModemCallService *); +static void conference_removed (gpointer _channel); + +static void on_connection_status_changed (TpBaseConnection *conn, + guint status, guint reason, + RingConferenceManager *self); + +#define METHOD(i, x) (i ## _ ## x) + +/* ---------------------------------------------------------------------- */ +/* GObject interface */ + +static void +ring_conference_manager_init (RingConferenceManager *self) +{ + self->priv = G_TYPE_INSTANCE_GET_PRIVATE ( + self, RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerPrivate); + + self->priv->channels = g_hash_table_new_full (g_str_hash, g_str_equal, + NULL, conference_removed); +} + +static void +ring_conference_manager_constructed (GObject *object) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER(object); + RingConferenceManagerPrivate *priv = self->priv; + + priv->signals.status_changed = g_signal_connect (priv->connection, + "status-changed", (GCallback) on_connection_status_changed, self); + + if (G_OBJECT_CLASS (ring_conference_manager_parent_class)->constructed) + G_OBJECT_CLASS (ring_conference_manager_parent_class)->constructed (object); +} + +static void +ring_conference_manager_dispose (GObject *object) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); + RingConferenceManagerPrivate *priv = self->priv; + + if (priv->dispose_has_run) + return; + priv->dispose_has_run = TRUE; + + ring_signal_disconnect (priv->connection, &priv->signals.status_changed); + + set_call_service (self, NULL); + + ((GObjectClass *) ring_conference_manager_parent_class)->dispose (object); +} + +static void +ring_conference_manager_finalize (GObject *object) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); + RingConferenceManagerPrivate *priv = self->priv; + + g_hash_table_destroy (priv->channels); +} + +static void +ring_conference_manager_get_property (GObject *object, + guint property_id, + GValue *value, + GParamSpec *pspec) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); + RingConferenceManagerPrivate *priv = self->priv; + + switch (property_id) + { + case PROP_CONNECTION: + g_value_set_object (value, priv->connection); + break; + case PROP_CALL_SERVICE: + g_value_set_pointer (value, priv->call_service); + break; + + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ring_conference_manager_set_property (GObject *object, + guint property_id, + const GValue *value, + GParamSpec *pspec) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (object); + RingConferenceManagerPrivate *priv = self->priv; + + switch (property_id) + { + case PROP_CONNECTION: + /* We don't ref the connection, because it owns a reference to the + * factory, and it guarantees that the factory's lifetime is + * less than its lifetime */ + priv->connection = g_value_get_object (value); + break; + case PROP_CALL_SERVICE: + set_call_service (self, g_value_get_pointer (value)); + break; + default: + G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec); + } +} + +static void +ring_conference_manager_class_init (RingConferenceManagerClass *klass) +{ + GObjectClass *object_class = (GObjectClass *) klass; + + g_type_class_add_private (klass, sizeof (RingConferenceManagerPrivate)); + + object_class->constructed = ring_conference_manager_constructed; + object_class->dispose = ring_conference_manager_dispose; + object_class->finalize = ring_conference_manager_finalize; + object_class->get_property = ring_conference_manager_get_property; + object_class->set_property = ring_conference_manager_set_property; + + g_object_class_install_property (object_class, PROP_CONNECTION, + ring_param_spec_connection ()); + + g_object_class_install_property (object_class, PROP_CALL_SERVICE, + g_param_spec_pointer ("call-service", + "Call Manager Object", + "oFono Call Manager", + G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); +} + +/* ---------------------------------------------------------------------- */ +/* RingConferenceManager interface */ + +RingConferenceChannel * +ring_conference_manager_lookup (RingConferenceManager *self, + char const *object_path) +{ + g_return_val_if_fail (self != NULL, NULL); + g_return_val_if_fail (object_path != NULL, NULL); + + return g_hash_table_lookup (self->priv->channels, object_path); +} + +static void +set_call_service (RingConferenceManager *self, ModemCallService *service) +{ + RingConferenceManagerPrivate *priv = self->priv; + + if (priv->call_service) + { + if (priv->call_service) + g_object_unref (priv->call_service); + priv->call_service = NULL; + } + + if (service) + { + priv->call_service = g_object_ref (MODEM_CALL_SERVICE (service)); + } +} + +static void +conference_removed (gpointer _channel) +{ + if (!tp_base_channel_is_destroyed (_channel)) + { + /* Ensure "Closed" has been emitted */ + g_object_run_dispose (_channel); + } + + g_object_unref (_channel); +} + +static void +on_connection_status_changed (TpBaseConnection *conn, + guint status, + guint reason, + RingConferenceManager *self) +{ + RingConferenceManagerPrivate *priv = self->priv; + + if (status == TP_CONNECTION_STATUS_DISCONNECTED) + { + g_hash_table_remove_all (priv->channels); + } +} + +/* ---------------------------------------------------------------------- */ +/* TpChannelManagerIface interface */ + +static GHashTable * +conference_channel_fixed_properties (void) +{ + static GHashTable *hash; + + if (hash) + return hash; + + hash = g_hash_table_new (g_str_hash, g_str_equal); + + char const *key; + GValue *value; + + key = TP_IFACE_CHANNEL ".ChannelType"; + value = tp_g_value_slice_new (G_TYPE_STRING); + g_value_set_static_string (value, TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA); + + g_hash_table_insert (hash, (gpointer)key, value); + + return hash; +} + +static char const * const conference_channel_allowed_properties[] = + { + TP_IFACE_CHANNEL ".InitialChannels", + TP_IFACE_CHANNEL ".TargetHandleType", + TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialAudio", + TP_IFACE_CHANNEL_TYPE_STREAMED_MEDIA ".InitialVideo", + NULL + }; + +RingInitialMembers * +tp_asv_get_initial_members (GHashTable *properties) +{ + return tp_asv_get_boxed (properties, + TP_IFACE_CHANNEL ".InitialChannels", + TP_ARRAY_TYPE_OBJECT_PATH_LIST); +} + +static void +conference_manager_foreach_channel_class (TpChannelManager *_self, + TpChannelManagerChannelClassFunc func, + gpointer userdata) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); + + /* If we're not connected, conferences aren't supported. */ + if (self->priv->call_service == NULL) + return; + + func (_self, + conference_channel_fixed_properties (), + conference_channel_allowed_properties, + userdata); +} + +static void +conference_manager_foreach_channel (TpChannelManager *_self, + TpExportableChannelFunc func, + gpointer user_data) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); + GHashTableIter i[1]; + gpointer channel; + + for (g_hash_table_iter_init (i, self->priv->channels); + g_hash_table_iter_next (i, NULL, &channel);) + func (channel, user_data); +} + +static gboolean +conference_manager_create_channel (TpChannelManager *_self, + gpointer request, + GHashTable *properties) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); + + if (!ring_properties_satisfy (properties, + conference_channel_fixed_properties (), + conference_channel_allowed_properties) || + tp_asv_get_initial_members (properties) == NULL) + return FALSE; + + return conference_requestotron (self, request, properties); +} + +static gboolean +conference_manager_ensure_channel (TpChannelManager *_self, + gpointer request, + GHashTable *properties) +{ + RingConferenceManager *self = RING_CONFERENCE_MANAGER (_self); + + if (!ring_properties_satisfy (properties, + conference_channel_fixed_properties (), + conference_channel_allowed_properties) || + tp_asv_get_initial_members (properties) == NULL) + return FALSE; + + return conference_requestotron (self, request, properties); +} + +static void +manager_iface_init (gpointer ifacep, gpointer data) +{ + TpChannelManagerIface *iface = ifacep; + +#define IMPLEMENT(x) iface->x = conference_manager_##x + + IMPLEMENT (foreach_channel); + IMPLEMENT (foreach_channel_class); + IMPLEMENT (create_channel); + IMPLEMENT (ensure_channel); + +#undef IMPLEMENT +} + +/* ---------------------------------------------------------------------- */ + +static RingConferenceChannel * +conference_manager_find_existing (RingConferenceManager const *self, + RingInitialMembers *initial) +{ + RingConferenceManagerPrivate const *priv = self->priv; + GHashTableIter i[1]; + gpointer existing = NULL; + + g_hash_table_iter_init (i, priv->channels); + + while (g_hash_table_iter_next (i, NULL, &existing)) + { + if (initial->len == 0 || + ring_conference_channel_check_initial_members (existing, initial)) + return existing; + } + + return NULL; +} + +static char * +conference_manager_new_object_path (RingConferenceManager const *self) +{ + RingConferenceManagerPrivate const *priv = self->priv; + char const *base_path = TP_BASE_CONNECTION (priv->connection)->object_path; + + static unsigned object_index; + static unsigned object_index_init; + + if (G_UNLIKELY (object_index_init == 0)) + { + object_index = (guint)(time(NULL) - (41 * 365 * 24 * 60 * 60)) * 10; + object_index_init = 1; + } + + /* Find an unique D-Bus object_path */ + for (;;) + { + char *path = g_strdup_printf ("%s/conf%u", base_path, ++object_index); + + if (!g_hash_table_lookup (priv->channels, path)) + return path; + + g_free (path); + } +} + +static RingConferenceChannel * +conference_manager_new_conference (RingConferenceManager *self, + RingInitialMembers *initial, + gboolean initial_audio) +{ + RingConferenceManagerPrivate *priv = self->priv; + char *object_path; + RingConferenceChannel *channel; + + object_path = conference_manager_new_object_path (self); + + channel = g_object_new (RING_TYPE_CONFERENCE_CHANNEL, + "connection", priv->connection, + /* KVXXX: "tones", priv->tones, */ + "object-path", object_path, + "initial-channels", initial, + "initial-audio", initial_audio, + "initiator-handle", tp_base_connection_get_self_handle ( + TP_BASE_CONNECTION (priv->connection)), + "requested", TRUE, + NULL); + + g_free (object_path); + + return channel; +} + +static void +on_conference_channel_closed (GObject *_channel, RingConferenceManager *self) +{ + gchar *object_path; + + g_object_get (_channel, "object-path", &object_path, NULL); + g_hash_table_remove (self->priv->channels, object_path); + tp_channel_manager_emit_channel_closed (self, object_path); + g_free (object_path); +} + +static void +conference_manager_emit_new_channel (RingConferenceManager *self, + gpointer request, + gpointer _channel, + GError *error) +{ + DEBUG ("%s (%p, %p, %p, %p) called", __func__, + self, request, _channel, error); + + RingConferenceManagerPrivate *priv = RING_CONFERENCE_MANAGER (self)->priv; + GSList *requests = request ? g_slist_prepend (NULL, request) : NULL; + + if (error == NULL) + { + RingConferenceChannel *channel = RING_CONFERENCE_CHANNEL (_channel); + char *object_path = NULL; + + g_signal_connect (channel, "closed", + G_CALLBACK (on_conference_channel_closed), self); + + g_object_get (channel, "object-path", &object_path, NULL); + + DEBUG ("got new channel %p nick %s type %s", + channel, channel->nick, G_OBJECT_TYPE_NAME (channel)); + + g_hash_table_insert (priv->channels, object_path, channel); + + tp_channel_manager_emit_new_channel (self, + TP_EXPORTABLE_CHANNEL (channel), requests); + + /* Emit Group and StreamedMedia signals */ + ring_conference_channel_emit_initial (channel); + } + else + { + if (_channel) + { + RingConferenceChannel *channel = RING_CONFERENCE_CHANNEL (_channel); + DEBUG ("new channel %p nick %s failed with " GERROR_MSG_FMT, + channel, channel->nick, GERROR_MSG_CODE (error)); + } + + if (request) + { + tp_channel_manager_emit_request_failed (self, + request, error->domain, error->code, error->message); + } + + if (_channel) + { + g_object_unref (_channel); + } + } + + g_slist_free (requests); +} + +static gboolean +conference_requestotron (RingConferenceManager *self, + gpointer request, + GHashTable *properties) +{ + RingInitialMembers *initial_members; + gboolean initial_audio, initial_video; + RingConferenceChannel *channel; + GError *error = NULL; + + if (self->priv->call_service == NULL) + { + tp_channel_manager_emit_request_failed (self, request, + TP_ERRORS, TP_ERROR_NOT_AVAILABLE, + "Modem does not support conferences"); + return TRUE; + } + + initial_members = tp_asv_get_initial_members (properties); + initial_audio = tp_asv_get_initial_audio (properties, TRUE); + initial_video = tp_asv_get_initial_video (properties, FALSE); + + if (initial_video) + { + tp_channel_manager_emit_request_failed (self, request, + TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED, + "Video calls are not supported"); + return TRUE; + } + + if (tp_asv_get_uint32 (properties, + TP_IFACE_CHANNEL ".TargetHandleType", NULL) != 0) + { + tp_channel_manager_emit_request_failed (self, request, + TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Invalid TargetHandleType"); + return TRUE; + } + + channel = conference_manager_find_existing (self, initial_members); + if (channel) + { + tp_channel_manager_emit_request_already_satisfied (self, request, + TP_EXPORTABLE_CHANNEL (channel)); + return TRUE; + } + + if (!ring_conference_manager_validate_initial_members (self, + initial_members, &error)) + { + tp_channel_manager_emit_request_failed (self, request, + error->domain, error->code, error->message); + g_clear_error (&error); + return TRUE; + } + + channel = conference_manager_new_conference (self, + initial_members, initial_audio); + + if (initial_audio) + ring_conference_channel_initial_audio (channel); + + conference_manager_emit_new_channel (self, request, channel, NULL); + + return TRUE; +} + +gboolean +ring_conference_manager_validate_initial_members (RingConferenceManager *self, + RingInitialMembers *initial, + GError **error) +{ + RingConferenceManagerPrivate *priv = self->priv; + RingMemberChannel *ch1, *ch2; + + if (initial == NULL) { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "No initial members"); + return FALSE; + } + + if (initial->len != 2) { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Expecting exactly two initial members"); + return FALSE; + } + + ch1 = ring_connection_lookup_channel (priv->connection, initial->odata[0]); + ch2 = ring_connection_lookup_channel (priv->connection, initial->odata[1]); + + if (ch1 == ch2) { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Expecting distinct initial members"); + return FALSE; + } + + if (!RING_IS_MEMBER_CHANNEL (ch1) || !RING_IS_MEMBER_CHANNEL (ch2)) { + g_set_error (error, TP_ERRORS, TP_ERROR_INVALID_ARGUMENT, + "Initial member is not a MediaStream channel"); + return FALSE; + } + + if (!ring_member_channel_can_become_member (ch1, error)) { + g_prefix_error (error, "First initial: "); + return FALSE; + } + + if (!ring_member_channel_can_become_member (ch2, error)) { + g_prefix_error (error, "Second initial: "); + return FALSE; + } + + return TRUE; +} + +#ifdef nomore +static void +on_modem_call_conference_joined (ModemCallConference *mcc, + ModemCall *mc, + RingConferenceManager *self) +{ + RingConferenceManagerPrivate *priv = RING_CONFERENCE_MANAGER (self)->priv; + ModemCall **members; + GPtrArray *initial; + guint i; + + if (modem_call_get_handler (MODEM_CALL (mcc))) + return; + + members = modem_call_service_get_calls (priv->call_service); + initial = g_ptr_array_sized_new (MODEM_MAX_CALLS + 1); + + for (i = 0; members[i]; i++) + { + if (modem_call_is_member (members[i]) && + modem_call_get_handler (members[i])) + { + RingMemberChannel *member; + char *object_path = NULL; + + member = RING_MEMBER_CHANNEL (modem_call_get_handler (members[i])); + g_object_get (member, "object-path", &object_path, NULL); + + if (object_path) + g_ptr_array_add (initial, object_path); + } + } + + if (initial->len >= 2) + { + RingConferenceChannel *channel; + char *object_path; + + object_path = ring_conference_manager_new_object_path (self); + + channel = (RingConferenceChannel *) + g_object_new (RING_TYPE_CONFERENCE_CHANNEL, + "connection", priv->connection, + "call-instance", mcc, + /* KVXXX: "tones", priv->tones, */ + "object-path", object_path, + "initial-channels", initial, + "initial-audio", TRUE, + "initiator-handle", tp_base_connection_get_self_handle ( + TP_BASE_CONNECTION (priv->connection)), + "requested", TRUE, + NULL); + + g_free (object_path); + + g_assert (channel == modem_call_get_handler (MODEM_CALL (mcc))); + + conference_manager_emit_new_channel (self, NULL, channel, NULL); + } + + g_ptr_array_add (initial, NULL); + g_strfreev ((char **)g_ptr_array_free (initial, FALSE)); + g_free (members); +} +#endif diff --git a/src/ring-conference-manager.h b/src/ring-conference-manager.h new file mode 100644 index 0000000..7eca039 --- /dev/null +++ b/src/ring-conference-manager.h @@ -0,0 +1,79 @@ +/* + * ring-conference-manager.h - Manager for conference channels + * + * Copyright (C) 2007-2010 Nokia Corporation + * @author Pekka Pessi <first.surname@nokia.com> + * + * 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 + */ + +#ifndef RING_CONFERENCE_MANAGER_H +#define RING_CONFERENCE_MANAGER_H + +#include <telepathy-glib/channel-manager.h> +#include <ring-util.h> +#include "modem/call.h" + +G_BEGIN_DECLS + +typedef struct _RingConferenceManager RingConferenceManager; +typedef struct _RingConferenceManagerClass RingConferenceManagerClass; +typedef struct _RingConferenceManagerPrivate RingConferenceManagerPrivate; + +G_END_DECLS + +#include <ring-conference-channel.h> + +G_BEGIN_DECLS + +struct _RingConferenceManagerClass { + GObjectClass parent_class; +}; + +struct _RingConferenceManager { + GObject parent; + RingConferenceManagerPrivate *priv; +}; + +GType ring_conference_manager_get_type (void); + +/* TYPE MACROS */ +#define RING_TYPE_CONFERENCE_MANAGER \ + (ring_conference_manager_get_type()) +#define RING_CONFERENCE_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), \ + RING_TYPE_CONFERENCE_MANAGER, RingConferenceManager)) +#define RING_CONFERENCE_MANAGER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_CAST ((cls), \ + RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerClass)) +#define RING_IS_CONFERENCE_MANAGER(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), \ + RING_TYPE_CONFERENCE_MANAGER)) +#define RING_IS_CONFERENCE_MANAGER_CLASS(cls) \ + (G_TYPE_CHECK_CLASS_TYPE ((cls), \ + RING_TYPE_CONFERENCE_MANAGER)) +#define RING_CONFERENCE_MANAGER_GET_CLASS(obj) \ + (G_TYPE_INSTANCE_GET_CLASS ((obj), \ + RING_TYPE_CONFERENCE_MANAGER, RingConferenceManagerClass)) + +RingConferenceChannel *ring_conference_manager_lookup (RingConferenceManager *, + char const *object_path); + +gboolean ring_conference_manager_validate_initial_members ( + RingConferenceManager *, RingInitialMembers *, GError **error); + +G_END_DECLS + +#endif diff --git a/src/ring-connection.c b/src/ring-connection.c index 192070a..886dd9c 100644 --- a/src/ring-connection.c +++ b/src/ring-connection.c @@ -25,7 +25,9 @@ #include "ring-connection.h" #include "ring-emergency-service.h" + #include "ring-media-manager.h" +#include "ring-conference-manager.h" #include "ring-text-manager.h" #include "ring-param-spec.h" @@ -75,6 +77,7 @@ struct _RingConnectionPrivate RingMediaManager *media; RingTextManager *text; + RingConferenceManager *conference; gchar *modem_path; Modem *modem; @@ -371,12 +374,14 @@ ring_connection_get_property(GObject *obj, break; case PROP_STORED_MESSAGES: #if nomore - g_value_take_boxed(value, ring_text_manager_list_stored_messages(priv->text)); + g_value_take_boxed(value, + ring_text_manager_list_stored_messages (priv->text)); #endif break; case PROP_KNOWN_SERVICE_POINTS: - g_value_take_boxed(value, ring_media_manager_emergency_services(priv->media)); + g_value_take_boxed(value, + ring_media_manager_emergency_services (priv->media)); break; case PROP_ANON_MANDATORY: @@ -870,11 +875,17 @@ ring_connection_create_channel_managers(TpBaseConnection *base) priv->media = g_object_new(RING_TYPE_MEDIA_MANAGER, - "connection", self, - "anon-modes", priv->anon_modes, - NULL); + "connection", self, + "anon-modes", priv->anon_modes, + NULL); g_ptr_array_add(channel_managers, priv->media); + priv->conference = + g_object_new (RING_TYPE_CONFERENCE_MANAGER, + "connection", self, + NULL); + g_ptr_array_add(channel_managers, priv->conference); + priv->text = g_object_new(RING_TYPE_TEXT_MANAGER, "connection", self, @@ -932,6 +943,7 @@ ring_connection_modem_interface_added (Modem *modem, else if (MODEM_IS_CALL_SERVICE (oface)) { g_object_set (priv->media, "call-service", oface, NULL); + g_object_set (priv->conference, "call-service", oface, NULL); } else if (MODEM_IS_SMS_SERVICE (oface)) { @@ -952,18 +964,19 @@ ring_connection_modem_interface_removed (Modem *modem, if (MODEM_IS_SIM_SERVICE (oface)) { - DEBUG ("active SIM_SERVICE"); + DEBUG ("removed SIM_SERVICE"); g_object_set (self, "sim-service", NULL, NULL); } else if (MODEM_IS_CALL_SERVICE (oface)) { - DEBUG ("active CALL_SERVICE"); + DEBUG ("removed CALL_SERVICE"); g_assert (priv->media); g_object_set (priv->media, "call-service", NULL, NULL); + g_object_set (priv->conference, "call-service", NULL, NULL); } else if (MODEM_IS_SMS_SERVICE (oface)) { - DEBUG ("active SMS_SERVICE"); + DEBUG ("removed SMS_SERVICE"); g_assert (priv->text); g_object_set (priv->text, "sms-service", NULL, NULL); } @@ -1311,14 +1324,21 @@ ring_connection_inspect_contact(RingConnection const *self, } gpointer -ring_connection_lookup_channel(RingConnection const *self, - char const *object_path) +ring_connection_lookup_channel (RingConnection const *self, + char const *object_path) { + RingConnectionPrivate *priv = RING_CONNECTION(self)->priv; gpointer channel; - channel = ring_media_manager_lookup(self->priv->media, object_path); - if (channel == NULL) - channel = ring_text_manager_lookup(self->priv->text, object_path); + channel = ring_media_manager_lookup (priv->media, object_path); + if (channel) + return channel; + + channel = ring_text_manager_lookup (priv->text, object_path); + if (channel) + return channel; + + channel = ring_conference_manager_lookup (priv->conference, object_path); return channel; } |