blob: 65a879136ff2ff4e2ad01b7e7bf144c5c672de26 [file] [log] [blame]
Neels Hofmeyr3531a192017-03-28 14:30:28 +02001# osmo_gsm_tester: DBUS client to talk to ofono
2#
3# Copyright (C) 2016-2017 by sysmocom - s.f.m.c. GmbH
4#
5# Author: Neels Hofmeyr <neels@hofmeyr.de>
6#
7# This program is free software: you can redistribute it and/or modify
8# it under the terms of the GNU Affero General Public License as
9# published by the Free Software Foundation, either version 3 of the
10# License, or (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU Affero General Public License for more details.
16#
17# You should have received a copy of the GNU Affero General Public License
18# along with this program. If not, see <http://www.gnu.org/licenses/>.
19
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020020from . import log, test
Neels Hofmeyr3531a192017-03-28 14:30:28 +020021
22from pydbus import SystemBus, Variant
23import time
24import pprint
25
26from gi.repository import GLib
27glib_main_loop = GLib.MainLoop()
28glib_main_ctx = glib_main_loop.get_context()
29bus = SystemBus()
30
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020031I_NETREG = 'org.ofono.NetworkRegistration'
32I_SMS = 'org.ofono.MessageManager'
33
Neels Hofmeyr3531a192017-03-28 14:30:28 +020034def poll():
35 global glib_main_ctx
36 while glib_main_ctx.pending():
37 glib_main_ctx.iteration()
38
Neels Hofmeyr93f58662017-05-03 16:32:16 +020039def systembus_get(path):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020040 global bus
41 return bus.get('org.ofono', path)
42
43def list_modems():
Neels Hofmeyr93f58662017-05-03 16:32:16 +020044 root = systembus_get('/')
Neels Hofmeyr3531a192017-03-28 14:30:28 +020045 return sorted(root.GetModems())
46
47
48class Modem(log.Origin):
49 'convenience for ofono Modem interaction'
50 msisdn = None
Neels Hofmeyrfec7d162017-05-02 16:29:09 +020051 sms_received_list = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020052
53 def __init__(self, conf):
54 self.conf = conf
55 self.path = conf.get('path')
56 self.set_name(self.path)
57 self.set_log_category(log.C_BUS)
58 self._dbus_obj = None
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020059 self._interfaces = set()
Neels Hofmeyrfec7d162017-05-02 16:29:09 +020060 self.sms_received_list = []
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020061 test.poll()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020062
63 def set_msisdn(self, msisdn):
64 self.msisdn = msisdn
65
66 def imsi(self):
Neels Hofmeyrb02c2112017-04-09 18:46:48 +020067 imsi = self.conf.get('imsi')
68 if not imsi:
69 with self:
70 raise RuntimeError('No IMSI')
71 return imsi
Neels Hofmeyr3531a192017-03-28 14:30:28 +020072
73 def ki(self):
74 return self.conf.get('ki')
75
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020076 def _dbus_set_bool(self, name, bool_val):
77 # to make sure any pending signals are received before we send out more DBus requests
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020078 test.poll()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020079
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020080 val = bool(bool_val)
81 self.log('Setting', name, val)
82 self.dbus_obj().SetProperty(name, Variant('b', val))
83
Neels Hofmeyr27d459c2017-05-02 16:30:18 +020084 test.wait(self.property_is, name, bool_val)
85
86 def property_is(self, name, val):
87 is_val = self.properties().get(name)
88 self.dbg(name, '==', is_val)
89 return is_val is not None and is_val == val
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020090
91 def set_powered(self, on=True):
92 self._dbus_set_bool('Powered', on)
93
Pau Espin Pedrolb9955762017-05-02 09:39:27 +020094 def set_online(self, on=True):
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020095 self._dbus_set_bool('Online', on)
Pau Espin Pedrolb9955762017-05-02 09:39:27 +020096
Neels Hofmeyr3531a192017-03-28 14:30:28 +020097 def dbus_obj(self):
98 if self._dbus_obj is not None:
99 return self._dbus_obj
Neels Hofmeyr93f58662017-05-03 16:32:16 +0200100 self._dbus_obj = systembus_get(self.path)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200101 self._dbus_obj.PropertyChanged.connect(self._on_property_change)
102 self._on_interfaces_change(self.properties().get('Interfaces'))
Neels Hofmeyrb02c2112017-04-09 18:46:48 +0200103 return self._dbus_obj
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200104
105 def properties(self):
106 return self.dbus_obj().GetProperties()
107
108 def _on_property_change(self, name, value):
109 if name == 'Interfaces':
110 self._on_interfaces_change(value)
111
112 def _on_interfaces_change(self, interfaces_now):
113 now = set(interfaces_now)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200114 additions = now - self._interfaces
115 removals = self._interfaces - now
116 self._interfaces = now
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200117 for iface in removals:
118 with log.Origin('modem.disable(%s)' % iface):
119 try:
120 self._on_interface_disabled(iface)
121 except:
122 self.log_exn()
123 for iface in additions:
124 with log.Origin('modem.enable(%s)' % iface):
125 try:
126 self._on_interface_enabled(iface)
127 except:
128 self.log_exn()
129
130 def _on_interface_enabled(self, interface_name):
131 self.dbg('Interface enabled:', interface_name)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200132 if interface_name == I_SMS:
Neels Hofmeyr943f8a22017-05-03 17:37:53 +0200133 retries = 3
Neels Hofmeyr5fe88812017-05-03 17:20:17 +0200134 while True:
Neels Hofmeyr5fe88812017-05-03 17:20:17 +0200135 try:
136 self.dbus_obj()[I_SMS].IncomingMessage.connect(self._on_incoming_message)
137 break
138 except:
139 self.dbg('Interface not yet available:', I_SMS)
140 retries -= 1
141 time.sleep(1)
142 if retries <= 0:
143 self.err('Interface enabled by signal, but not available:', I_SMS)
144 raise
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200145
146 def _on_interface_disabled(self, interface_name):
147 self.dbg('Interface disabled:', interface_name)
148
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200149 def has_interface(self, name):
150 return name in self._interfaces
151
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200152 def connect(self, nitb):
153 'set the modem up to connect to MCC+MNC from NITB config'
154 self.log('connect to', nitb)
Neels Hofmeyr27d459c2017-05-02 16:30:18 +0200155 self.set_powered(False)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200156 self.set_powered()
Neels Hofmeyr27d459c2017-05-02 16:30:18 +0200157 self.set_online(False)
Pau Espin Pedrolb9955762017-05-02 09:39:27 +0200158 self.set_online()
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200159 if not self.has_interface(I_NETREG):
160 self.log('No %r interface, hoping that the modem connects by itself' % I_NETREG)
161 else:
162 self.log('Use of %r interface not implemented yet, hoping that the modem connects by itself' % I_NETREG)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200163
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200164 def sms_send(self, to_msisdn):
165 if hasattr(to_msisdn, 'msisdn'):
166 to_msisdn = to_msisdn.msisdn
Neels Hofmeyr24bfcee2017-05-02 16:30:49 +0200167 sms = Sms(self.msisdn, to_msisdn)
Neels Hofmeyr1ea59ea2017-05-02 14:41:54 +0200168 self.log('sending sms to MSISDN', to_msisdn, sms=sms)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200169 if not self.has_interface(I_SMS):
170 raise RuntimeError('Modem cannot send SMS, interface not active: %r' % I_SMS)
Neels Hofmeyrb02c2112017-04-09 18:46:48 +0200171 mm = self.dbus_obj()[I_SMS]
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200172 mm.SendMessage(to_msisdn, str(sms))
173 return sms
174
175 def _on_incoming_message(self, message, info):
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200176 self.log('Incoming SMS:', repr(message), info=info)
177 self.sms_received_list.append((message, info))
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200178
Neels Hofmeyr863cb562017-05-02 16:27:59 +0200179 def sms_was_received(self, sms):
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200180 for msg, info in self.sms_received_list:
181 if sms.matches(msg):
182 self.log('SMS received as expected:', msg=msg, info=info)
183 return True
184 return False
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200185
186class Sms:
187 _last_sms_idx = 0
188 msg = None
189
190 def __init__(self, from_msisdn=None, to_msisdn=None):
191 Sms._last_sms_idx += 1
192 msgs = ['message nr. %d' % Sms._last_sms_idx]
193 if from_msisdn or to_msisdn:
194 msgs.append(' sent')
195 if from_msisdn:
196 msgs.append(' from %s' % from_msisdn)
197 if to_msisdn:
198 msgs.append(' to %s' % to_msisdn)
199 self.msg = ''.join(msgs)
200
201 def __str__(self):
202 return self.msg
203
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200204 def __repr__(self):
205 return repr(self.msg)
206
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200207 def __eq__(self, other):
208 if isinstance(other, Sms):
209 return self.msg == other.msg
210 return inself.msg == other
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200211
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200212 def matches(self, msg):
213 return self.msg == msg
214
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200215# vim: expandtab tabstop=4 shiftwidth=4