blob: ea128fafc61904d4556ae2dfd6a1198d50f042df [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
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +08005import httplib
6import logging
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +08007import os
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +08008import socket
Tom Wai-Hong Tam897fcf72014-04-25 14:02:54 +08009import tempfile
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +080010import xmlrpclib
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080011
12from autotest_lib.client.bin import utils
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +080013from autotest_lib.client.common_lib.cros import retry
Wai-Hong Tam44cb5452014-03-18 16:14:24 -070014from autotest_lib.client.cros import constants
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080015from autotest_lib.server import autotest
Tom Wai-Hong Tam897fcf72014-04-25 14:02:54 +080016from autotest_lib.server.cros.chameleon import image_generator
17
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080018
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080019class DisplayClient(object):
20 """DisplayClient is a layer to control display logic over a remote DUT.
21
22 The Autotest host object representing the remote DUT, passed to this
23 class on initialization, can be accessed from its _client property.
24
25 """
26
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080027 X_ENV_VARIABLES = 'DISPLAY=:0.0 XAUTHORITY=/home/chronos/.Xauthority'
28 XMLRPC_CONNECT_TIMEOUT = 30
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +080029 XMLRPC_RETRY_TIMEOUT = 180
30 XMLRPC_RETRY_DELAY = 10
Tom Wai-Hong Tam23362632014-04-09 16:38:37 +080031 HTTP_PORT = 8000
Tom Wai-Hong Tam897fcf72014-04-25 14:02:54 +080032 DEST_TMP_DIR = '/tmp'
33 DEST_IMAGE_FILENAME = 'calibration.svg'
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080034
35
36 def __init__(self, host):
37 """Construct a DisplayClient.
38
39 @param host: Host object representing a remote host.
40 """
41 self._client = host
42 self._display_xmlrpc_client = None
Tom Wai-Hong Tam897fcf72014-04-25 14:02:54 +080043 self._image_generator = image_generator.ImageGenerator()
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080044
45
Wai-Hong Tam44cb5452014-03-18 16:14:24 -070046 def initialize(self):
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080047 """Initializes some required servers, like HTTP daemon, RPC connection.
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080048 """
49 # Make sure the client library is on the device so that the proxy code
50 # is there when we try to call it.
51 client_at = autotest.Autotest(self._client)
52 client_at.install()
Tom Wai-Hong Tamfe395092014-02-12 20:16:22 +080053 self.connect()
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080054
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080055
Tom Wai-Hong Tamfe395092014-02-12 20:16:22 +080056 def connect(self):
57 """Connects the XML-RPC proxy on the client."""
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +080058 @retry.retry((socket.error,
59 xmlrpclib.ProtocolError,
60 httplib.BadStatusLine),
61 timeout_min=self.XMLRPC_RETRY_TIMEOUT / 60.0,
62 delay_sec=self.XMLRPC_RETRY_DELAY)
63 def connect_with_retries():
64 """Connects the XML-RPC proxy with retries."""
65 self._display_xmlrpc_client = self._client.xmlrpc_connect(
66 constants.DISPLAY_TESTING_XMLRPC_SERVER_COMMAND,
67 constants.DISPLAY_TESTING_XMLRPC_SERVER_PORT,
68 command_name=(
69 constants.DISPLAY_TESTING_XMLRPC_SERVER_CLEANUP_PATTERN
70 ),
71 ready_test_name=(
72 constants.DISPLAY_TESTING_XMLRPC_SERVER_READY_METHOD),
73 timeout_seconds=self.XMLRPC_CONNECT_TIMEOUT)
74
75 logging.info('Setup the display_client RPC server, with retries...')
76 connect_with_retries()
Tom Wai-Hong Tamfe395092014-02-12 20:16:22 +080077
78
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080079 def cleanup(self):
80 """Cleans up."""
Tom Wai-Hong Tam0d836592014-04-23 11:27:40 +080081 self._client.rpc_disconnect(
82 constants.DISPLAY_TESTING_XMLRPC_SERVER_PORT)
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +080083
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 """
Tom Wai-Hong Tam897fcf72014-04-25 14:02:54 +0800103 with tempfile.NamedTemporaryFile() as f:
104 self._image_generator.generate_image(
105 resolution[0], resolution[1], f.name)
106 os.chmod(f.name, 0644)
107 self._client.send_file(
108 f.name,
109 os.path.join(self.DEST_TMP_DIR, self.DEST_IMAGE_FILENAME))
110
111 page_url = 'file://%s/%s' % (self.DEST_TMP_DIR,
112 self.DEST_IMAGE_FILENAME)
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800113 self._display_xmlrpc_client.load_url(page_url)
114
115
116 def close_tab(self, index=-1):
117 """Closes the tab of the given index.
118
119 @param index: The tab index to close. Defaults to the last tab.
120 """
121 return self._display_xmlrpc_client.close_tab(index)
122
123
124 def set_mirrored(self, is_mirrored):
125 """Sets mirrored mode.
126
127 @param is_mirrored: True or False to indicate mirrored state.
128 """
129 return self._display_xmlrpc_client.set_mirrored(is_mirrored)
130
131
Tom Wai-Hong Tam328dbeb2014-02-14 11:20:19 +0800132 def suspend_resume(self, suspend_time=10):
133 """Suspends the DUT for a given time in second.
134
135 @param suspend_time: Suspend time in second, default: 10s.
136 """
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800137 # TODO(waihong): Use other general API instead of this RPC.
Tom Wai-Hong Tam328dbeb2014-02-14 11:20:19 +0800138 return self._display_xmlrpc_client.suspend_resume(suspend_time)
139
140
141 def suspend_resume_bg(self, suspend_time=10):
142 """Suspends the DUT for a given time in second in the background.
143
144 @param suspend_time: Suspend time in second, default: 10s.
145 """
146 # TODO(waihong): Use other general API instead of this RPC.
147 return self._display_xmlrpc_client.suspend_resume_bg(suspend_time)
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800148
149
150 def reconnect_output_and_wait(self):
151 """Reconnects output and waits it available."""
152 output = self.get_connector_name()
153 self._display_xmlrpc_client.reconnect_output(output)
154 self._display_xmlrpc_client.wait_output_connected(output)
155 utils.wait_for_value(lambda: (
156 len(self._display_xmlrpc_client.get_display_info())),
157 expected_value=2)
158
159
Hung-ying Tyan67541652014-03-12 11:44:46 +0800160 def hide_cursor(self):
161 """Hides mouse cursor by sending a keystroke."""
Tom Wai-Hong Tambd22f8f2014-06-03 03:05:56 +0800162 self._display_xmlrpc_client.press_key('Up')
Tom Wai-Hong Tamb2d39dc2014-01-21 15:26:23 +0800163
164
165 def capture_external_screen(self, file_path):
166 """Captures the external screen framebuffer.
167
168 @param file_path: The path of file for output.
169
170 @return: The byte-array for the screen.
171 """
172 output = self.get_connector_name()
173 fb_w, fb_h, fb_x, fb_y = (
174 self._display_xmlrpc_client.get_resolution(output))
175 basename = os.path.basename(file_path)
176 remote_path = os.path.join('/tmp', basename)
177 command = ('%s import -window root -depth 8 -crop %dx%d+%d+%d %s' %
178 (self.X_ENV_VARIABLES, fb_w, fb_h, fb_x, fb_y, remote_path))
179 self._client.run(command)
180 self._client.get_file(remote_path, file_path)
181 return open(file_path).read()
Tom Wai-Hong Tamf6bb17f2014-04-24 13:36:49 +0800182
183
184 def get_resolution(self):
185 """Gets the external resolution on framebuffer.
186
187 @return The resolution tuple (width, height)
188 """
189 output = self.get_connector_name()
190 width, height, _, _ = self._display_xmlrpc_client.get_resolution(output)
191 return (width, height)