xref: /aosp_15_r20/external/autotest/client/cros/cellular/mmtest.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1# Lint as: python2, python3
2# Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
3# Use of this source code is governed by a BSD-style license that can be
4# found in the LICENSE file.
5
6import dbus, os, subprocess, time
7
8from autotest_lib.client.common_lib import error
9from autotest_lib.client.cros.cellular import cellular_logging
10from autotest_lib.client.cros.cellular import modem
11
12log = cellular_logging.SetupCellularLogging('mm_test')
13
14
15class ModemManagerTest(object):
16    """Wrapper for starting up ModemManager in an artificial testing
17    environment, connected to a fake modem program and talking to a
18    fake (tun) network device.
19
20    The test using this must ensure the setup of the fakegudev and
21    fakemodem deps.
22    """
23
24    def __init__(self, autodir, modem_pattern_files):
25        self.autodir=autodir # not great. Examine deps directly?
26        self.modem_pattern_files = modem_pattern_files
27        self.modemmanager = None
28        self.fakemodem_process = None
29        self.fakenet_process = None
30
31    def _start_fake_network(self):
32        """Start the fakenetwork program and return the fake interface name
33
34        Start up the fakenet program, which uses the tun driver to create
35        a network device.
36
37        Returns the name of the fake network interface.
38        Sets self.fakenet_process as a handle to the process.
39        """
40        self.fakenet_process = subprocess.Popen(
41            os.path.join(self.autodir,'deps/fakemodem/bin','fakenet'),
42            stdout=subprocess.PIPE)
43        return self.fakenet_process.stdout.readline().rstrip()
44
45
46    def _start_fake_modem(self, patternfiles):
47        """Start the fakemodem program and return the pty path to access it
48
49        Start up the fakemodem program
50        Argument:
51        patternfiles -- List of files to read for command/response patterns
52
53        Returns the device path of the pty that serves the fake modem, e.g.
54        /dev/pts/4.
55        Sets self.fakemodem_process as a handle to the process, and
56        self.fakemodem as a DBus interface to it.
57        """
58        scriptargs = ["--patternfile=" + x for x in patternfiles]
59        name = os.path.join(self.autodir, 'deps/fakemodem/bin', 'fakemodem')
60        self.fakemodem_process = subprocess.Popen(
61            [os.path.join(self.autodir, 'deps/fakemodem/bin', 'fakemodem')]
62            + scriptargs,
63            stdout=subprocess.PIPE)
64        ptyname = self.fakemodem_process.stdout.readline().rstrip()
65        time.sleep(2) # XXX
66        self.fakemodem = dbus.Interface(
67            dbus.SystemBus().get_object('org.chromium.FakeModem', '/'),
68            'org.chromium.FakeModem')
69        return ptyname
70
71
72    def _start_modemmanager(self, netname, modemname):
73        """Start modemmanager under the control of fake devices.
74
75        Arguments:
76        netname -- fake network interface name (e.g. tun0)
77        modemname -- path to pty node device of fake modem (e.g. /dev/pts/4)
78
79        Returns...
80
81        """
82        id_props = ['property_ID_MM_CANDIDATE=1',
83                    'property_ID_VENDOR_ID=04e8', # Samsung USB VID
84                    'property_ID_MODEL_ID=6872' # Y3300 modem PID
85                    ]
86        tty_device = (['device_file=%s' % (modemname),
87                       'name=%s' % (modemname[5:]), # remove leading /dev/
88                       'subsystem=tty',
89                       'driver=fake',
90                       'sysfs_path=/sys/devices/fake/tty',
91                       'parent=/dev/fake-parent'] +
92                      id_props)
93        net_device = (['device_file=/dev/fakenet',
94                       'name=%s' % (netname),
95                       'subsystem=net',
96                       'driver=fake',
97                       'sysfs_path=/sys/devices/fake/net',
98                       'parent=/dev/fake-parent'] +
99                      id_props)
100        parent_device=['device_file=/dev/fake-parent',
101                       'sysfs_path=/sys/devices/fake/parent',
102                       'devtype=usb_device',
103                       'subsystem=usb']
104        environment = { 'FAKEGUDEV_DEVICES' : ':'.join(tty_device +
105                                                       net_device +
106                                                       parent_device),
107                        'FAKEGUDEV_BLOCK_REAL' : 'true',
108                        'G_DEBUG' : 'fatal_criticals',
109                        'LD_PRELOAD' : os.path.join(self.autodir,
110                                                    "deps/fakegudev/lib",
111                                                    "libfakegudev.so") }
112        self.modemmanager = subprocess.Popen(['/usr/sbin/modem-manager',
113                                              '--debug',
114                                              '--log-level=DEBUG',
115                                              '--log-file=/tmp/mm-log'],
116                                             env=environment)
117        time.sleep(3) # wait for DeviceAdded signal?
118        self.modemmanager.poll()
119        if self.modemmanager.returncode is not None:
120            self.modemmanager = None
121            raise error.TestFail("ModemManager quit early")
122
123        # wait for MM to stabilize?
124        return modem.ModemManager(provider='org.freedesktop')
125
126    def _stop_fake_network(self):
127        if self.fakenet_process:
128            self.fakenet_process.poll()
129            if self.fakenet_process.returncode is None:
130                self.fakenet_process.terminate()
131                self.fakenet_process.wait()
132
133    def _stop_fake_modem(self):
134        if self.fakemodem_process:
135            self.fakemodem_process.poll()
136            if self.fakemodem_process.returncode is None:
137                self.fakemodem_process.terminate()
138                self.fakemodem_process.wait()
139
140    def _stop_modemmanager(self):
141        if self.modemmanager:
142            self.modemmanager.poll()
143            if self.modemmanager.returncode is None:
144                self.modemmanager.terminate()
145                self.modemmanager.wait()
146
147
148    def __enter__(self):
149        fakenetname = self._start_fake_network()
150        fakemodemname = self._start_fake_modem(self.modem_pattern_files)
151        self.mm = self._start_modemmanager(fakenetname, fakemodemname)
152        # This would be better handled by listening for DeviceAdded, but
153        # since we've blocked everything else and only supplied data for
154        # one modem, it's going to be right
155        self.modem_object_path = self.mm.path + '/Modems/0'
156        return self
157
158    def __exit__(self, exception, value, traceback):
159        self._stop_modemmanager()
160        self._stop_fake_modem()
161        self._stop_fake_network()
162        return False
163