15eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow#!/usr/bin/env python
25eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
35eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow# Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
45eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow# Use of this source code is governed by a BSD-style license that can be
55eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow# found in the LICENSE file.
65eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow"""An implementation of the ModemManager1 DBUS interface.
75eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
85eff4080ae483fb090c1e361f9bab5a367398e8fJason GlasgowThis modem mimics a GSM (eventually LTE & CDMA) modem and allows a
95eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowuser to test shill and UI behaviors when a supported SIM is inserted
10d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowinto the device.  Invoked with the proper flags it can test that SMS
11d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowmessages are deliver to the UI.
125eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
135eff4080ae483fb090c1e361f9bab5a367398e8fJason GlasgowThis program creates a virtual network interface to simulate the
145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgownetwork interface of a modem.  It depends on modemmanager-next to
155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowset the dbus permissions properly.
165eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
175eff4080ae483fb090c1e361f9bab5a367398e8fJason GlasgowTODO:
185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow   * Use more appropriate values for many of the properties
195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow   * Support all ModemManager1 interfaces
205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow   * implement LTE modems
215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow   * implement CDMA modems
225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow"""
235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowfrom optparse import OptionParser
255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport logging
265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport os
275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport signal
285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport string
295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport subprocess
305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport sys
315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport time
325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
335eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport dbus
345eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowfrom dbus.exceptions import DBusException
355eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport dbus.mainloop.glib
365eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport dbus.service
375eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowfrom dbus.types import Int32
385eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowfrom dbus.types import ObjectPath
395eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowfrom dbus.types import Struct
405eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowfrom dbus.types import UInt32
415eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport glib
425eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport gobject
435eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowimport mm1
445eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
455eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
465eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow# Miscellaneous delays to simulate a modem
475eff4080ae483fb090c1e361f9bab5a367398e8fJason GlasgowDEFAULT_CONNECT_DELAY_MS = 1500
485eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
495eff4080ae483fb090c1e361f9bab5a367398e8fJason GlasgowDEFAULT_CARRIER = 'att'
505eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
515eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
525eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowclass DBusObjectWithProperties(dbus.service.Object):
535eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """Implements the org.freedesktop.DBus.Properties interface.
545eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
555eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    Implements the org.freedesktop.DBus.Properties interface, specifically
565eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    the Get and GetAll methods.  Class which inherit from this class must
575eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    implement the InterfacesAndProperties function which will return a
585eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    dictionary of all interfaces and the properties defined on those interfaces.
595eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """
605eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
615eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __init__(self, bus, path):
625eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        dbus.service.Object.__init__(self, bus, path)
635eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
645eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(dbus.PROPERTIES_IFACE,
655eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         in_signature='ss', out_signature='v')
665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Get(self, interface, property_name, *args, **kwargs):
675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Returns: The value of property_name on interface."""
685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('%s: Get %s, %s', self.path, interface, property_name)
695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        interfaces = self.InterfacesAndProperties()
705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        properties = interfaces.get(interface, None)
715eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if property_name in properties:
725eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            return properties[property_name]
735eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        raise dbus.exceptions.DBusException(
745eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MODEM_MANAGER_INTERFACE + '.UnknownProperty',
755eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Property %s not defined for interface %s' %
765eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            (property_name, interface))
775eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
785eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(dbus.PROPERTIES_IFACE,
795eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         in_signature='s', out_signature='a{sv}')
805eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def GetAll(self, interface, *args, **kwargs):
815eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Returns: A dictionary. The properties on interface."""
825eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('%s: GetAll %s', self.path, interface)
835eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        interfaces = self.InterfacesAndProperties()
845eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        properties = interfaces.get(interface, None)
855eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if properties is not None:
865eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            return properties
875eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        raise dbus.exceptions.DBusException(
885eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MODEM_MANAGER_INTERFACE + '.UnknownInterface',
895eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Object does not implement the %s interface' % interface)
905eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
915eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def InterfacesAndProperties(self):
925eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Subclasses must implement this function.
935eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
945eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Returns:
955eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            A dictionary of interfaces where the values are dictionaries
965eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            of dbus properties.
975eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
985eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        pass
995eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowclass SIM(DBusObjectWithProperties):
1025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """SIM Object.
1035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow       Mock SIM Card and the typical information it might contain.
1055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow       SIM cards of different carriers can be created by providing
1065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow       the MCC, MNC, operator name, imsi, and msin.  SIM objects are
1075eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow       passed to the Modem during Modem initialization.
1085eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """
1095eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1105eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    DEFAULT_MCC = '310'
1115eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    DEFAULT_MNC = '090'
1125eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    DEFAULT_OPERATOR = 'AT&T'
1135eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    DEFAULT_MSIN = '1234567890'
1145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    DEFAULT_IMSI = '888999111'
1155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    MCC_LIST = {
1165eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'us': '310',
1175eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'de': '262',
1185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'es': '214',
1195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'fr': '208',
1205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'gb': '234',
1215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'it': '222',
1225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'nl': '204',
1235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    }
1245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    CARRIERS = {
1255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'att': ('us', '090', 'AT&T'),
1265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'tmobile': ('us', '026', 'T-Mobile'),
1275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'simyo': ('de', '03', 'simyo'),
1285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'movistar': ('es', '07', 'Movistar'),
1295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'sfr': ('fr', '10', 'SFR'),
1305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'three': ('gb', '20', '3'),
1315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'threeita': ('it', '99', '3ITA'),
1325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        'kpn': ('nl', '08', 'KPN')
1335eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        }
1345eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1355eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __init__(self,
1365eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 manager,
1375eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 mcc_country='us',
1385eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 mnc=DEFAULT_MNC,
1395eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 operator_name=DEFAULT_OPERATOR,
1405eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 msin=DEFAULT_MSIN,
1415eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 imsi=None,
1425eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 mcc=None,
1435eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 name='/Sim/0'):
1445eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.manager = manager
1455eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.name = name
1465eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.path = manager.path + name
1475eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.mcc = mcc or SIM.MCC_LIST.get(mcc_country, '000')
1485eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.mnc = mnc
1495eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.operator_name = operator_name
1505eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.msin = msin
1515eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.imsi = imsi or (self.mcc + self.mnc + SIM.DEFAULT_IMSI)
1525eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        DBusObjectWithProperties.__init__(self, manager.bus, self.path)
1535eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1545eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @staticmethod
1555eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def FromCarrier(carrier, manager):
1565eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Creates a SIM card object for a given carrier."""
1575eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        args = SIM.CARRIERS.get(carrier, [])
1585eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return SIM(manager, *args)
1595eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1605eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Properties(self):
1615eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return {
1625eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'SimIdentifier': self.msin,
1635eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Imsi': self.imsi,
1645eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'OperatorIdentifier': self.mcc + self.mnc,
1655eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'OperatorName': self.operator_name
1665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            }
1675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
1685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def InterfacesAndProperties(self):
1695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return {mm1.SIM_INTERFACE: self.Properties()}
1705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
171d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowclass SMS(DBusObjectWithProperties):
172d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    """SMS Object.
173d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
174d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow       Mock SMS message.
175d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    """
176d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
177d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def __init__(self, manager, name='/SMS/0', text='test',
178d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                 number='123', timestamp='12:00', smsc=''):
179d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.manager = manager
180d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.name = name
181d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.path = manager.path + name
182d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.text = text or 'test sms at %s' % name
183d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.number = number
184d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.timestamp = timestamp
185d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.smsc = smsc
186d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        DBusObjectWithProperties.__init__(self, manager.bus, self.path)
187d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
188d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def Properties(self):
189d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        # TODO(jglasgow): State, Validity, Class, Storage are also defined
190d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        return {
191d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            'Text': self.text,
192d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            'Number': self.number,
193d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            'Timestamp': self.timestamp,
194d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            'SMSC': self.smsc
195d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            }
196d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
197d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def InterfacesAndProperties(self):
198d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        return {mm1.SMS_INTERFACE: self.Properties()}
199d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
2005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowclass PseudoNetworkInterface(object):
2025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """A Pseudo network interface.
2035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    This uses a pair of network interfaces and dnsmasq to simulate the
2055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    network device normally associated with a modem.
2065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """
2075eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2085eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __init__(self, interface, base):
2095eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.interface = interface
2105eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.peer = self.interface + 'p'
2115eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.base = base
2125eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.lease_file = '/tmp/dnsmasq.%s.leases' % self.interface
2135eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.dnsmasq = None
2145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __enter__(self):
2165eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Make usable with "with" statement."""
2175eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.CreateInterface()
2185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return self
2195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __exit__(self, exception, value, traceback):
2215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Make usable with "with" statement."""
2225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.DestroyInterface()
2235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return False
2245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def CreateInterface(self):
2265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Creates a virtual interface.
2275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Creates the virtual interface self.interface as well as a peer
2295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        interface.  Runs dnsmasq on the peer interface so that a DHCP
2305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        service can offer ip addresses to the virtual interface.
2315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
2325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('ip link add name %s type veth peer name %s' % (
2335eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.interface, self.peer))
2345eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2355eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('ifconfig %s %s.1/24' % (self.peer, self.base))
2365eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('ifconfig %s up' % self.peer)
2375eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2385eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('ifconfig %s up' % self.interface)
2395eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('route add -host 255.255.255.255 dev %s' % self.peer)
2405eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.close(os.open(self.lease_file, os.O_CREAT | os.O_TRUNC))
2415eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.dnsmasq = subprocess.Popen(
2425eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            ['/usr/local/sbin/dnsmasq',
2435eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '--pid-file',
2445eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '-k',
2455eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '--dhcp-leasefile=%s' % self.lease_file,
2465eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '--dhcp-range=%s.2,%s.254' % (self.base, self.base),
2475eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '--port=0',
2485eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '--interface=%s' % self.peer,
2495eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow             '--bind-interfaces'
2505eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            ])
2515eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        # iptables rejects packets on a newly defined interface.  Fix that.
2525eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('iptables -I INPUT -i %s -j ACCEPT' % self.peer)
2535eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('iptables -I INPUT -i %s -j ACCEPT' % self.interface)
2545eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2555eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def DestroyInterface(self):
2565eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Destroys the virtual interface.
2575eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2585eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Stops dnsmasq and cleans up all on disk state.
2595eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
2605eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if self.dnsmasq:
2615eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.dnsmasq.terminate()
2625eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        try:
2635eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            os.system('route del -host 255.255.255.255')
2645eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        except:
2655eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            pass
2665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        try:
2675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            os.system('ip link del %s' % self.interface)
2685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        except:
2695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            pass
2705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('iptables -D INPUT -i %s -j ACCEPT' % self.peer)
2715eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        os.system('iptables -D INPUT -i %s -j ACCEPT' % self.interface)
2725eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if os.path.exists(self.lease_file):
2735eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            os.remove(self.lease_file)
2745eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2755eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2765eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowclass Modem(DBusObjectWithProperties):
2775eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """A Modem object that implements the ModemManager DBUS API."""
2785eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2795eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __init__(self, manager, name='/Modem/0',
2805eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 device='pseudomodem0',
2815eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 mdn='0000001234',
2825eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 meid='A100000DCE2CA0',
2835eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 carrier='CrCarrier',
2845eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 esn='EDD1EDD1',
2855eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                 sim=None):
2865eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Instantiates a Modem with some options.
2875eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
2885eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Args:
2895eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            manager: a ModemManager object.
2905eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            name: string, a dbus path name.
2915eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            device: string, the network device to use.
2925eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mdn: string, the mobile directory number.
2935eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            meid: string, the mobile equipment id (CDMA only?).
2945eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            carrier: string, the name of the carrier.
2955eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            esn: string, the electronic serial number.
2965eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            sim: a SIM object.
2975eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
298845320fc3bab52407471d494e3c601a25862933bArman Uguray        self.state = mm1.MM_MODEM_STATE_DISABLED
2995eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.manager = manager
3005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.name = name
3015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.path = manager.path + name
3025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.device = device
3035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.mdn = mdn
3045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.meid = meid
3055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.carrier = carrier
3065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.operator_name = carrier
3075eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.operator_code = '123'
3085eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.esn = esn
3095eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.registration_state = mm1.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE
3105eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.sim = sim
3115eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        DBusObjectWithProperties.__init__(self, manager.bus, self.path)
3125eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.pseudo_interface = PseudoNetworkInterface(self.device, '192.168.7')
313d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.smses = {}
3145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __enter__(self):
3165eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Make usable with "with" statement."""
3175eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.pseudo_interface.__enter__()
3185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        # Add the device to the manager only after the pseudo
3195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        # interface has been created.
3205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.manager.Add(self)
3215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return self
3225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __exit__(self, exception, value, traceback):
3245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Make usable with "with" statement."""
3255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.manager.Remove(self)
3265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return self.pseudo_interface.__exit__(exception, value, traceback)
3275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def DiscardModem(self):
3295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Discard this DBUS Object.
3305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Send a message that a modem has disappeared and deregister from DBUS.
3325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
3335eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('DiscardModem')
3345eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.remove_from_connection()
3355eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.manager.Remove(self)
3365eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3375eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def ModemProperties(self):
3385eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Return the properties of the modem object."""
3395eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        properties = {
3405eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            # 'Sim': type='o'
3415eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'ModemCapabilities': UInt32(0),
3425eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'CurrentCapabilities': UInt32(0),
3435eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'MaxBearers': UInt32(2),
3445eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'MaxActiveBearers': UInt32(2),
3455eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Manufacturer': 'Foo Electronics',
3465eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Model': 'Super Foo Modem',
3475eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Revision': '1.0',
3485eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'DeviceIdentifier': '123456789',
3495eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Device': self.device,
3505eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Driver': 'fake',
3515eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Plugin': 'Foo Plugin',
3525eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'EquipmentIdentifier': self.meid,
3535eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'UnlockRequired': UInt32(0),
3545eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            #'UnlockRetries' type='a{uu}'
3555eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MM_MODEM_PROPERTY_STATE: Int32(self.state),
3565eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'AccessTechnologies': UInt32(self.state),
3575eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'SignalQuality': Struct([UInt32(90), True], signature='ub'),
3585eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'OwnNumbers': ['6175551212'],
3595eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'SupportedModes': UInt32(0),
3605eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'AllowedModes': UInt32(0),
3615eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'PreferredMode': UInt32(0),
3625eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'SupportedBands': [UInt32(0)],
3635eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Bands': [UInt32(0)]
3645eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            }
3655eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if self.sim:
3665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            properties['Sim'] = ObjectPath(self.sim.path)
3675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return properties
3685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def InterfacesAndProperties(self):
3705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Return all supported interfaces and their properties."""
3715eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return {
3725eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MODEM_INTERFACE: self.ModemProperties(),
3735eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            }
3745eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3755eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def ChangeState(self, new_state,
3765eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                    why=mm1.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN):
3775eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('Change state from %s to %s', self.state, new_state)
3785eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.StateChanged(Int32(self.state), Int32(new_state), UInt32(why))
3795eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.PropertiesChanged(mm1.MODEM_INTERFACE,
3805eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                               {mm1.MM_MODEM_PROPERTY_STATE: Int32(new_state)},
3815eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                               [])
3825eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.state = new_state
3835eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
3845eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(mm1.MODEM_INTERFACE,
3855eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         in_signature='b', out_signature='')
3865eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Enable(self, on, *args, **kwargs):
3875eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Enables the Modem."""
3885eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('Modem: Enable %s', str(on))
3895eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if on:
3905eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state <= mm1.MM_MODEM_STATE_ENABLING:
3915eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(mm1.MM_MODEM_STATE_ENABLING)
3925eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state <= mm1.MM_MODEM_STATE_ENABLED:
3935eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(mm1.MM_MODEM_STATE_ENABLED)
3945eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state <= mm1.MM_MODEM_STATE_SEARCHING:
3955eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(mm1.MM_MODEM_STATE_SEARCHING)
3965eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            glib.timeout_add(250, self.OnRegistered)
3975eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        else:
3985eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state >= mm1.MM_MODEM_STATE_DISABLING:
3995eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(mm1.MM_MODEM_STATE_DISABLING)
4005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state >= mm1.MM_MODEM_STATE_DISABLED:
4015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(mm1.MM_MODEM_STATE_DISABLED)
4025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeRegistrationState(
4035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                    mm1.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
4045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return None
4055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def ChangeRegistrationState(self, new_state):
4075eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Updates the registration state of the modem.
4085eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4095eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Updates the registration state of the modem and broadcasts a
4105eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        DBUS signal.
4115eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4125eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Args:
4135eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow          new_state: the new registation state of the modem.
4145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
4155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if new_state != self.registration_state:
4165eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.registration_state = new_state
4175eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.PropertiesChanged(
4185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                mm1.MODEM_MODEM3GPP_INTERFACE,
4195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                {mm1.MM_MODEM3GPP_PROPERTY_REGISTRATION_STATE:
4205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                     UInt32(new_state)},
4215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                [])
4225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def OnRegistered(self):
4245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Called when the Modem is Registered."""
4255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if (self.state >= mm1.MM_MODEM_STATE_ENABLED and
4265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.state <= mm1.MM_MODEM_STATE_REGISTERED):
4275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            logging.info('Modem: Marking Registered')
4285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.ChangeRegistrationState(
4295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                mm1.MM_MODEM_3GPP_REGISTRATION_STATE_HOME)
4305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            self.ChangeState(mm1.MM_MODEM_STATE_REGISTERED)
4315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(mm1.MODEM_SIMPLE_INTERFACE, in_signature='',
4335eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         out_signature='a{sv}')
4345eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def GetStatus(self, *args, **kwargs):
4355eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Gets the general modem status.
4365eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4375eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Returns:
4385eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            A dictionary of properties.
4395eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
4405eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('Modem: GetStatus')
4415eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        properties = {
4425eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'state': UInt32(self.state),
4435eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'signal-quality': UInt32(99),
4445eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'bands': self.carrier,
4455eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'access-technology': UInt32(0),
4465eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'm3gpp-registration-state': UInt32(self.registration_state),
4475eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'm3gpp-operator-code': '123',
4485eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'm3gpp-operator-name': '123',
4495eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'cdma-cdma1x-registration-state': UInt32(99),
4505eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'cdma-evdo-registration-state': UInt32(99),
4515eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'cdma-sid': '123',
4525eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'cdma-nid': '123',
4535eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            }
4545eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if self.state >= mm1.MM_MODEM_STATE_ENABLED:
4555eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            properties['carrier'] = 'Test Network'
4565eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return properties
4575eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4585eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.signal(mm1.MODEM_INTERFACE, signature='iiu')
4595eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def StateChanged(self, old_state, new_state, why):
4605eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        pass
4615eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4625eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(mm1.MODEM_SIMPLE_INTERFACE, in_signature='a{sv}',
4635eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         out_signature='o',
4645eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         async_callbacks=('return_cb', 'raise_cb'))
4655eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Connect(self, unused_props, return_cb, raise_cb, **kwargs):
4665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Connect the modem to the network.
4675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Args:
4695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            unused_props: connection properties. See ModemManager documentation.
4705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            return_cb: function to call to return result asynchronously.
4715eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            raise_cb: function to call to raise an error asynchronously.
4725eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """
4735eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4745eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        def ConnectDone(new, why):
4755eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            logging.info('Modem: ConnectDone %s -> %s because %s',
4765eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         str(self.state), str(new), str(why))
4775eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state == mm1.MM_MODEM_STATE_CONNECTING:
4785eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(new, why)
4795eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            # TODO(jglasgow): implement a bearer object
4805eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                bearer_path = '/Bearer/0'
4815eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                return_cb(bearer_path)
4825eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            else:
4835eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                raise_cb(mm1.ConnectionUnknownError())
4845eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4855eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('Modem: Connect')
4865eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        if self.state != mm1.MM_MODEM_STATE_REGISTERED:
4875eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            logging.info(
4885eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                'Modem: Connect fails on unregistered modem.  State = %s',
4895eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.state)
4905eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            raise mm1.NoNetworkError()
4915eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        delay_ms = kwargs.get('connect_delay_ms', DEFAULT_CONNECT_DELAY_MS)
4925eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        time.sleep(delay_ms / 1000.0)
4935eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.ChangeState(mm1.MM_MODEM_STATE_CONNECTING)
4945eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        glib.timeout_add(50, lambda: ConnectDone(
4955eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MM_MODEM_STATE_CONNECTED,
4965eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED))
4975eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
4985eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(mm1.MODEM_SIMPLE_INTERFACE, in_signature='o',
4995eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         async_callbacks=('return_cb', 'raise_cb'))
5005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Disconnect(self, bearer, return_cb, raise_cb, **kwargs):
5015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Disconnect the modem from the network."""
5025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        def DisconnectDone(old, new, why):
5045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            logging.info('Modem: DisconnectDone %s -> %s because %s',
5055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         str(old), str(new), str(why))
5065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            if self.state == mm1.MM_MODEM_STATE_DISCONNECTING:
507845320fc3bab52407471d494e3c601a25862933bArman Uguray                logging.info('Modem: State is DISCONNECTING, changing to %s',
508845320fc3bab52407471d494e3c601a25862933bArman Uguray                             str(new))
5095eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.ChangeState(new)
5105eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                return_cb()
511845320fc3bab52407471d494e3c601a25862933bArman Uguray            elif self.state == mm1.MM_MODEM_STATE_DISABLED:
512845320fc3bab52407471d494e3c601a25862933bArman Uguray                logging.info('Modem: State is DISABLED, not changing state')
513845320fc3bab52407471d494e3c601a25862933bArman Uguray                return_cb()
5145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            else:
5155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                raise_cb(mm1.ConnectionUnknownError())
5165eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5175eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('Modem: Disconnect')
5185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.ChangeState(mm1.MM_MODEM_STATE_DISCONNECTING)
5195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        glib.timeout_add(
5205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            500,
5215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            lambda: DisconnectDone(
5225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                self.state,
5235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                mm1.MM_MODEM_STATE_REGISTERED,
5245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                mm1.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED))
5255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.signal(dbus.PROPERTIES_IFACE, signature='sa{sv}as')
5275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def PropertiesChanged(self, interface, changed_properties,
5285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                          invalidated_properties):
5295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        pass
5305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
531d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def AddSMS(self, sms):
532d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        logging.info('Adding SMS %s to list', sms.path)
533d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.smses[sms.path] = sms
534d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        self.Added(self.path, True)
535d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
536d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    @dbus.service.method(mm1.MODEM_MESSAGING_INTERFACE, in_signature='',
537d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                         out_signature='ao')
538d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def List(self, *args, **kwargs):
539d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        logging.info('Modem.Messaging: List: %s',
540d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                     ', '.join(self.smses.keys()))
541d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        return self.smses.keys()
542d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
543d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    @dbus.service.method(mm1.MODEM_MESSAGING_INTERFACE, in_signature='o',
544d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                         out_signature='')
545d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def Delete(self, sms_path, *args, **kwargs):
546d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        logging.info('Modem.Messaging: Delete %s', sms_path)
547d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        del self.smses[sms_path]
548d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
549d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    @dbus.service.signal(mm1.MODEM_MESSAGING_INTERFACE, signature='ob')
550d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    def Added(self, sms_path, complete):
551d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        pass
552d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
5535eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5545eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowclass GSMModem(Modem):
5555eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """A GSMModem implements the mm1.MODEM_MODEM3GPP_INTERFACE interface."""
5565eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5575eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __init__(self, manager, imei='00112342342', **kwargs):
5585eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.imei = imei
5595eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        Modem.__init__(self, manager, **kwargs)
5605eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5615eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(mm1.MODEM_MODEM3GPP_INTERFACE,
5625eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         in_signature='s', out_signature='')
5635eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Register(self, operator_id, *args, **kwargs):
5645eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Register the modem on the network."""
5655eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        pass
5665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Modem3GPPProperties(self):
5685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Return the 3GPP Properties of the modem object."""
5695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return {
5705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'Imei': self.imei,
5715eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MM_MODEM3GPP_PROPERTY_REGISTRATION_STATE:
5725eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                UInt32(self.registration_state),
5735eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'OperatorCode': self.operator_code,
5745eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'OperatorName': self.operator_name,
5755eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            'EnabledFacilityLocks': UInt32(0)
5765eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            }
5775eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5785eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def InterfacesAndProperties(self):
5795eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Return all supported interfaces and their properties."""
5805eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return {
5815eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MODEM_INTERFACE: self.ModemProperties(),
5825eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mm1.MODEM_MODEM3GPP_INTERFACE: self.Modem3GPPProperties()
5835eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            }
5845eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5855eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.method(mm1.MODEM_MODEM3GPP_INTERFACE, in_signature='',
5865eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                         out_signature='aa{sv}')
5875eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Scan(self, *args, **kwargs):
5885eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Scan for networks."""
5895eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        raise mm1.CoreUnsupportedError()
5905eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5915eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5925eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowclass ModemManager(dbus.service.Object):
5935eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    """Implements the org.freedesktop.DBus.ObjectManager interface."""
5945eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
5955eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def __init__(self, bus, path):
5965eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.devices = []
5975eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.bus = bus
5985eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.path = path
5995eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        dbus.service.Object.__init__(self, bus, path)
6005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Add(self, device):
6025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Adds a modem device to the list of devices that are managed."""
6035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('ModemManager: add %s', device.name)
6045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.devices.append(device)
6055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        interfaces = device.InterfacesAndProperties()
6065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('Add: %s', interfaces)
6075eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.InterfacesAdded(device.path, interfaces)
6085eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6095eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def Remove(self, device):
6105eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Removes a modem device from the list of managed devices."""
6115eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('ModemManager: remove %s', device.name)
6125eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.devices.remove(device)
6135eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        interfaces = device.InterfacesAndProperties().keys()
6145eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        self.InterfacesRemoved(device.path, interfaces)
6155eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
616d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    @dbus.service.method(mm1.OFDOM, out_signature='a{oa{sa{sv}}}')
6175eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def GetManagedObjects(self):
6185eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        """Returns the list of managed objects and their properties."""
6195eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        results = {}
6205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        for device in self.devices:
6215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            results[device.path] = device.InterfacesAndProperties()
6225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        logging.info('GetManagedObjects: %s', ', '.join(results.keys()))
6235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        return results
6245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.signal(mm1.OFDOM, signature='oa{sa{sv}}')
6265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def InterfacesAdded(self, object_path, interfaces_and_properties):
6275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        pass
6285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    @dbus.service.signal(mm1.OFDOM, signature='oas')
6305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    def InterfacesRemoved(self, object_path, interfaces):
6315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        pass
6325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6335eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6345eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowdef main():
6355eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    usage = """
6365eff4080ae483fb090c1e361f9bab5a367398e8fJason GlasgowRun pseudo_modem to simulate a GSM modem using the modemmanager-next
637d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason GlasgowDBUS interfaces.  This can be used for the following:
638d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow  - to simpilify the verification process of UI features that use of
639d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    overseas SIM cards
640d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow  - to test shill on a virtual machine without a physical modem
641d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow  - to test that Chrome property displays SMS messages
642d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
6430967dd7bf9ff2e4b8cace650f84b9c2bfc1d454dChristopher WileyTo use on a test image you use test_that to run
644d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgownetwork_3GModemControl which will cause pseudo_modem.py to be
645d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowinstalled in /usr/local/autotests/cros/cellular.  Then stop
646d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowmodemmanager and start the pseudo modem with the commands:
6475eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
6485eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow  stop modemmanager
649d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow  /usr/local/autotest/cros/cellular/pseudo_modem.py
650d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
651d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason GlasgowWhen done, use Control-C to stop the process and restart modem manager:
6525eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow  start modemmanager
653d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
654d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason GlasgowAdditional help documentation is available by invoking pseudo_modem.py
655d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow--help.
656d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
657d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason GlasgowSMS testing can be accomnplished by supplying the -s flag to simulate
658d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowthe receipt of a number of SMS messages.  The message text can be
659d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowspecified with the --text option on the command line, or read from a
660d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowfile by using the --file option.  If the messages are located in a
661d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowfile, then each line corresponds to a single SMS message.
662d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
663d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason GlasgowChrome should display the SMS messages as soon as a user logs in to
664d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowthe Chromebook, or if the user is already logged in, then shortly
665d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgowafter the pseudo modem is recognized by shill.
6665eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow"""
6675eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    parser = OptionParser(usage=usage)
6685eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    parser.add_option('-c', '--carrier', dest='carrier_name',
6695eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                      metavar='<carrier name>',
6705eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                      help='<carrier name> := %s' % ' | '.join(
6715eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                          SIM.CARRIERS.keys()))
672d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    parser.add_option('-s', '--smscount', dest='sms_count',
673d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      default=0,
674d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      metavar='<smscount>',
675d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      help='<smscount> := integer')
676d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    parser.add_option('-l', '--logfile', dest='logfile',
677d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      default='',
678d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      metavar='<filename>',
679d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      help='<filename> := filename for logging output')
680d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    parser.add_option('-t', '--text', dest='sms_text',
681d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      default=None,
682d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      metavar='<text>',
683d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      help='<text> := text for sms messages')
684d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    parser.add_option('-f', '--file', dest='filename',
685d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      default=None,
686d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      metavar='<filename>',
687d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                      help='<filename> := file with text for sms messages')
688d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
6895eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    (options, args) = parser.parse_args()
6905eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
691d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    kwargs = {}
692d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    if options.logfile:
693d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        kwargs['filename'] = options.logfile
694d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    logging.basicConfig(format='%(asctime)-15s %(message)s',
695d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                        level=logging.DEBUG,
696d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                        **kwargs)
697d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow
6985eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    if not options.carrier_name:
6995eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        options.carrier_name = DEFAULT_CARRIER
7005eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
7015eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
7025eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    bus = dbus.SystemBus()
7035eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    name = dbus.service.BusName(mm1.MODEM_MANAGER_INTERFACE, bus)
7045eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    manager = ModemManager(bus, mm1.OMM)
7055eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    sim_card = SIM.FromCarrier(string.lower(options.carrier_name),
7065eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow                               manager)
707d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow    with GSMModem(manager, sim=sim_card) as modem:
708d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        if options.filename:
709d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            f = open(options.filename, 'r')
710d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            for index, line in enumerate(f.readlines()):
711d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                line = line.strip()
712d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                if line:
713d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                    sms = SMS(manager, name='/SMS/%s' % index, text=line)
714d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                    modem.AddSMS(sms)
715d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow        else:
716d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow            for index in xrange(int(options.sms_count)):
717d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                sms = SMS(manager, name='/SMS/%s' % index,
718d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                          text=options.sms_text)
719d54ba82b6a20a34b5f72dd40f15e335f7a20c6efJason Glasgow                modem.AddSMS(sms)
7205eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
7215eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        mainloop = gobject.MainLoop()
7225eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
7235eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        def SignalHandler(signum, frame):
7245eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            logging.info('Signal handler called with signal: %s', signum)
7255eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow            mainloop.quit()
7265eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
7275eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        signal.signal(signal.SIGTERM, SignalHandler)
7285eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
7295eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow        mainloop.run()
7305eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow
7315eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgowif __name__ == '__main__':
7325eff4080ae483fb090c1e361f9bab5a367398e8fJason Glasgow    main()
733