blob: 723343dc95246130e28e03c4bac9fd45a919a51f [file] [log] [blame]
Thieu Le98327a42014-08-21 18:11:41 -07001# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import contextlib
6import dbus
7import logging
8import sys
9import traceback
10
11import common
Thieu Lee3b3fcf2014-09-08 13:56:15 -070012from autotest_lib.client.bin import utils
Thieu Le98327a42014-08-21 18:11:41 -070013from autotest_lib.client.common_lib import error
14from autotest_lib.client.cros import backchannel
15from autotest_lib.client.cros.cellular import cell_tools
16from autotest_lib.client.cros.cellular import mm
17from autotest_lib.client.cros.cellular.pseudomodem import pseudomodem_context
18from autotest_lib.client.cros.cellular.wardmodem import wardmodem
19from autotest_lib.client.cros.networking import cellular_proxy
Thieu Le5fe5f512014-09-03 12:52:10 -070020from autotest_lib.client.cros.networking import shill_proxy
Thieu Le98327a42014-08-21 18:11:41 -070021
22# Import 'flimflam_test_path' first in order to import flimflam.
23# pylint: disable=W0611
24from autotest_lib.client.cros import flimflam_test_path
25import flimflam
26
27class CellularTestEnvironment(object):
28 """Setup and verify cellular test environment.
29
30 This context manager configures the following:
31 - Sets up backchannel.
32 - Shuts down other devices except cellular.
33 - Shill and MM logging is enabled appropriately for cellular.
34 - Initializes members that tests should use to access test environment
35 (eg. |shill|, |flimflam|, |modem_manager|, |modem|).
36
37 Then it verifies the following is valid:
38 - The backchannel is using an Ethernet device.
39 - The SIM is inserted and valid.
40 - There is one and only one modem in the device.
41 - The modem is registered to the network.
42 - There is a cellular service in shill and it's not connected.
43
44 Don't use this base class directly, use the appropriate subclass.
45
46 Setup for over-the-air tests:
47 with CellularOTATestEnvironment() as test_env:
48 # Test body
49
50 Setup for pseudomodem tests:
51 with CellularPseudoMMTestEnvironment(
52 pseudomm_args=({'family': '3GPP'})) as test_env:
53 # Test body
54
55 Setup for wardmodem tests:
56 with CellularWardModemTestEnvironment(
57 wardmodem_modem='e362') as test_env:
58 # Test body
59
60 """
61
62 def __init__(self, use_backchannel=True, shutdown_other_devices=True):
63 """
64 @param use_backchannel: Set up the backchannel that can be used to
65 communicate with the DUT.
66 @param shutdown_other_devices: If True, shutdown all devices except
67 cellular.
68
69 """
70 # Tests should use this main loop instead of creating their own.
71 self.mainloop = dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
72 self.bus = dbus.SystemBus(mainloop=self.mainloop)
73
74 self.shill = None
75 self.flim = None # Only use this for legacy tests.
76 self.modem_manager = None
77 self.modem = None
78
Thieu Le996e0a02014-09-15 12:36:08 -070079 self._nested = None
Thieu Le98327a42014-08-21 18:11:41 -070080 self._context_managers = []
81 if use_backchannel:
82 self._context_managers.append(backchannel.Backchannel())
83 if shutdown_other_devices:
84 self._context_managers.append(
85 cell_tools.OtherDeviceShutdownContext('cellular'))
86
87
Thieu Le996e0a02014-09-15 12:36:08 -070088 @contextlib.contextmanager
89 def _disable_shill_autoconnect(self):
90 self._enable_shill_cellular_autoconnect(False)
91 yield
92 self._enable_shill_cellular_autoconnect(True)
93
94
Thieu Le98327a42014-08-21 18:11:41 -070095 def __enter__(self):
96 try:
Thieu Le996e0a02014-09-15 12:36:08 -070097 # Temporarily disable shill autoconnect to cellular service while
98 # the test environment is setup to prevent a race condition
99 # between disconnecting the modem in _verify_cellular_service()
100 # and shill autoconnect.
101 with self._disable_shill_autoconnect():
102 self._nested = contextlib.nested(*self._context_managers)
103 self._nested.__enter__()
Thieu Le98327a42014-08-21 18:11:41 -0700104
Thieu Le996e0a02014-09-15 12:36:08 -0700105 self._initialize_shill()
Thieu Le99a39082014-09-09 16:00:13 -0700106
Thieu Le996e0a02014-09-15 12:36:08 -0700107 # Perform SIM verification now to ensure that we can enable the
108 # modem in _initialize_modem_components(). ModemManager does not
109 # allow enabling a modem without a SIM.
110 self._verify_sim()
111 self._initialize_modem_components()
Thieu Le99a39082014-09-09 16:00:13 -0700112
Thieu Le996e0a02014-09-15 12:36:08 -0700113 self._setup_logging()
Thieu Le98327a42014-08-21 18:11:41 -0700114
Thieu Le996e0a02014-09-15 12:36:08 -0700115 self._verify_backchannel()
116 self._wait_for_modem_registration()
117 self._verify_cellular_service()
Thieu Le98327a42014-08-21 18:11:41 -0700118
Thieu Le996e0a02014-09-15 12:36:08 -0700119 return self
Thieu Le5fe5f512014-09-03 12:52:10 -0700120 except (error.TestError, dbus.DBusException,
121 shill_proxy.ShillProxyError) as e:
Thieu Le98327a42014-08-21 18:11:41 -0700122 except_type, except_value, except_traceback = sys.exc_info()
123 lines = traceback.format_exception(except_type, except_value,
124 except_traceback)
125 logging.error('Error during test initialization:\n' +
126 ''.join(lines))
127 self.__exit__(*sys.exc_info())
128 raise error.TestError('INIT_ERROR: %s' % str(e))
Thieu Le5fe5f512014-09-03 12:52:10 -0700129 except:
130 self.__exit__(*sys.exc_info())
131 raise
Thieu Le98327a42014-08-21 18:11:41 -0700132
133
134 def __exit__(self, exception, value, traceback):
Thieu Le996e0a02014-09-15 12:36:08 -0700135 if self._nested:
136 return self._nested.__exit__(exception, value, traceback)
Thieu Le336aa102014-10-01 17:53:11 -0700137 self.shill = None
138 self.flim = None
139 self.modem_manager = None
140 self.modem = None
Thieu Le98327a42014-08-21 18:11:41 -0700141
142
Thieu Le99a39082014-09-09 16:00:13 -0700143 def _get_shill_cellular_device_object(self):
Thieu Lee3b3fcf2014-09-08 13:56:15 -0700144 modem_device = self.shill.find_cellular_device_object()
145 if not modem_device:
146 raise error.TestError('Cannot find cellular device in shill. '
147 'Is the modem plugged in?')
Thieu Le99a39082014-09-09 16:00:13 -0700148 return modem_device
149
150
151 def _enable_modem(self):
152 modem_device = self._get_shill_cellular_device_object()
Thieu Lee3b3fcf2014-09-08 13:56:15 -0700153 try:
154 modem_device.Enable()
155 except dbus.DBusException as e:
156 if (e.get_dbus_name() !=
157 shill_proxy.ShillProxy.ERROR_IN_PROGRESS):
158 raise
159
160 utils.poll_for_condition(
161 lambda: modem_device.GetProperties()['Powered'],
162 exception=error.TestError(
163 'Failed to enable modem.'),
164 timeout=shill_proxy.ShillProxy.DEVICE_ENABLE_DISABLE_TIMEOUT)
165
166
Thieu Le996e0a02014-09-15 12:36:08 -0700167 def _enable_shill_cellular_autoconnect(self, enable):
168 shill = cellular_proxy.CellularProxy.get_proxy(self.bus)
169 shill.manager.SetProperty(
170 shill_proxy.ShillProxy.
171 MANAGER_PROPERTY_NO_AUTOCONNECT_TECHNOLOGIES,
172 '' if enable else 'cellular')
173
174
Thieu Lea2aeab32014-09-15 14:29:43 -0700175 def _is_unsupported_error(self, e):
176 return (e.get_dbus_name() ==
177 shill_proxy.ShillProxy.ERROR_NOT_SUPPORTED or
178 (e.get_dbus_name() ==
179 shill_proxy.ShillProxy.ERROR_FAILURE and
180 'operation not supported' in e.get_dbus_message()))
181
Thieu Le996e0a02014-09-15 12:36:08 -0700182
Thieu Le98327a42014-08-21 18:11:41 -0700183 def _reset_modem(self):
Thieu Le99a39082014-09-09 16:00:13 -0700184 modem_device = self._get_shill_cellular_device_object()
Thieu Le98327a42014-08-21 18:11:41 -0700185 try:
Thieu Lea2aeab32014-09-15 14:29:43 -0700186 # Cromo/MBIM modems do not support being reset.
Thieu Le98327a42014-08-21 18:11:41 -0700187 self.shill.reset_modem(modem_device, expect_service=False)
188 except dbus.DBusException as e:
Thieu Lea2aeab32014-09-15 14:29:43 -0700189 if not self._is_unsupported_error(e):
Thieu Le98327a42014-08-21 18:11:41 -0700190 raise
191
192
Thieu Le99a39082014-09-09 16:00:13 -0700193 def _initialize_shill(self):
194 """Get access to shill."""
Thieu Le98327a42014-08-21 18:11:41 -0700195 # CellularProxy.get_proxy() checks to see if shill is running and
196 # responding to DBus requests. It returns None if that's not the case.
197 self.shill = cellular_proxy.CellularProxy.get_proxy(self.bus)
198 if self.shill is None:
199 raise error.TestError('Cannot connect to shill, is shill running?')
200
201 # Keep this around to support older tests that haven't migrated to
202 # cellular_proxy.
203 self.flim = flimflam.FlimFlam()
204
Thieu Le99a39082014-09-09 16:00:13 -0700205
206 def _initialize_modem_components(self):
207 """Reset the modem and get access to modem components."""
Thieu Lee3b3fcf2014-09-08 13:56:15 -0700208 # Enable modem first so shill initializes the modemmanager proxies so
209 # we can call reset on it.
210 self._enable_modem()
211 self._reset_modem()
212
Thieu Le98327a42014-08-21 18:11:41 -0700213 # PickOneModem() makes sure there's a modem manager and that there is
214 # one and only one modem.
Thieu Le98327a42014-08-21 18:11:41 -0700215 self.modem_manager, modem_path = mm.PickOneModem('')
216 self.modem = self.modem_manager.GetModem(modem_path)
217 if self.modem is None:
218 raise error.TestError('Cannot get modem object at %s.' % modem_path)
219
220
221 def _setup_logging(self):
222 self.shill.set_logging_for_cellular_test()
223 self.modem_manager.SetDebugLogging()
224
225
Thieu Le99a39082014-09-09 16:00:13 -0700226 def _verify_sim(self):
227 """Verify SIM is valid.
228
229 Make sure a SIM in inserted and that it is not locked.
230
231 @raise error.TestError if SIM does not exist or is locked.
232
233 """
234 modem_device = self._get_shill_cellular_device_object()
235 props = modem_device.GetProperties()
236
237 # No SIM in CDMA modems.
238 family = props[
239 cellular_proxy.CellularProxy.DEVICE_PROPERTY_TECHNOLOGY_FAMILY]
240 if (family ==
241 cellular_proxy.CellularProxy.
242 DEVICE_PROPERTY_TECHNOLOGY_FAMILY_CDMA):
243 return
244
245 # Make sure there is a SIM.
246 if not props[cellular_proxy.CellularProxy.DEVICE_PROPERTY_SIM_PRESENT]:
247 raise error.TestError('There is no SIM in the modem.')
248
249 # Make sure SIM is not locked.
250 lock_status = props.get(
251 cellular_proxy.CellularProxy.DEVICE_PROPERTY_SIM_LOCK_STATUS,
252 None)
253 if lock_status is None:
254 raise error.TestError('Failed to read SIM lock status.')
255 locked = lock_status.get(
256 cellular_proxy.CellularProxy.PROPERTY_KEY_SIM_LOCK_ENABLED,
257 None)
258 if locked is None:
259 raise error.TestError('Failed to read SIM LockEnabled status.')
260 elif locked:
261 raise error.TestError(
262 'SIM is locked, test requires an unlocked SIM.')
263
264
Thieu Le98327a42014-08-21 18:11:41 -0700265 def _verify_backchannel(self):
266 """Verify backchannel is on an ethernet device.
267
268 @raise error.TestError if backchannel is not on an ethernet device.
269
270 """
Thieu Le8bded2b2014-09-03 15:38:46 -0700271 if not backchannel.is_backchannel_using_ethernet():
272 raise error.TestError('An ethernet connection is required between '
273 'the test server and the device under test.')
Thieu Le98327a42014-08-21 18:11:41 -0700274
275
Thieu Le98327a42014-08-21 18:11:41 -0700276 def _wait_for_modem_registration(self):
277 """Wait for the modem to register with the network.
278
Thieu Le98327a42014-08-21 18:11:41 -0700279 @raise error.TestError if modem is not registered.
280
281 """
Thieu Lee3b3fcf2014-09-08 13:56:15 -0700282 utils.poll_for_condition(
283 self.modem.ModemIsRegistered,
284 exception=error.TestError(
285 'Modem failed to register with the network.'),
286 timeout=cellular_proxy.CellularProxy.SERVICE_REGISTRATION_TIMEOUT)
Thieu Le98327a42014-08-21 18:11:41 -0700287
288
289 def _verify_cellular_service(self):
290 """Make sure a cellular service exists.
291
292 The cellular service should not be connected to the network.
293
294 @raise error.TestError if cellular service does not exist or if
295 there are multiple cellular services.
296
297 """
298 service = self.shill.wait_for_cellular_service_object()
299
300 try:
301 service.Disconnect()
302 except dbus.DBusException as e:
303 if (e.get_dbus_name() !=
304 cellular_proxy.CellularProxy.ERROR_NOT_CONNECTED):
305 raise
Thieu Lee3b3fcf2014-09-08 13:56:15 -0700306 success, state, _ = self.shill.wait_for_property_in(
Thieu Le98327a42014-08-21 18:11:41 -0700307 service,
308 cellular_proxy.CellularProxy.SERVICE_PROPERTY_STATE,
309 ('idle',),
310 cellular_proxy.CellularProxy.SERVICE_DISCONNECT_TIMEOUT)
311 if not success:
312 raise error.TestError(
Thieu Lee3b3fcf2014-09-08 13:56:15 -0700313 'Cellular service needs to start in the "idle" state. '
314 'Current state is "%s". '
315 'Modem disconnect may have failed.' %
316 state)
Thieu Le98327a42014-08-21 18:11:41 -0700317
318
319class CellularOTATestEnvironment(CellularTestEnvironment):
320 """Setup and verify cellular over-the-air (OTA) test environment. """
321 def __init__(self, **kwargs):
322 super(CellularOTATestEnvironment, self).__init__(**kwargs)
323
324
325class CellularPseudoMMTestEnvironment(CellularTestEnvironment):
326 """Setup and verify cellular pseudomodem test environment. """
327 def __init__(self, pseudomm_args=None, **kwargs):
328 """
329 @param pseudomm_args: Tuple of arguments passed to the pseudomodem, see
330 pseudomodem_context.py for description of each argument in the
331 tuple: (flags_map, block_output, bus)
332
333 """
334 super(CellularPseudoMMTestEnvironment, self).__init__(**kwargs)
335 self._context_managers.append(
Thieu Le6724f282014-09-04 15:36:41 -0700336 pseudomodem_context.PseudoModemManagerContext(
337 True, bus=self.bus, *pseudomm_args))
Thieu Le98327a42014-08-21 18:11:41 -0700338
339
340class CellularWardModemTestEnvironment(CellularTestEnvironment):
341 """Setup and verify cellular ward modem test environment. """
342 def __init__(self, wardmodem_modem=None, **kwargs):
343 """
344 @param wardmodem_modem: Customized ward modem to use instead of the
345 default implementation, see wardmodem.py.
346
347 """
348 super(CellularWardModemTestEnvironment, self).__init__(**kwargs)
349 self._context_managers.append(
350 wardmodem.WardModemContext(args=['--modem', wardmodem_modem]))