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
5import logging
6
7import pm_errors
8import state_machine
9
10from autotest_lib.client.cros.cellular import mm1_constants
11
12class RegisterMachine(state_machine.StateMachine):
13    """
14    RegisterMachine handles the state transitions involved in bringing the
15    modem to the REGISTERED state.
16
17    """
18    def __init__(self, modem, operator_code="", return_cb=None, raise_cb=None):
19        super(RegisterMachine, self).__init__(modem)
20        self._networks = None
21        self._operator_code = operator_code
22        self._return_cb = return_cb
23        self._raise_cb = raise_cb
24
25
26    def Cancel(self):
27        """ Overriden from superclass. """
28        logging.info('RegisterMachine: Canceling register.')
29        super(RegisterMachine, self).Cancel()
30        state = self._modem.Get(mm1_constants.I_MODEM, 'State')
31        reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED
32        if state == mm1_constants.MM_MODEM_STATE_SEARCHING:
33            logging.info('RegisterMachine: Setting state to ENABLED.')
34            self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_ENABLED,
35                                    reason)
36            self._modem.SetRegistrationState(
37                mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
38        self._modem.register_step = None
39        if self._raise_cb:
40            self._raise_cb(
41                    pm_errors.MMCoreError(pm_errors.MMCoreError.CANCELLED,
42                                          'Cancelled'))
43
44
45    def _HandleEnabledState(self):
46        logging.info('RegisterMachine: Modem is ENABLED.')
47        logging.info('RegisterMachine: Setting registration state '
48                     'to SEARCHING.')
49        self._modem.SetRegistrationState(
50            mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_SEARCHING)
51        logging.info('RegisterMachine: Setting state to SEARCHING.')
52        reason = mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED
53        self._modem.ChangeState(mm1_constants.MM_MODEM_STATE_SEARCHING, reason)
54        logging.info('RegisterMachine: Starting network scan.')
55        try:
56            self._networks = self._modem.SyncScan()
57        except pm_errors.MMError as e:
58            self._modem.register_step = None
59            logging.error('An error occurred during network scan: ' + str(e))
60            self._modem.ChangeState(
61                    mm1_constants.MM_MODEM_STATE_ENABLED,
62                    mm1_constants.MODEM_STATE_CHANGE_REASON_UNKNOWN)
63            self._modem.SetRegistrationState(
64                    mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
65            if self._raise_cb:
66                self._raise_cb(e)
67            return False
68        logging.info('RegisterMachine: Found networks: ' + str(self._networks))
69        return True
70
71
72    def _HandleSearchingState(self):
73        logging.info('RegisterMachine: Modem is SEARCHING.')
74        if not self._networks:
75            logging.info('RegisterMachine: Scan returned no networks.')
76            logging.info('RegisterMachine: Setting state to ENABLED.')
77            self._modem.ChangeState(
78                    mm1_constants.MM_MODEM_STATE_ENABLED,
79                    mm1_constants.MM_MODEM_STATE_CHANGE_REASON_UNKNOWN)
80            # TODO(armansito): Figure out the correct registration
81            # state to transition to when no network is present.
82            logging.info('RegisterMachine: Setting registration state '
83                         'to IDLE.')
84            self._modem.SetRegistrationState(
85                    mm1_constants.MM_MODEM_3GPP_REGISTRATION_STATE_IDLE)
86            self._modem.register_step = None
87            if self._raise_cb:
88                self._raise_cb(pm_errors.MMMobileEquipmentError(
89                        pm_errors.MMMobileEquipmentError.NO_NETWORK,
90                        'No networks were found to register.'))
91            return False
92
93        # Pick the last network in the list. Roaming networks will come before
94        # the home network which makes the last item in the list the home
95        # network.
96        if self._operator_code:
97            if not self._operator_code in self._modem.scanned_networks:
98                if self._raise_cb:
99                    self._raise_cb(pm_errors.MMCoreError(
100                            pm_errors.MMCoreError.FAILED,
101                            "Unknown network: " + self._operator_code))
102                return False
103            network = self._modem.scanned_networks[self._operator_code]
104        else:
105            network = self._networks[-1]
106        logging.info(
107            'RegisterMachine: Registering to network: ' + str(network))
108        self._modem.SetRegistered(
109                network['operator-code'],
110                network['operator-long'])
111
112        # The previous call should have set the state to REGISTERED.
113        self._modem.register_step = None
114
115        if self._return_cb:
116            self._return_cb()
117        return False
118
119
120    def _GetModemStateFunctionMap(self):
121        return {
122            mm1_constants.MM_MODEM_STATE_ENABLED:
123                    RegisterMachine._HandleEnabledState,
124            mm1_constants.MM_MODEM_STATE_SEARCHING:
125                    RegisterMachine._HandleSearchingState
126        }
127
128
129    def _ShouldStartStateMachine(self):
130        if self._modem.register_step and self._modem.register_step != self:
131            # There is already an ongoing register operation.
132            message = 'Register operation already in progress.'
133            logging.info(message)
134            error = pm_errors.MMCoreError(pm_errors.MMCoreError.IN_PROGRESS,
135                                          message)
136            if self._raise_cb:
137                self._raise_cb(error)
138            else:
139                raise error
140        elif self._modem.register_step is None:
141            # There is no register operation going on, canceled or otherwise.
142            state = self._modem.Get(mm1_constants.I_MODEM, 'State')
143            if state != mm1_constants.MM_MODEM_STATE_ENABLED:
144                message = 'Cannot initiate register while in state %d, ' \
145                          'state needs to be ENABLED.' % state
146                error = pm_errors.MMCoreError(pm_errors.MMCoreError.WRONG_STATE,
147                                              message)
148                if self._raise_cb:
149                    self._raise_cb(error)
150                else:
151                    raise error
152
153            logging.info('Starting Register.')
154            self._modem.register_step = self
155        return True
156