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