Merge "Camera: fix recording test for legacy mode" into lmp-sprout-dev
diff --git a/apps/CameraITS/CameraITS.pdf b/apps/CameraITS/CameraITS.pdf
index 0d10bae..2430420 100644
--- a/apps/CameraITS/CameraITS.pdf
+++ b/apps/CameraITS/CameraITS.pdf
Binary files differ
diff --git a/apps/CameraITS/pymodules/its/device.py b/apps/CameraITS/pymodules/its/device.py
index beba0ae..035e70b 100644
--- a/apps/CameraITS/pymodules/its/device.py
+++ b/apps/CameraITS/pymodules/its/device.py
@@ -53,7 +53,9 @@
     PACKAGE = 'com.android.cts.verifier.camera.its'
     INTENT_START = 'com.android.cts.verifier.camera.its.START'
     ACTION_ITS_RESULT = 'com.android.cts.verifier.camera.its.ACTION_ITS_RESULT'
+    EXTRA_CAMERA_ID = 'camera.its.extra.CAMERA_ID'
     EXTRA_SUCCESS = 'camera.its.extra.SUCCESS'
+    EXTRA_SUMMARY = 'camera.its.extra.SUMMARY'
 
     # TODO: Handle multiple connected devices.
     ADB = "adb -d"
@@ -241,6 +243,20 @@
             raise its.error.Error('Invalid command response')
         return data['objValue']
 
+    def get_camera_ids(self):
+        """Get a list of camera device Ids that can be opened.
+
+        Returns:
+            a list of camera ID string
+        """
+        cmd = {}
+        cmd["cmdName"] = "getCameraIds"
+        self.sock.send(json.dumps(cmd) + "\n")
+        data,_ = self.__read_response_from_socket()
+        if data['tag'] != 'cameraIds':
+            raise its.error.Error('Invalid command response')
+        return data['objValue']['cameraIdArray']
+
     def get_camera_properties(self):
         """Get the camera properties object for the device.
 
@@ -510,21 +526,32 @@
             rets.append(objs if ncap>1 else objs[0])
         return rets if len(rets)>1 else rets[0]
 
-def report_result(camera_id, success):
+def report_result(camera_id, success, summary_path=None):
     """Send a pass/fail result to the device, via an intent.
 
     Args:
         camera_id: The ID string of the camera for which to report pass/fail.
         success: Boolean, indicating if the result was pass or fail.
+        summary_path: (Optional) path to ITS summary file on host PC
 
     Returns:
         Nothing.
     """
-    resultstr = "%s=%s" % (camera_id, 'True' if success else 'False')
-    _run(('%s shell am broadcast '
-          '-a %s --es %s %s') % (ItsSession.ADB, ItsSession.ACTION_ITS_RESULT,
-          ItsSession.EXTRA_SUCCESS, resultstr))
-
+    device_summary_path = "/sdcard/camera_" + camera_id + "_its_summary.txt"
+    if summary_path is not None:
+        _run("%s push %s %s" % (
+                ItsSession.ADB, summary_path, device_summary_path))
+        _run("%s shell am broadcast -a %s --es %s %s --es %s %s --es %s %s" % (
+                ItsSession.ADB, ItsSession.ACTION_ITS_RESULT,
+                ItsSession.EXTRA_CAMERA_ID, camera_id,
+                ItsSession.EXTRA_SUCCESS, 'True' if success else 'False',
+                ItsSession.EXTRA_SUMMARY, device_summary_path))
+    else:
+        _run("%s shell am broadcast -a %s --es %s %s --es %s %s --es %s %s" % (
+                ItsSession.ADB, ItsSession.ACTION_ITS_RESULT,
+                ItsSession.EXTRA_CAMERA_ID, camera_id,
+                ItsSession.EXTRA_SUCCESS, 'True' if success else 'False',
+                ItsSession.EXTRA_SUMMARY, "null"))
 
 def _run(cmd):
     """Replacement for os.system, with hiding of stdout+stderr messages.
diff --git a/apps/CameraITS/tests/scene1/test_capture_result.py b/apps/CameraITS/tests/scene1/test_capture_result.py
index 331d1cd..ec919f8 100644
--- a/apps/CameraITS/tests/scene1/test_capture_result.py
+++ b/apps/CameraITS/tests/scene1/test_capture_result.py
@@ -39,14 +39,15 @@
                              its.caps.per_frame_control(props))
 
         manual_tonemap = [0,0, 1,1] # Linear
