08698f3a0bb173dea01eb80d1945047dc474c217
[theonering] / hand_tests / dbus_signals.py
1 #!/usr/bin/env python
2
3 import sys
4 sys.path.insert(0,"../src")
5 import logging
6 import pprint
7
8 import gobject
9 import dbus
10 import dbus.mainloop.glib
11 import telepathy
12
13 import util.go_utils as gobject_utils
14 import gtk_toolbox
15
16
17 _moduleLogger = logging.getLogger("receptionist")
18 DBUS_PROPERTIES = 'org.freedesktop.DBus.Properties'
19
20
21 class AutoAcceptCall(object):
22
23         def __init__(self, bus, conn, chan, on_success, on_error):
24                 self._sessionBus = bus
25                 self._conn = conn
26                 self._chan = chan
27                 self._outstandingRequests = []
28                 self._on_success = on_success
29                 self._on_error = on_error
30
31                 self._initiatorHandle = None
32                 self._initiatorID = None
33                 self._targetHandle = None
34                 self._targetID = None
35                 self._requested = None
36                 self._pendingHandles = None
37
38                 self._chan[DBUS_PROPERTIES].GetAll(
39                         telepathy.interfaces.CHANNEL_INTERFACE,
40                         reply_handler = self._on_got_all,
41                         error_handler = self._custom_error(self._on_got_all),
42                 )
43                 self._outstandingRequests.append(self._on_got_all)
44
45                 if False:
46                         # @bug Unsure why this isn't working
47                         self._chan[DBUS_PROPERTIES].Get(
48                                 telepathy.interfaces.CHANNEL_INTERFACE_GROUP,
49                                 'LocalPendingMembers',
50                                 reply_handler = self._on_got_pending_members,
51                                 error_handler = self._custom_error(self._on_got_pending_members),
52                         )
53                 else:
54                         self._chan[telepathy.interfaces.CHANNEL_INTERFACE_GROUP].GetLocalPendingMembersWithInfo(
55                                 reply_handler = self._on_got_pending_members,
56                                 error_handler = self._custom_error(self._on_got_pending_members),
57                         )
58                 self._outstandingRequests.append(self._on_got_pending_members)
59
60         def is_inbound(self):
61                 return not self._requested
62                 isInbound = self._targetHandle == self._initiatorHandle
63                 return isInbound
64
65         @property
66         def initiator(self):
67                 return self._initiatorID
68
69         @property
70         def target(self):
71                 return self._targetID
72
73         def accept_call(self, on_accepted, on_error):
74                 self._chan[telepathy.interfaces.CHANNEL_INTERFACE_GROUP].AddMembers(
75                         self._pendingHandles,
76                         "",
77                         reply_handler = self._custom_on_accept(on_accepted),
78                         error_handler = self._custom_on_accept_error(on_error),
79                 )
80
81         def _custom_on_accept(self, callback):
82
83                 def on_accept(*args):
84                         callback(self)
85
86                 return on_accept
87
88         def _custom_on_accept_error(self, callback):
89
90                 def on_error(*args):
91                         callback(self, *args)
92
93                 return on_error
94
95         def _custom_error(self, action):
96
97                 def _on_error(*args):
98                         _moduleLogger.error("Failed for %r (%r)" % (action, args))
99                         self._outstandingRequests.remove(action)
100                         if self._outstandingRequests:
101                                 return
102
103                         self._on_error(self)
104
105                 return _on_error
106
107         def _report_callback_done(self, action):
108                 _moduleLogger.debug("Succeded with %r" % (action, ))
109                 self._outstandingRequests.remove(action)
110                 if self._outstandingRequests:
111                         return
112
113                 assert None not in (
114                         self._initiatorHandle,
115                         self._initiatorID,
116                         self._targetHandle,
117                         self._targetID,
118                         self._pendingHandles,
119                         self._requested,
120                 )
121
122                 self._on_success(self)
123
124         @gtk_toolbox.log_exception(_moduleLogger)
125         def _on_got_all(self, properties):
126                 self._initiatorID = properties["InitiatorID"]
127                 self._initiatorHandle = properties["InitiatorHandle"]
128                 self._targetID = properties["InitiatorID"]
129                 self._targetHandle = properties["InitiatorHandle"]
130                 self._requested = properties["Requested"]
131
132                 self._report_callback_done(self._on_got_all)
133
134         @gtk_toolbox.log_exception(_moduleLogger)
135         def _on_got_pending_members(self, pendings):
136                 for pendingHandle, instigatorHandle, reason, message in pendings:
137                         print pendingHandle, instigatorHandle, reason, message
138
139                 self._pendingHandles = [pendingWithInfo[0] for pendingWithInfo in pendings]
140
141                 self._report_callback_done(self._on_got_pending_members)
142
143
144 class WasMissedCall(object):
145
146         def __init__(self, bus, conn, chan, on_success, on_error):
147                 self._sessionBus = bus
148                 self._conn = conn
149                 self._chan = chan
150                 self._on_success = on_success
151                 self._on_error = on_error
152
153                 self._requested = None
154                 self._didMembersChange = False
155                 self._didClose = False
156                 self._didReport = False
157
158                 self._timeoutId = gobject_utils.timeout_add_seconds(10, self._on_timeout)
159
160                 self._chan[telepathy.interfaces.CHANNEL_INTERFACE_GROUP].connect_to_signal(
161                         "MembersChanged",
162                         self._on_members_changed,
163                 )
164
165                 self._chan[telepathy.interfaces.CHANNEL].connect_to_signal(
166                         "Closed",
167                         self._on_closed,
168                 )
169
170                 self._chan[DBUS_PROPERTIES].GetAll(
171                         telepathy.interfaces.CHANNEL_INTERFACE,
172                         reply_handler = self._on_got_all,
173                         error_handler = self._on_got_all,
174                 )
175
176         def _report_missed_if_ready(self):
177                 if self._didReport:
178                         pass
179                 elif self._requested is not None and (self._didMembersChange or self._didClose):
180                         if self._requested:
181                                 self._report_error("wrong direction")
182                         elif self._didClose:
183                                 self._report_success()
184                         else:
185                                 self._report_error("members added")
186                 else:
187                         if self._didClose:
188                                 self._report_error("closed too early")
189
190         def _report_success(self):
191                 assert not self._didReport
192                 self._didReport = True
193                 if self._timeoutId:
194                         gobject.source_remove(self._timeoutId)
195                         self._timeoutId = None
196                 self._on_success(self)
197
198         def _report_error(self, reason):
199                 assert not self._didReport
200                 self._didReport = True
201                 if self._timeoutId:
202                         gobject.source_remove(self._timeoutId)
203                         self._timeoutId = None
204                 self._on_error(self, reason)
205
206         @gtk_toolbox.log_exception(_moduleLogger)
207         @gtk_toolbox.trace(_moduleLogger)
208         def _on_got_all(self, properties):
209                 self._requested = properties["Requested"]
210                 self._report_missed_if_ready()
211
212         @gtk_toolbox.log_exception(_moduleLogger)
213         @gtk_toolbox.trace(_moduleLogger)
214         def _on_members_changed(self, message, added, removed, lp, rp, actor, reason):
215                 pprint.pprint((message, added, removed, lp, rp, actor, reason))
216                 if added:
217                         self._didMembersChange = True
218                         self._report_missed_if_ready()
219
220         @gtk_toolbox.log_exception(_moduleLogger)
221         @gtk_toolbox.trace(_moduleLogger)
222         def _on_closed(self):
223                 self._didClose = True
224                 self._report_missed_if_ready()
225
226         @gtk_toolbox.log_exception(_moduleLogger)
227         @gtk_toolbox.trace(_moduleLogger)
228         def _on_error(self, *args):
229                 self._report_error(args)
230
231         @gtk_toolbox.log_exception(_moduleLogger)
232         @gtk_toolbox.trace(_moduleLogger)
233         def _on_timeout(self):
234                 self._report_error("timeout")
235                 return False
236
237
238 class NewChannelSignaller(object):
239
240         def __init__(self, on_new_channel):
241                 self._sessionBus = dbus.SessionBus()
242                 self._on_user_new_channel = on_new_channel
243
244         def start(self):
245                 self._sessionBus.add_signal_receiver(
246                         self._on_new_channel,
247                         "NewChannel",
248                         "org.freedesktop.Telepathy.Connection",
249                         None,
250                         None
251                 )
252
253         def stop(self):
254                 self._sessionBus.remove_signal_receiver(
255                         self._on_new_channel,
256                         "NewChannel",
257                         "org.freedesktop.Telepathy.Connection",
258                         None,
259                         None
260                 )
261
262         @gtk_toolbox.log_exception(_moduleLogger)
263         def _on_new_channel(
264                 self, channelObjectPath, channelType, handleType, handle, supressHandler
265         ):
266                 connObjectPath = channelObjectPath.rsplit("/", 1)[0]
267                 serviceName = connObjectPath[1:].replace("/", ".")
268                 conn = telepathy.client.Connection(serviceName, connObjectPath)
269                 chan = telepathy.client.Channel(serviceName, channelObjectPath)
270                 self._on_user_new_channel(self._sessionBus, conn, chan, channelType)
271
272
273 class AutoAcceptManager(object):
274
275         def __init__(self):
276                 self._newChannelSignaller = NewChannelSignaller(self._on_new_channel)
277
278         def start(self):
279                 self._newChannelSignaller.start()
280
281         @gtk_toolbox.log_exception(_moduleLogger)
282         def _on_new_channel(self, bus, conn, chan, channelType):
283                 pprint.pprint((bus, conn, chan, channelType))
284                 if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
285                         return
286
287                 # @todo distinguish between preferred CMs
288                 attemptPickup = AutoAcceptCall(bus, conn, chan, self._on_inbound_call, self._on_inbound_call_error)
289
290         @gtk_toolbox.log_exception(_moduleLogger)
291         def _on_inbound_call(self, autoAcceptCall):
292                 # @todo Add a comparison for picking up for only certain contacts
293                 print autoAcceptCall.initiator, autoAcceptCall.target
294                 if autoAcceptCall.is_inbound():
295                         autoAcceptCall.accept_call(self._on_call_pickedup, self._on_pickup_error)
296                 else:
297                         _moduleLogger.debug(
298                                 "Not an inbound call (initiator=%r, target=%r)" % (autoAcceptCall.initiator, autoAcceptCall.target)
299                         )
300
301         @gtk_toolbox.log_exception(_moduleLogger)
302         def _on_inbound_call_error(self, *args):
303                 _moduleLogger.info("Inbound call error")
304
305         @gtk_toolbox.log_exception(_moduleLogger)
306         def _on_call_pickedup(self, autoAcceptCall):
307                 _moduleLogger.info("Call picked up")
308
309         @gtk_toolbox.log_exception(_moduleLogger)
310         def _on_pickup_error(self, autoAcceptCall, *args):
311                 _moduleLogger.info("Call failed to pick up (%r)" % (args, ))
312
313
314 class MissedManager(object):
315
316         def __init__(self):
317                 self._newChannelSignaller = NewChannelSignaller(self._on_new_channel)
318
319         def start(self):
320                 self._newChannelSignaller.start()
321
322         @gtk_toolbox.log_exception(_moduleLogger)
323         def _on_new_channel(self, bus, conn, chan, channelType):
324                 pprint.pprint((bus, conn, chan, channelType))
325                 if channelType != telepathy.interfaces.CHANNEL_TYPE_STREAMED_MEDIA:
326                         return
327
328                 missDetection = WasMissedCall(
329                         bus, conn, chan, self._on_missed_call, self._on_error_for_missed
330                 )
331
332         @gtk_toolbox.log_exception(_moduleLogger)
333         def _on_missed_call(self, missDetection):
334                 _moduleLogger.info("Missed a call")
335
336         @gtk_toolbox.log_exception(_moduleLogger)
337         def _on_error_for_missed(self, missDetection, reason):
338                 _moduleLogger.info("Error: %r claims %r" % (missDetection, reason))
339
340
341 if __name__ == "__main__":
342         logging.basicConfig(level=logging.DEBUG)
343         l = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
344         if False:
345                 manager = AutoAcceptManager()
346         else:
347                 manager = MissedManager()
348
349         gobject.threads_init()
350         gobject.idle_add(manager.start)
351
352         mainloop = gobject.MainLoop(is_running=True)
353         mainloop.run()