xref: /aosp_15_r20/external/autotest/client/cros/cellular/pseudomodem/sms_handler.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
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