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