• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 os
18import time
19import cv2
20
21import its_session_utils
22import lighting_control_utils
23from mobly import base_test
24from mobly import utils
25from mobly.controllers import android_device
26from snippet_uiautomator import uiautomator
27
28
29ADAPTIVE_BRIGHTNESS_OFF = '0'
30TABLET_CMD_DELAY_SEC = 0.5  # found empirically
31TABLET_DIMMER_TIMEOUT_MS = 1800000  # this is max setting possible
32CTS_VERIFIER_PKG = 'com.android.cts.verifier'
33WAIT_TIME_SEC = 5
34SCROLLER_TIMEOUT_MS = 3000
35VALID_NUM_DEVICES = (1, 2)
36FRONT_CAMERA_ID_PREFIX = '1'
37
38logging.getLogger('matplotlib.font_manager').disabled = True
39
40
41class ItsBaseTest(base_test.BaseTestClass):
42  """Base test for CameraITS tests.
43
44  Tests inherit from this class execute in the Camera ITS automation systems.
45  These systems consist of either:
46    1. a device under test (dut) and an external rotation controller
47    2. a device under test (dut) and one screen device(tablet)
48    3. a device under test (dut) and manual charts
49
50  Attributes:
51    dut: android_device.AndroidDevice, the device under test.
52    tablet: android_device.AndroidDevice, the tablet device used to display
53        scenes.
54  """
55
56  def setup_class(self):
57    devices = self.register_controller(android_device, min_number=1)
58    self.dut = devices[0]
59    self.camera = str(self.user_params['camera'])
60    logging.debug('Camera_id: %s', self.camera)
61    if self.user_params.get('chart_distance'):
62      self.chart_distance = float(self.user_params['chart_distance'])
63      logging.debug('Chart distance: %s cm', self.chart_distance)
64    if (self.user_params.get('lighting_cntl') and
65        self.user_params.get('lighting_ch')):
66      self.lighting_cntl = self.user_params['lighting_cntl']
67      self.lighting_ch = str(self.user_params['lighting_ch'])
68    else:
69      self.lighting_cntl = 'None'
70      self.lighting_ch = '1'
71    if (self.user_params.get('rotator_cntl') and
72        str(self.user_params.get('rotator_ch'))):
73      self.rotator_cntl = str(self.user_params['rotator_cntl'])
74      self.rotator_ch = str(self.user_params['rotator_ch'])
75    if self.user_params.get('tablet_device'):
76      self.tablet_device = self.user_params['tablet_device'] == 'True'
77    if self.user_params.get('debug_mode'):
78      self.debug_mode = self.user_params['debug_mode'] == 'True'
79    if self.user_params.get('scene'):
80      self.scene = self.user_params['scene']
81    self.parallel_execution = (
82        self.user_params.get('parallel_execution') == 'True'
83    )
84    camera_id_combo = self.parse_hidden_camera_id()
85    self.camera_id = camera_id_combo[0]
86    if len(camera_id_combo) == 2:
87      self.hidden_physical_id = camera_id_combo[1]
88    else:
89      self.hidden_physical_id = None
90
91    num_devices = len(devices)
92    if num_devices == 2:  # scenes [0,1,2,3,4,5,6]
93      try:
94        self.tablet = devices[1]
95        self.tablet_screen_brightness = self.user_params['brightness']
96        tablet_name_unencoded = self.tablet.adb.shell(
97            ['getprop', 'ro.product.device']
98        )
99        tablet_name = str(tablet_name_unencoded.decode('utf-8')).strip()
100        logging.debug('tablet name: %s', tablet_name)
101        its_session_utils.validate_tablet(
102            tablet_name, self.tablet_screen_brightness,
103            self.tablet.serial)
104      except KeyError:
105        logging.debug('Not all tablet arguments set.')
106    else:  # sensor_fusion or manual run
107      try:
108        self.fps = int(self.user_params['fps'])
109        img_size = self.user_params['img_size'].split(',')
110        self.img_w = int(img_size[0])
111        self.img_h = int(img_size[1])
112        self.test_length = float(self.user_params['test_length'])
113        self.rotator_cntl = str(self.user_params['rotator_cntl'])
114        self.rotator_ch = str(self.user_params['rotator_ch'])
115      except KeyError:
116        self.tablet = None
117        if self.scene == 'scene_ip':
118          logging.debug('Gen2 rig scene test run.')
119        else:
120          logging.debug('Not all arguments set. Manual run.')
121
122    self._setup_devices(num_devices)
123
124    if self.scene != 'scene_ip':
125      arduino_serial_port = lighting_control_utils.lighting_control(
126          self.lighting_cntl, self.lighting_ch)
127      if arduino_serial_port and self.scene != 'scene0':
128        lighting_control_utils.set_light_brightness(
129            self.lighting_ch, 255, arduino_serial_port)
130        logging.debug('Light is turned ON.')
131
132    # Check if current foldable state matches scene, if applicable
133    if self.user_params.get('foldable_device', 'False') == 'True':
134      foldable_state_unencoded = self.dut.adb.shell(
135          ['cmd', 'device_state', 'state']
136      )
137      foldable_state = str(foldable_state_unencoded.decode('utf-8')).strip()
138      is_folded = 'CLOSE' in foldable_state
139      scene_with_suffix = self.user_params.get('scene_with_suffix')
140      if scene_with_suffix:
141        if 'folded' in scene_with_suffix and not is_folded:
142          raise AssertionError(
143              f'Testing folded scene {scene_with_suffix} with unfolded device!')
144        if ('folded' not in scene_with_suffix and is_folded and
145            self.camera.startswith(FRONT_CAMERA_ID_PREFIX)):  # Not rear camera
146          raise AssertionError(
147              f'Testing unfolded scene {scene_with_suffix} with a '
148              'non-rear camera while device is folded!'
149          )
150      else:
151        logging.debug('Testing without `run_all_tests`')
152
153    cv2_version = cv2.__version__
154    logging.debug('cv2_version: %s', cv2_version)
155
156  def _setup_devices(self, num):
157    """Sets up each device in parallel if more than one device."""
158    if num not in VALID_NUM_DEVICES:
159      raise AssertionError(
160          f'Incorrect number of devices! Must be in {str(VALID_NUM_DEVICES)}')
161    if num == 1:
162      self.setup_dut(self.dut)
163    else:
164      logic = lambda d: self.setup_dut(d) if d else self.setup_tablet()
165      utils.concurrent_exec(
166          logic, [(self.dut,), (None,)],
167          max_workers=2,
168          raise_on_exception=True)
169
170  def setup_dut(self, device):
171    self.dut.adb.shell(
172        'am start -n com.android.cts.verifier/.CtsVerifierActivity')
173    logging.debug('Setting up device: %s', str(device))
174    # Wait for the app screen to appear.
175    time.sleep(WAIT_TIME_SEC)
176
177  def setup_tablet(self):
178    # KEYCODE_POWER to reset dimmer timer. KEYCODE_WAKEUP no effect if ON.
179    self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_POWER'])
180    time.sleep(TABLET_CMD_DELAY_SEC)
181    self.tablet.adb.shell(['input', 'keyevent', 'KEYCODE_WAKEUP'])
182    time.sleep(TABLET_CMD_DELAY_SEC)
183    # Dismiss keyguard
184    self.tablet.adb.shell(['wm', 'dismiss-keyguard'])
185    time.sleep(TABLET_CMD_DELAY_SEC)
186    # Turn off the adaptive brightness on tablet.
187    self.tablet.adb.shell(
188        ['settings', 'put', 'system', 'screen_brightness_mode',
189         ADAPTIVE_BRIGHTNESS_OFF])
190    # Set the screen brightness
191    self.tablet.adb.shell(
192        ['settings', 'put', 'system', 'screen_brightness',
193         str(self.tablet_screen_brightness)])
194    logging.debug('Tablet brightness set to: %s',
195                  format(self.tablet_screen_brightness))
196    self.tablet.adb.shell('settings put system screen_off_timeout {}'.format(
197        TABLET_DIMMER_TIMEOUT_MS))
198    self.set_tablet_landscape_orientation()
199    self.tablet.adb.shell('am force-stop com.google.android.apps.docs')
200    self.tablet.adb.shell('am force-stop com.google.android.apps.photos')
201    self.tablet.adb.shell('am force-stop com.android.gallery3d')
202    self.tablet.adb.shell('am force-stop com.sec.android.gallery3d')
203    self.tablet.adb.shell('am force-stop com.miui.gallery')
204    self.tablet.adb.shell(
205        'settings put global policy_control immersive.full=*')
206
207  def set_tablet_landscape_orientation(self):
208    """Sets the screen orientation to landscape.
209    """
210    # Get the landscape orientation value.
211    # This value is different for Pixel C/Huawei/Samsung tablets.
212    output = self.tablet.adb.shell('dumpsys window | grep mLandscapeRotation')
213    logging.debug('dumpsys window output: %s', output.decode('utf-8').strip())
214    output_list = str(output.decode('utf-8')).strip().split(' ')
215    for val in output_list:
216      if 'LandscapeRotation' in val:
217        landscape_val = str(val.split('=')[-1])
218        # For some tablets the values are in constant forms such as ROTATION_90
219        if 'ROTATION_90' in landscape_val:
220          landscape_val = '1'
221        elif 'ROTATION_0' in landscape_val:
222          landscape_val = '0'
223        logging.debug('Changing the orientation to landscape mode.')
224        self.tablet.adb.shell(['settings', 'put', 'system', 'user_rotation',
225                               landscape_val])
226        break
227    logging.debug('Reported tablet orientation is: %d',
228                  int(self.tablet.adb.shell(
229                      'settings get system user_rotation')))
230
231  def set_screen_brightness(self, brightness_level):
232    """Sets the screen brightness to desired level.
233
234    Args:
235       brightness_level : brightness level to set.
236    """
237    # Turn off the adaptive brightness on tablet.
238    self.tablet.adb.shell(
239        ['settings', 'put', 'system', 'screen_brightness_mode', '0'])
240    # Set the screen brightness
241    self.tablet.adb.shell([
242        'settings', 'put', 'system', 'screen_brightness',
243        brightness_level
244    ])
245    logging.debug('Tablet brightness set to: %s', brightness_level)
246    actual_brightness = self.tablet.adb.shell(
247        'settings get system screen_brightness')
248    if int(actual_brightness) != int(brightness_level):
249      raise AssertionError('Brightness was not set as expected! '
250                           f'Requested brightness: {brightness_level}, '
251                           f'Actual brightness: {actual_brightness}')
252
253  def turn_off_tablet(self):
254    """Turns off tablet, raising AssertionError if tablet is not found."""
255    if self.tablet:
256      lighting_control_utils.turn_off_device_screen(self.tablet)
257    else:
258      raise AssertionError('Test must be run with tablet.')
259
260  def parse_hidden_camera_id(self):
261    """Parse the string of camera ID into an array.
262
263    Returns:
264      Array with camera id and hidden_physical camera id.
265    """
266    camera_id_combo = self.camera.split(its_session_utils.SUB_CAMERA_SEPARATOR)
267    return camera_id_combo
268
269  def on_pass(self, record):
270    logging.debug('%s on PASS.', record.test_name)
271
272  def on_fail(self, record):
273    logging.debug('%s on FAIL.', record.test_name)
274
275  def teardown_class(self):
276    # edit root_output_path and summary_writer path
277    # to add test name to output directory
278    logging.debug('summary_writer._path: %s', self.summary_writer._path)
279    summary_head, summary_tail = os.path.split(self.summary_writer._path)
280    self.summary_writer._path = os.path.join(
281        f'{summary_head}_{self.__class__.__name__}', summary_tail)
282    os.rename(self.root_output_path,
283              f'{self.root_output_path}_{self.__class__.__name__}')
284    # print root_output_path so that it can be written to report log.
285    # Note: Do not replace print with logging.debug here.
286    print('root_output_path:',
287          f'{self.root_output_path}_{self.__class__.__name__}')
288
289
290class UiAutomatorItsBaseTest(ItsBaseTest):
291  def setup_class(self):
292    super().setup_class()
293    self.ui_app = None
294    self.dut.services.register(
295        uiautomator.ANDROID_SERVICE_NAME, uiautomator.UiAutomatorService
296    )
297
298  def setup_test(self):
299    super().setup_test()
300    if not self.ui_app:
301      raise AssertionError(
302          'UiAutomator ITS tests must specify an app for UI interaction!')
303    its_session_utils.check_apk_installed(self.dut.serial, self.ui_app)
304