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