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