xref: /aosp_15_r20/external/autotest/client/cros/cellular/modem1.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li
2*9c5db199SXin Li# Lint as: python2, python3
3*9c5db199SXin Li# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
4*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
5*9c5db199SXin Li# found in the LICENSE file.
6*9c5db199SXin Li"""Implement a modem proxy to talk to a ModemManager1 modem."""
7*9c5db199SXin Li
8*9c5db199SXin Lifrom __future__ import absolute_import
9*9c5db199SXin Lifrom __future__ import division
10*9c5db199SXin Lifrom __future__ import print_function
11*9c5db199SXin Li
12*9c5db199SXin Lifrom autotest_lib.client.common_lib import error
13*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import cellular
14*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import cellular_logging
15*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import mm1
16*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import mm1_constants
17*9c5db199SXin Li
18*9c5db199SXin Liimport dbus
19*9c5db199SXin Liimport six
20*9c5db199SXin Li
21*9c5db199SXin Lilog = cellular_logging.SetupCellularLogging('modem1')
22*9c5db199SXin Li
23*9c5db199SXin LiMODEM_TIMEOUT = 60
24*9c5db199SXin Li
25*9c5db199SXin Li
26*9c5db199SXin Liclass Modem(object):
27*9c5db199SXin Li    """An object which talks to a ModemManager1 modem."""
28*9c5db199SXin Li    # MM_MODEM_GSM_ACCESS_TECH (not exported)
29*9c5db199SXin Li    # From /usr/include/mm/mm-modem.h
30*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0
31*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_GSM = 1 << 1
32*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 1 << 2
33*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_GPRS = 1 << 3
34*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_EDGE = 1 << 4
35*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_UMTS = 1 << 5
36*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_HSDPA = 1 << 6
37*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_HSUPA = 1 << 7
38*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_HSPA = 1 << 8
39*9c5db199SXin Li
40*9c5db199SXin Li    # Mapping of modem technologies to cellular technologies
41*9c5db199SXin Li    _ACCESS_TECH_TO_TECHNOLOGY = {
42*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_GSM: cellular.Technology.WCDMA,
43*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT: cellular.Technology.WCDMA,
44*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_GPRS: cellular.Technology.GPRS,
45*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_EDGE: cellular.Technology.EGPRS,
46*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_UMTS: cellular.Technology.WCDMA,
47*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_HSDPA: cellular.Technology.HSDPA,
48*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_HSUPA: cellular.Technology.HSUPA,
49*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_HSPA: cellular.Technology.HSDUPA,
50*9c5db199SXin Li    }
51*9c5db199SXin Li
52*9c5db199SXin Li    def __init__(self, manager, path):
53*9c5db199SXin Li        self.manager = manager
54*9c5db199SXin Li        self.bus = manager.bus
55*9c5db199SXin Li        self.service = manager.service
56*9c5db199SXin Li        self.path = path
57*9c5db199SXin Li
58*9c5db199SXin Li    def Modem(self):
59*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
60*9c5db199SXin Li        return dbus.Interface(obj, mm1.MODEM_INTERFACE)
61*9c5db199SXin Li
62*9c5db199SXin Li    def SimpleModem(self):
63*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
64*9c5db199SXin Li        return dbus.Interface(obj, mm1.MODEM_SIMPLE_INTERFACE)
65*9c5db199SXin Li
66*9c5db199SXin Li    def GsmModem(self):
67*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
68*9c5db199SXin Li        return dbus.Interface(obj, mm1.MODEM_MODEM3GPP_INTERFACE)
69*9c5db199SXin Li
70*9c5db199SXin Li    def CdmaModem(self):
71*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
72*9c5db199SXin Li        return dbus.Interface(obj, mm1.MODEM_MODEMCDMA_INTERFACE)
73*9c5db199SXin Li
74*9c5db199SXin Li    def Sim(self):
75*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
76*9c5db199SXin Li        return dbus.Interface(obj, mm1.SIM_INTERFACE)
77*9c5db199SXin Li
78*9c5db199SXin Li    def PropertiesInterface(self):
79*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
80*9c5db199SXin Li        return dbus.Interface(obj, dbus.PROPERTIES_IFACE)
81*9c5db199SXin Li
82*9c5db199SXin Li    def GetAll(self, iface):
83*9c5db199SXin Li        obj_iface = self.PropertiesInterface()
84*9c5db199SXin Li        return obj_iface.GetAll(iface)
85*9c5db199SXin Li
86*9c5db199SXin Li    def _GetModemInterfaces(self):
87*9c5db199SXin Li        return [
88*9c5db199SXin Li            mm1.MODEM_INTERFACE,
89*9c5db199SXin Li            mm1.MODEM_SIMPLE_INTERFACE,
90*9c5db199SXin Li            mm1.MODEM_MODEM3GPP_INTERFACE,
91*9c5db199SXin Li            mm1.MODEM_MODEMCDMA_INTERFACE
92*9c5db199SXin Li            ]
93*9c5db199SXin Li
94*9c5db199SXin Li    @staticmethod
95*9c5db199SXin Li    def _CopyPropertiesCheckUnique(src, dest):
96*9c5db199SXin Li        """Copies properties from |src| to |dest| and makes sure there are no
97*9c5db199SXin Li           duplicate properties that have different values."""
98*9c5db199SXin Li        for key, value in six.iteritems(src):
99*9c5db199SXin Li            if key in dest and value != dest[key]:
100*9c5db199SXin Li                raise KeyError('Duplicate property %s, different values '
101*9c5db199SXin Li                               '("%s", "%s")' % (key, value, dest[key]))
102*9c5db199SXin Li            dest[key] = value
103*9c5db199SXin Li
104*9c5db199SXin Li    def GetModemProperties(self):
105*9c5db199SXin Li        """Returns all DBus Properties of all the modem interfaces."""
106*9c5db199SXin Li        props = dict()
107*9c5db199SXin Li        for iface in self._GetModemInterfaces():
108*9c5db199SXin Li            try:
109*9c5db199SXin Li                iface_props = self.GetAll(iface)
110*9c5db199SXin Li            except dbus.exceptions.DBusException:
111*9c5db199SXin Li                continue
112*9c5db199SXin Li            if iface_props:
113*9c5db199SXin Li                self._CopyPropertiesCheckUnique(iface_props, props)
114*9c5db199SXin Li
115*9c5db199SXin Li        try:
116*9c5db199SXin Li            sim_obj = self.bus.get_object(self.service, props['Sim'])
117*9c5db199SXin Li            sim_props_iface = dbus.Interface(sim_obj, dbus.PROPERTIES_IFACE)
118*9c5db199SXin Li            sim_props = sim_props_iface.GetAll(mm1.SIM_INTERFACE)
119*9c5db199SXin Li
120*9c5db199SXin Li            # SIM cards may store an empty operator name or store a value
121*9c5db199SXin Li            # different from the one obtained OTA. Rename the 'OperatorName'
122*9c5db199SXin Li            # property obtained from the SIM card to 'SimOperatorName' in
123*9c5db199SXin Li            # order to avoid a potential conflict with the 'OperatorName'
124*9c5db199SXin Li            # property obtained from the Modem3gpp interface.
125*9c5db199SXin Li            if 'OperatorName' in sim_props:
126*9c5db199SXin Li                sim_props['SimOperatorName'] = sim_props.pop('OperatorName')
127*9c5db199SXin Li
128*9c5db199SXin Li            self._CopyPropertiesCheckUnique(sim_props, props)
129*9c5db199SXin Li        except dbus.exceptions.DBusException:
130*9c5db199SXin Li            pass
131*9c5db199SXin Li
132*9c5db199SXin Li        return props
133*9c5db199SXin Li
134*9c5db199SXin Li    def GetAccessTechnology(self):
135*9c5db199SXin Li        """Returns the modem access technology."""
136*9c5db199SXin Li        props = self.GetModemProperties()
137*9c5db199SXin Li        tech = props['AccessTechnologies']
138*9c5db199SXin Li        return Modem._ACCESS_TECH_TO_TECHNOLOGY[tech]
139*9c5db199SXin Li
140*9c5db199SXin Li    def GetCurrentTechnologyFamily(self):
141*9c5db199SXin Li        """Returns the modem technology family."""
142*9c5db199SXin Li        props = self.GetAll(mm1.MODEM_INTERFACE)
143*9c5db199SXin Li        capabilities = props.get('SupportedCapabilities')
144*9c5db199SXin Li        if self._IsCDMAModem(capabilities):
145*9c5db199SXin Li            return cellular.TechnologyFamily.CDMA
146*9c5db199SXin Li        if self._Is3GPPModem(capabilities):
147*9c5db199SXin Li            return cellular.TechnologyFamily.UMTS
148*9c5db199SXin Li        raise error.TestError('Invalid modem type')
149*9c5db199SXin Li
150*9c5db199SXin Li    def GetVersion(self):
151*9c5db199SXin Li        """Returns the modem version information."""
152*9c5db199SXin Li        return self.GetModemProperties()['Revision']
153*9c5db199SXin Li
154*9c5db199SXin Li    def _IsCDMAModem(self, capabilities):
155*9c5db199SXin Li        return mm1_constants.MM_MODEM_CAPABILITY_CDMA_EVDO in capabilities
156*9c5db199SXin Li
157*9c5db199SXin Li    def _Is3GPPModem(self, capabilities):
158*9c5db199SXin Li        for capability in capabilities:
159*9c5db199SXin Li            if (capability &
160*9c5db199SXin Li                    (mm1_constants.MM_MODEM_CAPABILITY_LTE |
161*9c5db199SXin Li                     mm1_constants.MM_MODEM_CAPABILITY_LTE_ADVANCED |
162*9c5db199SXin Li                     mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS)):
163*9c5db199SXin Li                return True
164*9c5db199SXin Li        return False
165*9c5db199SXin Li
166*9c5db199SXin Li    def _CDMAModemIsRegistered(self):
167*9c5db199SXin Li        modem_status = self.SimpleModem().GetStatus()
168*9c5db199SXin Li        cdma1x_state = modem_status.get(
169*9c5db199SXin Li                'cdma-cdma1x-registration-state',
170*9c5db199SXin Li                mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
171*9c5db199SXin Li        evdo_state = modem_status.get(
172*9c5db199SXin Li                'cdma-evdo-registration-state',
173*9c5db199SXin Li                mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
174*9c5db199SXin Li        return (cdma1x_state !=
175*9c5db199SXin Li                mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN or
176*9c5db199SXin Li                evdo_state !=
177*9c5db199SXin Li                mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)
178*9c5db199SXin Li
179*9c5db199SXin Li    def _3GPPModemIsRegistered(self):
180*9c5db199SXin Li        modem_status = self.SimpleModem().GetStatus()
181*9c5db199SXin Li        state = modem_status.get('m3gpp-registration-state')
182*9c5db199SXin Li        return (state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME or
183*9c5db199SXin Li                state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING)
184*9c5db199SXin Li
185*9c5db199SXin Li    def ModemIsRegistered(self):
186*9c5db199SXin Li        """Ensure that modem is registered on the network."""
187*9c5db199SXin Li        props = self.GetAll(mm1.MODEM_INTERFACE)
188*9c5db199SXin Li        capabilities = props.get('SupportedCapabilities')
189*9c5db199SXin Li        if self._IsCDMAModem(capabilities):
190*9c5db199SXin Li            return self._CDMAModemIsRegistered()
191*9c5db199SXin Li        elif self._Is3GPPModem(capabilities):
192*9c5db199SXin Li            return self._3GPPModemIsRegistered()
193*9c5db199SXin Li        else:
194*9c5db199SXin Li            raise error.TestError('Invalid modem type')
195*9c5db199SXin Li
196*9c5db199SXin Li    def ModemIsRegisteredUsing(self, technology):
197*9c5db199SXin Li        """Ensure that modem is registered on the network with a technology."""
198*9c5db199SXin Li        if not self.ModemIsRegistered():
199*9c5db199SXin Li            return False
200*9c5db199SXin Li
201*9c5db199SXin Li        reported_tech = self.GetAccessTechnology()
202*9c5db199SXin Li
203*9c5db199SXin Li        # TODO(jglasgow): Remove this mapping.  Basestation and
204*9c5db199SXin Li        # reported technology should be identical.
205*9c5db199SXin Li        BASESTATION_TO_REPORTED_TECHNOLOGY = {
206*9c5db199SXin Li            cellular.Technology.GPRS: cellular.Technology.GPRS,
207*9c5db199SXin Li            cellular.Technology.EGPRS: cellular.Technology.GPRS,
208*9c5db199SXin Li            cellular.Technology.WCDMA: cellular.Technology.HSDUPA,
209*9c5db199SXin Li            cellular.Technology.HSDPA: cellular.Technology.HSDUPA,
210*9c5db199SXin Li            cellular.Technology.HSUPA: cellular.Technology.HSDUPA,
211*9c5db199SXin Li            cellular.Technology.HSDUPA: cellular.Technology.HSDUPA,
212*9c5db199SXin Li            cellular.Technology.HSPA_PLUS: cellular.Technology.HSPA_PLUS
213*9c5db199SXin Li        }
214*9c5db199SXin Li
215*9c5db199SXin Li        return BASESTATION_TO_REPORTED_TECHNOLOGY[technology] == reported_tech
216*9c5db199SXin Li
217*9c5db199SXin Li    def IsConnectingOrDisconnecting(self):
218*9c5db199SXin Li        props = self.GetAll(mm1.MODEM_INTERFACE)
219*9c5db199SXin Li        return props['State'] in [
220*9c5db199SXin Li            mm1.MM_MODEM_STATE_CONNECTING,
221*9c5db199SXin Li            mm1.MM_MODEM_STATE_DISCONNECTING
222*9c5db199SXin Li        ]
223*9c5db199SXin Li
224*9c5db199SXin Li    def IsEnabled(self):
225*9c5db199SXin Li        props = self.GetAll(mm1.MODEM_INTERFACE)
226*9c5db199SXin Li        return props['State'] in [
227*9c5db199SXin Li            mm1.MM_MODEM_STATE_ENABLED,
228*9c5db199SXin Li            mm1.MM_MODEM_STATE_SEARCHING,
229*9c5db199SXin Li            mm1.MM_MODEM_STATE_REGISTERED,
230*9c5db199SXin Li            mm1.MM_MODEM_STATE_DISCONNECTING,
231*9c5db199SXin Li            mm1.MM_MODEM_STATE_CONNECTING,
232*9c5db199SXin Li            mm1.MM_MODEM_STATE_CONNECTED
233*9c5db199SXin Li        ]
234*9c5db199SXin Li
235*9c5db199SXin Li    def IsDisabled(self):
236*9c5db199SXin Li        props = self.GetAll(mm1.MODEM_INTERFACE)
237*9c5db199SXin Li        return props['State'] == mm1.MM_MODEM_STATE_DISABLED
238*9c5db199SXin Li
239*9c5db199SXin Li    def Enable(self, enable, **kwargs):
240*9c5db199SXin Li        self.Modem().Enable(enable, timeout=MODEM_TIMEOUT, **kwargs)
241*9c5db199SXin Li
242*9c5db199SXin Li    def Connect(self, props):
243*9c5db199SXin Li        self.SimpleModem().Connect(props, timeout=MODEM_TIMEOUT)
244*9c5db199SXin Li
245*9c5db199SXin Li    def Disconnect(self):
246*9c5db199SXin Li        self.SimpleModem().Disconnect('/', timeout=MODEM_TIMEOUT)
247*9c5db199SXin Li
248*9c5db199SXin Li
249*9c5db199SXin Liclass ModemManager(object):
250*9c5db199SXin Li    """An object which talks to a ModemManager1 service."""
251*9c5db199SXin Li
252*9c5db199SXin Li    def __init__(self):
253*9c5db199SXin Li        self.bus = dbus.SystemBus()
254*9c5db199SXin Li        self.service = mm1.MODEM_MANAGER_INTERFACE
255*9c5db199SXin Li        self.path = mm1.OMM
256*9c5db199SXin Li        self.manager = dbus.Interface(
257*9c5db199SXin Li            self.bus.get_object(self.service, self.path),
258*9c5db199SXin Li            mm1.MODEM_MANAGER_INTERFACE)
259*9c5db199SXin Li        self.objectmanager = dbus.Interface(
260*9c5db199SXin Li            self.bus.get_object(self.service, self.path), mm1.OFDOM)
261*9c5db199SXin Li
262*9c5db199SXin Li    def EnumerateDevices(self):
263*9c5db199SXin Li        devices = self.objectmanager.GetManagedObjects()
264*9c5db199SXin Li        return list(devices.keys())
265*9c5db199SXin Li
266*9c5db199SXin Li    def GetModem(self, path):
267*9c5db199SXin Li        return Modem(self, path)
268*9c5db199SXin Li
269*9c5db199SXin Li    def SetDebugLogging(self):
270*9c5db199SXin Li        self.manager.SetLogging('DEBUG')
271