1#!/usr/bin/env python2
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
6"""XML RPC server for multimedia testing."""
7
8import argparse
9import code
10import logging
11import os
12import six.moves.xmlrpc_client
13import traceback
14
15import common   # pylint: disable=unused-import
16from autotest_lib.client.bin import utils
17from autotest_lib.client.common_lib import logging_config
18from autotest_lib.client.cros import constants
19from autotest_lib.client.cros import upstart
20from autotest_lib.client.cros import xmlrpc_server
21from autotest_lib.client.cros.multimedia import assistant_facade_native
22from autotest_lib.client.cros.multimedia import audio_facade_native
23from autotest_lib.client.cros.multimedia import bluetooth_facade_native
24from autotest_lib.client.cros.multimedia import browser_facade_native
25from autotest_lib.client.cros.multimedia import cfm_facade_native
26from autotest_lib.client.cros.multimedia import display_facade_native
27from autotest_lib.client.cros.multimedia import facade_resource
28from autotest_lib.client.cros.multimedia import graphics_facade_native
29from autotest_lib.client.cros.multimedia import input_facade_native
30from autotest_lib.client.cros.multimedia import kiosk_facade_native
31from autotest_lib.client.cros.multimedia import system_facade_native
32from autotest_lib.client.cros.multimedia import usb_facade_native
33from autotest_lib.client.cros.multimedia import video_facade_native
34
35
36class MultimediaXmlRpcDelegate(xmlrpc_server.XmlRpcDelegate):
37    """XML RPC delegate for multimedia testing."""
38
39    def __init__(self, resource):
40        """Initializes the facade objects."""
41
42        # TODO: (crbug.com/618111) Add test driven switch for
43        # supporting arc_mode enabled or disabled. At this time
44        # if ARC build is tested, arc_mode is always enabled.
45        arc_res = None
46        if utils.get_board_property('CHROMEOS_ARC_VERSION'):
47            logging.info('Using ARC resource on ARC enabled board.')
48            from autotest_lib.client.cros.multimedia import arc_resource
49            arc_res = arc_resource.ArcResource()
50
51        self._facades = {
52                'assistant':
53                assistant_facade_native.AssistantFacadeNative(resource),
54                'audio':
55                audio_facade_native.AudioFacadeNative(resource,
56                                                      arc_resource=arc_res),
57                'bluetooth':
58                bluetooth_facade_native.BluetoothFacadeNative(),
59                'video':
60                video_facade_native.VideoFacadeNative(resource,
61                                                      arc_resource=arc_res),
62                'display':
63                display_facade_native.DisplayFacadeNative(resource),
64                'system':
65                system_facade_native.SystemFacadeNative(),
66                'usb':
67                usb_facade_native.USBFacadeNative(),
68                'browser':
69                browser_facade_native.BrowserFacadeNative(resource),
70                'input':
71                input_facade_native.InputFacadeNative(),
72                'cfm_main_screen':
73                cfm_facade_native.CFMFacadeNative(resource, 'hotrod'),
74                'cfm_mimo_screen':
75                cfm_facade_native.CFMFacadeNative(resource, 'control'),
76                'kiosk':
77                kiosk_facade_native.KioskFacadeNative(resource),
78                'graphics':
79                graphics_facade_native.GraphicsFacadeNative()
80        }
81
82
83    def __exit__(self, exception, value, traceback):
84        """Clean up the resources."""
85        self._facades['audio'].cleanup()
86
87
88    def _dispatch(self, method, params):
89        """Dispatches the method to the proper facade.
90
91        We turn off allow_dotted_names option. The method handles the dot
92        and dispatches the method to the proper native facade, like
93        DisplayFacadeNative.
94
95        """
96        try:
97            try:
98                if '.' not in method:
99                    func = getattr(self, method)
100                else:
101                    facade_name, method_name = method.split('.', 1)
102                    if facade_name in self._facades:
103                        func = getattr(self._facades[facade_name], method_name)
104                    else:
105                        raise Exception('unknown facade: %s' % facade_name)
106            except AttributeError:
107                raise Exception('method %s not supported' % method)
108
109            logging.info('Dispatching method %s with args %s',
110                         str(func), str(params))
111            return func(*params)
112        except:
113            # TODO(ihf): Try to return meaningful stacktraces from the client.
114            return traceback.format_exc()
115
116
117def config_logging():
118    """Configs logging to be verbose and use console handler."""
119    config = logging_config.LoggingConfig()
120    config.configure_logging(use_console=True, verbose=True)
121
122
123def main():
124    """The main function, to run the XMLRPC server."""
125    parser = argparse.ArgumentParser()
126    parser.add_argument('-d', '--debug', action='store_true', required=False,
127                        help=('create a debug console with a ServerProxy "s" '
128                              'connecting to the XML RPC sever at localhost'))
129    args = parser.parse_args()
130    pid = os.getpid()
131
132    if args.debug:
133        s = six.moves.xmlrpc_client.ServerProxy('http://localhost:%d' %
134                                  constants.MULTIMEDIA_XMLRPC_SERVER_PORT,
135                                  allow_none=True)
136        code.interact(local=locals())
137    else:
138        config_logging()
139        logging.debug('multimedia_xmlrpc_server[%s] main...', pid)
140        xmlrpc_server.terminate_old(__file__)
141
142        # bind before full setup, so the error is the last thing in the log
143        server = xmlrpc_server.XmlRpcServer(
144                'localhost', constants.MULTIMEDIA_XMLRPC_SERVER_PORT)
145
146        # Restart Cras to clean up any audio activities.
147        upstart.restart_job('cras')
148
149        with facade_resource.FacadeResource() as res:
150            server.register_delegate(MultimediaXmlRpcDelegate(res))
151            server.run()
152            logging.debug('multimedia_xmlrpc_server[%s] exiting...', pid)
153        logging.debug('multimedia_xmlrpc_server[%s] done.\n', pid)
154
155
156if __name__ == '__main__':
157    main()
158