1# Copyright 2020 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15
16import logging
17import time
18
19from mobly import asserts
20from mobly import base_test
21from mobly import utils
22from mobly.controllers import android_device
23
24import its_session_utils
25
26ADAPTIVE_BRIGHTNESS_OFF = '0'
27TABLET_CMD_DELAY_SEC = 0.5  # found empirically
28TABLET_DIMMER_TIMEOUT_MS = 1800000  # this is max setting possible
29CTS_VERIFIER_PKG = 'com.android.cts.verifier'
30WAIT_TIME_SEC = 5
31SCROLLER_TIMEOUT_MS = 3000
32VALID_NUM_DEVICES = (1, 2)
33NOT_YET_MANDATED_ALL = 100
34
35# Not yet mandated tests ['test', first_api_level mandatory]
36# ie. ['test_test_patterns', 30] is MANDATED for first_api_level >= 30
37NOT_YET_MANDATED = {
38    'scene0': [['test_test_patterns', 30],
39               ['test_tonemap_curve', 30]],
40    'scene1_1': [['test_ae_precapture_trigger', 28],
41                 ['test_channel_saturation', 29]],
42    'scene1_2': [],
43    'scene2_a': [['test_jpeg_quality', 30]],
44    'scene2_b': [['test_auto_per_frame_control', NOT_YET_MANDATED_ALL]],
45    'scene2_c': [],
46    'scene2_d': [['test_num_faces', 30]],
47    'scene2_e': [['test_num_faces', 30], ['test_continuous_picture', 30]],
48    'scene3': [],
49    'scene4': [],
50    'scene5': [],
51    'scene6': [['test_zoom', 30]],
52    'sensor_fusion': [],
53    'scene_change': [['test_scene_change', 31]]
54}
55
56
57class ItsBaseTest(base_test.BaseTestClass):
58  """Base test for CameraITS tests.
59
60  Tests inherit from this class execute in the Camera ITS automation systems.
61  These systems consist of either:
62    1. a device under test (dut) and an external rotation controller
63    2. a device under test (dut) and one screen device(tablet)
64    3. a device under test (dut) and manual charts
65
66  Attributes:
67    dut: android_device.AndroidDevice, the device under test.
68    tablet: android_device.AndroidDevice, the tablet device used to display
69        scenes.
70  """
71
72  def setup_class(self):
73    devices = self.register_controller(android_device, min_number=1)
74    self.dut = devices[0]
75    self.camera = str(self.user_params['camera'])
76    logging.debug('Camera_id: %s', self.camera)
77    if self.user_params.get('chart_distance'):
78      self.chart_distance = float(self.user_params['chart_distance'])
79      logging.debug('Chart distance: %s cm', self.chart_distance)
80    if self.user_params.get('chart_loc_arg'):
81      self.chart_loc_arg = self.user_params['chart_loc_arg']
82    else:
83      self.chart_loc_arg = ''
84    if self.user_params.get('debug_mode'):
85      self.debug_mode = True if self.user_params[
86          'debug_mode'] == 'True' else False
87    if self.user_params.get('scene'):
88      self.scene = self.user_params['scene']
89    camera_id_combo = self.parse_hidden_camera_id()
90    self.camera_id = camera_id_combo[0]
91    if len(camera_id_combo) == 2:
92      self.hidden_physical_id = camera_id_combo[1]
93    else:
94      self.hidden_physical_id = None
95
96    num_devices = len(devices)
97    if num_devices == 2:  # scenes [0,1,2,3,4,5,6]
98      try:
99        self.tablet = devices[1]
100        self.tablet_screen_brightness = self.user_params['brightness']
101      except KeyError:
102        logging.debug('Not all tablet arguments set.')
103    else:  # sensor_fusion or manual run
104      try:
105        self.fps = int(self.user_params['fps'])
106        img_size = self.user_params['img_size'].split(',')
107        self.img_w = int(img_size[0])
108        self.img_h = int(img_size[1])
109        self.test_length = float(self.user_params['test_length'])
110        self.rotator_cntl = self.user_params['rotator_cntl']
111        self.rotator_ch = str(self.user_params['rotator_ch'])
112      except KeyError:
113        self.tablet = None
114        logging.debug('Not all arguments set. Manual run.')
115
116    self._setup_devices(num_devices)
117
118  def _setup_devices(self, num):
119    """Sets up each device in parallel if more than one device."""
120    if num not in VALID_NUM_DEVICES:
121      raise AssertionError(
122          f'Incorrect number of devices! Must be in {str(VALID_NUM_DEVICES)}')
123    if num == 1:
124      self.setup_dut(self.dut)
125    else:
126      logic = lambda d: self.setup_dut(d) if d else self.setup_tablet()
127      utils.concurrent_exec(
128          logic, [(self.dut,), (None,)],
129          max_workers=2,
130          raise_on_exception=True)
131
132  def setup_dut(self, device):
133    self.dut.adb.shell(
134        'am start -n com.android.cts.verifier/.CtsVerifierActivity')
135    # Wait for the app screen to appear.
136    time.sleep(WAIT_TIME_SEC)
137
138  def setup_tablet(self):
139    # KEYCODE_POWER to reset dimmer timer. KEYCODE_WAKEUP no effect if ON.
140    self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_POWER'])
141    time.sleep(TABLET_CMD_DELAY_SEC)
142    self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_WAKEUP'])
143    time.sleep(TABLET_CMD_DELAY_SEC)
144    # Dismiss keyguard
145    self.tablet.adb.shell(['wm', 'dismiss-keyguard'])
146    time.sleep(TABLET_CMD_DELAY_SEC)
147    # Turn off the adaptive brightness on tablet.
148    self.tablet.adb.shell(
149        ['settings', 'put', 'system', 'screen_brightness_mode',
150         ADAPTIVE_BRIGHTNESS_OFF])
151    # Set the screen brightness
152    self.tablet.adb.shell(
153        ['settings', 'put', 'system', 'screen_brightness',
154         str(self.tablet_screen_brightness)])
155    logging.debug('Tablet brightness set to: %s',
156                  format(self.tablet_screen_brightness))
157    self.tablet.adb.shell('settings put system screen_off_timeout {}'.format(
158        TABLET_DIMMER_TIMEOUT_MS))
159    self.set_tablet_landscape_orientation()
160    self.tablet.adb.shell('am force-stop com.google.android.apps.docs')
161    self.tablet.adb.shell('am force-stop com.google.android.apps.photos')
162    self.tablet.adb.shell('am force-stop com.android.gallery3d')
163
164  def set_tablet_landscape_orientation(self):
165    """Sets the screen orientation to landscape.
166    """
167    # Get the landscape orientation value.
168    # This value is different for Pixel C/Huawei/Samsung tablets.
169    output = self.tablet.adb.shell('dumpsys window | grep mLandscapeRotation')
170    logging.debug('dumpsys window output: %s', output.decode('utf-8').strip())
171    output_list = str(output.decode('utf-8')).strip().split(' ')
172    for val in output_list:
173        if 'LandscapeRotation' in val:
174            landscape_val = str(val.split('=')[-1])
175            # For some tablets the values are in constant forms such as ROTATION_90
176            if 'ROTATION_90' in landscape_val:
177                landscape_val = '1'
178            logging.debug('Changing the orientation to landscape mode.')
179            self.tablet.adb.shell(['settings', 'put', 'system', 'user_rotation',
180                                   landscape_val])
181            break
182    logging.debug('Reported tablet orientation is: %d',
183                  int(self.tablet.adb.shell('settings get system user_rotation')))
184
185  def parse_hidden_camera_id(self):
186    """Parse the string of camera ID into an array.
187
188    Returns:
189      Array with camera id and hidden_physical camera id.
190    """
191    camera_id_combo = self.camera.split(its_session_utils.SUB_CAMERA_SEPARATOR)
192    return camera_id_combo
193
194  def determine_not_yet_mandated_tests(self, device_id, scene):
195    """Determine not_yet_mandated tests from NOT_YET_MANDATED list & phone info.
196
197    Args:
198     device_id: string of device id number.
199     scene: scene to which tests belong to.
200
201    Returns:
202       dict of not yet mandated tests
203    """
204    # Initialize not_yet_mandated.
205    not_yet_mandated = {}
206    not_yet_mandated[scene] = []
207
208    # Determine first API level for device.
209    first_api_level = its_session_utils.get_first_api_level(device_id)
210
211    # Determine which test are not yet mandated for first api level.
212    tests = NOT_YET_MANDATED[scene]
213    for [test, first_api_level_mandated] in tests:
214      logging.debug('First API level %s MANDATED: %d',
215                    test, first_api_level_mandated)
216      if first_api_level < first_api_level_mandated:
217        not_yet_mandated[scene].append(test)
218    return not_yet_mandated
219
220  def on_pass(self, record):
221    logging.debug('%s on PASS.', record.test_name)
222
223  def on_fail(self, record):
224    logging.debug('%s on FAIL.', record.test_name)
225    if self.user_params.get('scene'):
226      not_yet_mandated_tests = self.determine_not_yet_mandated_tests(
227          self.dut.serial, self.scene)
228      if self.current_test_info.name in not_yet_mandated_tests[self.scene]:
229        logging.debug('%s is not yet mandated.', self.current_test_info.name)
230        asserts.fail('Not yet mandated test', extras='Not yet mandated test')
231
232
233