blob: b6c562881b0e3830e9eb9182681e836f0af31c4d [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
Pau Espin Pedrol504a6642017-05-04 11:38:23 +020031I_MODEM = 'org.ofono.Modem'
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020032I_NETREG = 'org.ofono.NetworkRegistration'
33I_SMS = 'org.ofono.MessageManager'
34
Neels Hofmeyr3531a192017-03-28 14:30:28 +020035def poll():
36 global glib_main_ctx
37 while glib_main_ctx.pending():
38 glib_main_ctx.iteration()
39
Neels Hofmeyr93f58662017-05-03 16:32:16 +020040def systembus_get(path):
Neels Hofmeyr3531a192017-03-28 14:30:28 +020041 global bus
42 return bus.get('org.ofono', path)
43
44def list_modems():
Neels Hofmeyr93f58662017-05-03 16:32:16 +020045 root = systembus_get('/')
Neels Hofmeyr3531a192017-03-28 14:30:28 +020046 return sorted(root.GetModems())
47
48
49class Modem(log.Origin):
50 'convenience for ofono Modem interaction'
51 msisdn = None
Neels Hofmeyrfec7d162017-05-02 16:29:09 +020052 sms_received_list = None
Neels Hofmeyr3531a192017-03-28 14:30:28 +020053
54 def __init__(self, conf):
55 self.conf = conf
56 self.path = conf.get('path')
57 self.set_name(self.path)
58 self.set_log_category(log.C_BUS)
59 self._dbus_obj = None
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020060 self._interfaces = set()
Neels Hofmeyrfec7d162017-05-02 16:29:09 +020061 self.sms_received_list = []
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020062 test.poll()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020063
64 def set_msisdn(self, msisdn):
65 self.msisdn = msisdn
66
67 def imsi(self):
Neels Hofmeyrb02c2112017-04-09 18:46:48 +020068 imsi = self.conf.get('imsi')
69 if not imsi:
70 with self:
71 raise RuntimeError('No IMSI')
72 return imsi
Neels Hofmeyr3531a192017-03-28 14:30:28 +020073
74 def ki(self):
75 return self.conf.get('ki')
76
Pau Espin Pedrol504a6642017-05-04 11:38:23 +020077 def _dbus_set_bool(self, name, bool_val, iface=I_MODEM):
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020078 # to make sure any pending signals are received before we send out more DBus requests
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020079 test.poll()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020080
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020081 val = bool(bool_val)
82 self.log('Setting', name, val)
Pau Espin Pedrol504a6642017-05-04 11:38:23 +020083 self.dbus_obj()[iface].SetProperty(name, Variant('b', val))
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020084
Neels Hofmeyr27d459c2017-05-02 16:30:18 +020085 test.wait(self.property_is, name, bool_val)
86
87 def property_is(self, name, val):
88 is_val = self.properties().get(name)
89 self.dbg(name, '==', is_val)
90 return is_val is not None and is_val == val
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020091
92 def set_powered(self, on=True):
93 self._dbus_set_bool('Powered', on)
94
Pau Espin Pedrolb9955762017-05-02 09:39:27 +020095 def set_online(self, on=True):
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020096 self._dbus_set_bool('Online', on)
Pau Espin Pedrolb9955762017-05-02 09:39:27 +020097
Neels Hofmeyr3531a192017-03-28 14:30:28 +020098 def dbus_obj(self):
99 if self._dbus_obj is not None:
100 return self._dbus_obj
Neels Hofmeyr93f58662017-05-03 16:32:16 +0200101 self._dbus_obj = systembus_get(self.path)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200102 self._dbus_obj.PropertyChanged.connect(self._on_property_change)
103 self._on_interfaces_change(self.properties().get('Interfaces'))
Neels Hofmeyrb02c2112017-04-09 18:46:48 +0200104 return self._dbus_obj
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200105
Pau Espin Pedrol504a6642017-05-04 11:38:23 +0200106 def properties(self, iface=I_MODEM):
107 return self.dbus_obj()[iface].GetProperties()
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200108
109 def _on_property_change(self, name, value):
110 if name == 'Interfaces':
111 self._on_interfaces_change(value)
112
113 def _on_interfaces_change(self, interfaces_now):
114 now = set(interfaces_now)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200115 additions = now - self._interfaces
116 removals = self._interfaces - now
117 self._interfaces = now
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200118 for iface in removals:
119 with log.Origin('modem.disable(%s)' % iface):
120 try:
121 self._on_interface_disabled(iface)
122 except:
123 self.log_exn()
124 for iface in additions:
125 with log.Origin('modem.enable(%s)' % iface):
126 try:
127 self._on_interface_enabled(iface)
128 except:
129 self.log_exn()
130
131 def _on_interface_enabled(self, interface_name):
132 self.dbg('Interface enabled:', interface_name)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200133 if interface_name == I_SMS:
Neels Hofmeyr943f8a22017-05-03 17:37:53 +0200134 retries = 3
Neels Hofmeyr5fe88812017-05-03 17:20:17 +0200135 while True:
Neels Hofmeyr5fe88812017-05-03 17:20:17 +0200136 try:
137 self.dbus_obj()[I_SMS].IncomingMessage.connect(self._on_incoming_message)
138 break
139 except:
140 self.dbg('Interface not yet available:', I_SMS)
141 retries -= 1
142 time.sleep(1)
143 if retries <= 0:
144 self.err('Interface enabled by signal, but not available:', I_SMS)
145 raise
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200146
147 def _on_interface_disabled(self, interface_name):
148 self.dbg('Interface disabled:', interface_name)
149
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200150 def has_interface(self, name):
151 return name in self._interfaces
152
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200153 def connect(self, nitb):
154 'set the modem up to connect to MCC+MNC from NITB config'
155 self.log('connect to', nitb)
Neels Hofmeyr27d459c2017-05-02 16:30:18 +0200156 self.set_powered(False)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200157 self.set_powered()
Neels Hofmeyr27d459c2017-05-02 16:30:18 +0200158 self.set_online(False)
Pau Espin Pedrolb9955762017-05-02 09:39:27 +0200159 self.set_online()
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200160 if not self.has_interface(I_NETREG):
161 self.log('No %r interface, hoping that the modem connects by itself' % I_NETREG)
162 else:
163 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 +0200164
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200165 def sms_send(self, to_msisdn):
166 if hasattr(to_msisdn, 'msisdn'):
167 to_msisdn = to_msisdn.msisdn
Neels Hofmeyr24bfcee2017-05-02 16:30:49 +0200168 sms = Sms(self.msisdn, to_msisdn)
Neels Hofmeyr1ea59ea2017-05-02 14:41:54 +0200169 self.log('sending sms to MSISDN', to_msisdn, sms=sms)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200170 if not self.has_interface(I_SMS):
171 raise RuntimeError('Modem cannot send SMS, interface not active: %r' % I_SMS)
Neels Hofmeyrb02c2112017-04-09 18:46:48 +0200172 mm = self.dbus_obj()[I_SMS]
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200173 mm.SendMessage(to_msisdn, str(sms))
174 return sms
175
176 def _on_incoming_message(self, message, info):
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200177 self.log('Incoming SMS:', repr(message), info=info)
178 self.sms_received_list.append((message, info))
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200179
Neels Hofmeyr863cb562017-05-02 16:27:59 +0200180 def sms_was_received(self, sms):
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200181 for msg, info in self.sms_received_list:
182 if sms.matches(msg):
183 self.log('SMS received as expected:', msg=msg, info=info)
184 return True
185 return False
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200186
187class Sms:
188 _last_sms_idx = 0
189 msg = None
190
191 def __init__(self, from_msisdn=None, to_msisdn=None):
192 Sms._last_sms_idx += 1
193 msgs = ['message nr. %d' % Sms._last_sms_idx]
194 if from_msisdn or to_msisdn:
195 msgs.append(' sent')
196 if from_msisdn:
197 msgs.append(' from %s' % from_msisdn)
198 if to_msisdn:
199 msgs.append(' to %s' % to_msisdn)
200 self.msg = ''.join(msgs)
201
202 def __str__(self):
203 return self.msg
204
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200205 def __repr__(self):
206 return repr(self.msg)
207
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200208 def __eq__(self, other):
209 if isinstance(other, Sms):
210 return self.msg == other.msg
211 return inself.msg == other
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200212
Neels Hofmeyrfec7d162017-05-02 16:29:09 +0200213 def matches(self, msg):
214 return self.msg == msg
215
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200216# vim: expandtab tabstop=4 shiftwidth=4