275 lines
9.8 KiB
Python
275 lines
9.8 KiB
Python
#!/usr/bin/python2
|
|
# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
|
|
# Use of this source code is governed by a BSD-style license that can be
|
|
# found in the LICENSE file.
|
|
|
|
import os
|
|
|
|
from autotest_lib.client.cros.cellular import cellular
|
|
import dbus
|
|
|
|
MODEM_TIMEOUT=60
|
|
|
|
class Modem(object):
|
|
"""An object which talks to a ModemManager modem."""
|
|
MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem'
|
|
SIMPLE_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Simple'
|
|
CDMA_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Cdma'
|
|
GSM_MODEM_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm'
|
|
GOBI_MODEM_INTERFACE = 'org.chromium.ModemManager.Modem.Gobi'
|
|
GSM_CARD_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.Card'
|
|
GSM_SMS_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.SMS'
|
|
GSM_NETWORK_INTERFACE = 'org.freedesktop.ModemManager.Modem.Gsm.Network'
|
|
PROPERTIES_INTERFACE = 'org.freedesktop.DBus.Properties'
|
|
|
|
GSM_MODEM = 1
|
|
CDMA_MODEM = 2
|
|
|
|
NETWORK_PREFERENCE_AUTOMATIC = 0
|
|
NETWORK_PREFERENCE_CDMA_2000 = 1
|
|
NETWORK_PREFERENCE_EVDO_1X = 2
|
|
NETWORK_PREFERENCE_GSM = 3
|
|
NETWORK_PREFERENCE_WCDMA = 4
|
|
|
|
# MM_MODEM_GSM_ACCESS_TECH (not exported)
|
|
# From /usr/include/mm/mm-modem.h
|
|
_MM_MODEM_GSM_ACCESS_TECH_UNKNOWN = 0
|
|
_MM_MODEM_GSM_ACCESS_TECH_GSM = 1
|
|
_MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT = 2
|
|
_MM_MODEM_GSM_ACCESS_TECH_GPRS = 3
|
|
_MM_MODEM_GSM_ACCESS_TECH_EDGE = 4
|
|
_MM_MODEM_GSM_ACCESS_TECH_UMTS = 5
|
|
_MM_MODEM_GSM_ACCESS_TECH_HSDPA = 6
|
|
_MM_MODEM_GSM_ACCESS_TECH_HSUPA = 7
|
|
_MM_MODEM_GSM_ACCESS_TECH_HSPA = 8
|
|
|
|
# MM_MODEM_STATE (not exported)
|
|
# From /usr/include/mm/mm-modem.h
|
|
_MM_MODEM_STATE_UNKNOWN = 0
|
|
_MM_MODEM_STATE_DISABLED = 10
|
|
_MM_MODEM_STATE_DISABLING = 20
|
|
_MM_MODEM_STATE_ENABLING = 30
|
|
_MM_MODEM_STATE_ENABLED = 40
|
|
_MM_MODEM_STATE_SEARCHING = 50
|
|
_MM_MODEM_STATE_REGISTERED = 60
|
|
_MM_MODEM_STATE_DISCONNECTING = 70
|
|
_MM_MODEM_STATE_CONNECTING = 80
|
|
_MM_MODEM_STATE_CONNECTED = 90
|
|
|
|
# Mapping of modem technologies to cellular technologies
|
|
_ACCESS_TECH_TO_TECHNOLOGY = {
|
|
_MM_MODEM_GSM_ACCESS_TECH_GSM: cellular.Technology.WCDMA,
|
|
_MM_MODEM_GSM_ACCESS_TECH_GSM_COMPACT: cellular.Technology.WCDMA,
|
|
_MM_MODEM_GSM_ACCESS_TECH_GPRS: cellular.Technology.GPRS,
|
|
_MM_MODEM_GSM_ACCESS_TECH_EDGE: cellular.Technology.EGPRS,
|
|
_MM_MODEM_GSM_ACCESS_TECH_UMTS: cellular.Technology.WCDMA,
|
|
_MM_MODEM_GSM_ACCESS_TECH_HSDPA: cellular.Technology.HSDPA,
|
|
_MM_MODEM_GSM_ACCESS_TECH_HSUPA: cellular.Technology.HSUPA,
|
|
_MM_MODEM_GSM_ACCESS_TECH_HSPA: cellular.Technology.HSDUPA,
|
|
}
|
|
|
|
def __init__(self, manager, path):
|
|
self.manager = manager
|
|
self.bus = manager.bus
|
|
self.service = manager.service
|
|
self.path = path
|
|
|
|
def Modem(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.MODEM_INTERFACE)
|
|
|
|
def SimpleModem(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.SIMPLE_MODEM_INTERFACE)
|
|
|
|
def CdmaModem(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.CDMA_MODEM_INTERFACE)
|
|
|
|
def GobiModem(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.GOBI_MODEM_INTERFACE)
|
|
|
|
def GsmModem(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.GSM_MODEM_INTERFACE)
|
|
|
|
def GsmCard(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.GSM_CARD_INTERFACE)
|
|
|
|
def GsmSms(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.GSM_SMS_INTERFACE)
|
|
|
|
def GsmNetwork(self):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
return dbus.Interface(obj, Modem.GSM_NETWORK_INTERFACE)
|
|
|
|
def GetAll(self, iface):
|
|
obj = self.bus.get_object(self.service, self.path)
|
|
obj_iface = dbus.Interface(obj, Modem.PROPERTIES_INTERFACE)
|
|
return obj_iface.GetAll(iface)
|
|
|
|
def _GetModemInterfaces(self):
|
|
return [
|
|
Modem.MODEM_INTERFACE,
|
|
Modem.SIMPLE_MODEM_INTERFACE,
|
|
Modem.CDMA_MODEM_INTERFACE,
|
|
Modem.GSM_MODEM_INTERFACE,
|
|
Modem.GSM_NETWORK_INTERFACE,
|
|
Modem.GOBI_MODEM_INTERFACE]
|
|
|
|
|
|
@staticmethod
|
|
def _CopyPropertiesCheckUnique(src, dest):
|
|
"""Copies properties from |src| to |dest| and makes sure there are no
|
|
duplicate properties that have different values."""
|
|
for key, value in src.iteritems():
|
|
if key in dest and value != dest[key]:
|
|
raise KeyError('Duplicate property %s, different values '
|
|
'("%s", "%s")' % (key, value, dest[key]))
|
|
dest[key] = value
|
|
|
|
def GetModemProperties(self):
|
|
"""Returns all DBus Properties of all the modem interfaces."""
|
|
props = dict()
|
|
for iface in self._GetModemInterfaces():
|
|
try:
|
|
iface_props = self.GetAll(iface)
|
|
except dbus.exceptions.DBusException:
|
|
continue
|
|
if iface_props:
|
|
self._CopyPropertiesCheckUnique(iface_props, props)
|
|
|
|
status = self.SimpleModem().GetStatus()
|
|
if 'meid' in status:
|
|
props['Meid'] = status['meid']
|
|
if 'imei' in status:
|
|
props['Imei'] = status['imei']
|
|
if 'imsi' in status:
|
|
props['Imsi'] = status['imsi']
|
|
if 'esn' in status:
|
|
props['Esn'] = status['esn']
|
|
|
|
# Operator information is not exposed through the properties interface.
|
|
# Try to get it directly. This may fail on a disabled modem.
|
|
try:
|
|
network = self.GsmNetwork()
|
|
_, operator_code, operator_name = network.GetRegistrationInfo()
|
|
if operator_code:
|
|
props['OperatorCode'] = operator_code
|
|
if operator_name:
|
|
props['OperatorName'] = operator_name
|
|
except dbus.DBusException:
|
|
pass
|
|
|
|
return props
|
|
|
|
def GetAccessTechnology(self):
|
|
"""Returns the modem access technology."""
|
|
props = self.GetModemProperties()
|
|
tech = props.get('AccessTechnology')
|
|
return Modem._ACCESS_TECH_TO_TECHNOLOGY[tech]
|
|
|
|
def GetCurrentTechnologyFamily(self):
|
|
"""Returns the modem technology family."""
|
|
try:
|
|
self.GetAll(Modem.GSM_CARD_INTERFACE)
|
|
return cellular.TechnologyFamily.UMTS
|
|
except dbus.exceptions.DBusException:
|
|
return cellular.TechnologyFamily.CDMA
|
|
|
|
def GetVersion(self):
|
|
"""Returns the modem version information."""
|
|
return self.Modem().GetInfo()[2]
|
|
|
|
def _GetRegistrationState(self):
|
|
try:
|
|
network = self.GsmNetwork()
|
|
(status, unused_code, unused_name) = network.GetRegistrationInfo()
|
|
# TODO(jglasgow): HOME - 1, ROAMING - 5
|
|
return status == 1 or status == 5
|
|
except dbus.exceptions.DBusException:
|
|
pass
|
|
|
|
cdma_modem = self.CdmaModem()
|
|
try:
|
|
cdma, evdo = cdma_modem.GetRegistrationState()
|
|
return cdma > 0 or evdo > 0
|
|
except dbus.exceptions.DBusException:
|
|
pass
|
|
|
|
return False
|
|
|
|
def ModemIsRegistered(self):
|
|
"""Ensure that modem is registered on the network."""
|
|
return self._GetRegistrationState()
|
|
|
|
def ModemIsRegisteredUsing(self, technology):
|
|
"""Ensure that modem is registered on the network with a technology."""
|
|
if not self.ModemIsRegistered():
|
|
return False
|
|
|
|
reported_tech = self.GetAccessTechnology()
|
|
|
|
# TODO(jglasgow): Remove this mapping. Basestation and
|
|
# reported technology should be identical.
|
|
BASESTATION_TO_REPORTED_TECHNOLOGY = {
|
|
cellular.Technology.GPRS: cellular.Technology.GPRS,
|
|
cellular.Technology.EGPRS: cellular.Technology.EGPRS,
|
|
cellular.Technology.WCDMA: cellular.Technology.HSDUPA,
|
|
cellular.Technology.HSDPA: cellular.Technology.HSDUPA,
|
|
cellular.Technology.HSUPA: cellular.Technology.HSDUPA,
|
|
cellular.Technology.HSDUPA: cellular.Technology.HSDUPA,
|
|
cellular.Technology.HSPA_PLUS: cellular.Technology.HSPA_PLUS
|
|
}
|
|
|
|
return BASESTATION_TO_REPORTED_TECHNOLOGY[technology] == reported_tech
|
|
|
|
def IsConnectingOrDisconnecting(self):
|
|
props = self.GetAll(Modem.MODEM_INTERFACE)
|
|
return props['State'] in [
|
|
Modem._MM_MODEM_STATE_CONNECTING,
|
|
Modem._MM_MODEM_STATE_DISCONNECTING
|
|
]
|
|
|
|
def IsEnabled(self):
|
|
props = self.GetAll(Modem.MODEM_INTERFACE)
|
|
return props['Enabled']
|
|
|
|
def IsDisabled(self):
|
|
return not self.IsEnabled()
|
|
|
|
def Enable(self, enable, **kwargs):
|
|
self.Modem().Enable(enable, timeout=MODEM_TIMEOUT, **kwargs)
|
|
|
|
def Connect(self, props):
|
|
self.SimpleModem().Connect(props, timeout=MODEM_TIMEOUT)
|
|
|
|
def Disconnect(self):
|
|
self.Modem().Disconnect(timeout=MODEM_TIMEOUT)
|
|
|
|
|
|
class ModemManager(object):
|
|
"""An object which talks to a ModemManager service."""
|
|
INTERFACE = 'org.freedesktop.ModemManager'
|
|
|
|
def __init__(self, provider=None):
|
|
self.bus = dbus.SystemBus()
|
|
self.provider = provider or os.getenv('MMPROVIDER') or 'org.chromium'
|
|
self.service = '%s.ModemManager' % self.provider
|
|
self.path = '/%s/ModemManager' % (self.provider.replace('.', '/'))
|
|
self.manager = dbus.Interface(
|
|
self.bus.get_object(self.service, self.path),
|
|
ModemManager.INTERFACE)
|
|
|
|
def EnumerateDevices(self):
|
|
return self.manager.EnumerateDevices()
|
|
|
|
def GetModem(self, path):
|
|
return Modem(self, path)
|
|
|
|
def SetDebugLogging(self):
|
|
self.manager.SetLogging('debug')
|