CameraITS: Changes for ITS-in-a-box

Bug: 28718586
Change-Id: Ia6e3db87a92cc596abf52048c2d72e8f374fa206
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index c33092d..03b45d1 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -17,7 +17,7 @@
 # and that the unit tests for the modules passed (indicating that the setup
 # is correct).
 
-CAMERA_ITS_TOP=$PWD
+export CAMERA_ITS_TOP=$PWD
 
 [[ "${BASH_SOURCE[0]}" != "${0}" ]] || \
     { echo ">> Script must be sourced with 'source $0'" >&2; exit 1; }
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index 692a62d..3b378cc 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -754,15 +754,24 @@
     Return the device ID provided in the command line if it's connected. If no
     device ID is provided in the command line and there is only one device
     connected, return the device ID by parsing the result of "adb devices".
+    Also, if the environment variable ANDROID_SERIAL is set, use it as device
+    id. When both ANDROID_SERIAL and device argument present, device argument
+    takes priority.
 
     Raise an exception if no device is connected; or the device ID provided in
     the command line is not connected; or no device ID is provided in the
-    command line and there are more than 1 device connected.
+    command line or environment variable and there are more than 1 device
+    connected.
 
     Returns:
         Device ID string.
     """
     device_id = None
+
+    # Check if device id is set in env
+    if "ANDROID_SERIAL" in os.environ:
+        device_id = os.environ["ANDROID_SERIAL"]
+
     for s in sys.argv[1:]:
         if s[:7] == "device=" and len(s) > 7:
             device_id = str(s[7:])
diff --git a/apps/CameraITS/tests/scene1/scene1.pdf b/apps/CameraITS/tests/scene1/scene1.pdf
new file mode 100644
index 0000000..e695748
--- /dev/null
+++ b/apps/CameraITS/tests/scene1/scene1.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/scene2.pdf b/apps/CameraITS/tests/scene2/scene2.pdf
new file mode 100644
index 0000000..ccde9d98
--- /dev/null
+++ b/apps/CameraITS/tests/scene2/scene2.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene2/test_faces.py b/apps/CameraITS/tests/scene2/test_faces.py
index cce74e7..c05fbc7 100644
--- a/apps/CameraITS/tests/scene2/test_faces.py
+++ b/apps/CameraITS/tests/scene2/test_faces.py
@@ -31,7 +31,10 @@
         fd_modes = props['android.statistics.info.availableFaceDetectModes']
         a = props['android.sensor.info.activeArraySize']
         aw, ah = a['right'] - a['left'], a['bottom'] - a['top']
-        cam.do_3a()
+        gain, exp, _, _, focus = cam.do_3a(get_results=True)
+        print 'iso = %d' % gain
+        print 'exp = %.2fms' % (exp*1.0E-6)
+        print 'fd = %.2fcm' % (1.0E2/focus)
         for fd_mode in fd_modes:
             assert(FD_MODE_OFF <= fd_mode <= FD_MODE_FULL)
             req = its.objects.auto_capture_request()
@@ -41,6 +44,9 @@
                 md = cap['metadata']
                 assert(md['android.statistics.faceDetectMode'] == fd_mode)
                 faces = md['android.statistics.faces']
+                img = its.image.convert_capture_to_rgb_image(cap, props=props)
+                img_name = "%s_fd_mode_%s.jpg" % (NAME, fd_mode)
+                its.image.write_image(img, img_name)
 
                 # 0 faces should be returned for OFF mode
                 if fd_mode == FD_MODE_OFF:
diff --git a/apps/CameraITS/tests/scene3/scene3.pdf b/apps/CameraITS/tests/scene3/scene3.pdf
new file mode 100644
index 0000000..4c787b1
--- /dev/null
+++ b/apps/CameraITS/tests/scene3/scene3.pdf
Binary files differ
diff --git a/apps/CameraITS/tests/scene4/scene4.pdf b/apps/CameraITS/tests/scene4/scene4.pdf
new file mode 100644
index 0000000..7dcc4b9
--- /dev/null
+++ b/apps/CameraITS/tests/scene4/scene4.pdf
Binary files differ
diff --git a/apps/CameraITS/tools/load_scene.py b/apps/CameraITS/tools/load_scene.py
new file mode 100644
index 0000000..ae35914
--- /dev/null
+++ b/apps/CameraITS/tools/load_scene.py
@@ -0,0 +1,57 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import re
+import subprocess
+import sys
+import time
+
+
+def main():
+    """Load charts on device and display."""
+    camera_id = -1
+    scene = None
+    for s in sys.argv[1:]:
+        if s[:6] == 'scene=' and len(s) > 6:
+            scene = s[6:]
+        elif s[:7] == 'screen=' and len(s) > 7:
+            screen_id = s[7:]
+
+    cmd = ('adb -s %s shell am force-stop com.google.android.apps.docs' %
+           screen_id)
+    subprocess.Popen(cmd.split())
+
+    if not scene:
+        print 'Error: need to specify which scene to load'
+        assert False
+
+    remote_scene_file = '/sdcard/Download/%s.pdf' % scene
+    local_scene_file = os.path.join(os.environ['CAMERA_ITS_TOP'], 'tests',
+                                    scene, scene+'.pdf')
+    print 'Loading %s on %s' % (remote_scene_file, screen_id)
+    cmd = 'adb -s %s push %s /mnt%s' % (screen_id, local_scene_file,
+                                        remote_scene_file)
+    subprocess.Popen(cmd.split())
+    time.sleep(1)  # wait-for-device doesn't always seem to work...
+    # The intent require PDF viewing app be installed on device.
+    # Also the first time such app is opened it might request some permission,
+    # so it's  better to grant those permissions before using this script
+    cmd = ("adb -s %s wait-for-device shell am start -d 'file://%s'"
+           " -a android.intent.action.VIEW" % (screen_id,
+                                               remote_scene_file))
+    subprocess.Popen(cmd.split())
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index 678c35c..c6ff548 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -34,6 +34,11 @@
         scenes: the test scene(s) to be executed. Use comma to separate multiple
                 scenes. Ex: "scenes=scene0,scene1" or "scenes=0,1,sensor_fusion"
                 (sceneX can be abbreviated by X where X is a integer)
+        chart: [Experimental] another android device served as test chart
+               display. When this argument presents, change of test scene will
+               be handled automatically. Note that this argument requires
+               special physical/hardware setup to work and may not work on
+               all android devices.
     """
 
     SKIP_RET_CODE = 101
