xref: /aosp_15_r20/external/autotest/client/cros/cellular/modem.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Lint as: python2, python3
2*9c5db199SXin Li# Copyright (c) 2013 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 Lifrom __future__ import absolute_import
7*9c5db199SXin Lifrom __future__ import division
8*9c5db199SXin Lifrom __future__ import print_function
9*9c5db199SXin Li
10*9c5db199SXin Liimport os
11*9c5db199SXin Li
12*9c5db199SXin Lifrom autotest_lib.client.cros.cellular import cellular
13*9c5db199SXin Li
14*9c5db199SXin Liimport dbus
15*9c5db199SXin Liimport six
16*9c5db199SXin Li
17*9c5db199SXin LiMODEM_TIMEOUT=60
18*9c5db199SXin Li
19*9c5db199SXin Liclass Modem(object):
20*9c5db199SXin Li    """An object which talks to a ModemManager modem."""
21*9c5db199SXin Li    MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem'
22*9c5db199SXin Li    SIMPLE_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Simple'
23*9c5db199SXin Li    CDMA_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Cdma'
24*9c5db199SXin Li    GSM_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm'
25*9c5db199SXin Li    GOBI_MODEM_INTERFACE = 'org.chromium.ModemManager.Modem.Gobi'
26*9c5db199SXin Li    GSM_CARD_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.Card'
27*9c5db199SXin Li    GSM_SMS_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.SMS'
28*9c5db199SXin Li    GSM_NETWORK_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.Network'
29*9c5db199SXin Li    PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties'
30*9c5db199SXin Li
31*9c5db199SXin Li    GSM_MODEM = 1
32*9c5db199SXin Li    CDMA_MODEM = 2
33*9c5db199SXin Li
34*9c5db199SXin Li    NETWORK_PREFERENCE_AUTOMATIC = 0
35*9c5db199SXin Li    NETWORK_PREFERENCE_CDMA_2000 = 1
36*9c5db199SXin Li    NETWORK_PREFERENCE_EVDO_1X = 2
37*9c5db199SXin Li    NETWORK_PREFERENCE_GSM = 3
38*9c5db199SXin Li    NETWORK_PREFERENCE_WCDMA = 4
39*9c5db199SXin Li
40*9c5db199SXin Li    # MM_MODEM_GSM_ACCESS_TECH (not exported)
41*9c5db199SXin Li    # From /usr/include/mm/mm-modem.h
42*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0
43*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_GSM = 1
44*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 2
45*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_GPRS = 3
46*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_EDGE = 4
47*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_UMTS = 5
48*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_HSDPA = 6
49*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_HSUPA = 7
50*9c5db199SXin Li    _MM_MODEM_GSM_ACCESS_TECH_HSPA = 8
51*9c5db199SXin Li
52*9c5db199SXin Li    # MM_MODEM_STATE (not exported)
53*9c5db199SXin Li    # From /usr/include/mm/mm-modem.h
54*9c5db199SXin Li    _MM_MODEM_STATE_UNKNOWN = 0
55*9c5db199SXin Li    _MM_MODEM_STATE_DISABLED = 10
56*9c5db199SXin Li    _MM_MODEM_STATE_DISABLING = 20
57*9c5db199SXin Li    _MM_MODEM_STATE_ENABLING = 30
58*9c5db199SXin Li    _MM_MODEM_STATE_ENABLED = 40
59*9c5db199SXin Li    _MM_MODEM_STATE_SEARCHING = 50
60*9c5db199SXin Li    _MM_MODEM_STATE_REGISTERED = 60
61*9c5db199SXin Li    _MM_MODEM_STATE_DISCONNECTING = 70
62*9c5db199SXin Li    _MM_MODEM_STATE_CONNECTING = 80
63*9c5db199SXin Li    _MM_MODEM_STATE_CONNECTED = 90
64*9c5db199SXin Li
65*9c5db199SXin Li    # Mapping of modem technologies to cellular technologies
66*9c5db199SXin Li    _ACCESS_TECH_TO_TECHNOLOGY = {
67*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_GSM: cellular.Technology.WCDMA,
68*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT: cellular.Technology.WCDMA,
69*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_GPRS: cellular.Technology.GPRS,
70*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_EDGE: cellular.Technology.EGPRS,
71*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_UMTS: cellular.Technology.WCDMA,
72*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_HSDPA: cellular.Technology.HSDPA,
73*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_HSUPA: cellular.Technology.HSUPA,
74*9c5db199SXin Li        _MM_MODEM_GSM_ACCESS_TECH_HSPA: cellular.Technology.HSDUPA,
75*9c5db199SXin Li    }
76*9c5db199SXin Li
77*9c5db199SXin Li    def __init__(self, manager, path):
78*9c5db199SXin Li        self.manager = manager
79*9c5db199SXin Li        self.bus = manager.bus
80*9c5db199SXin Li        self.service = manager.service
81*9c5db199SXin Li        self.path = path
82*9c5db199SXin Li
83*9c5db199SXin Li    def Modem(self):
84*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
85*9c5db199SXin Li        return dbus.Interface(obj, Modem.MODEM_INTERFACE)
86*9c5db199SXin Li
87*9c5db199SXin Li    def SimpleModem(self):
88*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
89*9c5db199SXin Li        return dbus.Interface(obj, Modem.SIMPLE_MODEM_INTERFACE)
90*9c5db199SXin Li
91*9c5db199SXin Li    def CdmaModem(self):
92*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
93*9c5db199SXin Li        return dbus.Interface(obj, Modem.CDMA_MODEM_INTERFACE)
94*9c5db199SXin Li
95*9c5db199SXin Li    def GobiModem(self):
96*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
97*9c5db199SXin Li        return dbus.Interface(obj, Modem.GOBI_MODEM_INTERFACE)
98*9c5db199SXin Li
99*9c5db199SXin Li    def GsmModem(self):
100*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
101*9c5db199SXin Li        return dbus.Interface(obj, Modem.GSM_MODEM_INTERFACE)
102*9c5db199SXin Li
103*9c5db199SXin Li    def GsmCard(self):
104*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
105*9c5db199SXin Li        return dbus.Interface(obj, Modem.GSM_CARD_INTERFACE)
106*9c5db199SXin Li
107*9c5db199SXin Li    def GsmSms(self):
108*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
109*9c5db199SXin Li        return dbus.Interface(obj, Modem.GSM_SMS_INTERFACE)
110*9c5db199SXin Li
111*9c5db199SXin Li    def GsmNetwork(self):
112*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
113*9c5db199SXin Li        return dbus.Interface(obj, Modem.GSM_NETWORK_INTERFACE)
114*9c5db199SXin Li
115*9c5db199SXin Li    def GetAll(self, iface):
116*9c5db199SXin Li        obj = self.bus.get_object(self.service, self.path)
117*9c5db199SXin Li        obj_iface = dbus.Interface(obj, Modem.PROPERTIES_INTERFACE)
118*9c5db199SXin Li        return obj_iface.GetAll(iface)
119*9c5db199SXin Li
120*9c5db199SXin Li    def _GetModemInterfaces(self):
121*9c5db199SXin Li        return [
122*9c5db199SXin Li            Modem.MODEM_INTERFACE,
123*9c5db199SXin Li            Modem.SIMPLE_MODEM_INTERFACE,
124*9c5db199SXin Li            Modem.CDMA_MODEM_INTERFACE,
125*9c5db199SXin Li            Modem.GSM_MODEM_INTERFACE,
126*9c5db199SXin Li            Modem.GSM_NETWORK_INTERFACE,
127*9c5db199SXin Li            Modem.GOBI_MODEM_INTERFACE]
128*9c5db199SXin Li
129*9c5db199SXin Li
130*9c5db199SXin Li    @staticmethod
131*9c5db199SXin Li    def _CopyPropertiesCheckUnique(src, dest):
132*9c5db199SXin Li        """Copies properties from |src| to |dest| and makes sure there are no
133*9c5db199SXin Li           duplicate properties that have different values."""
134*9c5db199SXin Li        for key, value in six.iteritems(src):
135*9c5db199SXin Li            if key in dest and value != dest[key]:
136*9c5db199SXin Li                raise KeyError('Duplicate property %s, different values '
137*9c5db199SXin Li                               '("%s", "%s")' % (key, value, dest[key]))
138*9c5db199SXin Li            dest[key] = value
139*9c5db199SXin Li
140*9c5db199SXin Li    def GetModemProperties(self):
141*9c5db199SXin Li        """Returns all DBus Properties of all the modem interfaces."""
142*9c5db199SXin Li        props = dict()
143*9c5db199SXin Li        for iface in self._GetModemInterfaces():
144*9c5db199SXin Li            try:
145*9c5db199SXin Li                iface_props = self.GetAll(iface)
146*9c5db199SXin Li            except dbus.exceptions.DBusException:
147*9c5db199SXin Li                continue
148*9c5db199SXin Li            if iface_props:
149*9c5db199SXin Li                self._CopyPropertiesCheckUnique(iface_props, props)
150*9c5db199SXin Li
151*9c5db199SXin Li        status = self.SimpleModem().GetStatus()
152*9c5db199SXin Li        if 'meid' in status:
153*9c5db199SXin Li            props['Meid'] = status['meid']
154*9c5db199SXin Li        if 'imei' in status:
155*9c5db199SXin Li            props['Imei'] = status['imei']
156*9c5db199SXin Li        if 'imsi' in status:
157*9c5db199SXin Li            props['Imsi'] = status['imsi']
158*9c5db199SXin Li        if 'esn' in status:
159*9c5db199SXin Li            props['Esn'] = status['esn']
160*9c5db199SXin Li
161*9c5db199SXin Li        # Operator information is not exposed through the properties interface.
162*9c5db199SXin Li        # Try to get it directly. This may fail on a disabled modem.
163*9c5db199SXin Li        try:
164*9c5db199SXin Li            network = self.GsmNetwork()
165*9c5db199SXin Li            _, operator_code, operator_name = network.GetRegistrationInfo()
166*9c5db199SXin Li            if operator_code:
167*9c5db199SXin Li                props['OperatorCode'] = operator_code
168*9c5db199SXin Li            if operator_name:
169*9c5db199SXin Li                props['OperatorName'] = operator_name
170*9c5db199SXin Li        except dbus.DBusException:
171*9c5db199SXin Li            pass
172*9c5db199SXin Li
173*9c5db199SXin Li        return props
174*9c5db199SXin Li
175*9c5db199SXin Li    def GetAccessTechnology(self):
176*9c5db199SXin Li        """Returns the modem access technology."""
177*9c5db199SXin Li        props = self.GetModemProperties()
178*9c5db199SXin Li        tech = props.get('AccessTechnology')
179*9c5db199SXin Li        return Modem._ACCESS_TECH_TO_TECHNOLOGY[tech]
180*9c5db199SXin Li
181*9c5db199SXin Li    def GetCurrentTechnologyFamily(self):
182*9c5db199SXin Li        """Returns the modem technology family."""
183*9c5db199SXin Li        try:
184*9c5db199SXin Li            self.GetAll(Modem.GSM_CARD_INTERFACE)
185*9c5db199SXin Li            return cellular.TechnologyFamily.UMTS
186*9c5db199SXin Li        except dbus.exceptions.DBusException:
187*9c5db199SXin Li            return cellular.TechnologyFamily.CDMA
188*9c5db199SXin Li
189*9c5db199SXin Li    def GetVersion(self):
190*9c5db199SXin Li        """Returns the modem version information."""
191*9c5db199SXin Li        return self.Modem().GetInfo()[2]
192*9c5db199SXin Li
193*9c5db199SXin Li    def _GetRegistrationState(self):
194*9c5db199SXin Li        try:
195*9c5db199SXin Li            network = self.GsmNetwork()
196*9c5db199SXin Li            (status, unused_code, unused_name) = network.GetRegistrationInfo()
197*9c5db199SXin Li            # TODO(jglasgow): HOME - 1, ROAMING - 5
198*9c5db199SXin Li            return status == 1 or status == 5
199*9c5db199SXin Li        except dbus.exceptions.DBusException:
200*9c5db199SXin Li            pass
201*9c5db199SXin Li
202*9c5db199SXin Li        cdma_modem = self.CdmaModem()
203*9c5db199SXin Li        try:
204*9c5db199SXin Li            cdma, evdo = cdma_modem.GetRegistrationState()
205*9c5db199SXin Li            return cdma > 0 or evdo > 0
206*9c5db199SXin Li        except dbus.exceptions.DBusException:
207*9c5db199SXin Li            pass
208*9c5db199SXin Li
209*9c5db199SXin Li        return False
210*9c5db199SXin Li
211*9c5db199SXin Li    def ModemIsRegistered(self):
212*9c5db199SXin Li        """Ensure that modem is registered on the network."""
213*9c5db199SXin Li        return self._GetRegistrationState()
214*9c5db199SXin Li
215*9c5db199SXin Li    def ModemIsRegisteredUsing(self, technology):
216*9c5db199SXin Li        """Ensure that modem is registered on the network with a technology."""
217*9c5db199SXin Li        if not self.ModemIsRegistered():
218*9c5db199SXin Li            return False
219*9c5db199SXin Li
220*9c5db199SXin Li        reported_tech = self.GetAccessTechnology()
221*9c5db199SXin Li
222*9c5db199SXin Li        # TODO(jglasgow): Remove this mapping.  Basestation and
223*9c5db199SXin Li        # reported technology should be identical.
224*9c5db199SXin Li        BASESTATION_TO_REPORTED_TECHNOLOGY = {
225*9c5db199SXin Li            cellular.Technology.GPRS: cellular.Technology.GPRS,
226*9c5db199SXin Li            cellular.Technology.EGPRS: cellular.Technology.EGPRS,
227*9c5db199SXin Li            cellular.Technology.WCDMA: cellular.Technology.HSDUPA,
228*9c5db199SXin Li            cellular.Technology.HSDPA: cellular.Technology.HSDUPA,
229*9c5db199SXin Li            cellular.Technology.HSUPA: cellular.Technology.HSDUPA,
230*9c5db199SXin Li            cellular.Technology.HSDUPA: cellular.Technology.HSDUPA,
231*9c5db199SXin Li            cellular.Technology.HSPA_PLUS: cellular.Technology.HSPA_PLUS
232*9c5db199SXin Li        }
233*9c5db199SXin Li
234*9c5db199SXin Li        return BASESTATION_TO_REPORTED_TECHNOLOGY[technology] == reported_tech
235*9c5db199SXin Li
236*9c5db199SXin Li    def IsConnectingOrDisconnecting(self):
237*9c5db199SXin Li        props = self.GetAll(Modem.MODEM_INTERFACE)
238*9c5db199SXin Li        return props['State'] in [
239*9c5db199SXin Li            Modem._MM_MODEM_STATE_CONNECTING,
240*9c5db199SXin Li            Modem._MM_MODEM_STATE_DISCONNECTING
241*9c5db199SXin Li        ]
242*9c5db199SXin Li
243*9c5db199SXin Li    def IsEnabled(self):
244*9c5db199SXin Li        props = self.GetAll(Modem.MODEM_INTERFACE)
245*9c5db199SXin Li        return props['Enabled']
246*9c5db199SXin Li
247*9c5db199SXin Li    def IsDisabled(self):
248*9c5db199SXin Li        return not self.IsEnabled()
249*9c5db199SXin Li
250*9c5db199SXin Li    def Enable(self, enable, **kwargs):
251*9c5db199SXin Li        self.Modem().Enable(enable, timeout=MODEM_TIMEOUT, **kwargs)
252*9c5db199SXin Li
253*9c5db199SXin Li    def Connect(self, props):
254*9c5db199SXin Li        self.SimpleModem().Connect(props, timeout=MODEM_TIMEOUT)
255*9c5db199SXin Li
256*9c5db199SXin Li    def Disconnect(self):
257*9c5db199SXin Li        self.Modem().Disconnect(timeout=MODEM_TIMEOUT)
258*9c5db199SXin Li
259*9c5db199SXin Li
260*9c5db199SXin Liclass ModemManager(object):
261*9c5db199SXin Li    """An object which talks to a ModemManager service."""
262*9c5db199SXin Li    INTERFACE = 'org.freedesktop.ModemManager'
263*9c5db199SXin Li
264*9c5db199SXin Li    def __init__(self, provider=None):
265*9c5db199SXin Li        self.bus = dbus.SystemBus()
266*9c5db199SXin Li        self.provider = provider or os.getenv('MMPROVIDER') or 'org.chromium'
267*9c5db199SXin Li        self.service = '%s.ModemManager' % self.provider
268*9c5db199SXin Li        self.path = '/%s/ModemManager' % (self.provider.replace('.', '/'))
269*9c5db199SXin Li        self.manager = dbus.Interface(
270*9c5db199SXin Li            self.bus.get_object(self.service, self.path),
271*9c5db199SXin Li            ModemManager.INTERFACE)
272*9c5db199SXin Li
273*9c5db199SXin Li    def EnumerateDevices(self):
274*9c5db199SXin Li        return self.manager.EnumerateDevices()
275*9c5db199SXin Li
276*9c5db199SXin Li    def GetModem(self, path):
277*9c5db199SXin Li        return Modem(self, path)
278*9c5db199SXin Li
279*9c5db199SXin Li    def SetDebugLogging(self):
280*9c5db199SXin Li        self.manager.SetLogging('debug')
281