blob: e36f95d32d4983739af422298402c9d7d8bcde2e [file] [log] [blame]
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +08001# Copyright (c) 2014 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 os
6import re
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +08007import sys
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +08008
9from autotest_lib.client.bin import utils
10from autotest_lib.client.cros import constants, httpd
11from autotest_lib.server import autotest
12
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080013class DisplayClient(object):
14 """DisplayClient is a layer to control display logic over a remote DUT.
15
16 The Autotest host object representing the remote DUT, passed to this
17 class on initialization, can be accessed from its _client property.
18
19 """
20
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080021 X_ENV_VARIABLES = 'DISPLAY=:0.0 XAUTHORITY=/home/chronos/.Xauthority'
22 XMLRPC_CONNECT_TIMEOUT = 30
23
24
25 def __init__(self, host):
26 """Construct a DisplayClient.
27
28 @param host: Host object representing a remote host.
29 """
30 self._client = host
31 self._display_xmlrpc_client = None
32 self._http_listener = None
33 self._server_ip = None
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +080034 self._server_port = None
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080035
36
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +080037 def initialize(self, run_httpd=True):
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080038 """Initializes some required servers, like HTTP daemon, RPC connection.
39
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +080040 @param run_httpd: True to run HTTP daemon, to serve the calibration
41 images.
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080042 """
43 # Make sure the client library is on the device so that the proxy code
44 # is there when we try to call it.
45 client_at = autotest.Autotest(self._client)
46 client_at.install()
Tom Wai-Hong Tamfe395092014-02-12 20:16:22 +080047 self.connect()
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080048
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +080049 if run_httpd:
50 # TODO(waihong): Auto-generate the calibration images.
51 module_dir = os.path.dirname(sys.modules[__name__].__file__)
52 image_dir = os.path.join(module_dir, 'calibration_images')
53 self._server_port = utils.get_unused_port()
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080054 self._http_listener = httpd.HTTPListener(
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +080055 port=self._server_port,
56 docroot=image_dir)
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080057 self._http_listener.run()
58 # SSH_CONNECTION is of the form:
59 # [client_ip] [client_port] [server_ip] [server_port]
60 self._server_ip = re.search(
61 r'([0-9.]+) \d+ [0-9.]+ \d+',
62 self._client.run('echo $SSH_CONNECTION').stdout).group(1)
63
64
Tom Wai-Hong Tamfe395092014-02-12 20:16:22 +080065 def connect(self):
66 """Connects the XML-RPC proxy on the client."""
67 self._display_xmlrpc_client = self._client.xmlrpc_connect(
68 constants.DISPLAY_TESTING_XMLRPC_SERVER_COMMAND,
69 constants.DISPLAY_TESTING_XMLRPC_SERVER_PORT,
70 command_name=(
71 constants.DISPLAY_TESTING_XMLRPC_SERVER_CLEANUP_PATTERN
72 ),
73 ready_test_name=(
74 constants.DISPLAY_TESTING_XMLRPC_SERVER_READY_METHOD),
75 timeout_seconds=self.XMLRPC_CONNECT_TIMEOUT)
76
77
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080078 def cleanup(self):
79 """Cleans up."""
80 if self._http_listener:
81 self._http_listener.stop()
82 self._client.rpc_disconnect_all()
83
84
85 def __del__(self):
86 """Destructor of DisplayClient."""
87 self.cleanup()
88
89
90 def get_connector_name(self):
91 """Gets the name of the external output connector.
92
93 @return The external output connector name as a string.
94 """
95 return self._display_xmlrpc_client.get_ext_connector_name()
96
97
98 def load_calibration_image(self, resolution):
99 """Load a full screen calibration image from the HTTP server.
100
101 @param resolution: A tuple (width, height) of resolution.
102 """
103 resolution_str = '%dx%d' % resolution
104 page_url = ('http://%s:%s/%s.png' %
Tom Wai-Hong Tamc3846422014-02-17 16:41:57 +0800105 (self._server_ip, self._server_port, resolution_str))
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800106 self._display_xmlrpc_client.load_url(page_url)
107
108
109 def close_tab(self, index=-1):
110 """Closes the tab of the given index.
111
112 @param index: The tab index to close. Defaults to the last tab.
113 """
114 return self._display_xmlrpc_client.close_tab(index)
115
116
117 def set_mirrored(self, is_mirrored):
118 """Sets mirrored mode.
119
120 @param is_mirrored: True or False to indicate mirrored state.
121 """
122 return self._display_xmlrpc_client.set_mirrored(is_mirrored)
123
124
Tom Wai-Hong Tam328dbeb2014-02-14 11:20:19 +0800125 def suspend_resume(self, suspend_time=10):
126 """Suspends the DUT for a given time in second.
127
128 @param suspend_time: Suspend time in second, default: 10s.
129 """
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800130 # TODO(waihong): Use other general API instead of this RPC.
Tom Wai-Hong Tam328dbeb2014-02-14 11:20:19 +0800131 return self._display_xmlrpc_client.suspend_resume(suspend_time)
132
133
134 def suspend_resume_bg(self, suspend_time=10):
135 """Suspends the DUT for a given time in second in the background.
136
137 @param suspend_time: Suspend time in second, default: 10s.
138 """
139 # TODO(waihong): Use other general API instead of this RPC.
140 return self._display_xmlrpc_client.suspend_resume_bg(suspend_time)
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800141
142
143 def reconnect_output_and_wait(self):
144 """Reconnects output and waits it available."""
145 output = self.get_connector_name()
146 self._display_xmlrpc_client.reconnect_output(output)
147 self._display_xmlrpc_client.wait_output_connected(output)
148 utils.wait_for_value(lambda: (
149 len(self._display_xmlrpc_client.get_display_info())),
150 expected_value=2)
151
152
153 def move_cursor_to_bottom_right(self):
154 """Moves mouse cursor to the bottom-right corner."""
155 output = self.get_connector_name()
156 fb_w, fb_h, _, _ = self._display_xmlrpc_client.get_resolution(output)
157 self._client.run('%s xdotool mousemove %d %d' %
158 (self.X_ENV_VARIABLES, fb_w, fb_h))
159
160
161 def capture_external_screen(self, file_path):
162 """Captures the external screen framebuffer.
163
164 @param file_path: The path of file for output.
165
166 @return: The byte-array for the screen.
167 """
168 output = self.get_connector_name()
169 fb_w, fb_h, fb_x, fb_y = (
170 self._display_xmlrpc_client.get_resolution(output))
171 basename = os.path.basename(file_path)
172 remote_path = os.path.join('/tmp', basename)
173 command = ('%s import -window root -depth 8 -crop %dx%d+%d+%d %s' %
174 (self.X_ENV_VARIABLES, fb_w, fb_h, fb_x, fb_y, remote_path))
175 self._client.run(command)
176 self._client.get_file(remote_path, file_path)
177 return open(file_path).read()