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