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