-        manual_transform = its.objects.int_to_rational([1,2,3, 4,5,6, 7,8,9])
-        manual_gains = [1,2,3,4]
+        manual_transform = its.objects.float_to_rational(
+                [-1.5,-1.0,-0.5, 0.0,0.5,1.0, 1.5,2.0,3.0])
+        manual_gains = [1,1.5,2.0,3.0]
         manual_region = [{"x":8,"y":8,"width":128,"height":128,"weight":1}]
         manual_exp_time = min(props['android.sensor.info.exposureTimeRange'])
         manual_sensitivity = min(props['android.sensor.info.sensitivityRange'])
 
         # The camera HAL may not support different gains for two G channels.
-        manual_gains_ok = [[1,2,3,4],[1,2,2,4],[1,3,3,4]]
+        manual_gains_ok = [[1,1.5,2.0,3.0],[1,1.5,1.5,3.0],[1,2.0,2.0,3.0]]
 
         auto_req = its.objects.auto_capture_request()
         auto_req["android.statistics.lensShadingMapMode"] = 1
diff --git a/apps/CameraITS/tools/get_camera_ids.py b/apps/CameraITS/tools/get_camera_ids.py
new file mode 100644
index 0000000..010b046
--- /dev/null
+++ b/apps/CameraITS/tools/get_camera_ids.py
@@ -0,0 +1,37 @@
+# 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 sys
+import its.device
+import its.objects
+import its.image
+
+def main():
+    """get camera ids and save it to disk.
+    """
+    out_path = ""
+    for s in sys.argv[1:]:
+        if s[:4] == "out=" and len(s) > 4:
+            out_path = s[4:]
+    # kind of weird we need to open a camera to get camera ids, but
+    # this is how ITS is working now.
+    with its.device.ItsSession() as cam:
+        camera_ids = cam.get_camera_ids()
+        if out_path != "":
+            with open(out_path, "w") as f:
+                for camera_id in camera_ids:
+                    f.write(camera_id + "\n")
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CameraITS/tools/run_all_tests.py b/apps/CameraITS/tools/run_all_tests.py
index f5a53b1..b56281d 100644
--- a/apps/CameraITS/tools/run_all_tests.py
+++ b/apps/CameraITS/tools/run_all_tests.py
@@ -18,6 +18,7 @@
 import subprocess
 import time
 import sys
+import textwrap
 import its.device
 
 def main():
@@ -57,62 +58,111 @@
 
     # Make output directories to hold the generated files.
     topdir = tempfile.mkdtemp()
-    for d in scenes:
-        os.mkdir(os.path.join(topdir, d))
     print "Saving output files to:", topdir, "\n"
 
-    # determine camera id
-    camera_id = 0
+    camera_ids = []
     for s in sys.argv[1:]:
         if s[:7] == "camera=" and len(s) > 7:
-            camera_id = s[7:]
+            camera_ids.append(s[7:])
 
-    # Run each test, capturing stdout and stderr.
-    numpass = 0
-    numskip = 0
-    numnotmandatedfail = 0
-    numfail = 0
-    for (scene,testname,testpath) in tests:
-        cmd = ['python', os.path.join(os.getcwd(),testpath)] + sys.argv[1:]
-        outdir = os.path.join(topdir,scene)
-        outpath = os.path.join(outdir,testname+"_stdout.txt")
-        errpath = os.path.join(outdir,testname+"_stderr.txt")
-        t0 = time.time()
-        with open(outpath,"w") as fout, open(errpath,"w") as ferr:
-            retcode = subprocess.call(cmd,stderr=ferr,stdout=fout,cwd=outdir)
-        t1 = time.time()
+    # user doesn't specify camera id, run through all cameras
+    if not camera_ids:
+        camera_ids_path = os.path.join(topdir, "camera_ids.txt")
+        out_arg = "out=" + camera_ids_path
+        cmd = ['python',
+               os.path.join(os.getcwd(),"tools/get_camera_ids.py"), out_arg]
+        retcode = subprocess.call(cmd,cwd=topdir)
+        assert(retcode == 0)
+        with open(camera_ids_path, "r") as f:
+            for line in f:
+                camera_ids.append(line.replace('\n', ''))
 
