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