xref: /aosp_15_r20/external/autotest/client/cros/cellular/test_environment.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
3*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
4*9c5db199SXin Li# found in the LICENSE file.
5*9c5db199SXin Li
6*9c5db199SXin Liimport contextlib
7*9c5db199SXin Liimport dbus
8*9c5db199SXin Liimport logging
9*9c5db199SXin Liimport sys
10*9c5db199SXin Liimport time
11*9c5db199SXin Liimport traceback
12*9c5db199SXin Li
13*9c5db199SXin Liimport common
14*9c5db199SXin Lifrom autotest_lib.client.bin import local_host
15*9c5db199SXin Lifrom autotest_lib.client.bin import utils
16*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
17*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import crash_detector
18*9c5db199SXin Lifrom autotest_lib.client.cros import upstart
19*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import mm
20*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import mm1_constants
21*9c5db199SXin Lifrom autotest_lib.client.cros.networking import cellular_proxy
22*9c5db199SXin Lifrom autotest_lib.client.cros.networking import mm1_proxy
23*9c5db199SXin Lifrom autotest_lib.client.cros.networking import shill_context
24*9c5db199SXin Lifrom autotest_lib.client.cros.networking import shill_proxy
25*9c5db199SXin Li
26*9c5db199SXin Li
27*9c5db199SXin Liclass CellularTestEnvironment(object):
28*9c5db199SXin Li    """Setup and verify cellular test environment.
29*9c5db199SXin Li
30*9c5db199SXin Li    This context manager configures the following:
31*9c5db199SXin Li        - Shuts down other devices except cellular.
32*9c5db199SXin Li        - Shill and MM logging is enabled appropriately for cellular.
33*9c5db199SXin Li        - Initializes members that tests should use to access test environment
34*9c5db199SXin Li          (eg. |shill|, |modem_manager|, |modem|).
35*9c5db199SXin Li        - modemfwd is stopped to prevent the modem from rebooting underneath
36*9c5db199SXin Li          us.
37*9c5db199SXin Li
38*9c5db199SXin Li    Then it verifies the following is valid:
39*9c5db199SXin Li        - The SIM is inserted and valid.
40*9c5db199SXin Li        - There is one and only one modem in the device.
41*9c5db199SXin Li        - The modem is registered to the network.
42*9c5db199SXin Li        - There is a cellular service in shill and it's not connected.
43*9c5db199SXin Li
44*9c5db199SXin Li    Don't use this base class directly, use the appropriate subclass.
45*9c5db199SXin Li
46*9c5db199SXin Li    Setup for over-the-air tests:
47*9c5db199SXin Li        with CellularOTATestEnvironment() as test_env:
48*9c5db199SXin Li            # Test body
49*9c5db199SXin Li
50*9c5db199SXin Li    Setup for pseudomodem tests:
51*9c5db199SXin Li        with CellularPseudoMMTestEnvironment(
52*9c5db199SXin Li                pseudomm_args=({'family': '3GPP'})) as test_env:
53*9c5db199SXin Li            # Test body
54*9c5db199SXin Li
55*9c5db199SXin Li    """
56*9c5db199SXin Li
57*9c5db199SXin Li    def __init__(self,
58*9c5db199SXin Li                 shutdown_other_devices=True,
59*9c5db199SXin Li                 modem_pattern='',
60*9c5db199SXin Li                 skip_modem_reset=False,
61*9c5db199SXin Li                 is_esim_test=False,
62*9c5db199SXin Li                 enable_temp_containments=True):
63*9c5db199SXin Li        """
64*9c5db199SXin Li        @param shutdown_other_devices: If True, shutdown all devices except
65*9c5db199SXin Li                cellular.
66*9c5db199SXin Li        @param modem_pattern: Search string used when looking for the modem.
67*9c5db199SXin Li        @param enable_temp_containments: Enable temporary containments to avoid
68*9c5db199SXin Li                failures on tests with known problems.
69*9c5db199SXin Li
70*9c5db199SXin Li        """
71*9c5db199SXin Li        # Tests should use this main loop instead of creating their own.
72*9c5db199SXin Li        self.mainloop = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
73*9c5db199SXin Li        self.bus = dbus.SystemBus(mainloop=self.mainloop)
74*9c5db199SXin Li
75*9c5db199SXin Li        self.shill = None
76*9c5db199SXin Li        self.modem_manager = None
77*9c5db199SXin Li        self.modem = None
78*9c5db199SXin Li        self.modem_path = None
79*9c5db199SXin Li
80*9c5db199SXin Li        self._modem_pattern = modem_pattern
81*9c5db199SXin Li        self._skip_modem_reset = skip_modem_reset
82*9c5db199SXin Li        self._is_esim_test = is_esim_test
83*9c5db199SXin Li        self._enable_temp_containments = enable_temp_containments
84*9c5db199SXin Li        self._system_service_order = ''
85*9c5db199SXin Li        self._test_service_order = 'cellular,ethernet'
86*9c5db199SXin Li
87*9c5db199SXin Li        self._nested = None
88*9c5db199SXin Li        self._context_managers = []
89*9c5db199SXin Li        self.detect_crash = crash_detector.CrashDetector(
90*9c5db199SXin Li                local_host.LocalHost())
91*9c5db199SXin Li        self.detect_crash.remove_crash_files()
92*9c5db199SXin Li        if shutdown_other_devices:
93*9c5db199SXin Li            self._context_managers.append(
94*9c5db199SXin Li                    shill_context.AllowedTechnologiesContext([
95*9c5db199SXin Li                            shill_proxy.ShillProxy.TECHNOLOGY_CELLULAR,
96*9c5db199SXin Li                            shill_proxy.ShillProxy.TECHNOLOGY_ETHERNET
97*9c5db199SXin Li                    ]))
98*9c5db199SXin Li
99*9c5db199SXin Li    @contextlib.contextmanager
100*9c5db199SXin Li    def _disable_shill_autoconnect(self):
101*9c5db199SXin Li        self._enable_shill_cellular_autoconnect(False)
102*9c5db199SXin Li        yield
103*9c5db199SXin Li        self._enable_shill_cellular_autoconnect(True)
104*9c5db199SXin Li
105*9c5db199SXin Li    def __enter__(self):
106*9c5db199SXin Li        try:
107*9c5db199SXin Li            # Wait for system daemons to stabilize before beginning the test.
108*9c5db199SXin Li            # Modemfwd, Chrome, Shill and Hermes might be active before the test
109*9c5db199SXin Li            # begins, and interrupting them abruptly during test setup might
110*9c5db199SXin Li            # lead to flaky tests. The modem might also appear/disappear
111*9c5db199SXin Li            # multiple times during this period. Ideally, we would wait for a
112*9c5db199SXin Li            # green signal from these daemons before performing test setup.
113*9c5db199SXin Li            with open('/proc/uptime') as uptime_file:
114*9c5db199SXin Li                uptime = float(uptime_file.readline().split()[0])
115*9c5db199SXin Li            if uptime < 60:
116*9c5db199SXin Li                logging.info(
117*9c5db199SXin Li                        "Waiting %.1f seconds to reach uptime of 1 minute before "
118*9c5db199SXin Li                        "starting test", 60 - uptime)
119*9c5db199SXin Li                time.sleep(60 - uptime)
120*9c5db199SXin Li
121*9c5db199SXin Li            if upstart.has_service('modemfwd') and upstart.is_running('modemfwd'):
122*9c5db199SXin Li                # Due to b/179796133, stopping modemfwd right after it was
123*9c5db199SXin Li                # started by a previous test, can wedge the modem. In many
124*9c5db199SXin Li                # devices, a ~1 second delay solves the problem.
125*9c5db199SXin Li                time.sleep(4)
126*9c5db199SXin Li                upstart.stop_job('modemfwd')
127*9c5db199SXin Li            # Temporarily disable shill autoconnect to cellular service while
128*9c5db199SXin Li            # the test environment is setup to prevent a race condition
129*9c5db199SXin Li            # between disconnecting the modem in _verify_cellular_service()
130*9c5db199SXin Li            # and shill autoconnect.
131*9c5db199SXin Li            with self._disable_shill_autoconnect():
132*9c5db199SXin Li                try:
133*9c5db199SXin Li                    from contextlib import nested # Python 2
134*9c5db199SXin Li                except ImportError:
135*9c5db199SXin Li                    from contextlib import ExitStack, contextmanager
136*9c5db199SXin Li
137*9c5db199SXin Li                    @contextmanager
138*9c5db199SXin Li                    def nested(*contexts):
139*9c5db199SXin Li                        """ Implementation of nested for python3"""
140*9c5db199SXin Li                        with ExitStack() as stack:
141*9c5db199SXin Li                            for ctx in contexts:
142*9c5db199SXin Li                                stack.enter_context(ctx)
143*9c5db199SXin Li                            yield contexts
144*9c5db199SXin Li
145*9c5db199SXin Li                self._nested = nested(*self._context_managers)
146*9c5db199SXin Li
147*9c5db199SXin Li                self._nested.__enter__()
148*9c5db199SXin Li
149*9c5db199SXin Li                self._initialize_shill()
150*9c5db199SXin Li
151*9c5db199SXin Li                # Perform SIM verification now to ensure that we can enable the
152*9c5db199SXin Li                # modem in _initialize_modem_components(). ModemManager does not
153*9c5db199SXin Li                # allow enabling a modem without a SIM.
154*9c5db199SXin Li                self._verify_sim()
155*9c5db199SXin Li                self._initialize_modem_components()
156*9c5db199SXin Li
157*9c5db199SXin Li                self._setup_logging()
158*9c5db199SXin Li
159*9c5db199SXin Li                if not self._is_esim_test:
160*9c5db199SXin Li                    self._wait_for_modem_registration()
161*9c5db199SXin Li                self._verify_cellular_service()
162*9c5db199SXin Li
163*9c5db199SXin Li                return self
164*9c5db199SXin Li        except (error.TestError, dbus.DBusException,
165*9c5db199SXin Li                shill_proxy.ShillProxyError) as e:
166*9c5db199SXin Li            except_type, except_value, except_traceback = sys.exc_info()
167*9c5db199SXin Li            lines = traceback.format_exception(except_type, except_value,
168*9c5db199SXin Li                                               except_traceback)
169*9c5db199SXin Li            logging.error('Error during test initialization:\n%s',
170*9c5db199SXin Li                          ''.join(lines))
171*9c5db199SXin Li            self.__exit__(*sys.exc_info())
172*9c5db199SXin Li            raise error.TestError('INIT_ERROR: %s' % str(e))
173*9c5db199SXin Li        except:
174*9c5db199SXin Li            self.__exit__(*sys.exc_info())
175*9c5db199SXin Li            raise
176*9c5db199SXin Li
177*9c5db199SXin Li    def __exit__(self, exception, value, traceback):
178*9c5db199SXin Li        exception_on_restore_state = None
179*9c5db199SXin Li        try:
180*9c5db199SXin Li            self._restore_state()
181*9c5db199SXin Li        except Exception as ex:
182*9c5db199SXin Li            # Exceptions thrown by _restore_state() should be ignored if a
183*9c5db199SXin Li            # previous exception exist, otherwise the root cause of the test
184*9c5db199SXin Li            # failure will be overwritten by the clean up error in
185*9c5db199SXin Li            # _restore_state, and that is not useful.
186*9c5db199SXin Li            if exception is None:
187*9c5db199SXin Li                exception_on_restore_state = ex
188*9c5db199SXin Li
189*9c5db199SXin Li        # If a test fails and a crash is detected, the crash error takes
190*9c5db199SXin Li        # priority over the previous failure.
191*9c5db199SXin Li        crash_files = self.detect_crash.get_new_crash_files()
192*9c5db199SXin Li        if any(cf for cf in crash_files if any(pr in cf for pr in [
193*9c5db199SXin Li                'ModemManager', 'shill', 'qmi', 'mbim', 'hermes', 'modemfwd'
194*9c5db199SXin Li        ])):
195*9c5db199SXin Li            logging.info(
196*9c5db199SXin Li                    'A crash was encountered. '
197*9c5db199SXin Li                    'Overriding the previous error: %s', value)
198*9c5db199SXin Li            raise error.TestError(
199*9c5db199SXin Li                    'One or more daemon crashes were detected. '
200*9c5db199SXin Li                    'See crash dumps: {}'.format(crash_files))
201*9c5db199SXin Li
202*9c5db199SXin Li        if exception_on_restore_state is not None:
203*9c5db199SXin Li            raise exception_on_restore_state
204*9c5db199SXin Li
205*9c5db199SXin Li        if self._nested:
206*9c5db199SXin Li            return self._nested.__exit__(exception, value, traceback)
207*9c5db199SXin Li        self.shill = None
208*9c5db199SXin Li        self.modem_manager = None
209*9c5db199SXin Li        self.modem = None
210*9c5db199SXin Li        self.modem_path = None
211*9c5db199SXin Li
212*9c5db199SXin Li    def _restore_state(self):
213*9c5db199SXin Li        """Try to restore the test environment to a good state.
214*9c5db199SXin Li        """
215*9c5db199SXin Li        if upstart.has_service('modemfwd'):
216*9c5db199SXin Li            upstart.restart_job('modemfwd')
217*9c5db199SXin Li        if self.shill:
218*9c5db199SXin Li            self._set_service_order(self._system_service_order)
219*9c5db199SXin Li
220*9c5db199SXin Li    def _get_shill_cellular_device_object(self):
221*9c5db199SXin Li        return utils.poll_for_condition(
222*9c5db199SXin Li            lambda: self.shill.find_cellular_device_object(),
223*9c5db199SXin Li            exception=error.TestError('Cannot find cellular device in shill. '
224*9c5db199SXin Li                                      'Is the modem plugged in?'),
225*9c5db199SXin Li            timeout=shill_proxy.ShillProxy.DEVICE_ENUMERATION_TIMEOUT)
226*9c5db199SXin Li
227*9c5db199SXin Li    def _get_service_order(self):
228*9c5db199SXin Li        """Get the shill service order.
229*9c5db199SXin Li
230*9c5db199SXin Li        @return string service order on success, None otherwise.
231*9c5db199SXin Li
232*9c5db199SXin Li        """
233*9c5db199SXin Li        return str(self.shill.manager.GetServiceOrder())
234*9c5db199SXin Li
235*9c5db199SXin Li    def _set_service_order(self, order):
236*9c5db199SXin Li        """Set the shill service order.
237*9c5db199SXin Li
238*9c5db199SXin Li        @param order string comma-delimited service order
239*9c5db199SXin Li        (eg. 'cellular,ethernet')
240*9c5db199SXin Li        @return bool True on success, False otherwise.
241*9c5db199SXin Li
242*9c5db199SXin Li        """
243*9c5db199SXin Li        self.shill.manager.SetServiceOrder(dbus.String(order))
244*9c5db199SXin Li        return True
245*9c5db199SXin Li
246*9c5db199SXin Li    def _enable_modem(self):
247*9c5db199SXin Li        modem_device = self._get_shill_cellular_device_object()
248*9c5db199SXin Li        try:
249*9c5db199SXin Li            modem_device.Enable()
250*9c5db199SXin Li        except dbus.DBusException as e:
251*9c5db199SXin Li            if (e.get_dbus_name() !=
252*9c5db199SXin Li                    shill_proxy.ShillProxy.ERROR_IN_PROGRESS):
253*9c5db199SXin Li                raise
254*9c5db199SXin Li
255*9c5db199SXin Li        utils.poll_for_condition(
256*9c5db199SXin Li            lambda: modem_device.GetProperties()['Powered'],
257*9c5db199SXin Li            exception=error.TestError(
258*9c5db199SXin Li                'Failed to enable modem.'),
259*9c5db199SXin Li            timeout=shill_proxy.ShillProxy.DEVICE_ENABLE_DISABLE_TIMEOUT)
260*9c5db199SXin Li
261*9c5db199SXin Li    def _enable_shill_cellular_autoconnect(self, enable):
262*9c5db199SXin Li        shill = cellular_proxy.CellularProxy.get_proxy(self.bus)
263*9c5db199SXin Li        shill.manager.SetProperty(
264*9c5db199SXin Li            shill_proxy.ShillProxy.
265*9c5db199SXin Li            MANAGER_PROPERTY_NO_AUTOCONNECT_TECHNOLOGIES,
266*9c5db199SXin Li            '' if enable else 'cellular')
267*9c5db199SXin Li
268*9c5db199SXin Li    def _is_unsupported_error(self, e):
269*9c5db199SXin Li        return (e.get_dbus_name() ==
270*9c5db199SXin Li                shill_proxy.ShillProxy.ERROR_NOT_SUPPORTED or
271*9c5db199SXin Li                (e.get_dbus_name() ==
272*9c5db199SXin Li                 shill_proxy.ShillProxy.ERROR_FAILURE and
273*9c5db199SXin Li                 'operation not supported' in e.get_dbus_message()))
274*9c5db199SXin Li
275*9c5db199SXin Li    def _reset_modem(self):
276*9c5db199SXin Li        modem_device = self._get_shill_cellular_device_object()
277*9c5db199SXin Li        try:
278*9c5db199SXin Li            # MBIM modems do not support being reset.
279*9c5db199SXin Li            self.shill.reset_modem(modem_device, expect_service=False)
280*9c5db199SXin Li        except dbus.DBusException as e:
281*9c5db199SXin Li            if not self._is_unsupported_error(e):
282*9c5db199SXin Li                raise
283*9c5db199SXin Li
284*9c5db199SXin Li    def _initialize_shill(self):
285*9c5db199SXin Li        """Get access to shill."""
286*9c5db199SXin Li        # CellularProxy.get_proxy() checks to see if shill is running and
287*9c5db199SXin Li        # responding to DBus requests. It returns None if that's not the case.
288*9c5db199SXin Li        self.shill = cellular_proxy.CellularProxy.get_proxy(self.bus)
289*9c5db199SXin Li        if self.shill is None:
290*9c5db199SXin Li            raise error.TestError('Cannot connect to shill, is shill running?')
291*9c5db199SXin Li
292*9c5db199SXin Li        self._system_service_order = self._get_service_order()
293*9c5db199SXin Li        self._set_service_order(self._test_service_order)
294*9c5db199SXin Li
295*9c5db199SXin Li    def _initialize_modem_components(self):
296*9c5db199SXin Li        """Reset the modem and get access to modem components."""
297*9c5db199SXin Li        # Enable modem first so shill initializes the modemmanager proxies so
298*9c5db199SXin Li        # we can call reset on it.
299*9c5db199SXin Li        self._enable_modem()
300*9c5db199SXin Li        if not self._skip_modem_reset:
301*9c5db199SXin Li            self._reset_modem()
302*9c5db199SXin Li
303*9c5db199SXin Li        # PickOneModem() makes sure there's a modem manager and that there is
304*9c5db199SXin Li        # one and only one modem.
305*9c5db199SXin Li        self.modem_manager, self.modem_path = \
306*9c5db199SXin Li            mm.PickOneModem(self._modem_pattern)
307*9c5db199SXin Li        self.modem = self.modem_manager.GetModem(self.modem_path)
308*9c5db199SXin Li        if self.modem is None:
309*9c5db199SXin Li            raise error.TestError('Cannot get modem object at %s.' %
310*9c5db199SXin Li                                  self.modem_path)
311*9c5db199SXin Li
312*9c5db199SXin Li    def _setup_logging(self):
313*9c5db199SXin Li        self.shill.set_logging_for_cellular_test()
314*9c5db199SXin Li        self.modem_manager.SetDebugLogging()
315*9c5db199SXin Li
316*9c5db199SXin Li    def _verify_sim(self):
317*9c5db199SXin Li        """Verify SIM is valid.
318*9c5db199SXin Li
319*9c5db199SXin Li        Make sure a SIM in inserted and that it is not locked.
320*9c5db199SXin Li
321*9c5db199SXin Li        @raise error.TestError if SIM does not exist or is locked.
322*9c5db199SXin Li
323*9c5db199SXin Li        """
324*9c5db199SXin Li        # check modem SIM slot and properties and switch slot as needed
325*9c5db199SXin Li        modem_proxy = self._check_for_modem_with_sim()
326*9c5db199SXin Li        if modem_proxy is None:
327*9c5db199SXin Li            raise error.TestError('There is no Modem with non empty SIM path.')
328*9c5db199SXin Li
329*9c5db199SXin Li        modem_device = self._get_shill_cellular_device_object()
330*9c5db199SXin Li        props = modem_device.GetProperties()
331*9c5db199SXin Li
332*9c5db199SXin Li        # No SIM in CDMA modems.
333*9c5db199SXin Li        family = props[
334*9c5db199SXin Li            cellular_proxy.CellularProxy.DEVICE_PROPERTY_TECHNOLOGY_FAMILY]
335*9c5db199SXin Li        if (family ==
336*9c5db199SXin Li                cellular_proxy.CellularProxy.
337*9c5db199SXin Li                DEVICE_PROPERTY_TECHNOLOGY_FAMILY_CDMA):
338*9c5db199SXin Li            return
339*9c5db199SXin Li
340*9c5db199SXin Li        # Make sure there is a SIM.
341*9c5db199SXin Li        if not props[cellular_proxy.CellularProxy.DEVICE_PROPERTY_SIM_PRESENT]:
342*9c5db199SXin Li            raise error.TestError('There is no SIM in the modem.')
343*9c5db199SXin Li
344*9c5db199SXin Li        # Make sure SIM is not locked.
345*9c5db199SXin Li        lock_status = props.get(
346*9c5db199SXin Li            cellular_proxy.CellularProxy.DEVICE_PROPERTY_SIM_LOCK_STATUS,
347*9c5db199SXin Li            None)
348*9c5db199SXin Li        if lock_status is None:
349*9c5db199SXin Li            raise error.TestError('Failed to read SIM lock status.')
350*9c5db199SXin Li        locked = lock_status.get(
351*9c5db199SXin Li            cellular_proxy.CellularProxy.PROPERTY_KEY_SIM_LOCK_ENABLED,
352*9c5db199SXin Li            None)
353*9c5db199SXin Li        if locked is None:
354*9c5db199SXin Li            raise error.TestError('Failed to read SIM LockEnabled status.')
355*9c5db199SXin Li        elif locked:
356*9c5db199SXin Li            raise error.TestError(
357*9c5db199SXin Li                'SIM is locked, test requires an unlocked SIM.')
358*9c5db199SXin Li
359*9c5db199SXin Li    def _check_for_modem_with_sim(self):
360*9c5db199SXin Li        """
361*9c5db199SXin Li        Make sure modem got active SIM and path is not empty
362*9c5db199SXin Li
363*9c5db199SXin Li        switch slot to get non empty sim path and active sim slot for modem
364*9c5db199SXin Li
365*9c5db199SXin Li        @return active modem object or None
366*9c5db199SXin Li
367*9c5db199SXin Li        """
368*9c5db199SXin Li        mm_proxy = mm1_proxy.ModemManager1Proxy.get_proxy()
369*9c5db199SXin Li        if mm_proxy is None:
370*9c5db199SXin Li            raise error.TestError('Modem manager is not initialized')
371*9c5db199SXin Li
372*9c5db199SXin Li        modem_proxy = mm_proxy.wait_for_modem(mm1_constants.MM_MODEM_POLL_TIME)
373*9c5db199SXin Li        if modem_proxy is None:
374*9c5db199SXin Li            raise error.TestError('Modem not initialized')
375*9c5db199SXin Li
376*9c5db199SXin Li        primary_slot = modem_proxy.get_primary_sim_slot()
377*9c5db199SXin Li        # Get SIM path from modem SIM properties
378*9c5db199SXin Li        modem_props = modem_proxy.properties(mm1_constants.I_MODEM)
379*9c5db199SXin Li        sim_path = modem_props['Sim']
380*9c5db199SXin Li
381*9c5db199SXin Li        logging.info('Device SIM values=> path:%s '
382*9c5db199SXin Li                'primary slot:%d', sim_path, primary_slot)
383*9c5db199SXin Li
384*9c5db199SXin Li        def is_usable_sim(path):
385*9c5db199SXin Li            """Check if sim at path can be used to establish a connection"""
386*9c5db199SXin Li            if path == mm1_constants.MM_EMPTY_SLOT_PATH:
387*9c5db199SXin Li                return False
388*9c5db199SXin Li            sim_proxy = modem_proxy.get_sim_at_path(path)
389*9c5db199SXin Li            sim_props = sim_proxy.properties()
390*9c5db199SXin Li            return sim_props[
391*9c5db199SXin Li                    'EsimStatus'] != mm1_constants.MM_SIM_ESIM_STATUS_NO_PROFILES
392*9c5db199SXin Li
393*9c5db199SXin Li        # Check current SIM path value and status
394*9c5db199SXin Li        if is_usable_sim(sim_path):
395*9c5db199SXin Li            return modem_proxy
396*9c5db199SXin Li
397*9c5db199SXin Li        slots = modem_props['SimSlots']
398*9c5db199SXin Li        logging.info('Dut not in expected state, '
399*9c5db199SXin Li                    'current sim path:%s slots:%s', sim_path, slots)
400*9c5db199SXin Li
401*9c5db199SXin Li        for idx, path in enumerate(slots):
402*9c5db199SXin Li            if not is_usable_sim(path):
403*9c5db199SXin Li                continue
404*9c5db199SXin Li            logging.info('Primary slot does not have a SIM, '
405*9c5db199SXin Li                        'switching slot to %d', idx+1)
406*9c5db199SXin Li
407*9c5db199SXin Li            if (primary_slot != idx + 1):
408*9c5db199SXin Li                logging.info('setting slot:%d path:%s', idx+1, path)
409*9c5db199SXin Li                modem_proxy.set_primary_slot(idx+1)
410*9c5db199SXin Li                modem_proxy = \
411*9c5db199SXin Li                    mm_proxy.wait_for_modem(mm1_constants.MM_MODEM_POLL_TIME)
412*9c5db199SXin Li                return modem_proxy
413*9c5db199SXin Li        return None
414*9c5db199SXin Li
415*9c5db199SXin Li    def _wait_for_modem_registration(self):
416*9c5db199SXin Li        """Wait for the modem to register with the network.
417*9c5db199SXin Li
418*9c5db199SXin Li        @raise error.TestError if modem is not registered.
419*9c5db199SXin Li
420*9c5db199SXin Li        """
421*9c5db199SXin Li        utils.poll_for_condition(
422*9c5db199SXin Li            self.modem.ModemIsRegistered,
423*9c5db199SXin Li            exception=error.TestError(
424*9c5db199SXin Li                'Modem failed to register with the network.'),
425*9c5db199SXin Li            timeout=cellular_proxy.CellularProxy.SERVICE_REGISTRATION_TIMEOUT)
426*9c5db199SXin Li
427*9c5db199SXin Li    def _verify_cellular_service(self):
428*9c5db199SXin Li        """Make sure a cellular service exists.
429*9c5db199SXin Li
430*9c5db199SXin Li        The cellular service should not be connected to the network.
431*9c5db199SXin Li
432*9c5db199SXin Li        @raise error.TestError if cellular service does not exist or if
433*9c5db199SXin Li                there are multiple cellular services.
434*9c5db199SXin Li
435*9c5db199SXin Li        """
436*9c5db199SXin Li        service = self.shill.wait_for_cellular_service_object()
437*9c5db199SXin Li
438*9c5db199SXin Li        try:
439*9c5db199SXin Li            service.Disconnect()
440*9c5db199SXin Li        except dbus.DBusException as e:
441*9c5db199SXin Li            if (e.get_dbus_name() !=
442*9c5db199SXin Li                    cellular_proxy.CellularProxy.ERROR_NOT_CONNECTED):
443*9c5db199SXin Li                raise
444*9c5db199SXin Li        success, state, _ = self.shill.wait_for_property_in(
445*9c5db199SXin Li            service,
446*9c5db199SXin Li            cellular_proxy.CellularProxy.SERVICE_PROPERTY_STATE,
447*9c5db199SXin Li            ('idle',),
448*9c5db199SXin Li            cellular_proxy.CellularProxy.SERVICE_DISCONNECT_TIMEOUT)
449*9c5db199SXin Li        if not success:
450*9c5db199SXin Li            raise error.TestError(
451*9c5db199SXin Li                'Cellular service needs to start in the "idle" state. '
452*9c5db199SXin Li                'Current state is "%s". '
453*9c5db199SXin Li                'Modem disconnect may have failed.' %
454*9c5db199SXin Li                state)
455*9c5db199SXin Li
456*9c5db199SXin Li
457*9c5db199SXin Liclass CellularOTATestEnvironment(CellularTestEnvironment):
458*9c5db199SXin Li    """Setup and verify cellular over-the-air (OTA) test environment. """
459*9c5db199SXin Li
460*9c5db199SXin Li    def __init__(self, **kwargs):
461*9c5db199SXin Li        super(CellularOTATestEnvironment, self).__init__(**kwargs)
462*9c5db199SXin Li
463*9c5db199SXin Li# pseudomodem tests disabled with b/180627893, cleaningup all pseudomodem
464*9c5db199SXin Li# related files and imports through: b/205769777
465*9c5db199SXin Li'''
466*9c5db199SXin Liclass CellularPseudoMMTestEnvironment(CellularTestEnvironment):
467*9c5db199SXin Li    """Setup and verify cellular pseudomodem test environment. """
468*9c5db199SXin Li
469*9c5db199SXin Li    def __init__(self, pseudomm_args=None, **kwargs):
470*9c5db199SXin Li        """
471*9c5db199SXin Li        @param pseudomm_args: Tuple of arguments passed to the pseudomodem, see
472*9c5db199SXin Li                pseudomodem_context.py for description of each argument in the
473*9c5db199SXin Li                tuple: (flags_map, block_output, bus)
474*9c5db199SXin Li
475*9c5db199SXin Li        """
476*9c5db199SXin Li        kwargs["skip_modem_reset"] = True
477*9c5db199SXin Li        super(CellularPseudoMMTestEnvironment, self).__init__(**kwargs)
478*9c5db199SXin Li        self._context_managers.append(
479*9c5db199SXin Li            pseudomodem_context.PseudoModemManagerContext(
480*9c5db199SXin Li                True, bus=self.bus, *pseudomm_args))
481*9c5db199SXin Li'''
482*9c5db199SXin Li
483*9c5db199SXin Liclass CellularESIMTestEnvironment(CellularTestEnvironment):
484*9c5db199SXin Li    """Setup cellular eSIM test environment. """
485*9c5db199SXin Li
486*9c5db199SXin Li    def __init__(self, esim_arguments=None, **kwargs):
487*9c5db199SXin Li        kwargs["skip_modem_reset"] = True
488*9c5db199SXin Li        kwargs["is_esim_test"] = True
489*9c5db199SXin Li        super(CellularESIMTestEnvironment, self).__init__(**kwargs)
490