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