-        if retcode == 0:
-            retstr = "PASS "
-            numpass += 1
-        elif retcode == SKIP_RET_CODE:
-            retstr = "SKIP "
-            numskip += 1
-        elif retcode != 0 and testname in NOT_YET_MANDATED[scene]:
-            retstr = "FAIL*"
-            numnotmandatedfail += 1
+    print "Running ITS on the following cameras:", camera_ids
+
+    for camera_id in camera_ids:
+        # Loop capturing images until user confirm test scene is correct
+        camera_id_arg = "camera=" + camera_id
+        print "Preparing to run ITS on camera", camera_id
+
+        os.mkdir(os.path.join(topdir, camera_id))
+        for d in scenes:
+            os.mkdir(os.path.join(topdir, camera_id, d))
+
+        out_path = os.path.join(topdir, camera_id, "scene.jpg")
+        out_arg = "out=" + out_path
+        cmd = ['python',
+               os.path.join(os.getcwd(),"tools/validate_scene.py"),
+               camera_id_arg, out_arg]
+        retcode = subprocess.call(cmd,cwd=topdir)
+        assert(retcode == 0)
+
+        print "Start running ITS on camera: ", camera_id
+        # Run each test, capturing stdout and stderr.
+        summary = "ITS test result summary for camera " + camera_id + "\n"
+        numpass = 0
+        numskip = 0
+        numnotmandatedfail = 0
+        numfail = 0
+
+        for (scene,testname,testpath) in tests:
+            cmd = ['python', os.path.join(os.getcwd(),testpath)] + \
+                  sys.argv[1:] + [camera_id_arg]
+            outdir = os.path.join(topdir,camera_id,scene)
+            outpath = os.path.join(outdir,testname+"_stdout.txt")
+            errpath = os.path.join(outdir,testname+"_stderr.txt")
+            t0 = time.time()
+            with open(outpath,"w") as fout, open(errpath,"w") as ferr:
+                retcode = subprocess.call(cmd,stderr=ferr,stdout=fout,cwd=outdir)
+            t1 = time.time()
+
+            if retcode == 0:
+                retstr = "PASS "
+                numpass += 1
+            elif retcode == SKIP_RET_CODE:
+                retstr = "SKIP "
+                numskip += 1
+            elif retcode != 0 and testname in NOT_YET_MANDATED[scene]:
+                retstr = "FAIL*"
+                numnotmandatedfail += 1
+            else:
+                retstr = "FAIL "
+                numfail += 1
+
+            msg = "%s %s/%s [%.1fs]" % (retstr, scene, testname, t1-t0)
+            print msg
+            summary += msg + "\n"
+            if retcode != 0 and retcode != SKIP_RET_CODE:
+                # Dump the stderr if the test fails
+                with open (errpath, "r") as error_file:
+                    errors = error_file.read()
+                    summary += errors + "\n"
+
+        if numskip > 0:
+            skipstr = ", %d test%s skipped" % (numskip, "s" if numskip > 1 else "")
         else:
-            retstr = "FAIL "
-            numfail += 1
+            skipstr = ""
 
-        print "%s %s/%s [%.1fs]" % (retstr, scene, testname, t1-t0)
+        test_result = "\n%d / %d tests passed (%.1f%%)%s" % (
+                numpass + numnotmandatedfail, len(tests) - numskip,
+                100.0 * float(numpass + numnotmandatedfail) / (len(tests) - numskip)
+                    if len(tests) != numskip else 100.0,
+                skipstr)
+        print test_result
+        summary += test_result + "\n"
 
-    if numskip > 0:
-        skipstr = ", %d test%s skipped" % (numskip, "s" if numskip > 1 else "")
-    else:
-        skipstr = ""
+        if numnotmandatedfail > 0:
+            msg = "(*) tests are not yet mandated"
+            print msg
+            summary += msg + "\n"
 
-    print "\n%d / %d tests passed (%.1f%%)%s" % (
-            numpass + numnotmandatedfail, len(tests) - numskip,
-            100.0 * float(numpass + numnotmandatedfail) / (len(tests) - numskip)
-                if len(tests) != numskip else 100.0,
-            skipstr)
+        result = numfail == 0
+        print "Reporting ITS result to CtsVerifier"
+        summary_path = os.path.join(topdir, camera_id, "summary.txt")
+        with open(summary_path, "w") as f:
+            f.write(summary)
+        its.device.report_result(camera_id, result, summary_path)
 
-    if numnotmandatedfail > 0:
-        print "(*) tests are not yet mandated"
-
-    its.device.report_result(camera_id, numfail == 0)
+    print "ITS tests finished. Please go back to CtsVerifier and proceed"
 
 if __name__ == '__main__':
     main()
