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