@@ -59,6 +64,8 @@
 
     all_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4", "scene5"]
 
+    auto_scenes = ["scene0", "scene1", "scene2", "scene3", "scene4"]
+
     scene_req = {
         "scene0" : None,
         "scene1" : "A grey card covering at least the middle 30% of the scene",
@@ -83,28 +90,34 @@
 
     camera_ids = []
     scenes = []
+    chart_host_id = None
     for s in sys.argv[1:]:
         if s[:7] == "camera=" and len(s) > 7:
             camera_ids = s[7:].split(',')
         elif s[:7] == "scenes=" and len(s) > 7:
             scenes = s[7:].split(',')
+        elif s[:6] == 'chart=' and len(s) > 6:
+            chart_host_id = s[6:]
+
+    auto_scene_switch = chart_host_id is not None
 
     # Run through all scenes if user does not supply one
+    possible_scenes = auto_scenes if auto_scene_switch else all_scenes
     if not scenes:
-        scenes = all_scenes
+        scenes = possible_scenes
     else:
         # Validate user input scene names
         valid_scenes = True
         temp_scenes = []
         for s in scenes:
-            if s in all_scenes:
+            if s in possible_scenes:
                 temp_scenes.append(s)
             else:
                 try:
                     # Try replace "X" to "sceneX"
                     scene_num = int(s)
                     scene_str = "scene" + s
-                    if scene_str not in all_scenes:
+                    if scene_str not in possible_scenes:
                         valid_scenes = False
                         break
                     temp_scenes.append(scene_str)
@@ -146,6 +159,14 @@
 
     print "Running ITS on camera: %s, scene %s" % (camera_ids, scenes)
 
+    if auto_scene_switch:
+        print 'Waking up chart screen: ', chart_host_id
+        screen_id_arg = ('screen=%s' % chart_host_id)
+        cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
+                                      'wake_up_screen.py'), screen_id_arg]
+        retcode = subprocess.call(cmd)
+        assert retcode == 0
+
     for camera_id in camera_ids:
         # Loop capturing images until user confirm test scene is correct
         camera_id_arg = "camera=" + camera_id
@@ -169,18 +190,34 @@
             if scene_req[scene] != None:
                 out_path = os.path.join(topdir, camera_id, scene+".jpg")
                 out_arg = "out=" + out_path