-
diff --git a/apps/CameraITS/tools/validate_scene.py b/apps/CameraITS/tools/validate_scene.py
new file mode 100644
index 0000000..e1e89f2
--- /dev/null
+++ b/apps/CameraITS/tools/validate_scene.py
@@ -0,0 +1,60 @@
+# 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 sys
+import its.device
+import its.objects
+import its.image
+
+def main():
+    """capture a yuv image and save it to argv[1]
+    """
+    camera_id = -1
+    out_path = ""
+    for s in sys.argv[1:]:
+        if s[:7] == "camera=" and len(s) > 7:
+            camera_id = s[7:]
+        elif s[:4] == "out=" and len(s) > 4:
+            out_path = s[4:]
+
+    if camera_id == -1:
+        print "Error: need to specify which camera to use"
+        assert(False)
+
+    with its.device.ItsSession() as cam:
+        raw_input("Press Enter after placing camera " + camera_id +
+                " to frame the test scene")
+        # Converge 3A prior to capture.
+        cam.do_3a(do_af=True, lock_ae=True, lock_awb=True)
+        props = cam.get_camera_properties()
+        req = its.objects.fastest_auto_capture_request(props)
+        req["android.control.awbLock"] = True
+        req["android.control.aeLock"] = True
+        while True:
+            print "Capture an image to check the test scene"
+            cap = cam.do_capture(req)
+            img = its.image.convert_capture_to_rgb_image(cap)
+            if out_path != "":
+                its.image.write_image(img, out_path)
+            print "Please check scene setup in", out_path
+            choice = raw_input(
+                "Is the image okay for ITS scene1? (Y/N)").lower()
+            if choice == "y":
+                break
+            else:
+                raw_input("Press Enter after placing camera " + camera_id +
+                          " to frame the test scene")
+
+if __name__ == '__main__':
+    main()
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 6c480cf..55b4f8d 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -358,8 +358,7 @@
                        android:value="android.hardware.bluetooth_le"/>
         </activity -->
 
-        <!-- TODO: Enable when test quality issues listed in b/18282549 is resolved -->
-        <!-- activity android:name=".bluetooth.BleScannerTestActivity"
+        <activity android:name=".bluetooth.BleScannerTestActivity"
                 android:label="@string/ble_scanner_test_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
@@ -370,7 +369,7 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
             <meta-data android:name="test_required_features"
                        android:value="android.hardware.bluetooth_le"/>
-        </activity -->
+        </activity>
 
         <activity android:name=".bluetooth.BleScannerPowerLevelActivity"
                 android:label="@string/ble_power_level_name"
@@ -394,8 +393,7 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BleScannerTestActivity" />
         </activity>
 
-        <!-- TODO: Enable when test quality issues listed in b/18282549 is resolved -->
-        <!-- activity android:name=".bluetooth.BleAdvertiserTestActivity"
+        <activity android:name=".bluetooth.BleAdvertiserTestActivity"
                 android:label="@string/ble_advertiser_test_name"
                 android:configChanges="keyboardHidden|orientation|screenSize">
             <intent-filter>
@@ -406,7 +404,7 @@
             <meta-data android:name="test_parent" android:value="com.android.cts.verifier.bluetooth.BluetoothTestActivity" />
             <meta-data android:name="test_required_features"
                        android:value="android.hardware.bluetooth_le"/>
-         </activity -->
+         </activity>
 
         <activity android:name=".bluetooth.BleAdvertiserPowerLevelActivity"
                 android:label="@string/ble_power_level_name"
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index bf07e8f..a19bcec 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -787,7 +787,7 @@
         \n\n3. Setup the test scene described in the CameraITS README file, and aim the camera
         at it.
         \n\n4. Run the full ITS test suite on all possible camera Ids.
-        (cd CameraITS; python tools/run_all_tests.py camera=[cameraId]).  Once all
+        (cd CameraITS; python tools/run_all_tests.py).  Once all
         of the tests have been run, the \'PASS\' button will be enabled if all of the tests have
         succeeded.  Please note that these tests can take 20+ minutes to run.
     </string>
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
index 281b2e8..b4a6416 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
@@ -89,8 +89,11 @@
     public static final byte MANUFACTURER_TEST_ID = (byte)0x07;
     public static final byte[] PRIVACY_MAC_DATA = new byte[]{3, 1, 4};
     public static final byte[] PRIVACY_RESPONSE = new byte[]{9, 2, 6};
