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