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 5import contextlib 6import json 7import logging 8from lxml import etree 9import os 10import StringIO 11 12from autotest_lib.client.common_lib import utils 13 14 15class ChartFixture: 16 """Sets up chart tablet to display dummy scene image.""" 17 DISPLAY_SCRIPT = '/usr/local/autotest/bin/display_chart.py' 18 19 def __init__(self, chart_host, scene_uri): 20 self.host = chart_host 21 self.scene_uri = scene_uri 22 self.display_pid = None 23 24 def initialize(self): 25 """Prepare scene file and display it on chart host.""" 26 logging.info('Prepare scene file') 27 chart_dir = self.host.get_tmp_dir() 28 scene_path = os.path.join( 29 chart_dir, self.scene_uri[self.scene_uri.rfind('/') + 1:]) 30 self.host.run('wget', args=('-O', scene_path, self.scene_uri)) 31 self.host.run('chmod', args=('-R', '755', chart_dir)) 32 33 logging.info('Display scene file') 34 self.display_pid = self.host.run_background( 35 'python %s %s' % (self.DISPLAY_SCRIPT, scene_path)) 36 # TODO(inker): Suppose chart should be displayed very soon. Or require 37 # of waiting until chart actually displayed. 38 39 def cleanup(self): 40 """Cleanup display script.""" 41 if self.display_pid is not None: 42 self.host.run('kill', args=('-2', str(self.display_pid))) 43 44 45def get_chart_address(host_address, args): 46 """Get address of chart tablet from commandline args or mapping logic in 47 test lab. 48 49 @param host_address: a list of hostname strings. 50 @param args: a dict parse from commandline args. 51 @return: 52 A list of strings for chart tablet addresses. 53 """ 54 address = utils.args_to_dict(args).get('chart') 55 if address is not None: 56 return address.split(',') 57 elif utils.is_in_container(): 58 return [ 59 utils.get_lab_chart_address(host) 60 for host in host_address 61 ] 62 else: 63 return None 64 65 66class DUTFixture: 67 """Sets up camera filter for target camera facing on DUT.""" 68 TEST_CONFIG_PATH = '/var/cache/camera/test_config.json' 69 GENERATE_CAMERA_PROFILE = os.path.join('/usr', 'bin', 70 'generate_camera_profile') 71 GENERATE_CAMERA_PROFILE_BACKUP = GENERATE_CAMERA_PROFILE + '.bak' 72 CAMERA_PROFILE_PATH = ('/mnt/stateful_partition/encrypted/var/cache/camera' 73 '/media_profiles.xml') 74 75 def __init__(self, test, host, facing): 76 self.test = test 77 self.host = host 78 self.facing = facing 79 80 @contextlib.contextmanager 81 def _set_selinux_permissive(self): 82 selinux_mode = self.host.run_output('getenforce') 83 self.host.run('setenforce 0') 84 yield 85 self.host.run('setenforce', args=(selinux_mode, )) 86 87 def _filter_camera_profile(self, content, facing): 88 """Filter camera profile of target facing from content of camera 89 profile. 90 91 @return: 92 New camera profile with only target facing, camera ids are 93 renumbered from 0. 94 """ 95 tree = etree.parse( 96 StringIO.StringIO(content), 97 parser=etree.XMLParser(compact=False)) 98 root = tree.getroot() 99 profiles = root.findall('CamcorderProfiles') 100 logging.debug('%d number of camera(s) found in camera profile', 101 len(profiles)) 102 assert 1 <= len(profiles) <= 2 103 if len(profiles) == 2: 104 cam_id = 0 if facing == 'back' else 1 105 for p in profiles: 106 if cam_id == int(p.attrib['cameraId']): 107 p.attrib['cameraId'] = '0' 108 else: 109 root.remove(p) 110 else: 111 with self.test._login_chrome( 112 board=self.test._get_board_name(), 113 reboot=False), self._set_selinux_permissive(): 114 has_front_camera = ( 115 'feature:android.hardware.camera.front' in self.host. 116 run_output('android-sh -c "pm list features"')) 117 logging.debug('has_front_camera=%s', has_front_camera) 118 if (facing == 'front') != has_front_camera: 119 root.remove(profiles[0]) 120 return etree.tostring( 121 tree, xml_declaration=True, encoding=tree.docinfo.encoding) 122 123 def _read_file(self, filepath): 124 """Read content of filepath from host.""" 125 tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath)) 126 self.host.get_file(filepath, tmp_path, delete_dest=True) 127 with open(tmp_path) as f: 128 return f.read() 129 130 def _write_file(self, filepath, content, permission=None, owner=None): 131 """Write content to filepath on remote host. 132 @param permission: set permission to 0xxx octal number of remote file. 133 @param owner: set owner of remote file. 134 """ 135 tmp_path = os.path.join(self.test.tmpdir, os.path.basename(filepath)) 136 with open(tmp_path, 'w') as f: 137 f.write(content) 138 if permission is not None: 139 os.chmod(tmp_path, permission) 140 self.host.send_file(tmp_path, filepath, delete_dest=True) 141 if owner is not None: 142 self.host.run('chown', args=(owner, filepath)) 143 144 def initialize(self): 145 """Filter out camera other than target facing on DUT.""" 146 logging.info('Restart camera service with filter option') 147 self._write_file( 148 self.TEST_CONFIG_PATH, 149 json.dumps({ 150 'enable_back_camera': self.facing == 'back', 151 'enable_front_camera': self.facing == 'front', 152 'enable_external_camera': False 153 }), 154 owner='arc-camera') 155 self.host.run('restart cros-camera') 156 157 # To replace camera profile in ARC++ container, arc_setup will run 158 # GENERATE_CAMERA_PROFILE and mount its generated profile under 159 # CAMERA_PROFILE_PATH into container. 160 logging.info('Replace camera profile in ARC++ container') 161 self.host.run( 162 'mv', 163 args=(self.GENERATE_CAMERA_PROFILE, 164 self.GENERATE_CAMERA_PROFILE_BACKUP)) 165 self._write_file( 166 self.GENERATE_CAMERA_PROFILE, 167 '''\ 168#!/bin/bash 169# Put this executable file to %r to make sure arc-setup knows 170# we're using dynamic media_profiles.xml copy from host path 171# %r''' % (self.GENERATE_CAMERA_PROFILE, self.CAMERA_PROFILE_PATH), 172 permission=0755) 173 profile = self._read_file(self.CAMERA_PROFILE_PATH) 174 new_profile = self._filter_camera_profile(profile, self.facing) 175 self._write_file(self.CAMERA_PROFILE_PATH, new_profile) 176 self.host.run('restart ui') 177 178 def cleanup(self): 179 """Cleanup camera filter.""" 180 logging.info('Remove filter option and restore camera service') 181 self.host.run('rm', args=('-f', self.TEST_CONFIG_PATH)) 182 self.host.run('restart cros-camera') 183 184 # Restore GENERATE_CAMERA_PROFILE to regenerate camera profile on DUT. 185 logging.info('Restore camera profile in ARC++ container') 186 self.host.run( 187 'mv', 188 args=(self.GENERATE_CAMERA_PROFILE_BACKUP, 189 self.GENERATE_CAMERA_PROFILE), 190 ignore_status=True) 191 self.host.run('restart ui') 192