Home | History | Annotate | Download | only in pseudomodem
      1 # Copyright (c) 2012 The Chromium OS Authors. All rights reserved.
      2 # Use of this source code is governed by a BSD-style license that can be
      3 # found in the LICENSE file.
      4 
      5 import dbus
      6 import dbus.service
      7 import dbus.types
      8 import gobject
      9 import logging
     10 import random
     11 
     12 import bearer
     13 import dbus_std_ifaces
     14 import messaging
     15 import modem_simple
     16 import pm_constants
     17 import pm_errors
     18 import sms_handler
     19 import state_machine_factory as smf
     20 import utils
     21 
     22 import common
     23 from autotest_lib.client.cros.cellular import mm1_constants
     24 from autotest_lib.client.cros.cellular import net_interface
     25 
     26 ALLOWED_BEARER_PROPERTIES = [
     27     'apn',
     28     'operator-id',
     29     'allowed-modes',
     30     'preferred-mode',
     31     'bands',
     32     'ip-type',
     33     'user',
     34     'password',
     35     'allow-roaming',
     36     'rm-protocol',
     37     'number'
     38 ]
     39 
     40 class Modem(dbus_std_ifaces.DBusProperties,
     41             modem_simple.ModemSimple,
     42             messaging.Messaging):
     43     """
     44     Pseudomodem implementation of the org.freedesktop.ModemManager1.Modem
     45     interface. This class serves as the abstract base class of all fake modem
     46     implementations.
     47 
     48     """
     49 
     50     SUPPORTS_MULTIPLE_OBJECT_PATHS = True
     51 
     52     def __init__(self,
     53                  state_machine_factory=None,
     54                  bus=None,
     55                  device='pseudomodem0',
     56                  device_port_type=mm1_constants.MM_MODEM_PORT_TYPE_AT,
     57                  index=0,
     58                  roaming_networks=None,
     59                  config=None):
     60         """
     61         Initializes the fake modem object. kwargs can contain the optional
     62         argument |config|, which is a dictionary of property-value mappings.
     63         These properties will be added to the underlying property dictionary,
     64         and must be one of the properties listed in the ModemManager Reference
     65         Manual. See _InitializeProperties for all of the properties that belong
     66         to this interface. Possible values for each are enumerated in
     67         mm1_constants.py.
     68 
     69         """
     70         if state_machine_factory:
     71             self._state_machine_factory = state_machine_factory
     72         else:
     73             self._state_machine_factory = smf.StateMachineFactory()
     74         self.device = device
     75         self.device_port_type = device_port_type
     76         self.index = index
     77         self.sim = None
     78 
     79         # The superclass construct will call _InitializeProperties
     80         dbus_std_ifaces.DBusProperties.__init__(self,
     81             mm1_constants.MM1 + '/Modem/' + str(index), bus, config)
     82 
     83         if roaming_networks is None:
     84             roaming_networks = []
     85         self.roaming_networks = roaming_networks
     86 
     87         self.bearers = {}
     88         self.active_bearers = {}
     89         self.enable_step = None
     90         self.disable_step = None
     91         self.connect_step = None
     92         self.disconnect_step = None
     93         self.register_step = None
     94 
     95         self._modemmanager = None
     96         self.resetting = False
     97 
     98         self._sms_handler = sms_handler.SmsHandler(self, bus)
     99 
    100 
    101     def _InitializeProperties(self):
    102         """ Sets up the default values for the properties. """
    103         props = {
    104             'Manufacturer' : 'Banana Technologies', # be creative here
    105             'Model' : 'Banana Peel 3000', # yep
    106             'Revision' : '1.0',
    107             'DeviceIdentifier' : 'Banana1234567890',
    108             'Device' : self.device,
    109             'Ports': [dbus.types.Struct(
    110                               [self.device,
    111                                dbus.types.UInt32(self.device_port_type)],
    112                               signature='su'),
    113                       dbus.types.Struct(
    114                               [net_interface.PseudoNetInterface.IFACE_NAME,
    115                                dbus.types.UInt32(
    116                                        mm1_constants.MM_MODEM_PORT_TYPE_NET)],
    117                               signature='su')],
    118             'Drivers' : ['FakeDriver'],
    119             'Plugin' : 'Banana Plugin',
    120             'UnlockRequired' :
    121                     dbus.types.UInt32(mm1_constants.MM_MODEM_LOCK_NONE),
    122             'UnlockRetries' : dbus.Dictionary(signature='uu'),
    123             'State' : dbus.types.Int32(mm1_constants.MM_MODEM_STATE_DISABLED),
    124             'SignalQuality' : dbus.types.Struct(
    125                                       [dbus.types.UInt32(100), True],
    126                                       signature='ub'),
    127             'OwnNumbers' : ['5555555555'],
    128             'PowerState' :
    129                     dbus.types.UInt32(mm1_constants.MM_MODEM_POWER_STATE_ON),
    130             'SupportedIpFamilies' :
    131                 dbus.types.UInt32(mm1_constants.MM_BEARER_IP_FAMILY_ANY),
    132             'Bearers' : dbus.Array([], signature='o'),
    133 
    134             # specified by subclass:
    135             'SupportedCapabilities' :
    136                     [dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_NONE)],
    137             'CurrentCapabilities' :
    138                     dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_NONE),
    139             'MaxBearers' : dbus.types.UInt32(0),
    140             'MaxActiveBearers' : dbus.types.UInt32(0),
    141             'EquipmentIdentifier' : '',
    142             'AccessTechnologies' :
    143                     dbus.types.UInt32(
    144                             mm1_constants.MM_MODEM_ACCESS_TECHNOLOGY_UNKNOWN),
    145             'SupportedModes' : [
    146                     dbus.types.Struct(
    147                             [dbus.types.UInt32(
    148                                     mm1_constants.MM_MODEM_MODE_NONE),
    149                              dbus.types.UInt32(
    150                                     mm1_constants.MM_MODEM_MODE_NONE)],
    151                             signature='uu')
    152             ],
    153             'CurrentModes' :
    154                     dbus.types.Struct(
    155                             [dbus.types.UInt32(
    156                                     mm1_constants.MM_MODEM_MODE_NONE),
    157                              dbus.types.UInt32(
    158                                     mm1_constants.MM_MODEM_MODE_NONE)],
    159                             signature='uu'),
    160             'SupportedBands' :
    161                     [dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_UNKNOWN)],
    162             'CurrentBands' :
    163                     [dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_UNKNOWN)],
    164             'Sim' : dbus.types.ObjectPath(mm1_constants.ROOT_PATH)
    165         }
    166         return {
    167             mm1_constants.I_MODEM : props,
    168             mm1_constants.I_MODEM_SIMPLE : {}
    169         }
    170 
    171 
    172     def IncrementPath(self):
    173         """
    174         Increments the current index at which this modem is exposed on DBus.
    175         E.g. if the current path is org/freedesktop/ModemManager/Modem/0, the
    176         path will change to org/freedesktop/ModemManager/Modem/1.
    177 
    178         Calling this method does not remove the object from its current path,
    179         which means that it will be available via both the old and the new
    180         paths. This is currently only used by Reset, in conjunction with
    181         dbus_std_ifaces.DBusObjectManager.[Add|Remove].
    182 
    183         """
    184         self.index += 1
    185         path = mm1_constants.MM1 + '/Modem/' + str(self.index)
    186         logging.info('Modem coming back as: ' + path)
    187         self.SetPath(path)
    188 
    189 
    190     @property
    191     def manager(self):
    192         """
    193         The current modemmanager.ModemManager instance that is managing this
    194         modem.
    195 
    196         @returns: A modemmanager.ModemManager object.
    197 
    198         """
    199         return self._modemmanager
    200 
    201 
    202     @manager.setter
    203     def manager(self, manager):
    204         """
    205         Sets the current modemmanager.ModemManager instance that is managing
    206         this modem.
    207 
    208         @param manager: A modemmanager.ModemManager object.
    209 
    210         """
    211         self._modemmanager = manager
    212 
    213 
    214     @property
    215     def sms_handler(self):
    216         """
    217         @returns: sms_handler.SmsHandler responsible for handling SMS.
    218 
    219         """
    220         return self._sms_handler
    221 
    222 
    223     def IsPendingEnable(self):
    224         """
    225         @returns: True, if a current enable state machine is active and hasn't
    226                 been cancelled.
    227 
    228         """
    229         return self.enable_step and not self.enable_step.cancelled
    230 
    231 
    232     def IsPendingDisable(self):
    233         """
    234         @returns: True, if a current disable state machine is active and hasn't
    235                 been cancelled.
    236 
    237         """
    238         return self.disable_step and not self.disable_step.cancelled
    239 
    240 
    241     def IsPendingConnect(self):
    242         """
    243         @returns: True, if a current connect state machine is active and hasn't
    244                 been cancelled.
    245 
    246         """
    247         return self.connect_step and not self.connect_step.cancelled
    248 
    249 
    250     def IsPendingDisconnect(self):
    251         """
    252         @returns: True, if a current disconnect state machine is active and
    253                 hasn't been cancelled.
    254 
    255         """
    256         return self.disconnect_step and not self.disconnect_step.cancelled
    257 
    258 
    259     def IsPendingRegister(self):
    260         """
    261         @returns: True, if a current register state machine is active and hasn't
    262                 been cancelled.
    263 
    264         """
    265         return self.register_step and not self.register_step.cancelled
    266 
    267 
    268     def CancelAllStateMachines(self):
    269         """ Cancels all state machines that are active. """
    270         if self.IsPendingEnable():
    271             self.enable_step.Cancel()
    272         if self.IsPendingDisable():
    273             self.disable_step.Cancel()
    274         if self.IsPendingConnect():
    275             self.connect_step.Cancel()
    276         if self.IsPendingDisconnect():
    277             self.disconnect_step.Cancel()
    278         if self.IsPendingRegister():
    279             self.register_step.Cancel()
    280 
    281 
    282     def SetSignalQuality(self, quality):
    283         """
    284         Sets the 'SignalQuality' property to the given value.
    285 
    286         @param quality: An integer value in the range 0-100.
    287         Emits:
    288             PropertiesChanged
    289 
    290         """
    291         self.Set(mm1_constants.I_MODEM, 'SignalQuality', (dbus.types.Struct(
    292             [dbus.types.UInt32(quality), True], signature='ub')))
    293 
    294 
    295     def ChangeState(self, state, reason):
    296         """
    297         Changes the modem state and emits the StateChanged signal.
    298 
    299         @param state: A MMModemState value.
    300         @param reason: A MMModemStateChangeReason value.
    301         Emits:
    302             PropertiesChanged
    303             StateChanged
    304 
    305         """
    306         old_state = self.Get(mm1_constants.I_MODEM, 'State')
    307         self.SetInt32(mm1_constants.I_MODEM, 'State', state)
    308         self.StateChanged(old_state, state, dbus.types.UInt32(reason))
    309 
    310 
    311     def SetSIM(self, sim):
    312         """
    313         Assigns a SIM object to this Modem. It exposes the SIM object via DBus
    314         and sets 'Sim' property of this Modem to the path of the SIM.
    315 
    316         @param sim: An instance of sim.SIM.
    317         Emits:
    318             PropertiesChanged
    319 
    320         """
    321         self.sim = sim
    322         if not sim:
    323             val = mm1_constants.ROOT_PATH
    324         else:
    325             val = sim.path
    326             self.sim.SetBus(self.bus)
    327             self.sim.modem = self
    328             self.UpdateLockStatus()
    329         self.Set(mm1_constants.I_MODEM, 'Sim', dbus.types.ObjectPath(val))
    330 
    331 
    332     def SetBus(self, bus):
    333         """
    334         Overridden from dbus_std_ifaces.DBusProperties.
    335 
    336         @param bus
    337 
    338         """
    339         dbus_std_ifaces.DBusProperties.SetBus(self, bus)
    340         self._state_machine_factory.SetBus(bus)
    341         self._sms_handler.bus = bus
    342 
    343 
    344     def UpdateLockStatus(self):
    345         """
    346         Tells the modem to update the current lock status. This method will
    347         update the modem state and the relevant modem properties.
    348 
    349         """
    350         if not self.sim:
    351             logging.info('SIM lock is the only kind of lock that is currently '
    352                          'supported. No SIM present, nothing to do.')
    353             return
    354         self.SetUInt32(mm1_constants.I_MODEM, 'UnlockRequired',
    355                        self.sim.lock_type)
    356         self.Set(mm1_constants.I_MODEM, 'UnlockRetries',
    357                  self.sim.unlock_retries)
    358         if self.sim.locked:
    359             def _SetLocked():
    360                 logging.info('There is a SIM lock in place. Setting state to '
    361                              'LOCKED')
    362                 self.ChangeState(
    363                         mm1_constants.MM_MODEM_STATE_LOCKED,
    364                         mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN)
    365 
    366             # If the modem is currently in an enabled state, disable it before
    367             # setting the modem state to LOCKED.
    368             if (self.Get(mm1_constants.I_MODEM, 'State') >=
    369                     mm1_constants.MM_MODEM_STATE_ENABLED):
    370                 logging.info('SIM got locked. Disabling modem.')
    371                 self.Enable(False, return_cb=_SetLocked)
    372             else:
    373                 _SetLocked()
    374         elif (self.Get(mm1_constants.I_MODEM, 'State') ==
    375                 mm1_constants.MM_MODEM_STATE_LOCKED):
    376             # Change the state to DISABLED. Shill will see the property change
    377             # and automatically attempt to enable the modem.
    378             logging.info('SIM became unlocked! Setting state to INITIALIZING.')
    379             self.ChangeState(mm1_constants.MM_MODEM_STATE_INITIALIZING,
    380                              mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN)
    381             logging.info('SIM became unlocked! Setting state to DISABLED.')
    382             self.ChangeState(mm1_constants.MM_MODEM_STATE_DISABLED,
    383                              mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN)
    384 
    385 
    386     @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
    387     @dbus.service.method(mm1_constants.I_MODEM,
    388                          in_signature='b', async_callbacks=('return_cb',
    389                                                             'raise_cb'))
    390     def Enable(self, enable, return_cb=None, raise_cb=None):
    391         """
    392         Enables or disables the modem.
    393 
    394         When enabled, the modem's radio is powered on and data sessions, voice
    395         calls, location services, and Short Message Service may be available.
    396 
    397         When disabled, the modem enters low-power state and no network-related
    398         operations are available.
    399 
    400         @param enable: True to enable the modem and False to disable it.
    401         @param return_cb: The asynchronous callback to invoke on success.
    402         @param raise_cb: The asynchronous callback to invoke on failure. Has to
    403                 take a python Exception or Error as its single argument.
    404 
    405         """
    406         if enable:
    407             logging.info('Modem enable')
    408             machine = self._state_machine_factory.CreateMachine(
    409                     pm_constants.STATE_MACHINE_ENABLE,
    410                     self,
    411                     return_cb,
    412                     raise_cb)
    413         else:
    414             logging.info('Modem disable')
    415             machine = self._state_machine_factory.CreateMachine(
    416                     pm_constants.STATE_MACHINE_DISABLE,
    417                     self,
    418                     return_cb,
    419                     raise_cb)
    420         machine.Start()
    421 
    422 
    423     def RegisterWithNetwork(
    424             self, operator_id="", return_cb=None, raise_cb=None):
    425         """
    426         Register with the network specified by the given |operator_id|.
    427         |operator_id| should be an MCCMNC value (for 3GPP) or an empty string.
    428         An implementation of this method must set the state to SEARCHING first,
    429         and eventually to REGISTERED, also setting technology specific
    430         registration state properties. Technology specific error cases need to
    431         be handled here (such as activation, the presence of a valid SIM card,
    432         etc).
    433 
    434         Must be implemented by a subclass.
    435 
    436         @param operator_id: String containing the operator code. This method
    437                 will typically initiate a network scan, yielding a list of
    438                 networks. If |operator_id| is non-empty, the modem will register
    439                 with the network in the scanned list that matches |operator_id|.
    440                 An empty |operator_id| means that registration should be
    441                 "automatic". In this case the implementation would typically
    442                 register with the home network. If a home network is not
    443                 available than any network that is returned by a network scan
    444                 can be registered with.
    445 
    446                 Note: CDMA doesn't support a network scan. In this case, the
    447                 only possible option is to register with the home network and
    448                 ignore the value of |operator_id|.
    449         @param return_cb: Async success callback.
    450         @param raise_cb: Async failure callback.
    451 
    452         """
    453         raise NotImplementedError()
    454 
    455 
    456     def UnregisterWithNetwork(self):
    457         """
    458         Unregisters with the currently registered network. This should
    459         transition the modem to the ENABLED state.
    460 
    461         Must be implemented by a subclass.
    462 
    463         """
    464         raise NotImplementedError()
    465 
    466 
    467     def ValidateBearerProperties(self, properties):
    468         """
    469         The default implementation makes sure that all keys in properties are
    470         one of the allowed bearer properties. Subclasses can override this
    471         method to provide CDMA/3GPP specific checks.
    472 
    473         @param properties: The dictionary of properties and values to validate.
    474         @raises: MMCoreError, if one or more properties are invalid.
    475 
    476         """
    477         for key in properties.iterkeys():
    478             if key not in ALLOWED_BEARER_PROPERTIES:
    479                 raise pm_errors.MMCoreError(
    480                         pm_errors.MMCoreError.INVALID_ARGS,
    481                         'Invalid property "%s", not creating bearer.' % key)
    482 
    483 
    484     @utils.log_dbus_method()
    485     @dbus.service.method(mm1_constants.I_MODEM, out_signature='ao')
    486     def ListBearers(self):
    487         """
    488         Lists configured packet data bearers (EPS Bearers, PDP Contexts, or
    489         CDMA2000 Packet Data Sessions).
    490 
    491         @returns: A list of bearer object paths.
    492 
    493         """
    494         return self.Get(mm1_constants.I_MODEM, 'Bearers')
    495 
    496 
    497     @utils.log_dbus_method()
    498     @dbus.service.method(mm1_constants.I_MODEM, in_signature='a{sv}',
    499                          out_signature='o')
    500     def CreateBearer(self, properties):
    501         """
    502         Creates a new packet data bearer using the given characteristics.
    503 
    504         This request may fail if the modem does not support additional bearers,
    505         if too many bearers are already defined, or if properties are invalid.
    506 
    507         @param properties: A dictionary containing the properties to assign to
    508                 the bearer after creating it. The allowed property values are
    509                 contained in modem.ALLOWED_PROPERTIES.
    510         @returns: On success, the object path of the newly created bearer.
    511 
    512         """
    513         logging.info('CreateBearer')
    514         maxbearers = self.Get(mm1_constants.I_MODEM, 'MaxBearers')
    515         if len(self.bearers) == maxbearers:
    516             raise pm_errors.MMCoreError(
    517                     pm_errors.MMCoreError.TOO_MANY,
    518                     ('Maximum number of bearers reached. Cannot create new '
    519                      'bearer.'))
    520         else:
    521             self.ValidateBearerProperties(properties)
    522             bearer_obj = bearer.Bearer(self.bus, properties)
    523             logging.info('Created bearer with path "%s".', bearer_obj.path)
    524             self.bearers[bearer_obj.path] = bearer_obj
    525             self._UpdateBearersProperty()
    526             return bearer_obj.path
    527 
    528 
    529     def ActivateBearer(self, bearer_path):
    530         """
    531         Activates a data bearer by setting its 'Connected' property to True.
    532 
    533         This request may fail if the modem does not support additional active
    534         bearers, if too many bearers are already active, if the requested
    535         bearer doesn't exist, or if the requested bearer is already active.
    536 
    537         @param bearer_path: DBus path of the bearer to activate.
    538 
    539         """
    540         logging.info('ActivateBearer: %s', bearer_path)
    541         bearer = self.bearers.get(bearer_path, None)
    542         if bearer is None:
    543             message = 'Could not find bearer with path "%s"' % bearer_path
    544             logging.info(message)
    545             raise pm_errors.MMCoreError(pm_errors.MMCoreError.NOT_FOUND,
    546                                         message)
    547 
    548         max_active_bearers = self.Get(mm1_constants.I_MODEM, 'MaxActiveBearers')
    549         if len(self.active_bearers) >= max_active_bearers:
    550             message = ('Cannot activate bearer: maximum active bearer count '
    551                        'reached.')
    552             logging.info(message)
    553             raise pm_errors.MMCoreError(pm_errors.MMCoreError.TOO_MANY, message)
    554         if bearer.IsActive():
    555             message = 'Bearer with path "%s" already active.', bearer_path
    556             logging.info(message)
    557             raise pm_errors.MMCoreError(pm_errors.MMCoreError.CONNECTED,
    558                                         message)
    559 
    560         self.active_bearers[bearer_path] = bearer
    561         bearer.Connect()
    562 
    563 
    564     def DeactivateBearer(self, bearer_path):
    565         """
    566         Deactivates data bearer by setting its 'Connected' property to False.
    567 
    568         This request may fail if the modem with the requested path doesn't
    569         exist, or if the bearer is not active.
    570 
    571         @param bearer_path: DBus path of the bearer to activate.
    572 
    573         """
    574         logging.info('DeactivateBearer: %s', bearer_path)
    575         bearer = self.bearers.get(bearer_path, None)
    576         if bearer is None:
    577             raise pm_errors.MMCoreError(
    578                     pm_errors.MMCoreError.NOT_FOUND,
    579                     'Could not find bearer with path "%s".' % bearer_path)
    580         if not bearer.IsActive():
    581             assert bearer_path not in self.active_bearers
    582             raise pm_errors.MMCoreError(
    583                     pm_errors.MMCoreError.WRONG_STATE,
    584                     'Bearer with path "%s" is not active.' % bearer_path)
    585         assert bearer_path in self.active_bearers
    586         bearer.Disconnect()
    587         self.active_bearers.pop(bearer_path)
    588 
    589 
    590     @utils.log_dbus_method()
    591     @dbus.service.method(mm1_constants.I_MODEM, in_signature='o')
    592     def DeleteBearer(self, bearer):
    593         """
    594         Deletes an existing packet data bearer.
    595 
    596         If the bearer is currently active, it will be deactivated.
    597 
    598         @param bearer: Object path of the bearer to delete.
    599 
    600         """
    601         logging.info('Modem.DeleteBearer: ' + str(bearer))
    602         if not bearer in self.bearers:
    603             logging.info('Unknown bearer. Nothing to do.')
    604             return
    605         bearer_object = self.bearers[bearer]
    606         bearer_object.remove_from_connection()
    607         self.bearers.pop(bearer)
    608         self._UpdateBearersProperty()
    609         if bearer in self.active_bearers:
    610             self.active_bearers.pop(bearer)
    611 
    612 
    613     def ClearBearers(self):
    614         """ Deletes all bearers that are managed by this modem. """
    615         for b in self.bearers.keys():
    616             self.DeleteBearer(b)
    617 
    618 
    619     @utils.log_dbus_method()
    620     @dbus.service.method(mm1_constants.I_MODEM)
    621     def Reset(self):
    622         """
    623         Clears non-persistent configuration and state, and returns the device to
    624         a newly-powered-on state.
    625 
    626         As a result of this operation, the modem will be removed from its
    627         current path and will be exposed on an incremented path. It will be
    628         enabled afterwards.
    629 
    630         """
    631         logging.info('Resetting modem.')
    632 
    633         if self.resetting:
    634             raise pm_errors.MMCoreError(pm_errors.MMCoreError.IN_PROGRESS,
    635                                         'Reset already in progress.')
    636 
    637         self.resetting = True
    638 
    639         self.CancelAllStateMachines()
    640 
    641         def _ResetFunc():
    642             # Disappear.
    643             manager = self.manager
    644             if manager:
    645                 manager.Remove(self)
    646                 if self.sim:
    647                     manager.Remove(self.sim)
    648 
    649             self.ClearBearers()
    650 
    651             # Reappear.
    652             def _DelayedReappear():
    653                 self.IncrementPath()
    654 
    655                 # Reset to defaults.
    656                 if self.sim:
    657                     self.sim.Reset()
    658                 self._properties = self._InitializeProperties()
    659                 if self.sim:
    660                     self.Set(mm1_constants.I_MODEM,
    661                              'Sim',
    662                              dbus.types.ObjectPath(self.sim.path))
    663                     self.UpdateLockStatus()
    664 
    665                 if manager:
    666                     manager.Add(self)
    667 
    668                 self.resetting = False
    669 
    670                 def _DelayedEnable():
    671                     state = self.Get(mm1_constants.I_MODEM, 'State')
    672                     if not self.IsPendingEnable() and \
    673                             state == mm1_constants.MM_MODEM_STATE_DISABLED:
    674                         self.Enable(True)
    675                     return False
    676 
    677                 gobject.timeout_add(1000, _DelayedEnable)
    678                 return False
    679 
    680             gobject.timeout_add(2000, _DelayedReappear)
    681 
    682         def _ErrorCallback(error):
    683             raise error
    684 
    685         if (self.Get(mm1_constants.I_MODEM, 'State') ==
    686                 mm1_constants.MM_MODEM_STATE_CONNECTED):
    687             self.Disconnect('/', _ResetFunc, _ErrorCallback)
    688         else:
    689             gobject.idle_add(_ResetFunc)
    690 
    691 
    692     @utils.log_dbus_method()
    693     @dbus.service.method(mm1_constants.I_MODEM, in_signature='s')
    694     def FactoryReset(self, code):
    695         """
    696         Clears the modem's configuration (including persistent configuration and
    697         state), and returns the device to a factory-default state.
    698 
    699         If not required by the modem, code may be ignored.
    700 
    701         This command may or may not power-cycle the device.
    702 
    703         @param code: Carrier specific activation code.
    704 
    705         """
    706         raise NotImplementedError()
    707 
    708 
    709     @utils.log_dbus_method()
    710     @dbus.service.method(mm1_constants.I_MODEM, in_signature='(uu)')
    711     def SetCurrentModes(self, modes):
    712         """
    713         Sets the access technologies (eg 2G/3G/4G preference) the device is
    714         currently allowed to use when connecting to a network.
    715 
    716         @param modes: Specifies all the modes allowed in the modem as a bitmask
    717                 of MMModemModem values.
    718         @param preferred: Specific MMModemMode preferred among the ones allowed,
    719                 if any.
    720 
    721         """
    722         allowed = self.Get(mm1_constants.I_MODEM, 'SupportedModes')
    723         if not modes in allowed:
    724             raise pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED,
    725                                         'Mode not supported: ' + repr(modes))
    726         self.Set(mm1_constants.I_MODEM, 'CurrentModes', modes)
    727 
    728 
    729     @utils.log_dbus_method()
    730     @dbus.service.method(mm1_constants.I_MODEM, in_signature='au')
    731     def SetCurrentBands(self, bands):
    732         """
    733         Sets the radio frequency and technology bands the device is currently
    734         allowed to use when connecting to a network.
    735 
    736         @param bands: Specifies the bands to be used as a list of MMModemBand
    737                 values.
    738 
    739         """
    740         band_list = [dbus.types.UInt32(band) for band in bands]
    741         self.Set(mm1_constants.I_MODEM, 'CurrentBands', band_list)
    742 
    743 
    744     @utils.log_dbus_method()
    745     @dbus.service.method(mm1_constants.I_MODEM, in_signature='su',
    746                          out_signature='s')
    747     def Command(self, cmd, timeout):
    748         """
    749         Allows clients to send commands to the modem. By default, this method
    750         does nothing, but responds by telling the client's fortune to brighten
    751         the client's day.
    752 
    753         @param cmd: Command to send to the modem.
    754         @param timeout: The timeout interval for the command.
    755         @returns: A string containing the response from the modem.
    756 
    757         """
    758         messages = ['Bananas are tasty and fresh. Have one!',
    759                     'A soft voice may be awfully persuasive.',
    760                     'Be careful or you could fall for some tricks today.',
    761                     'Believe in yourself and others will too.',
    762                     'Carve your name on your heart and not on marble.']
    763         return random.choice(messages)
    764 
    765 
    766     @utils.log_dbus_method()
    767     @dbus.service.method(mm1_constants.I_MODEM, in_signature='u')
    768     def SetPowerState(self, power_state):
    769         """
    770         Sets the power state of the modem. This action can only be run when the
    771         modem is in the MM_MODEM_STATE_DISABLED state.
    772 
    773         @param power_state: Specifies the desired power state as a
    774                 MMModemPowerState value.
    775         @raises: MMCoreError if state is not DISABLED.
    776 
    777         """
    778         if (self.Get(mm1_constants.I_MODEM, 'State') !=
    779                 mm1_constants.MM_MODEM_STATE_DISABLED):
    780             raise pm_errors.MMCoreError(
    781                     pm_errors.MMCoreError.WRONG_STATE,
    782                     'Cannot set the power state if modem is not DISABLED.')
    783         self.SetUInt32(mm1_constants.I_MODEM, 'PowerState', power_state);
    784 
    785 
    786     @utils.log_dbus_method()
    787     @dbus.service.method(mm1_constants.I_MODEM, in_signature='u')
    788     def SetCurrentCapabilities(self, capabilities):
    789         """
    790         Set the capabilities of the device. A restart of the modem may be
    791         required.
    792 
    793         @param capabilities: Bitmask of MMModemCapability values, to specify the
    794                 capabilities to use.
    795 
    796         """
    797         supported = self.Get(mm1_constants.I_MODEM, 'SupportedCapabilities')
    798         if not capabilities in supported:
    799             raise pm_errors.MMCoreError(
    800                     pm_errors.MMCoreError.FAILED,
    801                     'Given capabilities not supported: ' + capabilities)
    802         self.SetUInt32(mm1_constants.I_MODEM, 'CurrentCapabilities',
    803                        capabilities)
    804 
    805 
    806     @dbus.service.signal(mm1_constants.I_MODEM, signature='iiu')
    807     def StateChanged(self, old, new, reason):
    808         """
    809         Signals that the modem's 'State' property has changed.
    810 
    811         @param old: Specifies the old state, as a MMModemState value.
    812         @param new: Specifies the new state, as a MMModemState value.
    813         @param reason: Specifies the reason for this state change as a
    814                 MMModemStateChangeReason value.
    815 
    816         """
    817         logging.info('Modem state changed from %u to %u for reason %u',
    818                 old, new, reason)
    819 
    820 
    821     # org.freedesktop.ModemManager1.Messaging
    822 
    823     def List(self):
    824         """
    825         Overriden from messaging.Messaging.
    826 
    827         """
    828         return self._sms_handler.list_messages()
    829 
    830 
    831     def Delete(self, path):
    832         """
    833         Overriden from messaging.Messaging.
    834 
    835         @param path
    836 
    837         """
    838         self._sms_handler.delete_message(path)
    839 
    840 
    841     @dbus.service.signal(mm1_constants.I_MODEM_MESSAGING, signature='ob')
    842     def Added(self, path, received):
    843         """
    844         Overriden from messaging.Messaging.
    845 
    846         @param path
    847         @param received
    848 
    849         """
    850         logging.info('New SMS added: path: ' + path + ' received: ' +
    851                      str(received))
    852 
    853 
    854     def _UpdateBearersProperty(self):
    855         """
    856         Update the 'Bearers' property on |I_MODEM| interface to match the
    857         internal list.
    858 
    859         """
    860         bearers = dbus.Array(
    861                 [dbus.types.ObjectPath(key) for key in self.bearers.iterkeys()],
    862                 signature='o')
    863         self.Set(mm1_constants.I_MODEM, 'Bearers', bearers)
    864