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 dbus
6import dbus.types
7import logging
8
9import modem
10import pm_constants
11import utils
12
13from autotest_lib.client.cros.cellular import mm1_constants
14
15class ModemCdma(modem.Modem):
16    """
17    Pseudomodem implementation of the
18    org.freedesktop.ModemManager1.Modem.ModemCdma and
19    org.freedesktop.ModemManager1.Modem.Simple interfaces. This class provides
20    access to specific actions that may be performed in modems with CDMA
21    capabilities.
22
23    """
24
25    class CdmaNetwork(object):
26        """
27        Stores carrier specific information needed for a CDMA network.
28
29        """
30        def __init__(self,
31                     sid=99998,
32                     nid=0,
33                     activated=True,
34                     mdn='5555555555',
35                     standard='evdo'):
36            self.sid = sid
37            self.nid = nid
38            self.standard = standard
39            self.activated = activated
40            self._mdn = mdn
41
42
43        @property
44        def mdn(self):
45            """
46            @returns: The 'Mobile Directory Number' assigned to this modem by
47                    the carrier. If not activated, the first 6 digits will
48                    contain '0'.
49
50            """
51            if self.activated:
52                return self._mdn
53            return '000000' + self._mdn[6:]
54
55
56    def __init__(self,
57                 state_machine_factory=None,
58                 home_network=CdmaNetwork(),
59                 bus=None,
60                 device='pseudomodem0',
61                 roaming_networks=None,
62                 config=None):
63        self.home_network = home_network
64        self.cdma_activate_step = None
65        modem.Modem.__init__(self,
66                             state_machine_factory,
67                             bus=bus,
68                             device=device,
69                             roaming_networks=roaming_networks,
70                             config=config)
71
72
73    def _InitializeProperties(self):
74        ip = modem.Modem._InitializeProperties(self)
75        if self.home_network and self.home_network.activated:
76            activation_state = \
77                mm1_constants.MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED
78        else:
79            activation_state = \
80                mm1_constants.MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED
81        ip[mm1_constants.I_MODEM_CDMA] = {
82            'Meid' : 'A100000DCE2CA0',
83            'Esn' : 'EDD1EDD1',
84            'Sid' : dbus.types.UInt32(0),
85            'Nid' : dbus.types.UInt32(0),
86            'Cdma1xRegistrationState' : (
87            dbus.types.UInt32(
88                mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)),
89            'EvdoRegistrationState' : (
90            dbus.types.UInt32(
91                mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN)),
92            'ActivationState' : dbus.types.UInt32(activation_state)
93        }
94        props = ip[mm1_constants.I_MODEM]
95        props['SupportedCapabilities'] = [
96            dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_CDMA_EVDO)
97        ]
98        props['CurrentCapabilities'] = (
99            dbus.types.UInt32(mm1_constants.MM_MODEM_CAPABILITY_CDMA_EVDO))
100        props['MaxBearers'] = dbus.types.UInt32(1)
101        props['MaxActiveBearers'] = dbus.types.UInt32(1)
102        props['EquipmentIdentifier'] = ip[mm1_constants.I_MODEM_CDMA]['Meid']
103        props['AccessTechnologies'] = (
104            dbus.types.UInt32(mm1_constants.MM_MODEM_ACCESS_TECHNOLOGY_EVDO0))
105        props['SupportedModes'] = [
106            dbus.types.Struct(
107                [dbus.types.UInt32(mm1_constants.MM_MODEM_MODE_3G),
108                 dbus.types.UInt32(mm1_constants.MM_MODEM_MODE_4G)],
109                signature='uu')
110        ]
111        props['CurrentModes'] = props['SupportedModes'][0]
112        props['SupportedBands'] = [
113            dbus.types.UInt32(
114                mm1_constants.MM_MODEM_BAND_CDMA_BC0_CELLULAR_800),
115            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC1_PCS_1900),
116            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC2_TACS),
117            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC3_JTACS),
118            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC4_KOREAN_PCS),
119            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC5_NMT450),
120            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC6_IMT2000),
121            dbus.types.UInt32(
122                mm1_constants.MM_MODEM_BAND_CDMA_BC7_CELLULAR_700),
123            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC8_1800),
124            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC9_900),
125            dbus.types.UInt32(
126                mm1_constants.MM_MODEM_BAND_CDMA_BC10_SECONDARY_800),
127            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC11_PAMR_400)
128        ]
129        props['CurrentBands'] = [
130            dbus.types.UInt32(
131                mm1_constants.MM_MODEM_BAND_CDMA_BC0_CELLULAR_800),
132            dbus.types.UInt32(mm1_constants.MM_MODEM_BAND_CDMA_BC1_PCS_1900),
133        ]
134        if self.home_network:
135            props['OwnNumbers'] = [self.home_network.mdn]
136        else:
137            props['OwnNumbers'] = []
138
139        return ip
140
141
142    @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
143    @dbus.service.method(mm1_constants.I_MODEM_CDMA, in_signature='s',
144                         async_callbacks=('return_cb', 'raise_cb'))
145    def Activate(self, carrier, return_cb, raise_cb):
146        """
147        Provisions the modem for use with a given carrier using the modem's
148        OTA activation functionality, if any.
149
150        @param carrier: Automatic activation code.
151        @param return_cb: Asynchronous success callback.
152        @param raise_cb: Asynchronous failure callback. Has to take an instance
153                         of Exception or Error.
154        Emits:
155            ActivationStateChanged
156
157        """
158        logging.info('ModemCdma.Activate')
159        machine = self._state_machine_factory.CreateMachine(
160                pm_constants.STATE_MACHINE_CDMA_ACTIVATE,
161                self,
162                return_cb,
163                raise_cb)
164        machine.Start()
165
166
167    @utils.log_dbus_method()
168    @dbus.service.method(mm1_constants.I_MODEM_CDMA, in_signature='a{sv}')
169    def ActivateManual(self, properties):
170        """
171        Sets the modem provisioning data directly, without contacting the
172        carrier over the air. Some modems will reboot after this call is made.
173
174        @param properties: A dictionary of properties to set on the modem,
175                           including "mdn" and "min".
176        Emits:
177            ActivationStateChanged
178
179        """
180        raise NotImplementedError()
181
182
183    @dbus.service.signal(mm1_constants.I_MODEM_CDMA, signature='uua{sv}')
184    def ActivationStateChanged(
185            self,
186            activation_state,
187            activation_error,
188            status_changes):
189        """
190        The device activation state changed.
191
192        @param activation_state: Current activation state, given as a
193                MMModemCdmaActivationState.
194        @param activation_error: Carrier-specific error code, given as a
195                MMCdmaActivationError.
196        @param status_changes: Properties that have changed as a result of this
197                activation state chage, including "mdn" and "min".
198
199        """
200        logging.info('ModemCdma: activation state changed: state: %u, error: '
201                     '%u, status_changes: %s',
202                     activation_state,
203                     activation_error,
204                     str(status_changes))
205
206
207    def IsPendingActivation(self):
208        """
209        @returns: True, if a CdmaActivationMachine is currently active.
210
211        """
212        return self.cdma_activate_step and \
213            not self.cdma_activate_step.cancelled
214
215
216    def ChangeActivationState(self, state, error):
217        """
218        Changes the activation state of this modem to the one provided.
219
220        If the requested state is MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED and
221        a cdma_activation_machine.CdmaActivationMachine associated with this
222        modem is currently active, then this method won't update the DBus
223        properties until after the modem has reset.
224
225        @param state: Requested activation state, given as a
226                MMModemCdmaActivationState.
227        @param error: Carrier-specific error code, given as a
228                MMCdmaActivationError.
229
230        """
231        status_changes = {}
232        if state == mm1_constants.MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED:
233            self.home_network.activated = True
234            status_changes['mdn'] = [self.home_network.mdn]
235            self.Set(mm1_constants.I_MODEM, 'OwnNumbers', status_changes['mdn'])
236
237            if self.IsPendingActivation():
238                logging.info("A CdmaActivationMachine is currently active. "
239                             "Deferring setting the 'ActivationState' property"
240                             " to ACTIVATED until after the reset is "
241                             "complete.")
242                return
243
244        self.SetUInt32(mm1_constants.I_MODEM_CDMA, 'ActivationState', state)
245        self.ActivationStateChanged(state, error, status_changes)
246
247
248    def GetHomeNetwork(self):
249        """
250        @returns: A instance of CdmaNetwork that represents the
251                current home network that is assigned to this modem.
252
253        """
254        return self.home_network
255
256
257    def SetRegistered(self, network):
258        """
259        Sets the modem to be registered on the given network. Configures the
260        'ActivationState', 'Sid', and 'Nid' properties accordingly.
261
262        @param network: An instance of CdmaNetwork.
263
264        """
265        logging.info('ModemCdma.SetRegistered')
266        if network:
267            state = mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_HOME
268            sid = network.sid
269            nid = network.nid
270            if network.activated:
271                activation_state = \
272                    mm1_constants.MM_MODEM_CDMA_ACTIVATION_STATE_ACTIVATED
273            else:
274                activation_state = \
275                    mm1_constants.MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED
276        else:
277            state = mm1_constants.MM_MODEM_CDMA_REGISTRATION_STATE_UNKNOWN
278            sid = 0
279            nid = 0
280            activation_state = \
281                mm1_constants.MM_MODEM_CDMA_ACTIVATION_STATE_NOT_ACTIVATED
282        self.SetUInt32(mm1_constants.I_MODEM_CDMA, 'ActivationState',
283                       activation_state)
284        self.SetUInt32(mm1_constants.I_MODEM_CDMA, 'Sid', sid)
285        self.SetUInt32(mm1_constants.I_MODEM_CDMA, 'Nid', nid)
286        self.SetRegistrationState(state)
287
288
289    def SetRegistrationState(self, state):
290        """
291        Sets the CDMA1x and EVDO registration states to the provided value.
292
293        @param state: A MMModemCdmaRegistrationState value.
294
295        """
296        self.SetUInt32(mm1_constants.I_MODEM_CDMA, 'Cdma1xRegistrationState',
297                       state)
298        self.SetUInt32(mm1_constants.I_MODEM_CDMA, 'EvdoRegistrationState',
299                       state)
300
301
302    # Inherited from modem.Modem.
303    def RegisterWithNetwork(
304            self, operator_id="", return_cb=None, raise_cb=None):
305        """ Overridden from superclass.
306
307        @param operator_id: See superclass.
308        @param return_cb: See superclass.
309        @param raise_cb: See superclass.
310
311        """
312        logging.info('ModemCdma.RegisterWithNetwork')
313        machine = self._state_machine_factory.CreateMachine(
314                pm_constants.STATE_MACHINE_REGISTER_CDMA,
315                self,
316                operator_id,
317                return_cb,
318                raise_cb)
319        machine.Start()
320
321
322    def UnregisterWithNetwork(self):
323        """ Overridden from superclass. """
324        logging.info('ModemCdma.UnregisterWithNetwork')
325        if self.Get(mm1_constants.I_MODEM, 'State') != \
326            mm1_constants.MM_MODEM_STATE_REGISTERED:
327            logging.info('Currently not registered. Nothing to do.')
328            return
329        logging.info('Setting state to ENABLED.')
330        self.ChangeState(mm1_constants.MM_MODEM_STATE_ENABLED,
331            mm1_constants.MM_MODEM_STATE_CHANGE_REASON_USER_REQUESTED)
332        logging.info('Unregistering.')
333        self.SetRegistered(None)
334
335
336    # Inherited from modem_simple.ModemSimple.
337    @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
338    def Connect(self, properties, return_cb, raise_cb):
339        """
340        Overridden from superclass.
341
342        @param properties
343        @param return_cb
344        @param raise_cb
345
346        """
347        logging.info('ModemCdma.Connect')
348        machine = self._state_machine_factory.CreateMachine(
349                pm_constants.STATE_MACHINE_CONNECT_CDMA,
350                self,
351                properties,
352                return_cb,
353                raise_cb)
354        machine.Start()
355
356
357    # Inherited from modem_simple.ModemSimple.
358    @utils.log_dbus_method(return_cb_arg='return_cb', raise_cb_arg='raise_cb')
359    def Disconnect(self, bearer_path, return_cb, raise_cb, *return_cb_args):
360        """
361        Overridden from superclass.
362
363        @param bearer_path
364        @param return_cb
365        @param raise_cb
366        @param return_cb_args
367
368        """
369        logging.info('ModemCdma.Disconnect: %s', bearer_path)
370        machine = self._state_machine_factory.CreateMachine(
371                pm_constants.STATE_MACHINE_DISCONNECT,
372                self,
373                bearer_path,
374                return_cb,
375                raise_cb,
376                return_cb_args)
377        machine.Start()
378
379
380    # Inherited from modem_simple.ModemSimple.
381    @utils.log_dbus_method()
382    def GetStatus(self):
383        """ Overridden from superclass. """
384        modem_props = self.GetAll(mm1_constants.I_MODEM)
385        cdma_props = self.GetAll(mm1_constants.I_MODEM_CDMA)
386        retval = {}
387        retval['state'] = modem_props['State']
388        if retval['state'] >= mm1_constants.MM_MODEM_STATE_REGISTERED:
389            retval['signal-quality'] = modem_props['SignalQuality'][0]
390            retval['bands'] = modem_props['CurrentBands']
391            retval['cdma-cdma1x-registration-state'] = \
392                cdma_props['Cdma1xRegistrationState']
393            retval['cdma-evdo-registration-state'] = \
394                cdma_props['EvdoRegistrationState']
395            retval['cdma-sid'] = cdma_props['Sid']
396            retval['cdma-nid'] = cdma_props['Nid']
397        return retval
398