xref: /aosp_15_r20/external/autotest/client/bin/display_chart.py (revision 9c5db1993ded3edbeafc8092d69fe5de2ee02df7)
1*9c5db199SXin Li# Copyright 2018 The Chromium OS Authors. All rights reserved.
2*9c5db199SXin Li# Use of this source code is governed by a BSD-style license that can be
3*9c5db199SXin Li# found in the LICENSE file.
4*9c5db199SXin Li
5*9c5db199SXin Li'''Login with test account and display chart file using telemetry.'''
6*9c5db199SXin Li
7*9c5db199SXin Liimport argparse
8*9c5db199SXin Liimport contextlib
9*9c5db199SXin Liimport json
10*9c5db199SXin Liimport logging
11*9c5db199SXin Liimport os
12*9c5db199SXin Liimport select
13*9c5db199SXin Liimport signal
14*9c5db199SXin Liimport sys
15*9c5db199SXin Liimport tempfile
16*9c5db199SXin Li
17*9c5db199SXin Li# Set chart process preferred logging format before overridden by importing
18*9c5db199SXin Li# common package.
19*9c5db199SXin Lilogging.basicConfig(
20*9c5db199SXin Li        level=logging.DEBUG,
21*9c5db199SXin Li        format='%(asctime)s - %(levelname)s - %(message)s')
22*9c5db199SXin Li
23*9c5db199SXin Li# This sets up import paths for autotest.
24*9c5db199SXin Lisys.path.append('/usr/local/autotest/bin')
25*9c5db199SXin Liimport common
26*9c5db199SXin Lifrom autotest_lib.client.bin import utils
27*9c5db199SXin Lifrom autotest_lib.client.cros import constants
28*9c5db199SXin Lifrom autotest_lib.client.cros.multimedia import display_facade as display_facade_lib
29*9c5db199SXin Lifrom autotest_lib.client.cros.multimedia import facade_resource
30*9c5db199SXin Lifrom autotest_lib.client.common_lib.cros import chrome
31*9c5db199SXin Li
32*9c5db199SXin LiDEFAULT_DISPLAY_LEVEL = 96.0
33*9c5db199SXin Li
34*9c5db199SXin Li
35*9c5db199SXin Liclass Fifo:
36*9c5db199SXin Li    """Fifo to communicate with chart service."""
37*9c5db199SXin Li
38*9c5db199SXin Li    FIFO_POLL_TIMEOUT_MS = 300
39*9c5db199SXin Li
40*9c5db199SXin Li    def __init__(self):
41*9c5db199SXin Li        self._ready = False
42*9c5db199SXin Li
43*9c5db199SXin Li    def __enter__(self):
44*9c5db199SXin Li        # Prepare fifo file.
45*9c5db199SXin Li        self._tmpdir = tempfile.mkdtemp(prefix='chart_fifo_', dir='/tmp')
46*9c5db199SXin Li        self._path = os.path.join(self._tmpdir, 'fifo')
47*9c5db199SXin Li        os.mkfifo(self._path)
48*9c5db199SXin Li
49*9c5db199SXin Li        # Hook SIGINT signal to stop fifo.
50*9c5db199SXin Li        self._original_sig_handler = signal.getsignal(signal.SIGINT)
51*9c5db199SXin Li
52*9c5db199SXin Li        def handler(a, b):
53*9c5db199SXin Li            signal.signal(signal.SIGINT, self._original_sig_handler)
54*9c5db199SXin Li            self._ready = False
55*9c5db199SXin Li
56*9c5db199SXin Li        signal.signal(signal.SIGINT, handler)
57*9c5db199SXin Li
58*9c5db199SXin Li        self._ready = True
59*9c5db199SXin Li        return self
60*9c5db199SXin Li
61*9c5db199SXin Li    def __exit__(self, exc_type, exc_value, exc_traceback):
62*9c5db199SXin Li        signal.signal(signal.SIGINT, self._original_sig_handler)
63*9c5db199SXin Li        os.unlink(self._path)
64*9c5db199SXin Li        os.rmdir(self._tmpdir)
65*9c5db199SXin Li
66*9c5db199SXin Li    def get_path(self):
67*9c5db199SXin Li        return self._path
68*9c5db199SXin Li
69*9c5db199SXin Li    def read(self):
70*9c5db199SXin Li        """Read json format command from fifo."""
71*9c5db199SXin Li        while self._ready:
72*9c5db199SXin Li            with os.fdopen(os.open(self._path, os.O_RDONLY | os.O_NONBLOCK),
73*9c5db199SXin Li                           'r') as fd:
74*9c5db199SXin Li                p = select.poll()
75*9c5db199SXin Li                p.register(fd, select.POLLIN)
76*9c5db199SXin Li                if p.poll(self.FIFO_POLL_TIMEOUT_MS):
77*9c5db199SXin Li                    cmd = fd.read()
78*9c5db199SXin Li                    return json.loads(cmd)
79*9c5db199SXin Li        return None
80*9c5db199SXin Li
81*9c5db199SXin Li
82*9c5db199SXin Li@contextlib.contextmanager
83*9c5db199SXin Lidef control_brightness():
84*9c5db199SXin Li    """Help to programmatically control the brightness.
85*9c5db199SXin Li
86*9c5db199SXin Li    Returns:
87*9c5db199SXin Li      A function which can set brightness between [0.0, 100.0].
88*9c5db199SXin Li    """
89*9c5db199SXin Li
90*9c5db199SXin Li    def set_brightness(display_level):
91*9c5db199SXin Li        utils.system('backlight_tool --set_brightness_percent=%s' %
92*9c5db199SXin Li                     display_level)
93*9c5db199SXin Li        logging.info('Set display brightness to %r', display_level)
94*9c5db199SXin Li
95*9c5db199SXin Li    original_display_level = utils.system_output(
96*9c5db199SXin Li            'backlight_tool --get_brightness_percent')
97*9c5db199SXin Li    logging.info('Save original display brightness %r', original_display_level)
98*9c5db199SXin Li
99*9c5db199SXin Li    utils.system('stop powerd', ignore_status=True)
100*9c5db199SXin Li    yield set_brightness
101*9c5db199SXin Li    logging.info('Restore display brightness %r', original_display_level)
102*9c5db199SXin Li    utils.system('start powerd', ignore_status=True)
103*9c5db199SXin Li    set_brightness(original_display_level)
104*9c5db199SXin Li
105*9c5db199SXin Li
106*9c5db199SXin Li@contextlib.contextmanager
107*9c5db199SXin Lidef control_display(cr):
108*9c5db199SXin Li    """Fix the display orientation instead of using gyro orientation."""
109*9c5db199SXin Li    board = utils.get_board()
110*9c5db199SXin Li    logging.info("Board:%s", board)
111*9c5db199SXin Li    if board == 'scarlet':
112*9c5db199SXin Li        DISPLAY_ORIENTATION = 90
113*9c5db199SXin Li    else:
114*9c5db199SXin Li        DISPLAY_ORIENTATION = 0
115*9c5db199SXin Li
116*9c5db199SXin Li    logging.info('Set fullscreen.')
117*9c5db199SXin Li    facade = facade_resource.FacadeResource(cr)
118*9c5db199SXin Li    display_facade = display_facade_lib.DisplayFacadeLocal(facade)
119*9c5db199SXin Li    display_facade.set_fullscreen(True)
120*9c5db199SXin Li
121*9c5db199SXin Li    logging.info('Fix screen rotation %d.', DISPLAY_ORIENTATION)
122*9c5db199SXin Li    internal_display_id = display_facade.get_internal_display_id()
123*9c5db199SXin Li    original_display_orientation = display_facade.get_display_rotation(
124*9c5db199SXin Li            internal_display_id)
125*9c5db199SXin Li    display_facade.set_display_rotation(internal_display_id,
126*9c5db199SXin Li                                        rotation=DISPLAY_ORIENTATION)
127*9c5db199SXin Li    yield
128*9c5db199SXin Li    display_facade.set_display_rotation(internal_display_id,
129*9c5db199SXin Li                                        rotation=original_display_orientation)
130*9c5db199SXin Li
131*9c5db199SXin Li
132*9c5db199SXin Lidef display(chart_path, display_level):
133*9c5db199SXin Li    """Display chart on device by using telemetry."""
134*9c5db199SXin Li    chart_path = os.path.abspath(chart_path)
135*9c5db199SXin Li    if os.path.isfile(chart_path):
136*9c5db199SXin Li        first_chart_name = os.path.basename(chart_path)
137*9c5db199SXin Li        chart_dir_path = os.path.dirname(chart_path)
138*9c5db199SXin Li    elif os.path.isdir(chart_path):
139*9c5db199SXin Li        first_chart_name = None
140*9c5db199SXin Li        chart_dir_path = chart_path
141*9c5db199SXin Li    else:
142*9c5db199SXin Li        assert False, 'chart_path %r not found.' % chart_path
143*9c5db199SXin Li
144*9c5db199SXin Li    def show_chart(name):
145*9c5db199SXin Li        """Show image on chart base on file name"""
146*9c5db199SXin Li        filepath = os.path.join(chart_dir_path, name)
147*9c5db199SXin Li        logging.info('Display chart file of path %r.', filepath)
148*9c5db199SXin Li        tab = cr.browser.tabs[0]
149*9c5db199SXin Li        tab.Navigate(cr.browser.platform.http_server.UrlOf(filepath))
150*9c5db199SXin Li        tab.WaitForDocumentReadyStateToBeComplete()
151*9c5db199SXin Li
152*9c5db199SXin Li    logging.info('Setup SIGINT listener for stop displaying.')
153*9c5db199SXin Li
154*9c5db199SXin Li    with chrome.Chrome(
155*9c5db199SXin Li            extension_paths=[constants.DISPLAY_TEST_EXTENSION],
156*9c5db199SXin Li            autotest_ext=True,
157*9c5db199SXin Li            init_network_controller=True) as cr, \
158*9c5db199SXin Li            control_brightness() as set_brightness, \
159*9c5db199SXin Li            control_display(cr), \
160*9c5db199SXin Li            Fifo() as fifo:
161*9c5db199SXin Li        set_brightness(display_level)
162*9c5db199SXin Li
163*9c5db199SXin Li        cr.browser.platform.SetHTTPServerDirectories(chart_dir_path)
164*9c5db199SXin Li        if first_chart_name is not None:
165*9c5db199SXin Li            show_chart(first_chart_name)
166*9c5db199SXin Li
167*9c5db199SXin Li        logging.info('Chart is ready. Fifo: %s', fifo.get_path())
168*9c5db199SXin Li        # Flush the 'is ready' message for server test to sync with ready state.
169*9c5db199SXin Li        sys.stdout.flush()
170*9c5db199SXin Li        sys.stderr.flush()
171*9c5db199SXin Li
172*9c5db199SXin Li        while True:
173*9c5db199SXin Li            cmd = fifo.read()
174*9c5db199SXin Li            if cmd is None:
175*9c5db199SXin Li                break
176*9c5db199SXin Li            new_chart_name = cmd.get('chart_name')
177*9c5db199SXin Li            if new_chart_name is not None:
178*9c5db199SXin Li                show_chart(new_chart_name)
179*9c5db199SXin Li
180*9c5db199SXin Li            new_display_level = cmd.get('display_level')
181*9c5db199SXin Li            if new_display_level is not None:
182*9c5db199SXin Li                set_brightness(new_display_level)
183*9c5db199SXin Li
184*9c5db199SXin Li
185*9c5db199SXin Liif __name__ == '__main__':
186*9c5db199SXin Li    argparser = argparse.ArgumentParser(
187*9c5db199SXin Li            description='Display chart file on chrome by using telemetry.'
188*9c5db199SXin Li            ' Send SIGINT or keyboard interrupt to stop displaying.')
189*9c5db199SXin Li    argparser.add_argument(
190*9c5db199SXin Li            'chart_path',
191*9c5db199SXin Li            help='Path of displayed chart file'
192*9c5db199SXin Li            ' or the directory to put chart files for displaying in fifo mode.'
193*9c5db199SXin Li    )
194*9c5db199SXin Li    argparser.add_argument(
195*9c5db199SXin Li            '--display_level',
196*9c5db199SXin Li            type=float,
197*9c5db199SXin Li            default=DEFAULT_DISPLAY_LEVEL,
198*9c5db199SXin Li            help=
199*9c5db199SXin Li            'Set brightness as linearly-calculated percent in [0.0, 100.0].')
200*9c5db199SXin Li
201*9c5db199SXin Li    args = argparser.parse_args()
202*9c5db199SXin Li    display(args.chart_path, args.display_level)
203