普通文本  |  275行  |  9.84 KB

#!/usr/bin/python
# 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')