| #!/usr/bin/python2 |
| # Copyright (c) 2013 The Chromium OS Authors. All rights reserved. |
| # Use of this source code is governed by a BSD-style license that can be |
| # found in the LICENSE file. |
| |
| import os |
| |
| from autotest_lib.client.cros.cellular import cellular |
| import dbus |
| |
| MODEM_TIMEOUT=60 |
| |
| class Modem(object): |
| """An object which talks to a ModemManager modem.""" |
| MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem' |
| SIMPLE_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Simple' |
| CDMA_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Cdma' |
| GSM_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm' |
| GOBI_MODEM_INTERFACE = 'org.chromium.ModemManager.Modem.Gobi' |
| GSM_CARD_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.Card' |
| GSM_SMS_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.SMS' |
| GSM_NETWORK_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.Network' |
| PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties' |
| |
| GSM_MODEM = 1 |
| CDMA_MODEM = 2 |
| |
| NETWORK_PREFERENCE_AUTOMATIC = 0 |
| NETWORK_PREFERENCE_CDMA_2000 = 1 |
| NETWORK_PREFERENCE_EVDO_1X = 2 |
| NETWORK_PREFERENCE_GSM = 3 |
| NETWORK_PREFERENCE_WCDMA = 4 |
| |
| # MM_MODEM_GSM_ACCESS_TECH (not exported) |
| # From /usr/include/mm/mm-modem.h |
| _MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0 |
| _MM_MODEM_GSM_ACCESS_TECH_GSM = 1 |
| _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 2 |
| _MM_MODEM_GSM_ACCESS_TECH_GPRS = 3 |
| _MM_MODEM_GSM_ACCESS_TECH_EDGE = 4 |
| _MM_MODEM_GSM_ACCESS_TECH_UMTS = 5 |
| _MM_MODEM_GSM_ACCESS_TECH_HSDPA = 6 |
| _MM_MODEM_GSM_ACCESS_TECH_HSUPA = 7 |
| _MM_MODEM_GSM_ACCESS_TECH_HSPA = 8 |
| |
| # MM_MODEM_STATE (not exported) |
| # From /usr/include/mm/mm-modem.h |
| _MM_MODEM_STATE_UNKNOWN = 0 |
| _MM_MODEM_STATE_DISABLED = 10 |
| _MM_MODEM_STATE_DISABLING = 20 |
| _MM_MODEM_STATE_ENABLING = 30 |
| _MM_MODEM_STATE_ENABLED = 40 |
| _MM_MODEM_STATE_SEARCHING = 50 |
| _MM_MODEM_STATE_REGISTERED = 60 |
| _MM_MODEM_STATE_DISCONNECTING = 70 |
| _MM_MODEM_STATE_CONNECTING = 80 |
| _MM_MODEM_STATE_CONNECTED = 90 |
| |
| # Mapping of modem technologies to cellular technologies |
| _ACCESS_TECH_TO_TECHNOLOGY = { |
| _MM_MODEM_GSM_ACCESS_TECH_GSM: cellular.Technology.WCDMA, |
| _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT: cellular.Technology.WCDMA, |
| _MM_MODEM_GSM_ACCESS_TECH_GPRS: cellular.Technology.GPRS, |
| _MM_MODEM_GSM_ACCESS_TECH_EDGE: cellular.Technology.EGPRS, |
| _MM_MODEM_GSM_ACCESS_TECH_UMTS: cellular.Technology.WCDMA, |
| _MM_MODEM_GSM_ACCESS_TECH_HSDPA: cellular.Technology.HSDPA, |
| _MM_MODEM_GSM_ACCESS_TECH_HSUPA: cellular.Technology.HSUPA, |
| _MM_MODEM_GSM_ACCESS_TECH_HSPA: cellular.Technology.HSDUPA, |
| } |
| |
| def __init__(self, manager, path): |
| self.manager = manager |
| self.bus = manager.bus |
| self.service = manager.service |
| self.path = path |
| |
| def Modem(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.MODEM_INTERFACE) |
| |
| def SimpleModem(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.SIMPLE_MODEM_INTERFACE) |
| |
| def CdmaModem(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.CDMA_MODEM_INTERFACE) |
| |
| def GobiModem(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.GOBI_MODEM_INTERFACE) |
| |
| def GsmModem(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.GSM_MODEM_INTERFACE) |
| |
| def GsmCard(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.GSM_CARD_INTERFACE) |
| |
| def GsmSms(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.GSM_SMS_INTERFACE) |
| |
| def GsmNetwork(self): |
| obj = self.bus.get_object(self.service, self.path) |
| return dbus.Interface(obj, Modem.GSM_NETWORK_INTERFACE) |
| |
| def GetAll(self, iface): |
| obj = self.bus.get_object(self.service, self.path) |
| obj_iface = dbus.Interface(obj, Modem.PROPERTIES_INTERFACE) |
| return obj_iface.GetAll(iface) |
| |
| def _GetModemInterfaces(self): |
| return [ |
| Modem.MODEM_INTERFACE, |
| Modem.SIMPLE_MODEM_INTERFACE, |
| Modem.CDMA_MODEM_INTERFACE, |
| Modem.GSM_MODEM_INTERFACE, |
| Modem.GSM_NETWORK_INTERFACE, |
| Modem.GOBI_MODEM_INTERFACE] |
| |
| |
| @staticmethod |
| def _CopyPropertiesCheckUnique(src, dest): |
| """Copies properties from |src| to |dest| and makes sure there are no |
| duplicate properties that have different values.""" |
| for key, value in src.iteritems(): |
| if key in dest and value != dest[key]: |
| raise KeyError('Duplicate property %s, different values ' |
| '("%s", "%s")' % (key, value, dest[key])) |
| dest[key] = value |
| |
| def GetModemProperties(self): |
| """Returns all DBus Properties of all the modem interfaces.""" |
| props = dict() |
| for iface in self._GetModemInterfaces(): |
| try: |
| iface_props = self.GetAll(iface) |
| except dbus.exceptions.DBusException: |
| continue |
| if iface_props: |
| self._CopyPropertiesCheckUnique(iface_props, props) |
| |
| status = self.SimpleModem().GetStatus() |
| if 'meid' in status: |
| props['Meid'] = status['meid'] |
| if 'imei' in status: |
| props['Imei'] = status['imei'] |
| if 'imsi' in status: |
| props['Imsi'] = status['imsi'] |
| if 'esn' in status: |
| props['Esn'] = status['esn'] |
| |
| # Operator information is not exposed through the properties interface. |
| # Try to get it directly. This may fail on a disabled modem. |
| try: |
| network = self.GsmNetwork() |
| _, operator_code, operator_name = network.GetRegistrationInfo() |
| if operator_code: |
| props['OperatorCode'] = operator_code |
| if operator_name: |
| props['OperatorName'] = operator_name |
| except dbus.DBusException: |
| pass |
| |
| return props |
| |
| def GetAccessTechnology(self): |
| """Returns the modem access technology.""" |
| props = self.GetModemProperties() |
| tech = props.get('AccessTechnology') |
| return Modem._ACCESS_TECH_TO_TECHNOLOGY[tech] |
| |
| def GetCurrentTechnologyFamily(self): |
| """Returns the modem technology family.""" |
| try: |
| self.GetAll(Modem.GSM_CARD_INTERFACE) |
| return cellular.TechnologyFamily.UMTS |
| except dbus.exceptions.DBusException: |
| return cellular.TechnologyFamily.CDMA |
| |
| def GetVersion(self): |
| """Returns the modem version information.""" |
| return self.Modem().GetInfo()[2] |
| |
| def _GetRegistrationState(self): |
| try: |
| network = self.GsmNetwork() |
| (status, unused_code, unused_name) = network.GetRegistrationInfo() |
| # TODO(jglasgow): HOME - 1, ROAMING - 5 |
| return status == 1 or status == 5 |
| except dbus.exceptions.DBusException: |
| pass |
| |
| cdma_modem = self.CdmaModem() |
| try: |
| cdma, evdo = cdma_modem.GetRegistrationState() |
| return cdma > 0 or evdo > 0 |
| except dbus.exceptions.DBusException: |
| pass |
| |
| return False |
| |
| def ModemIsRegistered(self): |
| """Ensure that modem is registered on the network.""" |
| return self._GetRegistrationState() |
| |
| def ModemIsRegisteredUsing(self, technology): |
| """Ensure that modem is registered on the network with a technology.""" |
| if not self.ModemIsRegistered(): |
| return False |
| |
| reported_tech = self.GetAccessTechnology() |
| |
| # TODO(jglasgow): Remove this mapping. Basestation and |
| # reported technology should be identical. |
| BASESTATION_TO_REPORTED_TECHNOLOGY = { |
| cellular.Technology.GPRS: cellular.Technology.GPRS, |
| cellular.Technology.EGPRS: cellular.Technology.EGPRS, |
| cellular.Technology.WCDMA: cellular.Technology.HSDUPA, |
| cellular.Technology.HSDPA: cellular.Technology.HSDUPA, |
| cellular.Technology.HSUPA: cellular.Technology.HSDUPA, |
| cellular.Technology.HSDUPA: cellular.Technology.HSDUPA, |
| cellular.Technology.HSPA_PLUS: cellular.Technology.HSPA_PLUS |
| } |
| |
| return BASESTATION_TO_REPORTED_TECHNOLOGY[technology] == reported_tech |
| |
| def IsConnectingOrDisconnecting(self): |
| props = self.GetAll(Modem.MODEM_INTERFACE) |
| return props['State'] in [ |
| Modem._MM_MODEM_STATE_CONNECTING, |
| Modem._MM_MODEM_STATE_DISCONNECTING |
| ] |
| |
| def IsEnabled(self): |
| props = self.GetAll(Modem.MODEM_INTERFACE) |
| return props['Enabled'] |
| |
| def IsDisabled(self): |
| return not self.IsEnabled() |
| |
| def Enable(self, enable, **kwargs): |
| self.Modem().Enable(enable, timeout=MODEM_TIMEOUT, **kwargs) |
| |
| def Connect(self, props): |
| self.SimpleModem().Connect(props, timeout=MODEM_TIMEOUT) |
| |
| def Disconnect(self): |
| self.Modem().Disconnect(timeout=MODEM_TIMEOUT) |
| |
| |
| class ModemManager(object): |
| """An object which talks to a ModemManager service.""" |
| INTERFACE = 'org.freedesktop.ModemManager' |
| |
| def __init__(self, provider=None): |
| self.bus = dbus.SystemBus() |
| self.provider = provider or os.getenv('MMPROVIDER') or 'org.chromium' |
| self.service = '%s.ModemManager' % self.provider |
| self.path = '/%s/ModemManager' % (self.provider.replace('.', '/')) |
| self.manager = dbus.Interface( |
| self.bus.get_object(self.service, self.path), |
| ModemManager.INTERFACE) |
| |
| def EnumerateDevices(self): |
| return self.manager.EnumerateDevices() |
| |
| def GetModem(self, path): |
| return Modem(self, path) |
| |
| def SetDebugLogging(self): |
| self.manager.SetLogging('debug') |