device.py revision 87b68b020dd72c4cdcf3b8c1f9196c060f947991
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 5112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight DEVICE_FOLDER_ROOT = '/sdcard/its' 5212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight DEVICE_FOLDER_CAPTURE = 'captures' 5312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight INTENT_CAPTURE = 'com.android.camera2.its.CAPTURE' 5412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight INTENT_3A = 'com.android.camera2.its.3A' 5512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight INTENT_GETPROPS = 'com.android.camera2.its.GETPROPS' 5612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight TAG = 'CAMERA-ITS-PY' 5712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 5812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_RECV = "RECV" 5912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_SIZE = "SIZE" 6012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_FILE = "FILE" 6112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_CAPT = "CAPT" 6212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_DONE = "DONE" 6312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight MSG_FAIL = "FAIL" 6487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_AF = "3A-F" 6587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_AE = "3A-E" 6687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_AWB = "3A-W" 6787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSGS = [MSG_RECV, MSG_SIZE, MSG_FILE, MSG_CAPT, MSG_DONE, 6887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight MSG_FAIL, MSG_AE, MSG_AF, MSG_AWB] 6912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 7012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __init__(self): 7112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.proc = None 7212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot_device_on_argv() 7312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__open_logcat() 7412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 7512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __del__(self): 7612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__kill_logcat() 7712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 7812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __enter__(self): 7912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return self 8012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __exit__(self, type, value, traceback): 8212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return False 8312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __open_logcat(self): 8512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Opens the "adb logcat" stream. 8612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Internal function, called by this class's constructor. 8812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 8912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Gets the adb logcat stream that is intended for parsing by this python 9012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight script. Flushes it first to clear out existing messages. 9112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 9212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Populates the proc and logcat members of this class. 9312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 9412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s logcat -c' % (self.ADB)) 9512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.proc = subprocess.Popen( 9612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB.split() + ["logcat", "-s", "'%s:v'" % (self.TAG)], 9712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight stdout=subprocess.PIPE) 9812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.logcat = self.proc.stdout 9912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 10012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __get_next_msg(self): 10112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Gets the next message from the logcat stream. 10212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 10312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Reads from the logcat stdout stream. Blocks until a new line is ready, 10412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight but exits in the event of a keyboard interrupt (to allow the script to 10512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight be Ctrl-C killed). 10612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 10712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight If the special message "FAIL" is received, kills the script; the test 10812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight shouldn't continue running if something went wrong. The user can then 10912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight manually inspect the device to see what the problem is, for example by 11012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight looking at logcat themself. 11112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 11212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 11312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The next string from the logcat stdout stream. 11412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 11512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 11612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # Get the next logcat line. 11712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight line = self.logcat.readline().strip() 11812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # Get the message, which is the string following the "###" code. 11912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight idx = line.find('### ') 12012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if idx >= 0: 12112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = line[idx+4:] 12212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] == self.MSG_FAIL: 12312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('FAIL device msg received') 12412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return msg 12512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 12612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __kill_logcat(self): 12712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Kill the logcat process. 12812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 12912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Internal function called by this class's destructor. 13012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 13112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.proc: 13212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.proc.kill() 13312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 13412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __send_intent(self, intent_string, intent_params=None): 13512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Send an intent to the device. 13612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 13712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Takes a Python object object specifying the operation to be performed 13812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight on the device, converts it to JSON, sends it to the device over adb, 13912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight then sends an intent to ItsService.apk running on the device with 14012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the path to that JSON file (including starting the service). 14112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 14212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 14312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_string: The string corresponding to the intent to send (3A 14412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight or capture). 14512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_params: A Python dictionary object containing the operations 14612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight to perform; for a capture intent, the dict. contains either 14712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight captureRequest or captureRequestList key, and for a 3A intent, 14812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the dictionary contains a 3A params key. 14912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 15012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s shell mkdir -p "%s"' % ( 15112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB, self.DEVICE_FOLDER_ROOT)) 15212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_args = "" 15312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if intent_params: 15412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight with tempfile.NamedTemporaryFile( 15512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight mode="w", suffix=".json", delete=False) as f: 15612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight tmpfname = f.name 15712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight f.write(json.dumps(intent_params)) 15812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s push %s %s' % ( 15912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB, tmpfname, self.DEVICE_FOLDER_ROOT)) 16012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.remove(tmpfname) 16112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight intent_args = ' -d "file://%s/%s"' % ( 16212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.DEVICE_FOLDER_ROOT, os.path.basename(tmpfname)) 16312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # TODO: Figure out why "--user 0" is needed, and fix the problem 16412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run(('%s shell am startservice --user 0 -t text/plain ' 16512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight '-a %s%s') % (self.ADB, intent_string, intent_args)) 16612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 16712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __start_capture(self, request): 16812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__send_intent(self.INTENT_CAPTURE, request) 16912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __start_3a(self, params): 17112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__send_intent(self.INTENT_3A, params) 17212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __start_getprops(self): 17412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__send_intent(self.INTENT_GETPROPS) 17512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __unpack_msg(self, msg): 17712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Process a string containing a coded message from the device. 17812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 17912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The logcat messages intended to be parsed by this script are of the 18012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight following form: 18112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight RECV - Indicates capture command was received 18212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight SIZE <WIDTH> <HEIGHT> - The width,height of the captured image 18312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight FILE <PATH> - The path on the device of the captured image 18412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight CAPT <I> of <N> - Indicates capt cmd #I out of #N was issued 18512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight DONE - Indicates the capture sequence completed 18612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight FAIL - Indicates an error occurred 18712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 18812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 18912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg: The string message from the device. 19012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 19112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 19212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Tuple containing the message type (a string) and the message 19312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight payload (a list). 19412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 19512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight a = msg.split() 19612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if a[0] not in self.MSGS: 19712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Invalid device message: %s' % (msg)) 19812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return a[0], a[1:] 19912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 20012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __wait_for_camera_properties(self): 20112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Block until the requested camera properties object is available. 20212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 20312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Monitors messages from the service on the device (via logcat), looking 20412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for special coded messages that indicate the status of the request. 20512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 20612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 20712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The remote path (on the device) where the camera properties JSON 20812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight file is stored. 20912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 21012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = None 21112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 21212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] != self.MSG_RECV: 21312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Device msg not RECV: %s' % (msg)) 21412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 21512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 21612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 21712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if msgtype == self.MSG_FILE: 21812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = msgparams[0] 21912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_DONE: 22012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return fname 22112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 22212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __wait_for_capture_done_single(self): 22312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Block until a single capture is done. 22412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 22512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Monitors messages from the service on the device (via logcat), looking 22612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for special coded messages that indicate the status of the captures. 22712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 22812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 22912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The remote path (on the device) where the image file was stored, 23012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight along with the image's width and height. 23112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 23212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = None 23312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = None 23412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = None 23512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 23612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] != self.MSG_RECV: 23712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Device msg not RECV: %s' % (msg)) 23812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 23912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 24012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 24112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if msgtype == self.MSG_SIZE: 24212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = int(msgparams[0]) 24312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = int(msgparams[1]) 24412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_FILE: 24512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fname = msgparams[0] 24612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_DONE: 24712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return fname, w, h 24812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 24912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __wait_for_capture_done_burst(self, num_req): 25012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Block until a burst of captures is done. 25112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 25212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Monitors messages from the service on the device (via logcat), looking 25312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for special coded messages that indicate the status of the captures. 25412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 25512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 25612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight num_req: The number of captures to wait for. 25712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 25812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 25912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The remote paths (on the device) where the image files were stored, 26012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight along with their width and height. 26112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 26212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fnames = [] 26312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = None 26412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = None 26512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 26612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if self.__unpack_msg(msg)[0] != self.MSG_RECV: 26712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Device msg not RECV: %s' % (msg)) 26812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight while True: 26912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msg = self.__get_next_msg() 27012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 27112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if msgtype == self.MSG_SIZE: 27212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight w = int(msgparams[0]) 27312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight h = int(msgparams[1]) 27412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_FILE: 27512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fnames.append(msgparams[0]) 27612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif msgtype == self.MSG_DONE: 27712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if len(fnames) != num_req or not w or not h: 27812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error('Missing FILE or SIZE device msg') 27912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return fnames, w, h 28012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 28112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __get_json_path(self, image_fname): 28212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Get the path of the JSON metadata file associated with an image. 28312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 28412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 28512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight image_fname: Path of the image file (local or remote). 28612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 28712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 28812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The path of the associated JSON metadata file, which has the same 28912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight basename but different extension. 29012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 29112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight base, ext = os.path.splitext(image_fname) 29212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return base + ".json" 29312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 29412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __copy_captured_files(self, remote_fnames): 29512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Copy captured data from device back to host machine over adb. 29612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 29712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Copy captured images and associated metadata from the device to the 29812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight host machine. The image and metadata files have the same basename, but 29912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight different file extensions; the captured image is .yuv/.jpg/.raw, and 30012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the captured metadata is .json. 30112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 30212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight File names are unique, as each has the timestamp of the capture in it. 30312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 30412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Deletes the files from the device after they have been transferred off. 30512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 30612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 30712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fnames: List of paths of the captured image files on the 30812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote device. 30912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 31012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 31112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight List of paths of captured image files on the local host machine 31212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight (which is just in the current directory). 31312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 31412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames = [] 31512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for fname in remote_fnames: 31612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s pull %s .' % (self.ADB, fname)) 31712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s pull %s .' % ( 31812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.ADB, self.__get_json_path(fname))) 31912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames.append(os.path.basename(fname)) 32087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight _run('%s shell rm -rf %s/*' % (self.ADB, self.DEVICE_FOLDER_ROOT)) 32112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return local_fnames 32212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 32312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def __parse_captured_json(self, local_fnames): 32412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Parse the JSON objects that are returned alongside captured images. 32512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 32612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 32712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames: List of paths of captured image on the local machine. 32812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 32912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 33012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight List of Python objects obtained from loading the argument files 33112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight and converting from the JSON object form to native Python. 33212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 33312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return [json.load(open(self.__get_json_path(f))) for f in local_fnames] 33412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 33512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def get_camera_properties(self): 33612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Get the camera properties object for the device. 33712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 33812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 33912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The Python dictionary object for the CameraProperties object. 34012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 34112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_getprops() 34212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fname = self.__wait_for_camera_properties() 34312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run('%s pull %s .' % (self.ADB, remote_fname)) 34412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fname = os.path.basename(remote_fname) 34512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return self.__parse_captured_json([local_fname])[0]['cameraProperties'] 34612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 34787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight def do_3a(self, region_ae, region_af, region_awb, 34887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight do_ae=True, do_awb=True, do_af=True): 34912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Perform a 3A operation on the device. 35012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 35187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight Triggers some or all of AE, AWB, and AF, and returns once they have 35287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight converged. Uses the vendor 3A that is implemented inside the HAL. 35387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight 35487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight Throws an assertion if 3A fails to converge. 35512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 35612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 35712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight region_ae: Normalized rect. (x,y,w,h) specifying the AE region. 35812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight region_af: Normalized rect. (x,y,w,h) specifying the AF region. 35912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight region_awb: Normalized rect. (x,y,w,h) specifying the AWB region. 36012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 36112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 36287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight Five values: 36387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AE sensitivity; None if do_ae is False 36487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AE exposure time; None if do_ae is False 36587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AWB gains (list); None if do_awb is False 36687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AWB transform (list); None if do_awb is false 36787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight * AF focus position; None if do_af is false 36812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 36912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight params = {"regions" : {"ae": region_ae, 37012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "awb": region_awb, 37187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight "af": region_af }, 37287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight "triggers": {"ae": do_ae, 37387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight "af": do_af } } 37412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Running vendor 3A on device" 37512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_3a(params) 37687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_sens = None 37787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_exp = None 37887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_gains = None 37987b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_transform = None 38087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight af_dist = None 38187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight while True: 38287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight msg = self.__get_next_msg() 38387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight msgtype, msgparams = self.__unpack_msg(msg) 38487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight if msgtype == self.MSG_AE: 38587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_sens = int(msgparams[0]) 38687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight ae_exp = int(msgparams[1]) 38787b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight elif msgtype == self.MSG_AWB: 38887b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_gains = [float(x) for x in msgparams[:4]] 38987b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight awb_transform = [float(x) for x in msgparams[4:]] 39087b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight elif msgtype == self.MSG_AF: 39187b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight af_dist = float(msgparams[0]) 39287b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight elif msgtype == self.MSG_DONE: 39387b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight if (do_ae and ae_sens == None or do_awb and awb_gains == None 39487b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight or do_af and af_dist == None): 39587b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight raise its.error.Error('3A failed to converge') 39687b68b020dd72c4cdcf3b8c1f9196c060f947991Timothy Knight return ae_sens, ae_exp, awb_gains, awb_transform, af_dist 39712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 39812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight def do_capture(self, request, out_fname_prefix=None): 39912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Issue capture request(s), and read back the image(s) and metadata. 40012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 40112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The main top-level function for capturing one or more images using the 40212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight device. Captures a single image if the request has the "captureRequest" 40312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight key, and captures a burst if it has "captureRequestList". 40412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 40512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight The request object may also contain an "outputSurface" field to specify 40612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the width, height, and format of the captured image. Supported formats 40712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight are "yuv" and "jpeg". If no outputSurface field was passed inside the 40812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight request object, then the default is used, which is "yuv" (a YUV420 40912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight fully planar image) corresponding to a full sensor frame. 41012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 41112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Example request 1: 41212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 41312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight { 41412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "captureRequest": { 41512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.exposureTime": 100*1000*1000, 41612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.sensitivity": 100 41712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight } 41812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight } 41912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 42012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Example request 2: 42112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 42212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight { 42312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "captureRequestList": [ 42412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight { 42512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.exposureTime": 100*1000*1000, 42612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.sensitivity": 100 42712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight }, 42812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight { 42912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.exposureTime": 100*1000*1000, 43012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "android.sensor.sensitivity": 200 43112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight }], 43212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "outputSurface": { 43312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "width": 640, 43412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "height": 480, 43512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "format": "yuv" 43612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight } 43712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight } 43812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 43912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 44012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight request: The Python dictionary specifying the capture(s), which 44112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight will be converted to JSON and sent to the device. 44212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_fname_prefix: (Optionally) the file name prefix to use for the 44312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight captured files. If this arg is present, then the captured files 44412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight will be renamed appropriately. 44512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 44612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 44712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Four values: 44812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight * The path or list of paths of the captured images (depending on 44912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight whether the request was for a single or burst capture). The paths 45012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight are on the host machine. The captured metadata file(s) have the 45112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight same file names as their corresponding images, with a ".json" 45212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight extension. 45312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight * The width and height of the captured image(s). For a burst, all 45412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight are the same size. 45512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight * The Python dictionary or list of dictionaries (in the case of a 45612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight burst capture) containing the metadata of the captured image(s). 45712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 45812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if request.has_key("captureRequest"): 45912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 46012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Capturing image (including a pre-shot for settings synch)" 46112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 46212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # HACK: Take a pre-shot, to make sure the settings stick. 46312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # TODO: Remove this code once it is no longer needed. 46412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_capture(request) 46512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__wait_for_capture_done_single() 46612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 46712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_capture(request) 46812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fname, w, h = self.__wait_for_capture_done_single() 46912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fname = self.__copy_captured_files([remote_fname])[0] 47012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_metadata_obj = self.__parse_captured_json([local_fname])[0] 47112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if out_fname_prefix: 47212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _, image_ext = os.path.splitext(local_fname) 47312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(local_fname, out_fname_prefix + image_ext) 47412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(self.__get_json_path(local_fname), 47512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_fname_prefix + ".json") 47612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fname = out_fname_prefix + image_ext 47712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return local_fname, w, h, out_metadata_obj 47812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight else: 47912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if not request.has_key("captureRequestList"): 48012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight raise its.error.Error( 48112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 'Missing captureRequest or captureRequestList arg key') 48212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight n = len(request['captureRequestList']) 48312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Capture burst of %d images" % (n) 48412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight self.__start_capture(request) 48512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight remote_fnames, w, h = self.__wait_for_capture_done_burst(n) 48612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames = self.__copy_captured_files(remote_fnames) 48712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight out_metadata_objs = self.__parse_captured_json(local_fnames) 48812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if out_fname_prefix is not None: 48912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for i in range(len(local_fnames)): 49012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _, image_ext = os.path.splitext(local_fnames[i]) 49112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(local_fnames[i], 49212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "%s-%04d%s" % (out_fname_prefix, i, image_ext)) 49312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight os.rename(self.__get_json_path(local_fnames[i]), 49412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight "%s-%04d.json" % (out_fname_prefix, i)) 49512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight local_fnames[i] = out_fname_prefix + image_ext 49612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return local_fnames, w, h, out_metadata_objs 49712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 49812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightdef _run(cmd): 49912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Replacement for os.system, with hiding of stdout+stderr messages. 50012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 50112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight with open(os.devnull, 'wb') as devnull: 50212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight subprocess.check_call( 50312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight cmd.split(), stdout=devnull, stderr=subprocess.STDOUT) 50412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 50512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightdef reboot_device(sleep_duration=30): 50612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Function to reboot a device and block until it is ready. 50712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 50812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Can be used at the start of a test to get the device into a known good 50912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight state. Will disconnect any other adb sessions, so this function is not 51012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight a part of the ItsSession class (which encapsulates a session with a 51112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight device.) 51212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 51312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Args: 51412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight sleep_duration: (Optional) the length of time to sleep (seconds) after 51512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight the device comes online before returning; this gives the device 51612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight time to finish booting. 51712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 51812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Rebooting device" 51912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run("%s reboot" % (ItsSession.ADB)); 52012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight _run("%s wait-for-device" % (ItsSession.ADB)) 52112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight time.sleep(sleep_duration) 52212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight print "Reboot complete" 52312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 52412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightdef reboot_device_on_argv(): 52512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Examine sys.argv, and reboot if the "reboot" arg is present. 52612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 52712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight If the script command line contains either: 52812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 52912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot 53012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot=30 53112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 53212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight then the device will be rebooted, and if the optional numeric arg is 53312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight present, then that will be the sleep duration passed to the reboot 53412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight call. 53512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 53612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Returns: 53712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight Boolean, indicating whether the device was rebooted. 53812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 53912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight for s in sys.argv[1:]: 54012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if s[:6] == "reboot": 54112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight if len(s) > 7 and s[6] == "=": 54212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight duration = int(s[7:]) 54312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot_device(duration) 54412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight elif len(s) == 6: 54512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight reboot_device() 54612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return True 54712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight return False 54812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 54912339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightclass __UnitTest(unittest.TestCase): 55012339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """Run a suite of unit tests on this module. 55112339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight """ 55212339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 55312339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight # TODO: Add some unit tests. 55412339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight None 55512339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 55612339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knightif __name__ == '__main__': 55712339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight unittest.main() 55812339cdd90b2c98eb4f7adf794ddca8de53992b8Timothy Knight 559