blob: abf1de6ba0440527ddf0aa7f740009e09884cc0f [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
39def get(path):
40 global bus
41 return bus.get('org.ofono', path)
42
43def list_modems():
44 root = get('/')
45 return sorted(root.GetModems())
46
47
48class Modem(log.Origin):
49 'convenience for ofono Modem interaction'
50 msisdn = None
51
52 def __init__(self, conf):
53 self.conf = conf
54 self.path = conf.get('path')
55 self.set_name(self.path)
56 self.set_log_category(log.C_BUS)
57 self._dbus_obj = None
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020058 self._interfaces = set()
59 test.poll()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020060
61 def set_msisdn(self, msisdn):
62 self.msisdn = msisdn
63
64 def imsi(self):
Neels Hofmeyrb02c2112017-04-09 18:46:48 +020065 imsi = self.conf.get('imsi')
66 if not imsi:
67 with self:
68 raise RuntimeError('No IMSI')
69 return imsi
Neels Hofmeyr3531a192017-03-28 14:30:28 +020070
71 def ki(self):
72 return self.conf.get('ki')
73
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020074 def _dbus_set_bool(self, name, bool_val):
75 # to make sure any pending signals are received before we send out more DBus requests
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +020076 test.poll()
Neels Hofmeyr3531a192017-03-28 14:30:28 +020077
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020078 val = bool(bool_val)
79 self.log('Setting', name, val)
80 self.dbus_obj().SetProperty(name, Variant('b', val))
81
82 test.poll() # <-- probably not necessary
83
84 def set_powered(self, on=True):
85 self._dbus_set_bool('Powered', on)
86
Pau Espin Pedrolb9955762017-05-02 09:39:27 +020087 def set_online(self, on=True):
Neels Hofmeyr9dbcb822017-05-02 14:57:57 +020088 self._dbus_set_bool('Online', on)
Pau Espin Pedrolb9955762017-05-02 09:39:27 +020089
Neels Hofmeyr3531a192017-03-28 14:30:28 +020090 def dbus_obj(self):
91 if self._dbus_obj is not None:
92 return self._dbus_obj
93 self._dbus_obj = get(self.path)
94 self._dbus_obj.PropertyChanged.connect(self._on_property_change)
95 self._on_interfaces_change(self.properties().get('Interfaces'))
Neels Hofmeyrb02c2112017-04-09 18:46:48 +020096 return self._dbus_obj
Neels Hofmeyr3531a192017-03-28 14:30:28 +020097
98 def properties(self):
99 return self.dbus_obj().GetProperties()
100
101 def _on_property_change(self, name, value):
102 if name == 'Interfaces':
103 self._on_interfaces_change(value)
104
105 def _on_interfaces_change(self, interfaces_now):
106 now = set(interfaces_now)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200107 additions = now - self._interfaces
108 removals = self._interfaces - now
109 self._interfaces = now
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200110 for iface in removals:
111 with log.Origin('modem.disable(%s)' % iface):
112 try:
113 self._on_interface_disabled(iface)
114 except:
115 self.log_exn()
116 for iface in additions:
117 with log.Origin('modem.enable(%s)' % iface):
118 try:
119 self._on_interface_enabled(iface)
120 except:
121 self.log_exn()
122
123 def _on_interface_enabled(self, interface_name):
124 self.dbg('Interface enabled:', interface_name)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200125 if interface_name == I_SMS:
Neels Hofmeyrb02c2112017-04-09 18:46:48 +0200126 self.dbus_obj()[I_SMS].IncomingMessage.connect(self._on_incoming_message)
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200127
128 def _on_interface_disabled(self, interface_name):
129 self.dbg('Interface disabled:', interface_name)
130
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200131 def has_interface(self, name):
132 return name in self._interfaces
133
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200134 def connect(self, nitb):
135 'set the modem up to connect to MCC+MNC from NITB config'
136 self.log('connect to', nitb)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200137 self.set_powered()
Pau Espin Pedrolb9955762017-05-02 09:39:27 +0200138 self.set_online()
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200139 if not self.has_interface(I_NETREG):
140 self.log('No %r interface, hoping that the modem connects by itself' % I_NETREG)
141 else:
142 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 +0200143
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200144 def sms_send(self, to_msisdn):
145 if hasattr(to_msisdn, 'msisdn'):
146 to_msisdn = to_msisdn.msisdn
Neels Hofmeyr1ea59ea2017-05-02 14:41:54 +0200147 sms = Sms(self.msisdn(), to_msisdn)
148 self.log('sending sms to MSISDN', to_msisdn, sms=sms)
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200149 if not self.has_interface(I_SMS):
150 raise RuntimeError('Modem cannot send SMS, interface not active: %r' % I_SMS)
Neels Hofmeyrb02c2112017-04-09 18:46:48 +0200151 mm = self.dbus_obj()[I_SMS]
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200152 mm.SendMessage(to_msisdn, str(sms))
153 return sms
154
155 def _on_incoming_message(self, message, info):
156 self.log('Incoming SMS:', repr(message), **info)
157
Neels Hofmeyr863cb562017-05-02 16:27:59 +0200158 def sms_was_received(self, sms):
Neels Hofmeyrb3daaea2017-04-09 14:18:34 +0200159 pass
160
161class Sms:
162 _last_sms_idx = 0
163 msg = None
164
165 def __init__(self, from_msisdn=None, to_msisdn=None):
166 Sms._last_sms_idx += 1
167 msgs = ['message nr. %d' % Sms._last_sms_idx]
168 if from_msisdn or to_msisdn:
169 msgs.append(' sent')
170 if from_msisdn:
171 msgs.append(' from %s' % from_msisdn)
172 if to_msisdn:
173 msgs.append(' to %s' % to_msisdn)
174 self.msg = ''.join(msgs)
175
176 def __str__(self):
177 return self.msg
178
179 def __eq__(self, other):
180 if isinstance(other, Sms):
181 return self.msg == other.msg
182 return inself.msg == other
Neels Hofmeyr3531a192017-03-28 14:30:28 +0200183
184# vim: expandtab tabstop=4 shiftwidth=4