1 /*
2  * call-stream.h - high level API for Call streams
3  *
4  * Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 
21 /**
22  * SECTION:call-stream
23  * @title: TpCallStream
24  * @short_description: proxy object for a call stream
25  *
26  * #TpCallStream is a sub-class of #TpProxy providing convenient API
27  * to represent #TpCallChannel's stream.
28  */
29 
30 /**
31  * TpCallStream:
32  *
33  * Data structure representing a #TpCallStream.
34  *
35  * Since: 0.17.5
36  */
37 
38 /**
39  * TpCallStreamClass:
40  *
41  * The class of a #TpCallStream.
42  *
43  * Since: 0.17.5
44  */
45 
46 #include "config.h"
47 
48 #include "telepathy-glib/call-stream.h"
49 
50 #include <telepathy-glib/call-misc.h>
51 #include <telepathy-glib/call-content.h>
52 #include <telepathy-glib/dbus.h>
53 #include <telepathy-glib/enums.h>
54 #include <telepathy-glib/errors.h>
55 #include <telepathy-glib/gtypes.h>
56 #include <telepathy-glib/interfaces.h>
57 #include <telepathy-glib/proxy-subclass.h>
58 #include <telepathy-glib/util.h>
59 
60 #define DEBUG_FLAG TP_DEBUG_CALL
61 #include "telepathy-glib/debug-internal.h"
62 #include "telepathy-glib/call-internal.h"
63 #include "telepathy-glib/proxy-internal.h"
64 #include "telepathy-glib/util-internal.h"
65 
66 #include "_gen/tp-cli-call-stream-body.h"
67 
68 G_DEFINE_TYPE (TpCallStream, tp_call_stream, TP_TYPE_PROXY)
69 
70 struct _TpCallStreamPrivate
71 {
72   TpConnection *connection;
73 
74   TpCallContent *content;
75 
76   /* TpContact -> TpSendingState */
77   GHashTable *remote_members;
78   TpSendingState local_sending_state;
79   gboolean can_request_receiving;
80 
81   gboolean properties_retrieved;
82 };
83 
84 enum
85 {
86   PROP_CONNECTION = 1,
87   PROP_LOCAL_SENDING_STATE,
88   PROP_CAN_REQUEST_RECEIVING,
89   PROP_CONTENT
90 };
91 
92 enum /* signals */
93 {
94   LOCAL_SENDING_STATE_CHANGED,
95   REMOTE_MEMBERS_CHANGED,
96   LAST_SIGNAL
97 };
98 
99 static guint _signals[LAST_SIGNAL] = { 0, };
100 
101 static void
update_remote_members(TpCallStream * self,GHashTable * updates,GPtrArray * removed)102 update_remote_members (TpCallStream *self,
103     GHashTable *updates,
104     GPtrArray *removed)
105 {
106   if (updates != NULL)
107     {
108       tp_g_hash_table_update (self->priv->remote_members, updates,
109           g_object_ref, NULL);
110     }
111 
112   if (removed != NULL)
113     {
114       guint i;
115 
116       for (i = 0; i < removed->len; i++)
117         {
118           g_hash_table_remove (self->priv->remote_members,
119               g_ptr_array_index (removed, i));
120         }
121     }
122 }
123 
124 static void
remote_members_changed_cb(TpCallStream * self,GHashTable * updates,GHashTable * identifiers,const GArray * removed,const GValueArray * reason,gpointer user_data,GObject * weak_object)125 remote_members_changed_cb (TpCallStream *self,
126     GHashTable *updates,
127     GHashTable *identifiers,
128     const GArray *removed,
129     const GValueArray *reason,
130     gpointer user_data,
131     GObject *weak_object)
132 {
133   GHashTable *updates_contacts;
134   GPtrArray *removed_contacts;
135   TpCallStateReason *r;
136 
137   if (!self->priv->properties_retrieved)
138     return;
139 
140   DEBUG ("Remote members: %d updated, %d removed",
141       g_hash_table_size (updates), removed->len);
142 
143   updates_contacts = _tp_call_members_convert_table (self->priv->connection,
144       updates, identifiers);
145   removed_contacts = _tp_call_members_convert_array (self->priv->connection,
146       removed);
147   r = _tp_call_state_reason_new (reason);
148 
149   update_remote_members (self, updates_contacts, removed_contacts);
150 
151   g_signal_emit (self, _signals[REMOTE_MEMBERS_CHANGED], 0,
152       updates_contacts, removed_contacts, r);
153 
154   g_hash_table_unref (updates_contacts);
155   g_ptr_array_unref (removed_contacts);
156   _tp_call_state_reason_unref (r);
157 }
158 
159 static void
local_sending_state_changed_cb(TpCallStream * self,guint state,const GValueArray * reason,gpointer user_data,GObject * weak_object)160 local_sending_state_changed_cb (TpCallStream *self,
161     guint state,
162     const GValueArray *reason,
163     gpointer user_data,
164     GObject *weak_object)
165 {
166   TpCallStateReason *r;
167 
168   if (!self->priv->properties_retrieved)
169     return;
170 
171   self->priv->local_sending_state = state;
172   g_object_notify (G_OBJECT (self), "local-sending-state");
173 
174   r = _tp_call_state_reason_new (reason);
175   g_signal_emit (self, _signals[LOCAL_SENDING_STATE_CHANGED], 0,
176       self->priv->local_sending_state, r);
177   _tp_call_state_reason_unref (r);
178 }
179 
180 static void
got_all_properties_cb(TpProxy * proxy,GHashTable * properties,const GError * error,gpointer user_data,GObject * weak_object)181 got_all_properties_cb (TpProxy *proxy,
182     GHashTable *properties,
183     const GError *error,
184     gpointer user_data,
185     GObject *weak_object)
186 {
187   TpCallStream *self = (TpCallStream *) proxy;
188   const gchar * const *interfaces;
189   GHashTable *remote_members;
190   GHashTable *identifiers;
191   GHashTable *contacts;
192 
193   if (error != NULL)
194     {
195       DEBUG ("Could not get the call stream properties: %s", error->message);
196       _tp_proxy_set_feature_prepared (proxy,
197           TP_CALL_STREAM_FEATURE_CORE, FALSE);
198       return;
199     }
200 
201   self->priv->properties_retrieved = TRUE;
202 
203   interfaces = tp_asv_get_boxed (properties,
204       "Interfaces", G_TYPE_STRV);
205   remote_members = tp_asv_get_boxed (properties,
206       "RemoteMembers", TP_HASH_TYPE_CONTACT_SENDING_STATE_MAP),
207   identifiers = tp_asv_get_boxed (properties,
208       "RemoteMemberIdentifiers", TP_HASH_TYPE_HANDLE_IDENTIFIER_MAP);
209   self->priv->local_sending_state = tp_asv_get_uint32 (properties,
210       "LocalSendingState", NULL);
211   self->priv->can_request_receiving = tp_asv_get_boolean (properties,
212       "CanRequestReceiving", NULL);
213 
214   tp_proxy_add_interfaces ((TpProxy *) self, interfaces);
215 
216   contacts = _tp_call_members_convert_table (self->priv->connection,
217       remote_members, identifiers);
218   update_remote_members (self, contacts, NULL);
219   g_hash_table_unref (contacts);
220 
221   _tp_proxy_set_feature_prepared (proxy, TP_CALL_STREAM_FEATURE_CORE, TRUE);
222 }
223 
224 static void
tp_call_stream_constructed(GObject * obj)225 tp_call_stream_constructed (GObject *obj)
226 {
227   TpCallStream *self = (TpCallStream *) obj;
228 
229   ((GObjectClass *) tp_call_stream_parent_class)->constructed (obj);
230 
231   /* Connect signals for mutable properties */
232   tp_cli_call_stream_connect_to_remote_members_changed (self,
233       remote_members_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
234   tp_cli_call_stream_connect_to_local_sending_state_changed (self,
235       local_sending_state_changed_cb, NULL, NULL, G_OBJECT (self), NULL);
236 
237   tp_cli_dbus_properties_call_get_all (self, -1,
238       TP_IFACE_CALL_STREAM,
239       got_all_properties_cb, NULL, NULL, G_OBJECT (self));
240 }
241 
242 static void
tp_call_stream_dispose(GObject * object)243 tp_call_stream_dispose (GObject *object)
244 {
245   TpCallStream *self = (TpCallStream *) object;
246 
247   g_clear_object (&self->priv->content);
248   g_clear_object (&self->priv->connection);
249   tp_clear_pointer (&self->priv->remote_members, g_hash_table_unref);
250 
251   G_OBJECT_CLASS (tp_call_stream_parent_class)->dispose (object);
252 }
253 
254 static void
tp_call_stream_get_property(GObject * object,guint property_id,GValue * value,GParamSpec * pspec)255 tp_call_stream_get_property (GObject *object,
256     guint property_id,
257     GValue *value,
258     GParamSpec *pspec)
259 {
260   TpCallStream *self = (TpCallStream *) object;
261   TpCallStreamPrivate *priv = self->priv;
262 
263   switch (property_id)
264     {
265       case PROP_CONNECTION:
266         g_value_set_object (value, self->priv->connection);
267         break;
268       case PROP_LOCAL_SENDING_STATE:
269         g_value_set_uint (value, priv->local_sending_state);
270         break;
271       case PROP_CAN_REQUEST_RECEIVING:
272         g_value_set_boolean (value, priv->can_request_receiving);
273         break;
274       case PROP_CONTENT:
275         g_value_set_object (value, self->priv->content);
276         break;
277       default:
278         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
279         break;
280     }
281 }
282 
283 static void
tp_call_stream_set_property(GObject * object,guint property_id,const GValue * value,GParamSpec * pspec)284 tp_call_stream_set_property (GObject *object,
285     guint property_id,
286     const GValue *value,
287     GParamSpec *pspec)
288 {
289   TpCallStream *self = (TpCallStream *) object;
290 
291   switch (property_id)
292     {
293       case PROP_CONNECTION:
294         g_assert (self->priv->connection == NULL); /* construct-only */
295         self->priv->connection = g_value_dup_object (value);
296         break;
297       case PROP_CONTENT:
298         g_assert (self->priv->content == NULL); /* construct-only */
299         self->priv->content = g_value_dup_object (value);
300         break;
301       default:
302         G_OBJECT_WARN_INVALID_PROPERTY_ID (object, property_id, pspec);
303         break;
304     }
305 }
306 
307 enum {
308     FEAT_CORE,
309     N_FEAT
310 };
311 
312 static const TpProxyFeature *
tp_call_stream_list_features(TpProxyClass * cls G_GNUC_UNUSED)313 tp_call_stream_list_features (TpProxyClass *cls G_GNUC_UNUSED)
314 {
315   static TpProxyFeature features[N_FEAT + 1] = { { 0 } };
316 
317   if (G_LIKELY (features[0].name != 0))
318     return features;
319 
320   /* started from constructed */
321   features[FEAT_CORE].name = TP_CALL_STREAM_FEATURE_CORE;
322   features[FEAT_CORE].core = TRUE;
323 
324   /* assert that the terminator at the end is there */
325   g_assert (features[N_FEAT].name == 0);
326 
327   return features;
328 }
329 
330 static void
tp_call_stream_class_init(TpCallStreamClass * klass)331 tp_call_stream_class_init (TpCallStreamClass *klass)
332 {
333   GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
334   TpProxyClass *proxy_class = (TpProxyClass *) klass;
335   GParamSpec *param_spec;
336 
337   gobject_class->constructed = tp_call_stream_constructed;
338   gobject_class->get_property = tp_call_stream_get_property;
339   gobject_class->set_property = tp_call_stream_set_property;
340   gobject_class->dispose = tp_call_stream_dispose;
341 
342   proxy_class->list_features = tp_call_stream_list_features;
343   proxy_class->interface = TP_IFACE_QUARK_CALL_STREAM;
344 
345   g_type_class_add_private (gobject_class, sizeof (TpCallStreamPrivate));
346   tp_call_stream_init_known_interfaces ();
347 
348   /**
349    * TpCallStream:connection:
350    *
351    * The #TpConnection of the call.
352    *
353    * Since: 0.17.5
354    */
355   param_spec = g_param_spec_object ("connection", "Connection",
356       "The connection of this stream",
357       TP_TYPE_CONNECTION,
358       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
359   g_object_class_install_property (gobject_class, PROP_CONNECTION,
360       param_spec);
361 
362   /**
363    * TpCallStream:local-sending-state:
364    *
365    * The local user's sending state, from #TpSendingState.
366    *
367    * Since: 0.17.5
368    */
369   param_spec = g_param_spec_uint ("local-sending-state", "LocalSendingState",
370       "Local sending state",
371       0, G_MAXUINT, 0,
372       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
373   g_object_class_install_property (gobject_class, PROP_LOCAL_SENDING_STATE,
374       param_spec);
375 
376   /**
377    * TpCallStream:can-request-receiving:
378    *
379    * If %TRUE, the user can request that a remote contact starts sending on this
380    * stream.
381    *
382    * Since: 0.17.5
383    */
384   param_spec = g_param_spec_boolean ("can-request-receiving",
385       "CanRequestReceiving",
386       "If true, the user can request that a remote contact starts sending on"
387       "this stream.",
388       FALSE,
389       G_PARAM_READABLE | G_PARAM_STATIC_STRINGS);
390   g_object_class_install_property (gobject_class, PROP_CAN_REQUEST_RECEIVING,
391       param_spec);
392 
393   /**
394    * TpCallStream:content:
395    *
396    * The Content that this streams belongs to
397    *
398    * Since: 0.17.6
399    */
400   param_spec = g_param_spec_object ("content",
401       "Content",
402       "The content that this Stream belongs to",
403       TP_TYPE_CALL_CONTENT,
404       G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS);
405   g_object_class_install_property (gobject_class, PROP_CONTENT,
406       param_spec);
407 
408   /**
409    * TpCallStream::local-sending-state-changed:
410    * @self: the #TpCallStream
411    * @state: the new #TpSendingState
412    * @reason: the #TpCallStateReason for the change
413    *
414    * The ::local-sending-state-changed signal is emitted whenever the
415    * stream sending state changes.
416    *
417    * Since: 0.17.5
418    */
419   _signals[LOCAL_SENDING_STATE_CHANGED] = g_signal_new ("local-sending-state-changed",
420       G_OBJECT_CLASS_TYPE (klass),
421       G_SIGNAL_RUN_LAST,
422       0, NULL, NULL, NULL,
423       G_TYPE_NONE,
424       2, G_TYPE_UINT, TP_TYPE_CALL_STATE_REASON);
425 
426   /**
427    * TpCallStream::remote-members-changed:
428    * @self: the #TpCallStream
429    * @updates: (type GLib.HashTable) (element-type TelepathyGLib.Contact uint):
430    *   #GHashTable mapping #TpContact to its new #TpSendingState
431    * @removed: (type GLib.PtrArray) (element-type TelepathyGLib.Contact):
432    *  #GPtrArray of #TpContact removed from remote contacts
433    * @reason: the #TpCallStateReason for the change
434    *
435    * The ::remote-members-changed signal is emitted whenever the
436    * stream's remote members changes.
437    *
438    * It is NOT guaranteed that #TpContact objects have any feature prepared.
439    *
440    * Since: 0.17.5
441    */
442   _signals[REMOTE_MEMBERS_CHANGED] = g_signal_new ("remote-members-changed",
443       G_OBJECT_CLASS_TYPE (klass),
444       G_SIGNAL_RUN_LAST,
445       0, NULL, NULL, NULL,
446       G_TYPE_NONE,
447       3, G_TYPE_HASH_TABLE, G_TYPE_PTR_ARRAY, TP_TYPE_CALL_STATE_REASON);
448 }
449 
450 static void
tp_call_stream_init(TpCallStream * self)451 tp_call_stream_init (TpCallStream *self)
452 {
453   self->priv = G_TYPE_INSTANCE_GET_PRIVATE ((self), TP_TYPE_CALL_STREAM,
454       TpCallStreamPrivate);
455 
456   self->priv->remote_members = g_hash_table_new_full (NULL, NULL,
457       g_object_unref, NULL);
458 }
459 
460 /**
461  * tp_call_stream_init_known_interfaces:
462  *
463  * Ensure that the known interfaces for #TpCallStream have been set up.
464  * This is done automatically when necessary, but for correct
465  * overriding of library interfaces by local extensions, you should
466  * call this function before calling
467  * tp_proxy_or_subclass_hook_on_interface_add() with first argument
468  * %TP_TYPE_CALL_STREAM.
469  *
470  * Since: 0.17.5
471  */
472 void
tp_call_stream_init_known_interfaces(void)473 tp_call_stream_init_known_interfaces (void)
474 {
475   static gsize once = 0;
476 
477   if (g_once_init_enter (&once))
478     {
479       GType tp_type = TP_TYPE_CALL_STREAM;
480 
481       tp_proxy_init_known_interfaces ();
482       tp_proxy_or_subclass_hook_on_interface_add (tp_type,
483           tp_cli_call_stream_add_signals);
484       tp_proxy_subclass_add_error_mapping (tp_type,
485           TP_ERROR_PREFIX, TP_ERROR, TP_TYPE_ERROR);
486 
487       g_once_init_leave (&once, 1);
488     }
489 }
490 
491 /**
492  * TP_CALL_STREAM_FEATURE_CORE:
493  *
494  * Expands to a call to a function that returns a quark for the "core"
495  * feature on a #TpCallStream.
496  *
497  * One can ask for a feature to be prepared using the tp_proxy_prepare_async()
498  * function, and waiting for it to trigger the callback.
499  */
500 GQuark
tp_call_stream_get_feature_quark_core(void)501 tp_call_stream_get_feature_quark_core (void)
502 {
503   return g_quark_from_static_string ("tp-call-stream-feature-core");
504 }
505 
506 /**
507  * tp_call_stream_get_local_sending_state:
508  * @self: a #TpCallStream
509  *
510  * <!-- -->
511  *
512  * Returns: the value of #TpCallStream:local-sending-state
513  * Since: 0.17.5
514  */
515 TpSendingState
tp_call_stream_get_local_sending_state(TpCallStream * self)516 tp_call_stream_get_local_sending_state (TpCallStream *self)
517 {
518   g_return_val_if_fail (TP_IS_CALL_STREAM (self), TP_SENDING_STATE_NONE);
519 
520   return self->priv->local_sending_state;
521 }
522 
523 /**
524  * tp_call_stream_can_request_receiving:
525  * @self: a #TpCallStream
526  *
527  * <!-- -->
528  *
529  * Returns: the value of #TpCallStream:can-request-receiving
530  * Since: 0.17.5
531  */
532 gboolean
tp_call_stream_can_request_receiving(TpCallStream * self)533 tp_call_stream_can_request_receiving (TpCallStream *self)
534 {
535   g_return_val_if_fail (TP_IS_CALL_STREAM (self), FALSE);
536 
537   return self->priv->can_request_receiving;
538 }
539 
540 /**
541  * tp_call_stream_get_remote_members:
542  * @self: a #TpCallStream
543  *
544  * Get the remote contacts to who this stream is connected, mapped to their
545  * sending state.
546  *
547  * It is NOT guaranteed that #TpContact objects have any feature prepared.
548  *
549  * Returns: (transfer none) (type GLib.HashTable) (element-type TelepathyGLib.Contact uint):
550  *  #GHashTable mapping #TpContact to its new #TpSendingState
551  * Since: 0.17.5
552  */
553 GHashTable *
tp_call_stream_get_remote_members(TpCallStream * self)554 tp_call_stream_get_remote_members (TpCallStream *self)
555 {
556   g_return_val_if_fail (TP_IS_CALL_STREAM (self), NULL);
557 
558   return self->priv->remote_members;
559 }
560 
561 static void
generic_async_cb(TpCallStream * self,const GError * error,gpointer user_data,GObject * weak_object)562 generic_async_cb (TpCallStream *self,
563     const GError *error,
564     gpointer user_data,
565     GObject *weak_object)
566 {
567   GSimpleAsyncResult *result = user_data;
568 
569   if (error != NULL)
570     {
571       DEBUG ("Error: %s", error->message);
572       g_simple_async_result_set_from_error (result, error);
573     }
574 
575   g_simple_async_result_complete (result);
576 }
577 
578 /**
579  * tp_call_stream_set_sending_async:
580  * @self: a #TpCallStream
581  * @send: the requested sending state
582  * @callback: a callback to call when the operation finishes
583  * @user_data: data to pass to @callback
584  *
585  * Set the stream to start or stop sending media from the local user to other
586  * contacts.
587  *
588  * If @send is %TRUE, #TpCallStream:local-sending-state should change to
589  * %TP_SENDING_STATE_SENDING, if it isn't already.
590  * If @send is %FALSE, #TpCallStream:local-sending-state should change to
591  * %TP_SENDING_STATE_NONE, if it isn't already.
592  *
593  * Since: 0.17.5
594  */
595 void
tp_call_stream_set_sending_async(TpCallStream * self,gboolean send,GAsyncReadyCallback callback,gpointer user_data)596 tp_call_stream_set_sending_async (TpCallStream *self,
597     gboolean send,
598     GAsyncReadyCallback callback,
599     gpointer user_data)
600 {
601   GSimpleAsyncResult *result;
602 
603   g_return_if_fail (TP_IS_CALL_STREAM (self));
604 
605   result = g_simple_async_result_new (G_OBJECT (self), callback,
606       user_data, tp_call_stream_set_sending_async);
607 
608   tp_cli_call_stream_call_set_sending (self, -1, send,
609       generic_async_cb, result, g_object_unref, G_OBJECT (self));
610 }
611 
612 /**
613  * tp_call_stream_set_sending_finish:
614  * @self: a #TpCallStream
615  * @result: a #GAsyncResult
616  * @error: a #GError to fill
617  *
618  * Finishes tp_call_stream_set_sending_async().
619  *
620  * Since: 0.17.5
621  */
622 gboolean
tp_call_stream_set_sending_finish(TpCallStream * self,GAsyncResult * result,GError ** error)623 tp_call_stream_set_sending_finish (TpCallStream *self,
624     GAsyncResult *result,
625     GError **error)
626 {
627   _tp_implement_finish_void (self, tp_call_stream_set_sending_async);
628 }
629 
630 /**
631  * tp_call_stream_request_receiving_async:
632  * @self: a #TpCallStream
633  * @contact: contact from which sending is requested
634  * @receive: the requested receiving state
635  * @callback: a callback to call when the operation finishes
636  * @user_data: data to pass to @callback
637  *
638  * Request that a remote contact stops or starts sending on this stream.
639  *
640  * The #TpCallStream:can-request-receiving property defines whether the protocol
641  * allows the local user to request the other side start sending on this stream.
642  *
643  * If @receive is %TRUE, request that the given contact starts to send media.
644  * If @receive is %FALSE, request that the given contact stops sending media.
645  *
646  * Since: 0.17.5
647  */
648 void
tp_call_stream_request_receiving_async(TpCallStream * self,TpContact * contact,gboolean receive,GAsyncReadyCallback callback,gpointer user_data)649 tp_call_stream_request_receiving_async (TpCallStream *self,
650     TpContact *contact,
651     gboolean receive,
652     GAsyncReadyCallback callback,
653     gpointer user_data)
654 {
655   GSimpleAsyncResult *result;
656 
657   g_return_if_fail (TP_IS_CALL_STREAM (self));
658   g_return_if_fail (TP_IS_CONTACT (contact));
659   g_return_if_fail (tp_contact_get_connection (contact) ==
660       self->priv->connection);
661 
662   result = g_simple_async_result_new (G_OBJECT (self), callback,
663       user_data, tp_call_stream_set_sending_async);
664 
665   tp_cli_call_stream_call_request_receiving (self, -1,
666       tp_contact_get_handle (contact), receive,
667       generic_async_cb, result, g_object_unref, G_OBJECT (self));
668 }
669 
670 /**
671  * tp_call_stream_request_receiving_finish:
672  * @self: a #TpCallStream
673  * @result: a #GAsyncResult
674  * @error: a #GError to fill
675  *
676  * Finishes tp_call_stream_request_receiving_async().
677  *
678  * Since: 0.17.5
679  */
680 gboolean
tp_call_stream_request_receiving_finish(TpCallStream * self,GAsyncResult * result,GError ** error)681 tp_call_stream_request_receiving_finish (TpCallStream *self,
682     GAsyncResult *result,
683     GError **error)
684 {
685   _tp_implement_finish_void (self, tp_call_stream_request_receiving_async);
686 }
687