112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# Copyright 2013 The Android Open Source Project 212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# 312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# Licensed under the Apache License, Version 2.0 (the "License"); 412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# you may not use this file except in compliance with the License. 512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# You may obtain a copy of the License at 612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# 712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# http://www.apache.org/licenses/LICENSE-2.0 812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# 912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# Unless required by applicable law or agreed to in writing, software 1012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# distributed under the License is distributed on an "AS IS" BASIS, 1112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# See the License for the specific language governing permissions and 1312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight# limitations under the License. 1412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 1512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport its.error 1612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport os 1712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport os.path 1812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport sys 1912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport re 2012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport json 2112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport tempfile 2212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport time 2312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport unittest 2412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightimport subprocess 2512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 2612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightclass ItsSession(object): 2712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Controls a device over adb to run ITS scripts. 2812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 2912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The script importing this module (on the host machine) prepares JSON 3012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight objects encoding CaptureRequests, specifying sets of parameters to use 3112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight when capturing an image using the Camera2 APIs. This class encapsualtes 3212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight sending the requests to the device, monitoring the device's progress, and 3312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight copying the resultant captures back to the host machine when done. 3412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 3512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The device must have ItsService.apk installed. 3612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 3712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The "adb logcat" command is used to receive messages from the service 3812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight running on the device. 3912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 4012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Attributes: 4112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight proc: The handle to the process in which "adb logcat" is invoked. 4212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight logcat: The stdout stream from the logcat process. 4312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 4412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 4512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # TODO: Handle multiple connected devices. 4612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # The adb program is used for communication with the device. Need to handle 4712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # the case of multiple devices connected. Currently, uses the "-d" param 4812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # to adb, which causes it to fail if there is more than one device. 4912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight ADB = "adb -d" 5012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 5187df78b48064a9bd31083968476e10242807985fTimothy Knight # Set to True to take a pre-shot before capture and throw it away (for 5287df78b48064a9bd31083968476e10242807985fTimothy Knight # debug purposes). 5387df78b48064a9bd31083968476e10242807985fTimothy Knight CAPTURE_THROWAWAY_SHOTS = False 5487df78b48064a9bd31083968476e10242807985fTimothy Knight 5512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight DEVICE_FOLDER_ROOT = '/sdcard/its' 5612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight DEVICE_FOLDER_CAPTURE = 'captures' 5712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight INTENT_CAPTURE = 'com.android.camera2.its.CAPTURE' 5812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight INTENT_3A = 'com.android.camera2.its.3A' 5912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight INTENT_GETPROPS = 'com.android.camera2.its.GETPROPS' 6012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight TAG = 'CAMERA-ITS-PY' 6112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 6212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_RECV = "RECV" 6312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_SIZE = "SIZE" 6412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_FILE = "FILE" 6512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_CAPT = "CAPT" 6612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_DONE = "DONE" 6712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_FAIL = "FAIL" 6887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_AF = "3A-F" 6987b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_AE = "3A-E" 7087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_AWB = "3A-W" 7187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSGS = [MSG_RECV, MSG_SIZE, MSG_FILE, MSG_CAPT, MSG_DONE, 7287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_FAIL, MSG_AE, MSG_AF, MSG_AWB] 7312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 7412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __init__(self): 7512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.proc = None 7612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot_device_on_argv() 7712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__open_logcat() 7812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 7912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __del__(self): 8012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__kill_logcat() 8112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __enter__(self): 8312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return self 8412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __exit__(self, type, value, traceback): 8612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return False 8712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __open_logcat(self): 8912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Opens the "adb logcat" stream. 9012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 9112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Internal function, called by this class's constructor. 9212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 9312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Gets the adb logcat stream that is intended for parsing by this python 9412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight script. Flushes it first to clear out existing messages. 9512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 9612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Populates the proc and logcat members of this class. 9712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 9812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s logcat -c' % (self.ADB)) 9912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.proc = subprocess.Popen( 10012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB.split() + ["logcat", "-s", "'%s:v'" % (self.TAG)], 10112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight stdout=subprocess.PIPE) 10212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.logcat = self.proc.stdout 10312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 10412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __get_next_msg(self): 10512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Gets the next message from the logcat stream. 10612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 10712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Reads from the logcat stdout stream. Blocks until a new line is ready, 10812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight but exits in the event of a keyboard interrupt (to allow the script to 10912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight be Ctrl-C killed). 11012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 11112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight If the special message "FAIL" is received, kills the script; the test 11212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight shouldn't continue running if something went wrong. The user can then 11312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight manually inspect the device to see what the problem is, for example by 11412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight looking at logcat themself. 11512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 11612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 11712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The next string from the logcat stdout stream. 11812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 11912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 12012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # Get the next logcat line. 12112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight line = self.logcat.readline().strip() 12212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # Get the message, which is the string following the "###" code. 12312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight idx = line.find('### ') 12412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if idx >= 0: 12512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = line[idx+4:] 12612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] == self.MSG_FAIL: 12712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('FAIL device msg received') 12812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return msg 12912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 13012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __kill_logcat(self): 13112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Kill the logcat process. 13212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 13312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Internal function called by this class's destructor. 13412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 13512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.proc: 13612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.proc.kill() 13712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 13812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __send_intent(self, intent_string, intent_params=None): 13912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Send an intent to the device. 14012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 14112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Takes a Python object object specifying the operation to be performed 14212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight on the device, converts it to JSON, sends it to the device over adb, 14312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight then sends an intent to ItsService.apk running on the device with 14412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the path to that JSON file (including starting the service). 14512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 14612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 14712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_string: The string corresponding to the intent to send (3A 14812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight or capture). 14912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_params: A Python dictionary object containing the operations 15012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight to perform; for a capture intent, the dict. contains either 15112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight captureRequest or captureRequestList key, and for a 3A intent, 15212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the dictionary contains a 3A params key. 15312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 15412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s shell mkdir -p "%s"' % ( 15512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB, self.DEVICE_FOLDER_ROOT)) 15612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_args = "" 15712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if intent_params: 15812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight with tempfile.NamedTemporaryFile( 15912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight mode="w", suffix=".json", delete=False) as f: 16012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight tmpfname = f.name 16112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight f.write(json.dumps(intent_params)) 16212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s push %s %s' % ( 16312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB, tmpfname, self.DEVICE_FOLDER_ROOT)) 16412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.remove(tmpfname) 16512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_args = ' -d "file://%s/%s"' % ( 16612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.DEVICE_FOLDER_ROOT, os.path.basename(tmpfname)) 16712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # TODO: Figure out why "--user 0" is needed, and fix the problem 16812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run(('%s shell am startservice --user 0 -t text/plain ' 16912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight '-a %s%s') % (self.ADB, intent_string, intent_args)) 17012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __start_capture(self, request): 17212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__send_intent(self.INTENT_CAPTURE, request) 17312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __start_3a(self, params): 17512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__send_intent(self.INTENT_3A, params) 17612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __start_getprops(self): 17812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__send_intent(self.INTENT_GETPROPS) 17912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 18012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __unpack_msg(self, msg): 18112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Process a string containing a coded message from the device. 18212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 18312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The logcat messages intended to be parsed by this script are of the 18412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight following form: 18512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight RECV - Indicates capture command was received 18612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight SIZE <WIDTH> <HEIGHT> - The width,height of the captured image 18712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight FILE <PATH> - The path on the device of the captured image 18812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight CAPT <I> of <N> - Indicates capt cmd #I out of #N was issued 18912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight DONE - Indicates the capture sequence completed 19012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight FAIL - Indicates an error occurred 19112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 19212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 19312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg: The string message from the device. 19412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 19512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 19612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Tuple containing the message type (a string) and the message 19712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight payload (a list). 19812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 19912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight a = msg.split() 20012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if a[0] not in self.MSGS: 20112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Invalid device message: %s' % (msg)) 20212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return a[0], a[1:] 20312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 20412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __wait_for_camera_properties(self): 20512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Block until the requested camera properties object is available. 20612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 20712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Monitors messages from the service on the device (via logcat), looking 20812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for special coded messages that indicate the status of the request. 20912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 21012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 21112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The remote path (on the device) where the camera properties JSON 21212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight file is stored. 21312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 21412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = None 21512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 21612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] != self.MSG_RECV: 21712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Device msg not RECV: %s' % (msg)) 21812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 21912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 22012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 22112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if msgtype == self.MSG_FILE: 22212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = msgparams[0] 22312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_DONE: 22412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return fname 22512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 22612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __wait_for_capture_done_single(self): 22712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Block until a single capture is done. 22812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 22912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Monitors messages from the service on the device (via logcat), looking 23012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for special coded messages that indicate the status of the captures. 23112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 23212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 23312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The remote path (on the device) where the image file was stored, 23412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight along with the image's width and height. 23512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 23612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = None 23712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = None 23812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = None 23912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 24012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] != self.MSG_RECV: 24112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Device msg not RECV: %s' % (msg)) 24212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 24312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 24412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 24512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if msgtype == self.MSG_SIZE: 24612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = int(msgparams[0]) 24712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = int(msgparams[1]) 24812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_FILE: 24912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = msgparams[0] 25012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_DONE: 25112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return fname, w, h 25212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 25312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __wait_for_capture_done_burst(self, num_req): 25412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Block until a burst of captures is done. 25512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 25612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Monitors messages from the service on the device (via logcat), looking 25712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for special coded messages that indicate the status of the captures. 25812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 25912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 26012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight num_req: The number of captures to wait for. 26112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 26212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 26312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The remote paths (on the device) where the image files were stored, 26412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight along with their width and height. 26512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 26612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fnames = [] 26712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = None 26812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = None 26912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 27012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] != self.MSG_RECV: 27112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Device msg not RECV: %s' % (msg)) 27212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 27312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 27412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 27512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if msgtype == self.MSG_SIZE: 27612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = int(msgparams[0]) 27712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = int(msgparams[1]) 27812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_FILE: 27912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fnames.append(msgparams[0]) 28012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_DONE: 28112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if len(fnames) != num_req or not w or not h: 28212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Missing FILE or SIZE device msg') 28312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return fnames, w, h 28412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 28512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __get_json_path(self, image_fname): 28612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Get the path of the JSON metadata file associated with an image. 28712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 28812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 28912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight image_fname: Path of the image file (local or remote). 29012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 29112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 29212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The path of the associated JSON metadata file, which has the same 29312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight basename but different extension. 29412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 29512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight base, ext = os.path.splitext(image_fname) 29612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return base + ".json" 29712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 29812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __copy_captured_files(self, remote_fnames): 29912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Copy captured data from device back to host machine over adb. 30012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 30112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Copy captured images and associated metadata from the device to the 30212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight host machine. The image and metadata files have the same basename, but 30312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight different file extensions; the captured image is .yuv/.jpg/.raw, and 30412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the captured metadata is .json. 30512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 30612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight File names are unique, as each has the timestamp of the capture in it. 30712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 30812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Deletes the files from the device after they have been transferred off. 30912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 31012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 31112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fnames: List of paths of the captured image files on the 31212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote device. 31312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 31412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 31512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight List of paths of captured image files on the local host machine 31612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight (which is just in the current directory). 31712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 31812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames = [] 31912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for fname in remote_fnames: 32012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s pull %s .' % (self.ADB, fname)) 32112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s pull %s .' % ( 32212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB, self.__get_json_path(fname))) 32312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames.append(os.path.basename(fname)) 32487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight _run('%s shell rm -rf %s/*' % (self.ADB, self.DEVICE_FOLDER_ROOT)) 32512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return local_fnames 32612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 32712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __parse_captured_json(self, local_fnames): 32812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Parse the JSON objects that are returned alongside captured images. 32912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 33012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 33112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames: List of paths of captured image on the local machine. 33212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 33312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 33412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight List of Python objects obtained from loading the argument files 33512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight and converting from the JSON object form to native Python. 33612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 33712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return [json.load(open(self.__get_json_path(f))) for f in local_fnames] 33812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 33912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def get_camera_properties(self): 34012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Get the camera properties object for the device. 34112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 34212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 34312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The Python dictionary object for the CameraProperties object. 34412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 34512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_getprops() 34612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fname = self.__wait_for_camera_properties() 34712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s pull %s .' % (self.ADB, remote_fname)) 34812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fname = os.path.basename(remote_fname) 34912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return self.__parse_captured_json([local_fname])[0]['cameraProperties'] 35012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 3512f0c08fee536ac8d40fa419a67001a02b2cfd1f9Timothy Knight def do_3a(self, region_ae, region_awb, region_af, 35287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight do_ae=True, do_awb=True, do_af=True): 35312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Perform a 3A operation on the device. 35412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 35587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight Triggers some or all of AE, AWB, and AF, and returns once they have 35687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight converged. Uses the vendor 3A that is implemented inside the HAL. 35787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight 35887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight Throws an assertion if 3A fails to converge. 35912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 36012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 36112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight region_ae: Normalized rect. (x,y,w,h) specifying the AE region. 36212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight region_awb: Normalized rect. (x,y,w,h) specifying the AWB region. 3632f0c08fee536ac8d40fa419a67001a02b2cfd1f9Timothy Knight region_af: Normalized rect. (x,y,w,h) specifying the AF region. 36412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 36512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 36687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight Five values: 36787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AE sensitivity; None if do_ae is False 36887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AE exposure time; None if do_ae is False 36987b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AWB gains (list); None if do_awb is False 37087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AWB transform (list); None if do_awb is false 37187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AF focus position; None if do_af is false 37212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 37312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight params = {"regions" : {"ae": region_ae, 37412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "awb": region_awb, 37587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight "af": region_af }, 37687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight "triggers": {"ae": do_ae, 37787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight "af": do_af } } 37812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Running vendor 3A on device" 37912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_3a(params) 38087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_sens = None 38187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_exp = None 38287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_gains = None 38387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_transform = None 38487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight af_dist = None 38587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight while True: 38687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight msg = self.__get_next_msg() 38787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 38887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight if msgtype == self.MSG_AE: 38987b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_sens = int(msgparams[0]) 39087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_exp = int(msgparams[1]) 39187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight elif msgtype == self.MSG_AWB: 39287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_gains = [float(x) for x in msgparams[:4]] 39387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_transform = [float(x) for x in msgparams[4:]] 39487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight elif msgtype == self.MSG_AF: 395719a173531c31d85525bff81eb665aa3a8f06108Timothy Knight af_dist = float(msgparams[0]) if msgparams[0] != "null" else 0 39687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight elif msgtype == self.MSG_DONE: 39787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight if (do_ae and ae_sens == None or do_awb and awb_gains == None 39887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight or do_af and af_dist == None): 39987b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight raise its.error.Error('3A failed to converge') 40087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight return ae_sens, ae_exp, awb_gains, awb_transform, af_dist 40112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 4024902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight def do_capture(self, cap_request, out_surface=None, out_fname_prefix=None): 40312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Issue capture request(s), and read back the image(s) and metadata. 40412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 40512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The main top-level function for capturing one or more images using the 4064902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight device. Captures a single image if cap_request is a single object, and 4074902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight captures a burst if it is a list of objects. 40812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 4094902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight The out_surface field can specify the width, height, and format of 4104902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight the captured image. The format may be "yuv" or "jpeg". The default is 4114902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight a YUV420 frame ("yuv") corresponding to a full sensor frame. 41212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 4134902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight Example of a single capture request: 41412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 41512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight { 4164902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "android.sensor.exposureTime": 100*1000*1000, 4174902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "android.sensor.sensitivity": 100 4184902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight } 4194902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight 4204902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight Example of a list of capture requests: 4214902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight 4224902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight [ 4234902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight { 42412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.exposureTime": 100*1000*1000, 42512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.sensitivity": 100 4264902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight }, 4274902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight { 4284902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "android.sensor.exposureTime": 100*1000*1000, 4294902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "android.sensor.sensitivity": 200 43012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight } 4314902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight ] 43212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 4334902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight Example of an output surface specification: 43412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 43512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight { 4364902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "width": 640, 4374902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "height": 480, 4384902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight "format": "yuv" 43912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight } 44012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 44112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 4424902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight cap_request: The Python dict/list specifying the capture(s), which 44312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight will be converted to JSON and sent to the device. 44412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_fname_prefix: (Optionally) the file name prefix to use for the 44512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight captured files. If this arg is present, then the captured files 44612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight will be renamed appropriately. 44712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 44812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 44912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Four values: 45012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight * The path or list of paths of the captured images (depending on 45112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight whether the request was for a single or burst capture). The paths 45212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight are on the host machine. The captured metadata file(s) have the 45312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight same file names as their corresponding images, with a ".json" 45412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight extension. 45512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight * The width and height of the captured image(s). For a burst, all 45612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight are the same size. 45712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight * The Python dictionary or list of dictionaries (in the case of a 4584902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight burst capture) containing the returned capture result objects. 45912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 4604902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight if not isinstance(cap_request, list): 4614902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight request = {"captureRequest" : cap_request} 4624902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight if out_surface is not None: 4634902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight request["outputSurface"] = out_surface 46487df78b48064a9bd31083968476e10242807985fTimothy Knight if self.CAPTURE_THROWAWAY_SHOTS: 46587df78b48064a9bd31083968476e10242807985fTimothy Knight print "Capturing throw-away image" 46687df78b48064a9bd31083968476e10242807985fTimothy Knight self.__start_capture(request) 46787df78b48064a9bd31083968476e10242807985fTimothy Knight self.__wait_for_capture_done_single() 46887df78b48064a9bd31083968476e10242807985fTimothy Knight print "Capturing image" 46912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_capture(request) 47012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fname, w, h = self.__wait_for_capture_done_single() 47112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fname = self.__copy_captured_files([remote_fname])[0] 47212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_metadata_obj = self.__parse_captured_json([local_fname])[0] 47312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if out_fname_prefix: 47412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _, image_ext = os.path.splitext(local_fname) 47512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(local_fname, out_fname_prefix + image_ext) 47612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(self.__get_json_path(local_fname), 47712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_fname_prefix + ".json") 47812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fname = out_fname_prefix + image_ext 4794902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight return local_fname, w, h, out_metadata_obj["captureResult"] 48012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight else: 4814902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight request = {"captureRequestList" : cap_request} 4824902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight if out_surface is not None: 4834902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight request["outputSurface"] = out_surface 48412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight n = len(request['captureRequestList']) 48512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Capture burst of %d images" % (n) 48612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_capture(request) 48712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fnames, w, h = self.__wait_for_capture_done_burst(n) 48812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames = self.__copy_captured_files(remote_fnames) 48912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_metadata_objs = self.__parse_captured_json(local_fnames) 4904902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight for i in range(len(out_metadata_objs)): 4914902d71d230d21639be817a83e8998230a6ff5e0Timothy Knight out_metadata_objs[i] = out_metadata_objs[i]["captureResult"] 49212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if out_fname_prefix is not None: 49312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for i in range(len(local_fnames)): 49412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _, image_ext = os.path.splitext(local_fnames[i]) 49512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(local_fnames[i], 49612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "%s-%04d%s" % (out_fname_prefix, i, image_ext)) 49712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(self.__get_json_path(local_fnames[i]), 49812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "%s-%04d.json" % (out_fname_prefix, i)) 49912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames[i] = out_fname_prefix + image_ext 50012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return local_fnames, w, h, out_metadata_objs 50112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 50212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightdef _run(cmd): 50312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Replacement for os.system, with hiding of stdout+stderr messages. 50412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 50512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight with open(os.devnull, 'wb') as devnull: 50612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight subprocess.check_call( 50712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight cmd.split(), stdout=devnull, stderr=subprocess.STDOUT) 50812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 50912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightdef reboot_device(sleep_duration=30): 51012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Function to reboot a device and block until it is ready. 51112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 51212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Can be used at the start of a test to get the device into a known good 51312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight state. Will disconnect any other adb sessions, so this function is not 51412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight a part of the ItsSession class (which encapsulates a session with a 51512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight device.) 51612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 51712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 51812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight sleep_duration: (Optional) the length of time to sleep (seconds) after 51912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the device comes online before returning; this gives the device 52012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight time to finish booting. 52112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 52212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Rebooting device" 52312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run("%s reboot" % (ItsSession.ADB)); 52412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run("%s wait-for-device" % (ItsSession.ADB)) 52512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight time.sleep(sleep_duration) 52612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Reboot complete" 52712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 52812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightdef reboot_device_on_argv(): 52912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Examine sys.argv, and reboot if the "reboot" arg is present. 53012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 53112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight If the script command line contains either: 53212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 53312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot 53412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot=30 53512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 53612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight then the device will be rebooted, and if the optional numeric arg is 53712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight present, then that will be the sleep duration passed to the reboot 53812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight call. 53912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 54012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 54112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Boolean, indicating whether the device was rebooted. 54212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 54312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for s in sys.argv[1:]: 54412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if s[:6] == "reboot": 54512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if len(s) > 7 and s[6] == "=": 54612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight duration = int(s[7:]) 54712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot_device(duration) 54812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif len(s) == 6: 54912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot_device() 55012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return True 55112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return False 55212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 55312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightclass __UnitTest(unittest.TestCase): 55412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Run a suite of unit tests on this module. 55512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 55612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 55712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # TODO: Add some unit tests. 55812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight None 55912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 56012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightif __name__ == '__main__': 56112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight unittest.main() 56212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 563