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