#!/usr/bin/python # Copyright (c) 2012 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. """Implement a modem proxy to talk to a ModemManager1 modem.""" from autotest_lib.client.common_lib import error from autotest_lib.client.cros.cellular import cellular from autotest_lib.client.cros.cellular import mm1 from autotest_lib.client.cros.cellular import mm1_constants import dbus import cellular_logging log = cellular_logging.SetupCellularLogging('modem1') MODEM_TIMEOUT = 60 class Modem(object): """An object which talks to a ModemManager1 modem.""" # 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 << 1 _MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 1 << 2 _MM_MODEM_GSM_ACCESS_TECH_GPRS = 1 << 3 _MM_MODEM_GSM_ACCESS_TECH_EDGE = 1 << 4 _MM_MODEM_GSM_ACCESS_TECH_UMTS = 1 << 5 _MM_MODEM_GSM_ACCESS_TECH_HSDPA = 1 << 6 _MM_MODEM_GSM_ACCESS_TECH_HSUPA = 1 << 7 _MM_MODEM_GSM_ACCESS_TECH_HSPA = 1 << 8 # 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, mm1.MODEM_INTERFACE) def SimpleModem(self): obj = self.bus.get_object(self.service, self.path) return dbus.Interface(obj, mm1.MODEM_SIMPLE_INTERFACE) def GsmModem(self): obj = self.bus.get_object(self.service, self.path) return dbus.Interface(obj, mm1.MODEM_MODEM3GPP_INTERFACE) def CdmaModem(self): obj = self.bus.get_object(self.service, self.path) return dbus.Interface(obj, mm1.MODEM_MODEMCDMA_INTERFACE) def Sim(self): obj = self.bus.get_object(self.service, self.path) return dbus.Interface(obj, mm1.SIM_INTERFACE) def PropertiesInterface(self): obj = self.bus.get_object(self.service, self.path) return dbus.Interface(obj, dbus.PROPERTIES_IFACE) def GetAll(self, iface): obj_iface = self.PropertiesInterface() return obj_iface.GetAll(iface) def _GetModemInterfaces(self): return [ mm1.MODEM_INTERFACE, mm1.MODEM_SIMPLE_INTERFACE, mm1.MODEM_MODEM3GPP_INTERFACE, mm1.MODEM_MODEMCDMA_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) try: sim_obj = self.bus.get_object(self.service, props['Sim']) sim_props_iface = dbus.Interface(sim_obj, dbus.PROPERTIES_IFACE) sim_props = sim_props_iface.GetAll(mm1.SIM_INTERFACE) # SIM cards may store an empty operator name or store a value # different from the one obtained OTA. Rename the 'OperatorName' # property obtained from the SIM card to 'SimOperatorName' in # order to avoid a potential conflict with the 'OperatorName' # property obtained from the Modem3gpp interface. if 'OperatorName' in sim_props: sim_props['SimOperatorName'] = sim_props.pop('OperatorName') self._CopyPropertiesCheckUnique(sim_props, props) except dbus.exceptions.DBusException: pass return props def GetAccessTechnology(self): """Returns the modem access technology.""" props = self.GetModemProperties() tech = props['AccessTechnologies'] return Modem._ACCESS_TECH_TO_TECHNOLOGY[tech] def GetCurrentTechnologyFamily(self): """Returns the modem technology family.""" props = self.GetAll(mm1.MODEM_INTERFACE) capabilities = props.get('SupportedCapabilities') if self._IsCDMAModem(capabilities): return cellular.TechnologyFamily.CDMA if self._Is3GPPModem(capabilities): return cellular.TechnologyFamily.UMTS raise error.TestError('Invalid modem type') def GetVersion(self): """Returns the modem version information.""" return self.GetModemProperties()['Revision'] def _IsCDMAModem(self, capabilities): return mm1_constants.MM_MODEM_CAPABILITY_CDMA_EVDO in capabilities def _Is3GPPModem(self, capabilities): for capability in capabilities: if (capability & (mm1_constants.MM_MODEM_CAPABILITY_LTE | mm1_constants.MM_MODEM_CAPABILITY_LTE_ADVANCED | mm1_constants.MM_MODEM_CAPABILITY_GSM_UMTS)): return True return False def _CDMAModemIsRegistered(self): modem_status = self.SimpleModem().GetStatus() cdma1x_state = modem_status.get( 'cdma-cdma1x-registration-state', mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) evdo_state = modem_status.get( 'cdma-evdo-registration-state', mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) return (cdma1x_state != mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN or evdo_state != mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN) def _3GPPModemIsRegistered(self): modem_status = self.SimpleModem().GetStatus() state = modem_status.get('m3gpp-registration-state') return (state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_HOME or state == mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_ROAMING) def ModemIsRegistered(self): """Ensure that modem is registered on the network.""" props = self.GetAll(mm1.MODEM_INTERFACE) capabilities = props.get('SupportedCapabilities') if self._IsCDMAModem(capabilities): return self._CDMAModemIsRegistered() elif self._Is3GPPModem(capabilities): return self._3GPPModemIsRegistered() else: raise error.TestError('Invalid modem type') 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.GPRS, 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(mm1.MODEM_INTERFACE) return props['State'] in [ mm1.MM_MODEM_STATE_CONNECTING, mm1.MM_MODEM_STATE_DISCONNECTING ] def IsEnabled(self): props = self.GetAll(mm1.MODEM_INTERFACE) return props['State'] in [ mm1.MM_MODEM_STATE_ENABLED, mm1.MM_MODEM_STATE_SEARCHING, mm1.MM_MODEM_STATE_REGISTERED, mm1.MM_MODEM_STATE_DISCONNECTING, mm1.MM_MODEM_STATE_CONNECTING, mm1.MM_MODEM_STATE_CONNECTED ] def IsDisabled(self): props = self.GetAll(mm1.MODEM_INTERFACE) return props['State'] == mm1.MM_MODEM_STATE_DISABLED 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.SimpleModem().Disconnect('/', timeout=MODEM_TIMEOUT) class ModemManager(object): """An object which talks to a ModemManager1 service.""" def __init__(self): self.bus = dbus.SystemBus() self.service = mm1.MODEM_MANAGER_INTERFACE self.path = mm1.OMM self.manager = dbus.Interface( self.bus.get_object(self.service, self.path), mm1.MODEM_MANAGER_INTERFACE) self.objectmanager = dbus.Interface( self.bus.get_object(self.service, self.path), mm1.OFDOM) def EnumerateDevices(self): devices = self.objectmanager.GetManagedObjects() return devices.keys() def GetModem(self, path): return Modem(self, path) def SetDebugLogging(self): self.manager.SetLogging('DEBUG')