1# Lint as: python2, python3 2# Copyright (c) 2013 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 6import datetime 7import dbus 8import dbus.types 9import logging 10 11from autotest_lib.client.cros.cellular.pseudomodem import sms 12 13from autotest_lib.client.cros.cellular import mm1_constants 14 15class SmsHandlerException(Exception): 16 """ Exception class for errors raised by SmsHandler. """ 17 pass 18 19 20class SmsHandler(object): 21 """ 22 Handles all SMS operations, which includes storing received SMS messages, 23 as well as notifying the modem when a new SMS is received. 24 25 """ 26 27 # TODO(armansito): Apply a character limit to SMS messages for multi-part 28 # delivery. This constant here is defined for future reference but is 29 # currently unusued. The value that is currently assigned to it is 30 # arbitrary and a more meaningful default value should be used, though it 31 # really doesn't matter from a testing perspective. 32 SMS_CHAR_LIMIT = 200 33 34 def __init__(self, modem, bus=None): 35 self._modem = modem 36 self._messages = {} # Mapping from DBus Object paths to sms.SMS. 37 self._bus = bus 38 39 40 @property 41 def bus(self): 42 """ 43 Returns the current bus assigned to this object. This is the bus 44 on which new SMS objects will be created. 45 46 @returns: An instance of dbus.Bus. 47 48 """ 49 return self._bus 50 51 52 @bus.setter 53 def bus(self, bus): 54 """ 55 Sets the current bus on which SMS objects should be created. 56 57 @param bus: An instance of dbus.Bus. 58 59 """ 60 self._bus = bus 61 62 63 @classmethod 64 def set_char_limit(cls, limit): 65 cls.SMS_CHAR_LIMIT = limit 66 67 68 def clear_messages(self): 69 """ Clears all SMS messages. """ 70 self._messages.clear() 71 72 73 def delete_message(self, path): 74 """ 75 Removes the message with DBus object path |path|. This operation 76 has no effect if and SMS object with path |path| is unknown. 77 78 @param path: DBus object path of the SMS object to remove. 79 80 """ 81 try: 82 self._messages.pop(path) 83 except KeyError: 84 logging.info('SMS object with path "%s" not found.', path) 85 pass 86 87 88 def list_messages(self): 89 """ 90 Returns a list of DBus object paths belonging to stored SMS messages. 91 92 """ 93 return list(self._messages.keys()) 94 95 96 def get_message_with_path(self, path): 97 """ 98 Returns the SMS message with the DBus object path that matches |path|. 99 100 @param path: DBus object path of the requested SMS object. 101 @returns: An instance of sms.SMS or None, if an SMS object with the 102 requested path is not found. 103 104 """ 105 sms_object = self._messages.get(path, None) 106 if sms_object: 107 assert sms_object.path == path 108 return sms_object 109 110 111 def construct_sms(self, text, sender): 112 """ 113 Constructs an SMS object and stores it internally. SMS messages should 114 be created using this method instead of being instantiated directly. 115 Once an SMS is created, it can be obtained via get_message_with_path. 116 117 @param text: The message contents, in UTF-8 format. 118 @param sender: The phone number of the sender. 119 @returns: An instance of sms.SMS. 120 121 """ 122 if self._bus is None: 123 raise SmsHandlerException('A bus has to be set before SMS objects ' 124 'can be created.') 125 sms_object = sms.SMS(self._bus, sender, text) 126 self._messages[sms_object.path] = sms_object 127 # TODO(armansito): Split SMSs that are too big into multiple chunks. 128 return sms_object 129 130 131 def send_sms(self, text, sender): 132 """ 133 Queues up an SMS to be sent and simulates SMS delivery state updates. 134 135 @param text: The message contents, in UTF-8 format. 136 @param sender: The phone number of the sender. 137 138 """ 139 # TODO(armansito): Support this if it's ever needed (unlikely). 140 raise SmsHandlerException('Sending SMSs is not supported.') 141 142 143 def receive_sms(self, text, sender, is_status_report=False): 144 """ 145 Simulates a received SMS message. 146 147 @param text: The message contents, in UTF-8 format. 148 @param sender: The phone number of the sender. 149 @param is_status_report: If True, the SMS will be formatted as a status 150 report. 151 152 """ 153 sms_object = self.construct_sms(text, sender) 154 155 # Use the current time for both DischargeTimestamp and Timestamp. Our 156 # SMS messages travel faster than the speed of light. 157 timestamp = datetime.datetime.isoformat(datetime.datetime.now()) 158 sms_object.Set(mm1_constants.I_SMS, 'Timestamp', timestamp) 159 sms_object.Set(mm1_constants.I_SMS, 'DischargeTimestamp', timestamp) 160 161 # Receive messages right away. 162 sms_object.Set(mm1_constants.I_SMS, 'State', 163 mm1_constants.MM_SMS_STATE_RECEIVED) 164 sms_object.Set(mm1_constants.I_SMS, 'PduType', 165 mm1_constants.MM_SMS_PDU_TYPE_DELIVER) 166 167 # Emit an Added message. 168 self._modem.Added(dbus.types.ObjectPath(sms_object.path), True) 169