-    public static final byte[] POWER_LEVEL_DATA = new byte[]{1, 5, 0};
-    public static final byte[] POWER_LEVEL_MASK = new byte[]{1, 1, 0};
+    public static final byte[] POWER_LEVEL_DATA = new byte[]{1, 5, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // 15 bytes
+    public static final byte[] POWER_LEVEL_MASK = new byte[]{1, 1, 0, 0, 0,
+        0, 0, 0, 0, 0, 0, 0, 0, 0, 0};  // 15 bytes
+    public static final int POWER_LEVEL_DATA_LENGTH = 15;
     public static final byte[] SCANNABLE_DATA = new byte[]{5, 3, 5};
     public static final byte[] UNSCANNABLE_DATA = new byte[]{8, 9, 7};
 
@@ -221,8 +224,26 @@
                 break;
             case COMMAND_START_POWER_LEVEL:
                 for (int t : mPowerLevel) {
-                    AdvertiseData d =
-                            generateAdvertiseData(POWER_LEVEL_UUID, new byte[]{1, 5, (byte)t});
+                    // Service data:
+                    //    field overhead = 2 bytes
+                    //    uuid = 2 bytes
+                    //    data = 15 bytes
+                    // Manufacturer data:
+                    //    field overhead = 2 bytes
+                    //    Specific data length = 2 bytes
+                    //    data length = 2 bytes
+                    // Include power level:
+                    //    field overhead = 2 bytes
+                    //    1 byte
+                    // Connectable flag: 3 bytes (0 byte for Android 5.1+)
+                    // SUM = 31 bytes
+                    byte[] dataBytes = new byte[POWER_LEVEL_DATA_LENGTH];
+                    dataBytes[0] = 0x01;
+                    dataBytes[1] = 0x05;
+                    for (int i = 2; i < POWER_LEVEL_DATA_LENGTH; i++) {
+                        dataBytes[i] = (byte)t;
+                    }
+                    AdvertiseData d = generateAdvertiseData(POWER_LEVEL_UUID, dataBytes);
                     AdvertiseSettings settings = generateSetting(t);
                     mAdvertiser.startAdvertising(settings, d, mPowerCallback.get(t));
                 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java
index 637ef71..64c50bc 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserTestActivity.java
@@ -20,8 +20,12 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
+import android.bluetooth.BluetoothAdapter;
 import android.os.Bundle;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class BleAdvertiserTestActivity extends PassFailButtons.TestListActivity {
 
     @Override
@@ -31,6 +35,14 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.ble_advertiser_test_name, R.string.ble_advertiser_test_info, -1);
 
-        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        List<String> disabledTest = new ArrayList<String>();
+        if (adapter == null || !adapter.isOffloadedFilteringSupported()) {
+            disabledTest.add(
+                    "com.android.cts.verifier.bluetooth.BleAdvertiserHardwareScanFilterActivity.");
+        }
+
+        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+                disabledTest.toArray(new String[disabledTest.size()])));
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
index d3d96ac..eb71164 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerService.java
@@ -170,7 +170,7 @@
             if (serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID)) != null) {
                 byte[] data =
                         serviceData.get(new ParcelUuid(BleAdvertiserService.POWER_LEVEL_UUID));
-                if (data.length == 3) {
+                if (data.length == BleAdvertiserService.POWER_LEVEL_DATA.length) {
                     Intent powerIntent = new Intent(BLE_POWER_LEVEL);
                     powerIntent.putExtra(EXTRA_MAC_ADDRESS, result.getDevice().getAddress());
                     powerIntent.putExtra(EXTRA_POWER_LEVEL, record.getTxPowerLevel());
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java
index 1f54917..52933e0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleScannerTestActivity.java
@@ -20,8 +20,12 @@
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
+import android.bluetooth.BluetoothAdapter;
 import android.os.Bundle;
 
+import java.util.ArrayList;
+import java.util.List;
+
 public class BleScannerTestActivity extends PassFailButtons.TestListActivity {
 
     @Override
@@ -31,6 +35,14 @@
         setPassFailButtonClickListeners();
         setInfoResources(R.string.ble_scanner_test_name,
                          R.string.ble_scanner_test_info, -1);
-        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName()));
+        BluetoothAdapter adapter = BluetoothAdapter.getDefaultAdapter();
+        List<String> disabledTest = new ArrayList<String>();
+        if (adapter == null || !adapter.isOffloadedFilteringSupported()) {
+            disabledTest.add(
+                    "com.android.cts.verifier.bluetooth.BleScannerHardwareScanFilterActivity");
+        }
+
+        setTestListAdapter(new ManifestTestListAdapter(this, getClass().getName(),
+                disabledTest.toArray(new String[disabledTest.size()])));
     }
 }
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
index a305cd2..0fda75b 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsService.java
@@ -544,6 +544,8 @@
                     doCapture(cmdObj);
                 } else if ("doVibrate".equals(cmdObj.getString("cmdName"))) {
                     doVibrate(cmdObj);
+                } else if ("getCameraIds".equals(cmdObj.getString("cmdName"))) {
+                    doGetCameraIds();
                 } else {
                     throw new ItsException("Unknown command: " + cmd);
                 }
@@ -729,6 +731,38 @@
         mSocketRunnableObj.sendResponse(mCameraCharacteristics);
     }
 
