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