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