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