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