1#!/usr/bin/env python
2
3# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
4# Use of this source code is governed by a BSD-style license that can be
5# found in the LICENSE file.
6
7import cmd
8import dbus
9import dbus.exceptions
10
11import pm_constants
12
13import common
14from autotest_lib.client.cros.cellular import mm1_constants
15
16class PseudoModemClient(cmd.Cmd):
17    """
18    Interactive client for PseudoModemManager.
19
20    """
21    def __init__(self):
22        cmd.Cmd.__init__(self)
23        self.prompt = '> '
24        self._bus = dbus.SystemBus()
25
26
27    def _get_proxy(self, path=pm_constants.TESTING_PATH):
28        return self._bus.get_object(mm1_constants.I_MODEM_MANAGER, path)
29
30
31    def _get_ism_proxy(self, state_machine):
32        return self._get_proxy('/'.join([pm_constants.TESTING_PATH,
33                                         state_machine]))
34
35
36    def Begin(self):
37        """
38        Starts the interactive shell.
39
40        """
41        print '\nWelcome to the PseudoModemManager shell!\n'
42        self.cmdloop()
43
44
45    def can_exit(self):
46        """Override"""
47        return True
48
49
50    def do_is_alive(self, args):
51        """
52        Handles the 'is_alive' command.
53
54        @params args: ignored.
55
56        """
57        if args:
58            print '\nCommand "is_alive" expects no arguments.\n'
59            return
60        print self._get_proxy().IsAlive(dbus_interface=pm_constants.I_TESTING)
61
62
63    def help_is_alive(self):
64        """ Handles the 'help is_alive' command. """
65        print '\nChecks that pseudomodem child process is alive.\n'
66
67
68    def do_properties(self, args):
69        """
70        Handles the 'properties' command.
71
72        @param args: Arguments to the command. Unused.
73
74        """
75        if args:
76            print '\nCommand "properties" expects no arguments.\n'
77            return
78        try:
79            props = self._get_proxy().GetAll(
80                            pm_constants.I_TESTING,
81                            dbus_interface=mm1_constants.I_PROPERTIES)
82            print '\nProperties: '
83            for k, v in props.iteritems():
84                print '   ' + k + ': ' + str(v)
85            print
86        except dbus.exceptions.DBusException as e:
87            print ('\nAn error occurred while communicating with '
88                   'PseudoModemManager: ' + e.get_dbus_name() + ' - ' +
89                   e.message + '\n')
90        return False
91
92
93    def help_properties(self):
94        """Handles the 'help properties' command."""
95        print '\nReturns the properties under the testing interface.\n'
96
97
98    def do_sms(self, args):
99        """
100        Simulates a received SMS.
101
102        @param args: A string containing the sender and the text message
103                content, in which everything before the first ' ' character
104                belongs to the sender and everything else belongs to the
105                message content. For example "Gandalf You shall not pass!"
106                will be parsed into:
107
108                    sender="Gandalf"
109                    content="You shall not pass!"
110
111                Pseudomodem doesn't distinguish between phone numbers and
112                strings containing non-numeric characters for the sender field
113                so args can contain pretty much anything.
114
115        """
116        arglist = args.split(' ', 1)
117        if len(arglist) != 2:
118            print '\nMalformed SMS args: ' + args + '\n'
119            return
120        try:
121            self._get_proxy().ReceiveSms(
122                    arglist[0], arglist[1],
123                    dbus_interface=pm_constants.I_TESTING)
124            print '\nSMS sent!\n'
125        except dbus.exceptions.DBusException as e:
126            print ('\nAn error occurred while communicating with '
127                   'PseudoModemManager: ' + e.get_dbus_name() + ' - ' +
128                   e.message + '\n')
129        return False
130
131
132    def help_sms(self):
133        """Handles the 'help sms' command."""
134        print '\nUsage: sms <sender phone #> <message text>\n'
135
136
137    def do_set(self, args):
138        """
139        Handles various commands that start with 'set'.
140
141        @param args: Defines the set command to be issued and its
142                arguments. Currently supported commands are:
143
144                  set pco <pco-value>
145
146        """
147        arglist = args.split(' ')
148        if len(arglist) < 1:
149            print '\nInvalid command: set ' + args + '\n'
150            return
151        if arglist[0] == 'pco':
152            if len(arglist) == 1:
153                arglist.append('')
154            elif len(arglist) != 2:
155                print '\nExpected: pco <pco-value>. Found: ' + args + '\n'
156                return
157            pco_value = arglist[1]
158            try:
159                self._get_proxy().UpdatePcoInfo(
160                        pco_value, dbus_interface=pm_constants.I_TESTING)
161                print '\nPCO value updated!\n'
162            except dbus.exceptions.DBusException as e:
163                print ('\nAn error occurred while communicating with '
164                       'PseudoModemManager: ' + e.get_dbus_name() + ' - ' +
165                       e.message + '\n')
166        else:
167            print '\nUnknown command: set ' + args + '\n'
168        return False
169
170
171    def help_set(self):
172        """Handles the 'help set' command."""
173        print ('\nUsage: set pco <pco-value>\n<pco-value> can be empty to set'
174               ' the PCO value to an empty string.\n')
175
176
177    def _get_state_machine(self, args):
178        arglist = args.split()
179        if len(arglist) != 1:
180            print '\nExpected one argument: Name of state machine\n'
181            return None
182        try:
183            return self._get_ism_proxy(arglist[0])
184        except dbus.exceptions.DBusException as e:
185            print '\nNo such interactive state machine.\n'
186            print 'Error obtained: |%s|\n' % repr(e)
187            return None
188
189
190    def do_is_waiting(self, machine):
191        """
192        Determine if a machine is waiting for an advance call.
193
194        @param machine: Case sensitive name of the machine.
195        @return: True if |machine| is waiting to be advanced by the user.
196
197        """
198        ism = self._get_state_machine(machine)
199        if not ism:
200            return False
201
202        try:
203            is_waiting = ism.IsWaiting(
204                    dbus_interface=pm_constants.I_TESTING_ISM)
205            print ('\nState machine is %swaiting.\n' %
206                   ('' if is_waiting else 'not '))
207        except dbus.exceptions.DBusException as e:
208            print ('\nCould not determine if |%s| is waiting: |%s|\n' %
209                   (machine, repr(e)))
210        return False
211
212
213    def help_is_waiting(self):
214        """Handles the 'help is_waiting' command"""
215        print ('\nUsage: is_waiting <state-machine-name>\n'
216               'Check whether a state machine is waiting for user action. The '
217               'waiting machine can be advanced using the |advance| command.\n'
218               'state-machine-name is the case sensitive name of the machine'
219               'whose status is to be queried.\n')
220
221
222    def do_advance(self, machine):
223        """
224        Advance the given state machine.
225
226        @param machine: Case sensitive name of the state machine to advance.
227        @returns: True if |machine| was successfully advanced, False otherwise.
228
229        """
230        ism = self._get_state_machine(machine)
231        if not ism:
232            return False
233
234        try:
235            success = ism.Advance(dbus_interface=pm_constants.I_TESTING_ISM)
236            print ('\nAdvanced!\n' if success else '\nCould not advance.\n')
237        except dbus.exceptions.DBusException as e:
238            print '\nError while advancing state machine: |%s|\n' % repr(e)
239        return False
240
241
242    def help_advance(self):
243        """Handles the 'help advance' command"""
244        print ('\nUsage: advance <state-machine-name>\n'
245               'Advance a waiting state machine to the next step.\n'
246               'state-machine-name is the case sensitive name of the machine'
247               'to advance.\n')
248
249
250    def do_exit(self, args):
251        """
252        Handles the 'exit' command.
253
254        @param args: Arguments to the command. Unused.
255
256        """
257        if args:
258            print '\nCommand "exit" expects no arguments.\n'
259            return
260        resp = raw_input('Are you sure? (yes/no): ')
261        if resp == 'yes':
262            print '\nGoodbye!\n'
263            return True
264        if resp != 'no':
265            print '\nDid not understand: ' + resp + '\n'
266        return False
267
268
269    def help_exit(self):
270        """Handles the 'help exit' command."""
271        print ('\nExits the interpreter. Shuts down the pseudo modem manager '
272               'if the interpreter was launched by running pseudomodem.py')
273
274
275    do_EOF = do_exit
276    help_EOF = help_exit
277
278
279def main():
280    """ main method, run when this module is executed as stand-alone. """
281    client = PseudoModemClient()
282    client.Begin()
283
284
285if __name__ == '__main__':
286    main()
287