-                scene_arg = "scene=" + scene_req[scene]
-                extra_args = scene_extra_args.get(scene, [])
-                cmd = ['python',
-                        os.path.join(os.getcwd(),"tools/validate_scene.py"),
-                        camera_id_arg, out_arg, scene_arg, device_id_arg] + \
-                        extra_args
+                if auto_scene_switch:
+                    scene_arg = "scene=" + scene
+                    cmd = ['python',
+                           os.path.join(os.getcwd(), 'tools/load_scene.py'),
+                           scene_arg, screen_id_arg]
+                else:
+                    scene_arg = "scene=" + scene_req[scene]
+                    extra_args = scene_extra_args.get(scene, [])
+                    cmd = ['python',
+                            os.path.join(os.getcwd(),"tools/validate_scene.py"),
+                            camera_id_arg, out_arg,
+                            scene_arg, device_id_arg] + extra_args
                 retcode = subprocess.call(cmd,cwd=topdir)
                 assert(retcode == 0)
             print "Start running ITS on camera %s, %s" % (camera_id, scene)
 
             # Run each test, capturing stdout and stderr.
             for (testname,testpath) in tests:
+                if auto_scene_switch:
+                    # Send an input event to keep the screen not dimmed.
+                    # Since we are not using camera of chart screen, FOCUS event
+                    # should does nothing but keep the screen from dimming.
+                    # The "sleep after x minutes of inactivity" display setting
+                    # determines how long this command can keep screen bright.
+                    # Setting it to something like 30 minutes should be enough.
+                    cmd = ('adb -s %s shell input keyevent FOCUS'
+                           % chart_host_id)
+                    subprocess.call(cmd.split())
                 cmd = ['python', os.path.join(os.getcwd(),testpath)] + \
                       sys.argv[1:] + [camera_id_arg]
                 outdir = os.path.join(topdir,camera_id,scene)
@@ -243,6 +280,14 @@
         print "Reporting ITS result to CtsVerifier"
         its.device.report_result(device_id, camera_id, results)
 
+    if auto_scene_switch:
+        print 'Shutting down chart screen: ', chart_host_id
+        screen_id_arg = ('screen=%s' % chart_host_id)
+        cmd = ['python', os.path.join(os.environ['CAMERA_ITS_TOP'], 'tools',
+                                      'turn_off_screen.py'), screen_id_arg]
+        retcode = subprocess.call(cmd)
+        assert retcode == 0
+
     print "ITS tests finished. Please go back to CtsVerifier and proceed"
 
 if __name__ == '__main__':
diff --git a/apps/CameraITS/tools/turn_off_screen.py b/apps/CameraITS/tools/turn_off_screen.py
new file mode 100644
index 0000000..58c77c8
--- /dev/null
+++ b/apps/CameraITS/tools/turn_off_screen.py
@@ -0,0 +1,37 @@
+# Copyright 2016 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+import subprocess
+import sys
+
+
+def main():
+    """Put screen to sleep."""
+    screen_id = ''
+    for s in sys.argv[1:]:
+        if s[:7] == 'screen=' and len(s) > 7:
+            screen_id = s[7:]
+    cmd = ('adb -s %s shell dumpsys power | egrep "Display Power"'
+           % screen_id)
+    process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
+    cmd_ret = process.stdout.read()
+    screen_state = re.split(r'[s|=]', cmd_ret)[-1]
+    if screen_state == 'OFF\n':
+        print 'Screen OFF. Turning ON.'
+    else:
+        wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
+        subprocess.Popen(wakeup.split())
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tools/wake_up_screen.py b/apps/CameraITS/tools/wake_up_screen.py
new file mode 100644
index 0000000..2de4d22
--- /dev/null
+++ b/apps/CameraITS/tools/wake_up_screen.py
@@ -0,0 +1,42 @@
+# Copyright 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import re
+import subprocess
+import sys
+import time
+
+def main():
+    """Power up and unlock screen as needed."""
+    screen_id = None
+    for s in sys.argv[1:]:
+        if s[:7] == 'screen=' and len(s) > 7:
+            screen_id = s[7:]
+    cmd = ('adb -s %s shell dumpsys display | egrep "mScreenState"'
+           % screen_id)
+    process = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE)
+    cmd_ret = process.stdout.read()
+    screen_state = re.split(r'[s|=]', cmd_ret)[-1]
+    if 'OFF' in screen_state:
+        print 'Screen OFF. Turning ON.'
+        wakeup = ('adb -s %s shell input keyevent POWER' % screen_id)
+        subprocess.Popen(wakeup.split())
+        time.sleep(0.5)  # some screens need pause for next command
+    unlock = ('adb -s %s wait-for-device shell wm dismiss-keyguard'
+              % screen_id)
+    subprocess.Popen(unlock.split())
+    time.sleep(0.5)  # some screens need time for command to take effect
+
+if __name__ == '__main__':
+    main()