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