+    private void doGetCameraIds() throws ItsException {
+        String[] devices;
+        try {
+            devices = mCameraManager.getCameraIdList();
+            if (devices == null || devices.length == 0) {
+                throw new ItsException("No camera devices");
+            }
+        } catch (CameraAccessException e) {
+            throw new ItsException("Failed to get device ID list", e);
+        }
+
+        try {
+            JSONObject obj = new JSONObject();
+            JSONArray array = new JSONArray();
+            for (String id : devices) {
+                CameraCharacteristics characteristics = mCameraManager.getCameraCharacteristics(id);
+                // Only supply camera Id for non-legacy cameras since legacy camera does not
+                // support ITS
+                if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) !=
+                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
+                    array.put(id);
+                }
+            }
+            obj.put("cameraIdArray", array);
+            mSocketRunnableObj.sendResponse("cameraIds", obj);
+        } catch (org.json.JSONException e) {
+            throw new ItsException("JSON error: ", e);
+        } catch (android.hardware.camera2.CameraAccessException e) {
+            throw new ItsException("Access error: ", e);
+        }
+    }
+
     private void prepareCaptureReader(int[] widths, int[] heights, int formats[], int numSurfaces) {
         if (mCaptureReaders != null) {
             for (int i = 0; i < mCaptureReaders.length; i++) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
index 12b9bfc..17df106 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/its/ItsTestActivity.java
@@ -27,9 +27,19 @@
 import android.os.Bundle;
 import android.util.Log;
 import android.widget.Toast;
-import java.util.HashSet;
-import java.util.Arrays;
 
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.HashMap;
+import java.util.Arrays;
+import java.util.List;
+import java.io.BufferedReader;
+import java.io.FileReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+
+import com.android.compatibility.common.util.ResultType;
+import com.android.compatibility.common.util.ResultUnit;
 import com.android.cts.verifier.PassFailButtons;
 import com.android.cts.verifier.R;
 
@@ -41,7 +51,9 @@
  */
 public class ItsTestActivity extends PassFailButtons.Activity {
     private static final String TAG = "ItsTestActivity";
+    private static final String EXTRA_CAMERA_ID = "camera.its.extra.CAMERA_ID";
     private static final String EXTRA_SUCCESS = "camera.its.extra.SUCCESS";
+    private static final String EXTRA_SUMMARY = "camera.its.extra.SUMMARY";
     private static final String ACTION_ITS_RESULT =
             "com.android.cts.verifier.camera.its.ACTION_ITS_RESULT";
 
@@ -50,20 +62,32 @@
         public void onReceive(Context context, Intent intent) {
             Log.i(TAG, "Received result for Camera ITS tests");
             if (ACTION_ITS_RESULT.equals(intent.getAction())) {
+                String cameraId = intent.getStringExtra(EXTRA_CAMERA_ID);
                 String result = intent.getStringExtra(EXTRA_SUCCESS);
-                String[] parts = result.split("=");
-                if (parts.length != 2) {
-                    Toast.makeText(ItsTestActivity.this,
-                            "Received unknown ITS result string: " + result,
-                            Toast.LENGTH_SHORT).show();
+                String summaryPath = intent.getStringExtra(EXTRA_SUMMARY);
+                if (!mNonLegacyCameraIds.contains(cameraId)) {
+                    Log.e(TAG, "Unknown camera id " + cameraId + " reported to ITS");
+                    return;
                 }
-                String cameraId = parts[0];
-                boolean pass = parts[1].equals("True");
+
+                Log.i(TAG, "ITS summary path is: " + summaryPath);
+                mSummaryMap.put(cameraId, summaryPath);
+                // Create summary report
+                if (mSummaryMap.keySet().containsAll(mNonLegacyCameraIds)) {
+                    StringBuilder summary = new StringBuilder();
+                    for (String id : mNonLegacyCameraIds) {
+                        String path = mSummaryMap.get(id);
+                        appendFileContentToSummary(summary, path);
+                    }
+                    ItsTestActivity.this.getReportLog().setSummary(
+                            summary.toString(), 1.0, ResultType.NEUTRAL, ResultUnit.NONE);
+                }
+                boolean pass = result.equals("True");
                 if(pass) {
                     Log.i(TAG, "Received Camera " + cameraId + " ITS SUCCESS from host.");
                     mITSPassedCameraIds.add(cameraId);
-                    if (mCameraIds != null &&
-                            mITSPassedCameraIds.containsAll(Arrays.asList(mCameraIds))) {
+                    if (mNonLegacyCameraIds != null && mNonLegacyCameraIds.size() != 0 &&
+                            mITSPassedCameraIds.containsAll(mNonLegacyCameraIds)) {
                         ItsTestActivity.this.showToast(R.string.its_test_passed);
                         ItsTestActivity.this.getPassButton().setEnabled(true);
                     }
@@ -73,11 +97,40 @@
                 }
             }
         }
+
+        private void appendFileContentToSummary(StringBuilder summary, String path) {
+            BufferedReader reader = null;
+            try {
+                reader = new BufferedReader(new FileReader(path));
+                String line = null;
+                do {
+                    line = reader.readLine();
+                    if (line != null) {
+                        summary.append(line);
+                    }
+                } while (line != null);
+            } catch (FileNotFoundException e) {
+                Log.e(TAG, "Cannot find ITS summary file at " + path);
+                summary.append("Cannot find ITS summary file at " + path);
+            } catch (IOException e) {
+                Log.e(TAG, "IO exception when trying to read " + path);
+                summary.append("IO exception when trying to read " + path);
+            } finally {
+                if (reader != null) {
+                    try {
+                        reader.close();
+                    } catch (IOException e) {
+                    }
+                }
+            }
+        }
     }
 
     private final SuccessReceiver mSuccessReceiver = new SuccessReceiver();
     private final HashSet<String> mITSPassedCameraIds = new HashSet<>();
-    private String[] mCameraIds = null;
+    // map camera id to ITS summary report path
+    private final HashMap<String, String> mSummaryMap = new HashMap<>();
+    ArrayList<String> mNonLegacyCameraIds = null;
 
     @Override
     protected void onCreate(Bundle savedInstanceState) {
@@ -96,25 +149,27 @@
             showToast(R.string.no_camera_manager);
         } else {
             try {
-                mCameraIds = manager.getCameraIdList();
+                String[] cameraIds = manager.getCameraIdList();
+                mNonLegacyCameraIds = new ArrayList<String>();
                 boolean allCamerasAreLegacy = true;
-                for (String id : mCameraIds) {
+                for (String id : cameraIds) {
                     CameraCharacteristics characteristics = manager.getCameraCharacteristics(id);
                     if (characteristics.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL)
                             != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_LEGACY) {
+                        mNonLegacyCameraIds.add(id);
                         allCamerasAreLegacy = false;
-                        break;
                     }
                 }
                 if (allCamerasAreLegacy) {
                     showToast(R.string.all_legacy_devices);
-                    getPassButton().setEnabled(false);
+                    getPassButton().setEnabled(true);
                 }
             } catch (CameraAccessException e) {
                 Toast.makeText(ItsTestActivity.this,
                         "Received error from camera service while checking device capabilities: "
                                 + e, Toast.LENGTH_SHORT).show();
             }
+            Log.d(TAG, "register ITS result receiver");
             IntentFilter filter = new IntentFilter(ACTION_ITS_RESULT);
             registerReceiver(mSuccessReceiver, filter);
         }
@@ -123,6 +178,7 @@
     @Override
     protected void onPause() {
         super.onPause();
+        Log.d(TAG, "unregister ITS result receiver");
         unregisterReceiver(mSuccessReceiver);
     }
 
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index e93121e..7da4228 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -88,6 +88,14 @@
         assertFalse(listUsers().contains(mUserId));
     }
 
+    public void testMaxUsersStrictlyMoreThanOne() throws Exception {
+        if (hasDeviceFeature("android.software.managed_users")) {
+            assertTrue("Device must support more than 1 user "
+                    + "if android.software.managed_users feature is available",
+            getMaxNumberOfUsersSupported() > 1);
+        }
+    }
+
     public void testCrossProfileIntentFilters() throws Exception {
         if (!mHasFeature) {
             return;
diff --git a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
index 57945f8..080c08e 100644
--- a/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
+++ b/tests/core/runner/src/com/android/cts/runner/CtsTestRunListener.java
@@ -58,7 +58,7 @@
 
     @Override
     public void testRunStarted(Description description) throws Exception {
-        mEnvironment = new TestEnvironment(getInstrumentation().getContext());
+        mEnvironment = new TestEnvironment(getInstrumentation().getTargetContext());
 
         // We might want to move this to /sdcard, if is is mounted/writable.
         File cacheDir = getInstrumentation().getTargetContext().getCacheDir();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
index 097d409..bb5527f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CaptureRequestTest.java
@@ -2069,6 +2069,16 @@
      */
     private void verifyFpsNotSlowDown(CaptureRequest.Builder requestBuilder,
             int numFramesVerified)  throws Exception {
+        boolean frameDurationAvailable = true;
+        // Allow a few frames for AE to settle on target FPS range
+        final int NUM_FRAME_TO_SKIP = 6;
+        float frameDurationErrorMargin = FRAME_DURATION_ERROR_MARGIN;
+        if (!mStaticInfo.areKeysAvailable(CaptureResult.SENSOR_FRAME_DURATION)) {
+            frameDurationAvailable = false;
+            // Allow a larger error margin (1.5%) for timestamps
+            frameDurationErrorMargin = 0.015f;
+        }
+
         Range<Integer>[] fpsRanges = mStaticInfo.getAeAvailableTargetFpsRangesChecked();
         boolean antiBandingOffIsSupported = mStaticInfo.isAntiBandingOffModeSupported();
         Range<Integer> fpsRange;
@@ -2109,20 +2119,33 @@
             resultListener = new SimpleCaptureCallback();
             startPreview(requestBuilder, previewSz, resultListener);
             waitForSettingsApplied(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
+            // Wait several more frames for AE to settle on target FPS range
+            waitForNumResults(resultListener, NUM_FRAME_TO_SKIP);
 
             long[] frameDurationRange = new long[]{
                     (long) (1e9 / fpsRange.getUpper()), (long) (1e9 / fpsRange.getLower())};
+            long captureTime = 0, prevCaptureTime = 0;
             for (int j = 0; j < numFramesVerified; j++) {
+                long frameDuration = frameDurationRange[0];
                 CaptureResult result =
                         resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
                 validatePipelineDepth(result);
-                long frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+                if (frameDurationAvailable) {
+                    frameDuration = getValueNotNull(result, CaptureResult.SENSOR_FRAME_DURATION);
+                } else {
+                    // if frame duration is not available, check timestamp instead
+                    captureTime = getValueNotNull(result, CaptureResult.SENSOR_TIMESTAMP);
+                    if (j > 0) {
+                        frameDuration = captureTime - prevCaptureTime;
+                    }
+                    prevCaptureTime = captureTime;
+                }
                 mCollector.expectInRange(
                         "Frame duration must be in the range of " +
                                 Arrays.toString(frameDurationRange),
                         frameDuration,
-                        (long) (frameDurationRange[0] * (1 - FRAME_DURATION_ERROR_MARGIN)),
-                        (long) (frameDurationRange[1] * (1 + FRAME_DURATION_ERROR_MARGIN)));
+                        (long) (frameDurationRange[0] * (1 - frameDurationErrorMargin)),
+                        (long) (frameDurationRange[1] * (1 + frameDurationErrorMargin)));
             }
         }
 
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 9b3a5e4..6567be2 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -118,7 +118,7 @@
     @Override
     public void onFlushCompleted(Sensor sensor) {
         CountDownLatch latch = mFlushLatch;
-        mFlushLatch = new CountDownLatch(1);
+        mFlushLatch = null;
         if(latch != null) {
             latch.countDown();
         }
diff --git a/tests/tests/media/res/raw/video_640x360_webm_vp9_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_640x360_webm_vp9_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
new file mode 100644
index 0000000..3f02f9a
--- /dev/null
+++ b/tests/tests/media/res/raw/video_640x360_webm_vp9_1600kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
Binary files differ
diff --git a/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz.webm b/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
deleted file mode 100644
index 418cc91..0000000
--- a/tests/tests/media/res/raw/video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz.webm
+++ /dev/null
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/DecoderTest.java b/tests/tests/media/src/android/media/cts/DecoderTest.java
index 5371f67..bf5aa4a 100644
--- a/tests/tests/media/src/android/media/cts/DecoderTest.java
+++ b/tests/tests/media/src/android/media/cts/DecoderTest.java
@@ -996,7 +996,7 @@
     }
 
     public void testVP9Decode640x360() throws Exception {
-        testDecode(R.raw.video_640x360_webm_vp9_1638kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
+        testDecode(R.raw.video_640x360_webm_vp9_1600kbps_30fps_vorbis_stereo_128kbps_48000hz, 249);
     }
 
     public void testVP9Decode30fps1280x720Tv() throws Exception {
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
index 12fcd30..08276fa 100755
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
@@ -87,7 +87,8 @@
     // Encoder parameters.  We use the same width/height as the virtual display.
     private static final String MIME_TYPE = MediaFormat.MIMETYPE_VIDEO_AVC;
     private static int sFrameRate = 15;               // 15fps
-    private static final int IFRAME_INTERVAL = 10;    // 10 seconds between I-frames
+    // 100 days between I-frames
+    private static final int IFRAME_INTERVAL = 60 * 60 * 24 * 100;
     private static int sBitRate = 6000000;            // 6Mbps
 
     // Colors to test (RGB).  These must convert cleanly to and from BT.601 YUV.