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.service
7import dbus.types
8import gobject
9import logging
10import random
11
12import bearer
13import dbus_std_ifaces
14import messaging
15import modem_simple
16import pm_constants
17import pm_errors
18import sms_handler
19import state_machine_factory as smf
20import utils
21
22import common
23from autotest_lib.client.cros.cellular import mm1_constants
24from autotest_lib.client.cros.cellular import net_interface
25
26ALLOWED_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
40class 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