blob: 8b02230fe227aa6146f3a04ec4474a66dc1c157b [file] [log] [blame]
Ruben Brunk370e2432014-10-14 18:33:23 -07001# Copyright 2013 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14
15import its.error
16import os
17import os.path
18import sys
19import re
20import json
21import time
22import unittest
23import socket
24import subprocess
25import hashlib
26import numpy
Sam Linf2f66002017-02-27 21:08:11 -080027import string
Shuzhen Wang80822a92018-01-29 09:27:42 -080028import unicodedata
Ruben Brunk370e2432014-10-14 18:33:23 -070029
Clemenz Portmanna2815fa2017-05-23 08:31:19 -070030
Ruben Brunk370e2432014-10-14 18:33:23 -070031class ItsSession(object):
32 """Controls a device over adb to run ITS scripts.
33
34 The script importing this module (on the host machine) prepares JSON
35 objects encoding CaptureRequests, specifying sets of parameters to use
Chien-Yu Chen682faa22014-10-22 17:34:44 -070036 when capturing an image using the Camera2 APIs. This class encapsulates
Ruben Brunk370e2432014-10-14 18:33:23 -070037 sending the requests to the device, monitoring the device's progress, and
38 copying the resultant captures back to the host machine when done. TCP
39 forwarded over adb is the transport mechanism used.
40
41 The device must have CtsVerifier.apk installed.
42
43 Attributes:
44 sock: The open socket.
45 """
46
Chien-Yu Chen1da23132015-07-22 15:24:41 -070047 # Open a connection to localhost:<host_port>, forwarded to port 6000 on the
48 # device. <host_port> is determined at run-time to support multiple
49 # connected devices.
Ruben Brunk370e2432014-10-14 18:33:23 -070050 IPADDR = '127.0.0.1'
Chien-Yu Chen1da23132015-07-22 15:24:41 -070051 REMOTE_PORT = 6000
Ruben Brunk370e2432014-10-14 18:33:23 -070052 BUFFER_SIZE = 4096
53
Chien-Yu Chen1da23132015-07-22 15:24:41 -070054 # LOCK_PORT is used as a mutex lock to protect the list of forwarded ports
55 # among all processes. The script assumes LOCK_PORT is available and will
56 # try to use ports between CLIENT_PORT_START and
57 # CLIENT_PORT_START+MAX_NUM_PORTS-1 on host for ITS sessions.
58 CLIENT_PORT_START = 6000
59 MAX_NUM_PORTS = 100
60 LOCK_PORT = CLIENT_PORT_START + MAX_NUM_PORTS
61
Ruben Brunk370e2432014-10-14 18:33:23 -070062 # Seconds timeout on each socket operation.
Yin-Chia Yehea1c1a62016-07-12 15:29:30 -070063 SOCK_TIMEOUT = 20.0
Yin-Chia Yehc9d554d2016-06-01 17:30:32 -070064 # Additional timeout in seconds when ITS service is doing more complicated
65 # operations, for example: issuing warmup requests before actual capture.
66 EXTRA_SOCK_TIMEOUT = 5.0
67
Yin-Chia Yeh39f8f4b2015-07-17 12:29:31 -070068 SEC_TO_NSEC = 1000*1000*1000.0
Ruben Brunk370e2432014-10-14 18:33:23 -070069
70 PACKAGE = 'com.android.cts.verifier.camera.its'
71 INTENT_START = 'com.android.cts.verifier.camera.its.START'
72 ACTION_ITS_RESULT = 'com.android.cts.verifier.camera.its.ACTION_ITS_RESULT'
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -070073 EXTRA_VERSION = 'camera.its.extra.VERSION'
74 CURRENT_ITS_VERSION = '1.0' # version number to sync with CtsVerifier
Yin-Chia Yehab98ada2015-03-05 13:28:53 -080075 EXTRA_CAMERA_ID = 'camera.its.extra.CAMERA_ID'
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -070076 EXTRA_RESULTS = 'camera.its.extra.RESULTS'
Sam Linf2f66002017-02-27 21:08:11 -080077 ITS_TEST_ACTIVITY = 'com.android.cts.verifier/.camera.its.ItsTestActivity'
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -070078
79 RESULT_PASS = 'PASS'
80 RESULT_FAIL = 'FAIL'
81 RESULT_NOT_EXECUTED = 'NOT_EXECUTED'
82 RESULT_VALUES = {RESULT_PASS, RESULT_FAIL, RESULT_NOT_EXECUTED}
83 RESULT_KEY = 'result'
84 SUMMARY_KEY = 'summary'
Ruben Brunk370e2432014-10-14 18:33:23 -070085
Chien-Yu Chen1da23132015-07-22 15:24:41 -070086 adb = "adb -d"
87 device_id = ""
Ruben Brunk370e2432014-10-14 18:33:23 -070088
89 # Definitions for some of the common output format options for do_capture().
90 # Each gets images of full resolution for each requested format.
91 CAP_RAW = {"format":"raw"}
92 CAP_DNG = {"format":"dng"}
93 CAP_YUV = {"format":"yuv"}
94 CAP_JPEG = {"format":"jpeg"}
95 CAP_RAW_YUV = [{"format":"raw"}, {"format":"yuv"}]
96 CAP_DNG_YUV = [{"format":"dng"}, {"format":"yuv"}]
97 CAP_RAW_JPEG = [{"format":"raw"}, {"format":"jpeg"}]
98 CAP_DNG_JPEG = [{"format":"dng"}, {"format":"jpeg"}]
99 CAP_YUV_JPEG = [{"format":"yuv"}, {"format":"jpeg"}]
100 CAP_RAW_YUV_JPEG = [{"format":"raw"}, {"format":"yuv"}, {"format":"jpeg"}]
101 CAP_DNG_YUV_JPEG = [{"format":"dng"}, {"format":"yuv"}, {"format":"jpeg"}]
102
Lu75f22fc2016-02-19 10:54:07 -0800103 # Predefine camera props. Save props extracted from the function,
104 # "get_camera_properties".
105 props = None
106
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700107 # Initialize the socket port for the host to forward requests to the device.
108 # This method assumes localhost's LOCK_PORT is available and will try to
109 # use ports between CLIENT_PORT_START and CLIENT_PORT_START+MAX_NUM_PORTS-1
110 def __init_socket_port(self):
111 NUM_RETRIES = 100
112 RETRY_WAIT_TIME_SEC = 0.05
Ruben Brunk370e2432014-10-14 18:33:23 -0700113
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700114 # Bind a socket to use as mutex lock
115 socket_lock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
116 for i in range(NUM_RETRIES):
117 try:
118 socket_lock.bind((ItsSession.IPADDR, ItsSession.LOCK_PORT))
119 break
Clemenz Portmannb0e76152018-02-01 09:12:57 -0800120 except socket.error or socket.timeout:
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700121 if i == NUM_RETRIES - 1:
122 raise its.error.Error(self.device_id,
Clemenz Portmannb0e76152018-02-01 09:12:57 -0800123 "socket lock returns error")
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700124 else:
125 time.sleep(RETRY_WAIT_TIME_SEC)
126
127 # Check if a port is already assigned to the device.
128 command = "adb forward --list"
129 proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
130 output, error = proc.communicate()
131
132 port = None
133 used_ports = []
134 for line in output.split(os.linesep):
135 # each line should be formatted as:
136 # "<device_id> tcp:<host_port> tcp:<remote_port>"
137 forward_info = line.split()
138 if len(forward_info) >= 3 and \
139 len(forward_info[1]) > 4 and forward_info[1][:4] == "tcp:" and \
140 len(forward_info[2]) > 4 and forward_info[2][:4] == "tcp:":
141 local_p = int(forward_info[1][4:])
142 remote_p = int(forward_info[2][4:])
143 if forward_info[0] == self.device_id and \
144 remote_p == ItsSession.REMOTE_PORT:
145 port = local_p
146 break;
147 else:
148 used_ports.append(local_p)
149
150 # Find the first available port if no port is assigned to the device.
151 if port is None:
152 for p in range(ItsSession.CLIENT_PORT_START,
153 ItsSession.CLIENT_PORT_START +
154 ItsSession.MAX_NUM_PORTS):
155 if p not in used_ports:
156 # Try to run "adb forward" with the port
157 command = "%s forward tcp:%d tcp:%d" % \
158 (self.adb, p, self.REMOTE_PORT)
159 proc = subprocess.Popen(command.split(),
160 stdout=subprocess.PIPE,
161 stderr=subprocess.PIPE)
162 output, error = proc.communicate()
163
164 # Check if there is no error
165 if error is None or error.find("error") < 0:
166 port = p
167 break
168
169 if port is None:
170 raise its.error.Error(self.device_id, " cannot find an available " +
171 "port")
172
173 # Release the socket as mutex unlock
174 socket_lock.close()
175
176 # Connect to the socket
177 self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
178 self.sock.connect((self.IPADDR, port))
179 self.sock.settimeout(self.SOCK_TIMEOUT)
180
181 # Reboot the device if needed and wait for the service to be ready for
182 # connection.
183 def __wait_for_service(self):
Ruben Brunk370e2432014-10-14 18:33:23 -0700184 # This also includes the optional reboot handling: if the user
185 # provides a "reboot" or "reboot=N" arg, then reboot the device,
186 # waiting for N seconds (default 30) before returning.
187 for s in sys.argv[1:]:
188 if s[:6] == "reboot":
189 duration = 30
190 if len(s) > 7 and s[6] == "=":
191 duration = int(s[7:])
192 print "Rebooting device"
Lu75f22fc2016-02-19 10:54:07 -0800193 _run("%s reboot" % (self.adb))
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700194 _run("%s wait-for-device" % (self.adb))
Ruben Brunk370e2432014-10-14 18:33:23 -0700195 time.sleep(duration)
196 print "Reboot complete"
197
Yin-Chia Yeh23a7b422016-03-25 14:48:47 -0700198 # Flush logcat so following code won't be misled by previous
199 # 'ItsService ready' log.
200 _run('%s logcat -c' % (self.adb))
201 time.sleep(1)
202
Ruben Brunk370e2432014-10-14 18:33:23 -0700203 # TODO: Figure out why "--user 0" is needed, and fix the problem.
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700204 _run('%s shell am force-stop --user 0 %s' % (self.adb, self.PACKAGE))
Yin-Chia Yeh6bdae902018-01-02 15:17:13 -0800205 _run(('%s shell am start-foreground-service --user 0 -t text/plain '
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700206 '-a %s') % (self.adb, self.INTENT_START))
Ruben Brunk370e2432014-10-14 18:33:23 -0700207
208 # Wait until the socket is ready to accept a connection.
209 proc = subprocess.Popen(
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700210 self.adb.split() + ["logcat"],
Ruben Brunk370e2432014-10-14 18:33:23 -0700211 stdout=subprocess.PIPE)
212 logcat = proc.stdout
213 while True:
214 line = logcat.readline().strip()
215 if line.find('ItsService ready') >= 0:
216 break
217 proc.kill()
218
Clemenz Portmann9f852a02018-10-31 19:52:25 -0700219 def __init__(self, camera_id=None):
220 self._camera_id = camera_id
221
Yin-Chia Yeh191cbf82018-05-08 10:33:33 -0700222 def __enter__(self):
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700223 # Initialize device id and adb command.
224 self.device_id = get_device_id()
225 self.adb = "adb -s " + self.device_id
226
227 self.__wait_for_service()
228 self.__init_socket_port()
229
Ruben Brunk370e2432014-10-14 18:33:23 -0700230 self.__close_camera()
Clemenz Portmann9f852a02018-10-31 19:52:25 -0700231 self.__open_camera(self._camera_id)
Ruben Brunk370e2432014-10-14 18:33:23 -0700232 return self
233
234 def __exit__(self, type, value, traceback):
Yin-Chia Yeh191cbf82018-05-08 10:33:33 -0700235 if hasattr(self, 'sock') and self.sock:
236 self.__close_camera()
237 self.sock.close()
Ruben Brunk370e2432014-10-14 18:33:23 -0700238 return False
239
240 def __read_response_from_socket(self):
241 # Read a line (newline-terminated) string serialization of JSON object.
242 chars = []
243 while len(chars) == 0 or chars[-1] != '\n':
244 ch = self.sock.recv(1)
245 if len(ch) == 0:
246 # Socket was probably closed; otherwise don't get empty strings
247 raise its.error.Error('Problem with socket on device side')
248 chars.append(ch)
249 line = ''.join(chars)
250 jobj = json.loads(line)
251 # Optionally read a binary buffer of a fixed size.
252 buf = None
253 if jobj.has_key("bufValueSize"):
254 n = jobj["bufValueSize"]
255 buf = bytearray(n)
256 view = memoryview(buf)
257 while n > 0:
258 nbytes = self.sock.recv_into(view, n)
259 view = view[nbytes:]
260 n -= nbytes
261 buf = numpy.frombuffer(buf, dtype=numpy.uint8)
262 return jobj, buf
263
Clemenz Portmann9f852a02018-10-31 19:52:25 -0700264 def __open_camera(self, camera_id):
265 # Get the camera ID to open if it is an argument as a single camera.
266 # This allows passing camera=# to individual tests at command line
267 # and camera=#,#,# or an no camera argv with tools/run_all_tests.py.
268 if not camera_id:
269 camera_id = 0
270 for s in sys.argv[1:]:
271 if s[:7] == "camera=" and len(s) > 7:
272 camera_ids = s[7:].split(",")
273 if len(camera_ids) == 1:
274 camera_id = camera_ids[0]
Ruben Brunk370e2432014-10-14 18:33:23 -0700275 cmd = {"cmdName":"open", "cameraId":camera_id}
276 self.sock.send(json.dumps(cmd) + "\n")
277 data,_ = self.__read_response_from_socket()
278 if data['tag'] != 'cameraOpened':
279 raise its.error.Error('Invalid command response')
280
281 def __close_camera(self):
282 cmd = {"cmdName":"close"}
283 self.sock.send(json.dumps(cmd) + "\n")
284 data,_ = self.__read_response_from_socket()
285 if data['tag'] != 'cameraClosed':
286 raise its.error.Error('Invalid command response')
287
288 def do_vibrate(self, pattern):
289 """Cause the device to vibrate to a specific pattern.
290
291 Args:
292 pattern: Durations (ms) for which to turn on or off the vibrator.
293 The first value indicates the number of milliseconds to wait
294 before turning the vibrator on. The next value indicates the
295 number of milliseconds for which to keep the vibrator on
296 before turning it off. Subsequent values alternate between
297 durations in milliseconds to turn the vibrator off or to turn
298 the vibrator on.
299
300 Returns:
301 Nothing.
302 """
303 cmd = {}
304 cmd["cmdName"] = "doVibrate"
305 cmd["pattern"] = pattern
306 self.sock.send(json.dumps(cmd) + "\n")
307 data,_ = self.__read_response_from_socket()
308 if data['tag'] != 'vibrationStarted':
309 raise its.error.Error('Invalid command response')
310
Daniel Hung-yu Wu130411c2018-03-30 15:05:18 +0800311 def get_sensors(self):
312 """Get all sensors on the device.
313
314 Returns:
315 A Python dictionary that returns keys and booleans for each sensor.
316 """
317 cmd = {}
318 cmd["cmdName"] = "checkSensorExistence"
319 self.sock.send(json.dumps(cmd) + "\n")
320 data,_ = self.__read_response_from_socket()
321 if data['tag'] != 'sensorExistence':
322 raise its.error.Error('Invalid command response')
323 return data['objValue']
324
Ruben Brunk370e2432014-10-14 18:33:23 -0700325 def start_sensor_events(self):
326 """Start collecting sensor events on the device.
327
328 See get_sensor_events for more info.
329
330 Returns:
331 Nothing.
332 """
333 cmd = {}
334 cmd["cmdName"] = "startSensorEvents"
335 self.sock.send(json.dumps(cmd) + "\n")
336 data,_ = self.__read_response_from_socket()
337 if data['tag'] != 'sensorEventsStarted':
338 raise its.error.Error('Invalid command response')
339
340 def get_sensor_events(self):
341 """Get a trace of all sensor events on the device.
342
343 The trace starts when the start_sensor_events function is called. If
344 the test runs for a long time after this call, then the device's
345 internal memory can fill up. Calling get_sensor_events gets all events
346 from the device, and then stops the device from collecting events and
347 clears the internal buffer; to start again, the start_sensor_events
348 call must be used again.
349
350 Events from the accelerometer, compass, and gyro are returned; each
351 has a timestamp and x,y,z values.
352
353 Note that sensor events are only produced if the device isn't in its
354 standby mode (i.e.) if the screen is on.
355
356 Returns:
357 A Python dictionary with three keys ("accel", "mag", "gyro") each
358 of which maps to a list of objects containing "time","x","y","z"
359 keys.
360 """
361 cmd = {}
362 cmd["cmdName"] = "getSensorEvents"
363 self.sock.send(json.dumps(cmd) + "\n")
Yin-Chia Yehf16d6872016-06-06 13:54:42 -0700364 timeout = self.SOCK_TIMEOUT + self.EXTRA_SOCK_TIMEOUT
365 self.sock.settimeout(timeout)
Ruben Brunk370e2432014-10-14 18:33:23 -0700366 data,_ = self.__read_response_from_socket()
367 if data['tag'] != 'sensorEvents':
368 raise its.error.Error('Invalid command response')
Yin-Chia Yehf16d6872016-06-06 13:54:42 -0700369 self.sock.settimeout(self.SOCK_TIMEOUT)
Ruben Brunk370e2432014-10-14 18:33:23 -0700370 return data['objValue']
371
Yin-Chia Yehab98ada2015-03-05 13:28:53 -0800372 def get_camera_ids(self):
373 """Get a list of camera device Ids that can be opened.
374
375 Returns:
376 a list of camera ID string
377 """
378 cmd = {}
379 cmd["cmdName"] = "getCameraIds"
380 self.sock.send(json.dumps(cmd) + "\n")
381 data,_ = self.__read_response_from_socket()
382 if data['tag'] != 'cameraIds':
383 raise its.error.Error('Invalid command response')
384 return data['objValue']['cameraIdArray']
385
Ruben Brunk370e2432014-10-14 18:33:23 -0700386 def get_camera_properties(self):
387 """Get the camera properties object for the device.
388
389 Returns:
390 The Python dictionary object for the CameraProperties object.
391 """
392 cmd = {}
393 cmd["cmdName"] = "getCameraProperties"
394 self.sock.send(json.dumps(cmd) + "\n")
395 data,_ = self.__read_response_from_socket()
396 if data['tag'] != 'cameraProperties':
397 raise its.error.Error('Invalid command response')
Lu75f22fc2016-02-19 10:54:07 -0800398 self.props = data['objValue']['cameraProperties']
Ruben Brunk370e2432014-10-14 18:33:23 -0700399 return data['objValue']['cameraProperties']
400
Yin-Chia Yehb1801f92018-02-14 16:08:12 -0800401 def get_camera_properties_by_id(self, camera_id):
402 """Get the camera properties object for device with camera_id
403
404 Args:
405 camera_id: The ID string of the camera
406
407 Returns:
408 The Python dictionary object for the CameraProperties object. Empty
409 if no such device exists.
410
411 """
412 cmd = {}
413 cmd["cmdName"] = "getCameraPropertiesById"
414 cmd["cameraId"] = camera_id
415 self.sock.send(json.dumps(cmd) + "\n")
416 data,_ = self.__read_response_from_socket()
417 if data['tag'] != 'cameraProperties':
418 raise its.error.Error('Invalid command response')
419 return data['objValue']['cameraProperties']
420
Ruben Brunk370e2432014-10-14 18:33:23 -0700421 def do_3a(self, regions_ae=[[0,0,1,1,1]],
422 regions_awb=[[0,0,1,1,1]],
423 regions_af=[[0,0,1,1,1]],
424 do_ae=True, do_awb=True, do_af=True,
425 lock_ae=False, lock_awb=False,
Zhijun Heeb3ff472014-11-20 13:47:11 -0800426 get_results=False,
Clemenz Portmann4c869052018-03-27 13:31:09 -0700427 ev_comp=0, mono_camera=False):
Ruben Brunk370e2432014-10-14 18:33:23 -0700428 """Perform a 3A operation on the device.
429
430 Triggers some or all of AE, AWB, and AF, and returns once they have
431 converged. Uses the vendor 3A that is implemented inside the HAL.
Clemenz Portmann3c97e772017-03-23 13:22:06 -0700432 Note: do_awb is always enabled regardless of do_awb flag
Ruben Brunk370e2432014-10-14 18:33:23 -0700433
434 Throws an assertion if 3A fails to converge.
435
436 Args:
437 regions_ae: List of weighted AE regions.
438 regions_awb: List of weighted AWB regions.
439 regions_af: List of weighted AF regions.
440 do_ae: Trigger AE and wait for it to converge.
441 do_awb: Wait for AWB to converge.
442 do_af: Trigger AF and wait for it to converge.
443 lock_ae: Request AE lock after convergence, and wait for it.
444 lock_awb: Request AWB lock after convergence, and wait for it.
445 get_results: Return the 3A results from this function.
Zhijun Heeb3ff472014-11-20 13:47:11 -0800446 ev_comp: An EV compensation value to use when running AE.
Clemenz Portmann4c869052018-03-27 13:31:09 -0700447 mono_camera: Boolean for monochrome camera.
Ruben Brunk370e2432014-10-14 18:33:23 -0700448
449 Region format in args:
450 Arguments are lists of weighted regions; each weighted region is a
451 list of 5 values, [x,y,w,h, wgt], and each argument is a list of
452 these 5-value lists. The coordinates are given as normalized
453 rectangles (x,y,w,h) specifying the region. For example:
454 [[0.0, 0.0, 1.0, 0.5, 5], [0.0, 0.5, 1.0, 0.5, 10]].
455 Weights are non-negative integers.
456
457 Returns:
458 Five values are returned if get_results is true::
459 * AE sensitivity; None if do_ae is False
460 * AE exposure time; None if do_ae is False
Clemenz Portmann3c97e772017-03-23 13:22:06 -0700461 * AWB gains (list);
462 * AWB transform (list);
Ruben Brunk370e2432014-10-14 18:33:23 -0700463 * AF focus position; None if do_af is false
464 Otherwise, it returns five None values.
465 """
466 print "Running vendor 3A on device"
467 cmd = {}
468 cmd["cmdName"] = "do3A"
469 cmd["regions"] = {"ae": sum(regions_ae, []),
470 "awb": sum(regions_awb, []),
471 "af": sum(regions_af, [])}
472 cmd["triggers"] = {"ae": do_ae, "af": do_af}
473 if lock_ae:
474 cmd["aeLock"] = True
475 if lock_awb:
476 cmd["awbLock"] = True
Zhijun Heeb3ff472014-11-20 13:47:11 -0800477 if ev_comp != 0:
478 cmd["evComp"] = ev_comp
Ruben Brunk370e2432014-10-14 18:33:23 -0700479 self.sock.send(json.dumps(cmd) + "\n")
480
481 # Wait for each specified 3A to converge.
482 ae_sens = None
483 ae_exp = None
484 awb_gains = None
485 awb_transform = None
486 af_dist = None
487 converged = False
488 while True:
489 data,_ = self.__read_response_from_socket()
490 vals = data['strValue'].split()
491 if data['tag'] == 'aeResult':
Clemenz Portmann3c97e772017-03-23 13:22:06 -0700492 if do_ae:
493 ae_sens, ae_exp = [int(i) for i in vals]
Ruben Brunk370e2432014-10-14 18:33:23 -0700494 elif data['tag'] == 'afResult':
Clemenz Portmann3c97e772017-03-23 13:22:06 -0700495 if do_af:
496 af_dist = float(vals[0])
Ruben Brunk370e2432014-10-14 18:33:23 -0700497 elif data['tag'] == 'awbResult':
498 awb_gains = [float(f) for f in vals[:4]]
499 awb_transform = [float(f) for f in vals[4:]]
500 elif data['tag'] == '3aConverged':
501 converged = True
502 elif data['tag'] == '3aDone':
503 break
504 else:
505 raise its.error.Error('Invalid command response')
506 if converged and not get_results:
507 return None,None,None,None,None
Clemenz Portmann4c869052018-03-27 13:31:09 -0700508 if (do_ae and ae_sens == None or (not mono_camera and do_awb and awb_gains == None)
Ruben Brunk370e2432014-10-14 18:33:23 -0700509 or do_af and af_dist == None or not converged):
Clemenz Portmann4c869052018-03-27 13:31:09 -0700510
Ruben Brunk370e2432014-10-14 18:33:23 -0700511 raise its.error.Error('3A failed to converge')
512 return ae_sens, ae_exp, awb_gains, awb_transform, af_dist
513
Yin-Chia Yeh9e5d9ac2016-03-30 12:04:12 -0700514 def do_capture(self, cap_request,
515 out_surfaces=None, reprocess_format=None, repeat_request=None):
Ruben Brunk370e2432014-10-14 18:33:23 -0700516 """Issue capture request(s), and read back the image(s) and metadata.
517
518 The main top-level function for capturing one or more images using the
519 device. Captures a single image if cap_request is a single object, and
520 captures a burst if it is a list of objects.
521
Yin-Chia Yeh9e5d9ac2016-03-30 12:04:12 -0700522 The optional repeat_request field can be used to assign a repeating
523 request list ran in background for 3 seconds to warm up the capturing
524 pipeline before start capturing. The repeat_requests will be ran on a
525 640x480 YUV surface without sending any data back. The caller needs to
526 make sure the stream configuration defined by out_surfaces and
527 repeat_request are valid or do_capture may fail because device does not
528 support such stream configuration.
529
Ruben Brunk370e2432014-10-14 18:33:23 -0700530 The out_surfaces field can specify the width(s), height(s), and
531 format(s) of the captured image. The formats may be "yuv", "jpeg",
Timothy Knight67d8ec92015-08-31 13:14:46 -0700532 "dng", "raw", "raw10", "raw12", or "rawStats". The default is a YUV420
533 frame ("yuv") corresponding to a full sensor frame.
Ruben Brunk370e2432014-10-14 18:33:23 -0700534
Shuzhen Wang80822a92018-01-29 09:27:42 -0800535 Optionally the out_surfaces field can specify physical camera id(s) if the
536 current camera device is a logical multi-camera. The physical camera id
537 must refer to a physical camera backing this logical camera device. And
538 only "yuv", "raw", "raw10", "raw12" support the physical camera id field.
539
540 Currently only 2 physical streams with the same format are supported, one
541 from each physical camera:
542 - yuv physical streams of the same size.
543 - raw physical streams with the same or different sizes, depending on
544 device capability. (Different physical cameras may have different raw sizes).
545
Ruben Brunk370e2432014-10-14 18:33:23 -0700546 Note that one or more surfaces can be specified, allowing a capture to
547 request images back in multiple formats (e.g.) raw+yuv, raw+jpeg,
548 yuv+jpeg, raw+yuv+jpeg. If the size is omitted for a surface, the
549 default is the largest resolution available for the format of that
550 surface. At most one output surface can be specified for a given format,
551 and raw+dng, raw10+dng, and raw+raw10 are not supported as combinations.
552
Chien-Yu Chen4e1e2cc2015-06-08 17:46:52 -0700553 If reprocess_format is not None, for each request, an intermediate
554 buffer of the given reprocess_format will be captured from camera and
555 the intermediate buffer will be reprocessed to the output surfaces. The
556 following settings will be turned off when capturing the intermediate
557 buffer and will be applied when reprocessing the intermediate buffer.
558 1. android.noiseReduction.mode
559 2. android.edge.mode
560 3. android.reprocess.effectiveExposureFactor
561
562 Supported reprocess format are "yuv" and "private". Supported output
563 surface formats when reprocessing is enabled are "yuv" and "jpeg".
564
Ruben Brunk370e2432014-10-14 18:33:23 -0700565 Example of a single capture request:
566
567 {
568 "android.sensor.exposureTime": 100*1000*1000,
569 "android.sensor.sensitivity": 100
570 }
571
572 Example of a list of capture requests:
573
574 [
575 {
576 "android.sensor.exposureTime": 100*1000*1000,
577 "android.sensor.sensitivity": 100
578 },
579 {
580 "android.sensor.exposureTime": 100*1000*1000,
581 "android.sensor.sensitivity": 200
582 }
583 ]
584
585 Examples of output surface specifications:
586
587 {
588 "width": 640,
589 "height": 480,
590 "format": "yuv"
591 }
592
593 [
594 {
595 "format": "jpeg"
596 },
597 {
598 "format": "raw"
599 }
600 ]
601
602 The following variables defined in this class are shortcuts for
603 specifying one or more formats where each output is the full size for
604 that format; they can be used as values for the out_surfaces arguments:
605
606 CAP_RAW
607 CAP_DNG
608 CAP_YUV
609 CAP_JPEG
610 CAP_RAW_YUV
611 CAP_DNG_YUV
612 CAP_RAW_JPEG
613 CAP_DNG_JPEG
614 CAP_YUV_JPEG
615 CAP_RAW_YUV_JPEG
616 CAP_DNG_YUV_JPEG
617
Chien-Yu Chen682faa22014-10-22 17:34:44 -0700618 If multiple formats are specified, then this function returns multiple
Ruben Brunk370e2432014-10-14 18:33:23 -0700619 capture objects, one for each requested format. If multiple formats and
620 multiple captures (i.e. a burst) are specified, then this function
621 returns multiple lists of capture objects. In both cases, the order of
622 the returned objects matches the order of the requested formats in the
623 out_surfaces parameter. For example:
624
625 yuv_cap = do_capture( req1 )
626 yuv_cap = do_capture( req1, yuv_fmt )
627 yuv_cap, raw_cap = do_capture( req1, [yuv_fmt,raw_fmt] )
628 yuv_caps = do_capture( [req1,req2], yuv_fmt )
629 yuv_caps, raw_caps = do_capture( [req1,req2], [yuv_fmt,raw_fmt] )
630
Timothy Knight67d8ec92015-08-31 13:14:46 -0700631 The "rawStats" format processes the raw image and returns a new image
632 of statistics from the raw image. The format takes additional keys,
633 "gridWidth" and "gridHeight" which are size of grid cells in a 2D grid
634 of the raw image. For each grid cell, the mean and variance of each raw
635 channel is computed, and the do_capture call returns two 4-element float
636 images of dimensions (rawWidth / gridWidth, rawHeight / gridHeight),
637 concatenated back-to-back, where the first iamge contains the 4-channel
Timothy Knightff3032b2017-02-01 15:10:51 -0800638 means and the second contains the 4-channel variances. Note that only
639 pixels in the active array crop region are used; pixels outside this
640 region (for example optical black rows) are cropped out before the
641 gridding and statistics computation is performed.
Timothy Knight67d8ec92015-08-31 13:14:46 -0700642
643 For the rawStats format, if the gridWidth is not provided then the raw
644 image width is used as the default, and similarly for gridHeight. With
645 this, the following is an example of a output description that computes
646 the mean and variance across each image row:
647
648 {
649 "gridHeight": 1,
650 "format": "rawStats"
651 }
652
Ruben Brunk370e2432014-10-14 18:33:23 -0700653 Args:
654 cap_request: The Python dict/list specifying the capture(s), which
655 will be converted to JSON and sent to the device.
656 out_surfaces: (Optional) specifications of the output image formats
657 and sizes to use for each capture.
Chien-Yu Chen4e1e2cc2015-06-08 17:46:52 -0700658 reprocess_format: (Optional) The reprocessing format. If not None,
659 reprocessing will be enabled.
Ruben Brunk370e2432014-10-14 18:33:23 -0700660
661 Returns:
662 An object, list of objects, or list of lists of objects, where each
663 object contains the following fields:
664 * data: the image data as a numpy array of bytes.
665 * width: the width of the captured image.
666 * height: the height of the captured image.
Timothy Knight67d8ec92015-08-31 13:14:46 -0700667 * format: image the format, in [
668 "yuv","jpeg","raw","raw10","raw12","rawStats","dng"].
Chien-Yu Chen682faa22014-10-22 17:34:44 -0700669 * metadata: the capture result object (Python dictionary).
Ruben Brunk370e2432014-10-14 18:33:23 -0700670 """
671 cmd = {}
Chien-Yu Chen4e1e2cc2015-06-08 17:46:52 -0700672 if reprocess_format != None:
673 cmd["cmdName"] = "doReprocessCapture"
674 cmd["reprocessFormat"] = reprocess_format
675 else:
676 cmd["cmdName"] = "doCapture"
Yin-Chia Yeh9e5d9ac2016-03-30 12:04:12 -0700677
678 if repeat_request is not None and reprocess_format is not None:
679 raise its.error.Error('repeating request + reprocessing is not supported')
680
681 if repeat_request is None:
682 cmd["repeatRequests"] = []
683 elif not isinstance(repeat_request, list):
684 cmd["repeatRequests"] = [repeat_request]
685 else:
686 cmd["repeatRequests"] = repeat_request
687
Ruben Brunk370e2432014-10-14 18:33:23 -0700688 if not isinstance(cap_request, list):
689 cmd["captureRequests"] = [cap_request]
690 else:
691 cmd["captureRequests"] = cap_request
692 if out_surfaces is not None:
693 if not isinstance(out_surfaces, list):
694 cmd["outputSurfaces"] = [out_surfaces]
695 else:
696 cmd["outputSurfaces"] = out_surfaces
Lu75f22fc2016-02-19 10:54:07 -0800697 formats = [c["format"] if "format" in c else "yuv"
Ruben Brunk370e2432014-10-14 18:33:23 -0700698 for c in cmd["outputSurfaces"]]
699 formats = [s if s != "jpg" else "jpeg" for s in formats]
700 else:
Yin-Chia Yeh409f2e92016-03-16 18:55:31 -0700701 max_yuv_size = its.objects.get_available_output_sizes(
702 "yuv", self.props)[0]
Ruben Brunk370e2432014-10-14 18:33:23 -0700703 formats = ['yuv']
Yin-Chia Yeh409f2e92016-03-16 18:55:31 -0700704 cmd["outputSurfaces"] = [{"format": "yuv",
705 "width" : max_yuv_size[0],
706 "height": max_yuv_size[1]}]
Shuzhen Wang80822a92018-01-29 09:27:42 -0800707
708 # Figure out requested physical camera ids, physical and logical
709 # streams.
710 physical_cam_ids = {}
711 physical_buffers = {}
712 physical_cam_format = None
713 logical_cam_formats = []
714 for i,s in enumerate(cmd["outputSurfaces"]):
715 if "format" in s and s["format"] in ["yuv", "raw", "raw10", "raw12"]:
716 if "physicalCamera" in s:
717 if physical_cam_format is not None and s["format"] != physical_cam_format:
718 raise its.error.Error('ITS does not support capturing multiple ' +
719 'physical formats yet')
720 physical_cam_ids[i] = s["physicalCamera"]
721 physical_buffers[s["physicalCamera"]] = []
722 physical_cam_format = s["format"]
723 else:
724 logical_cam_formats.append(s["format"])
725 else:
726 logical_cam_formats.append(s["format"])
727
Ruben Brunk370e2432014-10-14 18:33:23 -0700728 ncap = len(cmd["captureRequests"])
729 nsurf = 1 if out_surfaces is None else len(cmd["outputSurfaces"])
Lu7c6f52e2015-12-15 14:42:18 -0800730 # Only allow yuv output to multiple targets
Shuzhen Wang80822a92018-01-29 09:27:42 -0800731 logical_yuv_surfaces = [s for s in cmd["outputSurfaces"] if s["format"]=="yuv"\
732 and "physicalCamera" not in s]
733 n_yuv = len(logical_yuv_surfaces)
Lu7c6f52e2015-12-15 14:42:18 -0800734 # Compute the buffer size of YUV targets
Lu75f22fc2016-02-19 10:54:07 -0800735 yuv_maxsize_1d = 0
Shuzhen Wang80822a92018-01-29 09:27:42 -0800736 for s in logical_yuv_surfaces:
Lu75f22fc2016-02-19 10:54:07 -0800737 if not ("width" in s and "height" in s):
738 if self.props is None:
739 raise its.error.Error('Camera props are unavailable')
740 yuv_maxsize_2d = its.objects.get_available_output_sizes(
741 "yuv", self.props)[0]
742 yuv_maxsize_1d = yuv_maxsize_2d[0] * yuv_maxsize_2d[1] * 3 / 2
743 break
744 yuv_sizes = [c["width"]*c["height"]*3/2
745 if "width" in c and "height" in c
746 else yuv_maxsize_1d
Shuzhen Wang80822a92018-01-29 09:27:42 -0800747 for c in logical_yuv_surfaces]
Lu7c6f52e2015-12-15 14:42:18 -0800748 # Currently we don't pass enough metadta from ItsService to distinguish
749 # different yuv stream of same buffer size
750 if len(yuv_sizes) != len(set(yuv_sizes)):
751 raise its.error.Error(
752 'ITS does not support yuv outputs of same buffer size')
Shuzhen Wang80822a92018-01-29 09:27:42 -0800753 if len(logical_cam_formats) > len(set(logical_cam_formats)):
754 if n_yuv != len(logical_cam_formats) - len(set(logical_cam_formats)) + 1:
Lu7c6f52e2015-12-15 14:42:18 -0800755 raise its.error.Error('Duplicate format requested')
756
Timothy Knight67d8ec92015-08-31 13:14:46 -0700757 raw_formats = 0;
758 raw_formats += 1 if "dng" in formats else 0
759 raw_formats += 1 if "raw" in formats else 0
760 raw_formats += 1 if "raw10" in formats else 0
761 raw_formats += 1 if "raw12" in formats else 0
762 raw_formats += 1 if "rawStats" in formats else 0
763 if raw_formats > 1:
Ruben Brunk370e2432014-10-14 18:33:23 -0700764 raise its.error.Error('Different raw formats not supported')
Yin-Chia Yeh39f8f4b2015-07-17 12:29:31 -0700765
766 # Detect long exposure time and set timeout accordingly
767 longest_exp_time = 0
768 for req in cmd["captureRequests"]:
769 if "android.sensor.exposureTime" in req and \
770 req["android.sensor.exposureTime"] > longest_exp_time:
771 longest_exp_time = req["android.sensor.exposureTime"]
772
773 extended_timeout = longest_exp_time / self.SEC_TO_NSEC + \
774 self.SOCK_TIMEOUT
Yin-Chia Yehc9d554d2016-06-01 17:30:32 -0700775 if repeat_request:
776 extended_timeout += self.EXTRA_SOCK_TIMEOUT
Yin-Chia Yeh39f8f4b2015-07-17 12:29:31 -0700777 self.sock.settimeout(extended_timeout)
778
Ruben Brunk370e2432014-10-14 18:33:23 -0700779 print "Capturing %d frame%s with %d format%s [%s]" % (
780 ncap, "s" if ncap>1 else "", nsurf, "s" if nsurf>1 else "",
781 ",".join(formats))
782 self.sock.send(json.dumps(cmd) + "\n")
783
784 # Wait for ncap*nsurf images and ncap metadata responses.
785 # Assume that captures come out in the same order as requested in
Chien-Yu Chen682faa22014-10-22 17:34:44 -0700786 # the burst, however individual images of different formats can come
Ruben Brunk370e2432014-10-14 18:33:23 -0700787 # out in any order for that capture.
788 nbufs = 0
Lu7c6f52e2015-12-15 14:42:18 -0800789 bufs = {"raw":[], "raw10":[], "raw12":[],
Timothy Knight67d8ec92015-08-31 13:14:46 -0700790 "rawStats":[], "dng":[], "jpeg":[]}
Lu7c6f52e2015-12-15 14:42:18 -0800791 yuv_bufs = {size:[] for size in yuv_sizes}
Ruben Brunk370e2432014-10-14 18:33:23 -0700792 mds = []
Shuzhen Wang96ecf752018-01-30 10:33:31 -0800793 physical_mds = []
Ruben Brunk370e2432014-10-14 18:33:23 -0700794 widths = None
795 heights = None
796 while nbufs < ncap*nsurf or len(mds) < ncap:
797 jsonObj,buf = self.__read_response_from_socket()
Lu7c6f52e2015-12-15 14:42:18 -0800798 if jsonObj['tag'] in ['jpegImage', 'rawImage', \
Timothy Knight67d8ec92015-08-31 13:14:46 -0700799 'raw10Image', 'raw12Image', 'rawStatsImage', 'dngImage'] \
800 and buf is not None:
Ruben Brunk370e2432014-10-14 18:33:23 -0700801 fmt = jsonObj['tag'][:-5]
802 bufs[fmt].append(buf)
803 nbufs += 1
Lu7c6f52e2015-12-15 14:42:18 -0800804 elif jsonObj['tag'] == 'yuvImage':
805 buf_size = numpy.product(buf.shape)
806 yuv_bufs[buf_size].append(buf)
807 nbufs += 1
Ruben Brunk370e2432014-10-14 18:33:23 -0700808 elif jsonObj['tag'] == 'captureResults':
809 mds.append(jsonObj['objValue']['captureResult'])
Shuzhen Wang96ecf752018-01-30 10:33:31 -0800810 physical_mds.append(jsonObj['objValue']['physicalResults'])
Ruben Brunk370e2432014-10-14 18:33:23 -0700811 outputs = jsonObj['objValue']['outputs']
812 widths = [out['width'] for out in outputs]
813 heights = [out['height'] for out in outputs]
814 else:
Shuzhen Wang80822a92018-01-29 09:27:42 -0800815 tagString = unicodedata.normalize('NFKD', jsonObj['tag']).encode('ascii', 'ignore');
816 for x in ['rawImage', 'raw10Image', 'raw12Image', 'yuvImage']:
817 if (tagString.startswith(x)):
818 physicalId = jsonObj['tag'][len(x):];
819 if physicalId in physical_cam_ids.values():
820 physical_buffers[physicalId].append(buf)
821 nbufs += 1
Ruben Brunk370e2432014-10-14 18:33:23 -0700822 rets = []
823 for j,fmt in enumerate(formats):
824 objs = []
825 for i in range(ncap):
826 obj = {}
Ruben Brunk370e2432014-10-14 18:33:23 -0700827 obj["width"] = widths[j]
828 obj["height"] = heights[j]
829 obj["format"] = fmt
Shuzhen Wang96ecf752018-01-30 10:33:31 -0800830 if j in physical_cam_ids:
831 for physical_md in physical_mds[i]:
832 if physical_cam_ids[j] in physical_md:
833 obj["metadata"] = physical_md[physical_cam_ids[j]]
834 break
835 else:
836 obj["metadata"] = mds[i]
Shuzhen Wang80822a92018-01-29 09:27:42 -0800837
838 if j in physical_cam_ids:
839 obj["data"] = physical_buffers[physical_cam_ids[j]][i]
840 elif fmt == 'yuv':
Lu7c6f52e2015-12-15 14:42:18 -0800841 buf_size = widths[j] * heights[j] * 3 / 2
842 obj["data"] = yuv_bufs[buf_size][i]
843 else:
844 obj["data"] = bufs[fmt][i]
Ruben Brunk370e2432014-10-14 18:33:23 -0700845 objs.append(obj)
846 rets.append(objs if ncap>1 else objs[0])
Yin-Chia Yeh39f8f4b2015-07-17 12:29:31 -0700847 self.sock.settimeout(self.SOCK_TIMEOUT)
Ruben Brunk370e2432014-10-14 18:33:23 -0700848 return rets if len(rets)>1 else rets[0]
849
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700850def get_device_id():
851 """ Return the ID of the device that the test is running on.
852
853 Return the device ID provided in the command line if it's connected. If no
854 device ID is provided in the command line and there is only one device
855 connected, return the device ID by parsing the result of "adb devices".
Clemenz Portmannf7f23ee2016-08-18 13:00:59 -0700856 Also, if the environment variable ANDROID_SERIAL is set, use it as device
857 id. When both ANDROID_SERIAL and device argument present, device argument
858 takes priority.
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700859
860 Raise an exception if no device is connected; or the device ID provided in
861 the command line is not connected; or no device ID is provided in the
Clemenz Portmannf7f23ee2016-08-18 13:00:59 -0700862 command line or environment variable and there are more than 1 device
863 connected.
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700864
865 Returns:
866 Device ID string.
867 """
868 device_id = None
Clemenz Portmannf7f23ee2016-08-18 13:00:59 -0700869
870 # Check if device id is set in env
871 if "ANDROID_SERIAL" in os.environ:
872 device_id = os.environ["ANDROID_SERIAL"]
873
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700874 for s in sys.argv[1:]:
875 if s[:7] == "device=" and len(s) > 7:
876 device_id = str(s[7:])
877
878 # Get a list of connected devices
879 devices = []
880 command = "adb devices"
881 proc = subprocess.Popen(command.split(), stdout=subprocess.PIPE)
882 output, error = proc.communicate()
883 for line in output.split(os.linesep):
884 device_info = line.split()
885 if len(device_info) == 2 and device_info[1] == "device":
886 devices.append(device_info[0])
887
888 if len(devices) == 0:
889 raise its.error.Error("No device is connected!")
890 elif device_id is not None and device_id not in devices:
891 raise its.error.Error(device_id + " is not connected!")
892 elif device_id is None and len(devices) >= 2:
893 raise its.error.Error("More than 1 device are connected. " +
894 "Use device=<device_id> to specify a device to test.")
895 elif len(devices) == 1:
896 device_id = devices[0]
897
898 return device_id
899
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -0700900def report_result(device_id, camera_id, results):
Timothy Knighted076002014-10-23 16:12:26 -0700901 """Send a pass/fail result to the device, via an intent.
902
903 Args:
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700904 device_id: The ID string of the device to report the results to.
Timothy Knighted076002014-10-23 16:12:26 -0700905 camera_id: The ID string of the camera for which to report pass/fail.
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -0700906 results: a dictionary contains all ITS scenes as key and result/summary
907 of current ITS run. See test_report_result unit test for
908 an example.
Timothy Knighted076002014-10-23 16:12:26 -0700909 Returns:
910 Nothing.
911 """
Yin-Chia Yeh89dfdda2018-04-26 08:15:10 -0700912 ACTIVITY_START_WAIT = 1.5 # seconds
Chien-Yu Chen1da23132015-07-22 15:24:41 -0700913 adb = "adb -s " + device_id
Sam Linf2f66002017-02-27 21:08:11 -0800914
Yin-Chia Yeh89dfdda2018-04-26 08:15:10 -0700915 # Start ItsTestActivity to receive test results
Sam Linf2f66002017-02-27 21:08:11 -0800916 cmd = "%s shell am start %s --activity-brought-to-front" % (adb, ItsSession.ITS_TEST_ACTIVITY)
917 _run(cmd)
Yin-Chia Yeh89dfdda2018-04-26 08:15:10 -0700918 time.sleep(ACTIVITY_START_WAIT)
Sam Linf2f66002017-02-27 21:08:11 -0800919
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -0700920 # Validate/process results argument
921 for scene in results:
922 result_key = ItsSession.RESULT_KEY
923 summary_key = ItsSession.SUMMARY_KEY
924 if result_key not in results[scene]:
925 raise its.error.Error('ITS result not found for ' + scene)
926 if results[scene][result_key] not in ItsSession.RESULT_VALUES:
927 raise its.error.Error('Unknown ITS result for %s: %s' % (
928 scene, results[result_key]))
929 if summary_key in results[scene]:
930 device_summary_path = "/sdcard/its_camera%s_%s.txt" % (
931 camera_id, scene)
932 _run("%s push %s %s" % (
933 adb, results[scene][summary_key], device_summary_path))
934 results[scene][summary_key] = device_summary_path
Sam Linf2f66002017-02-27 21:08:11 -0800935
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -0700936 json_results = json.dumps(results)
937 cmd = "%s shell am broadcast -a %s --es %s %s --es %s %s --es %s \'%s\'" % (
938 adb, ItsSession.ACTION_ITS_RESULT,
939 ItsSession.EXTRA_VERSION, ItsSession.CURRENT_ITS_VERSION,
940 ItsSession.EXTRA_CAMERA_ID, camera_id,
941 ItsSession.EXTRA_RESULTS, json_results)
942 if len(cmd) > 4095:
943 print "ITS command string might be too long! len:", len(cmd)
944 _run(cmd)
Ruben Brunk370e2432014-10-14 18:33:23 -0700945
Yin-Chia Yeh89dfdda2018-04-26 08:15:10 -0700946def adb_log(device_id, msg):
947 """Send a log message to adb logcat
948
949 Args:
950 device_id: The ID string of the adb device
951 msg: the message string to be send to logcat
952
953 Returns:
954 Nothing.
955 """
956 adb = "adb -s " + device_id
957 cmd = "%s shell log -p i -t \"ItsTestHost\" %s" % (adb, msg)
958 _run(cmd)
959
Sam Linf2f66002017-02-27 21:08:11 -0800960def get_device_fingerprint(device_id):
961 """ Return the Build FingerPrint of the device that the test is running on.
962
963 Returns:
964 Device Build Fingerprint string.
965 """
966 device_bfp = None
967
968 # Get a list of connected devices
969
970 com = ('adb -s %s shell getprop | grep ro.build.fingerprint' % device_id)
971 proc = subprocess.Popen(com.split(), stdout=subprocess.PIPE)
972 output, error = proc.communicate()
973 assert error is None
974
975 lst = string.split( \
976 string.replace( \
977 string.replace( \
978 string.replace(output,
979 '\n', ''), '[', ''), ']', ''), \
980 ' ')
981
982 if lst[0].find('ro.build.fingerprint') != -1:
983 device_bfp = lst[1]
984
985 return device_bfp
986
Ruben Brunk370e2432014-10-14 18:33:23 -0700987def _run(cmd):
988 """Replacement for os.system, with hiding of stdout+stderr messages.
989 """
990 with open(os.devnull, 'wb') as devnull:
991 subprocess.check_call(
992 cmd.split(), stdout=devnull, stderr=subprocess.STDOUT)
993
994class __UnitTest(unittest.TestCase):
995 """Run a suite of unit tests on this module.
996 """
997
Yin-Chia Yeh02eeffb2016-08-16 15:28:03 -0700998 """
999 # TODO: this test currently needs connected device to pass
1000 # Need to remove that dependency before enabling the test
1001 def test_report_result(self):
1002 device_id = get_device_id()
1003 camera_id = "1"
1004 result_key = ItsSession.RESULT_KEY
1005 results = {"scene0":{result_key:"PASS"},
1006 "scene1":{result_key:"PASS"},
1007 "scene2":{result_key:"PASS"},
1008 "scene3":{result_key:"PASS"},
1009 "sceneNotExist":{result_key:"FAIL"}}
1010 report_result(device_id, camera_id, results)
1011 """
Ruben Brunk370e2432014-10-14 18:33:23 -07001012
1013if __name__ == '__main__':
1014 unittest.main()
1015