blob: c79c56fef49c2358a857ff2c58bde07ccead5cae [file] [log] [blame]
# Copyright 2020 The Android Open Source Project
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import logging
import os
import time
import cv2
import its_session_utils
import lighting_control_utils
from mobly import base_test
from mobly import utils
from mobly.controllers import android_device
ADAPTIVE_BRIGHTNESS_OFF = '0'
TABLET_CMD_DELAY_SEC = 0.5 # found empirically
TABLET_DIMMER_TIMEOUT_MS = 1800000 # this is max setting possible
CTS_VERIFIER_PKG = 'com.android.cts.verifier'
WAIT_TIME_SEC = 5
SCROLLER_TIMEOUT_MS = 3000
VALID_NUM_DEVICES = (1, 2)
FRONT_CAMERA_ID_PREFIX = '1'
logging.getLogger('matplotlib.font_manager').disabled = True
class ItsBaseTest(base_test.BaseTestClass):
"""Base test for CameraITS tests.
Tests inherit from this class execute in the Camera ITS automation systems.
These systems consist of either:
1. a device under test (dut) and an external rotation controller
2. a device under test (dut) and one screen device(tablet)
3. a device under test (dut) and manual charts
Attributes:
dut: android_device.AndroidDevice, the device under test.
tablet: android_device.AndroidDevice, the tablet device used to display
scenes.
"""
def setup_class(self):
devices = self.register_controller(android_device, min_number=1)
self.dut = devices[0]
self.camera = str(self.user_params['camera'])
logging.debug('Camera_id: %s', self.camera)
if self.user_params.get('chart_distance'):
self.chart_distance = float(self.user_params['chart_distance'])
logging.debug('Chart distance: %s cm', self.chart_distance)
if (self.user_params.get('lighting_cntl') and
self.user_params.get('lighting_ch')):
self.lighting_cntl = self.user_params['lighting_cntl']
self.lighting_ch = str(self.user_params['lighting_ch'])
else:
self.lighting_cntl = 'None'
self.lighting_ch = '1'
if self.user_params.get('tablet_device'):
self.tablet_device = self.user_params['tablet_device'] == 'True'
if self.user_params.get('debug_mode'):
self.debug_mode = self.user_params['debug_mode'] == 'True'
if self.user_params.get('scene'):
self.scene = self.user_params['scene']
camera_id_combo = self.parse_hidden_camera_id()
self.camera_id = camera_id_combo[0]
if len(camera_id_combo) == 2:
self.hidden_physical_id = camera_id_combo[1]
else:
self.hidden_physical_id = None
num_devices = len(devices)
if num_devices == 2: # scenes [0,1,2,3,4,5,6]
try:
self.tablet = devices[1]
self.tablet_screen_brightness = self.user_params['brightness']
tablet_name_unencoded = self.tablet.adb.shell(
['getprop', 'ro.build.product']
)
tablet_name = str(tablet_name_unencoded.decode('utf-8')).strip()
logging.debug('tablet name: %s', tablet_name)
its_session_utils.validate_tablet_brightness(
tablet_name, self.tablet_screen_brightness)
except KeyError:
logging.debug('Not all tablet arguments set.')
else: # sensor_fusion or manual run
try:
self.fps = int(self.user_params['fps'])
img_size = self.user_params['img_size'].split(',')
self.img_w = int(img_size[0])
self.img_h = int(img_size[1])
self.test_length = float(self.user_params['test_length'])
self.rotator_cntl = self.user_params['rotator_cntl']
self.rotator_ch = str(self.user_params['rotator_ch'])
except KeyError:
self.tablet = None
logging.debug('Not all arguments set. Manual run.')
self._setup_devices(num_devices)
arduino_serial_port = lighting_control_utils.lighting_control(
self.lighting_cntl, self.lighting_ch)
if arduino_serial_port and self.scene != 'scene0':
lighting_control_utils.set_light_brightness(
self.lighting_ch, 255, arduino_serial_port)
logging.debug('Light is turned ON.')
# Check if current foldable state matches scene, if applicable
if self.user_params.get('foldable_device', 'False') == 'True':
foldable_state_unencoded = tablet_name_unencoded = self.dut.adb.shell(
['cmd', 'device_state', 'state']
)
foldable_state = str(foldable_state_unencoded.decode('utf-8')).strip()
is_folded = 'CLOSE' in foldable_state
scene_with_suffix = self.user_params.get('scene_with_suffix')
if scene_with_suffix:
if 'folded' in scene_with_suffix and not is_folded:
raise AssertionError(
f'Testing folded scene {scene_with_suffix} with unfolded device!')
if ('folded' not in scene_with_suffix and is_folded and
self.camera.startswith(FRONT_CAMERA_ID_PREFIX)): # Not rear camera
raise AssertionError(
f'Testing unfolded scene {scene_with_suffix} with a '
'non-rear camera while device is folded!'
)
else:
logging.debug('Testing without `run_all_tests`')
cv2_version = cv2.__version__
logging.debug('cv2_version: %s', cv2_version)
def _setup_devices(self, num):
"""Sets up each device in parallel if more than one device."""
if num not in VALID_NUM_DEVICES:
raise AssertionError(
f'Incorrect number of devices! Must be in {str(VALID_NUM_DEVICES)}')
if num == 1:
self.setup_dut(self.dut)
else:
logic = lambda d: self.setup_dut(d) if d else self.setup_tablet()
utils.concurrent_exec(
logic, [(self.dut,), (None,)],
max_workers=2,
raise_on_exception=True)
def setup_dut(self, device):
self.dut.adb.shell(
'am start -n com.android.cts.verifier/.CtsVerifierActivity')
logging.debug('Setting up device: %s', str(device))
# Wait for the app screen to appear.
time.sleep(WAIT_TIME_SEC)
def setup_tablet(self):
# KEYCODE_POWER to reset dimmer timer. KEYCODE_WAKEUP no effect if ON.
self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_POWER'])
time.sleep(TABLET_CMD_DELAY_SEC)
self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_WAKEUP'])
time.sleep(TABLET_CMD_DELAY_SEC)
# Dismiss keyguard
self.tablet.adb.shell(['wm', 'dismiss-keyguard'])
time.sleep(TABLET_CMD_DELAY_SEC)
# Turn off the adaptive brightness on tablet.
self.tablet.adb.shell(
['settings', 'put', 'system', 'screen_brightness_mode',
ADAPTIVE_BRIGHTNESS_OFF])
# Set the screen brightness
self.tablet.adb.shell(
['settings', 'put', 'system', 'screen_brightness',
str(self.tablet_screen_brightness)])
logging.debug('Tablet brightness set to: %s',
format(self.tablet_screen_brightness))
self.tablet.adb.shell('settings put system screen_off_timeout {}'.format(
TABLET_DIMMER_TIMEOUT_MS))
self.set_tablet_landscape_orientation()
self.tablet.adb.shell('am force-stop com.google.android.apps.docs')
self.tablet.adb.shell('am force-stop com.google.android.apps.photos')
self.tablet.adb.shell('am force-stop com.android.gallery3d')
self.tablet.adb.shell('am force-stop com.sec.android.gallery3d')
self.tablet.adb.shell('am force-stop com.miui.gallery')
self.tablet.adb.shell(
'settings put global policy_control immersive.full=*')
def set_tablet_landscape_orientation(self):
"""Sets the screen orientation to landscape.
"""
# Get the landscape orientation value.
# This value is different for Pixel C/Huawei/Samsung tablets.
output = self.tablet.adb.shell('dumpsys window | grep mLandscapeRotation')
logging.debug('dumpsys window output: %s', output.decode('utf-8').strip())
output_list = str(output.decode('utf-8')).strip().split(' ')
for val in output_list:
if 'LandscapeRotation' in val:
landscape_val = str(val.split('=')[-1])
# For some tablets the values are in constant forms such as ROTATION_90
if 'ROTATION_90' in landscape_val:
landscape_val = '1'
elif 'ROTATION_0' in landscape_val:
landscape_val = '0'
logging.debug('Changing the orientation to landscape mode.')
self.tablet.adb.shell(['settings', 'put', 'system', 'user_rotation',
landscape_val])
break
logging.debug('Reported tablet orientation is: %d',
int(self.tablet.adb.shell(
'settings get system user_rotation')))
def set_screen_brightness(self, brightness_level):
"""Sets the screen brightness to desired level.
Args:
brightness_level : brightness level to set.
"""
# Turn off the adaptive brightness on tablet.
self.tablet.adb.shell(
['settings', 'put', 'system', 'screen_brightness_mode', '0'])
# Set the screen brightness
self.tablet.adb.shell([
'settings', 'put', 'system', 'screen_brightness',
brightness_level
])
logging.debug('Tablet brightness set to: %s', brightness_level)
actual_brightness = self.tablet.adb.shell(
'settings get system screen_brightness')
if int(actual_brightness) != int(brightness_level):
raise AssertionError('Brightness was not set as expected! '
'Requested brightness: {brightness_level}, '
'Actual brightness: {actual_brightness}')
def turn_off_tablet(self):
"""Turns off tablet, raising AssertionError if tablet is not found."""
if self.tablet:
lighting_control_utils.turn_off_device(self.tablet)
else:
raise AssertionError('Test must be run with tablet.')
def parse_hidden_camera_id(self):
"""Parse the string of camera ID into an array.
Returns:
Array with camera id and hidden_physical camera id.
"""
camera_id_combo = self.camera.split(its_session_utils.SUB_CAMERA_SEPARATOR)
return camera_id_combo
def on_pass(self, record):
logging.debug('%s on PASS.', record.test_name)
def on_fail(self, record):
logging.debug('%s on FAIL.', record.test_name)
def teardown_class(self):
# edit root_output_path and summary_writer path
# to add test name to output directory
logging.debug('summary_writer._path: %s', self.summary_writer._path)
summary_head, summary_tail = os.path.split(self.summary_writer._path)
self.summary_writer._path = os.path.join(
f'{summary_head}_{self.__class__.__name__}', summary_tail)
os.rename(self.root_output_path,
f'{self.root_output_path}_{self.__class__.__name__}')
# print root_output_path so that it can be written to report log.
# Note: Do not replace print with logging.debug here.
print('root_output_path:',
f'{self.root_output_path}_{self.__class__.__name__}')