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 logging
      6 import subprocess
      7 
      8 import pm_errors
      9 import state_machine
     10 
     11 from autotest_lib.client.cros.cellular import mm1_constants
     12 
     13 class ConnectMachine(state_machine.StateMachine):
     14     """
     15     ConnectMachine handles the state transitions involved in bringing the modem
     16     to the CONNECTED state.
     17 
     18     """
     19     def __init__(self, modem, properties, return_cb, raise_cb):
     20         super(ConnectMachine, self).__init__(modem)
     21         self.connect_props = properties
     22         self.return_cb = return_cb
     23         self.raise_cb = raise_cb
     24         self.enable_initiated = False
     25         self.register_initiated = False
     26 
     27 
     28     def Cancel(self):
     29         """ Overriden from superclass. """
     30         logging.info('ConnectMachine: Canceling connect.')
     31         super(ConnectMachine, self).Cancel()
     32         state = self._modem.Get(mm1_constants.I_MODEM, 'State')
     33         reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED
     34         if state == mm1_constants.MM_MODEM_STATE_CONNECTING:
     35             logging.info('ConnectMachine: Setting state to REGISTERED.')
     36             self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_REGISTERED,
     37                                     reason)
     38         elif self.enable_initiated and self._modem.enable_step:
     39             self._modem.enable_step.Cancel()
     40         self._modem.connect_step = None
     41 
     42 
     43     def _HandleDisabledState(self):
     44         logging.info('ConnectMachine: Modem is DISABLED.')
     45         assert not self._modem.IsPendingEnable()
     46         if self.enable_initiated:
     47             message = 'ConnectMachine: Failed to enable modem.'
     48             logging.error(message)
     49             self.Cancel()
     50             self._modem.connect_step = None
     51             self.raise_cb(pm_errors.MMCoreError(
     52                     pm_errors.MMCoreError.FAILED, message))
     53             return False
     54         else:
     55             logging.info('ConnectMachine: Initiating Enable.')
     56             self.enable_initiated = True
     57             self._modem.Enable(True)
     58 
     59             # state machine will spin until modem gets enabled,
     60             # or if enable fails
     61             return True
     62 
     63 
     64     def _HandleEnablingState(self):
     65         logging.info('ConnectMachine: Modem is ENABLING.')
     66         assert self._modem.IsPendingEnable()
     67         logging.info('ConnectMachine: Waiting for enable.')
     68         return True
     69 
     70 
     71     def _HandleEnabledState(self):
     72         logging.info('ConnectMachine: Modem is ENABLED.')
     73 
     74         # Check to see if a register is going on, if not,
     75         # start register
     76         if self.register_initiated:
     77             message = 'ConnectMachine: Failed to register.'
     78             logging.error(message)
     79             self.Cancel()
     80             self._modem.connect_step = None
     81             self.raise_cb(pm_errors.MMCoreError(pm_errors.MMCoreError.FAILED,
     82                                                 message))
     83             return False
     84         else:
     85             logging.info('ConnectMachine: Waiting for Register.')
     86             if not self._modem.IsPendingRegister():
     87                 self._modem.RegisterWithNetwork(
     88                         "", self._return_cb, self._raise_cb)
     89             self.register_initiated = True
     90             return True
     91 
     92 
     93     def _HandleSearchingState(self):
     94         logging.info('ConnectMachine: Modem is SEARCHING.')
     95         logging.info('ConnectMachine: Waiting for modem to register.')
     96         assert self.register_initiated
     97         assert self._modem.IsPendingRegister()
     98         return True
     99 
    100 
    101     def _HandleRegisteredState(self):
    102         logging.info('ConnectMachine: Modem is REGISTERED.')
    103         assert not self._modem.IsPendingDisconnect()
    104         assert not self._modem.IsPendingEnable()
    105         assert not self._modem.IsPendingDisable()
    106         assert not self._modem.IsPendingRegister()
    107         logging.info('ConnectMachine: Setting state to CONNECTING.')
    108         reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED
    109         self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_CONNECTING,
    110                                 reason)
    111         return True
    112 
    113 
    114     def _GetBearerToActivate(self):
    115         # Import modem here to avoid circular imports.
    116         import modem
    117         bearer = None
    118         bearer_path = None
    119         bearer_props = {}
    120         for p, b in self._modem.bearers.iteritems():
    121             # assemble bearer props
    122             for key, val in self.connect_props.iteritems():
    123                 if key in modem.ALLOWED_BEARER_PROPERTIES:
    124                     bearer_props[key] = val
    125             if (b.bearer_properties == bearer_props):
    126                 logging.info('ConnectMachine: Found matching bearer.')
    127                 bearer = b
    128                 bearer_path = p
    129                 break
    130         if bearer is None:
    131             assert bearer_path is None
    132             logging.info(('ConnectMachine: No matching bearer found, '
    133                 'creating brearer with properties: ' +
    134                 str(self.connect_props)))
    135             bearer_path = self._modem.CreateBearer(self.connect_props)
    136 
    137         return bearer_path
    138 
    139 
    140     def _HandleConnectingState(self):
    141         logging.info('ConnectMachine: Modem is CONNECTING.')
    142         assert not self._modem.IsPendingDisconnect()
    143         assert not self._modem.IsPendingEnable()
    144         assert not self._modem.IsPendingDisable()
    145         assert not self._modem.IsPendingRegister()
    146         try:
    147             bearer_path = self._GetBearerToActivate()
    148             self._modem.ActivateBearer(bearer_path)
    149             logging.info('ConnectMachine: Setting state to CONNECTED.')
    150             reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED
    151             self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_CONNECTED,
    152                                     reason)
    153             self._modem.connect_step = None
    154             logging.info(
    155                 'ConnectMachine: Returning bearer path: %s', bearer_path)
    156             self.return_cb(bearer_path)
    157         except (pm_errors.MMError, subprocess.CalledProcessError) as e:
    158             logging.error('ConnectMachine: Failed to connect: ' + str(e))
    159             self.raise_cb(e)
    160             self._modem.ChangeState(
    161                     mm1_constants.MM_MODEM_STATE_REGISTERED,
    162                     mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN)
    163             self._modem.connect_step = None
    164         return False
    165 
    166 
    167     def _GetModemStateFunctionMap(self):
    168         return {
    169             mm1_constants.MM_MODEM_STATE_DISABLED:
    170                     ConnectMachine._HandleDisabledState,
    171             mm1_constants.MM_MODEM_STATE_ENABLING:
    172                     ConnectMachine._HandleEnablingState,
    173             mm1_constants.MM_MODEM_STATE_ENABLED:
    174                     ConnectMachine._HandleEnabledState,
    175             mm1_constants.MM_MODEM_STATE_SEARCHING:
    176                     ConnectMachine._HandleSearchingState,
    177             mm1_constants.MM_MODEM_STATE_REGISTERED:
    178                     ConnectMachine._HandleRegisteredState,
    179             mm1_constants.MM_MODEM_STATE_CONNECTING:
    180                     ConnectMachine._HandleConnectingState
    181         }
    182 
    183 
    184     def _ShouldStartStateMachine(self):
    185         if self._modem.connect_step and self._modem.connect_step != self:
    186             # There is already a connect operation in progress.
    187             message = 'There is already an ongoing connect operation.'
    188             logging.error(message)
    189             self.raise_cb(pm_errors.MMCoreError(
    190                     pm_errors.MMCoreError.IN_PROGRESS, message))
    191             return False
    192         elif self._modem.connect_step is None:
    193             # There is no connect operation going on, cancelled or otherwise.
    194             if self._modem.IsPendingDisable():
    195                 message = 'Modem is currently being disabled. Ignoring ' \
    196                           'connect.'
    197                 logging.error(message)
    198                 self.raise_cb(
    199                     pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE,
    200                                           message))
    201                 return False
    202             state = self._modem.Get(mm1_constants.I_MODEM, 'State')
    203             if state == mm1_constants.MM_MODEM_STATE_CONNECTED:
    204                 message = 'Modem is already connected.'
    205                 logging.error(message)
    206                 self.raise_cb(
    207                     pm_errors.MMCoreError(pm_errors.MMCoreError.CONNECTED,
    208                                           message))
    209                 return False
    210             if state == mm1_constants.MM_MODEM_STATE_DISCONNECTING:
    211                 assert self._modem.IsPendingDisconnect()
    212                 message = 'Cannot connect while disconnecting.'
    213                 logging.error(message)
    214                 self.raise_cb(
    215                     pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE,
    216                                           message))
    217                 return False
    218 
    219             logging.info('Starting Connect.')
    220             self._modem.connect_step = self
    221         return True
    222