2
* call-manager.c - an example channel manager for Call channels.
4
* This channel manager emulates a protocol like XMPP Jingle, where you can
5
* make several simultaneous calls to the same or different contacts.
7
* Copyright © 2007-2009 Collabora Ltd. <http://www.collabora.co.uk/>
8
* Copyright © 2007-2009 Nokia Corporation
10
* This library is free software; you can redistribute it and/or
11
* modify it under the terms of the GNU Lesser General Public
12
* License as published by the Free Software Foundation; either
13
* version 2.1 of the License, or (at your option) any later version.
15
* This library is distributed in the hope that it will be useful,
16
* but WITHOUT ANY WARRANTY; without even the implied warranty of
17
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18
* Lesser General Public License for more details.
20
* You should have received a copy of the GNU Lesser General Public
21
* License along with this library; if not, write to the Free Software
22
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
25
#include "call-manager.h"
27
#include <dbus/dbus-glib.h>
29
#include <telepathy-glib/base-connection.h>
30
#include <telepathy-glib/channel-manager.h>
31
#include <telepathy-glib/dbus.h>
32
#include <telepathy-glib/errors.h>
33
#include <telepathy-glib/interfaces.h>
35
#include "extensions/extensions.h"
37
#include "call-channel.h"
39
static void channel_manager_iface_init (gpointer, gpointer);
41
G_DEFINE_TYPE_WITH_CODE (ExampleCallManager,
44
G_IMPLEMENT_INTERFACE (TP_TYPE_CHANNEL_MANAGER,
45
channel_manager_iface_init))
47
/* type definition stuff */
52
PROP_SIMULATION_DELAY,
56
struct _ExampleCallManagerPrivate
58
TpBaseConnection *conn;
59
guint simulation_delay;
61
/* Map from reffed ExampleCallChannel to the same pointer; used as a
66
/* Next channel will be ("CallChannel%u", next_channel_index) */
67
guint next_channel_index;
69
gulong status_changed_id;
74
example_call_manager_init (ExampleCallManager *self)
76
self->priv = G_TYPE_INSTANCE_GET_PRIVATE (self,
77
EXAMPLE_TYPE_CALL_MANAGER,
78
ExampleCallManagerPrivate);
80
self->priv->conn = NULL;
81
self->priv->channels = g_hash_table_new_full (NULL, NULL, g_object_unref,
83
self->priv->status_changed_id = 0;
84
self->priv->available_id = 0;
88
example_call_manager_close_all (ExampleCallManager *self)
90
if (self->priv->channels != NULL)
92
GHashTable *tmp = self->priv->channels;
96
self->priv->channels = NULL;
98
g_hash_table_iter_init (&iter, tmp);
100
while (g_hash_table_iter_next (&iter, NULL, &v))
101
example_call_channel_disconnected (v);
103
g_hash_table_unref (tmp);
106
if (self->priv->available_id != 0)
108
g_signal_handler_disconnect (self->priv->conn,
109
self->priv->available_id);
110
self->priv->available_id = 0;
113
if (self->priv->status_changed_id != 0)
115
g_signal_handler_disconnect (self->priv->conn,
116
self->priv->status_changed_id);
117
self->priv->status_changed_id = 0;
122
dispose (GObject *object)
124
ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
126
example_call_manager_close_all (self);
127
g_assert (self->priv->channels == NULL);
129
((GObjectClass *) example_call_manager_parent_class)->dispose (
134
get_property (GObject *object,
139
ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
143
case PROP_CONNECTION:
144
g_value_set_object (value, self->priv->conn);
147
case PROP_SIMULATION_DELAY:
148
g_value_set_uint (value, self->priv->simulation_delay);
152
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
157
set_property (GObject *object,
162
ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
166
case PROP_CONNECTION:
167
/* We don't ref the connection, because it owns a reference to the
168
* channel manager, and it guarantees that the manager's lifetime is
169
* less than its lifetime */
170
self->priv->conn = g_value_get_object (value);
173
case PROP_SIMULATION_DELAY:
174
self->priv->simulation_delay = g_value_get_uint (value);
178
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
183
status_changed_cb (TpBaseConnection *conn,
186
ExampleCallManager *self)
190
case TP_CONNECTION_STATUS_DISCONNECTED:
192
example_call_manager_close_all (self);
201
static ExampleCallChannel *new_channel (ExampleCallManager *self,
202
TpHandle handle, TpHandle initiator, gpointer request_token,
203
gboolean initial_audio, gboolean initial_video);
206
simulate_incoming_call_cb (gpointer p)
208
ExampleCallManager *self = p;
209
TpHandleRepoIface *contact_repo;
212
/* do nothing if we've been disconnected while waiting for the contact to
214
if (self->priv->available_id == 0)
217
/* We're called by someone whose ID on the IM service is "caller" */
218
contact_repo = tp_base_connection_get_handles (self->priv->conn,
219
TP_HANDLE_TYPE_CONTACT);
220
caller = tp_handle_ensure (contact_repo, "caller", NULL, NULL);
222
new_channel (self, caller, caller, NULL, TRUE, FALSE);
227
/* Whenever our presence changes from away to available, and whenever our
228
* presence message changes while remaining available, simulate a call from
231
available_cb (GObject *conn G_GNUC_UNUSED,
232
const gchar *message,
233
ExampleCallManager *self)
235
g_timeout_add_full (G_PRIORITY_DEFAULT, self->priv->simulation_delay,
236
simulate_incoming_call_cb, g_object_ref (self), g_object_unref);
240
constructed (GObject *object)
242
ExampleCallManager *self = EXAMPLE_CALL_MANAGER (object);
243
void (*chain_up) (GObject *) =
244
((GObjectClass *) example_call_manager_parent_class)->constructed;
246
if (chain_up != NULL)
251
self->priv->status_changed_id = g_signal_connect (self->priv->conn,
252
"status-changed", (GCallback) status_changed_cb, self);
254
self->priv->available_id = g_signal_connect (self->priv->conn,
255
"available", (GCallback) available_cb, self);
259
example_call_manager_class_init (ExampleCallManagerClass *klass)
261
GParamSpec *param_spec;
262
GObjectClass *object_class = (GObjectClass *) klass;
264
object_class->constructed = constructed;
265
object_class->dispose = dispose;
266
object_class->get_property = get_property;
267
object_class->set_property = set_property;
269
param_spec = g_param_spec_object ("connection", "Connection object",
270
"The connection that owns this channel manager",
271
TP_TYPE_BASE_CONNECTION,
272
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
273
g_object_class_install_property (object_class, PROP_CONNECTION, param_spec);
275
param_spec = g_param_spec_uint ("simulation-delay", "Simulation delay",
276
"Delay between simulated network events",
277
0, G_MAXUINT32, 1000,
278
G_PARAM_CONSTRUCT_ONLY | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS);
279
g_object_class_install_property (object_class, PROP_SIMULATION_DELAY,
282
g_type_class_add_private (klass,
283
sizeof (ExampleCallManagerPrivate));
287
example_call_manager_foreach_channel (TpChannelManager *iface,
288
TpExportableChannelFunc callback,
291
ExampleCallManager *self = EXAMPLE_CALL_MANAGER (iface);
295
g_hash_table_iter_init (&iter, self->priv->channels);
297
while (g_hash_table_iter_next (&iter, &chan, NULL))
298
callback (chan, user_data);
302
channel_closed_cb (ExampleCallChannel *chan,
303
ExampleCallManager *self)
305
tp_channel_manager_emit_channel_closed_for_object (self,
306
TP_EXPORTABLE_CHANNEL (chan));
308
if (self->priv->channels != NULL)
309
g_hash_table_remove (self->priv->channels, chan);
312
static ExampleCallChannel *
313
new_channel (ExampleCallManager *self,
316
gpointer request_token,
317
gboolean initial_audio,
318
gboolean initial_video)
320
ExampleCallChannel *chan;
322
GSList *requests = NULL;
324
/* FIXME: This could potentially wrap around, but only after 4 billion
325
* calls, which is probably plenty. */
326
object_path = g_strdup_printf ("%s/CallChannel%u",
327
self->priv->conn->object_path, self->priv->next_channel_index++);
329
chan = g_object_new (EXAMPLE_TYPE_CALL_CHANNEL,
330
"connection", self->priv->conn,
331
"object-path", object_path,
333
"initiator-handle", initiator,
334
"requested", (self->priv->conn->self_handle == initiator),
335
"simulation-delay", self->priv->simulation_delay,
336
"initial-audio", initial_audio,
337
"initial-video", initial_video,
340
g_free (object_path);
342
g_signal_connect (chan, "closed", G_CALLBACK (channel_closed_cb), self);
344
g_hash_table_insert (self->priv->channels, chan, chan);
346
if (request_token != NULL)
347
requests = g_slist_prepend (requests, request_token);
349
tp_channel_manager_emit_new_channel (self, TP_EXPORTABLE_CHANNEL (chan),
351
g_slist_free (requests);
356
static const gchar * const audio_fixed_properties[] = {
357
TP_PROP_CHANNEL_CHANNEL_TYPE,
358
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
359
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO,
363
static const gchar * const video_fixed_properties[] = {
364
TP_PROP_CHANNEL_CHANNEL_TYPE,
365
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE,
366
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO,
370
static const gchar * const audio_allowed_properties[] = {
371
TP_PROP_CHANNEL_TARGET_HANDLE,
372
TP_PROP_CHANNEL_TARGET_ID,
373
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO,
377
static const gchar * const video_allowed_properties[] = {
378
TP_PROP_CHANNEL_TARGET_HANDLE,
379
TP_PROP_CHANNEL_TARGET_ID,
380
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO,
385
example_call_manager_foreach_channel_class (
386
TpChannelManager *manager,
387
TpChannelManagerChannelClassFunc func,
390
GHashTable *table = tp_asv_new (
391
TP_PROP_CHANNEL_CHANNEL_TYPE,
392
G_TYPE_STRING, FUTURE_IFACE_CHANNEL_TYPE_CALL,
393
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, G_TYPE_UINT, TP_HANDLE_TYPE_CONTACT,
394
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, G_TYPE_BOOLEAN, TRUE,
397
func (manager, table, audio_allowed_properties, user_data);
399
g_hash_table_remove (table, FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO);
400
tp_asv_set_boolean (table, FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO,
403
func (manager, table, video_allowed_properties, user_data);
405
g_hash_table_destroy (table);
409
example_call_manager_request (ExampleCallManager *self,
410
gpointer request_token,
411
GHashTable *request_properties,
412
gboolean require_new)
415
GError *error = NULL;
416
gboolean initial_audio, initial_video;
418
if (tp_strdiff (tp_asv_get_string (request_properties,
419
TP_PROP_CHANNEL_CHANNEL_TYPE),
420
FUTURE_IFACE_CHANNEL_TYPE_CALL))
425
if (tp_asv_get_uint32 (request_properties,
426
TP_PROP_CHANNEL_TARGET_HANDLE_TYPE, NULL) != TP_HANDLE_TYPE_CONTACT)
431
handle = tp_asv_get_uint32 (request_properties,
432
TP_PROP_CHANNEL_TARGET_HANDLE, NULL);
433
g_assert (handle != 0);
435
initial_audio = tp_asv_get_boolean (request_properties,
436
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_AUDIO, NULL);
437
initial_video = tp_asv_get_boolean (request_properties,
438
FUTURE_PROP_CHANNEL_TYPE_CALL_INITIAL_VIDEO, NULL);
440
if (!initial_audio && !initial_video)
442
g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
443
"Call channels must initially have either audio or video content");
447
/* the set of (fixed | allowed) properties is the same for audio and video,
448
* so we only need to check with one set */
449
if (tp_channel_manager_asv_has_unknown_properties (request_properties,
450
audio_fixed_properties, audio_allowed_properties, &error))
455
if (handle == self->priv->conn->self_handle)
457
/* In protocols with a concept of multiple "resources" signed in to
458
* one account (XMPP, and possibly MSN) it is technically possible to
459
* call yourself - e.g. if you're signed in on two PCs, you can call one
460
* from the other. For simplicity, this example simulates a protocol
461
* where this is not the case.
463
g_set_error (&error, TP_ERRORS, TP_ERROR_NOT_IMPLEMENTED,
464
"In this protocol, you can't call yourself");
470
/* see if we're already calling that handle */
474
g_hash_table_iter_init (&iter, self->priv->channels);
476
while (g_hash_table_iter_next (&iter, &chan, NULL))
481
"handle", &its_handle,
484
if (its_handle == handle)
486
tp_channel_manager_emit_request_already_satisfied (self,
487
request_token, TP_EXPORTABLE_CHANNEL (chan));
493
new_channel (self, handle, self->priv->conn->self_handle, request_token,
494
initial_audio, initial_video);
498
tp_channel_manager_emit_request_failed (self, request_token,
499
error->domain, error->code, error->message);
500
g_error_free (error);
505
example_call_manager_create_channel (TpChannelManager *manager,
506
gpointer request_token,
507
GHashTable *request_properties)
509
return example_call_manager_request (
510
EXAMPLE_CALL_MANAGER (manager),
511
request_token, request_properties, TRUE);
515
example_call_manager_ensure_channel (TpChannelManager *manager,
516
gpointer request_token,
517
GHashTable *request_properties)
519
return example_call_manager_request (
520
EXAMPLE_CALL_MANAGER (manager),
521
request_token, request_properties, FALSE);
525
channel_manager_iface_init (gpointer g_iface,
526
gpointer iface_data G_GNUC_UNUSED)
528
TpChannelManagerIface *iface = g_iface;
530
iface->foreach_channel = example_call_manager_foreach_channel;
531
iface->foreach_channel_class = example_call_manager_foreach_channel_class;
532
iface->create_channel = example_call_manager_create_channel;
533
iface->ensure_channel = example_call_manager_ensure_channel;
534
/* In this channel manager, RequestChannel is not supported; Call is not
535
* designed to work with the old RequestChannel API. */
536
iface->request_channel = NULL;