xref: /aosp_15_r20/cts/apps/CameraITS/utils/its_session_utils.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2013 The Android Open Source Project
2*b7c941bbSAndroid Build Coastguard Worker#
3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*b7c941bbSAndroid Build Coastguard Worker#
7*b7c941bbSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*b7c941bbSAndroid Build Coastguard Worker#
9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License.
14*b7c941bbSAndroid Build Coastguard Worker"""Utility functions to form an ItsSession and perform various camera actions.
15*b7c941bbSAndroid Build Coastguard Worker"""
16*b7c941bbSAndroid Build Coastguard Worker
17*b7c941bbSAndroid Build Coastguard Worker
18*b7c941bbSAndroid Build Coastguard Workerimport collections
19*b7c941bbSAndroid Build Coastguard Workerimport fnmatch
20*b7c941bbSAndroid Build Coastguard Workerimport glob
21*b7c941bbSAndroid Build Coastguard Workerimport json
22*b7c941bbSAndroid Build Coastguard Workerimport logging
23*b7c941bbSAndroid Build Coastguard Workerimport math
24*b7c941bbSAndroid Build Coastguard Workerimport os
25*b7c941bbSAndroid Build Coastguard Workerimport socket
26*b7c941bbSAndroid Build Coastguard Workerimport subprocess
27*b7c941bbSAndroid Build Coastguard Workerimport sys
28*b7c941bbSAndroid Build Coastguard Workerimport time
29*b7c941bbSAndroid Build Coastguard Workerimport types
30*b7c941bbSAndroid Build Coastguard Workerimport unicodedata
31*b7c941bbSAndroid Build Coastguard Worker
32*b7c941bbSAndroid Build Coastguard Workerfrom mobly.controllers.android_device_lib import adb
33*b7c941bbSAndroid Build Coastguard Workerimport numpy
34*b7c941bbSAndroid Build Coastguard Worker
35*b7c941bbSAndroid Build Coastguard Workerimport camera_properties_utils
36*b7c941bbSAndroid Build Coastguard Workerimport capture_request_utils
37*b7c941bbSAndroid Build Coastguard Workerimport error_util
38*b7c941bbSAndroid Build Coastguard Workerimport image_processing_utils
39*b7c941bbSAndroid Build Coastguard Workerimport its_device_utils
40*b7c941bbSAndroid Build Coastguard Workerimport opencv_processing_utils
41*b7c941bbSAndroid Build Coastguard Workerimport ui_interaction_utils
42*b7c941bbSAndroid Build Coastguard Worker
43*b7c941bbSAndroid Build Coastguard WorkerANDROID13_API_LEVEL = 33
44*b7c941bbSAndroid Build Coastguard WorkerANDROID14_API_LEVEL = 34
45*b7c941bbSAndroid Build Coastguard WorkerANDROID15_API_LEVEL = 35
46*b7c941bbSAndroid Build Coastguard WorkerANDROID16_API_LEVEL = 36
47*b7c941bbSAndroid Build Coastguard WorkerCHART_DISTANCE_NO_SCALING = 0
48*b7c941bbSAndroid Build Coastguard WorkerIMAGE_FORMAT_JPEG = 256
49*b7c941bbSAndroid Build Coastguard WorkerIMAGE_FORMAT_YUV_420_888 = 35
50*b7c941bbSAndroid Build Coastguard WorkerJCA_CAPTURE_PATH_TAG = 'JCA_CAPTURE_PATH'
51*b7c941bbSAndroid Build Coastguard WorkerJCA_CAPTURE_STATUS_TAG = 'JCA_CAPTURE_STATUS'
52*b7c941bbSAndroid Build Coastguard WorkerLOAD_SCENE_DELAY_SEC = 3
53*b7c941bbSAndroid Build Coastguard WorkerPREVIEW_MAX_TESTED_AREA = 1920 * 1440
54*b7c941bbSAndroid Build Coastguard WorkerPREVIEW_MIN_TESTED_AREA = 320 * 240
55*b7c941bbSAndroid Build Coastguard WorkerPRIVATE_FORMAT = 'priv'
56*b7c941bbSAndroid Build Coastguard WorkerJPEG_R_FMT_STR = 'jpeg_r'
57*b7c941bbSAndroid Build Coastguard WorkerSCALING_TO_FILE_ATOL = 0.01
58*b7c941bbSAndroid Build Coastguard WorkerSINGLE_CAPTURE_NCAP = 1
59*b7c941bbSAndroid Build Coastguard WorkerSUB_CAMERA_SEPARATOR = '.'
60*b7c941bbSAndroid Build Coastguard Worker# pylint: disable=line-too-long
61*b7c941bbSAndroid Build Coastguard Worker# Allowed tablets as listed on https://source.android.com/docs/compatibility/cts/camera-its-box#tablet-requirements
62*b7c941bbSAndroid Build Coastguard Worker# List entries must be entered in lowercase
63*b7c941bbSAndroid Build Coastguard WorkerTABLET_ALLOWLIST = (
64*b7c941bbSAndroid Build Coastguard Worker    'dragon',  # Google Pixel C
65*b7c941bbSAndroid Build Coastguard Worker    'hnhey-q',  # Honor Pad 8
66*b7c941bbSAndroid Build Coastguard Worker    'hwcmr09',  # Huawei MediaPad M5
67*b7c941bbSAndroid Build Coastguard Worker    'x306f',  # Lenovo Tab M10 HD (Gen 2)
68*b7c941bbSAndroid Build Coastguard Worker    'x606f',  # Lenovo Tab M10 Plus
69*b7c941bbSAndroid Build Coastguard Worker    'j606f',  # Lenovo Tab P11
70*b7c941bbSAndroid Build Coastguard Worker    'tb350fu',  # Lenovo Tab P11 (Gen 2)
71*b7c941bbSAndroid Build Coastguard Worker    'agta',  # Nokia T21
72*b7c941bbSAndroid Build Coastguard Worker    'gta4lwifi',  # Samsung Galaxy Tab A7
73*b7c941bbSAndroid Build Coastguard Worker    'gta8wifi',  # Samsung Galaxy Tab A8
74*b7c941bbSAndroid Build Coastguard Worker    'gta8',  # Samsung Galaxy Tab A8 LTE
75*b7c941bbSAndroid Build Coastguard Worker    'gta9pwifi',  # Samsung Galaxy Tab A9+
76*b7c941bbSAndroid Build Coastguard Worker    'gta9p',  # Samsung Galaxy Tab A9+ 5G
77*b7c941bbSAndroid Build Coastguard Worker    'dpd2221',  # Vivo Pad2
78*b7c941bbSAndroid Build Coastguard Worker    'nabu',  # Xiaomi Pad 5
79*b7c941bbSAndroid Build Coastguard Worker    'nabu_tw',  # Xiaomi Pad 5
80*b7c941bbSAndroid Build Coastguard Worker    'xun',  # Xiaomi Redmi Pad SE
81*b7c941bbSAndroid Build Coastguard Worker    'yunluo',  # Xiaomi Redmi Pad
82*b7c941bbSAndroid Build Coastguard Worker)
83*b7c941bbSAndroid Build Coastguard WorkerTABLET_DEFAULT_BRIGHTNESS = 192  # 8-bit tablet 75% brightness
84*b7c941bbSAndroid Build Coastguard WorkerTABLET_LEGACY_BRIGHTNESS = 96
85*b7c941bbSAndroid Build Coastguard WorkerTABLET_LEGACY_NAME = 'dragon'
86*b7c941bbSAndroid Build Coastguard Worker# List entries must be entered in lowercase
87*b7c941bbSAndroid Build Coastguard WorkerTABLET_OS_VERSION = types.MappingProxyType({
88*b7c941bbSAndroid Build Coastguard Worker    'nabu': ANDROID13_API_LEVEL,
89*b7c941bbSAndroid Build Coastguard Worker    'nabu_tw': ANDROID13_API_LEVEL,
90*b7c941bbSAndroid Build Coastguard Worker    'yunluo': ANDROID14_API_LEVEL
91*b7c941bbSAndroid Build Coastguard Worker    })
92*b7c941bbSAndroid Build Coastguard WorkerTABLET_REQUIREMENTS_URL = 'https://source.android.com/docs/compatibility/cts/camera-its-box#tablet-allowlist'
93*b7c941bbSAndroid Build Coastguard WorkerTABLET_BRIGHTNESS_ERROR_MSG = ('Tablet brightness not set as per '
94*b7c941bbSAndroid Build Coastguard Worker                               f'{TABLET_REQUIREMENTS_URL} in the config file')
95*b7c941bbSAndroid Build Coastguard WorkerTABLET_NOT_ALLOWED_ERROR_MSG = ('Tablet model or tablet Android version is '
96*b7c941bbSAndroid Build Coastguard Worker                                'not on our allowlist, please refer to '
97*b7c941bbSAndroid Build Coastguard Worker                                f'{TABLET_REQUIREMENTS_URL}')
98*b7c941bbSAndroid Build Coastguard WorkerTAP_COORDINATES = (500, 500)  # Location to tap tablet screen via adb
99*b7c941bbSAndroid Build Coastguard WorkerUSE_CASE_CROPPED_RAW = 6
100*b7c941bbSAndroid Build Coastguard WorkerVIDEO_SCENES = ('scene_video',)
101*b7c941bbSAndroid Build Coastguard WorkerNOT_YET_MANDATED_MESSAGE = 'Not yet mandated test'
102*b7c941bbSAndroid Build Coastguard WorkerRESULT_OK_STATUS = '-1'
103*b7c941bbSAndroid Build Coastguard Worker
104*b7c941bbSAndroid Build Coastguard Worker_FLASH_MODE_OFF = 0
105*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_PATCH_H = 0.05
106*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_PATCH_W = 0.05
107*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_REGIONS = {
108*b7c941bbSAndroid Build Coastguard Worker    'top-left': (0, 0),
109*b7c941bbSAndroid Build Coastguard Worker    'top-right': (0, 1-_VALIDATE_LIGHTING_PATCH_H),
110*b7c941bbSAndroid Build Coastguard Worker    'bottom-left': (1-_VALIDATE_LIGHTING_PATCH_W, 0),
111*b7c941bbSAndroid Build Coastguard Worker    'bottom-right': (1-_VALIDATE_LIGHTING_PATCH_W,
112*b7c941bbSAndroid Build Coastguard Worker                     1-_VALIDATE_LIGHTING_PATCH_H),
113*b7c941bbSAndroid Build Coastguard Worker}
114*b7c941bbSAndroid Build Coastguard Worker_MODULAR_MACRO_OFFSET = 0.35  # Determined empirically from modular rig testing
115*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_REGIONS_MODULAR_UW = {
116*b7c941bbSAndroid Build Coastguard Worker    'top-left': (_MODULAR_MACRO_OFFSET, _MODULAR_MACRO_OFFSET),
117*b7c941bbSAndroid Build Coastguard Worker    'bottom-left': (_MODULAR_MACRO_OFFSET,
118*b7c941bbSAndroid Build Coastguard Worker                    1-_MODULAR_MACRO_OFFSET-_VALIDATE_LIGHTING_PATCH_H),
119*b7c941bbSAndroid Build Coastguard Worker    'top-right': (1-_MODULAR_MACRO_OFFSET-_VALIDATE_LIGHTING_PATCH_W,
120*b7c941bbSAndroid Build Coastguard Worker                  _MODULAR_MACRO_OFFSET),
121*b7c941bbSAndroid Build Coastguard Worker    'bottom-right': (1-_MODULAR_MACRO_OFFSET-_VALIDATE_LIGHTING_PATCH_W,
122*b7c941bbSAndroid Build Coastguard Worker                     1-_MODULAR_MACRO_OFFSET-_VALIDATE_LIGHTING_PATCH_H),
123*b7c941bbSAndroid Build Coastguard Worker}
124*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_MACRO_FOV_THRESH = 110
125*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_THRESH = 0.05  # Determined empirically from scene[1:6] tests
126*b7c941bbSAndroid Build Coastguard Worker_VALIDATE_LIGHTING_THRESH_DARK = 0.3  # Determined empirically for night test
127*b7c941bbSAndroid Build Coastguard Worker_CMD_NAME_STR = 'cmdName'
128*b7c941bbSAndroid Build Coastguard Worker_OBJ_VALUE_STR = 'objValue'
129*b7c941bbSAndroid Build Coastguard Worker_STR_VALUE_STR = 'strValue'
130*b7c941bbSAndroid Build Coastguard Worker_TAG_STR = 'tag'
131*b7c941bbSAndroid Build Coastguard Worker_CAMERA_ID_STR = 'cameraId'
132*b7c941bbSAndroid Build Coastguard Worker_EXTRA_TIMEOUT_FACTOR = 10
133*b7c941bbSAndroid Build Coastguard Worker_COPY_SCENE_DELAY_SEC = 1
134*b7c941bbSAndroid Build Coastguard Worker_DST_SCENE_DIR = '/sdcard/Download/'
135*b7c941bbSAndroid Build Coastguard Worker_BIT_HLG10 = 0x01  # bit 1 for feature mask
136*b7c941bbSAndroid Build Coastguard Worker_BIT_STABILIZATION = 0x02  # bit 2 for feature mask
137*b7c941bbSAndroid Build Coastguard Worker
138*b7c941bbSAndroid Build Coastguard Worker
139*b7c941bbSAndroid Build Coastguard Workerdef validate_tablet(tablet_name, brightness, device_id):
140*b7c941bbSAndroid Build Coastguard Worker  """Ensures tablet brightness is set according to documentation.
141*b7c941bbSAndroid Build Coastguard Worker
142*b7c941bbSAndroid Build Coastguard Worker  https://source.android.com/docs/compatibility/cts/camera-its-box#tablet-allowlist
143*b7c941bbSAndroid Build Coastguard Worker  Args:
144*b7c941bbSAndroid Build Coastguard Worker    tablet_name: tablet product name specified by `ro.product.device`.
145*b7c941bbSAndroid Build Coastguard Worker    brightness: brightness specified by config file.
146*b7c941bbSAndroid Build Coastguard Worker    device_id: str; ID of the device.
147*b7c941bbSAndroid Build Coastguard Worker  """
148*b7c941bbSAndroid Build Coastguard Worker  tablet_name = tablet_name.lower()
149*b7c941bbSAndroid Build Coastguard Worker  if tablet_name not in TABLET_ALLOWLIST:
150*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(
151*b7c941bbSAndroid Build Coastguard Worker        f'Tablet product name: {tablet_name}. {TABLET_NOT_ALLOWED_ERROR_MSG}'
152*b7c941bbSAndroid Build Coastguard Worker    )
153*b7c941bbSAndroid Build Coastguard Worker  if tablet_name in TABLET_OS_VERSION:
154*b7c941bbSAndroid Build Coastguard Worker    if (device_sdk := get_build_sdk_version(
155*b7c941bbSAndroid Build Coastguard Worker        device_id)) < TABLET_OS_VERSION[tablet_name]:
156*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError(
157*b7c941bbSAndroid Build Coastguard Worker          f' Tablet product name: {tablet_name}. '
158*b7c941bbSAndroid Build Coastguard Worker          f'Android version: {device_sdk}. {TABLET_NOT_ALLOWED_ERROR_MSG}'
159*b7c941bbSAndroid Build Coastguard Worker      )
160*b7c941bbSAndroid Build Coastguard Worker  name_to_brightness = {
161*b7c941bbSAndroid Build Coastguard Worker      TABLET_LEGACY_NAME: TABLET_LEGACY_BRIGHTNESS,
162*b7c941bbSAndroid Build Coastguard Worker  }
163*b7c941bbSAndroid Build Coastguard Worker  if tablet_name in name_to_brightness:
164*b7c941bbSAndroid Build Coastguard Worker    if brightness != name_to_brightness[tablet_name]:
165*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError(TABLET_BRIGHTNESS_ERROR_MSG)
166*b7c941bbSAndroid Build Coastguard Worker  else:
167*b7c941bbSAndroid Build Coastguard Worker    if brightness != TABLET_DEFAULT_BRIGHTNESS:
168*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError(TABLET_BRIGHTNESS_ERROR_MSG)
169*b7c941bbSAndroid Build Coastguard Worker
170*b7c941bbSAndroid Build Coastguard Worker
171*b7c941bbSAndroid Build Coastguard Workerdef check_apk_installed(device_id, package_name):
172*b7c941bbSAndroid Build Coastguard Worker  """Verifies that an APK is installed on a given device.
173*b7c941bbSAndroid Build Coastguard Worker
174*b7c941bbSAndroid Build Coastguard Worker  Args:
175*b7c941bbSAndroid Build Coastguard Worker    device_id: str; ID of the device.
176*b7c941bbSAndroid Build Coastguard Worker    package_name: str; name of the package that should be installed.
177*b7c941bbSAndroid Build Coastguard Worker  """
178*b7c941bbSAndroid Build Coastguard Worker  verify_cts_cmd = (
179*b7c941bbSAndroid Build Coastguard Worker      f'adb -s {device_id} shell pm list packages | '
180*b7c941bbSAndroid Build Coastguard Worker      f'grep {package_name}'
181*b7c941bbSAndroid Build Coastguard Worker  )
182*b7c941bbSAndroid Build Coastguard Worker  bytes_output = subprocess.check_output(
183*b7c941bbSAndroid Build Coastguard Worker      verify_cts_cmd, stderr=subprocess.STDOUT, shell=True
184*b7c941bbSAndroid Build Coastguard Worker  )
185*b7c941bbSAndroid Build Coastguard Worker  output = str(bytes_output.decode('utf-8')).strip()
186*b7c941bbSAndroid Build Coastguard Worker  if package_name not in output:
187*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(
188*b7c941bbSAndroid Build Coastguard Worker        f'{package_name} not installed on device {device_id}!'
189*b7c941bbSAndroid Build Coastguard Worker    )
190*b7c941bbSAndroid Build Coastguard Worker
191*b7c941bbSAndroid Build Coastguard Worker
192*b7c941bbSAndroid Build Coastguard Workerdef get_array_size(buffer):
193*b7c941bbSAndroid Build Coastguard Worker  """Get array size based on different NumPy versions' functions.
194*b7c941bbSAndroid Build Coastguard Worker
195*b7c941bbSAndroid Build Coastguard Worker  Args:
196*b7c941bbSAndroid Build Coastguard Worker    buffer: A NumPy array.
197*b7c941bbSAndroid Build Coastguard Worker
198*b7c941bbSAndroid Build Coastguard Worker  Returns:
199*b7c941bbSAndroid Build Coastguard Worker    buffer_size: The size of the buffer.
200*b7c941bbSAndroid Build Coastguard Worker  """
201*b7c941bbSAndroid Build Coastguard Worker  np_version = numpy.__version__
202*b7c941bbSAndroid Build Coastguard Worker  if np_version.startswith(('1.25', '1.26', '2.')):
203*b7c941bbSAndroid Build Coastguard Worker    buffer_size = numpy.prod(buffer.shape)
204*b7c941bbSAndroid Build Coastguard Worker  else:
205*b7c941bbSAndroid Build Coastguard Worker    buffer_size = numpy.product(buffer.shape)
206*b7c941bbSAndroid Build Coastguard Worker  return buffer_size
207*b7c941bbSAndroid Build Coastguard Worker
208*b7c941bbSAndroid Build Coastguard Worker
209*b7c941bbSAndroid Build Coastguard Workerclass ItsSession(object):
210*b7c941bbSAndroid Build Coastguard Worker  """Controls a device over adb to run ITS scripts.
211*b7c941bbSAndroid Build Coastguard Worker
212*b7c941bbSAndroid Build Coastguard Worker    The script importing this module (on the host machine) prepares JSON
213*b7c941bbSAndroid Build Coastguard Worker    objects encoding CaptureRequests, specifying sets of parameters to use
214*b7c941bbSAndroid Build Coastguard Worker    when capturing an image using the Camera2 APIs. This class encapsulates
215*b7c941bbSAndroid Build Coastguard Worker    sending the requests to the device, monitoring the device's progress, and
216*b7c941bbSAndroid Build Coastguard Worker    copying the resultant captures back to the host machine when done. TCP
217*b7c941bbSAndroid Build Coastguard Worker    forwarded over adb is the transport mechanism used.
218*b7c941bbSAndroid Build Coastguard Worker
219*b7c941bbSAndroid Build Coastguard Worker    The device must have CtsVerifier.apk installed.
220*b7c941bbSAndroid Build Coastguard Worker
221*b7c941bbSAndroid Build Coastguard Worker    Attributes:
222*b7c941bbSAndroid Build Coastguard Worker        sock: The open socket.
223*b7c941bbSAndroid Build Coastguard Worker  """
224*b7c941bbSAndroid Build Coastguard Worker
225*b7c941bbSAndroid Build Coastguard Worker  # Open a connection to localhost:<host_port>, forwarded to port 6000 on the
226*b7c941bbSAndroid Build Coastguard Worker  # device. <host_port> is determined at run-time to support multiple
227*b7c941bbSAndroid Build Coastguard Worker  # connected devices.
228*b7c941bbSAndroid Build Coastguard Worker  IPADDR = '127.0.0.1'
229*b7c941bbSAndroid Build Coastguard Worker  REMOTE_PORT = 6000
230*b7c941bbSAndroid Build Coastguard Worker  BUFFER_SIZE = 4096
231*b7c941bbSAndroid Build Coastguard Worker
232*b7c941bbSAndroid Build Coastguard Worker  # LOCK_PORT is used as a mutex lock to protect the list of forwarded ports
233*b7c941bbSAndroid Build Coastguard Worker  # among all processes. The script assumes LOCK_PORT is available and will
234*b7c941bbSAndroid Build Coastguard Worker  # try to use ports between CLIENT_PORT_START and
235*b7c941bbSAndroid Build Coastguard Worker  # CLIENT_PORT_START+MAX_NUM_PORTS-1 on host for ITS sessions.
236*b7c941bbSAndroid Build Coastguard Worker  CLIENT_PORT_START = 6000
237*b7c941bbSAndroid Build Coastguard Worker  MAX_NUM_PORTS = 100
238*b7c941bbSAndroid Build Coastguard Worker  LOCK_PORT = CLIENT_PORT_START + MAX_NUM_PORTS
239*b7c941bbSAndroid Build Coastguard Worker
240*b7c941bbSAndroid Build Coastguard Worker  # Seconds timeout on each socket operation.
241*b7c941bbSAndroid Build Coastguard Worker  SOCK_TIMEOUT = 20.0
242*b7c941bbSAndroid Build Coastguard Worker  # Seconds timeout on performance measurement socket operation
243*b7c941bbSAndroid Build Coastguard Worker  SOCK_TIMEOUT_FOR_PERF_MEASURE = 40.0
244*b7c941bbSAndroid Build Coastguard Worker  # Seconds timeout on preview recording socket operation.
245*b7c941bbSAndroid Build Coastguard Worker  SOCK_TIMEOUT_PREVIEW = 30.0  # test_imu_drift is 30s
246*b7c941bbSAndroid Build Coastguard Worker
247*b7c941bbSAndroid Build Coastguard Worker  # Additional timeout in seconds when ITS service is doing more complicated
248*b7c941bbSAndroid Build Coastguard Worker  # operations, for example: issuing warmup requests before actual capture.
249*b7c941bbSAndroid Build Coastguard Worker  EXTRA_SOCK_TIMEOUT = 5.0
250*b7c941bbSAndroid Build Coastguard Worker
251*b7c941bbSAndroid Build Coastguard Worker  PACKAGE = 'com.android.cts.verifier.camera.its'
252*b7c941bbSAndroid Build Coastguard Worker  INTENT_START = 'com.android.cts.verifier.camera.its.START'
253*b7c941bbSAndroid Build Coastguard Worker
254*b7c941bbSAndroid Build Coastguard Worker  # This string must be in sync with ItsService. Updated when interface
255*b7c941bbSAndroid Build Coastguard Worker  # between script and ItsService is changed.
256*b7c941bbSAndroid Build Coastguard Worker  ITS_SERVICE_VERSION = '1.0'
257*b7c941bbSAndroid Build Coastguard Worker
258*b7c941bbSAndroid Build Coastguard Worker  SEC_TO_NSEC = 1000*1000*1000.0
259*b7c941bbSAndroid Build Coastguard Worker  adb = 'adb -d'
260*b7c941bbSAndroid Build Coastguard Worker
261*b7c941bbSAndroid Build Coastguard Worker  # Predefine camera props. Save props extracted from the function,
262*b7c941bbSAndroid Build Coastguard Worker  # "get_camera_properties".
263*b7c941bbSAndroid Build Coastguard Worker  props = None
264*b7c941bbSAndroid Build Coastguard Worker
265*b7c941bbSAndroid Build Coastguard Worker  IMAGE_FORMAT_LIST_1 = [
266*b7c941bbSAndroid Build Coastguard Worker      'jpegImage', 'rawImage', 'raw10Image', 'raw12Image', 'rawStatsImage',
267*b7c941bbSAndroid Build Coastguard Worker      'dngImage', 'y8Image', 'jpeg_rImage',
268*b7c941bbSAndroid Build Coastguard Worker      'rawQuadBayerImage', 'rawQuadBayerStatsImage',
269*b7c941bbSAndroid Build Coastguard Worker      'raw10StatsImage', 'raw10QuadBayerStatsImage', 'raw10QuadBayerImage'
270*b7c941bbSAndroid Build Coastguard Worker  ]
271*b7c941bbSAndroid Build Coastguard Worker
272*b7c941bbSAndroid Build Coastguard Worker  IMAGE_FORMAT_LIST_2 = [
273*b7c941bbSAndroid Build Coastguard Worker      'jpegImage', 'rawImage', 'raw10Image', 'raw12Image', 'rawStatsImage',
274*b7c941bbSAndroid Build Coastguard Worker      'yuvImage', 'jpeg_rImage',
275*b7c941bbSAndroid Build Coastguard Worker      'rawQuadBayerImage', 'rawQuadBayerStatsImage',
276*b7c941bbSAndroid Build Coastguard Worker      'raw10StatsImage', 'raw10QuadBayerStatsImage', 'raw10QuadBayerImage'
277*b7c941bbSAndroid Build Coastguard Worker  ]
278*b7c941bbSAndroid Build Coastguard Worker
279*b7c941bbSAndroid Build Coastguard Worker  CAP_JPEG = {'format': 'jpeg'}
280*b7c941bbSAndroid Build Coastguard Worker  CAP_RAW = {'format': 'raw'}
281*b7c941bbSAndroid Build Coastguard Worker  CAP_CROPPED_RAW = {'format': 'raw', 'useCase': USE_CASE_CROPPED_RAW}
282*b7c941bbSAndroid Build Coastguard Worker  CAP_YUV = {'format': 'yuv'}
283*b7c941bbSAndroid Build Coastguard Worker  CAP_RAW_YUV = [{'format': 'raw'}, {'format': 'yuv'}]
284*b7c941bbSAndroid Build Coastguard Worker
285*b7c941bbSAndroid Build Coastguard Worker  def __init_socket_port(self):
286*b7c941bbSAndroid Build Coastguard Worker    """Initialize the socket port for the host to forward requests to the device.
287*b7c941bbSAndroid Build Coastguard Worker
288*b7c941bbSAndroid Build Coastguard Worker    This method assumes localhost's LOCK_PORT is available and will try to
289*b7c941bbSAndroid Build Coastguard Worker    use ports between CLIENT_PORT_START and CLIENT_PORT_START+MAX_NUM_PORTS-1
290*b7c941bbSAndroid Build Coastguard Worker    """
291*b7c941bbSAndroid Build Coastguard Worker    num_retries = 100
292*b7c941bbSAndroid Build Coastguard Worker    retry_wait_time_sec = 0.05
293*b7c941bbSAndroid Build Coastguard Worker
294*b7c941bbSAndroid Build Coastguard Worker    # Bind a socket to use as mutex lock
295*b7c941bbSAndroid Build Coastguard Worker    socket_lock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
296*b7c941bbSAndroid Build Coastguard Worker    for i in range(num_retries):
297*b7c941bbSAndroid Build Coastguard Worker      try:
298*b7c941bbSAndroid Build Coastguard Worker        socket_lock.bind((ItsSession.IPADDR, ItsSession.LOCK_PORT))
299*b7c941bbSAndroid Build Coastguard Worker        break
300*b7c941bbSAndroid Build Coastguard Worker      except (socket.error, socket.timeout) as socket_issue:
301*b7c941bbSAndroid Build Coastguard Worker        if i == num_retries - 1:
302*b7c941bbSAndroid Build Coastguard Worker          raise error_util.CameraItsError(
303*b7c941bbSAndroid Build Coastguard Worker              self._device_id, 'socket lock returns error') from socket_issue
304*b7c941bbSAndroid Build Coastguard Worker        else:
305*b7c941bbSAndroid Build Coastguard Worker          time.sleep(retry_wait_time_sec)
306*b7c941bbSAndroid Build Coastguard Worker
307*b7c941bbSAndroid Build Coastguard Worker    # Check if a port is already assigned to the device.
308*b7c941bbSAndroid Build Coastguard Worker    command = 'adb forward --list'
309*b7c941bbSAndroid Build Coastguard Worker    proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
310*b7c941bbSAndroid Build Coastguard Worker    # pylint: disable=unused-variable
311*b7c941bbSAndroid Build Coastguard Worker    output, error = proc.communicate()
312*b7c941bbSAndroid Build Coastguard Worker    port = None
313*b7c941bbSAndroid Build Coastguard Worker    used_ports = []
314*b7c941bbSAndroid Build Coastguard Worker    for line  in output.decode('utf-8').split(os.linesep):
315*b7c941bbSAndroid Build Coastguard Worker      # each line should be formatted as:
316*b7c941bbSAndroid Build Coastguard Worker      # "<device_id> tcp:<host_port> tcp:<remote_port>"
317*b7c941bbSAndroid Build Coastguard Worker      forward_info = line.split()
318*b7c941bbSAndroid Build Coastguard Worker      if len(forward_info) >= 3 and len(
319*b7c941bbSAndroid Build Coastguard Worker          forward_info[1]) > 4 and forward_info[1][:4] == 'tcp:' and len(
320*b7c941bbSAndroid Build Coastguard Worker              forward_info[2]) > 4 and forward_info[2][:4] == 'tcp:':
321*b7c941bbSAndroid Build Coastguard Worker        local_p = int(forward_info[1][4:])
322*b7c941bbSAndroid Build Coastguard Worker        remote_p = int(forward_info[2][4:])
323*b7c941bbSAndroid Build Coastguard Worker        if forward_info[
324*b7c941bbSAndroid Build Coastguard Worker            0] == self._device_id and remote_p == ItsSession.REMOTE_PORT:
325*b7c941bbSAndroid Build Coastguard Worker          port = local_p
326*b7c941bbSAndroid Build Coastguard Worker          break
327*b7c941bbSAndroid Build Coastguard Worker        else:
328*b7c941bbSAndroid Build Coastguard Worker          used_ports.append(local_p)
329*b7c941bbSAndroid Build Coastguard Worker
330*b7c941bbSAndroid Build Coastguard Worker      # Find the first available port if no port is assigned to the device.
331*b7c941bbSAndroid Build Coastguard Worker    if port is None:
332*b7c941bbSAndroid Build Coastguard Worker      for p in range(ItsSession.CLIENT_PORT_START,
333*b7c941bbSAndroid Build Coastguard Worker                     ItsSession.CLIENT_PORT_START + ItsSession.MAX_NUM_PORTS):
334*b7c941bbSAndroid Build Coastguard Worker        if self.check_port_availability(p, used_ports):
335*b7c941bbSAndroid Build Coastguard Worker          port = p
336*b7c941bbSAndroid Build Coastguard Worker          break
337*b7c941bbSAndroid Build Coastguard Worker
338*b7c941bbSAndroid Build Coastguard Worker    if port is None:
339*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError(self._device_id,
340*b7c941bbSAndroid Build Coastguard Worker                                      ' cannot find an available ' + 'port')
341*b7c941bbSAndroid Build Coastguard Worker
342*b7c941bbSAndroid Build Coastguard Worker    # Release the socket as mutex unlock
343*b7c941bbSAndroid Build Coastguard Worker    socket_lock.close()
344*b7c941bbSAndroid Build Coastguard Worker
345*b7c941bbSAndroid Build Coastguard Worker    # Connect to the socket
346*b7c941bbSAndroid Build Coastguard Worker    self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
347*b7c941bbSAndroid Build Coastguard Worker    self.sock.connect((self.IPADDR, port))
348*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(self.SOCK_TIMEOUT)
349*b7c941bbSAndroid Build Coastguard Worker
350*b7c941bbSAndroid Build Coastguard Worker  def check_port_availability(self, check_port, used_ports):
351*b7c941bbSAndroid Build Coastguard Worker    """Check if the port is available or not.
352*b7c941bbSAndroid Build Coastguard Worker
353*b7c941bbSAndroid Build Coastguard Worker    Args:
354*b7c941bbSAndroid Build Coastguard Worker      check_port: Port to check for availability
355*b7c941bbSAndroid Build Coastguard Worker      used_ports: List of used ports
356*b7c941bbSAndroid Build Coastguard Worker
357*b7c941bbSAndroid Build Coastguard Worker    Returns:
358*b7c941bbSAndroid Build Coastguard Worker     True if the given port is available and can be assigned to the device.
359*b7c941bbSAndroid Build Coastguard Worker    """
360*b7c941bbSAndroid Build Coastguard Worker    if check_port not in used_ports:
361*b7c941bbSAndroid Build Coastguard Worker      # Try to run "adb forward" with the port
362*b7c941bbSAndroid Build Coastguard Worker      command = ('%s forward tcp:%d tcp:%d' %
363*b7c941bbSAndroid Build Coastguard Worker                 (self.adb, check_port, self.REMOTE_PORT))
364*b7c941bbSAndroid Build Coastguard Worker      proc = subprocess.Popen(
365*b7c941bbSAndroid Build Coastguard Worker          command.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE)
366*b7c941bbSAndroid Build Coastguard Worker      error = proc.communicate()[1]
367*b7c941bbSAndroid Build Coastguard Worker
368*b7c941bbSAndroid Build Coastguard Worker      # Check if there is no error
369*b7c941bbSAndroid Build Coastguard Worker      if error is None or error.find('error'.encode()) < 0:
370*b7c941bbSAndroid Build Coastguard Worker        return True
371*b7c941bbSAndroid Build Coastguard Worker      else:
372*b7c941bbSAndroid Build Coastguard Worker        return False
373*b7c941bbSAndroid Build Coastguard Worker
374*b7c941bbSAndroid Build Coastguard Worker  def __wait_for_service(self):
375*b7c941bbSAndroid Build Coastguard Worker    """Wait for ItsService to be ready and reboot the device if needed.
376*b7c941bbSAndroid Build Coastguard Worker
377*b7c941bbSAndroid Build Coastguard Worker    This also includes the optional reboot handling: if the user
378*b7c941bbSAndroid Build Coastguard Worker    provides a "reboot" or "reboot=N" arg, then reboot the device,
379*b7c941bbSAndroid Build Coastguard Worker    waiting for N seconds (default 30) before returning.
380*b7c941bbSAndroid Build Coastguard Worker    """
381*b7c941bbSAndroid Build Coastguard Worker
382*b7c941bbSAndroid Build Coastguard Worker    for s in sys.argv[1:]:
383*b7c941bbSAndroid Build Coastguard Worker      if s[:6] == 'reboot':
384*b7c941bbSAndroid Build Coastguard Worker        duration = 30
385*b7c941bbSAndroid Build Coastguard Worker        if len(s) > 7 and s[6] == '=':
386*b7c941bbSAndroid Build Coastguard Worker          duration = int(s[7:])
387*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Rebooting device')
388*b7c941bbSAndroid Build Coastguard Worker        its_device_utils.run(f'{self.adb} reboot')
389*b7c941bbSAndroid Build Coastguard Worker        its_device_utils.run(f'{self.adb} wait-for-device')
390*b7c941bbSAndroid Build Coastguard Worker        time.sleep(duration)
391*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Reboot complete')
392*b7c941bbSAndroid Build Coastguard Worker
393*b7c941bbSAndroid Build Coastguard Worker    # Flush logcat so following code won't be misled by previous
394*b7c941bbSAndroid Build Coastguard Worker    # 'ItsService ready' log.
395*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run(f'{self.adb} logcat -c')
396*b7c941bbSAndroid Build Coastguard Worker    time.sleep(1)
397*b7c941bbSAndroid Build Coastguard Worker
398*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run(
399*b7c941bbSAndroid Build Coastguard Worker        f'{self.adb} shell am force-stop --user cur {self.PACKAGE}')
400*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run(
401*b7c941bbSAndroid Build Coastguard Worker        f'{self.adb} shell am start-foreground-service --user cur '
402*b7c941bbSAndroid Build Coastguard Worker        f'-t text/plain -a {self.INTENT_START}'
403*b7c941bbSAndroid Build Coastguard Worker    )
404*b7c941bbSAndroid Build Coastguard Worker
405*b7c941bbSAndroid Build Coastguard Worker    # Wait until the socket is ready to accept a connection.
406*b7c941bbSAndroid Build Coastguard Worker    proc = subprocess.Popen(
407*b7c941bbSAndroid Build Coastguard Worker        self.adb.split() + ['logcat'], stdout=subprocess.PIPE)
408*b7c941bbSAndroid Build Coastguard Worker    logcat = proc.stdout
409*b7c941bbSAndroid Build Coastguard Worker    while True:
410*b7c941bbSAndroid Build Coastguard Worker      line = logcat.readline().strip()
411*b7c941bbSAndroid Build Coastguard Worker      if line.find(b'ItsService ready') >= 0:
412*b7c941bbSAndroid Build Coastguard Worker        break
413*b7c941bbSAndroid Build Coastguard Worker    proc.kill()
414*b7c941bbSAndroid Build Coastguard Worker    proc.communicate()
415*b7c941bbSAndroid Build Coastguard Worker
416*b7c941bbSAndroid Build Coastguard Worker  def __init__(self, device_id=None, camera_id=None, hidden_physical_id=None,
417*b7c941bbSAndroid Build Coastguard Worker               override_to_portrait=None):
418*b7c941bbSAndroid Build Coastguard Worker    self._camera_id = camera_id
419*b7c941bbSAndroid Build Coastguard Worker    self._device_id = device_id
420*b7c941bbSAndroid Build Coastguard Worker    self._hidden_physical_id = hidden_physical_id
421*b7c941bbSAndroid Build Coastguard Worker    self._override_to_portrait = override_to_portrait
422*b7c941bbSAndroid Build Coastguard Worker
423*b7c941bbSAndroid Build Coastguard Worker    # Initialize device id and adb command.
424*b7c941bbSAndroid Build Coastguard Worker    self.adb = 'adb -s ' + self._device_id
425*b7c941bbSAndroid Build Coastguard Worker    self.__wait_for_service()
426*b7c941bbSAndroid Build Coastguard Worker    self.__init_socket_port()
427*b7c941bbSAndroid Build Coastguard Worker
428*b7c941bbSAndroid Build Coastguard Worker  def __enter__(self):
429*b7c941bbSAndroid Build Coastguard Worker    self.close_camera()
430*b7c941bbSAndroid Build Coastguard Worker    self.__open_camera()
431*b7c941bbSAndroid Build Coastguard Worker    return self
432*b7c941bbSAndroid Build Coastguard Worker
433*b7c941bbSAndroid Build Coastguard Worker  def __exit__(self, exec_type, exec_value, exec_traceback):
434*b7c941bbSAndroid Build Coastguard Worker    if hasattr(self, 'sock') and self.sock:
435*b7c941bbSAndroid Build Coastguard Worker      self.close_camera()
436*b7c941bbSAndroid Build Coastguard Worker      self.sock.close()
437*b7c941bbSAndroid Build Coastguard Worker    return False
438*b7c941bbSAndroid Build Coastguard Worker
439*b7c941bbSAndroid Build Coastguard Worker  def override_with_hidden_physical_camera_props(self, props):
440*b7c941bbSAndroid Build Coastguard Worker    """Check that it is a valid sub-camera backing the logical camera.
441*b7c941bbSAndroid Build Coastguard Worker
442*b7c941bbSAndroid Build Coastguard Worker    If current session is for a hidden physical camera, check that it is a valid
443*b7c941bbSAndroid Build Coastguard Worker    sub-camera backing the logical camera, override self.props, and return the
444*b7c941bbSAndroid Build Coastguard Worker    characteristics of sub-camera. Otherwise, return "props" directly.
445*b7c941bbSAndroid Build Coastguard Worker
446*b7c941bbSAndroid Build Coastguard Worker    Args:
447*b7c941bbSAndroid Build Coastguard Worker     props: Camera properties object.
448*b7c941bbSAndroid Build Coastguard Worker
449*b7c941bbSAndroid Build Coastguard Worker    Returns:
450*b7c941bbSAndroid Build Coastguard Worker     The properties of the hidden physical camera if possible.
451*b7c941bbSAndroid Build Coastguard Worker    """
452*b7c941bbSAndroid Build Coastguard Worker    if self._hidden_physical_id:
453*b7c941bbSAndroid Build Coastguard Worker      if not camera_properties_utils.logical_multi_camera(props):
454*b7c941bbSAndroid Build Coastguard Worker        logging.debug('cam %s not a logical multi-camera: no change in props.',
455*b7c941bbSAndroid Build Coastguard Worker                      self._hidden_physical_id)
456*b7c941bbSAndroid Build Coastguard Worker        return props
457*b7c941bbSAndroid Build Coastguard Worker      physical_ids = camera_properties_utils.logical_multi_camera_physical_ids(
458*b7c941bbSAndroid Build Coastguard Worker          props)
459*b7c941bbSAndroid Build Coastguard Worker      if self._hidden_physical_id not in physical_ids:
460*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(f'{self._hidden_physical_id} is not a hidden '
461*b7c941bbSAndroid Build Coastguard Worker                             f'sub-camera of {self._camera_id}')
462*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Overriding cam %s props', self._hidden_physical_id)
463*b7c941bbSAndroid Build Coastguard Worker      props = self.get_camera_properties_by_id(self._hidden_physical_id)
464*b7c941bbSAndroid Build Coastguard Worker      self.props = props
465*b7c941bbSAndroid Build Coastguard Worker    return props
466*b7c941bbSAndroid Build Coastguard Worker
467*b7c941bbSAndroid Build Coastguard Worker  def get_camera_properties(self):
468*b7c941bbSAndroid Build Coastguard Worker    """Get the camera properties object for the device.
469*b7c941bbSAndroid Build Coastguard Worker
470*b7c941bbSAndroid Build Coastguard Worker    Returns:
471*b7c941bbSAndroid Build Coastguard Worker     The Python dictionary object for the CameraProperties object.
472*b7c941bbSAndroid Build Coastguard Worker    """
473*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
474*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'getCameraProperties'
475*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
476*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
477*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraProperties':
478*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
479*b7c941bbSAndroid Build Coastguard Worker    self.props = data[_OBJ_VALUE_STR]['cameraProperties']
480*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]['cameraProperties']
481*b7c941bbSAndroid Build Coastguard Worker
482*b7c941bbSAndroid Build Coastguard Worker  def get_session_properties(self, out_surfaces, cap_request):
483*b7c941bbSAndroid Build Coastguard Worker    """Get the camera properties object for a session configuration.
484*b7c941bbSAndroid Build Coastguard Worker
485*b7c941bbSAndroid Build Coastguard Worker    Args:
486*b7c941bbSAndroid Build Coastguard Worker      out_surfaces: output surfaces used to query session props.
487*b7c941bbSAndroid Build Coastguard Worker      cap_request: capture request used to query session props.
488*b7c941bbSAndroid Build Coastguard Worker
489*b7c941bbSAndroid Build Coastguard Worker    Returns:
490*b7c941bbSAndroid Build Coastguard Worker     The Python dictionary object for the CameraProperties object.
491*b7c941bbSAndroid Build Coastguard Worker    """
492*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
493*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'getCameraSessionProperties'
494*b7c941bbSAndroid Build Coastguard Worker    if out_surfaces:
495*b7c941bbSAndroid Build Coastguard Worker      if isinstance(out_surfaces, list):
496*b7c941bbSAndroid Build Coastguard Worker        cmd['outputSurfaces'] = out_surfaces
497*b7c941bbSAndroid Build Coastguard Worker      else:
498*b7c941bbSAndroid Build Coastguard Worker        cmd['outputSurfaces'] = [out_surfaces]
499*b7c941bbSAndroid Build Coastguard Worker      formats = [
500*b7c941bbSAndroid Build Coastguard Worker          c['format'] if 'format' in c else 'yuv' for c in cmd['outputSurfaces']
501*b7c941bbSAndroid Build Coastguard Worker      ]
502*b7c941bbSAndroid Build Coastguard Worker      formats = [s if s != 'jpg' else 'jpeg' for s in formats]
503*b7c941bbSAndroid Build Coastguard Worker    else:
504*b7c941bbSAndroid Build Coastguard Worker      max_yuv_size = capture_request_utils.get_available_output_sizes(
505*b7c941bbSAndroid Build Coastguard Worker          'yuv', self.props)[0]
506*b7c941bbSAndroid Build Coastguard Worker      formats = ['yuv']
507*b7c941bbSAndroid Build Coastguard Worker      cmd['outputSurfaces'] = [{
508*b7c941bbSAndroid Build Coastguard Worker          'format': 'yuv',
509*b7c941bbSAndroid Build Coastguard Worker          'width': max_yuv_size[0],
510*b7c941bbSAndroid Build Coastguard Worker          'height': max_yuv_size[1]
511*b7c941bbSAndroid Build Coastguard Worker      }]
512*b7c941bbSAndroid Build Coastguard Worker    cmd['captureRequest'] = cap_request
513*b7c941bbSAndroid Build Coastguard Worker
514*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
515*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
516*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraProperties':
517*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
518*b7c941bbSAndroid Build Coastguard Worker    self.props = data[_OBJ_VALUE_STR]['cameraProperties']
519*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]['cameraProperties']
520*b7c941bbSAndroid Build Coastguard Worker
521*b7c941bbSAndroid Build Coastguard Worker  def get_camera_properties_by_id(self, camera_id, override_to_portrait=None):
522*b7c941bbSAndroid Build Coastguard Worker    """Get the camera properties object for device with camera_id.
523*b7c941bbSAndroid Build Coastguard Worker
524*b7c941bbSAndroid Build Coastguard Worker    Args:
525*b7c941bbSAndroid Build Coastguard Worker     camera_id: The ID string of the camera
526*b7c941bbSAndroid Build Coastguard Worker     override_to_portrait: Optional value for overrideToPortrait
527*b7c941bbSAndroid Build Coastguard Worker
528*b7c941bbSAndroid Build Coastguard Worker    Returns:
529*b7c941bbSAndroid Build Coastguard Worker     The Python dictionary object for the CameraProperties object. Empty
530*b7c941bbSAndroid Build Coastguard Worker     if no such device exists.
531*b7c941bbSAndroid Build Coastguard Worker    """
532*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
533*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'getCameraPropertiesById'
534*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = camera_id
535*b7c941bbSAndroid Build Coastguard Worker    if override_to_portrait is not None:
536*b7c941bbSAndroid Build Coastguard Worker      cmd['overrideToPortrait'] = override_to_portrait
537*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
538*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
539*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraProperties':
540*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
541*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]['cameraProperties']
542*b7c941bbSAndroid Build Coastguard Worker
543*b7c941bbSAndroid Build Coastguard Worker  def __read_response_from_socket(self):
544*b7c941bbSAndroid Build Coastguard Worker    """Reads a line (newline-terminated) string serialization of JSON object.
545*b7c941bbSAndroid Build Coastguard Worker
546*b7c941bbSAndroid Build Coastguard Worker    Returns:
547*b7c941bbSAndroid Build Coastguard Worker     Deserialized json obj.
548*b7c941bbSAndroid Build Coastguard Worker    """
549*b7c941bbSAndroid Build Coastguard Worker    chars = []
550*b7c941bbSAndroid Build Coastguard Worker    while not chars or chars[-1] != '\n':
551*b7c941bbSAndroid Build Coastguard Worker      ch = self.sock.recv(1).decode('utf-8')
552*b7c941bbSAndroid Build Coastguard Worker      if not ch:
553*b7c941bbSAndroid Build Coastguard Worker        # Socket was probably closed; otherwise don't get empty strings
554*b7c941bbSAndroid Build Coastguard Worker        raise error_util.CameraItsError('Problem with socket on device side')
555*b7c941bbSAndroid Build Coastguard Worker      chars.append(ch)
556*b7c941bbSAndroid Build Coastguard Worker    line = ''.join(chars)
557*b7c941bbSAndroid Build Coastguard Worker    jobj = json.loads(line)
558*b7c941bbSAndroid Build Coastguard Worker    # Optionally read a binary buffer of a fixed size.
559*b7c941bbSAndroid Build Coastguard Worker    buf = None
560*b7c941bbSAndroid Build Coastguard Worker    if 'bufValueSize' in jobj:
561*b7c941bbSAndroid Build Coastguard Worker      n = jobj['bufValueSize']
562*b7c941bbSAndroid Build Coastguard Worker      buf = bytearray(n)
563*b7c941bbSAndroid Build Coastguard Worker      view = memoryview(buf)
564*b7c941bbSAndroid Build Coastguard Worker      while n > 0:
565*b7c941bbSAndroid Build Coastguard Worker        nbytes = self.sock.recv_into(view, n)
566*b7c941bbSAndroid Build Coastguard Worker        view = view[nbytes:]
567*b7c941bbSAndroid Build Coastguard Worker        n -= nbytes
568*b7c941bbSAndroid Build Coastguard Worker      buf = numpy.frombuffer(buf, dtype=numpy.uint8)
569*b7c941bbSAndroid Build Coastguard Worker    return jobj, buf
570*b7c941bbSAndroid Build Coastguard Worker
571*b7c941bbSAndroid Build Coastguard Worker  def __open_camera(self):
572*b7c941bbSAndroid Build Coastguard Worker    """Get the camera ID to open if it is an argument as a single camera.
573*b7c941bbSAndroid Build Coastguard Worker
574*b7c941bbSAndroid Build Coastguard Worker    This allows passing camera=# to individual tests at command line
575*b7c941bbSAndroid Build Coastguard Worker    and camera=#,#,# or an no camera argv with tools/run_all_tests.py.
576*b7c941bbSAndroid Build Coastguard Worker    In case the camera is a logical multi-camera, to run ITS on the
577*b7c941bbSAndroid Build Coastguard Worker    hidden physical sub-camera, pass camera=[logical ID]:[physical ID]
578*b7c941bbSAndroid Build Coastguard Worker    to an individual test at the command line, and same applies to multiple
579*b7c941bbSAndroid Build Coastguard Worker    camera IDs for tools/run_all_tests.py: camera=#,#:#,#:#,#
580*b7c941bbSAndroid Build Coastguard Worker    """
581*b7c941bbSAndroid Build Coastguard Worker    if not self._camera_id:
582*b7c941bbSAndroid Build Coastguard Worker      self._camera_id = 0
583*b7c941bbSAndroid Build Coastguard Worker      for s in sys.argv[1:]:
584*b7c941bbSAndroid Build Coastguard Worker        if s[:7] == 'camera=' and len(s) > 7:
585*b7c941bbSAndroid Build Coastguard Worker          camera_ids = s[7:].split(',')
586*b7c941bbSAndroid Build Coastguard Worker          camera_id_combos = parse_camera_ids(camera_ids)
587*b7c941bbSAndroid Build Coastguard Worker          if len(camera_id_combos) == 1:
588*b7c941bbSAndroid Build Coastguard Worker            self._camera_id = camera_id_combos[0].id
589*b7c941bbSAndroid Build Coastguard Worker            self._hidden_physical_id = camera_id_combos[0].sub_id
590*b7c941bbSAndroid Build Coastguard Worker
591*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Opening camera: %s', self._camera_id)
592*b7c941bbSAndroid Build Coastguard Worker    cmd = {_CMD_NAME_STR: 'open', _CAMERA_ID_STR: self._camera_id}
593*b7c941bbSAndroid Build Coastguard Worker    if self._override_to_portrait is not None:
594*b7c941bbSAndroid Build Coastguard Worker      cmd['overrideToPortrait'] = self._override_to_portrait
595*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
596*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
597*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraOpened':
598*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
599*b7c941bbSAndroid Build Coastguard Worker
600*b7c941bbSAndroid Build Coastguard Worker  def close_camera(self):
601*b7c941bbSAndroid Build Coastguard Worker    cmd = {_CMD_NAME_STR: 'close'}
602*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
603*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
604*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraClosed':
605*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
606*b7c941bbSAndroid Build Coastguard Worker
607*b7c941bbSAndroid Build Coastguard Worker  def zoom_ratio_within_range(self, zoom_ratio):
608*b7c941bbSAndroid Build Coastguard Worker    """Determine if a given zoom ratio is within device zoom range.
609*b7c941bbSAndroid Build Coastguard Worker
610*b7c941bbSAndroid Build Coastguard Worker    Args:
611*b7c941bbSAndroid Build Coastguard Worker      zoom_ratio: float; zoom ratio requested
612*b7c941bbSAndroid Build Coastguard Worker    Returns:
613*b7c941bbSAndroid Build Coastguard Worker      Boolean: True, if zoom_ratio inside device range. False otherwise.
614*b7c941bbSAndroid Build Coastguard Worker    """
615*b7c941bbSAndroid Build Coastguard Worker    zoom_range = self.props['android.control.zoomRatioRange']
616*b7c941bbSAndroid Build Coastguard Worker    return zoom_ratio >= zoom_range[0] and zoom_ratio <= zoom_range[1]
617*b7c941bbSAndroid Build Coastguard Worker
618*b7c941bbSAndroid Build Coastguard Worker  def get_sensors(self):
619*b7c941bbSAndroid Build Coastguard Worker    """Get all sensors on the device.
620*b7c941bbSAndroid Build Coastguard Worker
621*b7c941bbSAndroid Build Coastguard Worker    Returns:
622*b7c941bbSAndroid Build Coastguard Worker       A Python dictionary that returns keys and booleans for each sensor.
623*b7c941bbSAndroid Build Coastguard Worker    """
624*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
625*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'checkSensorExistence'
626*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
627*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
628*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'sensorExistence':
629*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid response for command: %s' %
630*b7c941bbSAndroid Build Coastguard Worker                                      cmd[_CMD_NAME_STR])
631*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]
632*b7c941bbSAndroid Build Coastguard Worker
633*b7c941bbSAndroid Build Coastguard Worker  def get_default_camera_pkg(self):
634*b7c941bbSAndroid Build Coastguard Worker    """Get default camera app package name.
635*b7c941bbSAndroid Build Coastguard Worker
636*b7c941bbSAndroid Build Coastguard Worker    Returns:
637*b7c941bbSAndroid Build Coastguard Worker       Default camera app pkg name.
638*b7c941bbSAndroid Build Coastguard Worker    """
639*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
640*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'doGetDefaultCameraPkgName'
641*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
642*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
643*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'defaultCameraPkg':
644*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid response for command: %s' %
645*b7c941bbSAndroid Build Coastguard Worker                                      cmd[_CMD_NAME_STR])
646*b7c941bbSAndroid Build Coastguard Worker    return data['strValue']
647*b7c941bbSAndroid Build Coastguard Worker
648*b7c941bbSAndroid Build Coastguard Worker  def check_gain_map_present(self, file_path):
649*b7c941bbSAndroid Build Coastguard Worker    """Check if the image has gainmap present or not.
650*b7c941bbSAndroid Build Coastguard Worker
651*b7c941bbSAndroid Build Coastguard Worker    The image stored at file_path is decoded and analyzed
652*b7c941bbSAndroid Build Coastguard Worker    to check whether the gainmap is present or not. If the image
653*b7c941bbSAndroid Build Coastguard Worker    captured is UltraHDR, it should have gainmap present.
654*b7c941bbSAndroid Build Coastguard Worker
655*b7c941bbSAndroid Build Coastguard Worker    Args:
656*b7c941bbSAndroid Build Coastguard Worker      file_path: path of the image to be analyzed on DUT.
657*b7c941bbSAndroid Build Coastguard Worker    Returns:
658*b7c941bbSAndroid Build Coastguard Worker      Boolean: True if the image has gainmap present.
659*b7c941bbSAndroid Build Coastguard Worker    """
660*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
661*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'doGainMapCheck'
662*b7c941bbSAndroid Build Coastguard Worker    cmd['filePath'] = file_path
663*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
664*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
665*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'gainmapPresent':
666*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError(
667*b7c941bbSAndroid Build Coastguard Worker          'Invalid response for command: %s' % cmd[_CMD_NAME_STR])
668*b7c941bbSAndroid Build Coastguard Worker    return data['strValue']
669*b7c941bbSAndroid Build Coastguard Worker
670*b7c941bbSAndroid Build Coastguard Worker  def start_sensor_events(self):
671*b7c941bbSAndroid Build Coastguard Worker    """Start collecting sensor events on the device.
672*b7c941bbSAndroid Build Coastguard Worker
673*b7c941bbSAndroid Build Coastguard Worker    See get_sensor_events for more info.
674*b7c941bbSAndroid Build Coastguard Worker
675*b7c941bbSAndroid Build Coastguard Worker    Returns:
676*b7c941bbSAndroid Build Coastguard Worker       Nothing.
677*b7c941bbSAndroid Build Coastguard Worker    """
678*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
679*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'startSensorEvents'
680*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
681*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
682*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'sensorEventsStarted':
683*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid response for command: %s' %
684*b7c941bbSAndroid Build Coastguard Worker                                      cmd[_CMD_NAME_STR])
685*b7c941bbSAndroid Build Coastguard Worker
686*b7c941bbSAndroid Build Coastguard Worker  def get_sensor_events(self):
687*b7c941bbSAndroid Build Coastguard Worker    """Get a trace of all sensor events on the device.
688*b7c941bbSAndroid Build Coastguard Worker
689*b7c941bbSAndroid Build Coastguard Worker        The trace starts when the start_sensor_events function is called. If
690*b7c941bbSAndroid Build Coastguard Worker        the test runs for a long time after this call, then the device's
691*b7c941bbSAndroid Build Coastguard Worker        internal memory can fill up. Calling get_sensor_events gets all events
692*b7c941bbSAndroid Build Coastguard Worker        from the device, and then stops the device from collecting events and
693*b7c941bbSAndroid Build Coastguard Worker        clears the internal buffer; to start again, the start_sensor_events
694*b7c941bbSAndroid Build Coastguard Worker        call must be used again.
695*b7c941bbSAndroid Build Coastguard Worker
696*b7c941bbSAndroid Build Coastguard Worker        Events from the accelerometer, compass, and gyro are returned; each
697*b7c941bbSAndroid Build Coastguard Worker        has a timestamp and x,y,z values.
698*b7c941bbSAndroid Build Coastguard Worker
699*b7c941bbSAndroid Build Coastguard Worker        Note that sensor events are only produced if the device isn't in its
700*b7c941bbSAndroid Build Coastguard Worker        standby mode (i.e.) if the screen is on.
701*b7c941bbSAndroid Build Coastguard Worker
702*b7c941bbSAndroid Build Coastguard Worker    Returns:
703*b7c941bbSAndroid Build Coastguard Worker            A Python dictionary with three keys ("accel", "mag", "gyro") each
704*b7c941bbSAndroid Build Coastguard Worker            of which maps to a list of objects containing "time","x","y","z"
705*b7c941bbSAndroid Build Coastguard Worker            keys.
706*b7c941bbSAndroid Build Coastguard Worker    """
707*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
708*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'getSensorEvents'
709*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
710*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
711*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
712*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
713*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'sensorEvents':
714*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid response for command: %s ' %
715*b7c941bbSAndroid Build Coastguard Worker                                      cmd[_CMD_NAME_STR])
716*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(self.SOCK_TIMEOUT)
717*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]
718*b7c941bbSAndroid Build Coastguard Worker
719*b7c941bbSAndroid Build Coastguard Worker  def get_camera_ids(self):
720*b7c941bbSAndroid Build Coastguard Worker    """Returns the list of all camera_ids.
721*b7c941bbSAndroid Build Coastguard Worker
722*b7c941bbSAndroid Build Coastguard Worker    Returns:
723*b7c941bbSAndroid Build Coastguard Worker      List of camera ids on the device.
724*b7c941bbSAndroid Build Coastguard Worker    """
725*b7c941bbSAndroid Build Coastguard Worker    cmd = {'cmdName': 'getCameraIds'}
726*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
727*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
728*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
729*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
730*b7c941bbSAndroid Build Coastguard Worker    if data['tag'] != 'cameraIds':
731*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
732*b7c941bbSAndroid Build Coastguard Worker    return data['objValue']
733*b7c941bbSAndroid Build Coastguard Worker
734*b7c941bbSAndroid Build Coastguard Worker  def get_camera_name(self):
735*b7c941bbSAndroid Build Coastguard Worker    """Gets the camera name.
736*b7c941bbSAndroid Build Coastguard Worker
737*b7c941bbSAndroid Build Coastguard Worker    Returns:
738*b7c941bbSAndroid Build Coastguard Worker      The camera name with camera id and/or hidden physical id.
739*b7c941bbSAndroid Build Coastguard Worker    """
740*b7c941bbSAndroid Build Coastguard Worker    if self._hidden_physical_id:
741*b7c941bbSAndroid Build Coastguard Worker      return f'{self._camera_id}.{self._hidden_physical_id}'
742*b7c941bbSAndroid Build Coastguard Worker    else:
743*b7c941bbSAndroid Build Coastguard Worker      return self._camera_id
744*b7c941bbSAndroid Build Coastguard Worker
745*b7c941bbSAndroid Build Coastguard Worker  def get_unavailable_physical_cameras(self, camera_id):
746*b7c941bbSAndroid Build Coastguard Worker    """Get the unavailable physical cameras ids.
747*b7c941bbSAndroid Build Coastguard Worker
748*b7c941bbSAndroid Build Coastguard Worker    Args:
749*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device id
750*b7c941bbSAndroid Build Coastguard Worker    Returns:
751*b7c941bbSAndroid Build Coastguard Worker      List of all physical camera ids which are unavailable.
752*b7c941bbSAndroid Build Coastguard Worker    """
753*b7c941bbSAndroid Build Coastguard Worker    cmd = {_CMD_NAME_STR: 'doGetUnavailablePhysicalCameras',
754*b7c941bbSAndroid Build Coastguard Worker           _CAMERA_ID_STR: camera_id}
755*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
756*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
757*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
758*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
759*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'unavailablePhysicalCameras':
760*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
761*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]
762*b7c941bbSAndroid Build Coastguard Worker
763*b7c941bbSAndroid Build Coastguard Worker  def is_hlg10_recording_supported_for_profile(self, profile_id):
764*b7c941bbSAndroid Build Coastguard Worker    """Query whether the camera device supports HLG10 video recording.
765*b7c941bbSAndroid Build Coastguard Worker
766*b7c941bbSAndroid Build Coastguard Worker    Args:
767*b7c941bbSAndroid Build Coastguard Worker      profile_id: int; profile id corresponding to the quality level.
768*b7c941bbSAndroid Build Coastguard Worker    Returns:
769*b7c941bbSAndroid Build Coastguard Worker      Boolean: True if device supports HLG10 video recording, False in
770*b7c941bbSAndroid Build Coastguard Worker      all other cases.
771*b7c941bbSAndroid Build Coastguard Worker    """
772*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
773*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isHLG10SupportedForProfile'
774*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
775*b7c941bbSAndroid Build Coastguard Worker    cmd['profileId'] = profile_id
776*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
777*b7c941bbSAndroid Build Coastguard Worker
778*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
779*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'hlg10Response':
780*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query HLG10 support')
781*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
782*b7c941bbSAndroid Build Coastguard Worker
783*b7c941bbSAndroid Build Coastguard Worker  def is_hlg10_recording_supported_for_size_and_fps(
784*b7c941bbSAndroid Build Coastguard Worker      self, video_size, max_fps):
785*b7c941bbSAndroid Build Coastguard Worker    """Query whether the camera device supports HLG10 video recording.
786*b7c941bbSAndroid Build Coastguard Worker
787*b7c941bbSAndroid Build Coastguard Worker    Args:
788*b7c941bbSAndroid Build Coastguard Worker      video_size: String; the hlg10 video recording size.
789*b7c941bbSAndroid Build Coastguard Worker      max_fps: int; the maximum frame rate of the camera.
790*b7c941bbSAndroid Build Coastguard Worker    Returns:
791*b7c941bbSAndroid Build Coastguard Worker      Boolean: True if device supports HLG10 video recording, False in
792*b7c941bbSAndroid Build Coastguard Worker      all other cases.
793*b7c941bbSAndroid Build Coastguard Worker    """
794*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
795*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isHLG10SupportedForSizeAndFps'
796*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
797*b7c941bbSAndroid Build Coastguard Worker    cmd['videoSize'] = video_size
798*b7c941bbSAndroid Build Coastguard Worker    cmd['maxFps'] = max_fps
799*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
800*b7c941bbSAndroid Build Coastguard Worker
801*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
802*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'hlg10Response':
803*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query HLG10 support')
804*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
805*b7c941bbSAndroid Build Coastguard Worker
806*b7c941bbSAndroid Build Coastguard Worker  def is_p3_capture_supported(self):
807*b7c941bbSAndroid Build Coastguard Worker    """Query whether the camera device supports P3 image capture.
808*b7c941bbSAndroid Build Coastguard Worker
809*b7c941bbSAndroid Build Coastguard Worker    Returns:
810*b7c941bbSAndroid Build Coastguard Worker      Boolean: True, if device supports P3 image capture, False in
811*b7c941bbSAndroid Build Coastguard Worker      all other cases.
812*b7c941bbSAndroid Build Coastguard Worker    """
813*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
814*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isP3Supported'
815*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
816*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
817*b7c941bbSAndroid Build Coastguard Worker
818*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
819*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'p3Response':
820*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query P3 support')
821*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
822*b7c941bbSAndroid Build Coastguard Worker
823*b7c941bbSAndroid Build Coastguard Worker  def is_landscape_to_portrait_enabled(self):
824*b7c941bbSAndroid Build Coastguard Worker    """Query whether the device has enabled the landscape to portrait property.
825*b7c941bbSAndroid Build Coastguard Worker
826*b7c941bbSAndroid Build Coastguard Worker    Returns:
827*b7c941bbSAndroid Build Coastguard Worker      Boolean: True, if the device has the system property enabled. False
828*b7c941bbSAndroid Build Coastguard Worker      otherwise.
829*b7c941bbSAndroid Build Coastguard Worker    """
830*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
831*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isLandscapeToPortraitEnabled'
832*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
833*b7c941bbSAndroid Build Coastguard Worker
834*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
835*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'landscapeToPortraitEnabledResponse':
836*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError(
837*b7c941bbSAndroid Build Coastguard Worker          'Failed to query landscape to portrait system property')
838*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
839*b7c941bbSAndroid Build Coastguard Worker
840*b7c941bbSAndroid Build Coastguard Worker  def get_supported_video_sizes_capped(self, camera_id):
841*b7c941bbSAndroid Build Coastguard Worker    """Get the supported video sizes for camera id.
842*b7c941bbSAndroid Build Coastguard Worker
843*b7c941bbSAndroid Build Coastguard Worker    Args:
844*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device id
845*b7c941bbSAndroid Build Coastguard Worker    Returns:
846*b7c941bbSAndroid Build Coastguard Worker      Sorted list of supported video sizes.
847*b7c941bbSAndroid Build Coastguard Worker    """
848*b7c941bbSAndroid Build Coastguard Worker
849*b7c941bbSAndroid Build Coastguard Worker    cmd = {
850*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'doGetSupportedVideoSizesCapped',
851*b7c941bbSAndroid Build Coastguard Worker        _CAMERA_ID_STR: camera_id,
852*b7c941bbSAndroid Build Coastguard Worker    }
853*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
854*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
855*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
856*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
857*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'supportedVideoSizes':
858*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
859*b7c941bbSAndroid Build Coastguard Worker    if not data[_STR_VALUE_STR]:
860*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No supported video sizes')
861*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR].split(';')
862*b7c941bbSAndroid Build Coastguard Worker
863*b7c941bbSAndroid Build Coastguard Worker  def do_basic_recording(self, profile_id, quality, duration,
864*b7c941bbSAndroid Build Coastguard Worker                         video_stabilization_mode=0, hlg10_enabled=False,
865*b7c941bbSAndroid Build Coastguard Worker                         zoom_ratio=None, ae_target_fps_min=None,
866*b7c941bbSAndroid Build Coastguard Worker                         ae_target_fps_max=None, antibanding_mode=None,
867*b7c941bbSAndroid Build Coastguard Worker                         face_detect_mode=None):
868*b7c941bbSAndroid Build Coastguard Worker    """Issue a recording request and read back the video recording object.
869*b7c941bbSAndroid Build Coastguard Worker
870*b7c941bbSAndroid Build Coastguard Worker    The recording will be done with the format specified in quality. These
871*b7c941bbSAndroid Build Coastguard Worker    quality levels correspond to the profiles listed in CamcorderProfile.
872*b7c941bbSAndroid Build Coastguard Worker    The duration is the time in seconds for which the video will be recorded.
873*b7c941bbSAndroid Build Coastguard Worker    The recorded object consists of a path on the device at which the
874*b7c941bbSAndroid Build Coastguard Worker    recorded video is saved.
875*b7c941bbSAndroid Build Coastguard Worker
876*b7c941bbSAndroid Build Coastguard Worker    Args:
877*b7c941bbSAndroid Build Coastguard Worker      profile_id: int; profile id corresponding to the quality level.
878*b7c941bbSAndroid Build Coastguard Worker      quality: Video recording quality such as High, Low, VGA.
879*b7c941bbSAndroid Build Coastguard Worker      duration: The time in seconds for which the video will be recorded.
880*b7c941bbSAndroid Build Coastguard Worker      video_stabilization_mode: Video stabilization mode ON/OFF. Value can be
881*b7c941bbSAndroid Build Coastguard Worker      0: 'OFF', 1: 'ON', 2: 'PREVIEW'
882*b7c941bbSAndroid Build Coastguard Worker      hlg10_enabled: boolean: True Enable 10-bit HLG video recording, False
883*b7c941bbSAndroid Build Coastguard Worker      record using the regular SDR profile
884*b7c941bbSAndroid Build Coastguard Worker      zoom_ratio: float; zoom ratio. None if default zoom
885*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min: int; CONTROL_AE_TARGET_FPS_RANGE min. Set if not None
886*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_max: int; CONTROL_AE_TARGET_FPS_RANGE max. Set if not None
887*b7c941bbSAndroid Build Coastguard Worker      antibanding_mode: int; CONTROL_AE_ANTIBANDING_MODE. Set if not None
888*b7c941bbSAndroid Build Coastguard Worker      face_detect_mode: int; STATISTICS_FACE_DETECT_MODE. Set if not None
889*b7c941bbSAndroid Build Coastguard Worker    Returns:
890*b7c941bbSAndroid Build Coastguard Worker      video_recorded_object: The recorded object returned from ItsService which
891*b7c941bbSAndroid Build Coastguard Worker      contains path at which the recording is saved on the device, quality of
892*b7c941bbSAndroid Build Coastguard Worker      the recorded video, video size of the recorded video, video frame rate
893*b7c941bbSAndroid Build Coastguard Worker      and 'hlg10' if 'hlg10_enabled' is set to True.
894*b7c941bbSAndroid Build Coastguard Worker      Ex:
895*b7c941bbSAndroid Build Coastguard Worker      VideoRecordingObject: {
896*b7c941bbSAndroid Build Coastguard Worker        'tag': 'recordingResponse',
897*b7c941bbSAndroid Build Coastguard Worker        'objValue': {
898*b7c941bbSAndroid Build Coastguard Worker          'recordedOutputPath':
899*b7c941bbSAndroid Build Coastguard Worker            '/storage/emulated/0/Android/data/com.android.cts.verifier'
900*b7c941bbSAndroid Build Coastguard Worker            '/files/VideoITS/VID_20220324_080414_0_CIF_352x288.mp4',
901*b7c941bbSAndroid Build Coastguard Worker          'quality': 'CIF',
902*b7c941bbSAndroid Build Coastguard Worker          'videoFrameRate': 30,
903*b7c941bbSAndroid Build Coastguard Worker          'videoSize': '352x288'
904*b7c941bbSAndroid Build Coastguard Worker        }
905*b7c941bbSAndroid Build Coastguard Worker      }
906*b7c941bbSAndroid Build Coastguard Worker    """
907*b7c941bbSAndroid Build Coastguard Worker    cmd = {_CMD_NAME_STR: 'doBasicRecording', _CAMERA_ID_STR: self._camera_id,
908*b7c941bbSAndroid Build Coastguard Worker           'profileId': profile_id, 'quality': quality,
909*b7c941bbSAndroid Build Coastguard Worker           'recordingDuration': duration,
910*b7c941bbSAndroid Build Coastguard Worker           'videoStabilizationMode': video_stabilization_mode,
911*b7c941bbSAndroid Build Coastguard Worker           'hlg10Enabled': hlg10_enabled}
912*b7c941bbSAndroid Build Coastguard Worker    if zoom_ratio:
913*b7c941bbSAndroid Build Coastguard Worker      if self.zoom_ratio_within_range(zoom_ratio):
914*b7c941bbSAndroid Build Coastguard Worker        cmd['zoomRatio'] = zoom_ratio
915*b7c941bbSAndroid Build Coastguard Worker      else:
916*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(f'Zoom ratio {zoom_ratio} out of range')
917*b7c941bbSAndroid Build Coastguard Worker    if ae_target_fps_min and ae_target_fps_max:
918*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMin'] = ae_target_fps_min
919*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMax'] = ae_target_fps_max
920*b7c941bbSAndroid Build Coastguard Worker    if antibanding_mode:
921*b7c941bbSAndroid Build Coastguard Worker      cmd['aeAntibandingMode'] = antibanding_mode
922*b7c941bbSAndroid Build Coastguard Worker    else:
923*b7c941bbSAndroid Build Coastguard Worker      cmd['aeAntibandingMode'] = 0
924*b7c941bbSAndroid Build Coastguard Worker    if face_detect_mode:
925*b7c941bbSAndroid Build Coastguard Worker      cmd['faceDetectMode'] = face_detect_mode
926*b7c941bbSAndroid Build Coastguard Worker    else:
927*b7c941bbSAndroid Build Coastguard Worker      cmd['faceDetectMode'] = 0
928*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
929*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
930*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
931*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
932*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'recordingResponse':
933*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError(
934*b7c941bbSAndroid Build Coastguard Worker          f'Invalid response for command: {cmd[_CMD_NAME_STR]}')
935*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]
936*b7c941bbSAndroid Build Coastguard Worker
937*b7c941bbSAndroid Build Coastguard Worker  def _execute_preview_recording(self, cmd):
938*b7c941bbSAndroid Build Coastguard Worker    """Send preview recording command over socket and retrieve output object.
939*b7c941bbSAndroid Build Coastguard Worker
940*b7c941bbSAndroid Build Coastguard Worker    Args:
941*b7c941bbSAndroid Build Coastguard Worker      cmd: dict; Mapping from command key to corresponding value
942*b7c941bbSAndroid Build Coastguard Worker    Returns:
943*b7c941bbSAndroid Build Coastguard Worker      video_recorded_object: The recorded object returned from ItsService which
944*b7c941bbSAndroid Build Coastguard Worker      contains path at which the recording is saved on the device, quality of
945*b7c941bbSAndroid Build Coastguard Worker      the recorded video which is always set to "preview", video size of the
946*b7c941bbSAndroid Build Coastguard Worker      recorded video, video frame rate.
947*b7c941bbSAndroid Build Coastguard Worker      Ex:
948*b7c941bbSAndroid Build Coastguard Worker      VideoRecordingObject: {
949*b7c941bbSAndroid Build Coastguard Worker        'tag': 'recordingResponse',
950*b7c941bbSAndroid Build Coastguard Worker        'objValue': {
951*b7c941bbSAndroid Build Coastguard Worker          'recordedOutputPath': '/storage/emulated/0/Android/data/'
952*b7c941bbSAndroid Build Coastguard Worker                                'com.android.cts.verifier/files/VideoITS/'
953*b7c941bbSAndroid Build Coastguard Worker                                'VID_20220324_080414_0_CIF_352x288.mp4',
954*b7c941bbSAndroid Build Coastguard Worker          'quality': 'preview',
955*b7c941bbSAndroid Build Coastguard Worker          'videoSize': '352x288'
956*b7c941bbSAndroid Build Coastguard Worker        }
957*b7c941bbSAndroid Build Coastguard Worker      }
958*b7c941bbSAndroid Build Coastguard Worker    """
959*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
960*b7c941bbSAndroid Build Coastguard Worker    timeout = (self.SOCK_TIMEOUT_PREVIEW +
961*b7c941bbSAndroid Build Coastguard Worker               self.EXTRA_SOCK_TIMEOUT * _EXTRA_TIMEOUT_FACTOR)
962*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
963*b7c941bbSAndroid Build Coastguard Worker
964*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
965*b7c941bbSAndroid Build Coastguard Worker    logging.debug('VideoRecordingObject: %s', str(data))
966*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'recordingResponse':
967*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError(
968*b7c941bbSAndroid Build Coastguard Worker          f'Invalid response from command{cmd[_CMD_NAME_STR]}')
969*b7c941bbSAndroid Build Coastguard Worker    return data[_OBJ_VALUE_STR]
970*b7c941bbSAndroid Build Coastguard Worker
971*b7c941bbSAndroid Build Coastguard Worker  def do_preview_recording_multiple_surfaces(
972*b7c941bbSAndroid Build Coastguard Worker      self, output_surfaces, duration, stabilize, ois=False,
973*b7c941bbSAndroid Build Coastguard Worker      zoom_ratio=None, ae_target_fps_min=None, ae_target_fps_max=None,
974*b7c941bbSAndroid Build Coastguard Worker      antibanding_mode=None, face_detect_mode=None):
975*b7c941bbSAndroid Build Coastguard Worker    """Issue a preview request and read back the preview recording object.
976*b7c941bbSAndroid Build Coastguard Worker
977*b7c941bbSAndroid Build Coastguard Worker    The resolution of the preview and its recording will be determined by
978*b7c941bbSAndroid Build Coastguard Worker    video_size. The duration is the time in seconds for which the preview will
979*b7c941bbSAndroid Build Coastguard Worker    be recorded. The recorded object consists of a path on the device at
980*b7c941bbSAndroid Build Coastguard Worker    which the recorded video is saved.
981*b7c941bbSAndroid Build Coastguard Worker
982*b7c941bbSAndroid Build Coastguard Worker    Args:
983*b7c941bbSAndroid Build Coastguard Worker      output_surfaces: list; The list of output surfaces used for creating
984*b7c941bbSAndroid Build Coastguard Worker                             preview recording session. The first surface
985*b7c941bbSAndroid Build Coastguard Worker                             is used for recording.
986*b7c941bbSAndroid Build Coastguard Worker      duration: int; The time in seconds for which the video will be recorded.
987*b7c941bbSAndroid Build Coastguard Worker      stabilize: boolean; Whether the preview should be stabilized or not
988*b7c941bbSAndroid Build Coastguard Worker      ois: boolean; Whether the preview should be optically stabilized or not
989*b7c941bbSAndroid Build Coastguard Worker      zoom_ratio: float; static zoom ratio. None if default zoom
990*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min: int; CONTROL_AE_TARGET_FPS_RANGE min. Set if not None
991*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_max: int; CONTROL_AE_TARGET_FPS_RANGE max. Set if not None
992*b7c941bbSAndroid Build Coastguard Worker      antibanding_mode: int; CONTROL_AE_ANTIBANDING_MODE. Set if not None
993*b7c941bbSAndroid Build Coastguard Worker      face_detect_mode: int; STATISTICS_FACE_DETECT_MODE. Set if not None
994*b7c941bbSAndroid Build Coastguard Worker    Returns:
995*b7c941bbSAndroid Build Coastguard Worker      video_recorded_object: The recorded object returned from ItsService
996*b7c941bbSAndroid Build Coastguard Worker    """
997*b7c941bbSAndroid Build Coastguard Worker    cam_id = self._camera_id
998*b7c941bbSAndroid Build Coastguard Worker    if 'physicalCamera' in output_surfaces[0]:
999*b7c941bbSAndroid Build Coastguard Worker      cam_id = output_surfaces[0]['physicalCamera']
1000*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1001*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'doStaticPreviewRecording',
1002*b7c941bbSAndroid Build Coastguard Worker        _CAMERA_ID_STR: cam_id,
1003*b7c941bbSAndroid Build Coastguard Worker        'outputSurfaces': output_surfaces,
1004*b7c941bbSAndroid Build Coastguard Worker        'recordingDuration': duration,
1005*b7c941bbSAndroid Build Coastguard Worker        'stabilize': stabilize,
1006*b7c941bbSAndroid Build Coastguard Worker        'ois': ois,
1007*b7c941bbSAndroid Build Coastguard Worker    }
1008*b7c941bbSAndroid Build Coastguard Worker    if zoom_ratio:
1009*b7c941bbSAndroid Build Coastguard Worker      if self.zoom_ratio_within_range(zoom_ratio):
1010*b7c941bbSAndroid Build Coastguard Worker        cmd['zoomRatio'] = zoom_ratio
1011*b7c941bbSAndroid Build Coastguard Worker      else:
1012*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(f'Zoom ratio {zoom_ratio} out of range')
1013*b7c941bbSAndroid Build Coastguard Worker    if ae_target_fps_min and ae_target_fps_max:
1014*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMin'] = ae_target_fps_min
1015*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMax'] = ae_target_fps_max
1016*b7c941bbSAndroid Build Coastguard Worker    if antibanding_mode is not None:
1017*b7c941bbSAndroid Build Coastguard Worker      cmd['aeAntibandingMode'] = antibanding_mode
1018*b7c941bbSAndroid Build Coastguard Worker    if face_detect_mode is not None:
1019*b7c941bbSAndroid Build Coastguard Worker      cmd['faceDetectMode'] = face_detect_mode
1020*b7c941bbSAndroid Build Coastguard Worker    return self._execute_preview_recording(cmd)
1021*b7c941bbSAndroid Build Coastguard Worker
1022*b7c941bbSAndroid Build Coastguard Worker  def do_preview_recording(
1023*b7c941bbSAndroid Build Coastguard Worker      self, video_size, duration, stabilize, ois=False, zoom_ratio=None,
1024*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min=None, ae_target_fps_max=None, hlg10_enabled=False,
1025*b7c941bbSAndroid Build Coastguard Worker      antibanding_mode=None, face_detect_mode=None):
1026*b7c941bbSAndroid Build Coastguard Worker    """Issue a preview request and read back the preview recording object.
1027*b7c941bbSAndroid Build Coastguard Worker
1028*b7c941bbSAndroid Build Coastguard Worker    The resolution of the preview and its recording will be determined by
1029*b7c941bbSAndroid Build Coastguard Worker    video_size. The duration is the time in seconds for which the preview will
1030*b7c941bbSAndroid Build Coastguard Worker    be recorded. The recorded object consists of a path on the device at
1031*b7c941bbSAndroid Build Coastguard Worker    which the recorded video is saved.
1032*b7c941bbSAndroid Build Coastguard Worker
1033*b7c941bbSAndroid Build Coastguard Worker    Args:
1034*b7c941bbSAndroid Build Coastguard Worker      video_size: str; Preview resolution at which to record. ex. "1920x1080"
1035*b7c941bbSAndroid Build Coastguard Worker      duration: int; The time in seconds for which the video will be recorded.
1036*b7c941bbSAndroid Build Coastguard Worker      stabilize: boolean; Whether the preview should be stabilized or not
1037*b7c941bbSAndroid Build Coastguard Worker      ois: boolean; Whether the preview should be optically stabilized or not
1038*b7c941bbSAndroid Build Coastguard Worker      zoom_ratio: float; static zoom ratio. None if default zoom
1039*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min: int; CONTROL_AE_TARGET_FPS_RANGE min. Set if not None
1040*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_max: int; CONTROL_AE_TARGET_FPS_RANGE max. Set if not None
1041*b7c941bbSAndroid Build Coastguard Worker      hlg10_enabled: boolean; True Eanable 10-bit HLG video recording, False
1042*b7c941bbSAndroid Build Coastguard Worker                              record using the regular SDK profile.
1043*b7c941bbSAndroid Build Coastguard Worker      antibanding_mode: int; CONTROL_AE_ANTIBANDING_MODE. Set if not None
1044*b7c941bbSAndroid Build Coastguard Worker      face_detect_mode: int; STATISTICS_FACE_DETECT_MODE. Set if not None
1045*b7c941bbSAndroid Build Coastguard Worker    Returns:
1046*b7c941bbSAndroid Build Coastguard Worker      video_recorded_object: The recorded object returned from ItsService
1047*b7c941bbSAndroid Build Coastguard Worker    """
1048*b7c941bbSAndroid Build Coastguard Worker    output_surfaces = self.preview_surface(video_size, hlg10_enabled)
1049*b7c941bbSAndroid Build Coastguard Worker    return self.do_preview_recording_multiple_surfaces(
1050*b7c941bbSAndroid Build Coastguard Worker        output_surfaces, duration, stabilize, ois, zoom_ratio,
1051*b7c941bbSAndroid Build Coastguard Worker        ae_target_fps_min, ae_target_fps_max, antibanding_mode,
1052*b7c941bbSAndroid Build Coastguard Worker        face_detect_mode)
1053*b7c941bbSAndroid Build Coastguard Worker
1054*b7c941bbSAndroid Build Coastguard Worker  def do_preview_recording_with_dynamic_zoom(self, video_size, stabilize,
1055*b7c941bbSAndroid Build Coastguard Worker                                             sweep_zoom,
1056*b7c941bbSAndroid Build Coastguard Worker                                             ae_target_fps_min=None,
1057*b7c941bbSAndroid Build Coastguard Worker                                             ae_target_fps_max=None,
1058*b7c941bbSAndroid Build Coastguard Worker                                             padded_frames=False):
1059*b7c941bbSAndroid Build Coastguard Worker    """Issue a preview request with dynamic zoom and read back output object.
1060*b7c941bbSAndroid Build Coastguard Worker
1061*b7c941bbSAndroid Build Coastguard Worker    The resolution of the preview and its recording will be determined by
1062*b7c941bbSAndroid Build Coastguard Worker    video_size. The duration will be determined by the duration at each zoom
1063*b7c941bbSAndroid Build Coastguard Worker    ratio and the total number of zoom ratios. The recorded object consists
1064*b7c941bbSAndroid Build Coastguard Worker    of a path on the device at which the recorded video is saved.
1065*b7c941bbSAndroid Build Coastguard Worker
1066*b7c941bbSAndroid Build Coastguard Worker    Args:
1067*b7c941bbSAndroid Build Coastguard Worker      video_size: str; Preview resolution at which to record. ex. "1920x1080"
1068*b7c941bbSAndroid Build Coastguard Worker      stabilize: boolean; Whether the preview should be stabilized or not
1069*b7c941bbSAndroid Build Coastguard Worker      sweep_zoom: tuple of (zoom_start, zoom_end, step_size, step_duration).
1070*b7c941bbSAndroid Build Coastguard Worker        Used to control zoom ratio during recording.
1071*b7c941bbSAndroid Build Coastguard Worker        zoom_start (float) is the starting zoom ratio during recording
1072*b7c941bbSAndroid Build Coastguard Worker        zoom_end (float) is the ending zoom ratio during recording
1073*b7c941bbSAndroid Build Coastguard Worker        step_size (float) is the step for zoom ratio during recording
1074*b7c941bbSAndroid Build Coastguard Worker        step_duration (float) sleep in ms between zoom ratios
1075*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min: int; CONTROL_AE_TARGET_FPS_RANGE min. Set if not None
1076*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_max: int; CONTROL_AE_TARGET_FPS_RANGE max. Set if not None
1077*b7c941bbSAndroid Build Coastguard Worker      padded_frames: boolean; Whether to add additional frames at the beginning
1078*b7c941bbSAndroid Build Coastguard Worker        and end of recording to workaround issue with MediaRecorder.
1079*b7c941bbSAndroid Build Coastguard Worker    Returns:
1080*b7c941bbSAndroid Build Coastguard Worker      video_recorded_object: The recorded object returned from ItsService
1081*b7c941bbSAndroid Build Coastguard Worker    """
1082*b7c941bbSAndroid Build Coastguard Worker    output_surface = self.preview_surface(video_size)
1083*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1084*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'doDynamicZoomPreviewRecording',
1085*b7c941bbSAndroid Build Coastguard Worker        _CAMERA_ID_STR: self._camera_id,
1086*b7c941bbSAndroid Build Coastguard Worker        'outputSurfaces': output_surface,
1087*b7c941bbSAndroid Build Coastguard Worker        'stabilize': stabilize,
1088*b7c941bbSAndroid Build Coastguard Worker        'ois': False
1089*b7c941bbSAndroid Build Coastguard Worker    }
1090*b7c941bbSAndroid Build Coastguard Worker    zoom_start, zoom_end, step_size, step_duration = sweep_zoom
1091*b7c941bbSAndroid Build Coastguard Worker    if (not self.zoom_ratio_within_range(zoom_start) or
1092*b7c941bbSAndroid Build Coastguard Worker        not self.zoom_ratio_within_range(zoom_end)):
1093*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError(
1094*b7c941bbSAndroid Build Coastguard Worker          f'Starting zoom ratio {zoom_start} or '
1095*b7c941bbSAndroid Build Coastguard Worker          f'ending zoom ratio {zoom_end} out of range'
1096*b7c941bbSAndroid Build Coastguard Worker      )
1097*b7c941bbSAndroid Build Coastguard Worker    if zoom_start > zoom_end or step_size < 0:
1098*b7c941bbSAndroid Build Coastguard Worker      raise NotImplementedError('Only increasing zoom ratios are supported')
1099*b7c941bbSAndroid Build Coastguard Worker    cmd['zoomStart'] = zoom_start
1100*b7c941bbSAndroid Build Coastguard Worker    cmd['zoomEnd'] = zoom_end
1101*b7c941bbSAndroid Build Coastguard Worker    cmd['stepSize'] = step_size
1102*b7c941bbSAndroid Build Coastguard Worker    cmd['stepDuration'] = step_duration
1103*b7c941bbSAndroid Build Coastguard Worker    cmd['hlg10Enabled'] = False
1104*b7c941bbSAndroid Build Coastguard Worker    cmd['paddedFrames'] = padded_frames
1105*b7c941bbSAndroid Build Coastguard Worker    if ae_target_fps_min and ae_target_fps_max:
1106*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMin'] = ae_target_fps_min
1107*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMax'] = ae_target_fps_max
1108*b7c941bbSAndroid Build Coastguard Worker    return self._execute_preview_recording(cmd)
1109*b7c941bbSAndroid Build Coastguard Worker
1110*b7c941bbSAndroid Build Coastguard Worker  def do_preview_recording_with_dynamic_ae_awb_region(
1111*b7c941bbSAndroid Build Coastguard Worker      self, video_size, ae_awb_regions, ae_awb_region_duration, stabilize=False,
1112*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min=None, ae_target_fps_max=None):
1113*b7c941bbSAndroid Build Coastguard Worker    """Issue a preview request with dynamic 3A region and read back output object.
1114*b7c941bbSAndroid Build Coastguard Worker
1115*b7c941bbSAndroid Build Coastguard Worker    The resolution of the preview and its recording will be determined by
1116*b7c941bbSAndroid Build Coastguard Worker    video_size. The recorded object consists of a path on the device at which
1117*b7c941bbSAndroid Build Coastguard Worker    the recorded video is saved.
1118*b7c941bbSAndroid Build Coastguard Worker
1119*b7c941bbSAndroid Build Coastguard Worker    Args:
1120*b7c941bbSAndroid Build Coastguard Worker      video_size: str; Preview resolution at which to record. ex. "1920x1080"
1121*b7c941bbSAndroid Build Coastguard Worker      ae_awb_regions: dictionary of (aeAwbRegionOne/Two/Three/Four).
1122*b7c941bbSAndroid Build Coastguard Worker        Used to control 3A region during recording.
1123*b7c941bbSAndroid Build Coastguard Worker        aeAwbRegionOne (metering rectangle) first ae/awb region of recording.
1124*b7c941bbSAndroid Build Coastguard Worker        aeAwbRegionTwo (metering rectangle) second ae/awb region of recording.
1125*b7c941bbSAndroid Build Coastguard Worker        aeAwbRegionThree (metering rectangle) third ae/awb region of recording.
1126*b7c941bbSAndroid Build Coastguard Worker        aeAwbRegionFour (metering rectangle) fourth ae/awb region of recording.
1127*b7c941bbSAndroid Build Coastguard Worker      ae_awb_region_duration: float; sleep in ms between 3A regions.
1128*b7c941bbSAndroid Build Coastguard Worker      stabilize: boolean; Whether the preview should be stabilized.
1129*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_min: int; If not none, set CONTROL_AE_TARGET_FPS_RANGE min.
1130*b7c941bbSAndroid Build Coastguard Worker      ae_target_fps_max: int; If not none, set CONTROL_AE_TARGET_FPS_RANGE max.
1131*b7c941bbSAndroid Build Coastguard Worker    Returns:
1132*b7c941bbSAndroid Build Coastguard Worker      video_recorded_object: The recorded object returned from ItsService.
1133*b7c941bbSAndroid Build Coastguard Worker    """
1134*b7c941bbSAndroid Build Coastguard Worker    output_surface = self.preview_surface(video_size)
1135*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1136*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'doDynamicMeteringRegionPreviewRecording',
1137*b7c941bbSAndroid Build Coastguard Worker        _CAMERA_ID_STR: self._camera_id,
1138*b7c941bbSAndroid Build Coastguard Worker        'outputSurfaces': output_surface,
1139*b7c941bbSAndroid Build Coastguard Worker        'stabilize': stabilize,
1140*b7c941bbSAndroid Build Coastguard Worker        'ois': False,
1141*b7c941bbSAndroid Build Coastguard Worker        'aeAwbRegionDuration': ae_awb_region_duration
1142*b7c941bbSAndroid Build Coastguard Worker    }
1143*b7c941bbSAndroid Build Coastguard Worker
1144*b7c941bbSAndroid Build Coastguard Worker    cmd['aeAwbRegionOne'] = ae_awb_regions['aeAwbRegionOne']
1145*b7c941bbSAndroid Build Coastguard Worker    cmd['aeAwbRegionTwo'] = ae_awb_regions['aeAwbRegionTwo']
1146*b7c941bbSAndroid Build Coastguard Worker    cmd['aeAwbRegionThree'] = ae_awb_regions['aeAwbRegionThree']
1147*b7c941bbSAndroid Build Coastguard Worker    cmd['aeAwbRegionFour'] = ae_awb_regions['aeAwbRegionFour']
1148*b7c941bbSAndroid Build Coastguard Worker    cmd['hlg10Enabled'] = False
1149*b7c941bbSAndroid Build Coastguard Worker    if ae_target_fps_min and ae_target_fps_max:
1150*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMin'] = ae_target_fps_min
1151*b7c941bbSAndroid Build Coastguard Worker      cmd['aeTargetFpsMax'] = ae_target_fps_max
1152*b7c941bbSAndroid Build Coastguard Worker    return self._execute_preview_recording(cmd)
1153*b7c941bbSAndroid Build Coastguard Worker
1154*b7c941bbSAndroid Build Coastguard Worker  def get_supported_video_qualities(self, camera_id):
1155*b7c941bbSAndroid Build Coastguard Worker    """Get all supported video qualities for this camera device.
1156*b7c941bbSAndroid Build Coastguard Worker
1157*b7c941bbSAndroid Build Coastguard Worker    ie. ['480:4', '1080:6', '2160:8', '720:5', 'CIF:3', 'HIGH:1', 'LOW:0',
1158*b7c941bbSAndroid Build Coastguard Worker         'QCIF:2', 'QVGA:7']
1159*b7c941bbSAndroid Build Coastguard Worker
1160*b7c941bbSAndroid Build Coastguard Worker    Args:
1161*b7c941bbSAndroid Build Coastguard Worker      camera_id: device id
1162*b7c941bbSAndroid Build Coastguard Worker    Returns:
1163*b7c941bbSAndroid Build Coastguard Worker      List of all supported video qualities and corresponding profileIds.
1164*b7c941bbSAndroid Build Coastguard Worker    """
1165*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
1166*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'getSupportedVideoQualities'
1167*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = camera_id
1168*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1169*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1170*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'supportedVideoQualities':
1171*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1172*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR].split(';')[:-1]  # remove the last appended ';'
1173*b7c941bbSAndroid Build Coastguard Worker
1174*b7c941bbSAndroid Build Coastguard Worker  def get_all_supported_preview_sizes(self, camera_id, filter_recordable=False):
1175*b7c941bbSAndroid Build Coastguard Worker    """Get all supported preview resolutions for this camera device.
1176*b7c941bbSAndroid Build Coastguard Worker
1177*b7c941bbSAndroid Build Coastguard Worker    ie. ['640x480', '800x600', '1280x720', '1440x1080', '1920x1080']
1178*b7c941bbSAndroid Build Coastguard Worker
1179*b7c941bbSAndroid Build Coastguard Worker    Note: resolutions are sorted by width x height in ascending order
1180*b7c941bbSAndroid Build Coastguard Worker
1181*b7c941bbSAndroid Build Coastguard Worker    Args:
1182*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device id
1183*b7c941bbSAndroid Build Coastguard Worker      filter_recordable: filter preview sizes if supported for video recording
1184*b7c941bbSAndroid Build Coastguard Worker                       using MediaRecorder
1185*b7c941bbSAndroid Build Coastguard Worker
1186*b7c941bbSAndroid Build Coastguard Worker    Returns:
1187*b7c941bbSAndroid Build Coastguard Worker      List of all supported preview resolutions in ascending order.
1188*b7c941bbSAndroid Build Coastguard Worker    """
1189*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1190*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'getSupportedPreviewSizes',
1191*b7c941bbSAndroid Build Coastguard Worker        _CAMERA_ID_STR: camera_id,
1192*b7c941bbSAndroid Build Coastguard Worker        'filter_recordable': filter_recordable,
1193*b7c941bbSAndroid Build Coastguard Worker    }
1194*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1195*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1196*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1197*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1198*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'supportedPreviewSizes':
1199*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1200*b7c941bbSAndroid Build Coastguard Worker    if not data[_STR_VALUE_STR]:
1201*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No supported preview sizes')
1202*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes = data[_STR_VALUE_STR].split(';')
1203*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Supported preview sizes: %s', supported_preview_sizes)
1204*b7c941bbSAndroid Build Coastguard Worker    return supported_preview_sizes
1205*b7c941bbSAndroid Build Coastguard Worker
1206*b7c941bbSAndroid Build Coastguard Worker  def get_supported_preview_sizes(self, camera_id):
1207*b7c941bbSAndroid Build Coastguard Worker    """Get supported preview resolutions for this camera device.
1208*b7c941bbSAndroid Build Coastguard Worker
1209*b7c941bbSAndroid Build Coastguard Worker    ie. ['640x480', '800x600', '1280x720', '1440x1080', '1920x1080']
1210*b7c941bbSAndroid Build Coastguard Worker
1211*b7c941bbSAndroid Build Coastguard Worker    Note: resolutions are sorted by width x height in ascending order
1212*b7c941bbSAndroid Build Coastguard Worker    Note: max resolution is capped at 1440x1920.
1213*b7c941bbSAndroid Build Coastguard Worker    Note: min resolution is capped at 320x240.
1214*b7c941bbSAndroid Build Coastguard Worker
1215*b7c941bbSAndroid Build Coastguard Worker    Args:
1216*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device id
1217*b7c941bbSAndroid Build Coastguard Worker
1218*b7c941bbSAndroid Build Coastguard Worker    Returns:
1219*b7c941bbSAndroid Build Coastguard Worker      List of all supported preview resolutions with floor & ceiling set
1220*b7c941bbSAndroid Build Coastguard Worker      by _CONSTANTS in ascending order.
1221*b7c941bbSAndroid Build Coastguard Worker    """
1222*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes = self.get_all_supported_preview_sizes(camera_id)
1223*b7c941bbSAndroid Build Coastguard Worker    resolution_to_area = lambda s: int(s.split('x')[0])*int(s.split('x')[1])
1224*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes = [size for size in supported_preview_sizes
1225*b7c941bbSAndroid Build Coastguard Worker                               if (resolution_to_area(size)
1226*b7c941bbSAndroid Build Coastguard Worker                                   <= PREVIEW_MAX_TESTED_AREA
1227*b7c941bbSAndroid Build Coastguard Worker                                   and resolution_to_area(size)
1228*b7c941bbSAndroid Build Coastguard Worker                                   >= PREVIEW_MIN_TESTED_AREA)]
1229*b7c941bbSAndroid Build Coastguard Worker    logging.debug(
1230*b7c941bbSAndroid Build Coastguard Worker        'Supported preview sizes (MIN: %d, MAX: %d area in pixels): %s',
1231*b7c941bbSAndroid Build Coastguard Worker        PREVIEW_MIN_TESTED_AREA, PREVIEW_MAX_TESTED_AREA,
1232*b7c941bbSAndroid Build Coastguard Worker        supported_preview_sizes
1233*b7c941bbSAndroid Build Coastguard Worker    )
1234*b7c941bbSAndroid Build Coastguard Worker    return supported_preview_sizes
1235*b7c941bbSAndroid Build Coastguard Worker
1236*b7c941bbSAndroid Build Coastguard Worker  def get_supported_extension_preview_sizes(self, camera_id, extension):
1237*b7c941bbSAndroid Build Coastguard Worker    """Get all supported preview resolutions for the extension mode.
1238*b7c941bbSAndroid Build Coastguard Worker
1239*b7c941bbSAndroid Build Coastguard Worker    ie. ['640x480', '800x600', '1280x720', '1440x1080', '1920x1080']
1240*b7c941bbSAndroid Build Coastguard Worker
1241*b7c941bbSAndroid Build Coastguard Worker    Note: resolutions are sorted by width x height in ascending order
1242*b7c941bbSAndroid Build Coastguard Worker
1243*b7c941bbSAndroid Build Coastguard Worker    Args:
1244*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device id
1245*b7c941bbSAndroid Build Coastguard Worker      extension: int; camera extension mode
1246*b7c941bbSAndroid Build Coastguard Worker
1247*b7c941bbSAndroid Build Coastguard Worker    Returns:
1248*b7c941bbSAndroid Build Coastguard Worker      List of all supported camera extension preview resolutions in
1249*b7c941bbSAndroid Build Coastguard Worker      ascending order.
1250*b7c941bbSAndroid Build Coastguard Worker    """
1251*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1252*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'getSupportedExtensionPreviewSizes',
1253*b7c941bbSAndroid Build Coastguard Worker        _CAMERA_ID_STR: camera_id,
1254*b7c941bbSAndroid Build Coastguard Worker        "extension": extension  # pylint: disable=g-inconsistent-quotes
1255*b7c941bbSAndroid Build Coastguard Worker    }
1256*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1257*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1258*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1259*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1260*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'supportedExtensionPreviewSizes':
1261*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1262*b7c941bbSAndroid Build Coastguard Worker    if not data[_STR_VALUE_STR]:
1263*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No supported extension preview sizes')
1264*b7c941bbSAndroid Build Coastguard Worker    supported_preview_sizes = data[_STR_VALUE_STR].split(';')
1265*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Supported extension preview sizes: %s', supported_preview_sizes)
1266*b7c941bbSAndroid Build Coastguard Worker    return supported_preview_sizes
1267*b7c941bbSAndroid Build Coastguard Worker
1268*b7c941bbSAndroid Build Coastguard Worker  def get_queryable_stream_combinations(self):
1269*b7c941bbSAndroid Build Coastguard Worker    """Get all queryable stream combinations for this camera device.
1270*b7c941bbSAndroid Build Coastguard Worker
1271*b7c941bbSAndroid Build Coastguard Worker    This function parses the queryable stream combinations string
1272*b7c941bbSAndroid Build Coastguard Worker    returned from ItsService. The return value includes both the
1273*b7c941bbSAndroid Build Coastguard Worker    string and the parsed result.
1274*b7c941bbSAndroid Build Coastguard Worker
1275*b7c941bbSAndroid Build Coastguard Worker    One example of the queryable stream combination string is:
1276*b7c941bbSAndroid Build Coastguard Worker
1277*b7c941bbSAndroid Build Coastguard Worker    'priv:1920x1080+jpeg:4032x2268;priv:1280x720+priv:1280x720'
1278*b7c941bbSAndroid Build Coastguard Worker
1279*b7c941bbSAndroid Build Coastguard Worker    which can be parsed to:
1280*b7c941bbSAndroid Build Coastguard Worker
1281*b7c941bbSAndroid Build Coastguard Worker    [
1282*b7c941bbSAndroid Build Coastguard Worker      {
1283*b7c941bbSAndroid Build Coastguard Worker       "name": "priv:1920x1080+jpeg:4032x2268",
1284*b7c941bbSAndroid Build Coastguard Worker       "combination": [
1285*b7c941bbSAndroid Build Coastguard Worker                        {
1286*b7c941bbSAndroid Build Coastguard Worker                         "format": "priv",
1287*b7c941bbSAndroid Build Coastguard Worker                         "size": "1920x1080"
1288*b7c941bbSAndroid Build Coastguard Worker                        }
1289*b7c941bbSAndroid Build Coastguard Worker                        {
1290*b7c941bbSAndroid Build Coastguard Worker                         "format": "jpeg",
1291*b7c941bbSAndroid Build Coastguard Worker                         "size": "4032x2268"
1292*b7c941bbSAndroid Build Coastguard Worker                        }
1293*b7c941bbSAndroid Build Coastguard Worker                      ]
1294*b7c941bbSAndroid Build Coastguard Worker      }
1295*b7c941bbSAndroid Build Coastguard Worker      {
1296*b7c941bbSAndroid Build Coastguard Worker       "name": "priv:1280x720+priv:1280x720",
1297*b7c941bbSAndroid Build Coastguard Worker       "combination": [
1298*b7c941bbSAndroid Build Coastguard Worker                        {
1299*b7c941bbSAndroid Build Coastguard Worker                         "format": "priv",
1300*b7c941bbSAndroid Build Coastguard Worker                         "size": "1280x720"
1301*b7c941bbSAndroid Build Coastguard Worker                        },
1302*b7c941bbSAndroid Build Coastguard Worker                        {
1303*b7c941bbSAndroid Build Coastguard Worker                         "format": "priv",
1304*b7c941bbSAndroid Build Coastguard Worker                         "size": "1280x720"
1305*b7c941bbSAndroid Build Coastguard Worker                        }
1306*b7c941bbSAndroid Build Coastguard Worker                      ]
1307*b7c941bbSAndroid Build Coastguard Worker      }
1308*b7c941bbSAndroid Build Coastguard Worker    ]
1309*b7c941bbSAndroid Build Coastguard Worker
1310*b7c941bbSAndroid Build Coastguard Worker    Returns:
1311*b7c941bbSAndroid Build Coastguard Worker      Tuple of:
1312*b7c941bbSAndroid Build Coastguard Worker      - queryable stream combination string, and
1313*b7c941bbSAndroid Build Coastguard Worker      - parsed stream combinations
1314*b7c941bbSAndroid Build Coastguard Worker    """
1315*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1316*b7c941bbSAndroid Build Coastguard Worker        _CMD_NAME_STR: 'getQueryableStreamCombinations',
1317*b7c941bbSAndroid Build Coastguard Worker    }
1318*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1319*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1320*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1321*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1322*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'queryableStreamCombinations':
1323*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1324*b7c941bbSAndroid Build Coastguard Worker    if not data[_STR_VALUE_STR]:
1325*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No queryable stream combinations')
1326*b7c941bbSAndroid Build Coastguard Worker
1327*b7c941bbSAndroid Build Coastguard Worker    # Parse the stream combination string
1328*b7c941bbSAndroid Build Coastguard Worker    combinations = [{
1329*b7c941bbSAndroid Build Coastguard Worker        'name': c, 'combination': [
1330*b7c941bbSAndroid Build Coastguard Worker            {'format': s.split(':')[0],
1331*b7c941bbSAndroid Build Coastguard Worker             'size': s.split(':')[1]} for s in c.split('+')]}
1332*b7c941bbSAndroid Build Coastguard Worker                    for c in data[_STR_VALUE_STR].split(';')]
1333*b7c941bbSAndroid Build Coastguard Worker
1334*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR], combinations
1335*b7c941bbSAndroid Build Coastguard Worker
1336*b7c941bbSAndroid Build Coastguard Worker  def get_supported_extensions(self, camera_id):
1337*b7c941bbSAndroid Build Coastguard Worker    """Get all supported camera extensions for this camera device.
1338*b7c941bbSAndroid Build Coastguard Worker
1339*b7c941bbSAndroid Build Coastguard Worker    ie. [EXTENSION_AUTOMATIC, EXTENSION_BOKEH,
1340*b7c941bbSAndroid Build Coastguard Worker         EXTENSION_FACE_RETOUCH, EXTENSION_HDR, EXTENSION_NIGHT]
1341*b7c941bbSAndroid Build Coastguard Worker    where EXTENSION_AUTOMATIC is 0, EXTENSION_BOKEH is 1, etc.
1342*b7c941bbSAndroid Build Coastguard Worker
1343*b7c941bbSAndroid Build Coastguard Worker    Args:
1344*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device ID
1345*b7c941bbSAndroid Build Coastguard Worker    Returns:
1346*b7c941bbSAndroid Build Coastguard Worker      List of all supported extensions (as int) in ascending order.
1347*b7c941bbSAndroid Build Coastguard Worker    """
1348*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1349*b7c941bbSAndroid Build Coastguard Worker        'cmdName': 'getSupportedExtensions',
1350*b7c941bbSAndroid Build Coastguard Worker        'cameraId': camera_id
1351*b7c941bbSAndroid Build Coastguard Worker    }
1352*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1353*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1354*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1355*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1356*b7c941bbSAndroid Build Coastguard Worker    if data['tag'] != 'supportedExtensions':
1357*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1358*b7c941bbSAndroid Build Coastguard Worker    if not data['strValue']:
1359*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No supported extensions')
1360*b7c941bbSAndroid Build Coastguard Worker    return [int(x) for x in str(data['strValue'][1:-1]).split(', ') if x]
1361*b7c941bbSAndroid Build Coastguard Worker
1362*b7c941bbSAndroid Build Coastguard Worker  def get_supported_extension_sizes(self, camera_id, extension, image_format):
1363*b7c941bbSAndroid Build Coastguard Worker    """Get all supported camera sizes for this camera, extension, and format.
1364*b7c941bbSAndroid Build Coastguard Worker
1365*b7c941bbSAndroid Build Coastguard Worker    Sorts in ascending order according to area, i.e.
1366*b7c941bbSAndroid Build Coastguard Worker    ['640x480', '800x600', '1280x720', '1440x1080', '1920x1080']
1367*b7c941bbSAndroid Build Coastguard Worker
1368*b7c941bbSAndroid Build Coastguard Worker    Args:
1369*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device ID
1370*b7c941bbSAndroid Build Coastguard Worker      extension: int; the integer value of the extension.
1371*b7c941bbSAndroid Build Coastguard Worker      image_format: int; the integer value of the format.
1372*b7c941bbSAndroid Build Coastguard Worker    Returns:
1373*b7c941bbSAndroid Build Coastguard Worker      List of sizes supported for this camera, extension, and format.
1374*b7c941bbSAndroid Build Coastguard Worker    """
1375*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1376*b7c941bbSAndroid Build Coastguard Worker        'cmdName': 'getSupportedExtensionSizes',
1377*b7c941bbSAndroid Build Coastguard Worker        'cameraId': camera_id,
1378*b7c941bbSAndroid Build Coastguard Worker        'extension': extension,
1379*b7c941bbSAndroid Build Coastguard Worker        'format': image_format
1380*b7c941bbSAndroid Build Coastguard Worker    }
1381*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1382*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1383*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1384*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1385*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'supportedExtensionSizes':
1386*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1387*b7c941bbSAndroid Build Coastguard Worker    if not data[_STR_VALUE_STR]:
1388*b7c941bbSAndroid Build Coastguard Worker      logging.debug('No supported extension sizes')
1389*b7c941bbSAndroid Build Coastguard Worker      return ''
1390*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR].split(';')
1391*b7c941bbSAndroid Build Coastguard Worker
1392*b7c941bbSAndroid Build Coastguard Worker  def get_display_size(self):
1393*b7c941bbSAndroid Build Coastguard Worker    """Get the display size of the screen.
1394*b7c941bbSAndroid Build Coastguard Worker
1395*b7c941bbSAndroid Build Coastguard Worker    Returns:
1396*b7c941bbSAndroid Build Coastguard Worker      The size of the display resolution in pixels.
1397*b7c941bbSAndroid Build Coastguard Worker    """
1398*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1399*b7c941bbSAndroid Build Coastguard Worker        'cmdName': 'getDisplaySize'
1400*b7c941bbSAndroid Build Coastguard Worker    }
1401*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1402*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1403*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1404*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1405*b7c941bbSAndroid Build Coastguard Worker    if data['tag'] != 'displaySize':
1406*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1407*b7c941bbSAndroid Build Coastguard Worker    if not data['strValue']:
1408*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No display size')
1409*b7c941bbSAndroid Build Coastguard Worker    return data['strValue'].split('x')
1410*b7c941bbSAndroid Build Coastguard Worker
1411*b7c941bbSAndroid Build Coastguard Worker  def get_max_camcorder_profile_size(self, camera_id):
1412*b7c941bbSAndroid Build Coastguard Worker    """Get the maximum camcorder profile size for this camera device.
1413*b7c941bbSAndroid Build Coastguard Worker
1414*b7c941bbSAndroid Build Coastguard Worker    Args:
1415*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device id
1416*b7c941bbSAndroid Build Coastguard Worker    Returns:
1417*b7c941bbSAndroid Build Coastguard Worker      The maximum size among all camcorder profiles supported by this camera.
1418*b7c941bbSAndroid Build Coastguard Worker    """
1419*b7c941bbSAndroid Build Coastguard Worker    cmd = {
1420*b7c941bbSAndroid Build Coastguard Worker        'cmdName': 'getMaxCamcorderProfileSize',
1421*b7c941bbSAndroid Build Coastguard Worker        'cameraId': camera_id
1422*b7c941bbSAndroid Build Coastguard Worker    }
1423*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1424*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
1425*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
1426*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
1427*b7c941bbSAndroid Build Coastguard Worker    if data['tag'] != 'maxCamcorderProfileSize':
1428*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
1429*b7c941bbSAndroid Build Coastguard Worker    if not data['strValue']:
1430*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('No max camcorder profile size')
1431*b7c941bbSAndroid Build Coastguard Worker    return data['strValue'].split('x')
1432*b7c941bbSAndroid Build Coastguard Worker
1433*b7c941bbSAndroid Build Coastguard Worker  def do_simple_capture(self, cmd, out_surface):
1434*b7c941bbSAndroid Build Coastguard Worker    """Issue single capture request via command and read back image/metadata.
1435*b7c941bbSAndroid Build Coastguard Worker
1436*b7c941bbSAndroid Build Coastguard Worker    Args:
1437*b7c941bbSAndroid Build Coastguard Worker      cmd: Dictionary specifying command name, requests, and output surface.
1438*b7c941bbSAndroid Build Coastguard Worker      out_surface: Dictionary describing output surface.
1439*b7c941bbSAndroid Build Coastguard Worker    Returns:
1440*b7c941bbSAndroid Build Coastguard Worker      An object which contains following fields:
1441*b7c941bbSAndroid Build Coastguard Worker      * data: the image data as a numpy array of bytes.
1442*b7c941bbSAndroid Build Coastguard Worker      * width: the width of the captured image.
1443*b7c941bbSAndroid Build Coastguard Worker      * height: the height of the captured image.
1444*b7c941bbSAndroid Build Coastguard Worker      * format: image format
1445*b7c941bbSAndroid Build Coastguard Worker      * metadata: the capture result object
1446*b7c941bbSAndroid Build Coastguard Worker    """
1447*b7c941bbSAndroid Build Coastguard Worker    fmt = out_surface['format'] if 'format' in out_surface else 'yuv'
1448*b7c941bbSAndroid Build Coastguard Worker    if fmt == 'jpg': fmt = 'jpeg'
1449*b7c941bbSAndroid Build Coastguard Worker
1450*b7c941bbSAndroid Build Coastguard Worker    # we only have 1 capture request and 1 surface by definition.
1451*b7c941bbSAndroid Build Coastguard Worker    ncap = SINGLE_CAPTURE_NCAP
1452*b7c941bbSAndroid Build Coastguard Worker
1453*b7c941bbSAndroid Build Coastguard Worker    cam_id = None
1454*b7c941bbSAndroid Build Coastguard Worker    bufs = {}
1455*b7c941bbSAndroid Build Coastguard Worker    yuv_bufs = {}
1456*b7c941bbSAndroid Build Coastguard Worker    if self._hidden_physical_id:
1457*b7c941bbSAndroid Build Coastguard Worker      out_surface['physicalCamera'] = self._hidden_physical_id
1458*b7c941bbSAndroid Build Coastguard Worker
1459*b7c941bbSAndroid Build Coastguard Worker    if 'physicalCamera' in out_surface:
1460*b7c941bbSAndroid Build Coastguard Worker      cam_id = out_surface['physicalCamera']
1461*b7c941bbSAndroid Build Coastguard Worker    else:
1462*b7c941bbSAndroid Build Coastguard Worker      cam_id = self._camera_id
1463*b7c941bbSAndroid Build Coastguard Worker
1464*b7c941bbSAndroid Build Coastguard Worker    bufs[cam_id] = {
1465*b7c941bbSAndroid Build Coastguard Worker        'raw': [],
1466*b7c941bbSAndroid Build Coastguard Worker        'raw10': [],
1467*b7c941bbSAndroid Build Coastguard Worker        'raw12': [],
1468*b7c941bbSAndroid Build Coastguard Worker        'rawStats': [],
1469*b7c941bbSAndroid Build Coastguard Worker        'dng': [],
1470*b7c941bbSAndroid Build Coastguard Worker        'jpeg': [],
1471*b7c941bbSAndroid Build Coastguard Worker        'y8': [],
1472*b7c941bbSAndroid Build Coastguard Worker        'rawQuadBayer': [],
1473*b7c941bbSAndroid Build Coastguard Worker        'rawQuadBayerStats': [],
1474*b7c941bbSAndroid Build Coastguard Worker        'raw10Stats': [],
1475*b7c941bbSAndroid Build Coastguard Worker        'raw10QuadBayerStats': [],
1476*b7c941bbSAndroid Build Coastguard Worker        'raw10QuadBayer': [],
1477*b7c941bbSAndroid Build Coastguard Worker    }
1478*b7c941bbSAndroid Build Coastguard Worker
1479*b7c941bbSAndroid Build Coastguard Worker    # Only allow yuv output to multiple targets
1480*b7c941bbSAndroid Build Coastguard Worker    yuv_surface = None
1481*b7c941bbSAndroid Build Coastguard Worker    if cam_id == self._camera_id:
1482*b7c941bbSAndroid Build Coastguard Worker      if 'physicalCamera' not in out_surface:
1483*b7c941bbSAndroid Build Coastguard Worker        if out_surface['format'] == 'yuv':
1484*b7c941bbSAndroid Build Coastguard Worker          yuv_surface = out_surface
1485*b7c941bbSAndroid Build Coastguard Worker    else:
1486*b7c941bbSAndroid Build Coastguard Worker      if ('physicalCamera' in out_surface and
1487*b7c941bbSAndroid Build Coastguard Worker          out_surface['physicalCamera'] == cam_id):
1488*b7c941bbSAndroid Build Coastguard Worker        if out_surface['format'] == 'yuv':
1489*b7c941bbSAndroid Build Coastguard Worker          yuv_surface = out_surface
1490*b7c941bbSAndroid Build Coastguard Worker
1491*b7c941bbSAndroid Build Coastguard Worker    # Compute the buffer size of YUV targets
1492*b7c941bbSAndroid Build Coastguard Worker    yuv_maxsize_1d = 0
1493*b7c941bbSAndroid Build Coastguard Worker    if yuv_surface is not None:
1494*b7c941bbSAndroid Build Coastguard Worker      if ('width' not in yuv_surface and 'height' not in yuv_surface):
1495*b7c941bbSAndroid Build Coastguard Worker        if self.props is None:
1496*b7c941bbSAndroid Build Coastguard Worker          raise error_util.CameraItsError('Camera props are unavailable')
1497*b7c941bbSAndroid Build Coastguard Worker        yuv_maxsize_2d = capture_request_utils.get_available_output_sizes(
1498*b7c941bbSAndroid Build Coastguard Worker            'yuv', self.props)[0]
1499*b7c941bbSAndroid Build Coastguard Worker        # YUV420 size = 1.5 bytes per pixel
1500*b7c941bbSAndroid Build Coastguard Worker        yuv_maxsize_1d = (yuv_maxsize_2d[0] * yuv_maxsize_2d[1] * 3) // 2
1501*b7c941bbSAndroid Build Coastguard Worker      if 'width' in yuv_surface and 'height' in yuv_surface:
1502*b7c941bbSAndroid Build Coastguard Worker        yuv_size = (yuv_surface['width'] * yuv_surface['height'] * 3) // 2
1503*b7c941bbSAndroid Build Coastguard Worker      else:
1504*b7c941bbSAndroid Build Coastguard Worker        yuv_size = yuv_maxsize_1d
1505*b7c941bbSAndroid Build Coastguard Worker
1506*b7c941bbSAndroid Build Coastguard Worker      yuv_bufs[cam_id] = {yuv_size: []}
1507*b7c941bbSAndroid Build Coastguard Worker
1508*b7c941bbSAndroid Build Coastguard Worker    cam_ids = self._camera_id
1509*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT)
1510*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
1511*b7c941bbSAndroid Build Coastguard Worker
1512*b7c941bbSAndroid Build Coastguard Worker    nbufs = 0
1513*b7c941bbSAndroid Build Coastguard Worker    md = None
1514*b7c941bbSAndroid Build Coastguard Worker    physical_md = None
1515*b7c941bbSAndroid Build Coastguard Worker    width = None
1516*b7c941bbSAndroid Build Coastguard Worker    height = None
1517*b7c941bbSAndroid Build Coastguard Worker    capture_results_returned = False
1518*b7c941bbSAndroid Build Coastguard Worker    while (nbufs < ncap) or (not capture_results_returned):
1519*b7c941bbSAndroid Build Coastguard Worker      json_obj, buf = self.__read_response_from_socket()
1520*b7c941bbSAndroid Build Coastguard Worker      if (json_obj[_TAG_STR] in ItsSession.IMAGE_FORMAT_LIST_1 and
1521*b7c941bbSAndroid Build Coastguard Worker          buf is not None):
1522*b7c941bbSAndroid Build Coastguard Worker        fmt = json_obj[_TAG_STR][:-5]
1523*b7c941bbSAndroid Build Coastguard Worker        bufs[self._camera_id][fmt].append(buf)
1524*b7c941bbSAndroid Build Coastguard Worker        nbufs += 1
1525*b7c941bbSAndroid Build Coastguard Worker      elif json_obj[_TAG_STR] == 'yuvImage':
1526*b7c941bbSAndroid Build Coastguard Worker        buf_size = get_array_size(buf)
1527*b7c941bbSAndroid Build Coastguard Worker        yuv_bufs[self._camera_id][buf_size].append(buf)
1528*b7c941bbSAndroid Build Coastguard Worker        nbufs += 1
1529*b7c941bbSAndroid Build Coastguard Worker      elif json_obj[_TAG_STR] == 'captureResults':
1530*b7c941bbSAndroid Build Coastguard Worker        capture_results_returned = True
1531*b7c941bbSAndroid Build Coastguard Worker        md = json_obj[_OBJ_VALUE_STR]['captureResult']
1532*b7c941bbSAndroid Build Coastguard Worker        physical_md = json_obj[_OBJ_VALUE_STR]['physicalResults']
1533*b7c941bbSAndroid Build Coastguard Worker        outputs = json_obj[_OBJ_VALUE_STR]['outputs']
1534*b7c941bbSAndroid Build Coastguard Worker        returned_fmt = outputs[0]['format']
1535*b7c941bbSAndroid Build Coastguard Worker        if fmt != returned_fmt:
1536*b7c941bbSAndroid Build Coastguard Worker          raise AssertionError(
1537*b7c941bbSAndroid Build Coastguard Worker              f'Incorrect format. Requested: {fmt}, '
1538*b7c941bbSAndroid Build Coastguard Worker              f'Received: {returned_fmt}')
1539*b7c941bbSAndroid Build Coastguard Worker        width = outputs[0]['width']
1540*b7c941bbSAndroid Build Coastguard Worker        height = outputs[0]['height']
1541*b7c941bbSAndroid Build Coastguard Worker        requested_width = out_surface['width']
1542*b7c941bbSAndroid Build Coastguard Worker        requested_height = out_surface['height']
1543*b7c941bbSAndroid Build Coastguard Worker        if requested_width != width or requested_height != height:
1544*b7c941bbSAndroid Build Coastguard Worker          raise AssertionError(
1545*b7c941bbSAndroid Build Coastguard Worker              'Incorrect size. '
1546*b7c941bbSAndroid Build Coastguard Worker              f'Requested: {requested_width}x{requested_height}, '
1547*b7c941bbSAndroid Build Coastguard Worker              f'Received: {width}x{height}')
1548*b7c941bbSAndroid Build Coastguard Worker      else:
1549*b7c941bbSAndroid Build Coastguard Worker        tag_string = unicodedata.normalize('NFKD', json_obj[_TAG_STR]).encode(
1550*b7c941bbSAndroid Build Coastguard Worker            'ascii', 'ignore')
1551*b7c941bbSAndroid Build Coastguard Worker        for x in ItsSession.IMAGE_FORMAT_LIST_2:
1552*b7c941bbSAndroid Build Coastguard Worker          x = bytes(x, encoding='utf-8')
1553*b7c941bbSAndroid Build Coastguard Worker          if tag_string.startswith(x):
1554*b7c941bbSAndroid Build Coastguard Worker            if x == b'yuvImage':
1555*b7c941bbSAndroid Build Coastguard Worker              physical_id = json_obj[_TAG_STR][len(x):]
1556*b7c941bbSAndroid Build Coastguard Worker              if physical_id in cam_ids:
1557*b7c941bbSAndroid Build Coastguard Worker                buf_size = get_array_size(buf)
1558*b7c941bbSAndroid Build Coastguard Worker                yuv_bufs[physical_id][buf_size].append(buf)
1559*b7c941bbSAndroid Build Coastguard Worker                nbufs += 1
1560*b7c941bbSAndroid Build Coastguard Worker            else:
1561*b7c941bbSAndroid Build Coastguard Worker              physical_id = json_obj[_TAG_STR][len(x):]
1562*b7c941bbSAndroid Build Coastguard Worker              if physical_id in cam_ids:
1563*b7c941bbSAndroid Build Coastguard Worker                fmt = x[:-5].decode('UTF-8')
1564*b7c941bbSAndroid Build Coastguard Worker                bufs[physical_id][fmt].append(buf)
1565*b7c941bbSAndroid Build Coastguard Worker                nbufs += 1
1566*b7c941bbSAndroid Build Coastguard Worker
1567*b7c941bbSAndroid Build Coastguard Worker    if 'physicalCamera' in out_surface:
1568*b7c941bbSAndroid Build Coastguard Worker      cam_id = out_surface['physicalCamera']
1569*b7c941bbSAndroid Build Coastguard Worker    else:
1570*b7c941bbSAndroid Build Coastguard Worker      cam_id = self._camera_id
1571*b7c941bbSAndroid Build Coastguard Worker    ret = {'width': width, 'height': height, 'format': fmt}
1572*b7c941bbSAndroid Build Coastguard Worker    if cam_id == self._camera_id:
1573*b7c941bbSAndroid Build Coastguard Worker      ret['metadata'] = md
1574*b7c941bbSAndroid Build Coastguard Worker    else:
1575*b7c941bbSAndroid Build Coastguard Worker      if cam_id in physical_md:
1576*b7c941bbSAndroid Build Coastguard Worker        ret['metadata'] = physical_md[cam_id]
1577*b7c941bbSAndroid Build Coastguard Worker
1578*b7c941bbSAndroid Build Coastguard Worker    if fmt == 'yuv':
1579*b7c941bbSAndroid Build Coastguard Worker      buf_size = (width * height * 3) // 2
1580*b7c941bbSAndroid Build Coastguard Worker      ret['data'] = yuv_bufs[cam_id][buf_size][0]
1581*b7c941bbSAndroid Build Coastguard Worker    else:
1582*b7c941bbSAndroid Build Coastguard Worker      ret['data'] = bufs[cam_id][fmt][0]
1583*b7c941bbSAndroid Build Coastguard Worker
1584*b7c941bbSAndroid Build Coastguard Worker    return ret
1585*b7c941bbSAndroid Build Coastguard Worker
1586*b7c941bbSAndroid Build Coastguard Worker  def do_jca_capture(self, dut, log_path, flash, facing):
1587*b7c941bbSAndroid Build Coastguard Worker    """Take a capture using JCA, modifying capture settings using the UI.
1588*b7c941bbSAndroid Build Coastguard Worker
1589*b7c941bbSAndroid Build Coastguard Worker    Selects UI elements to modify settings, and presses the capture button.
1590*b7c941bbSAndroid Build Coastguard Worker    Reads response from socket containing the capture path, and
1591*b7c941bbSAndroid Build Coastguard Worker    pulls the image from the DUT.
1592*b7c941bbSAndroid Build Coastguard Worker
1593*b7c941bbSAndroid Build Coastguard Worker    This method is included here because an ITS session is needed to retrieve
1594*b7c941bbSAndroid Build Coastguard Worker    the capture path from the device.
1595*b7c941bbSAndroid Build Coastguard Worker
1596*b7c941bbSAndroid Build Coastguard Worker    Args:
1597*b7c941bbSAndroid Build Coastguard Worker      dut: An Android controller device object.
1598*b7c941bbSAndroid Build Coastguard Worker      log_path: str; log path to save screenshots.
1599*b7c941bbSAndroid Build Coastguard Worker      flash: str; constant describing the desired flash mode.
1600*b7c941bbSAndroid Build Coastguard Worker        Acceptable values: 'OFF' and 'AUTO'.
1601*b7c941bbSAndroid Build Coastguard Worker      facing: str; constant describing the direction the camera lens faces.
1602*b7c941bbSAndroid Build Coastguard Worker        Acceptable values: camera_properties_utils.LENS_FACING[BACK, FRONT]
1603*b7c941bbSAndroid Build Coastguard Worker    Returns:
1604*b7c941bbSAndroid Build Coastguard Worker      The host-side path of the capture.
1605*b7c941bbSAndroid Build Coastguard Worker    """
1606*b7c941bbSAndroid Build Coastguard Worker    ui_interaction_utils.open_jca_viewfinder(dut, log_path)
1607*b7c941bbSAndroid Build Coastguard Worker    ui_interaction_utils.switch_jca_camera(dut, log_path, facing)
1608*b7c941bbSAndroid Build Coastguard Worker    # Bring up settings, switch flash mode, and close settings
1609*b7c941bbSAndroid Build Coastguard Worker    dut.ui(res=ui_interaction_utils.QUICK_SETTINGS_RESOURCE_ID).click()
1610*b7c941bbSAndroid Build Coastguard Worker    if flash not in ui_interaction_utils.FLASH_MODE_TO_CLICKS:
1611*b7c941bbSAndroid Build Coastguard Worker      raise ValueError(f'Flash mode {flash} not supported')
1612*b7c941bbSAndroid Build Coastguard Worker    for _ in range(ui_interaction_utils.FLASH_MODE_TO_CLICKS[flash]):
1613*b7c941bbSAndroid Build Coastguard Worker      dut.ui(res=ui_interaction_utils.QUICK_SET_FLASH_RESOURCE_ID).click()
1614*b7c941bbSAndroid Build Coastguard Worker    dut.take_screenshot(log_path, prefix='flash_mode_set')
1615*b7c941bbSAndroid Build Coastguard Worker    dut.ui(res=ui_interaction_utils.QUICK_SETTINGS_RESOURCE_ID).click()
1616*b7c941bbSAndroid Build Coastguard Worker    # Take capture
1617*b7c941bbSAndroid Build Coastguard Worker    dut.ui(res=ui_interaction_utils.CAPTURE_BUTTON_RESOURCE_ID).click()
1618*b7c941bbSAndroid Build Coastguard Worker    return self.get_and_pull_jca_capture(dut, log_path)
1619*b7c941bbSAndroid Build Coastguard Worker
1620*b7c941bbSAndroid Build Coastguard Worker  def do_jca_video_capture(self, dut, log_path, duration):
1621*b7c941bbSAndroid Build Coastguard Worker    """Take a capture using JCA using the UI.
1622*b7c941bbSAndroid Build Coastguard Worker
1623*b7c941bbSAndroid Build Coastguard Worker    Captures JCA video by holding the capture button with requested duration.
1624*b7c941bbSAndroid Build Coastguard Worker    Reads response from socket containing the capture path, and
1625*b7c941bbSAndroid Build Coastguard Worker    pulls the image from the DUT.
1626*b7c941bbSAndroid Build Coastguard Worker
1627*b7c941bbSAndroid Build Coastguard Worker    This method is included here because an ITS session is needed to retrieve
1628*b7c941bbSAndroid Build Coastguard Worker    the capture path from the device.
1629*b7c941bbSAndroid Build Coastguard Worker
1630*b7c941bbSAndroid Build Coastguard Worker    Args:
1631*b7c941bbSAndroid Build Coastguard Worker      dut: An Android controller device object.
1632*b7c941bbSAndroid Build Coastguard Worker      log_path: str; log path to save screenshots.
1633*b7c941bbSAndroid Build Coastguard Worker      duration: int; requested video duration, in ms.
1634*b7c941bbSAndroid Build Coastguard Worker    Returns:
1635*b7c941bbSAndroid Build Coastguard Worker      The host-side path of the capture.
1636*b7c941bbSAndroid Build Coastguard Worker    """
1637*b7c941bbSAndroid Build Coastguard Worker    # Make sure JCA is started
1638*b7c941bbSAndroid Build Coastguard Worker    jca_capture_button_visible = dut.ui(
1639*b7c941bbSAndroid Build Coastguard Worker        res=ui_interaction_utils.CAPTURE_BUTTON_RESOURCE_ID).wait.exists(
1640*b7c941bbSAndroid Build Coastguard Worker            ui_interaction_utils.UI_OBJECT_WAIT_TIME_SECONDS)
1641*b7c941bbSAndroid Build Coastguard Worker    if not jca_capture_button_visible:
1642*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError('JCA was not started! Please use'
1643*b7c941bbSAndroid Build Coastguard Worker                           'open_jca_viewfinder() or do_jca_video_setup()'
1644*b7c941bbSAndroid Build Coastguard Worker                           'in ui_interaction_utils.py to start JCA.')
1645*b7c941bbSAndroid Build Coastguard Worker    dut.ui(res=ui_interaction_utils.CAPTURE_BUTTON_RESOURCE_ID).click(duration)
1646*b7c941bbSAndroid Build Coastguard Worker    return self.get_and_pull_jca_capture(dut, log_path)
1647*b7c941bbSAndroid Build Coastguard Worker
1648*b7c941bbSAndroid Build Coastguard Worker  def get_and_pull_jca_capture(self, dut, log_path):
1649*b7c941bbSAndroid Build Coastguard Worker    """Retrieve a capture path from the socket and pulls capture to host.
1650*b7c941bbSAndroid Build Coastguard Worker
1651*b7c941bbSAndroid Build Coastguard Worker    Args:
1652*b7c941bbSAndroid Build Coastguard Worker      dut: An Android controller device object.
1653*b7c941bbSAndroid Build Coastguard Worker      log_path: str; log path to save screenshots.
1654*b7c941bbSAndroid Build Coastguard Worker    Returns:
1655*b7c941bbSAndroid Build Coastguard Worker      The host-side path of the capture.
1656*b7c941bbSAndroid Build Coastguard Worker    Raises:
1657*b7c941bbSAndroid Build Coastguard Worker      CameraItsError: If unexpected data is retrieved from the socket.
1658*b7c941bbSAndroid Build Coastguard Worker    """
1659*b7c941bbSAndroid Build Coastguard Worker    capture_path, capture_status = None, None
1660*b7c941bbSAndroid Build Coastguard Worker    while not capture_path or not capture_status:
1661*b7c941bbSAndroid Build Coastguard Worker      data, _ = self.__read_response_from_socket()
1662*b7c941bbSAndroid Build Coastguard Worker      if data[_TAG_STR] == JCA_CAPTURE_PATH_TAG:
1663*b7c941bbSAndroid Build Coastguard Worker        capture_path = data[_STR_VALUE_STR]
1664*b7c941bbSAndroid Build Coastguard Worker      elif data[_TAG_STR] == JCA_CAPTURE_STATUS_TAG:
1665*b7c941bbSAndroid Build Coastguard Worker        capture_status = data[_STR_VALUE_STR]
1666*b7c941bbSAndroid Build Coastguard Worker      else:
1667*b7c941bbSAndroid Build Coastguard Worker        raise error_util.CameraItsError(
1668*b7c941bbSAndroid Build Coastguard Worker            f'Invalid response {data[_TAG_STR]} for JCA capture')
1669*b7c941bbSAndroid Build Coastguard Worker    if capture_status != RESULT_OK_STATUS:
1670*b7c941bbSAndroid Build Coastguard Worker      logging.error('Capture failed! Expected status %d, received %d',
1671*b7c941bbSAndroid Build Coastguard Worker                    RESULT_OK_STATUS, capture_status)
1672*b7c941bbSAndroid Build Coastguard Worker    logging.debug('capture path: %s', capture_path)
1673*b7c941bbSAndroid Build Coastguard Worker    _, capture_name = os.path.split(capture_path)
1674*b7c941bbSAndroid Build Coastguard Worker    its_device_utils.run(f'adb -s {dut.serial} pull {capture_path} {log_path}')
1675*b7c941bbSAndroid Build Coastguard Worker    return os.path.join(log_path, capture_name)
1676*b7c941bbSAndroid Build Coastguard Worker
1677*b7c941bbSAndroid Build Coastguard Worker  def do_capture_with_flash(self,
1678*b7c941bbSAndroid Build Coastguard Worker                            preview_request_start,
1679*b7c941bbSAndroid Build Coastguard Worker                            preview_request_idle,
1680*b7c941bbSAndroid Build Coastguard Worker                            still_capture_req,
1681*b7c941bbSAndroid Build Coastguard Worker                            out_surface):
1682*b7c941bbSAndroid Build Coastguard Worker    """Issue capture request with flash and read back the image and metadata.
1683*b7c941bbSAndroid Build Coastguard Worker
1684*b7c941bbSAndroid Build Coastguard Worker    Captures a single image with still_capture_req as capture request
1685*b7c941bbSAndroid Build Coastguard Worker    with flash. It triggers the precapture sequence with preview request
1686*b7c941bbSAndroid Build Coastguard Worker    preview_request_start with capture intent preview by setting aePrecapture
1687*b7c941bbSAndroid Build Coastguard Worker    trigger to Start. This is followed by repeated preview requests
1688*b7c941bbSAndroid Build Coastguard Worker    preview_request_idle with aePrecaptureTrigger set to IDLE.
1689*b7c941bbSAndroid Build Coastguard Worker    Once the AE is converged, a single image is captured still_capture_req
1690*b7c941bbSAndroid Build Coastguard Worker    during which the flash must be fired.
1691*b7c941bbSAndroid Build Coastguard Worker    Note: The part where we read output data from socket is cloned from
1692*b7c941bbSAndroid Build Coastguard Worker    do_capture and will be consolidated in U.
1693*b7c941bbSAndroid Build Coastguard Worker
1694*b7c941bbSAndroid Build Coastguard Worker    Args:
1695*b7c941bbSAndroid Build Coastguard Worker      preview_request_start: Preview request with aePrecaptureTrigger set to
1696*b7c941bbSAndroid Build Coastguard Worker        Start
1697*b7c941bbSAndroid Build Coastguard Worker      preview_request_idle: Preview request with aePrecaptureTrigger set to Idle
1698*b7c941bbSAndroid Build Coastguard Worker      still_capture_req: Single still capture request.
1699*b7c941bbSAndroid Build Coastguard Worker      out_surface: Specifications of the output image formats and
1700*b7c941bbSAndroid Build Coastguard Worker        sizes to use for capture. Supports yuv and jpeg.
1701*b7c941bbSAndroid Build Coastguard Worker    Returns:
1702*b7c941bbSAndroid Build Coastguard Worker      An object which contains following fields:
1703*b7c941bbSAndroid Build Coastguard Worker      * data: the image data as a numpy array of bytes.
1704*b7c941bbSAndroid Build Coastguard Worker      * width: the width of the captured image.
1705*b7c941bbSAndroid Build Coastguard Worker      * height: the height of the captured image.
1706*b7c941bbSAndroid Build Coastguard Worker      * format: image format
1707*b7c941bbSAndroid Build Coastguard Worker      * metadata: the capture result object
1708*b7c941bbSAndroid Build Coastguard Worker    """
1709*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
1710*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'doCaptureWithFlash'
1711*b7c941bbSAndroid Build Coastguard Worker    cmd['previewRequestStart'] = [preview_request_start]
1712*b7c941bbSAndroid Build Coastguard Worker    cmd['previewRequestIdle'] = [preview_request_idle]
1713*b7c941bbSAndroid Build Coastguard Worker    cmd['stillCaptureRequest'] = [still_capture_req]
1714*b7c941bbSAndroid Build Coastguard Worker    cmd['outputSurfaces'] = [out_surface]
1715*b7c941bbSAndroid Build Coastguard Worker    if 'android.control.aeMode' in still_capture_req:
1716*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Capturing image with aeMode: %d',
1717*b7c941bbSAndroid Build Coastguard Worker                    still_capture_req['android.control.aeMode'])
1718*b7c941bbSAndroid Build Coastguard Worker    return self.do_simple_capture(cmd, out_surface)
1719*b7c941bbSAndroid Build Coastguard Worker
1720*b7c941bbSAndroid Build Coastguard Worker  def do_capture_with_extensions(self,
1721*b7c941bbSAndroid Build Coastguard Worker                                 cap_request,
1722*b7c941bbSAndroid Build Coastguard Worker                                 extension,
1723*b7c941bbSAndroid Build Coastguard Worker                                 out_surface):
1724*b7c941bbSAndroid Build Coastguard Worker    """Issue extension capture request(s), and read back image(s) and metadata.
1725*b7c941bbSAndroid Build Coastguard Worker
1726*b7c941bbSAndroid Build Coastguard Worker    Args:
1727*b7c941bbSAndroid Build Coastguard Worker      cap_request: The Python dict/list specifying the capture(s), which will be
1728*b7c941bbSAndroid Build Coastguard Worker        converted to JSON and sent to the device.
1729*b7c941bbSAndroid Build Coastguard Worker      extension: The extension to be requested.
1730*b7c941bbSAndroid Build Coastguard Worker      out_surface: specifications of the output image format and
1731*b7c941bbSAndroid Build Coastguard Worker        size to use for the capture.
1732*b7c941bbSAndroid Build Coastguard Worker
1733*b7c941bbSAndroid Build Coastguard Worker    Returns:
1734*b7c941bbSAndroid Build Coastguard Worker      An object, list of objects, or list of lists of objects, where each
1735*b7c941bbSAndroid Build Coastguard Worker      object contains the following fields:
1736*b7c941bbSAndroid Build Coastguard Worker      * data: the image data as a numpy array of bytes.
1737*b7c941bbSAndroid Build Coastguard Worker      * width: the width of the captured image.
1738*b7c941bbSAndroid Build Coastguard Worker      * height: the height of the captured image.
1739*b7c941bbSAndroid Build Coastguard Worker      * format: image the format, in [
1740*b7c941bbSAndroid Build Coastguard Worker                        "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
1741*b7c941bbSAndroid Build Coastguard Worker      * metadata: the capture result object (Python dictionary).
1742*b7c941bbSAndroid Build Coastguard Worker    """
1743*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
1744*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'doCaptureWithExtensions'
1745*b7c941bbSAndroid Build Coastguard Worker    cmd['repeatRequests'] = []
1746*b7c941bbSAndroid Build Coastguard Worker    cmd['captureRequests'] = [cap_request]
1747*b7c941bbSAndroid Build Coastguard Worker    cmd['extension'] = extension
1748*b7c941bbSAndroid Build Coastguard Worker    cmd['outputSurfaces'] = [out_surface]
1749*b7c941bbSAndroid Build Coastguard Worker
1750*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Capturing image with EXTENSIONS.')
1751*b7c941bbSAndroid Build Coastguard Worker    return self.do_simple_capture(cmd, out_surface)
1752*b7c941bbSAndroid Build Coastguard Worker
1753*b7c941bbSAndroid Build Coastguard Worker  def do_capture(self,
1754*b7c941bbSAndroid Build Coastguard Worker                 cap_request,
1755*b7c941bbSAndroid Build Coastguard Worker                 out_surfaces=None,
1756*b7c941bbSAndroid Build Coastguard Worker                 reprocess_format=None,
1757*b7c941bbSAndroid Build Coastguard Worker                 repeat_request=None,
1758*b7c941bbSAndroid Build Coastguard Worker                 reuse_session=False,
1759*b7c941bbSAndroid Build Coastguard Worker                 first_surface_for_3a=False):
1760*b7c941bbSAndroid Build Coastguard Worker    """Issue capture request(s), and read back the image(s) and metadata.
1761*b7c941bbSAndroid Build Coastguard Worker
1762*b7c941bbSAndroid Build Coastguard Worker    The main top-level function for capturing one or more images using the
1763*b7c941bbSAndroid Build Coastguard Worker    device. Captures a single image if cap_request is a single object, and
1764*b7c941bbSAndroid Build Coastguard Worker    captures a burst if it is a list of objects.
1765*b7c941bbSAndroid Build Coastguard Worker
1766*b7c941bbSAndroid Build Coastguard Worker    The optional repeat_request field can be used to assign a repeating
1767*b7c941bbSAndroid Build Coastguard Worker    request list ran in background for 3 seconds to warm up the capturing
1768*b7c941bbSAndroid Build Coastguard Worker    pipeline before start capturing. The repeat_requests will be ran on a
1769*b7c941bbSAndroid Build Coastguard Worker    640x480 YUV surface without sending any data back. The caller needs to
1770*b7c941bbSAndroid Build Coastguard Worker    make sure the stream configuration defined by out_surfaces and
1771*b7c941bbSAndroid Build Coastguard Worker    repeat_request are valid or do_capture may fail because device does not
1772*b7c941bbSAndroid Build Coastguard Worker    support such stream configuration.
1773*b7c941bbSAndroid Build Coastguard Worker
1774*b7c941bbSAndroid Build Coastguard Worker    The out_surfaces field can specify the width(s), height(s), and
1775*b7c941bbSAndroid Build Coastguard Worker    format(s) of the captured image. The formats may be "yuv", "jpeg",
1776*b7c941bbSAndroid Build Coastguard Worker    "dng", "raw", "raw10", "raw12", "rawStats" or "y8". The default is a
1777*b7c941bbSAndroid Build Coastguard Worker    YUV420 frame ("yuv") corresponding to a full sensor frame.
1778*b7c941bbSAndroid Build Coastguard Worker
1779*b7c941bbSAndroid Build Coastguard Worker    1. Optionally the out_surfaces field can specify physical camera id(s) if
1780*b7c941bbSAndroid Build Coastguard Worker    the current camera device is a logical multi-camera. The physical camera
1781*b7c941bbSAndroid Build Coastguard Worker    id must refer to a physical camera backing this logical camera device.
1782*b7c941bbSAndroid Build Coastguard Worker    2. Optionally The output_surfaces field can also specify the use case(s) if
1783*b7c941bbSAndroid Build Coastguard Worker    the current camera device has STREAM_USE_CASE capability.
1784*b7c941bbSAndroid Build Coastguard Worker
1785*b7c941bbSAndroid Build Coastguard Worker    Note that one or more surfaces can be specified, allowing a capture to
1786*b7c941bbSAndroid Build Coastguard Worker    request images back in multiple formats (e.g.) raw+yuv, raw+jpeg,
1787*b7c941bbSAndroid Build Coastguard Worker    yuv+jpeg, raw+yuv+jpeg. If the size is omitted for a surface, the
1788*b7c941bbSAndroid Build Coastguard Worker    default is the largest resolution available for the format of that
1789*b7c941bbSAndroid Build Coastguard Worker    surface. At most one output surface can be specified for a given format,
1790*b7c941bbSAndroid Build Coastguard Worker    and raw+dng, raw10+dng, and raw+raw10 are not supported as combinations.
1791*b7c941bbSAndroid Build Coastguard Worker
1792*b7c941bbSAndroid Build Coastguard Worker    If reprocess_format is not None, for each request, an intermediate
1793*b7c941bbSAndroid Build Coastguard Worker    buffer of the given reprocess_format will be captured from camera and
1794*b7c941bbSAndroid Build Coastguard Worker    the intermediate buffer will be reprocessed to the output surfaces. The
1795*b7c941bbSAndroid Build Coastguard Worker    following settings will be turned off when capturing the intermediate
1796*b7c941bbSAndroid Build Coastguard Worker    buffer and will be applied when reprocessing the intermediate buffer.
1797*b7c941bbSAndroid Build Coastguard Worker    1. android.noiseReduction.mode
1798*b7c941bbSAndroid Build Coastguard Worker    2. android.edge.mode
1799*b7c941bbSAndroid Build Coastguard Worker    3. android.reprocess.effectiveExposureFactor
1800*b7c941bbSAndroid Build Coastguard Worker
1801*b7c941bbSAndroid Build Coastguard Worker    Supported reprocess format are "yuv" and "private". Supported output
1802*b7c941bbSAndroid Build Coastguard Worker    surface formats when reprocessing is enabled are "yuv" and "jpeg".
1803*b7c941bbSAndroid Build Coastguard Worker
1804*b7c941bbSAndroid Build Coastguard Worker    Example of a single capture request:
1805*b7c941bbSAndroid Build Coastguard Worker
1806*b7c941bbSAndroid Build Coastguard Worker    {
1807*b7c941bbSAndroid Build Coastguard Worker     "android.sensor.exposureTime": 100*1000*1000,
1808*b7c941bbSAndroid Build Coastguard Worker     "android.sensor.sensitivity": 100
1809*b7c941bbSAndroid Build Coastguard Worker    }
1810*b7c941bbSAndroid Build Coastguard Worker
1811*b7c941bbSAndroid Build Coastguard Worker    Example of a list of capture requests:
1812*b7c941bbSAndroid Build Coastguard Worker    [
1813*b7c941bbSAndroid Build Coastguard Worker     {
1814*b7c941bbSAndroid Build Coastguard Worker       "android.sensor.exposureTime": 100*1000*1000,
1815*b7c941bbSAndroid Build Coastguard Worker       "android.sensor.sensitivity": 100
1816*b7c941bbSAndroid Build Coastguard Worker     },
1817*b7c941bbSAndroid Build Coastguard Worker    {
1818*b7c941bbSAndroid Build Coastguard Worker      "android.sensor.exposureTime": 100*1000*1000,
1819*b7c941bbSAndroid Build Coastguard Worker       "android.sensor.sensitivity": 200
1820*b7c941bbSAndroid Build Coastguard Worker     }
1821*b7c941bbSAndroid Build Coastguard Worker    ]
1822*b7c941bbSAndroid Build Coastguard Worker
1823*b7c941bbSAndroid Build Coastguard Worker    Example of output surface specifications:
1824*b7c941bbSAndroid Build Coastguard Worker    {
1825*b7c941bbSAndroid Build Coastguard Worker     "width": 640,
1826*b7c941bbSAndroid Build Coastguard Worker     "height": 480,
1827*b7c941bbSAndroid Build Coastguard Worker     "format": "yuv"
1828*b7c941bbSAndroid Build Coastguard Worker    }
1829*b7c941bbSAndroid Build Coastguard Worker    [
1830*b7c941bbSAndroid Build Coastguard Worker     {
1831*b7c941bbSAndroid Build Coastguard Worker       "format": "jpeg"
1832*b7c941bbSAndroid Build Coastguard Worker     },
1833*b7c941bbSAndroid Build Coastguard Worker     {
1834*b7c941bbSAndroid Build Coastguard Worker       "format": "raw"
1835*b7c941bbSAndroid Build Coastguard Worker     }
1836*b7c941bbSAndroid Build Coastguard Worker    ]
1837*b7c941bbSAndroid Build Coastguard Worker
1838*b7c941bbSAndroid Build Coastguard Worker    The following variables defined in this class are shortcuts for
1839*b7c941bbSAndroid Build Coastguard Worker    specifying one or more formats where each output is the full size for
1840*b7c941bbSAndroid Build Coastguard Worker    that format; they can be used as values for the out_surfaces arguments:
1841*b7c941bbSAndroid Build Coastguard Worker
1842*b7c941bbSAndroid Build Coastguard Worker    CAP_RAW
1843*b7c941bbSAndroid Build Coastguard Worker    CAP_DNG
1844*b7c941bbSAndroid Build Coastguard Worker    CAP_YUV
1845*b7c941bbSAndroid Build Coastguard Worker    CAP_JPEG
1846*b7c941bbSAndroid Build Coastguard Worker    CAP_RAW_YUV
1847*b7c941bbSAndroid Build Coastguard Worker    CAP_DNG_YUV
1848*b7c941bbSAndroid Build Coastguard Worker    CAP_RAW_JPEG
1849*b7c941bbSAndroid Build Coastguard Worker    CAP_DNG_JPEG
1850*b7c941bbSAndroid Build Coastguard Worker    CAP_YUV_JPEG
1851*b7c941bbSAndroid Build Coastguard Worker    CAP_RAW_YUV_JPEG
1852*b7c941bbSAndroid Build Coastguard Worker    CAP_DNG_YUV_JPEG
1853*b7c941bbSAndroid Build Coastguard Worker
1854*b7c941bbSAndroid Build Coastguard Worker    If multiple formats are specified, then this function returns multiple
1855*b7c941bbSAndroid Build Coastguard Worker    capture objects, one for each requested format. If multiple formats and
1856*b7c941bbSAndroid Build Coastguard Worker    multiple captures (i.e. a burst) are specified, then this function
1857*b7c941bbSAndroid Build Coastguard Worker    returns multiple lists of capture objects. In both cases, the order of
1858*b7c941bbSAndroid Build Coastguard Worker    the returned objects matches the order of the requested formats in the
1859*b7c941bbSAndroid Build Coastguard Worker    out_surfaces parameter. For example:
1860*b7c941bbSAndroid Build Coastguard Worker
1861*b7c941bbSAndroid Build Coastguard Worker    yuv_cap = do_capture(req1)
1862*b7c941bbSAndroid Build Coastguard Worker    yuv_cap = do_capture(req1,yuv_fmt)
1863*b7c941bbSAndroid Build Coastguard Worker    yuv_cap, raw_cap = do_capture(req1, [yuv_fmt,raw_fmt])
1864*b7c941bbSAndroid Build Coastguard Worker    yuv_caps = do_capture([req1,req2], yuv_fmt)
1865*b7c941bbSAndroid Build Coastguard Worker    yuv_caps, raw_caps = do_capture([req1,req2], [yuv_fmt,raw_fmt])
1866*b7c941bbSAndroid Build Coastguard Worker
1867*b7c941bbSAndroid Build Coastguard Worker    The "rawStats" format processes the raw image and returns a new image
1868*b7c941bbSAndroid Build Coastguard Worker    of statistics from the raw image. The format takes additional keys,
1869*b7c941bbSAndroid Build Coastguard Worker    "gridWidth" and "gridHeight" which are size of grid cells in a 2D grid
1870*b7c941bbSAndroid Build Coastguard Worker    of the raw image. For each grid cell, the mean and variance of each raw
1871*b7c941bbSAndroid Build Coastguard Worker    channel is computed, and the do_capture call returns two 4-element float
1872*b7c941bbSAndroid Build Coastguard Worker    images of dimensions (rawWidth / gridWidth, rawHeight / gridHeight),
1873*b7c941bbSAndroid Build Coastguard Worker    concatenated back-to-back, where the first image contains the 4-channel
1874*b7c941bbSAndroid Build Coastguard Worker    means and the second contains the 4-channel variances. Note that only
1875*b7c941bbSAndroid Build Coastguard Worker    pixels in the active array crop region are used; pixels outside this
1876*b7c941bbSAndroid Build Coastguard Worker    region (for example optical black rows) are cropped out before the
1877*b7c941bbSAndroid Build Coastguard Worker    gridding and statistics computation is performed.
1878*b7c941bbSAndroid Build Coastguard Worker
1879*b7c941bbSAndroid Build Coastguard Worker    For the rawStats format, if the gridWidth is not provided then the raw
1880*b7c941bbSAndroid Build Coastguard Worker    image width is used as the default, and similarly for gridHeight. With
1881*b7c941bbSAndroid Build Coastguard Worker    this, the following is an example of a output description that computes
1882*b7c941bbSAndroid Build Coastguard Worker    the mean and variance across each image row:
1883*b7c941bbSAndroid Build Coastguard Worker    {
1884*b7c941bbSAndroid Build Coastguard Worker      "gridHeight": 1,
1885*b7c941bbSAndroid Build Coastguard Worker      "format": "rawStats"
1886*b7c941bbSAndroid Build Coastguard Worker    }
1887*b7c941bbSAndroid Build Coastguard Worker
1888*b7c941bbSAndroid Build Coastguard Worker    Args:
1889*b7c941bbSAndroid Build Coastguard Worker      cap_request: The Python dict/list specifying the capture(s), which will be
1890*b7c941bbSAndroid Build Coastguard Worker        converted to JSON and sent to the device.
1891*b7c941bbSAndroid Build Coastguard Worker      out_surfaces: (Optional) specifications of the output image formats and
1892*b7c941bbSAndroid Build Coastguard Worker        sizes to use for each capture.
1893*b7c941bbSAndroid Build Coastguard Worker      reprocess_format: (Optional) The reprocessing format. If not
1894*b7c941bbSAndroid Build Coastguard Worker        None,reprocessing will be enabled.
1895*b7c941bbSAndroid Build Coastguard Worker      repeat_request: Repeating request list.
1896*b7c941bbSAndroid Build Coastguard Worker      reuse_session: True if ItsService.java should try to use
1897*b7c941bbSAndroid Build Coastguard Worker        the existing CameraCaptureSession.
1898*b7c941bbSAndroid Build Coastguard Worker      first_surface_for_3a: Use first surface in out_surfaces for 3A, not capture
1899*b7c941bbSAndroid Build Coastguard Worker        Only applicable if out_surfaces contains at least 1 surface.
1900*b7c941bbSAndroid Build Coastguard Worker
1901*b7c941bbSAndroid Build Coastguard Worker    Returns:
1902*b7c941bbSAndroid Build Coastguard Worker      An object, list of objects, or list of lists of objects, where each
1903*b7c941bbSAndroid Build Coastguard Worker      object contains the following fields:
1904*b7c941bbSAndroid Build Coastguard Worker      * data: the image data as a numpy array of bytes.
1905*b7c941bbSAndroid Build Coastguard Worker      * width: the width of the captured image.
1906*b7c941bbSAndroid Build Coastguard Worker      * height: the height of the captured image.
1907*b7c941bbSAndroid Build Coastguard Worker      * format: image the format, in [
1908*b7c941bbSAndroid Build Coastguard Worker                        "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
1909*b7c941bbSAndroid Build Coastguard Worker      * metadata: the capture result object (Python dictionary).
1910*b7c941bbSAndroid Build Coastguard Worker    """
1911*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
1912*b7c941bbSAndroid Build Coastguard Worker    if reprocess_format is not None:
1913*b7c941bbSAndroid Build Coastguard Worker      if repeat_request is not None:
1914*b7c941bbSAndroid Build Coastguard Worker        raise error_util.CameraItsError(
1915*b7c941bbSAndroid Build Coastguard Worker            'repeating request + reprocessing is not supported')
1916*b7c941bbSAndroid Build Coastguard Worker      cmd[_CMD_NAME_STR] = 'doReprocessCapture'
1917*b7c941bbSAndroid Build Coastguard Worker      cmd['reprocessFormat'] = reprocess_format
1918*b7c941bbSAndroid Build Coastguard Worker    else:
1919*b7c941bbSAndroid Build Coastguard Worker      cmd[_CMD_NAME_STR] = 'doCapture'
1920*b7c941bbSAndroid Build Coastguard Worker
1921*b7c941bbSAndroid Build Coastguard Worker    if repeat_request is None:
1922*b7c941bbSAndroid Build Coastguard Worker      cmd['repeatRequests'] = []
1923*b7c941bbSAndroid Build Coastguard Worker    elif not isinstance(repeat_request, list):
1924*b7c941bbSAndroid Build Coastguard Worker      cmd['repeatRequests'] = [repeat_request]
1925*b7c941bbSAndroid Build Coastguard Worker    else:
1926*b7c941bbSAndroid Build Coastguard Worker      cmd['repeatRequests'] = repeat_request
1927*b7c941bbSAndroid Build Coastguard Worker
1928*b7c941bbSAndroid Build Coastguard Worker    if not isinstance(cap_request, list):
1929*b7c941bbSAndroid Build Coastguard Worker      cmd['captureRequests'] = [cap_request]
1930*b7c941bbSAndroid Build Coastguard Worker    else:
1931*b7c941bbSAndroid Build Coastguard Worker      cmd['captureRequests'] = cap_request
1932*b7c941bbSAndroid Build Coastguard Worker
1933*b7c941bbSAndroid Build Coastguard Worker    if out_surfaces:
1934*b7c941bbSAndroid Build Coastguard Worker      if isinstance(out_surfaces, list):
1935*b7c941bbSAndroid Build Coastguard Worker        cmd['outputSurfaces'] = out_surfaces
1936*b7c941bbSAndroid Build Coastguard Worker      else:
1937*b7c941bbSAndroid Build Coastguard Worker        cmd['outputSurfaces'] = [out_surfaces]
1938*b7c941bbSAndroid Build Coastguard Worker      formats = [
1939*b7c941bbSAndroid Build Coastguard Worker          c['format'] if 'format' in c else 'yuv' for c in cmd['outputSurfaces']
1940*b7c941bbSAndroid Build Coastguard Worker      ]
1941*b7c941bbSAndroid Build Coastguard Worker      formats = [s if s != 'jpg' else 'jpeg' for s in formats]
1942*b7c941bbSAndroid Build Coastguard Worker    else:
1943*b7c941bbSAndroid Build Coastguard Worker      max_yuv_size = capture_request_utils.get_available_output_sizes(
1944*b7c941bbSAndroid Build Coastguard Worker          'yuv', self.props)[0]
1945*b7c941bbSAndroid Build Coastguard Worker      formats = ['yuv']
1946*b7c941bbSAndroid Build Coastguard Worker      cmd['outputSurfaces'] = [{
1947*b7c941bbSAndroid Build Coastguard Worker          'format': 'yuv',
1948*b7c941bbSAndroid Build Coastguard Worker          'width': max_yuv_size[0],
1949*b7c941bbSAndroid Build Coastguard Worker          'height': max_yuv_size[1]
1950*b7c941bbSAndroid Build Coastguard Worker      }]
1951*b7c941bbSAndroid Build Coastguard Worker
1952*b7c941bbSAndroid Build Coastguard Worker    cmd['reuseSession'] = reuse_session
1953*b7c941bbSAndroid Build Coastguard Worker    cmd['firstSurfaceFor3A'] = first_surface_for_3a
1954*b7c941bbSAndroid Build Coastguard Worker
1955*b7c941bbSAndroid Build Coastguard Worker    requested_surfaces = cmd['outputSurfaces'][:]
1956*b7c941bbSAndroid Build Coastguard Worker    if first_surface_for_3a:
1957*b7c941bbSAndroid Build Coastguard Worker      formats.pop(0)
1958*b7c941bbSAndroid Build Coastguard Worker      requested_surfaces.pop(0)
1959*b7c941bbSAndroid Build Coastguard Worker
1960*b7c941bbSAndroid Build Coastguard Worker    ncap = len(cmd['captureRequests'])
1961*b7c941bbSAndroid Build Coastguard Worker    nsurf = len(formats)
1962*b7c941bbSAndroid Build Coastguard Worker
1963*b7c941bbSAndroid Build Coastguard Worker    cam_ids = []
1964*b7c941bbSAndroid Build Coastguard Worker    bufs = {}
1965*b7c941bbSAndroid Build Coastguard Worker    yuv_bufs = {}
1966*b7c941bbSAndroid Build Coastguard Worker    for i, s in enumerate(cmd['outputSurfaces']):
1967*b7c941bbSAndroid Build Coastguard Worker      if self._hidden_physical_id:
1968*b7c941bbSAndroid Build Coastguard Worker        s['physicalCamera'] = self._hidden_physical_id
1969*b7c941bbSAndroid Build Coastguard Worker
1970*b7c941bbSAndroid Build Coastguard Worker      if 'physicalCamera' in s:
1971*b7c941bbSAndroid Build Coastguard Worker        cam_id = s['physicalCamera']
1972*b7c941bbSAndroid Build Coastguard Worker      else:
1973*b7c941bbSAndroid Build Coastguard Worker        cam_id = self._camera_id
1974*b7c941bbSAndroid Build Coastguard Worker
1975*b7c941bbSAndroid Build Coastguard Worker      if cam_id not in cam_ids:
1976*b7c941bbSAndroid Build Coastguard Worker        cam_ids.append(cam_id)
1977*b7c941bbSAndroid Build Coastguard Worker        bufs[cam_id] = {
1978*b7c941bbSAndroid Build Coastguard Worker            'raw': [],
1979*b7c941bbSAndroid Build Coastguard Worker            'raw10': [],
1980*b7c941bbSAndroid Build Coastguard Worker            'raw12': [],
1981*b7c941bbSAndroid Build Coastguard Worker            'rawStats': [],
1982*b7c941bbSAndroid Build Coastguard Worker            'dng': [],
1983*b7c941bbSAndroid Build Coastguard Worker            'jpeg': [],
1984*b7c941bbSAndroid Build Coastguard Worker            'jpeg_r': [],
1985*b7c941bbSAndroid Build Coastguard Worker            'y8': [],
1986*b7c941bbSAndroid Build Coastguard Worker            'rawQuadBayer': [],
1987*b7c941bbSAndroid Build Coastguard Worker            'rawQuadBayerStats': [],
1988*b7c941bbSAndroid Build Coastguard Worker            'raw10Stats': [],
1989*b7c941bbSAndroid Build Coastguard Worker            'raw10QuadBayerStats': [],
1990*b7c941bbSAndroid Build Coastguard Worker            'raw10QuadBayer': [],
1991*b7c941bbSAndroid Build Coastguard Worker        }
1992*b7c941bbSAndroid Build Coastguard Worker
1993*b7c941bbSAndroid Build Coastguard Worker    for cam_id in cam_ids:
1994*b7c941bbSAndroid Build Coastguard Worker       # Only allow yuv output to multiple targets
1995*b7c941bbSAndroid Build Coastguard Worker      if cam_id == self._camera_id:
1996*b7c941bbSAndroid Build Coastguard Worker        yuv_surfaces = [
1997*b7c941bbSAndroid Build Coastguard Worker            s for s in requested_surfaces
1998*b7c941bbSAndroid Build Coastguard Worker            if s['format'] == 'yuv' and 'physicalCamera' not in s
1999*b7c941bbSAndroid Build Coastguard Worker        ]
2000*b7c941bbSAndroid Build Coastguard Worker        formats_for_id = [
2001*b7c941bbSAndroid Build Coastguard Worker            s['format']
2002*b7c941bbSAndroid Build Coastguard Worker            for s in requested_surfaces
2003*b7c941bbSAndroid Build Coastguard Worker            if 'physicalCamera' not in s
2004*b7c941bbSAndroid Build Coastguard Worker        ]
2005*b7c941bbSAndroid Build Coastguard Worker      else:
2006*b7c941bbSAndroid Build Coastguard Worker        yuv_surfaces = [
2007*b7c941bbSAndroid Build Coastguard Worker            s for s in requested_surfaces if s['format'] == 'yuv' and
2008*b7c941bbSAndroid Build Coastguard Worker            'physicalCamera' in s and s['physicalCamera'] == cam_id
2009*b7c941bbSAndroid Build Coastguard Worker        ]
2010*b7c941bbSAndroid Build Coastguard Worker        formats_for_id = [
2011*b7c941bbSAndroid Build Coastguard Worker            s['format']
2012*b7c941bbSAndroid Build Coastguard Worker            for s in requested_surfaces
2013*b7c941bbSAndroid Build Coastguard Worker            if 'physicalCamera' in s and s['physicalCamera'] == cam_id
2014*b7c941bbSAndroid Build Coastguard Worker        ]
2015*b7c941bbSAndroid Build Coastguard Worker
2016*b7c941bbSAndroid Build Coastguard Worker      n_yuv = len(yuv_surfaces)
2017*b7c941bbSAndroid Build Coastguard Worker      # Compute the buffer size of YUV targets
2018*b7c941bbSAndroid Build Coastguard Worker      yuv_maxsize_1d = 0
2019*b7c941bbSAndroid Build Coastguard Worker      for s in yuv_surfaces:
2020*b7c941bbSAndroid Build Coastguard Worker        if ('width' not in s and 'height' not in s):
2021*b7c941bbSAndroid Build Coastguard Worker          if self.props is None:
2022*b7c941bbSAndroid Build Coastguard Worker            raise error_util.CameraItsError('Camera props are unavailable')
2023*b7c941bbSAndroid Build Coastguard Worker          yuv_maxsize_2d = capture_request_utils.get_available_output_sizes(
2024*b7c941bbSAndroid Build Coastguard Worker              'yuv', self.props)[0]
2025*b7c941bbSAndroid Build Coastguard Worker          # YUV420 size = 1.5 bytes per pixel
2026*b7c941bbSAndroid Build Coastguard Worker          yuv_maxsize_1d = (yuv_maxsize_2d[0] * yuv_maxsize_2d[1] * 3) // 2
2027*b7c941bbSAndroid Build Coastguard Worker          break
2028*b7c941bbSAndroid Build Coastguard Worker      yuv_sizes = [
2029*b7c941bbSAndroid Build Coastguard Worker          (c['width'] * c['height'] * 3) // 2
2030*b7c941bbSAndroid Build Coastguard Worker          if 'width' in c and 'height' in c else yuv_maxsize_1d
2031*b7c941bbSAndroid Build Coastguard Worker          for c in yuv_surfaces
2032*b7c941bbSAndroid Build Coastguard Worker      ]
2033*b7c941bbSAndroid Build Coastguard Worker      # Currently we don't pass enough metadata from ItsService to distinguish
2034*b7c941bbSAndroid Build Coastguard Worker      # different yuv stream of same buffer size
2035*b7c941bbSAndroid Build Coastguard Worker      if len(yuv_sizes) != len(set(yuv_sizes)):
2036*b7c941bbSAndroid Build Coastguard Worker        raise error_util.CameraItsError(
2037*b7c941bbSAndroid Build Coastguard Worker            'ITS does not support yuv outputs of same buffer size')
2038*b7c941bbSAndroid Build Coastguard Worker      if len(formats_for_id) > len(set(formats_for_id)):
2039*b7c941bbSAndroid Build Coastguard Worker        if n_yuv != len(formats_for_id) - len(set(formats_for_id)) + 1:
2040*b7c941bbSAndroid Build Coastguard Worker          raise error_util.CameraItsError('Duplicate format requested')
2041*b7c941bbSAndroid Build Coastguard Worker
2042*b7c941bbSAndroid Build Coastguard Worker      yuv_bufs[cam_id] = {size: [] for size in yuv_sizes}
2043*b7c941bbSAndroid Build Coastguard Worker
2044*b7c941bbSAndroid Build Coastguard Worker    logging.debug('yuv bufs: %s', yuv_bufs)
2045*b7c941bbSAndroid Build Coastguard Worker    raw_formats = 0
2046*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'dng' in formats else 0
2047*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'raw' in formats else 0
2048*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'raw10' in formats else 0
2049*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'raw12' in formats else 0
2050*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'rawStats' in formats else 0
2051*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'rawQuadBayer' in formats else 0
2052*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'rawQuadBayerStats' in formats else 0
2053*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'raw10Stats' in formats else 0
2054*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'raw10QuadBayer' in formats else 0
2055*b7c941bbSAndroid Build Coastguard Worker    raw_formats += 1 if 'raw10QuadBayerStats' in formats else 0
2056*b7c941bbSAndroid Build Coastguard Worker
2057*b7c941bbSAndroid Build Coastguard Worker    if raw_formats > 1:
2058*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Different raw formats not supported')
2059*b7c941bbSAndroid Build Coastguard Worker
2060*b7c941bbSAndroid Build Coastguard Worker    # Detect long exposure time and set timeout accordingly
2061*b7c941bbSAndroid Build Coastguard Worker    longest_exp_time = 0
2062*b7c941bbSAndroid Build Coastguard Worker    for req in cmd['captureRequests']:
2063*b7c941bbSAndroid Build Coastguard Worker      if 'android.sensor.exposureTime' in req and req[
2064*b7c941bbSAndroid Build Coastguard Worker          'android.sensor.exposureTime'] > longest_exp_time:
2065*b7c941bbSAndroid Build Coastguard Worker        longest_exp_time = req['android.sensor.exposureTime']
2066*b7c941bbSAndroid Build Coastguard Worker
2067*b7c941bbSAndroid Build Coastguard Worker    extended_timeout = longest_exp_time // self.SEC_TO_NSEC + self.SOCK_TIMEOUT
2068*b7c941bbSAndroid Build Coastguard Worker    if repeat_request:
2069*b7c941bbSAndroid Build Coastguard Worker      extended_timeout += self.EXTRA_SOCK_TIMEOUT
2070*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(extended_timeout)
2071*b7c941bbSAndroid Build Coastguard Worker
2072*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Capturing %d frame%s with %d format%s [%s]', ncap,
2073*b7c941bbSAndroid Build Coastguard Worker                  's' if ncap > 1 else '', nsurf, 's' if nsurf > 1 else '',
2074*b7c941bbSAndroid Build Coastguard Worker                  ','.join(formats))
2075*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2076*b7c941bbSAndroid Build Coastguard Worker
2077*b7c941bbSAndroid Build Coastguard Worker    # Wait for ncap*nsurf images and ncap metadata responses.
2078*b7c941bbSAndroid Build Coastguard Worker    # Assume that captures come out in the same order as requested in
2079*b7c941bbSAndroid Build Coastguard Worker    # the burst, however individual images of different formats can come
2080*b7c941bbSAndroid Build Coastguard Worker    # out in any order for that capture.
2081*b7c941bbSAndroid Build Coastguard Worker    nbufs = 0
2082*b7c941bbSAndroid Build Coastguard Worker    mds = []
2083*b7c941bbSAndroid Build Coastguard Worker    physical_mds = []
2084*b7c941bbSAndroid Build Coastguard Worker    widths = None
2085*b7c941bbSAndroid Build Coastguard Worker    heights = None
2086*b7c941bbSAndroid Build Coastguard Worker    camera_id = (
2087*b7c941bbSAndroid Build Coastguard Worker        self._camera_id
2088*b7c941bbSAndroid Build Coastguard Worker        if not self._hidden_physical_id
2089*b7c941bbSAndroid Build Coastguard Worker        else self._hidden_physical_id
2090*b7c941bbSAndroid Build Coastguard Worker    )
2091*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Using camera_id %s to store buffers', camera_id)
2092*b7c941bbSAndroid Build Coastguard Worker    while nbufs < ncap * nsurf or len(mds) < ncap:
2093*b7c941bbSAndroid Build Coastguard Worker      json_obj, buf = self.__read_response_from_socket()
2094*b7c941bbSAndroid Build Coastguard Worker      if (json_obj[_TAG_STR] in ItsSession.IMAGE_FORMAT_LIST_1 and
2095*b7c941bbSAndroid Build Coastguard Worker          buf is not None):
2096*b7c941bbSAndroid Build Coastguard Worker        fmt = json_obj[_TAG_STR][:-5]
2097*b7c941bbSAndroid Build Coastguard Worker        bufs[camera_id][fmt].append(buf)
2098*b7c941bbSAndroid Build Coastguard Worker        nbufs += 1
2099*b7c941bbSAndroid Build Coastguard Worker      # Physical camera is appended to the tag string of a private capture
2100*b7c941bbSAndroid Build Coastguard Worker      elif json_obj[_TAG_STR].startswith('privImage'):
2101*b7c941bbSAndroid Build Coastguard Worker        # The private image format buffers are opaque to camera clients
2102*b7c941bbSAndroid Build Coastguard Worker        # and cannot be accessed.
2103*b7c941bbSAndroid Build Coastguard Worker        nbufs += 1
2104*b7c941bbSAndroid Build Coastguard Worker      elif json_obj[_TAG_STR] == 'yuvImage':
2105*b7c941bbSAndroid Build Coastguard Worker        buf_size = get_array_size(buf)
2106*b7c941bbSAndroid Build Coastguard Worker        yuv_bufs[camera_id][buf_size].append(buf)
2107*b7c941bbSAndroid Build Coastguard Worker        nbufs += 1
2108*b7c941bbSAndroid Build Coastguard Worker      elif json_obj[_TAG_STR] == 'captureResults':
2109*b7c941bbSAndroid Build Coastguard Worker        mds.append(json_obj[_OBJ_VALUE_STR]['captureResult'])
2110*b7c941bbSAndroid Build Coastguard Worker        physical_mds.append(json_obj[_OBJ_VALUE_STR]['physicalResults'])
2111*b7c941bbSAndroid Build Coastguard Worker        outputs = json_obj[_OBJ_VALUE_STR]['outputs']
2112*b7c941bbSAndroid Build Coastguard Worker        widths = [out['width'] for out in outputs]
2113*b7c941bbSAndroid Build Coastguard Worker        heights = [out['height'] for out in outputs]
2114*b7c941bbSAndroid Build Coastguard Worker      else:
2115*b7c941bbSAndroid Build Coastguard Worker        tag_string = unicodedata.normalize('NFKD', json_obj[_TAG_STR]).encode(
2116*b7c941bbSAndroid Build Coastguard Worker            'ascii', 'ignore')
2117*b7c941bbSAndroid Build Coastguard Worker        for x in ItsSession.IMAGE_FORMAT_LIST_2:
2118*b7c941bbSAndroid Build Coastguard Worker          x = bytes(x, encoding='utf-8')
2119*b7c941bbSAndroid Build Coastguard Worker          if tag_string.startswith(x):
2120*b7c941bbSAndroid Build Coastguard Worker            if x == b'yuvImage':
2121*b7c941bbSAndroid Build Coastguard Worker              physical_id = json_obj[_TAG_STR][len(x):]
2122*b7c941bbSAndroid Build Coastguard Worker              if physical_id in cam_ids:
2123*b7c941bbSAndroid Build Coastguard Worker                buf_size = get_array_size(buf)
2124*b7c941bbSAndroid Build Coastguard Worker                yuv_bufs[physical_id][buf_size].append(buf)
2125*b7c941bbSAndroid Build Coastguard Worker                nbufs += 1
2126*b7c941bbSAndroid Build Coastguard Worker            else:
2127*b7c941bbSAndroid Build Coastguard Worker              physical_id = json_obj[_TAG_STR][len(x):]
2128*b7c941bbSAndroid Build Coastguard Worker              if physical_id in cam_ids:
2129*b7c941bbSAndroid Build Coastguard Worker                fmt = x[:-5].decode('UTF-8')
2130*b7c941bbSAndroid Build Coastguard Worker                bufs[physical_id][fmt].append(buf)
2131*b7c941bbSAndroid Build Coastguard Worker                nbufs += 1
2132*b7c941bbSAndroid Build Coastguard Worker    rets = []
2133*b7c941bbSAndroid Build Coastguard Worker    for j, fmt in enumerate(formats):
2134*b7c941bbSAndroid Build Coastguard Worker      objs = []
2135*b7c941bbSAndroid Build Coastguard Worker      if 'physicalCamera' in requested_surfaces[j]:
2136*b7c941bbSAndroid Build Coastguard Worker        cam_id = requested_surfaces[j]['physicalCamera']
2137*b7c941bbSAndroid Build Coastguard Worker      else:
2138*b7c941bbSAndroid Build Coastguard Worker        cam_id = self._camera_id
2139*b7c941bbSAndroid Build Coastguard Worker
2140*b7c941bbSAndroid Build Coastguard Worker      for i in range(ncap):
2141*b7c941bbSAndroid Build Coastguard Worker        obj = {}
2142*b7c941bbSAndroid Build Coastguard Worker        obj['width'] = widths[j]
2143*b7c941bbSAndroid Build Coastguard Worker        obj['height'] = heights[j]
2144*b7c941bbSAndroid Build Coastguard Worker        obj['format'] = fmt
2145*b7c941bbSAndroid Build Coastguard Worker        if cam_id == self._camera_id:
2146*b7c941bbSAndroid Build Coastguard Worker          obj['metadata'] = mds[i]
2147*b7c941bbSAndroid Build Coastguard Worker        else:
2148*b7c941bbSAndroid Build Coastguard Worker          for physical_md in physical_mds[i]:
2149*b7c941bbSAndroid Build Coastguard Worker            if cam_id in physical_md:
2150*b7c941bbSAndroid Build Coastguard Worker              obj['metadata'] = physical_md[cam_id]
2151*b7c941bbSAndroid Build Coastguard Worker              break
2152*b7c941bbSAndroid Build Coastguard Worker
2153*b7c941bbSAndroid Build Coastguard Worker        if fmt == 'yuv':
2154*b7c941bbSAndroid Build Coastguard Worker          buf_size = (widths[j] * heights[j] * 3) // 2
2155*b7c941bbSAndroid Build Coastguard Worker          obj['data'] = yuv_bufs[cam_id][buf_size][i]
2156*b7c941bbSAndroid Build Coastguard Worker        elif fmt != 'priv':
2157*b7c941bbSAndroid Build Coastguard Worker          obj['data'] = bufs[cam_id][fmt][i]
2158*b7c941bbSAndroid Build Coastguard Worker        objs.append(obj)
2159*b7c941bbSAndroid Build Coastguard Worker      rets.append(objs if ncap > 1 else objs[0])
2160*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(self.SOCK_TIMEOUT)
2161*b7c941bbSAndroid Build Coastguard Worker    if len(rets) > 1 or (isinstance(rets[0], dict) and
2162*b7c941bbSAndroid Build Coastguard Worker                         isinstance(cap_request, list)):
2163*b7c941bbSAndroid Build Coastguard Worker      return rets
2164*b7c941bbSAndroid Build Coastguard Worker    else:
2165*b7c941bbSAndroid Build Coastguard Worker      return rets[0]
2166*b7c941bbSAndroid Build Coastguard Worker
2167*b7c941bbSAndroid Build Coastguard Worker  def do_vibrate(self, pattern):
2168*b7c941bbSAndroid Build Coastguard Worker    """Cause the device to vibrate to a specific pattern.
2169*b7c941bbSAndroid Build Coastguard Worker
2170*b7c941bbSAndroid Build Coastguard Worker    Args:
2171*b7c941bbSAndroid Build Coastguard Worker      pattern: Durations (ms) for which to turn on or off the vibrator.
2172*b7c941bbSAndroid Build Coastguard Worker      The first value indicates the number of milliseconds to wait
2173*b7c941bbSAndroid Build Coastguard Worker      before turning the vibrator on. The next value indicates the
2174*b7c941bbSAndroid Build Coastguard Worker      number of milliseconds for which to keep the vibrator on
2175*b7c941bbSAndroid Build Coastguard Worker      before turning it off. Subsequent values alternate between
2176*b7c941bbSAndroid Build Coastguard Worker      durations in milliseconds to turn the vibrator off or to turn
2177*b7c941bbSAndroid Build Coastguard Worker      the vibrator on.
2178*b7c941bbSAndroid Build Coastguard Worker
2179*b7c941bbSAndroid Build Coastguard Worker    Returns:
2180*b7c941bbSAndroid Build Coastguard Worker      Nothing.
2181*b7c941bbSAndroid Build Coastguard Worker    """
2182*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2183*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'doVibrate'
2184*b7c941bbSAndroid Build Coastguard Worker    cmd['pattern'] = pattern
2185*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2186*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2187*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'vibrationStarted':
2188*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid response for command: %s' %
2189*b7c941bbSAndroid Build Coastguard Worker                                      cmd[_CMD_NAME_STR])
2190*b7c941bbSAndroid Build Coastguard Worker
2191*b7c941bbSAndroid Build Coastguard Worker  def set_audio_restriction(self, mode):
2192*b7c941bbSAndroid Build Coastguard Worker    """Set the audio restriction mode for this camera device.
2193*b7c941bbSAndroid Build Coastguard Worker
2194*b7c941bbSAndroid Build Coastguard Worker    Args:
2195*b7c941bbSAndroid Build Coastguard Worker     mode: int; the audio restriction mode. See CameraDevice.java for valid
2196*b7c941bbSAndroid Build Coastguard Worker     value.
2197*b7c941bbSAndroid Build Coastguard Worker    Returns:
2198*b7c941bbSAndroid Build Coastguard Worker     Nothing.
2199*b7c941bbSAndroid Build Coastguard Worker    """
2200*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2201*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'setAudioRestriction'
2202*b7c941bbSAndroid Build Coastguard Worker    cmd['mode'] = mode
2203*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2204*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2205*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'audioRestrictionSet':
2206*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid response for command: %s' %
2207*b7c941bbSAndroid Build Coastguard Worker                                      cmd[_CMD_NAME_STR])
2208*b7c941bbSAndroid Build Coastguard Worker
2209*b7c941bbSAndroid Build Coastguard Worker  # pylint: disable=dangerous-default-value
2210*b7c941bbSAndroid Build Coastguard Worker  def do_3a(self,
2211*b7c941bbSAndroid Build Coastguard Worker            regions_ae=[[0, 0, 1, 1, 1]],
2212*b7c941bbSAndroid Build Coastguard Worker            regions_awb=[[0, 0, 1, 1, 1]],
2213*b7c941bbSAndroid Build Coastguard Worker            regions_af=[[0, 0, 1, 1, 1]],
2214*b7c941bbSAndroid Build Coastguard Worker            do_awb=True,
2215*b7c941bbSAndroid Build Coastguard Worker            do_af=True,
2216*b7c941bbSAndroid Build Coastguard Worker            lock_ae=False,
2217*b7c941bbSAndroid Build Coastguard Worker            lock_awb=False,
2218*b7c941bbSAndroid Build Coastguard Worker            get_results=False,
2219*b7c941bbSAndroid Build Coastguard Worker            ev_comp=0,
2220*b7c941bbSAndroid Build Coastguard Worker            auto_flash=False,
2221*b7c941bbSAndroid Build Coastguard Worker            mono_camera=False,
2222*b7c941bbSAndroid Build Coastguard Worker            zoom_ratio=None,
2223*b7c941bbSAndroid Build Coastguard Worker            out_surfaces=None,
2224*b7c941bbSAndroid Build Coastguard Worker            repeat_request=None,
2225*b7c941bbSAndroid Build Coastguard Worker            first_surface_for_3a=False,
2226*b7c941bbSAndroid Build Coastguard Worker            flash_mode=_FLASH_MODE_OFF):
2227*b7c941bbSAndroid Build Coastguard Worker    """Perform a 3A operation on the device.
2228*b7c941bbSAndroid Build Coastguard Worker
2229*b7c941bbSAndroid Build Coastguard Worker    Triggers some or all of AE, AWB, and AF, and returns once they have
2230*b7c941bbSAndroid Build Coastguard Worker    converged. Uses the vendor 3A that is implemented inside the HAL.
2231*b7c941bbSAndroid Build Coastguard Worker    Note: do_awb is always enabled regardless of do_awb flag
2232*b7c941bbSAndroid Build Coastguard Worker
2233*b7c941bbSAndroid Build Coastguard Worker    Throws an assertion if 3A fails to converge.
2234*b7c941bbSAndroid Build Coastguard Worker
2235*b7c941bbSAndroid Build Coastguard Worker    Args:
2236*b7c941bbSAndroid Build Coastguard Worker      regions_ae: List of weighted AE regions.
2237*b7c941bbSAndroid Build Coastguard Worker      regions_awb: List of weighted AWB regions.
2238*b7c941bbSAndroid Build Coastguard Worker      regions_af: List of weighted AF regions.
2239*b7c941bbSAndroid Build Coastguard Worker      do_awb: Wait for AWB to converge.
2240*b7c941bbSAndroid Build Coastguard Worker      do_af: Trigger AF and wait for it to converge.
2241*b7c941bbSAndroid Build Coastguard Worker      lock_ae: Request AE lock after convergence, and wait for it.
2242*b7c941bbSAndroid Build Coastguard Worker      lock_awb: Request AWB lock after convergence, and wait for it.
2243*b7c941bbSAndroid Build Coastguard Worker      get_results: Return the 3A results from this function.
2244*b7c941bbSAndroid Build Coastguard Worker      ev_comp: An EV compensation value to use when running AE.
2245*b7c941bbSAndroid Build Coastguard Worker      auto_flash: AE control boolean to enable auto flash.
2246*b7c941bbSAndroid Build Coastguard Worker      mono_camera: Boolean for monochrome camera.
2247*b7c941bbSAndroid Build Coastguard Worker      zoom_ratio: Zoom ratio. None if default zoom
2248*b7c941bbSAndroid Build Coastguard Worker      out_surfaces: dict; see do_capture() for specifications on out_surfaces.
2249*b7c941bbSAndroid Build Coastguard Worker        CameraCaptureSession will only be reused if out_surfaces is specified.
2250*b7c941bbSAndroid Build Coastguard Worker      repeat_request: repeating request list.
2251*b7c941bbSAndroid Build Coastguard Worker        See do_capture() for specifications on repeat_request.
2252*b7c941bbSAndroid Build Coastguard Worker      first_surface_for_3a: Use first surface in output_surfaces for 3A.
2253*b7c941bbSAndroid Build Coastguard Worker        Only applicable if out_surfaces contains at least 1 surface.
2254*b7c941bbSAndroid Build Coastguard Worker      flash_mode: FLASH_MODE to be used during 3A
2255*b7c941bbSAndroid Build Coastguard Worker        0: OFF
2256*b7c941bbSAndroid Build Coastguard Worker        1: SINGLE
2257*b7c941bbSAndroid Build Coastguard Worker        2: TORCH
2258*b7c941bbSAndroid Build Coastguard Worker
2259*b7c941bbSAndroid Build Coastguard Worker      Region format in args:
2260*b7c941bbSAndroid Build Coastguard Worker         Arguments are lists of weighted regions; each weighted region is a
2261*b7c941bbSAndroid Build Coastguard Worker         list of 5 values, [x, y, w, h, wgt], and each argument is a list of
2262*b7c941bbSAndroid Build Coastguard Worker         these 5-value lists. The coordinates are given as normalized
2263*b7c941bbSAndroid Build Coastguard Worker         rectangles (x, y, w, h) specifying the region. For example:
2264*b7c941bbSAndroid Build Coastguard Worker         [[0.0, 0.0, 1.0, 0.5, 5], [0.0, 0.5, 1.0, 0.5, 10]].
2265*b7c941bbSAndroid Build Coastguard Worker         Weights are non-negative integers.
2266*b7c941bbSAndroid Build Coastguard Worker
2267*b7c941bbSAndroid Build Coastguard Worker    Returns:
2268*b7c941bbSAndroid Build Coastguard Worker      Five values are returned if get_results is true:
2269*b7c941bbSAndroid Build Coastguard Worker      * AE sensitivity;
2270*b7c941bbSAndroid Build Coastguard Worker      * AE exposure time;
2271*b7c941bbSAndroid Build Coastguard Worker      * AWB gains (list);
2272*b7c941bbSAndroid Build Coastguard Worker      * AWB transform (list);
2273*b7c941bbSAndroid Build Coastguard Worker      * AF focus position; None if do_af is false
2274*b7c941bbSAndroid Build Coastguard Worker      Otherwise, it returns five None values.
2275*b7c941bbSAndroid Build Coastguard Worker    """
2276*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Running vendor 3A on device')
2277*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2278*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'do3A'
2279*b7c941bbSAndroid Build Coastguard Worker    reuse_session = False
2280*b7c941bbSAndroid Build Coastguard Worker    if out_surfaces:
2281*b7c941bbSAndroid Build Coastguard Worker      reuse_session = True
2282*b7c941bbSAndroid Build Coastguard Worker      if isinstance(out_surfaces, list):
2283*b7c941bbSAndroid Build Coastguard Worker        cmd['outputSurfaces'] = out_surfaces
2284*b7c941bbSAndroid Build Coastguard Worker      else:
2285*b7c941bbSAndroid Build Coastguard Worker        cmd['outputSurfaces'] = [out_surfaces]
2286*b7c941bbSAndroid Build Coastguard Worker    if repeat_request is None:
2287*b7c941bbSAndroid Build Coastguard Worker      cmd['repeatRequests'] = []
2288*b7c941bbSAndroid Build Coastguard Worker    elif not isinstance(repeat_request, list):
2289*b7c941bbSAndroid Build Coastguard Worker      cmd['repeatRequests'] = [repeat_request]
2290*b7c941bbSAndroid Build Coastguard Worker    else:
2291*b7c941bbSAndroid Build Coastguard Worker      cmd['repeatRequests'] = repeat_request
2292*b7c941bbSAndroid Build Coastguard Worker
2293*b7c941bbSAndroid Build Coastguard Worker    cmd['regions'] = {
2294*b7c941bbSAndroid Build Coastguard Worker        'ae': sum(regions_ae, []),
2295*b7c941bbSAndroid Build Coastguard Worker        'awb': sum(regions_awb, []),
2296*b7c941bbSAndroid Build Coastguard Worker        'af': sum(regions_af, [])
2297*b7c941bbSAndroid Build Coastguard Worker    }
2298*b7c941bbSAndroid Build Coastguard Worker    do_ae = True  # Always run AE
2299*b7c941bbSAndroid Build Coastguard Worker    cmd['triggers'] = {'ae': do_ae, 'af': do_af}
2300*b7c941bbSAndroid Build Coastguard Worker    if lock_ae:
2301*b7c941bbSAndroid Build Coastguard Worker      cmd['aeLock'] = True
2302*b7c941bbSAndroid Build Coastguard Worker    if lock_awb:
2303*b7c941bbSAndroid Build Coastguard Worker      cmd['awbLock'] = True
2304*b7c941bbSAndroid Build Coastguard Worker    if ev_comp != 0:
2305*b7c941bbSAndroid Build Coastguard Worker      cmd['evComp'] = ev_comp
2306*b7c941bbSAndroid Build Coastguard Worker    if flash_mode != 0:
2307*b7c941bbSAndroid Build Coastguard Worker      cmd['flashMode'] = flash_mode
2308*b7c941bbSAndroid Build Coastguard Worker    if auto_flash:
2309*b7c941bbSAndroid Build Coastguard Worker      cmd['autoFlash'] = True
2310*b7c941bbSAndroid Build Coastguard Worker    if self._hidden_physical_id:
2311*b7c941bbSAndroid Build Coastguard Worker      cmd['physicalId'] = self._hidden_physical_id
2312*b7c941bbSAndroid Build Coastguard Worker    if zoom_ratio:
2313*b7c941bbSAndroid Build Coastguard Worker      if self.zoom_ratio_within_range(zoom_ratio):
2314*b7c941bbSAndroid Build Coastguard Worker        cmd['zoomRatio'] = zoom_ratio
2315*b7c941bbSAndroid Build Coastguard Worker      else:
2316*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(f'Zoom ratio {zoom_ratio} out of range')
2317*b7c941bbSAndroid Build Coastguard Worker    cmd['reuseSession'] = reuse_session
2318*b7c941bbSAndroid Build Coastguard Worker    cmd['firstSurfaceFor3A'] = first_surface_for_3a
2319*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2320*b7c941bbSAndroid Build Coastguard Worker
2321*b7c941bbSAndroid Build Coastguard Worker    # Wait for each specified 3A to converge.
2322*b7c941bbSAndroid Build Coastguard Worker    ae_sens = None
2323*b7c941bbSAndroid Build Coastguard Worker    ae_exp = None
2324*b7c941bbSAndroid Build Coastguard Worker    awb_gains = None
2325*b7c941bbSAndroid Build Coastguard Worker    awb_transform = None
2326*b7c941bbSAndroid Build Coastguard Worker    af_dist = None
2327*b7c941bbSAndroid Build Coastguard Worker    converged = False
2328*b7c941bbSAndroid Build Coastguard Worker    while True:
2329*b7c941bbSAndroid Build Coastguard Worker      data, _ = self.__read_response_from_socket()
2330*b7c941bbSAndroid Build Coastguard Worker      vals = data[_STR_VALUE_STR].split()
2331*b7c941bbSAndroid Build Coastguard Worker      if data[_TAG_STR] == 'aeResult':
2332*b7c941bbSAndroid Build Coastguard Worker        if do_ae:
2333*b7c941bbSAndroid Build Coastguard Worker          ae_sens, ae_exp = [int(i) for i in vals]
2334*b7c941bbSAndroid Build Coastguard Worker      elif data[_TAG_STR] == 'afResult':
2335*b7c941bbSAndroid Build Coastguard Worker        if do_af:
2336*b7c941bbSAndroid Build Coastguard Worker          af_dist = float(vals[0])
2337*b7c941bbSAndroid Build Coastguard Worker      elif data[_TAG_STR] == 'awbResult':
2338*b7c941bbSAndroid Build Coastguard Worker        awb_gains = [float(f) for f in vals[:4]]
2339*b7c941bbSAndroid Build Coastguard Worker        awb_transform = [float(f) for f in vals[4:]]
2340*b7c941bbSAndroid Build Coastguard Worker      elif data[_TAG_STR] == '3aConverged':
2341*b7c941bbSAndroid Build Coastguard Worker        converged = True
2342*b7c941bbSAndroid Build Coastguard Worker      elif data[_TAG_STR] == '3aDone':
2343*b7c941bbSAndroid Build Coastguard Worker        break
2344*b7c941bbSAndroid Build Coastguard Worker      else:
2345*b7c941bbSAndroid Build Coastguard Worker        raise error_util.CameraItsError('Invalid command response')
2346*b7c941bbSAndroid Build Coastguard Worker    if converged and not get_results:
2347*b7c941bbSAndroid Build Coastguard Worker      return None, None, None, None, None
2348*b7c941bbSAndroid Build Coastguard Worker    if (do_ae and ae_sens is None or
2349*b7c941bbSAndroid Build Coastguard Worker        (not mono_camera and do_awb and awb_gains is None) or
2350*b7c941bbSAndroid Build Coastguard Worker        do_af and af_dist is None or not converged):
2351*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('3A failed to converge')
2352*b7c941bbSAndroid Build Coastguard Worker    return ae_sens, ae_exp, awb_gains, awb_transform, af_dist
2353*b7c941bbSAndroid Build Coastguard Worker
2354*b7c941bbSAndroid Build Coastguard Worker  def calc_camera_fov(self, props):
2355*b7c941bbSAndroid Build Coastguard Worker    """Determine the camera field of view from internal params.
2356*b7c941bbSAndroid Build Coastguard Worker
2357*b7c941bbSAndroid Build Coastguard Worker    Args:
2358*b7c941bbSAndroid Build Coastguard Worker      props: Camera properties object.
2359*b7c941bbSAndroid Build Coastguard Worker
2360*b7c941bbSAndroid Build Coastguard Worker    Returns:
2361*b7c941bbSAndroid Build Coastguard Worker      camera_fov: string; field of view for camera.
2362*b7c941bbSAndroid Build Coastguard Worker    """
2363*b7c941bbSAndroid Build Coastguard Worker
2364*b7c941bbSAndroid Build Coastguard Worker    focal_ls = props['android.lens.info.availableFocalLengths']
2365*b7c941bbSAndroid Build Coastguard Worker    if len(focal_ls) > 1:
2366*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Doing capture to determine logical camera focal length')
2367*b7c941bbSAndroid Build Coastguard Worker      cap = self.do_capture(capture_request_utils.auto_capture_request())
2368*b7c941bbSAndroid Build Coastguard Worker      focal_l = cap['metadata']['android.lens.focalLength']
2369*b7c941bbSAndroid Build Coastguard Worker    else:
2370*b7c941bbSAndroid Build Coastguard Worker      focal_l = focal_ls[0]
2371*b7c941bbSAndroid Build Coastguard Worker
2372*b7c941bbSAndroid Build Coastguard Worker    sensor_size = props['android.sensor.info.physicalSize']
2373*b7c941bbSAndroid Build Coastguard Worker    diag = math.sqrt(sensor_size['height']**2 + sensor_size['width']**2)
2374*b7c941bbSAndroid Build Coastguard Worker    try:
2375*b7c941bbSAndroid Build Coastguard Worker      fov = str(round(2 * math.degrees(math.atan(diag / (2 * focal_l))), 2))
2376*b7c941bbSAndroid Build Coastguard Worker    except ValueError:
2377*b7c941bbSAndroid Build Coastguard Worker      fov = str(0)
2378*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Calculated FoV: %s', fov)
2379*b7c941bbSAndroid Build Coastguard Worker    return fov
2380*b7c941bbSAndroid Build Coastguard Worker
2381*b7c941bbSAndroid Build Coastguard Worker  def get_file_name_to_load(self, chart_distance, camera_fov, scene):
2382*b7c941bbSAndroid Build Coastguard Worker    """Get the image to load on the tablet depending on fov and chart_distance.
2383*b7c941bbSAndroid Build Coastguard Worker
2384*b7c941bbSAndroid Build Coastguard Worker    Args:
2385*b7c941bbSAndroid Build Coastguard Worker     chart_distance: float; distance in cm from camera of displayed chart
2386*b7c941bbSAndroid Build Coastguard Worker     camera_fov: float; camera field of view.
2387*b7c941bbSAndroid Build Coastguard Worker     scene: String; Scene to be used in the test.
2388*b7c941bbSAndroid Build Coastguard Worker
2389*b7c941bbSAndroid Build Coastguard Worker    Returns:
2390*b7c941bbSAndroid Build Coastguard Worker     file_name: file name to display on the tablet.
2391*b7c941bbSAndroid Build Coastguard Worker
2392*b7c941bbSAndroid Build Coastguard Worker    """
2393*b7c941bbSAndroid Build Coastguard Worker    chart_scaling = opencv_processing_utils.calc_chart_scaling(
2394*b7c941bbSAndroid Build Coastguard Worker        chart_distance, camera_fov)
2395*b7c941bbSAndroid Build Coastguard Worker    if math.isclose(
2396*b7c941bbSAndroid Build Coastguard Worker        chart_scaling,
2397*b7c941bbSAndroid Build Coastguard Worker        opencv_processing_utils.SCALE_WIDE_IN_22CM_RIG,
2398*b7c941bbSAndroid Build Coastguard Worker        abs_tol=SCALING_TO_FILE_ATOL):
2399*b7c941bbSAndroid Build Coastguard Worker      file_name = f'{scene}_{opencv_processing_utils.SCALE_WIDE_IN_22CM_RIG}x_scaled.png'
2400*b7c941bbSAndroid Build Coastguard Worker    elif math.isclose(
2401*b7c941bbSAndroid Build Coastguard Worker        chart_scaling,
2402*b7c941bbSAndroid Build Coastguard Worker        opencv_processing_utils.SCALE_TELE_IN_22CM_RIG,
2403*b7c941bbSAndroid Build Coastguard Worker        abs_tol=SCALING_TO_FILE_ATOL):
2404*b7c941bbSAndroid Build Coastguard Worker      file_name = f'{scene}_{opencv_processing_utils.SCALE_TELE_IN_22CM_RIG}x_scaled.png'
2405*b7c941bbSAndroid Build Coastguard Worker    elif math.isclose(
2406*b7c941bbSAndroid Build Coastguard Worker        chart_scaling,
2407*b7c941bbSAndroid Build Coastguard Worker        opencv_processing_utils.SCALE_TELE25_IN_31CM_RIG,
2408*b7c941bbSAndroid Build Coastguard Worker        abs_tol=SCALING_TO_FILE_ATOL):
2409*b7c941bbSAndroid Build Coastguard Worker      file_name = f'{scene}_{opencv_processing_utils.SCALE_TELE25_IN_31CM_RIG}x_scaled.png'
2410*b7c941bbSAndroid Build Coastguard Worker    elif math.isclose(
2411*b7c941bbSAndroid Build Coastguard Worker        chart_scaling,
2412*b7c941bbSAndroid Build Coastguard Worker        opencv_processing_utils.SCALE_TELE40_IN_31CM_RIG,
2413*b7c941bbSAndroid Build Coastguard Worker        abs_tol=SCALING_TO_FILE_ATOL):
2414*b7c941bbSAndroid Build Coastguard Worker      file_name = f'{scene}_{opencv_processing_utils.SCALE_TELE40_IN_31CM_RIG}x_scaled.png'
2415*b7c941bbSAndroid Build Coastguard Worker    elif math.isclose(
2416*b7c941bbSAndroid Build Coastguard Worker        chart_scaling,
2417*b7c941bbSAndroid Build Coastguard Worker        opencv_processing_utils.SCALE_TELE_IN_31CM_RIG,
2418*b7c941bbSAndroid Build Coastguard Worker        abs_tol=SCALING_TO_FILE_ATOL):
2419*b7c941bbSAndroid Build Coastguard Worker      file_name = f'{scene}_{opencv_processing_utils.SCALE_TELE_IN_31CM_RIG}x_scaled.png'
2420*b7c941bbSAndroid Build Coastguard Worker    else:
2421*b7c941bbSAndroid Build Coastguard Worker      file_name = f'{scene}.png'
2422*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Scene to load: %s', file_name)
2423*b7c941bbSAndroid Build Coastguard Worker    return file_name
2424*b7c941bbSAndroid Build Coastguard Worker
2425*b7c941bbSAndroid Build Coastguard Worker  def is_stream_combination_supported(self, out_surfaces, settings=None):
2426*b7c941bbSAndroid Build Coastguard Worker    """Query whether out_surfaces combination and settings are supported by the camera device.
2427*b7c941bbSAndroid Build Coastguard Worker
2428*b7c941bbSAndroid Build Coastguard Worker    This function hooks up to the isSessionConfigurationSupported()/
2429*b7c941bbSAndroid Build Coastguard Worker    isSessionConfigurationWithSettingsSupported() camera API
2430*b7c941bbSAndroid Build Coastguard Worker    to query whether a particular stream combination and settings are supported.
2431*b7c941bbSAndroid Build Coastguard Worker
2432*b7c941bbSAndroid Build Coastguard Worker    Args:
2433*b7c941bbSAndroid Build Coastguard Worker      out_surfaces: dict; see do_capture() for specifications on out_surfaces.
2434*b7c941bbSAndroid Build Coastguard Worker      settings: dict; optional capture request settings metadata.
2435*b7c941bbSAndroid Build Coastguard Worker
2436*b7c941bbSAndroid Build Coastguard Worker    Returns:
2437*b7c941bbSAndroid Build Coastguard Worker      Boolean
2438*b7c941bbSAndroid Build Coastguard Worker    """
2439*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2440*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isStreamCombinationSupported'
2441*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
2442*b7c941bbSAndroid Build Coastguard Worker
2443*b7c941bbSAndroid Build Coastguard Worker    if isinstance(out_surfaces, list):
2444*b7c941bbSAndroid Build Coastguard Worker      cmd['outputSurfaces'] = out_surfaces
2445*b7c941bbSAndroid Build Coastguard Worker      for out_surface in out_surfaces:
2446*b7c941bbSAndroid Build Coastguard Worker        if self._hidden_physical_id:
2447*b7c941bbSAndroid Build Coastguard Worker          out_surface['physicalCamera'] = self._hidden_physical_id
2448*b7c941bbSAndroid Build Coastguard Worker    else:
2449*b7c941bbSAndroid Build Coastguard Worker      cmd['outputSurfaces'] = [out_surfaces]
2450*b7c941bbSAndroid Build Coastguard Worker      if self._hidden_physical_id:
2451*b7c941bbSAndroid Build Coastguard Worker        out_surfaces['physicalCamera'] = self._hidden_physical_id
2452*b7c941bbSAndroid Build Coastguard Worker
2453*b7c941bbSAndroid Build Coastguard Worker    if settings:
2454*b7c941bbSAndroid Build Coastguard Worker      cmd['settings'] = settings
2455*b7c941bbSAndroid Build Coastguard Worker
2456*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2457*b7c941bbSAndroid Build Coastguard Worker
2458*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2459*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'streamCombinationSupport':
2460*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query stream combination')
2461*b7c941bbSAndroid Build Coastguard Worker
2462*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'supportedCombination'
2463*b7c941bbSAndroid Build Coastguard Worker
2464*b7c941bbSAndroid Build Coastguard Worker  def is_camera_privacy_mode_supported(self):
2465*b7c941bbSAndroid Build Coastguard Worker    """Query whether the mobile device supports camera privacy mode.
2466*b7c941bbSAndroid Build Coastguard Worker
2467*b7c941bbSAndroid Build Coastguard Worker    This function checks whether the mobile device has FEATURE_CAMERA_TOGGLE
2468*b7c941bbSAndroid Build Coastguard Worker    feature support, which indicates the camera device can run in privacy mode.
2469*b7c941bbSAndroid Build Coastguard Worker
2470*b7c941bbSAndroid Build Coastguard Worker    Returns:
2471*b7c941bbSAndroid Build Coastguard Worker      Boolean
2472*b7c941bbSAndroid Build Coastguard Worker    """
2473*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2474*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isCameraPrivacyModeSupported'
2475*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2476*b7c941bbSAndroid Build Coastguard Worker
2477*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2478*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraPrivacyModeSupport':
2479*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query camera privacy mode'
2480*b7c941bbSAndroid Build Coastguard Worker                                      ' support')
2481*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
2482*b7c941bbSAndroid Build Coastguard Worker
2483*b7c941bbSAndroid Build Coastguard Worker  def is_primary_camera(self):
2484*b7c941bbSAndroid Build Coastguard Worker    """Query whether the camera device is a primary rear/front camera.
2485*b7c941bbSAndroid Build Coastguard Worker
2486*b7c941bbSAndroid Build Coastguard Worker    A primary rear/front facing camera is a camera device with the lowest
2487*b7c941bbSAndroid Build Coastguard Worker    camera Id for that facing.
2488*b7c941bbSAndroid Build Coastguard Worker
2489*b7c941bbSAndroid Build Coastguard Worker    Returns:
2490*b7c941bbSAndroid Build Coastguard Worker      Boolean
2491*b7c941bbSAndroid Build Coastguard Worker    """
2492*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2493*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isPrimaryCamera'
2494*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
2495*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2496*b7c941bbSAndroid Build Coastguard Worker
2497*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2498*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'primaryCamera':
2499*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query primary camera')
2500*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
2501*b7c941bbSAndroid Build Coastguard Worker
2502*b7c941bbSAndroid Build Coastguard Worker  def is_performance_class(self):
2503*b7c941bbSAndroid Build Coastguard Worker    """Query whether the mobile device is an R or S performance class device.
2504*b7c941bbSAndroid Build Coastguard Worker
2505*b7c941bbSAndroid Build Coastguard Worker    Returns:
2506*b7c941bbSAndroid Build Coastguard Worker      Boolean
2507*b7c941bbSAndroid Build Coastguard Worker    """
2508*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2509*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isPerformanceClass'
2510*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2511*b7c941bbSAndroid Build Coastguard Worker
2512*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2513*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'performanceClass':
2514*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query performance class')
2515*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
2516*b7c941bbSAndroid Build Coastguard Worker
2517*b7c941bbSAndroid Build Coastguard Worker  def is_vic_performance_class(self):
2518*b7c941bbSAndroid Build Coastguard Worker    """Return whether the mobile device is VIC performance class device.
2519*b7c941bbSAndroid Build Coastguard Worker    """
2520*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2521*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'isVicPerformanceClass'
2522*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2523*b7c941bbSAndroid Build Coastguard Worker
2524*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2525*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'vicPerformanceClass':
2526*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to query performance class')
2527*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
2528*b7c941bbSAndroid Build Coastguard Worker
2529*b7c941bbSAndroid Build Coastguard Worker  def measure_camera_launch_ms(self):
2530*b7c941bbSAndroid Build Coastguard Worker    """Measure camera launch latency in millisecond, from open to first frame.
2531*b7c941bbSAndroid Build Coastguard Worker
2532*b7c941bbSAndroid Build Coastguard Worker    Returns:
2533*b7c941bbSAndroid Build Coastguard Worker      Camera launch latency from camera open to receipt of first frame
2534*b7c941bbSAndroid Build Coastguard Worker    """
2535*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2536*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'measureCameraLaunchMs'
2537*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
2538*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2539*b7c941bbSAndroid Build Coastguard Worker
2540*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT_FOR_PERF_MEASURE
2541*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
2542*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2543*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(self.SOCK_TIMEOUT)
2544*b7c941bbSAndroid Build Coastguard Worker
2545*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'cameraLaunchMs':
2546*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Failed to measure camera launch latency')
2547*b7c941bbSAndroid Build Coastguard Worker    return float(data[_STR_VALUE_STR])
2548*b7c941bbSAndroid Build Coastguard Worker
2549*b7c941bbSAndroid Build Coastguard Worker  def measure_camera_1080p_jpeg_capture_ms(self):
2550*b7c941bbSAndroid Build Coastguard Worker    """Measure camera 1080P jpeg capture latency in milliseconds.
2551*b7c941bbSAndroid Build Coastguard Worker
2552*b7c941bbSAndroid Build Coastguard Worker    Returns:
2553*b7c941bbSAndroid Build Coastguard Worker      Camera jpeg capture latency in milliseconds
2554*b7c941bbSAndroid Build Coastguard Worker    """
2555*b7c941bbSAndroid Build Coastguard Worker    cmd = {}
2556*b7c941bbSAndroid Build Coastguard Worker    cmd[_CMD_NAME_STR] = 'measureCamera1080pJpegCaptureMs'
2557*b7c941bbSAndroid Build Coastguard Worker    cmd[_CAMERA_ID_STR] = self._camera_id
2558*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2559*b7c941bbSAndroid Build Coastguard Worker
2560*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT_FOR_PERF_MEASURE
2561*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
2562*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2563*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(self.SOCK_TIMEOUT)
2564*b7c941bbSAndroid Build Coastguard Worker
2565*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'camera1080pJpegCaptureMs':
2566*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError(
2567*b7c941bbSAndroid Build Coastguard Worker          'Failed to measure camera 1080p jpeg capture latency')
2568*b7c941bbSAndroid Build Coastguard Worker    return float(data[_STR_VALUE_STR])
2569*b7c941bbSAndroid Build Coastguard Worker
2570*b7c941bbSAndroid Build Coastguard Worker  def _camera_id_to_props(self):
2571*b7c941bbSAndroid Build Coastguard Worker    """Return the properties of each camera ID."""
2572*b7c941bbSAndroid Build Coastguard Worker    unparsed_ids = self.get_camera_ids().get('cameraIdArray', [])
2573*b7c941bbSAndroid Build Coastguard Worker    parsed_ids = parse_camera_ids(unparsed_ids)
2574*b7c941bbSAndroid Build Coastguard Worker    id_to_props = {}
2575*b7c941bbSAndroid Build Coastguard Worker    for unparsed_id, id_combo in zip(unparsed_ids, parsed_ids):
2576*b7c941bbSAndroid Build Coastguard Worker      if id_combo.sub_id is None:
2577*b7c941bbSAndroid Build Coastguard Worker        props = self.get_camera_properties_by_id(id_combo.id)
2578*b7c941bbSAndroid Build Coastguard Worker      else:
2579*b7c941bbSAndroid Build Coastguard Worker        props = self.get_camera_properties_by_id(id_combo.sub_id)
2580*b7c941bbSAndroid Build Coastguard Worker      id_to_props[unparsed_id] = props
2581*b7c941bbSAndroid Build Coastguard Worker    if not id_to_props:
2582*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError('No camera IDs were found.')
2583*b7c941bbSAndroid Build Coastguard Worker    return id_to_props
2584*b7c941bbSAndroid Build Coastguard Worker
2585*b7c941bbSAndroid Build Coastguard Worker  def has_ultrawide_camera(self, facing):
2586*b7c941bbSAndroid Build Coastguard Worker    """Return if device has an ultrawide camera facing the same direction.
2587*b7c941bbSAndroid Build Coastguard Worker
2588*b7c941bbSAndroid Build Coastguard Worker    Args:
2589*b7c941bbSAndroid Build Coastguard Worker      facing: constant describing the direction the camera device lens faces.
2590*b7c941bbSAndroid Build Coastguard Worker
2591*b7c941bbSAndroid Build Coastguard Worker    Returns:
2592*b7c941bbSAndroid Build Coastguard Worker      True if the device has an ultrawide camera facing in that direction.
2593*b7c941bbSAndroid Build Coastguard Worker    """
2594*b7c941bbSAndroid Build Coastguard Worker    camera_ids = self.get_camera_ids()
2595*b7c941bbSAndroid Build Coastguard Worker    primary_rear_camera_id = camera_ids.get('primaryRearCameraId', '')
2596*b7c941bbSAndroid Build Coastguard Worker    primary_front_camera_id = camera_ids.get('primaryFrontCameraId', '')
2597*b7c941bbSAndroid Build Coastguard Worker    if facing == camera_properties_utils.LENS_FACING['BACK']:
2598*b7c941bbSAndroid Build Coastguard Worker      primary_camera_id = primary_rear_camera_id
2599*b7c941bbSAndroid Build Coastguard Worker    elif facing == camera_properties_utils.LENS_FACING['FRONT']:
2600*b7c941bbSAndroid Build Coastguard Worker      primary_camera_id = primary_front_camera_id
2601*b7c941bbSAndroid Build Coastguard Worker    else:
2602*b7c941bbSAndroid Build Coastguard Worker      raise NotImplementedError('Cameras not facing either front or back '
2603*b7c941bbSAndroid Build Coastguard Worker                                'are currently unsupported.')
2604*b7c941bbSAndroid Build Coastguard Worker    id_to_props = self._camera_id_to_props()
2605*b7c941bbSAndroid Build Coastguard Worker    fov_and_facing = collections.namedtuple('FovAndFacing', ['fov', 'facing'])
2606*b7c941bbSAndroid Build Coastguard Worker    id_to_fov_facing = {
2607*b7c941bbSAndroid Build Coastguard Worker        unparsed_id: fov_and_facing(
2608*b7c941bbSAndroid Build Coastguard Worker            self.calc_camera_fov(props), props['android.lens.facing']
2609*b7c941bbSAndroid Build Coastguard Worker        )
2610*b7c941bbSAndroid Build Coastguard Worker        for unparsed_id, props in id_to_props.items()
2611*b7c941bbSAndroid Build Coastguard Worker    }
2612*b7c941bbSAndroid Build Coastguard Worker    logging.debug('IDs to (FOVs, facing): %s', id_to_fov_facing)
2613*b7c941bbSAndroid Build Coastguard Worker    primary_camera_fov, primary_camera_facing = id_to_fov_facing[
2614*b7c941bbSAndroid Build Coastguard Worker        primary_camera_id]
2615*b7c941bbSAndroid Build Coastguard Worker    for unparsed_id, fov_facing_combo in id_to_fov_facing.items():
2616*b7c941bbSAndroid Build Coastguard Worker      if (float(fov_facing_combo.fov) > float(primary_camera_fov) and
2617*b7c941bbSAndroid Build Coastguard Worker          fov_facing_combo.facing == primary_camera_facing and
2618*b7c941bbSAndroid Build Coastguard Worker          unparsed_id != primary_camera_id):
2619*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Ultrawide camera found with ID %s and FoV %.3f. '
2620*b7c941bbSAndroid Build Coastguard Worker                      'Primary camera has ID %s and FoV: %.3f.',
2621*b7c941bbSAndroid Build Coastguard Worker                      unparsed_id, float(fov_facing_combo.fov),
2622*b7c941bbSAndroid Build Coastguard Worker                      primary_camera_id, float(primary_camera_fov))
2623*b7c941bbSAndroid Build Coastguard Worker        return True
2624*b7c941bbSAndroid Build Coastguard Worker    return False
2625*b7c941bbSAndroid Build Coastguard Worker
2626*b7c941bbSAndroid Build Coastguard Worker  def get_facing_to_ids(self):
2627*b7c941bbSAndroid Build Coastguard Worker    """Returns mapping from lens facing to list of corresponding camera IDs."""
2628*b7c941bbSAndroid Build Coastguard Worker    id_to_props = self._camera_id_to_props()
2629*b7c941bbSAndroid Build Coastguard Worker    facing_to_ids = collections.defaultdict(list)
2630*b7c941bbSAndroid Build Coastguard Worker    for unparsed_id, props in id_to_props.items():
2631*b7c941bbSAndroid Build Coastguard Worker      facing_to_ids[props['android.lens.facing']].append(unparsed_id)
2632*b7c941bbSAndroid Build Coastguard Worker    for ids in facing_to_ids.values():
2633*b7c941bbSAndroid Build Coastguard Worker      ids.sort()
2634*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Facing to camera IDs: %s', facing_to_ids)
2635*b7c941bbSAndroid Build Coastguard Worker    return facing_to_ids
2636*b7c941bbSAndroid Build Coastguard Worker
2637*b7c941bbSAndroid Build Coastguard Worker  def is_low_light_boost_available(self, camera_id, extension=-1):
2638*b7c941bbSAndroid Build Coastguard Worker    """Checks if low light boost is available for camera id and extension.
2639*b7c941bbSAndroid Build Coastguard Worker
2640*b7c941bbSAndroid Build Coastguard Worker    If the extension is not provided (or -1) then low light boost support is
2641*b7c941bbSAndroid Build Coastguard Worker    checked for a camera2 session.
2642*b7c941bbSAndroid Build Coastguard Worker
2643*b7c941bbSAndroid Build Coastguard Worker    Args:
2644*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device ID
2645*b7c941bbSAndroid Build Coastguard Worker      extension: int; extension type
2646*b7c941bbSAndroid Build Coastguard Worker    Returns:
2647*b7c941bbSAndroid Build Coastguard Worker      True if low light boost is available and false otherwise.
2648*b7c941bbSAndroid Build Coastguard Worker    """
2649*b7c941bbSAndroid Build Coastguard Worker    cmd = {
2650*b7c941bbSAndroid Build Coastguard Worker        'cmdName': 'isLowLightBoostAvailable',
2651*b7c941bbSAndroid Build Coastguard Worker        'cameraId': camera_id,
2652*b7c941bbSAndroid Build Coastguard Worker        'extension': extension
2653*b7c941bbSAndroid Build Coastguard Worker    }
2654*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2655*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
2656*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
2657*b7c941bbSAndroid Build Coastguard Worker    data, _ = self.__read_response_from_socket()
2658*b7c941bbSAndroid Build Coastguard Worker    if data['tag'] != 'isLowLightBoostAvailable':
2659*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
2660*b7c941bbSAndroid Build Coastguard Worker    return data[_STR_VALUE_STR] == 'true'
2661*b7c941bbSAndroid Build Coastguard Worker
2662*b7c941bbSAndroid Build Coastguard Worker  def do_capture_preview_frame(self,
2663*b7c941bbSAndroid Build Coastguard Worker                               camera_id,
2664*b7c941bbSAndroid Build Coastguard Worker                               preview_size,
2665*b7c941bbSAndroid Build Coastguard Worker                               frame_num=0,
2666*b7c941bbSAndroid Build Coastguard Worker                               extension=-1,
2667*b7c941bbSAndroid Build Coastguard Worker                               cap_request={}):
2668*b7c941bbSAndroid Build Coastguard Worker    """Captures the nth preview frame from the preview stream.
2669*b7c941bbSAndroid Build Coastguard Worker
2670*b7c941bbSAndroid Build Coastguard Worker    By default the 0th frame is the first frame. The extension type can also be
2671*b7c941bbSAndroid Build Coastguard Worker    provided or -1 to use Camera2 which is the default.
2672*b7c941bbSAndroid Build Coastguard Worker
2673*b7c941bbSAndroid Build Coastguard Worker    Args:
2674*b7c941bbSAndroid Build Coastguard Worker      camera_id: int; device ID
2675*b7c941bbSAndroid Build Coastguard Worker      preview_size: int; preview size
2676*b7c941bbSAndroid Build Coastguard Worker      frame_num: int; frame number to capture
2677*b7c941bbSAndroid Build Coastguard Worker      extension: int; extension type
2678*b7c941bbSAndroid Build Coastguard Worker      cap_request: dict; python dict specifying the key/value pair of capture
2679*b7c941bbSAndroid Build Coastguard Worker        request keys, which will be converted to JSON and sent to the device.
2680*b7c941bbSAndroid Build Coastguard Worker    Returns:
2681*b7c941bbSAndroid Build Coastguard Worker      Single JPEG frame capture as numpy array of bytes
2682*b7c941bbSAndroid Build Coastguard Worker    """
2683*b7c941bbSAndroid Build Coastguard Worker    cmd = {
2684*b7c941bbSAndroid Build Coastguard Worker        'cmdName': 'doCapturePreviewFrame',
2685*b7c941bbSAndroid Build Coastguard Worker        'cameraId': camera_id,
2686*b7c941bbSAndroid Build Coastguard Worker        'previewSize': preview_size,
2687*b7c941bbSAndroid Build Coastguard Worker        'frameNum': frame_num,
2688*b7c941bbSAndroid Build Coastguard Worker        'extension': extension,
2689*b7c941bbSAndroid Build Coastguard Worker        'captureRequest': cap_request,
2690*b7c941bbSAndroid Build Coastguard Worker    }
2691*b7c941bbSAndroid Build Coastguard Worker    self.sock.send(json.dumps(cmd).encode() + '\n'.encode())
2692*b7c941bbSAndroid Build Coastguard Worker    timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
2693*b7c941bbSAndroid Build Coastguard Worker    self.sock.settimeout(timeout)
2694*b7c941bbSAndroid Build Coastguard Worker    data, buf = self.__read_response_from_socket()
2695*b7c941bbSAndroid Build Coastguard Worker    if data[_TAG_STR] != 'jpegImage':
2696*b7c941bbSAndroid Build Coastguard Worker      raise error_util.CameraItsError('Invalid command response')
2697*b7c941bbSAndroid Build Coastguard Worker    return buf
2698*b7c941bbSAndroid Build Coastguard Worker
2699*b7c941bbSAndroid Build Coastguard Worker  def preview_surface(self, size, hlg10_enabled=False):
2700*b7c941bbSAndroid Build Coastguard Worker    """Create a surface dictionary based on size and hdr-ness.
2701*b7c941bbSAndroid Build Coastguard Worker
2702*b7c941bbSAndroid Build Coastguard Worker    Args:
2703*b7c941bbSAndroid Build Coastguard Worker      size: str, Resolution of an output surface. ex. "1920x1080"
2704*b7c941bbSAndroid Build Coastguard Worker      hlg10_enabled: boolean; Whether the output is hlg10 or not.
2705*b7c941bbSAndroid Build Coastguard Worker
2706*b7c941bbSAndroid Build Coastguard Worker    Returns:
2707*b7c941bbSAndroid Build Coastguard Worker      a dictionary object containing format, size, and hdr-ness.
2708*b7c941bbSAndroid Build Coastguard Worker    """
2709*b7c941bbSAndroid Build Coastguard Worker    surface = {
2710*b7c941bbSAndroid Build Coastguard Worker        'format': 'priv',
2711*b7c941bbSAndroid Build Coastguard Worker        'width': int(size.split('x')[0]),
2712*b7c941bbSAndroid Build Coastguard Worker        'height': int(size.split('x')[1]),
2713*b7c941bbSAndroid Build Coastguard Worker        'hlg10': hlg10_enabled
2714*b7c941bbSAndroid Build Coastguard Worker    }
2715*b7c941bbSAndroid Build Coastguard Worker    if self._hidden_physical_id:
2716*b7c941bbSAndroid Build Coastguard Worker      surface['physicalCamera'] = self._hidden_physical_id
2717*b7c941bbSAndroid Build Coastguard Worker    return [surface]
2718*b7c941bbSAndroid Build Coastguard Worker
2719*b7c941bbSAndroid Build Coastguard Worker
2720*b7c941bbSAndroid Build Coastguard Workerdef parse_camera_ids(ids):
2721*b7c941bbSAndroid Build Coastguard Worker  """Parse the string of camera IDs into array of CameraIdCombo tuples.
2722*b7c941bbSAndroid Build Coastguard Worker
2723*b7c941bbSAndroid Build Coastguard Worker  Args:
2724*b7c941bbSAndroid Build Coastguard Worker   ids: List of camera ids.
2725*b7c941bbSAndroid Build Coastguard Worker
2726*b7c941bbSAndroid Build Coastguard Worker  Returns:
2727*b7c941bbSAndroid Build Coastguard Worker   Array of CameraIdCombo
2728*b7c941bbSAndroid Build Coastguard Worker  """
2729*b7c941bbSAndroid Build Coastguard Worker  camera_id_combo = collections.namedtuple('CameraIdCombo', ['id', 'sub_id'])
2730*b7c941bbSAndroid Build Coastguard Worker  id_combos = []
2731*b7c941bbSAndroid Build Coastguard Worker  for one_id in ids:
2732*b7c941bbSAndroid Build Coastguard Worker    one_combo = one_id.split(SUB_CAMERA_SEPARATOR)
2733*b7c941bbSAndroid Build Coastguard Worker    if len(one_combo) == 1:
2734*b7c941bbSAndroid Build Coastguard Worker      id_combos.append(camera_id_combo(one_combo[0], None))
2735*b7c941bbSAndroid Build Coastguard Worker    elif len(one_combo) == 2:
2736*b7c941bbSAndroid Build Coastguard Worker      id_combos.append(camera_id_combo(one_combo[0], one_combo[1]))
2737*b7c941bbSAndroid Build Coastguard Worker    else:
2738*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError('Camera id parameters must be either ID or '
2739*b7c941bbSAndroid Build Coastguard Worker                           f'ID{SUB_CAMERA_SEPARATOR}SUB_ID')
2740*b7c941bbSAndroid Build Coastguard Worker  return id_combos
2741*b7c941bbSAndroid Build Coastguard Worker
2742*b7c941bbSAndroid Build Coastguard Worker
2743*b7c941bbSAndroid Build Coastguard Workerdef do_capture_with_latency(cam, req, sync_latency, fmt=None):
2744*b7c941bbSAndroid Build Coastguard Worker  """Helper function to take enough frames to allow sync latency.
2745*b7c941bbSAndroid Build Coastguard Worker
2746*b7c941bbSAndroid Build Coastguard Worker  Args:
2747*b7c941bbSAndroid Build Coastguard Worker    cam: camera object
2748*b7c941bbSAndroid Build Coastguard Worker    req: request for camera
2749*b7c941bbSAndroid Build Coastguard Worker    sync_latency: integer number of frames
2750*b7c941bbSAndroid Build Coastguard Worker    fmt: format for the capture
2751*b7c941bbSAndroid Build Coastguard Worker  Returns:
2752*b7c941bbSAndroid Build Coastguard Worker    single capture with the unsettled frames discarded
2753*b7c941bbSAndroid Build Coastguard Worker  """
2754*b7c941bbSAndroid Build Coastguard Worker  caps = cam.do_capture([req]*(sync_latency+1), fmt)
2755*b7c941bbSAndroid Build Coastguard Worker  return caps[-1]
2756*b7c941bbSAndroid Build Coastguard Worker
2757*b7c941bbSAndroid Build Coastguard Worker
2758*b7c941bbSAndroid Build Coastguard Workerdef load_scene(cam, props, scene, tablet, chart_distance, lighting_check=True,
2759*b7c941bbSAndroid Build Coastguard Worker               log_path=None):
2760*b7c941bbSAndroid Build Coastguard Worker  """Load the scene for the camera based on the FOV.
2761*b7c941bbSAndroid Build Coastguard Worker
2762*b7c941bbSAndroid Build Coastguard Worker  Args:
2763*b7c941bbSAndroid Build Coastguard Worker    cam: camera object
2764*b7c941bbSAndroid Build Coastguard Worker    props: camera properties
2765*b7c941bbSAndroid Build Coastguard Worker    scene: scene to be loaded
2766*b7c941bbSAndroid Build Coastguard Worker    tablet: tablet to load scene on
2767*b7c941bbSAndroid Build Coastguard Worker    chart_distance: distance to tablet
2768*b7c941bbSAndroid Build Coastguard Worker    lighting_check: Boolean for lighting check enabled
2769*b7c941bbSAndroid Build Coastguard Worker    log_path: [Optional] path to store artifacts
2770*b7c941bbSAndroid Build Coastguard Worker  """
2771*b7c941bbSAndroid Build Coastguard Worker  if not tablet:
2772*b7c941bbSAndroid Build Coastguard Worker    logging.info('Manual run: no tablet to load scene on.')
2773*b7c941bbSAndroid Build Coastguard Worker    return
2774*b7c941bbSAndroid Build Coastguard Worker  # Calculate camera_fov, which determines the image/video to load on tablet.
2775*b7c941bbSAndroid Build Coastguard Worker  camera_fov = cam.calc_camera_fov(props)
2776*b7c941bbSAndroid Build Coastguard Worker  file_name = cam.get_file_name_to_load(chart_distance, camera_fov, scene)
2777*b7c941bbSAndroid Build Coastguard Worker  if 'scene' not in file_name:
2778*b7c941bbSAndroid Build Coastguard Worker    file_name = f'scene{file_name}'
2779*b7c941bbSAndroid Build Coastguard Worker  if scene in VIDEO_SCENES:
2780*b7c941bbSAndroid Build Coastguard Worker    root_file_name, _ = os.path.splitext(file_name)
2781*b7c941bbSAndroid Build Coastguard Worker    file_name = root_file_name + '.mp4'
2782*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Displaying %s on the tablet', file_name)
2783*b7c941bbSAndroid Build Coastguard Worker
2784*b7c941bbSAndroid Build Coastguard Worker  # Display the image/video on the tablet using the default media player.
2785*b7c941bbSAndroid Build Coastguard Worker  view_file_type = 'image/png' if scene not in VIDEO_SCENES else 'video/mp4'
2786*b7c941bbSAndroid Build Coastguard Worker  uri_prefix = 'file://mnt' if scene not in VIDEO_SCENES else ''
2787*b7c941bbSAndroid Build Coastguard Worker  tablet.adb.shell(
2788*b7c941bbSAndroid Build Coastguard Worker      f'am start -a android.intent.action.VIEW -t {view_file_type} '
2789*b7c941bbSAndroid Build Coastguard Worker      f'-d {uri_prefix}/sdcard/Download/{file_name}')
2790*b7c941bbSAndroid Build Coastguard Worker  time.sleep(LOAD_SCENE_DELAY_SEC)
2791*b7c941bbSAndroid Build Coastguard Worker  # Tap tablet to remove gallery buttons
2792*b7c941bbSAndroid Build Coastguard Worker  tablet.adb.shell(
2793*b7c941bbSAndroid Build Coastguard Worker      f'input tap {TAP_COORDINATES[0]} {TAP_COORDINATES[1]}')
2794*b7c941bbSAndroid Build Coastguard Worker  rfov_camera_in_rfov_box = (
2795*b7c941bbSAndroid Build Coastguard Worker      math.isclose(
2796*b7c941bbSAndroid Build Coastguard Worker          chart_distance,
2797*b7c941bbSAndroid Build Coastguard Worker          opencv_processing_utils.CHART_DISTANCE_31CM, rel_tol=0.1) and
2798*b7c941bbSAndroid Build Coastguard Worker      opencv_processing_utils.FOV_THRESH_TELE <= float(camera_fov)
2799*b7c941bbSAndroid Build Coastguard Worker      <= opencv_processing_utils.FOV_THRESH_UW)
2800*b7c941bbSAndroid Build Coastguard Worker  wfov_camera_in_wfov_box = (
2801*b7c941bbSAndroid Build Coastguard Worker      math.isclose(
2802*b7c941bbSAndroid Build Coastguard Worker          chart_distance,
2803*b7c941bbSAndroid Build Coastguard Worker          opencv_processing_utils.CHART_DISTANCE_22CM, rel_tol=0.1) and
2804*b7c941bbSAndroid Build Coastguard Worker      float(camera_fov) > opencv_processing_utils.FOV_THRESH_UW)
2805*b7c941bbSAndroid Build Coastguard Worker  if (rfov_camera_in_rfov_box or wfov_camera_in_wfov_box) and lighting_check:
2806*b7c941bbSAndroid Build Coastguard Worker    cam.do_3a()
2807*b7c941bbSAndroid Build Coastguard Worker    cap = cam.do_capture(
2808*b7c941bbSAndroid Build Coastguard Worker        capture_request_utils.auto_capture_request(), cam.CAP_YUV)
2809*b7c941bbSAndroid Build Coastguard Worker    y_plane, _, _ = image_processing_utils.convert_capture_to_planes(cap)
2810*b7c941bbSAndroid Build Coastguard Worker    validate_lighting(y_plane, scene, log_path=log_path, fov=float(camera_fov))
2811*b7c941bbSAndroid Build Coastguard Worker
2812*b7c941bbSAndroid Build Coastguard Worker
2813*b7c941bbSAndroid Build Coastguard Workerdef copy_scenes_to_tablet(scene, tablet_id):
2814*b7c941bbSAndroid Build Coastguard Worker  """Copies scenes onto the tablet before running the tests.
2815*b7c941bbSAndroid Build Coastguard Worker
2816*b7c941bbSAndroid Build Coastguard Worker  Args:
2817*b7c941bbSAndroid Build Coastguard Worker    scene: Name of the scene to copy image files.
2818*b7c941bbSAndroid Build Coastguard Worker    tablet_id: device id of tablet
2819*b7c941bbSAndroid Build Coastguard Worker  """
2820*b7c941bbSAndroid Build Coastguard Worker  logging.info('Copying files to tablet: %s', tablet_id)
2821*b7c941bbSAndroid Build Coastguard Worker  scene_path = os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests', scene)
2822*b7c941bbSAndroid Build Coastguard Worker  scene_dir = os.listdir(scene_path)
2823*b7c941bbSAndroid Build Coastguard Worker  for file_name in scene_dir:
2824*b7c941bbSAndroid Build Coastguard Worker    if file_name.endswith('.png') or file_name.endswith('.mp4'):
2825*b7c941bbSAndroid Build Coastguard Worker      src_scene_file = os.path.join(scene_path, file_name)
2826*b7c941bbSAndroid Build Coastguard Worker      cmd = f'adb -s {tablet_id} push {src_scene_file} {_DST_SCENE_DIR}'
2827*b7c941bbSAndroid Build Coastguard Worker      subprocess.Popen(cmd.split())
2828*b7c941bbSAndroid Build Coastguard Worker  time.sleep(_COPY_SCENE_DELAY_SEC)
2829*b7c941bbSAndroid Build Coastguard Worker  logging.info('Finished copying files to tablet.')
2830*b7c941bbSAndroid Build Coastguard Worker
2831*b7c941bbSAndroid Build Coastguard Worker
2832*b7c941bbSAndroid Build Coastguard Workerdef validate_lighting(y_plane, scene, state='ON', log_path=None,
2833*b7c941bbSAndroid Build Coastguard Worker                      tablet_state='ON', fov=None):
2834*b7c941bbSAndroid Build Coastguard Worker  """Validates the lighting level in scene corners based on empirical values.
2835*b7c941bbSAndroid Build Coastguard Worker
2836*b7c941bbSAndroid Build Coastguard Worker  Args:
2837*b7c941bbSAndroid Build Coastguard Worker    y_plane: Y plane of YUV image
2838*b7c941bbSAndroid Build Coastguard Worker    scene: scene name
2839*b7c941bbSAndroid Build Coastguard Worker    state: string 'ON' or 'OFF'
2840*b7c941bbSAndroid Build Coastguard Worker    log_path: [Optional] path to store artifacts
2841*b7c941bbSAndroid Build Coastguard Worker    tablet_state: string 'ON' or 'OFF'
2842*b7c941bbSAndroid Build Coastguard Worker    fov: [Optional] float, calculated camera FoV
2843*b7c941bbSAndroid Build Coastguard Worker
2844*b7c941bbSAndroid Build Coastguard Worker  Returns:
2845*b7c941bbSAndroid Build Coastguard Worker    boolean True if lighting validated, else raise AssertionError
2846*b7c941bbSAndroid Build Coastguard Worker  """
2847*b7c941bbSAndroid Build Coastguard Worker  logging.debug('Validating lighting levels.')
2848*b7c941bbSAndroid Build Coastguard Worker  file_name = f'validate_lighting_{scene}.jpg'
2849*b7c941bbSAndroid Build Coastguard Worker  if log_path:
2850*b7c941bbSAndroid Build Coastguard Worker    file_name = os.path.join(log_path, f'validate_lighting_{scene}.jpg')
2851*b7c941bbSAndroid Build Coastguard Worker
2852*b7c941bbSAndroid Build Coastguard Worker  if tablet_state == 'OFF':
2853*b7c941bbSAndroid Build Coastguard Worker    validate_lighting_thresh = _VALIDATE_LIGHTING_THRESH_DARK
2854*b7c941bbSAndroid Build Coastguard Worker  else:
2855*b7c941bbSAndroid Build Coastguard Worker    validate_lighting_thresh = _VALIDATE_LIGHTING_THRESH
2856*b7c941bbSAndroid Build Coastguard Worker
2857*b7c941bbSAndroid Build Coastguard Worker  validate_lighting_regions = _VALIDATE_LIGHTING_REGIONS
2858*b7c941bbSAndroid Build Coastguard Worker  if fov and fov > _VALIDATE_LIGHTING_MACRO_FOV_THRESH:
2859*b7c941bbSAndroid Build Coastguard Worker    validate_lighting_regions = _VALIDATE_LIGHTING_REGIONS_MODULAR_UW
2860*b7c941bbSAndroid Build Coastguard Worker
2861*b7c941bbSAndroid Build Coastguard Worker  # Test patches from each corner.
2862*b7c941bbSAndroid Build Coastguard Worker  for location, coordinates in validate_lighting_regions.items():
2863*b7c941bbSAndroid Build Coastguard Worker    patch = image_processing_utils.get_image_patch(
2864*b7c941bbSAndroid Build Coastguard Worker        y_plane, coordinates[0], coordinates[1],
2865*b7c941bbSAndroid Build Coastguard Worker        _VALIDATE_LIGHTING_PATCH_W, _VALIDATE_LIGHTING_PATCH_H)
2866*b7c941bbSAndroid Build Coastguard Worker    y_mean = image_processing_utils.compute_image_means(patch)[0]
2867*b7c941bbSAndroid Build Coastguard Worker    logging.debug('%s corner Y mean: %.3f', location, y_mean)
2868*b7c941bbSAndroid Build Coastguard Worker    if state == 'ON':
2869*b7c941bbSAndroid Build Coastguard Worker      if y_mean > validate_lighting_thresh:
2870*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Lights ON in test rig.')
2871*b7c941bbSAndroid Build Coastguard Worker        return True
2872*b7c941bbSAndroid Build Coastguard Worker      else:
2873*b7c941bbSAndroid Build Coastguard Worker        image_processing_utils.write_image(y_plane, file_name)
2874*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError('Lights OFF in test rig. Turn ON and retry.')
2875*b7c941bbSAndroid Build Coastguard Worker    elif state == 'OFF':
2876*b7c941bbSAndroid Build Coastguard Worker      if y_mean < validate_lighting_thresh:
2877*b7c941bbSAndroid Build Coastguard Worker        logging.debug('Lights OFF in test rig.')
2878*b7c941bbSAndroid Build Coastguard Worker        return True
2879*b7c941bbSAndroid Build Coastguard Worker      else:
2880*b7c941bbSAndroid Build Coastguard Worker        image_processing_utils.write_image(y_plane, file_name)
2881*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError('Lights ON in test rig. Turn OFF and retry.')
2882*b7c941bbSAndroid Build Coastguard Worker    else:
2883*b7c941bbSAndroid Build Coastguard Worker      raise AssertionError('Invalid lighting state string. '
2884*b7c941bbSAndroid Build Coastguard Worker                           "Valid strings: 'ON', 'OFF'.")
2885*b7c941bbSAndroid Build Coastguard Worker
2886*b7c941bbSAndroid Build Coastguard Worker
2887*b7c941bbSAndroid Build Coastguard Workerdef get_build_fingerprint(device_id):
2888*b7c941bbSAndroid Build Coastguard Worker  """Return the build fingerprint of the device."""
2889*b7c941bbSAndroid Build Coastguard Worker  cmd = f'adb -s {device_id} shell getprop ro.build.fingerprint'
2890*b7c941bbSAndroid Build Coastguard Worker  try:
2891*b7c941bbSAndroid Build Coastguard Worker    build_fingerprint = subprocess.check_output(cmd.split()).decode('utf-8').strip()
2892*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Build fingerprint: %s', build_fingerprint)
2893*b7c941bbSAndroid Build Coastguard Worker  except (subprocess.CalledProcessError, ValueError) as exp_errors:
2894*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError('No build_fingerprint.') from exp_errors
2895*b7c941bbSAndroid Build Coastguard Worker  return build_fingerprint
2896*b7c941bbSAndroid Build Coastguard Worker
2897*b7c941bbSAndroid Build Coastguard Worker
2898*b7c941bbSAndroid Build Coastguard Workerdef get_build_sdk_version(device_id):
2899*b7c941bbSAndroid Build Coastguard Worker  """Return the int build version of the device."""
2900*b7c941bbSAndroid Build Coastguard Worker  cmd = f'adb -s {device_id} shell getprop ro.build.version.sdk'
2901*b7c941bbSAndroid Build Coastguard Worker  try:
2902*b7c941bbSAndroid Build Coastguard Worker    build_sdk_version = int(subprocess.check_output(cmd.split()).rstrip())
2903*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Build SDK version: %d', build_sdk_version)
2904*b7c941bbSAndroid Build Coastguard Worker  except (subprocess.CalledProcessError, ValueError) as exp_errors:
2905*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError('No build_sdk_version.') from exp_errors
2906*b7c941bbSAndroid Build Coastguard Worker  return build_sdk_version
2907*b7c941bbSAndroid Build Coastguard Worker
2908*b7c941bbSAndroid Build Coastguard Worker
2909*b7c941bbSAndroid Build Coastguard Workerdef get_first_api_level(device_id):
2910*b7c941bbSAndroid Build Coastguard Worker  """Return the int value for the first API level of the device."""
2911*b7c941bbSAndroid Build Coastguard Worker  cmd = f'adb -s {device_id} shell getprop ro.product.first_api_level'
2912*b7c941bbSAndroid Build Coastguard Worker  try:
2913*b7c941bbSAndroid Build Coastguard Worker    first_api_level = int(subprocess.check_output(cmd.split()).rstrip())
2914*b7c941bbSAndroid Build Coastguard Worker    logging.debug('First API level: %d', first_api_level)
2915*b7c941bbSAndroid Build Coastguard Worker  except (subprocess.CalledProcessError, ValueError):
2916*b7c941bbSAndroid Build Coastguard Worker    logging.error('No first_api_level. Setting to build version.')
2917*b7c941bbSAndroid Build Coastguard Worker    first_api_level = get_build_sdk_version(device_id)
2918*b7c941bbSAndroid Build Coastguard Worker  return first_api_level
2919*b7c941bbSAndroid Build Coastguard Worker
2920*b7c941bbSAndroid Build Coastguard Worker
2921*b7c941bbSAndroid Build Coastguard Workerdef get_vendor_api_level(device_id):
2922*b7c941bbSAndroid Build Coastguard Worker  """Return the int value for the vendor API level of the device."""
2923*b7c941bbSAndroid Build Coastguard Worker  cmd = f'adb -s {device_id} shell getprop ro.vendor.api_level'
2924*b7c941bbSAndroid Build Coastguard Worker  try:
2925*b7c941bbSAndroid Build Coastguard Worker    vendor_api_level = int(subprocess.check_output(cmd.split()).rstrip())
2926*b7c941bbSAndroid Build Coastguard Worker    logging.debug('First vendor API level: %d', vendor_api_level)
2927*b7c941bbSAndroid Build Coastguard Worker  except (subprocess.CalledProcessError, ValueError):
2928*b7c941bbSAndroid Build Coastguard Worker    logging.error('No vendor_api_level. Setting to build version.')
2929*b7c941bbSAndroid Build Coastguard Worker    vendor_api_level = get_build_sdk_version(device_id)
2930*b7c941bbSAndroid Build Coastguard Worker  return vendor_api_level
2931*b7c941bbSAndroid Build Coastguard Worker
2932*b7c941bbSAndroid Build Coastguard Worker
2933*b7c941bbSAndroid Build Coastguard Workerdef get_media_performance_class(device_id):
2934*b7c941bbSAndroid Build Coastguard Worker  """Return the int value for the media performance class of the device."""
2935*b7c941bbSAndroid Build Coastguard Worker  cmd = (f'adb -s {device_id} shell '
2936*b7c941bbSAndroid Build Coastguard Worker         'getprop ro.odm.build.media_performance_class')
2937*b7c941bbSAndroid Build Coastguard Worker  try:
2938*b7c941bbSAndroid Build Coastguard Worker    media_performance_class = int(
2939*b7c941bbSAndroid Build Coastguard Worker        subprocess.check_output(cmd.split()).rstrip())
2940*b7c941bbSAndroid Build Coastguard Worker    logging.debug('Media performance class: %d', media_performance_class)
2941*b7c941bbSAndroid Build Coastguard Worker  except (subprocess.CalledProcessError, ValueError):
2942*b7c941bbSAndroid Build Coastguard Worker    logging.debug('No media performance class. Setting to 0.')
2943*b7c941bbSAndroid Build Coastguard Worker    media_performance_class = 0
2944*b7c941bbSAndroid Build Coastguard Worker  return media_performance_class
2945*b7c941bbSAndroid Build Coastguard Worker
2946*b7c941bbSAndroid Build Coastguard Worker
2947*b7c941bbSAndroid Build Coastguard Workerdef raise_mpc_assertion_error(required_mpc, test_name, found_mpc):
2948*b7c941bbSAndroid Build Coastguard Worker  raise AssertionError(f'With MPC >= {required_mpc}, {test_name} must be run. '
2949*b7c941bbSAndroid Build Coastguard Worker                       f'Found MPC: {found_mpc}')
2950*b7c941bbSAndroid Build Coastguard Worker
2951*b7c941bbSAndroid Build Coastguard Worker
2952*b7c941bbSAndroid Build Coastguard Workerdef stop_video_playback(tablet):
2953*b7c941bbSAndroid Build Coastguard Worker  """Force-stop activities used for video playback on the tablet.
2954*b7c941bbSAndroid Build Coastguard Worker
2955*b7c941bbSAndroid Build Coastguard Worker  Args:
2956*b7c941bbSAndroid Build Coastguard Worker    tablet: a controller object for the ITS tablet.
2957*b7c941bbSAndroid Build Coastguard Worker  """
2958*b7c941bbSAndroid Build Coastguard Worker  try:
2959*b7c941bbSAndroid Build Coastguard Worker    activities_unencoded = tablet.adb.shell(
2960*b7c941bbSAndroid Build Coastguard Worker        ['dumpsys', 'activity', 'recents', '|',
2961*b7c941bbSAndroid Build Coastguard Worker         'grep', '"baseIntent=Intent.*act=android.intent.action"']
2962*b7c941bbSAndroid Build Coastguard Worker    )
2963*b7c941bbSAndroid Build Coastguard Worker  except adb.AdbError as e:
2964*b7c941bbSAndroid Build Coastguard Worker    logging.warning('ADB error when finding intent activities: %s. '
2965*b7c941bbSAndroid Build Coastguard Worker                    'Please close the default video player manually.', e)
2966*b7c941bbSAndroid Build Coastguard Worker    return
2967*b7c941bbSAndroid Build Coastguard Worker  activity_lines = (
2968*b7c941bbSAndroid Build Coastguard Worker      str(activities_unencoded.decode('utf-8')).strip().splitlines()
2969*b7c941bbSAndroid Build Coastguard Worker  )
2970*b7c941bbSAndroid Build Coastguard Worker  for activity_line in activity_lines:
2971*b7c941bbSAndroid Build Coastguard Worker    activity = activity_line.split('cmp=')[-1].split('/')[0]
2972*b7c941bbSAndroid Build Coastguard Worker    try:
2973*b7c941bbSAndroid Build Coastguard Worker      tablet.adb.shell(['am', 'force-stop', activity])
2974*b7c941bbSAndroid Build Coastguard Worker    except adb.AdbError as e:
2975*b7c941bbSAndroid Build Coastguard Worker      logging.warning('ADB error when killing intent activity %s: %s. '
2976*b7c941bbSAndroid Build Coastguard Worker                      'Please close the default video player manually.',
2977*b7c941bbSAndroid Build Coastguard Worker                      activity, e)
2978*b7c941bbSAndroid Build Coastguard Worker
2979*b7c941bbSAndroid Build Coastguard Worker
2980*b7c941bbSAndroid Build Coastguard Workerdef raise_not_yet_mandated_error(message, api_level, mandated_api_level):
2981*b7c941bbSAndroid Build Coastguard Worker  if api_level >= mandated_api_level:
2982*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(
2983*b7c941bbSAndroid Build Coastguard Worker        f'Test is mandated for API level {mandated_api_level} or above. '
2984*b7c941bbSAndroid Build Coastguard Worker        f'Found API level {api_level}.\n\n{message}'
2985*b7c941bbSAndroid Build Coastguard Worker    )
2986*b7c941bbSAndroid Build Coastguard Worker  else:
2987*b7c941bbSAndroid Build Coastguard Worker    raise AssertionError(f'{NOT_YET_MANDATED_MESSAGE}\n\n{message}')
2988*b7c941bbSAndroid Build Coastguard Worker
2989*b7c941bbSAndroid Build Coastguard Worker
2990*b7c941bbSAndroid Build Coastguard Workerdef pull_file_from_dut(dut, dut_path, log_folder):
2991*b7c941bbSAndroid Build Coastguard Worker  """Pulls and returns file from dut and return file name.
2992*b7c941bbSAndroid Build Coastguard Worker
2993*b7c941bbSAndroid Build Coastguard Worker  Args:
2994*b7c941bbSAndroid Build Coastguard Worker    dut: device under test
2995*b7c941bbSAndroid Build Coastguard Worker    dut_path: pull file from this path
2996*b7c941bbSAndroid Build Coastguard Worker    log_folder: store pulled file to this folder
2997*b7c941bbSAndroid Build Coastguard Worker
2998*b7c941bbSAndroid Build Coastguard Worker  Returns:
2999*b7c941bbSAndroid Build Coastguard Worker    filename of file pulled from dut
3000*b7c941bbSAndroid Build Coastguard Worker  """
3001*b7c941bbSAndroid Build Coastguard Worker  dut.adb.pull([dut_path, log_folder])
3002*b7c941bbSAndroid Build Coastguard Worker  file_name = (dut_path.split('/')[-1])
3003*b7c941bbSAndroid Build Coastguard Worker  logging.debug('%s pulled from dut', file_name)
3004*b7c941bbSAndroid Build Coastguard Worker  return file_name
3005*b7c941bbSAndroid Build Coastguard Worker
3006*b7c941bbSAndroid Build Coastguard Worker
3007*b7c941bbSAndroid Build Coastguard Workerdef remove_tmp_files(log_path, match_pattern):
3008*b7c941bbSAndroid Build Coastguard Worker  """Remove temp file with given directory path.
3009*b7c941bbSAndroid Build Coastguard Worker
3010*b7c941bbSAndroid Build Coastguard Worker  Args:
3011*b7c941bbSAndroid Build Coastguard Worker    log_path: path-like object, path of directory
3012*b7c941bbSAndroid Build Coastguard Worker    match_pattern: string, pattern to be matched and removed
3013*b7c941bbSAndroid Build Coastguard Worker
3014*b7c941bbSAndroid Build Coastguard Worker  Returns:
3015*b7c941bbSAndroid Build Coastguard Worker    List of error messages if encountering error while removing files
3016*b7c941bbSAndroid Build Coastguard Worker  """
3017*b7c941bbSAndroid Build Coastguard Worker  temp_files = []
3018*b7c941bbSAndroid Build Coastguard Worker  try:
3019*b7c941bbSAndroid Build Coastguard Worker    temp_files = os.listdir(log_path)
3020*b7c941bbSAndroid Build Coastguard Worker  except FileNotFoundError:
3021*b7c941bbSAndroid Build Coastguard Worker    logging.debug('/tmp directory: %s not found', log_path)
3022*b7c941bbSAndroid Build Coastguard Worker  for file in temp_files:
3023*b7c941bbSAndroid Build Coastguard Worker    if fnmatch.fnmatch(file, match_pattern):
3024*b7c941bbSAndroid Build Coastguard Worker      file_to_remove = os.path.join(log_path, file)
3025*b7c941bbSAndroid Build Coastguard Worker      try:
3026*b7c941bbSAndroid Build Coastguard Worker        os.remove(file_to_remove)
3027*b7c941bbSAndroid Build Coastguard Worker      except FileNotFoundError:
3028*b7c941bbSAndroid Build Coastguard Worker        logging.debug('File not found: %s', str(file))
3029*b7c941bbSAndroid Build Coastguard Worker
3030*b7c941bbSAndroid Build Coastguard Worker
3031*b7c941bbSAndroid Build Coastguard Workerdef remove_frame_files(dir_name, save_files_list=None):
3032*b7c941bbSAndroid Build Coastguard Worker  """Removes the generated frame files from test dir.
3033*b7c941bbSAndroid Build Coastguard Worker
3034*b7c941bbSAndroid Build Coastguard Worker  Args:
3035*b7c941bbSAndroid Build Coastguard Worker    dir_name: test directory name.
3036*b7c941bbSAndroid Build Coastguard Worker    save_files_list: list of files not to be removed. Default is empty list.
3037*b7c941bbSAndroid Build Coastguard Worker  """
3038*b7c941bbSAndroid Build Coastguard Worker  if os.path.exists(dir_name):
3039*b7c941bbSAndroid Build Coastguard Worker    for image in glob.glob('%s/*.png' % dir_name):
3040*b7c941bbSAndroid Build Coastguard Worker      if save_files_list is None or image not in save_files_list:
3041*b7c941bbSAndroid Build Coastguard Worker        os.remove(image)
3042*b7c941bbSAndroid Build Coastguard Worker
3043*b7c941bbSAndroid Build Coastguard Worker
3044*b7c941bbSAndroid Build Coastguard Workerdef remove_file(file_name_with_path):
3045*b7c941bbSAndroid Build Coastguard Worker  """Removes file at given path.
3046*b7c941bbSAndroid Build Coastguard Worker
3047*b7c941bbSAndroid Build Coastguard Worker  Args:
3048*b7c941bbSAndroid Build Coastguard Worker    file_name_with_path: string, filename with path.
3049*b7c941bbSAndroid Build Coastguard Worker  """
3050*b7c941bbSAndroid Build Coastguard Worker  remove_mp4_file(file_name_with_path)
3051*b7c941bbSAndroid Build Coastguard Worker
3052*b7c941bbSAndroid Build Coastguard Worker
3053*b7c941bbSAndroid Build Coastguard Workerdef remove_mp4_file(file_name_with_path):
3054*b7c941bbSAndroid Build Coastguard Worker  """Removes the mp4 file at given path.
3055*b7c941bbSAndroid Build Coastguard Worker
3056*b7c941bbSAndroid Build Coastguard Worker  Args:
3057*b7c941bbSAndroid Build Coastguard Worker    file_name_with_path: string, path to mp4 recording.
3058*b7c941bbSAndroid Build Coastguard Worker  """
3059*b7c941bbSAndroid Build Coastguard Worker  try:
3060*b7c941bbSAndroid Build Coastguard Worker    os.remove(file_name_with_path)
3061*b7c941bbSAndroid Build Coastguard Worker  except FileNotFoundError:
3062*b7c941bbSAndroid Build Coastguard Worker    logging.debug('File not found: %s', file_name_with_path)
3063*b7c941bbSAndroid Build Coastguard Worker
3064*b7c941bbSAndroid Build Coastguard Worker
3065*b7c941bbSAndroid Build Coastguard Workerdef check_features_passed(
3066*b7c941bbSAndroid Build Coastguard Worker    features_passed, hlg10, is_stabilized):
3067*b7c941bbSAndroid Build Coastguard Worker  """Check if the [hlg10, is_stabilized] combination is already tested
3068*b7c941bbSAndroid Build Coastguard Worker  to be supported.
3069*b7c941bbSAndroid Build Coastguard Worker
3070*b7c941bbSAndroid Build Coastguard Worker  Args:
3071*b7c941bbSAndroid Build Coastguard Worker    features_passed: The list of feature combinations already supported
3072*b7c941bbSAndroid Build Coastguard Worker    hlg10: boolean; Whether HLG10 is enabled
3073*b7c941bbSAndroid Build Coastguard Worker    is_stabilized: boolean; Whether preview stabilizatoin is enabled
3074*b7c941bbSAndroid Build Coastguard Worker
3075*b7c941bbSAndroid Build Coastguard Worker  Returns:
3076*b7c941bbSAndroid Build Coastguard Worker    Whether the [hlg10, is_stabilized] is already tested to be supported.
3077*b7c941bbSAndroid Build Coastguard Worker  """
3078*b7c941bbSAndroid Build Coastguard Worker  feature_mask = 0
3079*b7c941bbSAndroid Build Coastguard Worker  if hlg10: feature_mask |= _BIT_HLG10
3080*b7c941bbSAndroid Build Coastguard Worker  if is_stabilized: feature_mask |= _BIT_STABILIZATION
3081*b7c941bbSAndroid Build Coastguard Worker  tested = False
3082*b7c941bbSAndroid Build Coastguard Worker  for tested_feature in features_passed:
3083*b7c941bbSAndroid Build Coastguard Worker    # Only test a combination if they aren't already a subset
3084*b7c941bbSAndroid Build Coastguard Worker    # of another tested combination.
3085*b7c941bbSAndroid Build Coastguard Worker    if (tested_feature | feature_mask) == tested_feature:
3086*b7c941bbSAndroid Build Coastguard Worker      tested = True
3087*b7c941bbSAndroid Build Coastguard Worker      break
3088*b7c941bbSAndroid Build Coastguard Worker  return tested
3089*b7c941bbSAndroid Build Coastguard Worker
3090*b7c941bbSAndroid Build Coastguard Worker
3091*b7c941bbSAndroid Build Coastguard Workerdef mark_features_passed(
3092*b7c941bbSAndroid Build Coastguard Worker    features_passed, hlg10, is_stabilized):
3093*b7c941bbSAndroid Build Coastguard Worker  """Mark the [hlg10, is_stabilized] combination as tested to pass.
3094*b7c941bbSAndroid Build Coastguard Worker
3095*b7c941bbSAndroid Build Coastguard Worker  Args:
3096*b7c941bbSAndroid Build Coastguard Worker    features_passed: The list of feature combinations already tested
3097*b7c941bbSAndroid Build Coastguard Worker    hlg10: boolean; Whether HLG10 is enabled
3098*b7c941bbSAndroid Build Coastguard Worker    is_stabilized: boolean; Whether preview stabilizatoin is enabled
3099*b7c941bbSAndroid Build Coastguard Worker  """
3100*b7c941bbSAndroid Build Coastguard Worker  feature_mask = 0
3101*b7c941bbSAndroid Build Coastguard Worker  if hlg10: feature_mask |= _BIT_HLG10
3102*b7c941bbSAndroid Build Coastguard Worker  if is_stabilized: feature_mask |= _BIT_STABILIZATION
3103*b7c941bbSAndroid Build Coastguard Worker  features_passed.append(feature_mask)
3104