merge in mnc-dr-release history after reset to mnc-dr-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index f4cd7d7..8e0d83d 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -19,6 +19,8 @@
     CtsDocumentClient \
     CtsExternalStorageApp \
     CtsInstrumentationAppDiffCert \
+    CtsUsePermissionApp \
+    CtsUsePermissionAppCompat \
     CtsPermissionDeclareApp \
     CtsPermissionDeclareAppCompat \
     CtsReadExternalStorageApp \
@@ -97,6 +99,7 @@
     CtsUsbSerialTestApp \
     CtsVoiceInteractionService \
     CtsVoiceInteractionApp \
+    CtsVoiceSettingsService \
     $(cts_security_apps_list) \
     $(cts_security_keysets_list)
 
@@ -125,6 +128,7 @@
     CtsAccessibilityServiceTestCases \
     CtsAccessibilityTestCases \
     CtsAdminTestCases \
+    CtsAlarmClockTestCases \
     CtsAnimationTestCases \
     CtsAppTestCases \
     CtsAppWidgetTestCases \
@@ -186,6 +190,7 @@
     CtsUtilTestCases \
     CtsViewTestCases \
     CtsVoiceInteractionTestCases \
+    CtsVoiceSettingsTestCases \
     CtsWebkitTestCases \
     CtsWidgetTestCases
 
diff --git a/apps/CameraITS/pymodules/its/caps.py b/apps/CameraITS/pymodules/its/caps.py
index b6d398f..95f19d9 100644
--- a/apps/CameraITS/pymodules/its/caps.py
+++ b/apps/CameraITS/pymodules/its/caps.py
@@ -307,6 +307,35 @@
     return props.has_key("android.request.availableCapabilities") and \
            4 in props["android.request.availableCapabilities"]
 
+def noise_reduction_mode(props, mode):
+    """Returns whether a device supports the noise reduction mode.
+
+    Args:
+        props: Camera properties objects.
+        mode: Integer, indicating the noise reduction mode to check for
+              availability.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key(
+            "android.noiseReduction.availableNoiseReductionModes") and mode \
+            in props["android.noiseReduction.availableNoiseReductionModes"];
+
+def edge_mode(props, mode):
+    """Returns whether a device supports the edge mode.
+
+    Args:
+        props: Camera properties objects.
+        mode: Integer, indicating the edge mode to check for availability.
+
+    Returns:
+        Boolean.
+    """
+    return props.has_key(
+            "android.edge.availableEdgeModes") and mode \
+            in props["android.edge.availableEdgeModes"];
+
 class __UnitTest(unittest.TestCase):
     """Run a suite of unit tests on this module.
     """
diff --git a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
index f5176a7..27da8fc 100644
--- a/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_param_noise_reduction.py
@@ -45,7 +45,8 @@
     with its.device.ItsSession() as cam:
         props = cam.get_camera_properties()
         its.caps.skip_unless(its.caps.compute_target_exposure(props) and
-                             its.caps.per_frame_control(props))
+                             its.caps.per_frame_control(props) and
+                             its.caps.noise_reduction_mode(props, 0))
 
         # NR mode 0 with low gain
         e, s = its.target.get_target_exposure_combos(cam)["minSensitivity"]
@@ -62,37 +63,51 @@
             ref_variance.append(its.image.compute_image_variances(tile)[0])
         print "Ref variances:", ref_variance
 
-        for i in range(3):
-            # NR modes 0, 1, 2 with high gain
+        # NR modes 0, 1, 2, 3, 4 with high gain
+        for mode in range(5):
+            # Skip unavailable modes
+            if not its.caps.noise_reduction_mode(props, mode):
+                nr_modes_reported.append(mode)
+                for channel in range(3):
+                    variances[channel].append(0)
+                continue;
+
             e, s = its.target.get_target_exposure_combos(cam)["maxSensitivity"]
             req = its.objects.manual_capture_request(s, e)
-            req["android.noiseReduction.mode"] = i
+            req["android.noiseReduction.mode"] = mode
             cap = cam.do_capture(req)
             nr_modes_reported.append(
                     cap["metadata"]["android.noiseReduction.mode"])
             its.image.write_image(
                     its.image.convert_capture_to_rgb_image(cap),
-                    "%s_high_gain_nr=%d.jpg" % (NAME, i))
+                    "%s_high_gain_nr=%d.jpg" % (NAME, mode))
             planes = its.image.convert_capture_to_planes(cap)
             for j in range(3):
                 img = planes[j]
                 tile = its.image.get_image_patch(img, 0.45, 0.45, 0.1, 0.1)
                 variance = its.image.compute_image_variances(tile)[0]
                 variances[j].append(variance / ref_variance[j])
-        print "Variances with NR mode [0,1,2]:", variances
+        print "Variances with NR mode [0,1,2,3,4]:", variances
 
     # Draw a plot.
     for j in range(3):
-        pylab.plot(range(3), variances[j], "rgb"[j])
+        pylab.plot(range(5), variances[j], "rgb"[j])
     matplotlib.pyplot.savefig("%s_plot_variances.png" % (NAME))
 
-    assert(nr_modes_reported == [0,1,2])
+    assert(nr_modes_reported == [0,1,2,3,4])
 
     # Check that the variance of the NR=0 image is higher than for the
     # NR=1 and NR=2 images.
     for j in range(3):
-        for i in range(1,3):
-            assert(variances[j][i] < variances[j][0])
+        for mode in [1,2]:
+            if its.caps.noise_reduction_mode(props, mode):
+                assert(variances[j][mode] < variances[j][0])
+                # Variance of MINIMAL should be higher than for FAST, HQ
+                if its.caps.noise_reduction_mode(props, 3):
+                    assert(variances[j][mode] < variances[j][3])
+                # Variance of ZSL should be higher than for FAST, HQ
+                if its.caps.noise_reduction_mode(props, 4):
+                    assert(variances[j][mode] < variances[j][4])
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
index e9240ba..c2657c7 100644
--- a/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
+++ b/apps/CameraITS/tests/scene1/test_reprocess_noise_reduction.py
@@ -42,9 +42,13 @@
 
         its.caps.skip_unless(its.caps.compute_target_exposure(props) and
                              its.caps.per_frame_control(props) and
+                             its.caps.noise_reduction_mode(props, 0) and
                              (its.caps.yuv_reprocess(props) or
                               its.caps.private_reprocess(props)))
 
+        # If reprocessing is supported, ZSL NR mode must be avaiable.
+        assert(its.caps.noise_reduction_mode(props, 4))
+
         reprocess_formats = []
         if (its.caps.yuv_reprocess(props)):
             reprocess_formats.append("yuv")
@@ -73,8 +77,14 @@
             ref_variance = its.image.compute_image_variances(tile)
             print "Ref variances:", ref_variance
 
-            for nr_mode in range(3):
-                # NR modes 0, 1, 2 with high gain
+            for nr_mode in range(5):
+                # Skipp unavailable modes
+                if not its.caps.noise_reduction_mode(props, nr_mode):
+                    nr_modes_reported.append(nr_mode)
+                    variances.append(0)
+                    continue
+
+                # NR modes with high gain
                 e, s = its.target.get_target_exposure_combos(cam) \
                     ["maxSensitivity"]
                 req = its.objects.manual_capture_request(s, e)
@@ -91,21 +101,31 @@
                 variance = its.image.compute_image_variances(tile)
                 variances.append(
                     [variance[chan] / ref_variance[chan] for chan in range(3)])
-            print "Variances with NR mode [0,1,2]:", variances
+            print "Variances with NR mode [0,1,2,3,4]:", variances
 
             # Draw a plot.
-            for nr_mode in range(3):
-                pylab.plot(range(3), variances[nr_mode], "rgb"[nr_mode])
+            for nr_mode in range(5):
+                if not its.caps.noise_reduction_mode(props, nr_mode):
+                    continue
+                pylab.plot(range(3), variances[nr_mode], "rgbcm"[nr_mode])
             matplotlib.pyplot.savefig("%s_plot_%s_variances.png" %
                                       (NAME, reprocess_format))
 
-            assert(nr_modes_reported == [0,1,2])
+            assert(nr_modes_reported == [0,1,2,3,4]
 
-            # Check that the variance of the NR=0 image is higher than for the
-            # NR=1 and NR=2 images.
-            for j in range(3):
-                for i in range(1,3):
-                    assert(variances[i][j] < variances[0][j])
+            # Check that the variances of the NR=0 and NR=3 and NR=4 images are
+            # higher than for the NR=1 and NR=2 images.
+            for channel in range(3):
+                for nr_mode in [1, 2]:
+                    if its.caps.noise_reduction_mode(props, nr_mode):
+                        assert(variances[nr_mode][channel] <
+                               variances[0][channel])
+                        if its.caps.noise_reduction_mode(props, 3):
+                            assert(variances[nr_mode][channel] <
+                                   variances[3][channel])
+                        if its.caps.noise_reduction_mode(props, 4):
+                            assert(variances[nr_mode][channel] <
+                                   variances[4][channel])
 
 if __name__ == '__main__':
     main()
diff --git a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
index 1966d9e..77208e7 100644
--- a/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
+++ b/apps/CameraITS/tests/scene3/test_reprocess_edge_enhancement.py
@@ -90,9 +90,13 @@
 
         its.caps.skip_unless(its.caps.read_3a(props) and
                              its.caps.per_frame_control(props) and
+                             its.caps.edge_mode(props, 0) and
                              (its.caps.yuv_reprocess(props) or
                               its.caps.private_reprocess(props)))
 
+        # If reprocessing is supported, ZSL EE mode must be avaiable.
+        assert(its.caps.edge_mode(props, 3))
+
         reprocess_formats = []
         if (its.caps.yuv_reprocess(props)):
             reprocess_formats.append("yuv")
@@ -108,13 +112,18 @@
         # Get the sharpness for each edge mode for regular requests
         sharpness_regular = []
         edge_mode_reported_regular = []
-        for edge_mode in range(3):
+        for edge_mode in range(4):
+            # Skip unavailable modes
+            if not its.caps.edge_mode(props, edge_mode):
+                edge_mode_reported_regular.append(edge_mode)
+                sharpness_regular.append(0)
+                continue
             ret = test_edge_mode(cam, edge_mode, s, e, fd, out_surface)
             edge_mode_reported_regular.append(ret["edge_mode"])
             sharpness_regular.append(ret["sharpness"])
 
         print "Reported edge modes:", edge_mode_reported_regular
-        print "Sharpness with EE mode [0,1,2]:", sharpness_regular
+        print "Sharpness with EE mode [0,1,2,3]:", sharpness_regular
 
         # Get the sharpness for each reprocess format and edge mode for
         # reprocess requests.
@@ -125,7 +134,13 @@
             # List of sharpness
             sharpnesses = []
             edge_mode_reported = []
-            for edge_mode in range(3):
+            for edge_mode in range(4):
+                # Skip unavailable modes
+                if not its.caps.edge_mode(props, edge_mode):
+                    edge_mode_reported.append(edge_mode)
+                    sharpnesses.append(0)
+                    continue
+
                 ret = test_edge_mode(cam, edge_mode, s, e, fd, out_surface,
                     reprocess_format)
                 edge_mode_reported.append(ret["edge_mode"])
@@ -135,24 +150,31 @@
             edge_mode_reported_reprocess.append(edge_mode_reported)
 
             print "Reported edge modes:", edge_mode_reported
-            print "Sharpness with EE mode [0,1,2] for %s reprocess:" % \
+            print "Sharpness with EE mode [0,1,2,3] for %s reprocess:" % \
                 (reprocess_format) , sharpnesses
 
-        # Verify the results
-        assert(edge_mode_reported_regular == [0,1,2])
-        assert(sharpness_regular[1] >
-            sharpness_regular[0] * (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
-        assert(sharpness_regular[2] >
-            sharpness_regular[0] * (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
+        # Verify reported modes for regular results
+        assert(edge_mode_reported_regular == [0,1,2,3])
+        # Verify the images for all modes are not too blurrier than OFF.
+        for edge_mode in [1, 2, 3]:
+            if its.caps.edge_mode(props, edge_mode):
+                assert(sharpness_regular[edge_mode] >
+                    sharpness_regular[0] *
+                    (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF))
+        # Verify the image for ZSL are not too sharper than OFF
+        assert(sharpness_regular[3] <
+                sharpness_regular[0] * (1.0 + THRESHOLD_RELATIVE_SHARPNESS_DIFF))
 
-        # Verify the reprocess
+        # Verify sharpness of reprocess captures are similar to sharpness of
+        # regular captures.
         for reprocess_format in range(len(reprocess_formats)):
-            assert(edge_mode_reported_reprocess[reprocess_format] == [0,1,2])
-            for edge_mode in range(3):
-                assert(sharpnesses_reprocess[reprocess_format][edge_mode] >=
-                    (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF) *
-                    sharpnesses_reprocess[reprocess_format][0] *
-                    sharpness_regular[edge_mode] / sharpness_regular[0])
+            assert(edge_mode_reported_reprocess[reprocess_format] == [0,1,2,3])
+            for edge_mode in range(4):
+                if its.caps.edge_mode(props, edge_mode):
+                    assert(sharpnesses_reprocess[reprocess_format][edge_mode] >=
+                        (1.0 - THRESHOLD_RELATIVE_SHARPNESS_DIFF) *
+                        sharpnesses_reprocess[reprocess_format][0] *
+                        sharpness_regular[edge_mode] / sharpness_regular[0])
 
 if __name__ == '__main__':
     main()
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 b4a6416..5a60ad0 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/bluetooth/BleAdvertiserService.java
@@ -42,7 +42,7 @@
 public class BleAdvertiserService extends Service {
 
     public static final boolean DEBUG = true;
-    public static final String TAG = "BleAdvertiseService";
+    public static final String TAG = "BleAdvertiserService";
 
     public static final int COMMAND_START_ADVERTISE = 0;
     public static final int COMMAND_STOP_ADVERTISE = 1;
diff --git a/hostsidetests/appsecurity/src/com/android/cts/appsecurity/PermissionsHostTest.java b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/PermissionsHostTest.java
new file mode 100644
index 0000000..091da24
--- /dev/null
+++ b/hostsidetests/appsecurity/src/com/android/cts/appsecurity/PermissionsHostTest.java
@@ -0,0 +1,135 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.cts.appsecurity;
+
+import com.android.cts.tradefed.build.CtsBuildHelper;
+import com.android.tradefed.build.IBuildInfo;
+import com.android.tradefed.device.DeviceNotAvailableException;
+import com.android.tradefed.testtype.DeviceTestCase;
+import com.android.tradefed.testtype.IAbi;
+import com.android.tradefed.testtype.IAbiReceiver;
+import com.android.tradefed.testtype.IBuildReceiver;
+
+public class PermissionsHostTest extends DeviceTestCase implements IAbiReceiver, IBuildReceiver {
+    private static final String PKG = "com.android.cts.usepermission";
+
+    private static final String APK = "CtsUsePermissionApp.apk";
+    private static final String APK_COMPAT = "CtsUsePermissionAppCompat.apk";
+
+    private IAbi mAbi;
+    private CtsBuildHelper mCtsBuild;
+
+    @Override
+    public void setAbi(IAbi abi) {
+        mAbi = abi;
+    }
+
+    @Override
+    public void setBuild(IBuildInfo buildInfo) {
+        mCtsBuild = CtsBuildHelper.createBuildHelper(buildInfo);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+
+        assertNotNull(mAbi);
+        assertNotNull(mCtsBuild);
+
+        getDevice().uninstallPackage(PKG);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+
+        getDevice().uninstallPackage(PKG);
+    }
+
+    public void testFail() throws Exception {
+        // Sanity check that remote failure is host failure
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK), false, false));
+        try {
+            runDeviceTests(PKG, ".UsePermissionTest", "testFail");
+            fail("Expected remote failure");
+        } catch (AssertionError expected) {
+        }
+    }
+
+    public void testKill() throws Exception {
+        // Sanity check that remote kill is host failure
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK), false, false));
+        try {
+            runDeviceTests(PKG, ".UsePermissionTest", "testKill");
+            fail("Expected remote failure");
+        } catch (AssertionError expected) {
+        }
+    }
+
+    public void testDefault() throws Exception {
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK), false, false));
+        runDeviceTests(PKG, ".UsePermissionTest", "testDefault");
+    }
+
+    public void testGranted() throws Exception {
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK), false, false));
+        grantPermission(PKG, "android.permission.WRITE_EXTERNAL_STORAGE");
+        runDeviceTests(PKG, ".UsePermissionTest", "testGranted");
+    }
+
+    public void testInteractiveGrant() throws Exception {
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK), false, false));
+        runDeviceTests(PKG, ".UsePermissionTest", "testInteractiveGrant");
+    }
+
+    public void testCompatDefault() throws Exception {
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK_COMPAT), false, false));
+        runDeviceTests(PKG, ".UsePermissionCompatTest", "testCompatDefault");
+    }
+
+    public void testCompatRevoked() throws Exception {
+        assertNull(getDevice().installPackage(mCtsBuild.getTestApp(APK_COMPAT), false, false));
+        setAppOps(PKG, "android:read_external_storage", "deny");
+        setAppOps(PKG, "android:write_external_storage", "deny");
+        runDeviceTests(PKG, ".UsePermissionCompatTest", "testCompatRevoked");
+    }
+
+    private void runDeviceTests(String packageName, String testClassName, String testMethodName)
+            throws DeviceNotAvailableException {
+        Utils.runDeviceTests(getDevice(), packageName, testClassName, testMethodName);
+    }
+
+    private void grantPermission(String pkg, String permission) throws Exception {
+        assertEmpty(getDevice().executeShellCommand("pm grant " + pkg + " " + permission));
+    }
+
+    private void revokePermission(String pkg, String permission) throws Exception {
+        assertEmpty(getDevice().executeShellCommand("pm revoke " + pkg + " " + permission));
+    }
+
+    private void setAppOps(String pkg, String op, String mode) throws Exception {
+        assertEmpty(getDevice().executeShellCommand("appops set " + pkg + " " + op + " " + mode));
+    }
+
+    private static void assertEmpty(String str) {
+        if (str == null || str.length() == 0) {
+            return;
+        } else {
+            fail("Expected empty string but found " + str);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
index aa09f75..379de5f 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
@@ -351,7 +351,7 @@
         }
     }
 
-    private static void logCommand(String... cmd) throws Exception {
+    public static void logCommand(String... cmd) throws Exception {
         final Process proc = new ProcessBuilder(cmd).redirectErrorStream(true).start();
 
         final ByteArrayOutputStream buf = new ByteArrayOutputStream();
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionApp/Android.mk
new file mode 100644
index 0000000..f91d0c4
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := current
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+
+LOCAL_PACKAGE_NAME := CtsUsePermissionApp
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionApp/AndroidManifest.xml
new file mode 100644
index 0000000..253d85d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.usepermission">
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".MyActivity" />
+    </application>
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.usepermission" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp/src/com/android/cts/usepermission/MyActivity.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp/src/com/android/cts/usepermission/MyActivity.java
new file mode 100644
index 0000000..5af3886
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp/src/com/android/cts/usepermission/MyActivity.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.cts.usepermission;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.WindowManager;
+
+import java.util.concurrent.SynchronousQueue;
+import java.util.concurrent.TimeUnit;
+
+public class MyActivity extends Activity {
+    private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+
+    public static class Result {
+        public final int requestCode;
+        public final String[] permissions;
+        public final int[] grantResults;
+
+        public Result(int requestCode, String[] permissions, int[] grantResults) {
+            this.requestCode = requestCode;
+            this.permissions = permissions;
+            this.grantResults = grantResults;
+        }
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        getWindow().addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON
+                | WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD);
+    }
+
+    @Override
+    public void onRequestPermissionsResult(int requestCode, String[] permissions,
+            int[] grantResults) {
+        try {
+            mResult.offer(new Result(requestCode, permissions, grantResults), 5, TimeUnit.SECONDS);
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public Result getResult() {
+        try {
+            return mResult.take();
+        } catch (InterruptedException e) {
+            throw new RuntimeException(e);
+        }
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionApp/src/com/android/cts/usepermission/UsePermissionTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionApp/src/com/android/cts/usepermission/UsePermissionTest.java
new file mode 100644
index 0000000..d464e48
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionApp/src/com/android/cts/usepermission/UsePermissionTest.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.cts.usepermission;
+
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.logCommand;
+
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.support.test.uiautomator.UiDevice;
+import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiSelector;
+import android.test.InstrumentationTestCase;
+
+public class UsePermissionTest extends InstrumentationTestCase {
+    private static final String TAG = "UsePermissionTest";
+
+    private UiDevice mDevice;
+    private MyActivity mActivity;
+
+    public void testFail() throws Exception {
+        fail("Expected");
+    }
+
+    public void testKill() throws Exception {
+        android.os.Process.killProcess(android.os.Process.myPid());
+    }
+
+    public void testDefault() throws Exception {
+        logCommand("/system/bin/cat", "/proc/self/mountinfo");
+
+        // New permission model is denied by default
+        assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getContext()
+                .checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE));
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+        assertDirNoAccess(Environment.getExternalStorageDirectory());
+        assertDirReadWriteAccess(getInstrumentation().getContext().getExternalCacheDir());
+    }
+
+    public void testGranted() throws Exception {
+        logCommand("/system/bin/cat", "/proc/self/mountinfo");
+
+        assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE));
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+        assertDirReadWriteAccess(Environment.getExternalStorageDirectory());
+        assertDirReadWriteAccess(getInstrumentation().getContext().getExternalCacheDir());
+    }
+
+    public void testInteractiveGrant() throws Exception {
+        logCommand("/system/bin/cat", "/proc/self/mountinfo");
+
+        // Start out without permission
+        assertEquals(PackageManager.PERMISSION_DENIED, getInstrumentation().getContext()
+                .checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE));
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+        assertDirNoAccess(Environment.getExternalStorageDirectory());
+        assertDirReadWriteAccess(getInstrumentation().getContext().getExternalCacheDir());
+
+        // Go through normal grant flow
+        mDevice = UiDevice.getInstance(getInstrumentation());
+        mActivity = launchActivity(getInstrumentation().getTargetContext().getPackageName(),
+                MyActivity.class, null);
+        mDevice.waitForIdle();
+
+        mActivity.requestPermissions(new String[] {
+                android.Manifest.permission.WRITE_EXTERNAL_STORAGE }, 42);
+        mDevice.waitForIdle();
+
+        new UiObject(new UiSelector()
+                .resourceId("com.android.packageinstaller:id/permission_allow_button")).click();
+        mDevice.waitForIdle();
+
+        final MyActivity.Result result = mActivity.getResult();
+        assertEquals(42, result.requestCode);
+        assertEquals(android.Manifest.permission.WRITE_EXTERNAL_STORAGE, result.permissions[0]);
+        assertEquals(PackageManager.PERMISSION_GRANTED, result.grantResults[0]);
+
+        logCommand("/system/bin/cat", "/proc/self/mountinfo");
+
+        // We should have permission now!
+        assertEquals(PackageManager.PERMISSION_GRANTED, getInstrumentation().getContext()
+                .checkSelfPermission(android.Manifest.permission.WRITE_EXTERNAL_STORAGE));
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+        assertDirReadWriteAccess(Environment.getExternalStorageDirectory());
+        assertDirReadWriteAccess(getInstrumentation().getContext().getExternalCacheDir());
+
+        mActivity.finish();
+    }
+}
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/Android.mk b/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/Android.mk
new file mode 100644
index 0000000..70b4b0f
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/Android.mk
@@ -0,0 +1,35 @@
+#
+# Copyright (C) 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.
+#
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+LOCAL_SDK_VERSION := 21
+LOCAL_STATIC_JAVA_LIBRARIES := android-support-test ctsdeviceutil ctstestrunner ub-uiautomator
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src) \
+    ../ExternalStorageApp/src/com/android/cts/externalstorageapp/CommonExternalStorageTest.java
+
+LOCAL_PACKAGE_NAME := CtsUsePermissionAppCompat
+
+LOCAL_CERTIFICATE := cts/hostsidetests/appsecurity/certs/cts-testkey2
+
+LOCAL_PROGUARD_ENABLED := disabled
+LOCAL_DEX_PREOPT := false
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/AndroidManifest.xml
new file mode 100644
index 0000000..253d85d
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/AndroidManifest.xml
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 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.
+-->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+        package="com.android.cts.usepermission">
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name=".MyActivity" />
+    </application>
+    <instrumentation
+        android:name="android.support.test.runner.AndroidJUnitRunner"
+        android:targetPackage="com.android.cts.usepermission" />
+    <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
+</manifest>
diff --git a/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/src/com/android/cts/usepermission/UsePermissionCompatTest.java b/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/src/com/android/cts/usepermission/UsePermissionCompatTest.java
new file mode 100644
index 0000000..b30e8a5
--- /dev/null
+++ b/hostsidetests/appsecurity/test-apps/UsePermissionAppCompat/src/com/android/cts/usepermission/UsePermissionCompatTest.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package com.android.cts.usepermission;
+
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirNoAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.assertDirReadWriteAccess;
+import static com.android.cts.externalstorageapp.CommonExternalStorageTest.logCommand;
+
+import android.content.pm.PackageManager;
+import android.os.Environment;
+import android.os.Process;
+import android.test.InstrumentationTestCase;
+
+import com.android.cts.externalstorageapp.CommonExternalStorageTest;
+
+public class UsePermissionCompatTest extends InstrumentationTestCase {
+    private static final String TAG = "UsePermissionTest";
+
+    public void testCompatDefault() throws Exception {
+        logCommand("/system/bin/cat", "/proc/self/mountinfo");
+
+        // Legacy permission model is granted by default
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                getInstrumentation().getContext().checkPermission(
+                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Process.myPid(),
+                        Process.myUid()));
+        assertEquals(Environment.MEDIA_MOUNTED, Environment.getExternalStorageState());
+        assertDirReadWriteAccess(Environment.getExternalStorageDirectory());
+        assertDirReadWriteAccess(getInstrumentation().getContext().getExternalCacheDir());
+    }
+
+    public void testCompatRevoked() throws Exception {
+        CommonExternalStorageTest.logCommand("/system/bin/cat", "/proc/self/mountinfo");
+
+        // Legacy permission model appears granted, but storage looks and
+        // behaves like it's ejected
+        assertEquals(PackageManager.PERMISSION_GRANTED,
+                getInstrumentation().getContext().checkPermission(
+                        android.Manifest.permission.WRITE_EXTERNAL_STORAGE, Process.myPid(),
+                        Process.myUid()));
+        assertEquals(Environment.MEDIA_UNMOUNTED, Environment.getExternalStorageState());
+        assertDirNoAccess(Environment.getExternalStorageDirectory());
+        assertNull(getInstrumentation().getContext().getExternalCacheDir());
+    }
+}
diff --git a/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
index 286d4fd..f9daa3c 100644
--- a/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
+++ b/suite/cts/deviceTests/tvproviderperf/src/com/android/cts/tvproviderperf/TvProviderPerfTest.java
@@ -80,7 +80,7 @@
     @TimeoutReq(minutes = 8)
     public void testChannels() throws Exception {
         if (!mHasTvInputFramework) return;
-        double[] averages = new double[4];
+        double[] averages = new double[5];
 
         // Insert
         final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
@@ -138,23 +138,42 @@
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
         averages[1] = Stat.getAverage(applyBatchTimes);
 
-        // Query
+        // Query channels
         applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
             @Override
             public void run(int i) {
-                int j = 0;
                 try (Cursor cursor = mContentResolver.query(Channels.CONTENT_URI, null, null,
                         null, null)) {
                     while (cursor.moveToNext()) {
-                        ++j;
+                        // Do nothing. Just iterate all the items.
                     }
                 }
             }
         });
-        getReportLog().printArray("Elapsed time for query: ",
+        getReportLog().printArray("Elapsed time for query (channels): ",
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
         averages[2] = Stat.getAverage(applyBatchTimes);
 
+        // Query a channel
+        try (final Cursor cursor = mContentResolver.query(Channels.CONTENT_URI,
+                projection, null, null, null)) {
+            final Uri channelUri = TvContract.buildChannelUri(cursor.getLong(0));
+            applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+                @Override
+                public void run(int i) {
+                    assertTrue(cursor.moveToNext());
+                    try (Cursor c = mContentResolver.query(channelUri, null, null, null, null)) {
+                        while (c.moveToNext()) {
+                            // Do nothing. Just iterate all the items.
+                        }
+                    }
+                }
+            });
+        }
+        getReportLog().printArray("Elapsed time for query (a channel): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[3] = Stat.getAverage(applyBatchTimes);
+
         // Delete
         applyBatchTimes = MeasureTime.measure(1, new MeasureRun() {
             @Override
@@ -164,16 +183,17 @@
         });
         getReportLog().printArray("Elapsed time for delete: ",
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
-        averages[3] = Stat.getAverage(applyBatchTimes);
+        averages[4] = Stat.getAverage(applyBatchTimes);
 
-        getReportLog().printArray("Average elapsed time for (insert, update, query, delete): ",
+        getReportLog().printArray("Average elapsed time for insert, update, query (channels), "
+                + "query (a channel), delete: ",
                 averages, ResultType.LOWER_BETTER, ResultUnit.MS);
     }
 
     @TimeoutReq(minutes = 12)
     public void testPrograms() throws Exception {
         if (!mHasTvInputFramework) return;
-        double[] averages = new double[6];
+        double[] averages = new double[7];
 
         // Prepare (insert channels)
         final ArrayList<ContentProviderOperation> operations = new ArrayList<>();
@@ -262,20 +282,19 @@
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
         averages[1] = Stat.getAverage(applyBatchTimes);
 
-        // Query
+        // Query programs
         applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
             @Override
             public void run(int i) {
-                int j = 0;
                 try (Cursor cursor = mContentResolver.query(Programs.CONTENT_URI, null, null,
                         null, null)) {
                     while (cursor.moveToNext()) {
-                        ++j;
+                        // Do nothing. Just iterate all the items.
                     }
                 }
             }
         });
-        getReportLog().printArray("Elapsed time for query: ",
+        getReportLog().printArray("Elapsed time for query (programs): ",
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
         averages[2] = Stat.getAverage(applyBatchTimes);
 
@@ -284,22 +303,41 @@
             @Override
             public void run(int i) {
                 Uri channelUri = channelUris.get(i);
-                int j = 0;
                 try (Cursor cursor = mContentResolver.query(
                         TvContract.buildProgramsUriForChannel(
                                 channelUri, 0,
                                 PROGRAM_DURATION_MS * TRANSACTION_SIZE / 2),
                         null, null, null, null)) {
                     while (cursor.moveToNext()) {
-                        ++j;
+                        // Do nothing. Just iterate all the items.
                     }
                 }
             }
         });
-        getReportLog().printArray("Elapsed time for query with selection: ",
+        getReportLog().printArray("Elapsed time for query (programs with selection): ",
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
         averages[3] = Stat.getAverage(applyBatchTimes);
 
+        // Query a program
+        try (final Cursor cursor = mContentResolver.query(Programs.CONTENT_URI,
+                projection, null, null, null)) {
+            final Uri programUri = TvContract.buildProgramUri(cursor.getLong(0));
+            applyBatchTimes = MeasureTime.measure(QUERY_RUNS, new MeasureRun() {
+                @Override
+                public void run(int i) {
+                    assertTrue(cursor.moveToNext());
+                    try (Cursor c = mContentResolver.query(programUri, null, null, null, null)) {
+                        while (c.moveToNext()) {
+                            // Do nothing. Just iterate all the items.
+                        }
+                    }
+                }
+            });
+        }
+        getReportLog().printArray("Elapsed time for query (a program): ",
+                applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
+        averages[4] = Stat.getAverage(applyBatchTimes);
+
         // Delete programs
         applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
             @Override
@@ -315,7 +353,7 @@
         });
         getReportLog().printArray("Elapsed time for delete programs: ",
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
-        averages[4] = Stat.getAverage(applyBatchTimes);
+        averages[5] = Stat.getAverage(applyBatchTimes);
 
         // Delete channels
         applyBatchTimes = MeasureTime.measure(NUM_CHANNELS, new MeasureRun() {
@@ -327,10 +365,11 @@
         });
         getReportLog().printArray("Elapsed time for delete channels: ",
                 applyBatchTimes, ResultType.LOWER_BETTER, ResultUnit.MS);
-        averages[5] = Stat.getAverage(applyBatchTimes);
+        averages[6] = Stat.getAverage(applyBatchTimes);
 
-        getReportLog().printArray("Average elapsed time for (insert, update, query, "
-                + "query with selection, delete channels, delete programs): ",
+        getReportLog().printArray("Average elapsed time for insert, update, query (programs), "
+                + "query (programs with selection), query (a channel), delete (channels), "
+                + "delete (programs): ",
                 averages, ResultType.LOWER_BETTER, ResultUnit.MS);
     }
 }
diff --git a/tests/tests/alarmclock/Android.mk b/tests/tests/alarmclock/Android.mk
new file mode 100644
index 0000000..c99f953
--- /dev/null
+++ b/tests/tests/alarmclock/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner ctsdeviceutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsAlarmClockTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/alarmclock/AndroidManifest.xml b/tests/tests/alarmclock/AndroidManifest.xml
new file mode 100644
index 0000000..88bb91d
--- /dev/null
+++ b/tests/tests/alarmclock/AndroidManifest.xml
@@ -0,0 +1,41 @@
+<!--
+ * Copyright (C) 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.alarmclock.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="com.android.alarm.permission.SET_ALARM" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <activity android:name="TestActivity"
+            android:label="The Target Activity for alarmClock Intents Test">
+            <intent-filter>
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.alarmclock.cts"
+                     android:label="CTS tests of android.alarmclock">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockIntentsTest.java b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockIntentsTest.java
new file mode 100644
index 0000000..33ac546
--- /dev/null
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/AlarmClockIntentsTest.java
@@ -0,0 +1,151 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.alarmclock.cts;
+
+import android.app.AlarmManager;
+import android.app.AlarmManager.AlarmClockInfo;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.provider.AlarmClock;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import java.util.Calendar;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class AlarmClockIntentsTest extends ActivityInstrumentationTestCase2<TestActivity> {
+    static final String TAG = "AlarmClockIntentsTest";
+    static final int ALARM_TEST_WINDOW_MINS = 2;
+    private static final int TIMEOUT_MS = 20 * 1000;
+
+    private TestActivity mActivity;
+    private AlarmManager mAlarmManager;
+    private NextAlarmReceiver mReceiver = new NextAlarmReceiver();
+    private final CountDownLatch mLatch = new CountDownLatch(1);
+    private Context mContext;
+
+    public AlarmClockIntentsTest() {
+      super(TestActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getTargetContext();
+        mActivity = getActivity();
+        mAlarmManager = (AlarmManager) mActivity.getSystemService(Context.ALARM_SERVICE);
+        IntentFilter filter = new IntentFilter(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED);
+        filter.addAction(AlarmClock.ACTION_SET_ALARM);
+        mContext.registerReceiver(mReceiver, filter);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mContext.unregisterReceiver(mReceiver);
+        super.tearDown();
+    }
+
+    public void testSetAlarm() throws Exception {
+        // set an alarm for ALARM_TEST_WINDOW millisec from now.
+        // Assume the next alarm is NOT within the next ALARM_TEST_WINDOW millisec
+        // TODO: fix this assumption
+        AlarmTime expected = getAlarmTime();
+
+        // set the alarm
+        assertNotNull(mActivity);
+        assertTrue(mActivity.setAlarm(expected));
+
+        // wait for the alarm to be set
+        if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Failed to receive broadcast in " + (TIMEOUT_MS / 1000) + "sec");
+        }
+
+        // verify the next alarm
+        assertTrue(isNextAlarmSameAs(expected));
+    }
+
+    public void testDismissAlarm() throws Exception {
+        assertTrue(mActivity.cancelAlarm(getAlarmTime()));
+    }
+
+    public void testSnoozeAlarm() throws Exception {
+        assertTrue(mActivity.snoozeAlarm());
+    }
+
+    private AlarmTime getAlarmTime() {
+        Calendar now = Calendar.getInstance();
+        now.add(Calendar.MINUTE, ALARM_TEST_WINDOW_MINS);
+        long nextAlarmTime = now.getTimeInMillis();
+        return new AlarmTime(nextAlarmTime);
+    }
+
+    private boolean isNextAlarmSameAs(AlarmTime expected) {
+        AlarmClockInfo alarmInfo = mAlarmManager.getNextAlarmClock();
+        if (alarmInfo == null) {
+            return false;
+        }
+        AlarmTime next = new AlarmTime(alarmInfo.getTriggerTime());
+        if (expected.mIsPm != next.mIsPm ||
+            expected.mHour != next.mHour ||
+            expected.mMinute != next.mMinute) {
+          Log.i(TAG, "Next Alarm time is not same expected time: " +
+              "next = " + next + ", expected = " + expected);
+          return false;
+        }
+        return true;
+    }
+
+    class NextAlarmReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(AlarmManager.ACTION_NEXT_ALARM_CLOCK_CHANGED)) {
+                Log.i(TAG, "received broadcast");
+                mLatch.countDown();
+            }
+        }
+    }
+
+    static class AlarmTime {
+      int mHour;
+      int mMinute;
+      boolean mIsPm;
+
+      AlarmTime(long l) {
+          Calendar cal = Calendar.getInstance();
+          cal.setTimeInMillis(l);
+          mHour = cal.get(Calendar.HOUR);
+          mMinute = cal.get(Calendar.MINUTE);
+          mIsPm = cal.get(Calendar.AM_PM) == 1;
+          Log.i(TAG, "Calendar converted is: " + cal);
+      }
+
+      AlarmTime(int hour, int minute, boolean isPM) {
+          mHour = hour;
+          mMinute = minute;
+          mIsPm = isPM;
+      }
+
+      @Override
+      public String toString() {
+          String isPmString = (mIsPm) ? "pm" : "am";
+          return  mHour + ":" + mMinute + isPmString;
+      }
+    }
+}
diff --git a/tests/tests/alarmclock/src/android/alarmclock/cts/TestActivity.java b/tests/tests/alarmclock/src/android/alarmclock/cts/TestActivity.java
new file mode 100644
index 0000000..9ec33ae
--- /dev/null
+++ b/tests/tests/alarmclock/src/android/alarmclock/cts/TestActivity.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.alarmclock.cts;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.AlarmClock;
+import android.util.Log;
+
+import java.util.Calendar;
+
+public class TestActivity extends Activity {
+    static final String TAG = "TestActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, " in onCreate");
+    }
+
+    private void setParams(int hour, int minute, boolean isPM, Intent intent) {
+        intent.putExtra(AlarmClock.EXTRA_ALARM_SEARCH_MODE, AlarmClock.ALARM_SEARCH_MODE_TIME);
+        intent.putExtra(AlarmClock.EXTRA_IS_PM, isPM);
+        intent.putExtra(AlarmClock.EXTRA_HOUR, hour);
+        intent.putExtra(AlarmClock.EXTRA_MINUTES, minute);
+    }
+
+    private boolean start(Intent intent) {
+        try {
+            startActivity(intent);
+        } catch (ActivityNotFoundException e) {
+            Log.wtf(TAG, e);
+            return false;
+        }
+        return true;
+    }
+
+    public boolean cancelAlarm(AlarmClockIntentsTest.AlarmTime time) {
+        Intent intent = new Intent(AlarmClock.ACTION_DISMISS_ALARM);
+        setParams(time.mHour, time.mMinute, time.mIsPm, intent);
+        Log.i(TAG, "sending DISMISS_ALARM intent for: " + time + ", Intent = " + intent);
+        return start(intent);
+    }
+
+    public boolean setAlarm(AlarmClockIntentsTest.AlarmTime time) {
+        Intent intent = new Intent(AlarmClock.ACTION_SET_ALARM);
+        int hour = time.mHour;
+        if (time.mIsPm) {
+          hour += 12;
+        }
+        setParams(hour, time.mMinute, time.mIsPm, intent);
+        Log.i(TAG, "Setting alarm: " + hour + ":" + time.mMinute + ", Intent = " + intent);
+        return start(intent);
+    }
+
+    public boolean snoozeAlarm() {
+        Intent intent = new Intent(AlarmClock.ACTION_SNOOZE_ALARM);
+        Log.i(TAG, "sending SNOOZE_ALARM intent." + intent);
+        return start(intent);
+  }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
index 42b8d33..10992c5 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTestCase.java
@@ -32,15 +32,12 @@
     protected static final String LOG_TAG = "TestRunner";
 
     /**
-     * By default tests need to run in a {@link TestSensorEnvironment} that assumes each sensor is
-     * running with a load of several listeners, requesting data at different rates.
-     *
-     * In a better world the component acting as builder of {@link SensorOperation} would compute
-     * this value based on the tests composed.
-     *
-     * Ideally, each {@link Sensor} object would expose this information to clients.
+     * Previously for L release, we had this flag to know if each sensor is running with multiple
+     * listeners each requesting different data rates. Now before running CTS tests all sensors
+     * are de-activated by putting SensorService in RESTRICTED mode. Only CTS tests can
+     * activate/deactivate sensors in this mode. So we can default this flag value to false.
      */
-    private volatile boolean mEmulateSensorUnderLoad = true;
+    private volatile boolean mEmulateSensorUnderLoad = false;
 
     /**
      * By default the test class is the root of the test hierarchy.
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
index 1e775e3..4c449dd 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -82,4 +82,8 @@
             this.previousEvent = previousEvent;
         }
     }
+
+    protected double nanosToMillis(long nanos) {
+        return nanos/(1000.0 * 1000.0);
+    }
 }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
index b692f0f..b0c0fc0 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -28,6 +28,10 @@
     // Number of events to truncate (discard) from the initial events received
     private static final int TRUNCATE_EVENTS_COUNT = 1;
 
+    // Number of event gaps to tolerate is the minimum of 1% of total events received or 10.
+    private static final int EVENT_GAP_TOLERANCE_COUNT = 10;
+    private static final double EVENT_GAP_TOLERANCE_PERCENT = 0.01;
+
     private final int mExpectedDelayUs;
 
     private final List<IndexedEventPair> mEventGaps = new LinkedList<IndexedEventPair>();
@@ -68,24 +72,24 @@
         }
 
         final int count = mEventGaps.size();
-        stats.addValue(PASSED_KEY, count == 0);
+        int eventGapTolerance = (int) Math.min(EVENT_GAP_TOLERANCE_COUNT, 0.01 * mIndex);
+        stats.addValue(PASSED_KEY, count <= eventGapTolerance);
         stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
         stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, getIndexArray(mEventGaps));
 
-        if (count > 0) {
+        if (count > eventGapTolerance) {
             StringBuilder sb = new StringBuilder();
             sb.append(count).append(" events gaps: ");
             for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
                 IndexedEventPair info = mEventGaps.get(i);
-                sb.append(String.format("position=%d, delta_time=%dns; ", info.index,
-                        info.event.timestamp - info.previousEvent.timestamp));
+                sb.append(String.format("position=%d, delta_time=%.2fms; ", info.index,
+                        nanosToMillis(info.event.timestamp - info.previousEvent.timestamp)));
             }
             if (count > TRUNCATE_MESSAGE_LENGTH) {
                 sb.append(count - TRUNCATE_MESSAGE_LENGTH).append(" more; ");
             }
-            sb.append(String.format("(expected <%dns)",
-                    TimeUnit.NANOSECONDS.convert((int) (THRESHOLD * mExpectedDelayUs),
-                            TimeUnit.MICROSECONDS)));
+            sb.append(String.format("(expected <%.2fms)",
+                    (double)(THRESHOLD * mExpectedDelayUs)/1000.0));
             Assert.fail(sb.toString());
         }
     }
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
index 3af3f03..6f97439 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -149,9 +149,12 @@
             if (eventTimestampNs < lowerThresholdNs || eventTimestampNs > upperThresholdNs) {
                 if (failures.size() < TRUNCATE_MESSAGE_LENGTH) {
                     builder.append("position=").append(i);
-                    builder.append(", timestamp=").append(eventTimestampNs).append("ns");
-                    builder.append(", expected=[").append(lowerThresholdNs);
-                    builder.append(", ").append(upperThresholdNs).append("]ns; ");
+                    builder.append(", timestamp=").append(String.format("%.2fms",
+                                nanosToMillis(eventTimestampNs)));
+                    builder.append(", expected=[").append(String.format("%.2fms",
+                                nanosToMillis(lowerThresholdNs)));
+                    builder.append(", ").append(String.format("%.2f]ms; ",
+                                nanosToMillis(upperThresholdNs)));
                 }
                 failures.add(new IndexedEvent(i, event));
             }
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES128GCMNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128GCMNoPaddingCipherTest.java
index 6ae13ce..ed9c2b1 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AES128GCMNoPaddingCipherTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128GCMNoPaddingCipherTest.java
@@ -23,9 +23,14 @@
     private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
             "6d7596a8fd56ceaec61de7940984b7736fec44f572afc3c8952e4dc6541e2bc6a702c440a37610989543f6"
             + "3fedb047ca2173bc18581944");
-    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+    private static final byte[] KAT_CIPHERTEXT_WITHOUT_AAD = HexEncoding.decode(
             "b3f6799e8f9326f2df1e80fcd2cb16d78c9dc7cc14bb677862dc6c639b3a6338d24b312d3989e5920b5dbf"
             + "c976765efbfe57bb385940a7a43bdf05bddae3c9d6a2fbbdfcc0cba0");
+    private static final byte[] KAT_AAD = HexEncoding.decode(
+            "d3bc7458914f45d56d5fcfbb2eeff2dcc0e620c1229d90904e98930ea71aa43b6898f846f3244d");
+    private static final byte[] KAT_CIPHERTEXT_WITH_AAD = HexEncoding.decode(
+            "b3f6799e8f9326f2df1e80fcd2cb16d78c9dc7cc14bb677862dc6c639b3a6338d24b312d3989e5920b5dbf"
+            + "c976765efbfe57bb385940a70c106264d81506f8daf9cd6a1c70988c");
 
     @Override
     protected byte[] getKatKey() {
@@ -44,6 +49,16 @@
 
     @Override
     protected byte[] getKatCiphertext() {
-        return KAT_CIPHERTEXT.clone();
+        return KAT_CIPHERTEXT_WITHOUT_AAD.clone();
+    }
+
+    @Override
+    protected byte[] getKatAad() {
+        return KAT_AAD.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertextWhenKatAadPresent() {
+        return KAT_CIPHERTEXT_WITH_AAD.clone();
     }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES192CBCNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES192CBCNoPaddingCipherTest.java
new file mode 100644
index 0000000..f49c7c3
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES192CBCNoPaddingCipherTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES192CBCNoPaddingCipherTest extends AESCBCNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "be8cc4e25cce46e5d55725e2391f7d3cf59ed60062f5a43b");
+    private static final byte[] KAT_IV = HexEncoding.decode("80a199aab0eee77e7762ddf3b3a32f40");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "064f9200e0df37d4711af4a69d11addf9e1c345d9d8195f9f1f715019ce96a167f2497c994bd496eb80bfb"
+            + "2ba2c9d5af");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "859b90becaa85e95a71e104efbd7a3b723bcbf4eb39865544a05d9e90b6fe572c134552f3a138e726fbe49"
+            + "3b3a839598");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES192CBCPKCS7PaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES192CBCPKCS7PaddingCipherTest.java
new file mode 100644
index 0000000..b17befc
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES192CBCPKCS7PaddingCipherTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES192CBCPKCS7PaddingCipherTest extends AESCBCPKCS7PaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "68969215ec41e4df7d23de0e806f458f52aff492bd7c5263");
+    private static final byte[] KAT_IV = HexEncoding.decode("e61d13dfbf0533289f0e7950209da418");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "8d4c1cac27511ee2d82409a7f378e7e402b0eb189c1eaa5c506eb72a9074b170");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "e70bcd62c595dc1b2b8c197bb91a7447e1be2cbcf3fdc69e7e991faf0f57cf4e3884138ff403a41fd99818"
+            + "708ada301c");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES192CTRNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES192CTRNoPaddingCipherTest.java
new file mode 100644
index 0000000..d4a4143
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES192CTRNoPaddingCipherTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES192CTRNoPaddingCipherTest extends AESCTRNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "5e2036e790d38815c90beb67a1c9e5aa0e167ef082927317");
+    private static final byte[] KAT_IV = HexEncoding.decode("df0694959b89054156962d68a226965c");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "6ed2781c99e03e45314d6019932220c2c98130c53f9f67ad10ac519adf50e928091e09cdbbd3b42b");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "e427b6666502e05b82d0b20ae50e862b1936d71266fc49178ac984e71571f22ae0f90f0c19f42b4a");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES192ECBNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES192ECBNoPaddingCipherTest.java
new file mode 100644
index 0000000..0557d03
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES192ECBNoPaddingCipherTest.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES192ECBNoPaddingCipherTest extends AESECBNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "3cab83fb338ba985fbfe74c5e9d2e900adb570b1d67faf92");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "2cc64c335a13fb838f3c6aad0a6b47297ca90bb886ddb059200f0b41740c44ab");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "9c5c825328f5ee0aa24947e374d3f9165f484b39dd808c790d7a129648102453");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return null;
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES192ECBPKCS7PaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES192ECBPKCS7PaddingCipherTest.java
new file mode 100644
index 0000000..1cf193c
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES192ECBPKCS7PaddingCipherTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES192ECBPKCS7PaddingCipherTest extends AESECBPKCS7PaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "d57f4e5446f736c16476ec4db5decc7b1bf3936e4f7e4618");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "b115777f1ee7a43a07daa6401e59c46b7a98213a8747eabfbe3ca4ec93524de2c7");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "1e92cd20da08bb5fa174a7a69879d4fc25a155e6af06d75b26c5b450d273c8bb7e3a889dd4a9589098b44a"
+            + "cf1056e7aa");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return null;
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES192GCMNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES192GCMNoPaddingCipherTest.java
new file mode 100644
index 0000000..66b37d3
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES192GCMNoPaddingCipherTest.java
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES192GCMNoPaddingCipherTest extends AESGCMNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "21339fc1d011abca65d50ce2365230603fd47d07e8830f6e");
+    private static final byte[] KAT_IV = HexEncoding.decode("d5fb1469a8d81dd75286a418");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "cf776dedf53a828d51a0073db3ef0dd1ee19e2e9e243ce97e95841bb9ad4e3ff52");
+    private static final byte[] KAT_CIPHERTEXT_WITHOUT_AAD = HexEncoding.decode(
+            "3a0d48278111d3296bc663df8a5dbeb2474ea47fd85b608f8d9375d9dcf7de1413ad70fb0e1970669095ad"
+            + "77ebb5974ae8");
+    private static final byte[] KAT_AAD = HexEncoding.decode(
+            "04cdc1d840c17dcfccf78b3d792463740ce0bfdc167b98a632e144cafe9663");
+    private static final byte[] KAT_CIPHERTEXT_WITH_AAD = HexEncoding.decode(
+            "3a0d48278111d3296bc663df8a5dbeb2474ea47fd85b608f8d9375d9dcf7de141380718b9f6c023fb091c7"
+            + "6891a91683e2");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT_WITHOUT_AAD.clone();
+    }
+
+    @Override
+    protected byte[] getKatAad() {
+        return KAT_AAD.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertextWhenKatAadPresent() {
+        return KAT_CIPHERTEXT_WITH_AAD.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES256CBCNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES256CBCNoPaddingCipherTest.java
new file mode 100644
index 0000000..b6620ef
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES256CBCNoPaddingCipherTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES256CBCNoPaddingCipherTest extends AESCBCNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "dd2f20dc6b98c100bac919120ff95eb5d96003f8229987b283a1e777b0cd5c30");
+    private static final byte[] KAT_IV = HexEncoding.decode("23b4d85239fb90db93b07a981e90a170");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "2fbe5d46dca5cea433e550d8b291740ab9551c2a2d37680d7fb7b993225f58494cb53caca353e4b637ba05"
+            + "687be20f8d");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "5aba24fc316936c8369061ee8fe463e4faed04288e204456626b988c0e376b6047da1e4fd7c4e1cf265609"
+            + "7f75ae8685");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES256CBCPKCS7PaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES256CBCPKCS7PaddingCipherTest.java
new file mode 100644
index 0000000..6613463
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES256CBCPKCS7PaddingCipherTest.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES256CBCPKCS7PaddingCipherTest extends AESCBCPKCS7PaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "03ab2510520f5cfebfab0a17a7f8324c9634911f6fc59e586f85346bb38ac88a");
+    private static final byte[] KAT_IV = HexEncoding.decode("9af96967195bb0184f129beffa8241ae");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "2d6944653ac14988a772a2730b7c5bfa99a21732ae26f40cdc5b3a2874c7942545a82b73c48078b9dae622"
+            + "61c65909");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "26b308f7e1668b55705a79c8b3ad10e244655f705f027f390a5c34e4536f519403a71987b95124073d69f2"
+            + "a3cb95b0ab");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES256CTRNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES256CTRNoPaddingCipherTest.java
new file mode 100644
index 0000000..bdcff41
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES256CTRNoPaddingCipherTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES256CTRNoPaddingCipherTest extends AESCTRNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "928b380a8fed4b4b4cfeb56e0c66a4cb0f9ff58d61ac68bcfd0e3fbd910a684f");
+    private static final byte[] KAT_IV = HexEncoding.decode("0b678a5249e6eeda461dfb4776b6c58e");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "f358de57543b297e997cba46fb9100553d6abd65377e55b9aac3006400ead11f6db3c884");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "a07a35fbd1776ad81462e1935f542337add60962bf289249476817b6ddd532a7be30d4c3");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES256ECBNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES256ECBNoPaddingCipherTest.java
new file mode 100644
index 0000000..847a767
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES256ECBNoPaddingCipherTest.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES256ECBNoPaddingCipherTest extends AESECBNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "fa4622d9cf6485075daedd33d2c4fffdf859e2edb7f7df4f04603f7e647fae90");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "96ccabbe0c68970d8cdee2b30ab43c2d61cc50ee68271e77571e72478d713a31a476d6806b8116089c6ec5"
+            + "0bb543200f");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "0e81839e9dfbfe3b503d619e676abe5ac80fac3f245d8f09b9134b1b32a67dc83e377faf246288931136be"
+            + "f2a07c0be4");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return null;
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES256ECBPKCS7PaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES256ECBPKCS7PaddingCipherTest.java
new file mode 100644
index 0000000..0faffe9
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES256ECBPKCS7PaddingCipherTest.java
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES256ECBPKCS7PaddingCipherTest extends AESECBPKCS7PaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "bf3f07c68467fead0ca8e2754500ab514258abf02eb7e615a493bcaaa45d5ee1");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "af0757e49018dad628f16998628a407db5f28291bef3bc2e4d8a5a31fb238e6f");
+    private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+            "21ec3011074bf1ef140643d47130326c5e183f61237c69bc77551ca207d71fc2b90cfac6c8d2d125e5cd9f"
+            + "f353dee0df");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return null;
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES256GCMNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES256GCMNoPaddingCipherTest.java
new file mode 100644
index 0000000..971e610
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES256GCMNoPaddingCipherTest.java
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+public class AES256GCMNoPaddingCipherTest extends AESGCMNoPaddingCipherTestBase {
+
+    private static final byte[] KAT_KEY = HexEncoding.decode(
+            "7972140d831eedac75d5ea515c9a4c3bb124499a90b5f317ac1a685e88fae395");
+    private static final byte[] KAT_IV = HexEncoding.decode("a66c5252808d823dd4151fed");
+    private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+            "c2b9dabf3a55adaa94e8c0d1e77a84a3435aee23b2c3c4abb587b09a9c2afbf0");
+    private static final byte[] KAT_CIPHERTEXT_WITHOUT_AAD = HexEncoding.decode(
+            "a960619314657b2afb96b93bebb372bffd09e19d53e351f17d1ba2611f9dc33c9c92d563e8fd381254ac26"
+            + "2aa2a4ea0d");
+    private static final byte[] KAT_AAD = HexEncoding.decode(
+            "3727229db7a3ccda7283f628fb8a3cdf093ea1f4e8bd1bc40a830fc6df6fb0e249845dd7d449b2bc3b5ba4"
+            + "2258fb92c7");
+    private static final byte[] KAT_CIPHERTEXT_WITH_AAD = HexEncoding.decode(
+            "a960619314657b2afb96b93bebb372bffd09e19d53e351f17d1ba2611f9dc33c1501caa6cca0a281f42bc3"
+            + "10d1e4488f");
+
+    @Override
+    protected byte[] getKatKey() {
+        return KAT_KEY.clone();
+    }
+
+    @Override
+    protected byte[] getKatIv() {
+        return KAT_IV.clone();
+    }
+
+    @Override
+    protected byte[] getKatPlaintext() {
+        return KAT_PLAINTEXT.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertext() {
+        return KAT_CIPHERTEXT_WITHOUT_AAD.clone();
+    }
+
+    @Override
+    protected byte[] getKatAad() {
+        return KAT_AAD.clone();
+    }
+
+    @Override
+    protected byte[] getKatCiphertextWhenKatAadPresent() {
+        return KAT_CIPHERTEXT_WITH_AAD.clone();
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java
index 5ecf22f..b2752dc 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java
@@ -54,4 +54,8 @@
         }
         return null;
     }
+
+    public void testInitRejectsIvParameterSpec() throws Exception {
+        assertInitRejectsIvParameterSpec(new byte[getBlockSize()]);
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java
index d901674..1c3404a 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java
@@ -16,14 +16,21 @@
 
 package android.keystore.cts;
 
+import java.nio.ByteBuffer;
 import java.security.AlgorithmParameters;
+import java.security.Key;
 import java.security.spec.AlgorithmParameterSpec;
 import java.security.spec.InvalidParameterSpecException;
 
+import javax.crypto.AEADBadTagException;
+import javax.crypto.Cipher;
 import javax.crypto.spec.GCMParameterSpec;
 
 abstract class AESGCMCipherTestBase extends BlockCipherTestBase {
 
+    protected abstract byte[] getKatAad();
+    protected abstract byte[] getKatCiphertextWhenKatAadPresent();
+
     @Override
     protected boolean isStreamCipher() {
         return true;
@@ -54,4 +61,148 @@
         GCMParameterSpec spec = params.getParameterSpec(GCMParameterSpec.class);
         return spec.getIV();
     }
+
+    public void testKatEncryptWithAadProvidedInOneGo() throws Exception {
+        createCipher();
+        assertKatTransformWithAadProvidedInOneGo(
+                Cipher.ENCRYPT_MODE,
+                getKatAad(),
+                getKatPlaintext(),
+                getKatCiphertextWhenKatAadPresent());
+    }
+
+    public void testKatDecryptWithAadProvidedInOneGo() throws Exception {
+        createCipher();
+        assertKatTransformWithAadProvidedInOneGo(
+                Cipher.DECRYPT_MODE,
+                getKatAad(),
+                getKatCiphertextWhenKatAadPresent(),
+                getKatPlaintext());
+    }
+
+    public void testKatEncryptWithAadProvidedInChunks() throws Exception {
+        createCipher();
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.ENCRYPT_MODE,
+                getKatAad(),
+                getKatPlaintext(),
+                getKatCiphertextWhenKatAadPresent(),
+                1);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.ENCRYPT_MODE,
+                getKatAad(),
+                getKatPlaintext(),
+                getKatCiphertextWhenKatAadPresent(),
+                8);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.ENCRYPT_MODE,
+                getKatAad(),
+                getKatPlaintext(),
+                getKatCiphertextWhenKatAadPresent(),
+                3);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.ENCRYPT_MODE,
+                getKatAad(),
+                getKatPlaintext(),
+                getKatCiphertextWhenKatAadPresent(),
+                7);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.ENCRYPT_MODE,
+                getKatAad(),
+                getKatPlaintext(),
+                getKatCiphertextWhenKatAadPresent(),
+                23);
+    }
+
+    public void testKatDecryptWithAadProvidedInChunks() throws Exception {
+        createCipher();
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.DECRYPT_MODE,
+                getKatAad(),
+                getKatCiphertextWhenKatAadPresent(),
+                getKatPlaintext(),
+                1);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.DECRYPT_MODE,
+                getKatAad(),
+                getKatCiphertextWhenKatAadPresent(),
+                getKatPlaintext(),
+                8);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.DECRYPT_MODE,
+                getKatAad(),
+                getKatCiphertextWhenKatAadPresent(),
+                getKatPlaintext(),
+                3);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.DECRYPT_MODE,
+                getKatAad(),
+                getKatCiphertextWhenKatAadPresent(),
+                getKatPlaintext(),
+                7);
+        assertKatTransformWithAadProvidedInChunks(
+                Cipher.DECRYPT_MODE,
+                getKatAad(),
+                getKatCiphertextWhenKatAadPresent(),
+                getKatPlaintext(),
+                23);
+    }
+
+    private void assertKatTransformWithAadProvidedInOneGo(int opmode,
+            byte[] aad, byte[] input, byte[] expectedOutput) throws Exception {
+        initKat(opmode);
+        updateAAD(aad);
+        assertEquals(expectedOutput, doFinal(input));
+
+        initKat(opmode);
+        updateAAD(aad, 0, aad.length);
+        assertEquals(expectedOutput, doFinal(input));
+
+        initKat(opmode);
+        updateAAD(ByteBuffer.wrap(aad));
+        assertEquals(expectedOutput, doFinal(input));
+    }
+
+    private void assertKatTransformWithAadProvidedInChunks(int opmode,
+            byte[] aad, byte[] input, byte[] expectedOutput, int maxChunkSize) throws Exception {
+        createCipher();
+        initKat(opmode);
+        int aadOffset = 0;
+        while (aadOffset < aad.length) {
+            int chunkSize = Math.min(aad.length - aadOffset, maxChunkSize);
+            updateAAD(aad, aadOffset, chunkSize);
+            aadOffset += chunkSize;
+        }
+        assertEquals(expectedOutput, doFinal(input));
+    }
+
+    public void testCiphertextBitflipDetectedWhenDecrypting() throws Exception {
+        createCipher();
+        Key key = importKey(getKatKey());
+        byte[] ciphertext = getKatCiphertext();
+        ciphertext[ciphertext.length / 2] ^= 0x40;
+        init(Cipher.DECRYPT_MODE, key, getKatAlgorithmParameterSpec());
+        try {
+            doFinal(ciphertext);
+            fail();
+        } catch (AEADBadTagException expected) {}
+    }
+
+    public void testAadBitflipDetectedWhenDecrypting() throws Exception {
+        createCipher();
+        Key key = importKey(getKatKey());
+        byte[] ciphertext = getKatCiphertextWhenKatAadPresent();
+        byte[] aad = getKatCiphertext();
+        aad[aad.length / 3] ^= 0x2;
+        init(Cipher.DECRYPT_MODE, key, getKatAlgorithmParameterSpec());
+        updateAAD(aad);
+        try {
+            doFinal(ciphertext);
+            fail();
+        } catch (AEADBadTagException expected) {}
+    }
+
+    public void testInitRejectsIvParameterSpec() throws Exception {
+        assertInitRejectsIvParameterSpec(getKatIv());
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
index 398d373..f583c51 100644
--- a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
+++ b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
@@ -45,6 +45,7 @@
 import javax.crypto.NoSuchPaddingException;
 import javax.crypto.SecretKey;
 import javax.crypto.ShortBufferException;
+import javax.crypto.spec.IvParameterSpec;
 import javax.crypto.spec.SecretKeySpec;
 
 abstract class BlockCipherTestBase extends AndroidTestCase {
@@ -828,21 +829,33 @@
     }
 
     public void testUpdateAADNotSupported() throws Exception {
-        createCipher();
-        initKat(Cipher.ENCRYPT_MODE);
         if (isAuthenticatedCipher()) {
-            assertUpdateAADSupported();
-        } else {
-            assertUpdateAADNotSupported();
+            // Not applicable to authenticated ciphers where updateAAD is supported.
+            return;
         }
 
         createCipher();
+        initKat(Cipher.ENCRYPT_MODE);
+        assertUpdateAADNotSupported();
+
+        createCipher();
         initKat(Cipher.DECRYPT_MODE);
-        if (isAuthenticatedCipher()) {
-            assertUpdateAADSupported();
-        } else {
-            assertUpdateAADNotSupported();
+        assertUpdateAADNotSupported();
+    }
+
+    public void testUpdateAADSupported() throws Exception {
+        if (!isAuthenticatedCipher()) {
+            // Not applicable to unauthenticated ciphers where updateAAD is not supported.
+            return;
         }
+
+        createCipher();
+        initKat(Cipher.ENCRYPT_MODE);
+        assertUpdateAADSupported();
+
+        createCipher();
+        initKat(Cipher.DECRYPT_MODE);
+        assertUpdateAADSupported();
     }
 
     private void assertUpdateAADNotSupported() throws Exception {
@@ -1235,7 +1248,7 @@
                 subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
     }
 
-    private void createCipher() throws NoSuchAlgorithmException,
+    protected void createCipher() throws NoSuchAlgorithmException,
             NoSuchPaddingException  {
         mCipher = Cipher.getInstance(getTransformation());
     }
@@ -1279,7 +1292,7 @@
         return importKey(getKatKey());
     }
 
-    private SecretKey importKey(byte[] keyMaterial) {
+    protected SecretKey importKey(byte[] keyMaterial) {
         try {
             int keyId = mNextKeyId++;
             String keyAlias = "key" + keyId;
@@ -1318,75 +1331,75 @@
         }
     }
 
-    private void initKat(int opmode)
+    protected void initKat(int opmode)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
         init(opmode, getKey(), getKatAlgorithmParameterSpec());
     }
 
-    private void init(int opmode, Key key, AlgorithmParameters spec)
+    protected void init(int opmode, Key key, AlgorithmParameters spec)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
         mCipher.init(opmode, key, spec);
         mOpmode = opmode;
     }
 
-    private void init(int opmode, Key key, AlgorithmParameters spec, SecureRandom random)
+    protected void init(int opmode, Key key, AlgorithmParameters spec, SecureRandom random)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
         mCipher.init(opmode, key, spec, random);
         mOpmode = opmode;
     }
 
-    private void init(int opmode, Key key, AlgorithmParameterSpec spec)
+    protected void init(int opmode, Key key, AlgorithmParameterSpec spec)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
         mCipher.init(opmode, key, spec);
         mOpmode = opmode;
     }
 
-    private void init(int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random)
+    protected void init(int opmode, Key key, AlgorithmParameterSpec spec, SecureRandom random)
             throws InvalidKeyException, InvalidAlgorithmParameterException {
         mCipher.init(opmode, key, spec, random);
         mOpmode = opmode;
     }
 
-    private void init(int opmode, Key key) throws InvalidKeyException {
+    protected void init(int opmode, Key key) throws InvalidKeyException {
         mCipher.init(opmode, key);
         mOpmode = opmode;
     }
 
-    private void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
+    protected void init(int opmode, Key key, SecureRandom random) throws InvalidKeyException {
         mCipher.init(opmode, key, random);
         mOpmode = opmode;
     }
 
-    private byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException {
+    protected byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException {
         return mCipher.doFinal();
     }
 
-    private byte[] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
+    protected byte[] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
         return mCipher.doFinal(input);
     }
 
-    private byte[] doFinal(byte[] input, int inputOffset, int inputLen)
+    protected byte[] doFinal(byte[] input, int inputOffset, int inputLen)
             throws IllegalBlockSizeException, BadPaddingException {
         return mCipher.doFinal(input, inputOffset, inputLen);
     }
 
-    private int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output)
+    protected int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output)
             throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
         return mCipher.doFinal(input, inputOffset, inputLen, output);
     }
 
-    private int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
+    protected int doFinal(byte[] input, int inputOffset, int inputLen, byte[] output,
             int outputOffset) throws ShortBufferException, IllegalBlockSizeException,
             BadPaddingException {
         return mCipher.doFinal(input, inputOffset, inputLen, output, outputOffset);
     }
 
-    private int doFinal(byte[] output, int outputOffset) throws IllegalBlockSizeException,
+    protected int doFinal(byte[] output, int outputOffset) throws IllegalBlockSizeException,
             ShortBufferException, BadPaddingException {
         return mCipher.doFinal(output, outputOffset);
     }
 
-    private int doFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException,
+    protected int doFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException,
             IllegalBlockSizeException, BadPaddingException {
         return mCipher.doFinal(input, output);
     }
@@ -1415,21 +1428,21 @@
         }
     }
 
-    private byte[] update(byte[] input) {
+    protected byte[] update(byte[] input) {
         byte[] output = mCipher.update(input);
         assertUpdateOutputSize(
                 (input != null) ? input.length : 0, (output != null) ? output.length : 0);
         return output;
     }
 
-    private byte[] update(byte[] input, int offset, int len) {
+    protected byte[] update(byte[] input, int offset, int len) {
         byte[] output = mCipher.update(input, offset, len);
         assertUpdateOutputSize(len, (output != null) ? output.length : 0);
 
         return output;
     }
 
-    private int update(byte[] input, int offset, int len, byte[] output)
+    protected int update(byte[] input, int offset, int len, byte[] output)
             throws ShortBufferException {
         int outputLen = mCipher.update(input, offset, len, output);
         assertUpdateOutputSize(len, outputLen);
@@ -1437,7 +1450,7 @@
         return outputLen;
     }
 
-    private int update(byte[] input, int offset, int len, byte[] output, int outputOffset)
+    protected int update(byte[] input, int offset, int len, byte[] output, int outputOffset)
             throws ShortBufferException {
         int outputLen = mCipher.update(input, offset, len, output, outputOffset);
         assertUpdateOutputSize(len, outputLen);
@@ -1445,7 +1458,7 @@
         return outputLen;
     }
 
-    private int update(ByteBuffer input, ByteBuffer output) throws ShortBufferException {
+    protected int update(ByteBuffer input, ByteBuffer output) throws ShortBufferException {
         int inputLimitBefore = input.limit();
         int outputLimitBefore = output.limit();
         int inputLen = input.remaining();
@@ -1463,8 +1476,20 @@
         return outputLen;
     }
 
+    protected void updateAAD(byte[] input) {
+        mCipher.updateAAD(input);
+    }
+
+    protected void updateAAD(byte[] input, int offset, int len) {
+        mCipher.updateAAD(input, offset, len);
+    }
+
+    protected void updateAAD(ByteBuffer input) {
+        mCipher.updateAAD(input);
+    }
+
     @SuppressWarnings("unused")
-    private static void assertEquals(Buffer expected, Buffer actual) {
+    protected static void assertEquals(Buffer expected, Buffer actual) {
         throw new RuntimeException(
                 "Comparing ByteBuffers using their .equals is probably not what you want"
                 + " -- use assertByteBufferEquals instead.");
@@ -1474,7 +1499,7 @@
      * Asserts that the position, limit, and capacity of the provided buffers are the same, and that
      * their contents (from position {@code 0} to capacity) are the same.
      */
-    private static void assertByteBufferEquals(ByteBuffer expected, ByteBuffer actual) {
+    protected static void assertByteBufferEquals(ByteBuffer expected, ByteBuffer actual) {
         if (expected == null) {
             if (actual == null) {
                 return;
@@ -1504,7 +1529,7 @@
                         buffer.array(), buffer.arrayOffset(), buffer.capacity()) + "]";
     }
 
-    private static boolean equals(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
+    protected static boolean equals(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
             int len2) {
         if (arr1 == null) {
             return (arr2 == null);
@@ -1523,13 +1548,13 @@
         }
     }
 
-    private static byte[] subarray(byte[] array, int beginIndex, int endIndex) {
+    protected static byte[] subarray(byte[] array, int beginIndex, int endIndex) {
         byte[] result = new byte[endIndex - beginIndex];
         System.arraycopy(array, beginIndex, result, 0, result.length);
         return result;
     }
 
-    private static byte[] concat(byte[]... arrays) {
+    protected static byte[] concat(byte[]... arrays) {
         int resultLength = 0;
         for (byte[] array : arrays) {
             resultLength += (array != null) ? array.length : 0;
@@ -1546,11 +1571,11 @@
         return result;
     }
 
-    private static void assertEquals(byte[] expected, byte[] actual) {
+    protected static void assertEquals(byte[] expected, byte[] actual) {
         assertEquals(null, expected, actual);
     }
 
-    private static void assertEquals(String message, byte[] expected, byte[] actual) {
+    protected static void assertEquals(String message, byte[] expected, byte[] actual) {
         if (!Arrays.equals(expected, actual)) {
             StringBuilder detail = new StringBuilder();
             if (expected != null) {
@@ -1572,4 +1597,51 @@
             }
         }
     }
+
+    protected final void assertInitRejectsIvParameterSpec(byte[] iv) throws Exception {
+        Key key = importKey(getKatKey());
+        createCipher();
+        IvParameterSpec spec = new IvParameterSpec(iv);
+        try {
+            init(Cipher.ENCRYPT_MODE, key, spec);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        try {
+            init(Cipher.WRAP_MODE, key, spec);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        try {
+            init(Cipher.DECRYPT_MODE, key, spec);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        try {
+            init(Cipher.UNWRAP_MODE, key, spec);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        AlgorithmParameters param = AlgorithmParameters.getInstance("AES");
+        param.init(new IvParameterSpec(iv));
+        try {
+            init(Cipher.ENCRYPT_MODE, key, param);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        try {
+            init(Cipher.WRAP_MODE, key, param);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        try {
+            init(Cipher.DECRYPT_MODE, key, param);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+
+        try {
+            init(Cipher.UNWRAP_MODE, key, param);
+            fail();
+        } catch (InvalidAlgorithmParameterException expected) {}
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/CipherTest.java b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
new file mode 100644
index 0000000..0300ec6
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/CipherTest.java
@@ -0,0 +1,1081 @@
+/*
+ * 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.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import com.android.cts.keystore.R;
+
+import java.security.AlgorithmParameters;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyPair;
+import java.security.Provider;
+import java.security.Security;
+import java.security.interfaces.RSAKey;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.MGF1ParameterSpec;
+import java.security.Provider.Service;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.OAEPParameterSpec;
+import javax.crypto.spec.PSource;
+import javax.crypto.spec.SecretKeySpec;
+
+/**
+ * Tests for algorithm-agnostic functionality of {@code Cipher} implementations backed by Android
+ * Keystore.
+ */
+public class CipherTest extends AndroidTestCase {
+
+    private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
+
+    private static final String[] EXPECTED_ALGORITHMS = {
+        "AES/ECB/NoPadding",
+        "AES/ECB/PKCS7Padding",
+        "AES/CBC/NoPadding",
+        "AES/CBC/PKCS7Padding",
+        "AES/CTR/NoPadding",
+        "AES/GCM/NoPadding",
+        "RSA/ECB/NoPadding",
+        "RSA/ECB/PKCS1Padding",
+        "RSA/ECB/OAEPPadding",
+        "RSA/ECB/OAEPWithSHA-1AndMGF1Padding",
+        "RSA/ECB/OAEPWithSHA-224AndMGF1Padding",
+        "RSA/ECB/OAEPWithSHA-256AndMGF1Padding",
+        "RSA/ECB/OAEPWithSHA-384AndMGF1Padding",
+        "RSA/ECB/OAEPWithSHA-512AndMGF1Padding",
+    };
+
+    private static class KatVector {
+        private final byte[] plaintext;
+        private final byte[] ciphertext;
+        private final AlgorithmParameterSpec params;
+
+        private KatVector(String plaintextHex, String ciphertextHex) {
+            this(plaintextHex, null, ciphertextHex);
+        }
+
+        private KatVector(String plaintextHex, AlgorithmParameterSpec params,
+                String ciphertextHex) {
+            this(HexEncoding.decode(plaintextHex), params, HexEncoding.decode(ciphertextHex));
+        }
+
+        private KatVector(byte[] plaintext, byte[] ciphertext) {
+            this(plaintext, null, ciphertext);
+        }
+
+        private KatVector(byte[] plaintext, AlgorithmParameterSpec params, byte[] ciphertext) {
+            this.plaintext = plaintext;
+            this.ciphertext = ciphertext;
+            this.params = params;
+        }
+    }
+    private static final Map<String, KatVector> KAT_VECTORS =
+            new TreeMap<String, KatVector>(String.CASE_INSENSITIVE_ORDER);
+    static {
+        // From RI
+        KAT_VECTORS.put("AES/ECB/NoPadding", new KatVector(
+                "0383911bb1519d58e6656f3fd35639c502dbeb2196cea937fca272666cb4a80b",
+                "6574c5065283b89e0c930019e4655d8516b98170db6516cd83e589bd9c5e5adc"));
+        KAT_VECTORS.put("AES/ECB/PKCS7Padding", new KatVector(
+                "1ad3d73a3cfa66dac78a51a95c2cb2125ea701e6e9ecbca2415b436f0258e2ba7439b67545",
+                "920f873f2f9e91bac4c9c948d66496a21b8b2606850490dac7abecae83317488ee550b9973ac5cd142"
+                + "f387d7d2a12752"));
+        KAT_VECTORS.put("AES/CBC/NoPadding", new KatVector(
+                "1dffe21c8f18276c3a39ed0c53ab257b84efcedab60095c4cadd131143058cf7",
+                new IvParameterSpec(HexEncoding.decode("10b3eea6cc8a7d6f48337e9b6987d28c")),
+                "47ab115bfadca91eaebec73ab942a06f3121fdd5aa55d223bd2cbcc3855e1ef8"));
+        KAT_VECTORS.put("AES/CBC/PKCS7Padding", new KatVector(
+                "9d49fb970b23bfe742ae7c45a773ada9faad84708c8858a06e4a192e0a90e2f6083548e0bf3f67",
+                new IvParameterSpec(HexEncoding.decode("ecd87bf9c49f37dcd2294e309192289a")),
+                "aeb64f48ec18a086eda7ee080948651a50b6f582ab54aac5454c9ab0a4de5b4a4abac526a4307011d1"
+                + "2881f1849c32ae"));
+        KAT_VECTORS.put("AES/CTR/NoPadding", new KatVector(
+                "b4e786cab9df48d2fce0c7872651314db1318d1f31a1b10a2c334d2555b4117668",
+                new IvParameterSpec(HexEncoding.decode("94d9f7a6d16f58018819b668020b68cc")),
+                "022e74572a70be57a0b65b2fb5bc9b803ce48973b6163f528bbe1fd001e29d330a"));
+        KAT_VECTORS.put("AES/GCM/NoPadding", new KatVector(
+                "03889a6ca811e3fd7e78467e3dae587d2110e80e98edbc9dfe17afba238c4c493186",
+                new GCMParameterSpec(128, HexEncoding.decode("f67aaf97cdec65b12188315e")),
+                "159eb1ffc86589b38f18097c32db646c7de3525b603876c3ae671bc2ca52a5395a374b377a915c9ed1"
+                + "a349abf9fc54c9ca81"));
+        KAT_VECTORS.put("RSA/ECB/NoPadding", new KatVector(
+                "50c499d558c38fd48ea76832887db2abc76e4e153a98fd4323ccb8006d34f11724a5692fb101b0eb96"
+                + "060eb9d15222",
+                "349b1d5061e98d0ab3f2327680bbc0cbb1b8ef8ee26148d7c67cf535223e3f78d822d369592ede29b1"
+                + "654aab25e6ae5e098318e55c13dc405f5ba27e5cc69ced32778592a51e6293a03f95e14ed17099fb"
+                + "0ac585e41297b87c3432953df0d98be7e505dc7de7bfe9d9ec750f475afeba4cc2dd78838c0d4399"
+                + "d8de02b07f00b292dc3d32d2a2f98ea5a5dac1a0fec4d01e5c3aea8c56eeff264896fb6cf2144401"
+                + "278c6663417bc00aafbb9eb97c056573cdec88d6ac6fd6c333d131337b16031da229029e3b6fe6f8"
+                + "ee427f2e90041e9636d67cddac75845914ce4be56092eed7188fe7e2bb33769efdeed86a7acbe15d"
+                + "debf92d9fbaaddede206acfa650697"));
+        KAT_VECTORS.put("RSA/ECB/PKCS1Padding", new KatVector(
+                "aed8cd94f35b2a54cdd3ed771482bd87e256b995408558fb82e5d475d1ee54711472f899ad6cbb6847"
+                + "99e52ff1d57cbc39f4",
+                "64148dee294dd3ea31d2b595ea661318cf90c89f71393cf6559087d6e8993e73eb1e6b5f4d3cfde3cb"
+                + "267938c5eca522b95a2df02df9c703dbe3103c157af0d2ed5b70da51cb4caa49061319420d0ea433"
+                + "f24b727530c162226bc806b7f39079cd494a5c8a242737413d27063f9fb74aadd20f521211316719"
+                + "c628fd4351d0608928949b6f59f351d9ccec4c596514335010834fcabd53a2cbb2642e0f83c4f89c"
+                + "199ee2c68ace9182cf484d99e86b0b2213c1cc113d24891958e5a0774b7486abae1475e46a939a94"
+                + "5d6491b98ad7979fd6e752b47e43e960557a0c0589d7d0444b011d75c9f5b143da6e1dcf7b678a2e"
+                + "f82fbe37a74df3e20fb1a9dbfd5978"));
+        KAT_VECTORS.put("RSA/ECB/OAEPPadding", new KatVector(
+                "c219f4e3e37eae2315f0fa4ebc4b46ef0c6befbb43a51ceda07435fc88a9",
+                "7a9bcfd0d02b6434025bbf5ba09c2dad118a4a3bca7cced8b404bc0fc2f17ddee13de82c8324294bf2"
+                + "60ad6e5171c2c3728a0c0fab20dd60e4e56cfef3e66239439ed2eddcc83ac8eeaedfd970e9966de3"
+                + "94ad1df0df503a0a640a49e10885b3a4115c3e94e893fff87bf9a5808350f957d6bc556ca6b08f81"
+                + "bf697704a3eb3db774797f883af0dcdc9bd9196d7595bab5e87d3187eb45b5771abe4e4dc70c25fa"
+                + "b9e3cddb6ae453a1d8e517d000779472e1376e5848b1654a51a9e90be4a4a6d0f6b8723c6e93c471"
+                + "313ea94f24504ca377b502057331355965a7e0b9c3b1d1fbd24ab5a4167f721d1ddac4d3c094d5c9"
+                + "0d2e277e9b5617cbf2770186323e89"));
+        KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-1AndMGF1Padding", new KatVector(
+                "bb2854620bb0e361d1384703dda12acee1fefc22024bcfc40a86390d5342c693aab8c7ed6517d8da86"
+                + "04492c9d",
+                "77033c578f24ef0ed93bfe6dc6f7c3f9f0505e7562f67ce987a269cabaa8a3ae7dd5e567a8b37db42d"
+                + "a79aa86ea2e189af5b9560b39407ff86f2785cdaf660fc7c93649bc24a818de564cb0d03e7681fa8"
+                + "f3cd42b3bfc58c49d3f049e0c98b07aff95876f05ddc45ebaa7127a198f27ae0cfd161c5598ac795"
+                + "8ed386d98b13d45730e6dc16313fe012af27d7be0e45215040bbfb07f2d35e34291fe4335a68175a"
+                + "46be99a15c1ccf673659157e1f52105de5a0a6f8c9d946740216eefe2a01a37b0ab144a44ff0d800"
+                + "be713b5b44acf4fcb1a60d5db977af4d77fa77bdb8594032b2f5bbdd49346b08e0e98ab1051b462e"
+                + "160c1bff62b927cd26c936948b723a"));
+        KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-224AndMGF1Padding", new KatVector(
+                "1bae19434be6599d1987b1ed866dd6b684dcd908bd98d797250be545eafea46d05ebdf9018",
+                "0f18b4a1153c6f8821e18a4275e4b570d540c8ad86bfc99146e5475238a43ecbe63bc81368cd64b9a2"
+                + "ab3ccd586e6afaad054c9d7bdc986adf022ec86335d110c53ebd5f2f2bd49d48d6da9541312c9b1b"
+                + "cc299ca4f59475869e4ec2253c91b137eae274a245fc9ee6262f74754bbda55d8bd25bfa4c1698f3"
+                + "a22d2d8d7fc6e9fbb56d828e61912b3085d82cceaeb1d2da425871575e7ba31a3d47b1b7d7df0bda"
+                + "81d62c75a9887bbc528fc6bb51db09884bb513b4cc94ca4a5fe0b370ca548dcdf60eebbf61e7efe7"
+                + "630fc47256d6d617fc1c2c774405f385650898abea03502cfbdcb53579fd18d896490e67aecdb7c7"
+                + "b7b950dc7ddba5c64188494c1a177b"));
+        KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-256AndMGF1Padding", new KatVector(
+                "332c2f2fc066fb29ec0928a52b5111ce6965546ce73927340c42d33b56b6ba547b77ac361ac0d13316"
+                + "345ca953840023d892fa4ff1aa32cc66d5aa88b79867",
+                "942c0ba1c67a34a7e116d9281b1df5084c66bc1458faf1b26d4f0f63a57307a9addcd3e5d2f3320071"
+                + "5a3d95ae84fb40a8dfe4cb0a28873fd5883ff8ee6efbfe38c460c755577b34fcf05bb2077afec7b2"
+                + "203799022be6a0903915e01e94abc51efe9c5548eb86bbbb4fd7f3bfc7b86f388128b6df1e6ce651"
+                + "230c6bc18bbf55b029f1e31da880c27d947ff97519df66a57ead6db791c4978f1d62edec0d89bb16"
+                + "83d237213f3f24271ddb8c4b50a82527954f0e49ae44d3acd8ddd3a57cfbfa456dd40675d5d75542"
+                + "31c6b79c7fb3500b1631be1d100e67d85ce423845fdc7c7f45e346a8ba573f5d11de9009069468dd"
+                + "8d517ad4adb1509dd5173ee1862d74"));
+        KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-384AndMGF1Padding", new KatVector(
+                "f51f158cbad4dbab38403b839c724f09a480c49be29c0e72615539dbe57ec86143f31f19392f419b5b"
+                + "e4ba9e3c6f1e870d307a7cf1a9e2",
+                "944243f35f534e7a273e94986b6835a4f5cdc5bc4efb9970d4760986599a02f652a848fcae333ff25a"
+                + "64108c9b900aaf002688398ad9fc17c73be52726306af9c13540df9d1765336b6f09ba4cb8a54d72"
+                + "5a4e45854bfa3802cfb110a6d7f7054e6072440ec00da62828cb75fe2566ec5be79eb8a3d1fbe2c2"
+                + "4439c107e5018e445e201ad80725755543c00dec50bb464c6ca897600eb3cda51fcef8161ac13d75"
+                + "a3eb30d385a1e718a61ae1b5d47aadb966fc007becc84db397d0b3cd983121872f9975995153e869"
+                + "9e24554a3c5e885f0ed8cd03e916da5ed541f1598da9bd6209447301d00f086153da353deff9d045"
+                + "8976ff7570410f0bdcfb3f56b782f5"));
+        KAT_VECTORS.put("RSA/ECB/OAEPWithSHA-512AndMGF1Padding", new KatVector(
+                "d45f6ccc7e663957f234c237c1f09bf7791f6f5c1b9ef4fefb16e55ded0d96112e590f1bb08a60f85c"
+                + "2d0d2533f1d69792dfd8d647d880b18f87cfe32488c73613a3d535da7d776d90d9a4ba6a0311f456"
+                + "8511da49107c",
+                "5a037df3e5d6f3f703541e2db2aef7c69985e513bdff67c8ade6a09f50e27267bfb444f6c69b40a77a"
+                + "9136a27b29876af9d2bf4e7099863445d35b188d31f376b89fbd196059667ca657e10b9454c2b25f"
+                + "046fc9f7b42506e382e6b6fd99409cf97e865e65f8dce5d14a06b8aa8833c4bc72c8764467758f2d"
+                + "7960243161dce4ca8231e91bfcd3c933a80bc703ceab976224c876b1f550f91a6c2a0332d4377bd8"
+                + "dfe4b1283ab114e517b7b9e4a6e0bf166d5b506e7a3b7328078e12cb23b1d938760767dc9b3c3eb0"
+                + "848ddda101792aca9273ad414314c13fc511ffa0358a8f4c5f38edded3a2dc111fa62c80e6032c32"
+                + "ae04aeac7729f16a6310f1f6785c27"));
+    }
+
+    private static final long DAY_IN_MILLIS = TestUtils.DAY_IN_MILLIS;
+
+    private static final byte[] AES_KAT_KEY_BYTES =
+            HexEncoding.decode("7d9f11a0da111e9d8bdd14f04648ed91");
+
+    private static final KeyProtection.Builder GOOD_IMPORT_PARAMS_BUILDER =
+            new KeyProtection.Builder(
+                    KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                    .setBlockModes(KeyProperties.BLOCK_MODE_ECB,
+                            KeyProperties.BLOCK_MODE_CBC,
+                            KeyProperties.BLOCK_MODE_CTR,
+                            KeyProperties.BLOCK_MODE_GCM)
+                    .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                    .setDigests(KeyProperties.DIGEST_NONE)
+                    .setRandomizedEncryptionRequired(false);
+
+    public void testAlgorithmList() {
+        // Assert that Android Keystore Provider exposes exactly the expected Cipher
+        // transformations. We don't care whether the transformations are exposed via aliases, as
+        // long as canonical names of transformation are accepted.
+        // If the Provider exposes extraneous algorithms, it'll be caught because it'll have to
+        // expose at least one Service for such an algorithm, and this Service's algorithm will
+        // not be in the expected set.
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        Set<Service> services = provider.getServices();
+        Set<String> actualAlgsLowerCase = new HashSet<String>();
+        Set<String> expectedAlgsLowerCase = new HashSet<String>(
+                Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
+        for (Service service : services) {
+            if ("Cipher".equalsIgnoreCase(service.getType())) {
+                String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+                actualAlgsLowerCase.add(algLowerCase);
+            }
+        }
+
+        TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+                expectedAlgsLowerCase.toArray(new String[0]));
+    }
+
+    public void testAndroidKeyStoreKeysHandledByAndroidKeyStoreProviderWhenDecrypting()
+            throws Exception {
+        Collection<SecretKey> secretKeys = importDefaultKatSecretKeys();
+        Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                // Decryption may need additional parameters. Initializing a Cipher for encryption
+                // forces it to generate any such parameters.
+                Cipher cipher = Cipher.getInstance(algorithm, provider);
+                cipher.init(Cipher.ENCRYPT_MODE, getEncryptionKey(algorithm, secretKeys, keyPairs));
+                AlgorithmParameters params = cipher.getParameters();
+
+                // Test DECRYPT_MODE
+                Key key = getDecryptionKey(algorithm, secretKeys, keyPairs);
+                cipher = Cipher.getInstance(algorithm);
+                cipher.init(Cipher.DECRYPT_MODE, key, params);
+                assertSame(provider, cipher.getProvider());
+
+                // Test UNWRAP_MODE
+                cipher = Cipher.getInstance(algorithm);
+                if (params != null) {
+                    cipher.init(Cipher.UNWRAP_MODE, key, params);
+                } else {
+                    cipher.init(Cipher.UNWRAP_MODE, key);
+                }
+                assertSame(provider, cipher.getProvider());
+            } catch (Throwable e) {
+                throw new RuntimeException(algorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testAndroidKeyStorePublicKeysAcceptedByHighestPriorityProviderWhenEncrypting()
+            throws Exception {
+        Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(algorithm)) {
+                continue;
+            }
+            try {
+                Key key = getEncryptionKey(algorithm, Collections.<SecretKey>emptyList(), keyPairs);
+
+                Cipher cipher = Cipher.getInstance(algorithm);
+                cipher.init(Cipher.ENCRYPT_MODE, key);
+
+                cipher = Cipher.getInstance(algorithm);
+                cipher.init(Cipher.WRAP_MODE, key);
+            } catch (Throwable e) {
+                throw new RuntimeException(algorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testCiphertextGeneratedByAndroidKeyStoreDecryptsByAndroidKeyStore()
+            throws Exception {
+        Collection<SecretKey> secretKeys = importDefaultKatSecretKeys();
+        Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                Key encryptionKey = getEncryptionKey(algorithm, secretKeys, keyPairs);
+                Cipher cipher = Cipher.getInstance(algorithm, provider);
+                cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
+                AlgorithmParameters params = cipher.getParameters();
+                byte[] expectedPlaintext = "Very secret message goes here...".getBytes("UTF-8");
+                byte[] ciphertext = cipher.doFinal(expectedPlaintext);
+                if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
+                    // RSA decryption without padding left-pads resulting plaintext with NUL bytes
+                    // to the length of RSA modulus.
+                    int modulusLengthBytes =
+                            (((RSAKey) encryptionKey).getModulus().bitLength() + 7) / 8;
+                    expectedPlaintext = TestUtils.leftPadWithZeroBytes(
+                            expectedPlaintext, modulusLengthBytes);
+                }
+
+                cipher = Cipher.getInstance(algorithm, provider);
+                Key decryptionKey = getDecryptionKey(algorithm, secretKeys, keyPairs);
+                cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
+                byte[] actualPlaintext = cipher.doFinal(ciphertext);
+                MoreAsserts.assertEquals(expectedPlaintext, actualPlaintext);
+            } catch (Throwable e) {
+                throw new RuntimeException(algorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testCiphertextGeneratedByHighestPriorityProviderDecryptsByAndroidKeyStore()
+            throws Exception {
+        Collection<SecretKey> secretKeys = getDefaultKatSecretKeys();
+        Collection<SecretKey> keystoreSecretKeys = importDefaultKatSecretKeys();
+        Collection<KeyPair> keyPairs = getDefaultKatKeyPairs();
+        Collection<KeyPair> keystoreKeyPairs = importDefaultKatKeyPairs();
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                Key encryptionKey = getEncryptionKey(algorithm, secretKeys, keyPairs);
+                Cipher cipher;
+                try {
+                    cipher = Cipher.getInstance(algorithm);
+                    cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
+                } catch (InvalidKeyException e) {
+                    // No providers support encrypting using this algorithm and key.
+                    continue;
+                }
+                if (provider == cipher.getProvider()) {
+                    // This is covered by another test.
+                    continue;
+                }
+                AlgorithmParameters params = cipher.getParameters();
+
+                // TODO: Remove this workaround for Bug 22405492 once the issue is fixed. The issue
+                // is that Bouncy Castle incorrectly defaults the MGF1 digest to the digest
+                // specified in the transformation. RI and Android Keystore keep the MGF1 digest
+                // defaulted at SHA-1.
+                if ((params != null) && ("OAEP".equalsIgnoreCase(params.getAlgorithm()))) {
+                    OAEPParameterSpec spec = params.getParameterSpec(OAEPParameterSpec.class);
+                    if (!"SHA-1".equalsIgnoreCase(
+                            ((MGF1ParameterSpec) spec.getMGFParameters()).getDigestAlgorithm())) {
+                        cipher.init(Cipher.ENCRYPT_MODE, encryptionKey, new OAEPParameterSpec(
+                                spec.getDigestAlgorithm(),
+                                "MGF1",
+                                MGF1ParameterSpec.SHA1,
+                                PSource.PSpecified.DEFAULT));
+                        params = cipher.getParameters();
+                    }
+                }
+
+                byte[] expectedPlaintext = "Very secret message goes here...".getBytes("UTF-8");
+                byte[] ciphertext = cipher.doFinal(expectedPlaintext);
+                if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
+                    // RSA decryption without padding left-pads resulting plaintext with NUL bytes
+                    // to the length of RSA modulus.
+                    int modulusLengthBytes =
+                            (((RSAKey) encryptionKey).getModulus().bitLength() + 7) / 8;
+                    expectedPlaintext = TestUtils.leftPadWithZeroBytes(
+                            expectedPlaintext, modulusLengthBytes);
+                }
+
+                // TODO: Remove this workaround for Bug 22319986 once the issue is fixed. The issue
+                // is that Conscrypt and Bouncy Castle's AES/GCM/NoPadding implementations return
+                // AlgorithmParameters of algorithm "AES" from which it's impossible to obtain a
+                // GCMParameterSpec. They should be returning AlgorithmParameters of algorithm
+                // "GCM".
+                if (("AES/GCM/NoPadding".equalsIgnoreCase(algorithm))
+                        && (!"GCM".equalsIgnoreCase(params.getAlgorithm()))) {
+                    params = AlgorithmParameters.getInstance("GCM");
+                    params.init(new GCMParameterSpec(128, cipher.getIV()));
+                }
+
+                cipher = Cipher.getInstance(algorithm, provider);
+                Key decryptionKey = getDecryptionKey(
+                        algorithm, keystoreSecretKeys, keystoreKeyPairs);
+                cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
+                byte[] actualPlaintext = cipher.doFinal(ciphertext);
+                MoreAsserts.assertEquals(expectedPlaintext, actualPlaintext);
+            } catch (Throwable e) {
+                throw new RuntimeException(algorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testCiphertextGeneratedByAndroidKeyStoreDecryptsByHighestPriorityProvider()
+            throws Exception {
+        Collection<SecretKey> secretKeys = getDefaultKatSecretKeys();
+        Collection<SecretKey> keystoreSecretKeys = importDefaultKatSecretKeys();
+        Collection<KeyPair> keyPairs = getDefaultKatKeyPairs();
+        Collection<KeyPair> keystoreKeyPairs = importDefaultKatKeyPairs();
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                Key encryptionKey =
+                        getEncryptionKey(algorithm, keystoreSecretKeys, keystoreKeyPairs);
+                Cipher cipher = Cipher.getInstance(algorithm, provider);
+                cipher.init(Cipher.ENCRYPT_MODE, encryptionKey);
+                AlgorithmParameters params = cipher.getParameters();
+
+                byte[] expectedPlaintext = "Very secret message goes here...".getBytes("UTF-8");
+                byte[] ciphertext = cipher.doFinal(expectedPlaintext);
+                if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
+                    // RSA decryption without padding left-pads resulting plaintext with NUL bytes
+                    // to the length of RSA modulus.
+                    int modulusLengthBytes =
+                            (((RSAKey) encryptionKey).getModulus().bitLength() + 7) / 8;
+                    expectedPlaintext = TestUtils.leftPadWithZeroBytes(
+                            expectedPlaintext, modulusLengthBytes);
+                }
+
+                Key decryptionKey = getDecryptionKey(algorithm, secretKeys, keyPairs);
+                try {
+                    cipher = Cipher.getInstance(algorithm);
+                    if (params != null) {
+                        cipher.init(Cipher.DECRYPT_MODE, decryptionKey, params);
+                    } else {
+                        cipher.init(Cipher.DECRYPT_MODE, decryptionKey);
+                    }
+                } catch (InvalidKeyException e) {
+                    // No providers support decrypting using this algorithm and key.
+                    continue;
+                }
+                if (provider == cipher.getProvider()) {
+                    // This is covered by another test.
+                    continue;
+                }
+                byte[] actualPlaintext = cipher.doFinal(ciphertext);
+                MoreAsserts.assertEquals(expectedPlaintext, actualPlaintext);
+            } catch (Throwable e) {
+                throw new RuntimeException(algorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testKat() throws Exception {
+        Collection<SecretKey> secretKeys = importDefaultKatSecretKeys();
+        Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                Key key = getDecryptionKey(algorithm, secretKeys, keyPairs);
+                KatVector testVector = KAT_VECTORS.get(algorithm);
+                assertNotNull(testVector);
+                Cipher cipher = Cipher.getInstance(algorithm, provider);
+                cipher.init(Cipher.DECRYPT_MODE, key, testVector.params);
+                byte[] actualPlaintext = cipher.doFinal(testVector.ciphertext);
+                byte[] expectedPlaintext = testVector.plaintext;
+                if ("RSA/ECB/NoPadding".equalsIgnoreCase(algorithm)) {
+                    // RSA decryption without padding left-pads resulting plaintext with NUL bytes
+                    // to the length of RSA modulus.
+                    int modulusLengthBytes =
+                            (((RSAKey) key).getModulus().bitLength() + 7) / 8;
+                    expectedPlaintext = TestUtils.leftPadWithZeroBytes(
+                            expectedPlaintext, modulusLengthBytes);
+                }
+                MoreAsserts.assertEquals(expectedPlaintext, actualPlaintext);
+                if (!isRandomizedEncryption(algorithm)) {
+                    // Deterministic encryption: ciphertext depends only on plaintext and input
+                    // parameters. Assert that encrypting the plaintext results in the same
+                    // ciphertext as in the test vector.
+                    key = getEncryptionKey(algorithm, secretKeys, keyPairs);
+                    cipher = Cipher.getInstance(algorithm, provider);
+                    cipher.init(Cipher.ENCRYPT_MODE, key, testVector.params);
+                    byte[] actualCiphertext = cipher.doFinal(testVector.plaintext);
+                    MoreAsserts.assertEquals(testVector.ciphertext, actualCiphertext);
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    private static boolean isRandomizedEncryption(String transformation) {
+        String transformationUpperCase = transformation.toUpperCase(Locale.US);
+        return (transformationUpperCase.endsWith("/PKCS1PADDING"))
+                || (transformationUpperCase.contains("OAEP"));
+    }
+
+    public void testInitDecryptFailsWhenNotAuthorizedToDecrypt() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good, KeyProperties.PURPOSE_ENCRYPT).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricFailsWhenNotAuthorizedToEncrypt() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good, KeyProperties.PURPOSE_DECRYPT).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptAsymmetricIgnoresAuthorizedPurposes() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(transformation)) {
+                continue;
+            }
+
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good, 0).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitDecryptFailsWhenBlockModeNotAuthorized() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            String transformationUpperCase = transformation.toUpperCase(Locale.US);
+            if (transformationUpperCase.startsWith("RSA/")) {
+                // Block modes do not apply
+                continue;
+            }
+            String authorizedBlockMode =
+                    (transformationUpperCase.contains("/CBC/")) ? "CTR" : "CBC";
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good).setBlockModes(authorizedBlockMode).build());
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Failed for " + transformation + " when authorized only for "
+                                + authorizedBlockMode,
+                        e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricFailsWhenBlockModeNotAuthorized() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+
+            String transformationUpperCase = transformation.toUpperCase(Locale.US);
+            if (transformationUpperCase.startsWith("RSA/")) {
+                // Block modes do not apply
+                continue;
+            }
+            String authorizedBlockMode =
+                    (transformationUpperCase.contains("/CBC/")) ? "CTR" : "CBC";
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good).setBlockModes(authorizedBlockMode).build());
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Failed for " + transformation + " when authorized only for "
+                                + authorizedBlockMode,
+                        e);
+            }
+        }
+    }
+
+    public void testInitEncryptAsymmetricIgnoresAuthorizedBlockModes() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(transformation)) {
+                continue;
+            }
+
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good).setBlockModes().build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitDecryptFailsWhenDigestNotAuthorized() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            String transformationUpperCase = transformation.toUpperCase(Locale.US);
+            if ((transformationUpperCase.endsWith("/NOPADDING"))
+                    || (transformationUpperCase.endsWith("/PKCS1PADDING"))
+                    || (transformationUpperCase.endsWith("/PKCS7PADDING"))) {
+                // Digest not used
+                continue;
+            }
+            String authorizedDigest =
+                    (transformationUpperCase.contains("SHA-256")) ? "SHA-512" : "SHA-256";
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good).setDigests(authorizedDigest).build());
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Failed for " + transformation + " when authorized only for "
+                                + authorizedDigest,
+                        e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricFailsWhenDigestNotAuthorized() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+            String transformationUpperCase = transformation.toUpperCase(Locale.US);
+            if ((transformationUpperCase.endsWith("/NOPADDING"))
+                    || (transformationUpperCase.endsWith("/PKCS1PADDING"))
+                    || (transformationUpperCase.endsWith("/PKCS7PADDING"))) {
+                // Digest not used
+                continue;
+            }
+            String authorizedDigest =
+                    (transformationUpperCase.contains("SHA-256")) ? "SHA-512" : "SHA-256";
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good).setDigests(authorizedDigest).build());
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Failed for " + transformation + " when authorized only for "
+                                + authorizedDigest,
+                        e);
+            }
+        }
+    }
+
+    public void testInitEncryptAsymmetricIgnoresAuthorizedDigests() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good).setDigests().build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitDecryptFailsWhenPaddingSchemeNotAuthorized() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            String transformationUpperCase = transformation.toUpperCase(Locale.US);
+            String authorizedPaddingScheme;
+            if (transformationUpperCase.startsWith("RSA/")) {
+                authorizedPaddingScheme = transformationUpperCase.contains("PKCS1PADDING")
+                        ? "OAEPPadding" : "PKCS1Padding";
+            } else {
+                authorizedPaddingScheme = transformationUpperCase.contains("PKCS1PADDING")
+                        ? "NoPadding" : "PKCS1Padding";
+            }
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good)
+                                .setEncryptionPaddings(authorizedPaddingScheme)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Failed for " + transformation + " when authorized only for "
+                                + authorizedPaddingScheme,
+                        e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricFailsWhenPaddingSchemeNotAuthorized() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+            String transformationUpperCase = transformation.toUpperCase(Locale.US);
+            String authorizedPaddingScheme;
+            if (transformationUpperCase.startsWith("RSA/")) {
+                authorizedPaddingScheme = transformationUpperCase.contains("PKCS1PADDING")
+                        ? "OAEPPadding" : "PKCS1Padding";
+            } else {
+                authorizedPaddingScheme = transformationUpperCase.contains("PKCS1PADDING")
+                        ? "NoPadding" : "PKCS1Padding";
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good)
+                                .setEncryptionPaddings(authorizedPaddingScheme)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        "Failed for " + transformation + " when authorized only for "
+                                + authorizedPaddingScheme,
+                        e);
+            }
+        }
+    }
+
+    public void testInitEncryptAsymmetricIgnoresAuthorizedPaddingSchemes() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good)
+                                .setEncryptionPaddings()
+                                .setSignaturePaddings()
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitDecryptFailsWhenKeyNotYetValid() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricFailsWhenKeyNotYetValid() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptAsymmetricIgnoresThatKeyNotYetValid() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitDecryptFailsWhenKeyNoLongerValidForConsumption() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForConsumptionEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitDecryptIgnoresThatKeyNoLongerValidForOrigination() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            try {
+                assertInitDecryptSucceeds(transformation, good.build());
+                assertInitDecryptSucceeds(transformation,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForOriginationEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricFailsWhenKeyNoLongerValidForOrigination() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptThrowsInvalidKeyException(transformation,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForOriginationEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptSymmetricIgnoresThatKeyNoLongerValidForConsumption()
+            throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (!isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForConsumptionEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    public void testInitEncryptAsymmetricIgnoresThatKeyNoLongerValid() throws Exception {
+        KeyProtection.Builder good = GOOD_IMPORT_PARAMS_BUILDER;
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String transformation : EXPECTED_ALGORITHMS) {
+            if (isSymmetric(transformation)) {
+                continue;
+            }
+            try {
+                assertInitEncryptSucceeds(transformation, good.build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForOriginationEnd(badEndDate)
+                                .build());
+                assertInitEncryptSucceeds(transformation,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForConsumptionEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + transformation, e);
+            }
+        }
+    }
+
+    private AlgorithmParameterSpec getWorkingDecryptionParameterSpec(String transformation) {
+        String transformationUpperCase = transformation.toUpperCase();
+        if (transformationUpperCase.startsWith("RSA/")) {
+            return null;
+        } else if (transformationUpperCase.startsWith("AES/")) {
+            if (transformationUpperCase.startsWith("AES/ECB")) {
+                return null;
+            } else if ((transformationUpperCase.startsWith("AES/CBC"))
+                    || (transformationUpperCase.startsWith("AES/CTR"))) {
+                return new IvParameterSpec(
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16});
+            } else if (transformationUpperCase.startsWith("AES/GCM")) {
+                return new GCMParameterSpec(
+                        128,
+                        new byte[] {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12});
+            }
+        }
+
+        throw new IllegalArgumentException("Unsupported transformation: " + transformation);
+    }
+
+    private void assertInitDecryptSucceeds(String transformation, KeyProtection importParams)
+            throws Exception {
+        Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
+        Key key = importDefaultKatDecryptionKey(transformation, importParams);
+        AlgorithmParameterSpec params = getWorkingDecryptionParameterSpec(transformation);
+        cipher.init(Cipher.DECRYPT_MODE, key, params);
+    }
+
+    private void assertInitDecryptThrowsInvalidKeyException(
+            String transformation, KeyProtection importParams) throws Exception {
+        Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
+        Key key = importDefaultKatDecryptionKey(transformation, importParams);
+        AlgorithmParameterSpec params = getWorkingDecryptionParameterSpec(transformation);
+        try {
+            cipher.init(Cipher.DECRYPT_MODE, key, params);
+            fail("InvalidKeyException should have been thrown");
+        } catch (InvalidKeyException expected) {}
+    }
+
+    private void assertInitEncryptSucceeds(String transformation, KeyProtection importParams)
+            throws Exception {
+        Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
+        Key key = importDefaultKatEncryptionKey(transformation, importParams);
+        cipher.init(Cipher.ENCRYPT_MODE, key);
+    }
+
+    private void assertInitEncryptThrowsInvalidKeyException(
+            String transformation, KeyProtection importParams) throws Exception {
+        Cipher cipher = Cipher.getInstance(transformation, EXPECTED_PROVIDER_NAME);
+        Key key = importDefaultKatEncryptionKey(transformation, importParams);
+        try {
+            cipher.init(Cipher.ENCRYPT_MODE, key);
+            fail("InvalidKeyException should have been thrown");
+        } catch (InvalidKeyException expected) {}
+    }
+
+    private Key importDefaultKatEncryptionKey(String transformation,
+            KeyProtection importParams) throws Exception {
+        String transformationUpperCase = transformation.toUpperCase();
+        if (transformationUpperCase.startsWith("RSA/")) {
+            return TestUtils.importIntoAndroidKeyStore("testRsa",
+                    TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key2_pkcs8),
+                    TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key2_cert),
+                    importParams).getPublic();
+        } else if (transformationUpperCase.startsWith("AES/")) {
+            return TestUtils.importIntoAndroidKeyStore("testAes",
+                    new SecretKeySpec(AES_KAT_KEY_BYTES, "AES"),
+                    importParams);
+        } else {
+            throw new IllegalArgumentException("Unsupported transformation: " + transformation);
+        }
+    }
+
+    private Key importDefaultKatDecryptionKey(String transformation,
+            KeyProtection importParams) throws Exception {
+        String transformationUpperCase = transformation.toUpperCase();
+        if (transformationUpperCase.startsWith("RSA/")) {
+            return TestUtils.importIntoAndroidKeyStore("testRsa",
+                    TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key2_pkcs8),
+                    TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key2_cert),
+                    importParams).getPrivate();
+        } else if (transformationUpperCase.startsWith("AES/")) {
+            return TestUtils.importIntoAndroidKeyStore("testAes",
+                    new SecretKeySpec(AES_KAT_KEY_BYTES, "AES"),
+                    importParams);
+        } else {
+            throw new IllegalArgumentException("Unsupported transformation: " + transformation);
+        }
+    }
+
+    private static Key getEncryptionKey(String transformation,
+            Iterable<SecretKey> secretKeys,
+            Iterable<KeyPair> keyPairs) {
+        String transformationUpperCase = transformation.toUpperCase();
+        if (transformationUpperCase.startsWith("RSA/")) {
+            return TestUtils.getKeyPairForKeyAlgorithm("RSA", keyPairs).getPublic();
+        } else if (transformationUpperCase.startsWith("AES/")) {
+            return TestUtils.getKeyForKeyAlgorithm("AES", secretKeys);
+        } else {
+            throw new IllegalArgumentException("Unsupported transformation: " + transformation);
+        }
+    }
+
+    private static Key getDecryptionKey(String transformation,
+            Iterable<SecretKey> secretKeys,
+            Iterable<KeyPair> keyPairs) {
+        String transformationUpperCase = transformation.toUpperCase();
+        if (transformationUpperCase.startsWith("RSA/")) {
+            return TestUtils.getKeyPairForKeyAlgorithm("RSA", keyPairs).getPrivate();
+        } else if (transformationUpperCase.startsWith("AES/")) {
+            return TestUtils.getKeyForKeyAlgorithm("AES", secretKeys);
+        } else {
+            throw new IllegalArgumentException("Unsupported transformation: " + transformation);
+        }
+    }
+
+    private Collection<KeyPair> getDefaultKatKeyPairs() throws Exception {
+        return Arrays.asList(
+                new KeyPair(
+                        TestUtils.getRawResX509Certificate(
+                                getContext(), R.raw.rsa_key2_cert).getPublicKey(),
+                        TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key2_pkcs8)));
+    }
+
+    private Collection<KeyPair> importDefaultKatKeyPairs() throws Exception {
+        return Arrays.asList(
+                TestUtils.importIntoAndroidKeyStore(
+                        "testRsa",
+                        TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key2_pkcs8),
+                        TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key2_cert),
+                        new KeyProtection.Builder(
+                                KeyProperties.PURPOSE_DECRYPT)
+                                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                                .setRandomizedEncryptionRequired(false) // due to PADDING_NONE
+                                .setDigests(KeyProperties.DIGEST_NONE) // due to OAEP
+                                .build()));
+    }
+
+    private Collection<SecretKey> getDefaultKatSecretKeys() throws Exception {
+        return Arrays.asList((SecretKey) new SecretKeySpec(AES_KAT_KEY_BYTES, "AES"));
+    }
+
+    private Collection<SecretKey> importDefaultKatSecretKeys() throws Exception {
+        return Arrays.asList(
+                TestUtils.importIntoAndroidKeyStore("testAes",
+                        new SecretKeySpec(AES_KAT_KEY_BYTES, "AES"),
+                        new KeyProtection.Builder(
+                                KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT)
+                                .setBlockModes(KeyProperties.BLOCK_MODE_ECB,
+                                        KeyProperties.BLOCK_MODE_CBC,
+                                        KeyProperties.BLOCK_MODE_CTR,
+                                        KeyProperties.BLOCK_MODE_GCM)
+                                .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                                .setRandomizedEncryptionRequired(false) // due to ECB
+                                .build()));
+    }
+
+    private static boolean isSymmetric(String transformation) {
+        return transformation.toUpperCase(Locale.US).startsWith("AES/");
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
index 2330209..271b40b 100644
--- a/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/ECDSASignatureTest.java
@@ -109,10 +109,7 @@
         assertTrue(signature.verify(sigBytes));
 
         // Assert that the message is left-padded with zero bits
-        byte[] fullLengthMessage = new byte[keySizeBits / 8];
-        System.arraycopy(message, 0,
-                fullLengthMessage, fullLengthMessage.length - message.length,
-                message.length);
+        byte[] fullLengthMessage = TestUtils.leftPadWithZeroBytes(message, keySizeBits / 8);
         signature.update(fullLengthMessage);
         assertTrue(signature.verify(sigBytes));
     }
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
new file mode 100644
index 0000000..b51d300
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyGeneratorTest.java
@@ -0,0 +1,475 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.keystore.cts;
+
+import android.security.keystore.KeyGenParameterSpec;
+import android.security.keystore.KeyInfo;
+import android.security.keystore.KeyProperties;
+import android.test.MoreAsserts;
+
+import junit.framework.TestCase;
+
+import java.security.InvalidAlgorithmParameterException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.ECGenParameterSpec;
+import java.security.Provider.Service;
+import java.security.SecureRandom;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+
+public class KeyGeneratorTest extends TestCase {
+    private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME;
+
+    private static final String[] EXPECTED_ALGORITHMS = {
+        "AES",
+        "HmacSHA1",
+        "HmacSHA224",
+        "HmacSHA256",
+        "HmacSHA384",
+        "HmacSHA512",
+    };
+
+    private static final Map<String, Integer> DEFAULT_KEY_SIZES =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    static {
+        DEFAULT_KEY_SIZES.put("AES", 128);
+        DEFAULT_KEY_SIZES.put("HmacSHA1", 160);
+        DEFAULT_KEY_SIZES.put("HmacSHA224", 224);
+        DEFAULT_KEY_SIZES.put("HmacSHA256", 256);
+        DEFAULT_KEY_SIZES.put("HmacSHA384", 384);
+        DEFAULT_KEY_SIZES.put("HmacSHA512", 512);
+    }
+
+    private static final int[] AES_SUPPORTED_KEY_SIZES = new int[] {128, 192, 256};
+
+    public void testAlgorithmList() {
+        // Assert that Android Keystore Provider exposes exactly the expected KeyGenerator
+        // algorithms. We don't care whether the algorithms are exposed via aliases, as long as
+        // canonical names of algorithms are accepted. If the Provider exposes extraneous
+        // algorithms, it'll be caught because it'll have to expose at least one Service for such an
+        // algorithm, and this Service's algorithm will not be in the expected set.
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        Set<Service> services = provider.getServices();
+        Set<String> actualAlgsLowerCase = new HashSet<String>();
+        Set<String> expectedAlgsLowerCase = new HashSet<String>(
+                Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
+        for (Service service : services) {
+            if ("KeyGenerator".equalsIgnoreCase(service.getType())) {
+                String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+                actualAlgsLowerCase.add(algLowerCase);
+            }
+        }
+
+        TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+                expectedAlgsLowerCase.toArray(new String[0]));
+    }
+
+    public void testGenerateWithoutInitThrowsIllegalStateException() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.generateKey();
+                    fail();
+                } catch (IllegalStateException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithKeySizeThrowsUnsupportedOperationException() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                int keySizeBits = DEFAULT_KEY_SIZES.get(algorithm);
+                try {
+                    keyGenerator.init(keySizeBits);
+                    fail();
+                } catch (UnsupportedOperationException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithKeySizeAndSecureRandomThrowsUnsupportedOperationException()
+            throws Exception {
+        SecureRandom rng = new SecureRandom();
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                int keySizeBits = DEFAULT_KEY_SIZES.get(algorithm);
+                try {
+                    keyGenerator.init(keySizeBits, rng);
+                    fail();
+                } catch (UnsupportedOperationException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithNullAlgParamsThrowsInvalidAlgorithmParameterException()
+            throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init((AlgorithmParameterSpec) null);
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithNullAlgParamsAndSecureRandomThrowsInvalidAlgorithmParameterException()
+            throws Exception {
+        SecureRandom rng = new SecureRandom();
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init((AlgorithmParameterSpec) null, rng);
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithAlgParamsAndNullSecureRandom()
+            throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                keyGenerator.init(getWorkingSpec().build(), (SecureRandom) null);
+                // Check that generateKey doesn't fail either, just in case null SecureRandom
+                // causes trouble there.
+                keyGenerator.generateKey();
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnsupportedAlgParamsTypeThrowsInvalidAlgorithmParameterException()
+            throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init(new ECGenParameterSpec("secp256r1"));
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testDefaultKeySize() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                int expectedSizeBits = DEFAULT_KEY_SIZES.get(algorithm);
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                keyGenerator.init(getWorkingSpec().build());
+                SecretKey key = keyGenerator.generateKey();
+                assertEquals(expectedSizeBits, TestUtils.getKeyInfo(key).getKeySize());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testAesKeySupportedSizes() throws Exception {
+        KeyGenerator keyGenerator = getKeyGenerator("AES");
+        KeyGenParameterSpec.Builder goodSpec = getWorkingSpec();
+        CountingSecureRandom rng = new CountingSecureRandom();
+        for (int i = -16; i <= 512; i++) {
+            try {
+                rng.resetCounters();
+                KeyGenParameterSpec spec;
+                if (i >= 0) {
+                    spec = TestUtils.buildUpon(goodSpec.setKeySize(i)).build();
+                } else {
+                    try {
+                        spec = TestUtils.buildUpon(goodSpec.setKeySize(i)).build();
+                        fail();
+                    } catch (IllegalArgumentException expected) {
+                        continue;
+                    }
+                }
+                rng.resetCounters();
+                if (TestUtils.contains(AES_SUPPORTED_KEY_SIZES, i)) {
+                    keyGenerator.init(spec, rng);
+                    SecretKey key = keyGenerator.generateKey();
+                    assertEquals(i, TestUtils.getKeyInfo(key).getKeySize());
+                    assertEquals((i + 7) / 8, rng.getOutputSizeBytes());
+                } else {
+                    try {
+                        keyGenerator.init(spec, rng);
+                        fail();
+                    } catch (InvalidAlgorithmParameterException expected) {}
+                    assertEquals(0, rng.getOutputSizeBytes());
+                }
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed to key size " + i, e);
+            }
+        }
+    }
+
+    public void testHmacKeySupportedSizes() throws Exception {
+        CountingSecureRandom rng = new CountingSecureRandom();
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            if (!TestUtils.isHmacAlgorithm(algorithm)) {
+                continue;
+            }
+
+            for (int i = -16; i <= 1024; i++) {
+                try {
+                    rng.resetCounters();
+                    KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                    KeyGenParameterSpec spec;
+                    if (i >= 0) {
+                        spec = getWorkingSpec().setKeySize(i).build();
+                    } else {
+                        try {
+                            spec = getWorkingSpec().setKeySize(i).build();
+                            fail();
+                        } catch (IllegalArgumentException expected) {
+                            continue;
+                        }
+                    }
+                    if ((i > 0) && ((i % 8 ) == 0)) {
+                        keyGenerator.init(spec, rng);
+                        SecretKey key = keyGenerator.generateKey();
+                        assertEquals(i, TestUtils.getKeyInfo(key).getKeySize());
+                        assertEquals((i + 7) / 8, rng.getOutputSizeBytes());
+                    } else {
+                        try {
+                            keyGenerator.init(spec, rng);
+                            fail();
+                        } catch (InvalidAlgorithmParameterException expected) {}
+                        assertEquals(0, rng.getOutputSizeBytes());
+                    }
+                } catch (Throwable e) {
+                    throw new RuntimeException(
+                            "Failed for " + algorithm + " with key size " + i, e);
+                }
+            }
+        }
+    }
+
+    public void testInitWithUnknownBlockModeFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init(getWorkingSpec().setBlockModes("weird").build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnknownEncryptionPaddingFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init(getWorkingSpec().setEncryptionPaddings("weird").build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithSignaturePaddingFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init(getWorkingSpec()
+                            .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+                            .build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnknownDigestFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    String[] digests;
+                    if (TestUtils.isHmacAlgorithm(algorithm)) {
+                        // The digest from HMAC key algorithm must be specified in the list of
+                        // authorized digests (if the list if provided).
+                        digests = new String[] {algorithm, "weird"};
+                    } else {
+                        digests = new String[] {"weird"};
+                    }
+                    keyGenerator.init(getWorkingSpec().setDigests(digests).build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithKeyAlgorithmDigestMissingFromAuthorizedDigestFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            if (!TestUtils.isHmacAlgorithm(algorithm)) {
+                continue;
+            }
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+
+                // Authorized for digest(s) none of which is the one implied by key algorithm.
+                try {
+                    String digest = TestUtils.getHmacAlgorithmDigest(algorithm);
+                    String anotherDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest)
+                            ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256;
+                    keyGenerator.init(getWorkingSpec().setDigests(anotherDigest).build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+
+                // Authorized for empty set of digests
+                try {
+                    keyGenerator.init(getWorkingSpec().setDigests().build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitRandomizedEncryptionRequiredButViolatedFails() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                try {
+                    keyGenerator.init(getWorkingSpec(
+                            KeyProperties.PURPOSE_ENCRYPT)
+                            .setBlockModes(KeyProperties.BLOCK_MODE_ECB)
+                            .build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testGenerateHonorsAuthorizations() throws Exception {
+        Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS);
+        Date keyValidityForOriginationEnd =
+                new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS);
+        Date keyValidityForConsumptionEnd =
+                new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                String[] blockModes =
+                        new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC};
+                String[] encryptionPaddings =
+                        new String[] {KeyProperties.ENCRYPTION_PADDING_PKCS7,
+                                KeyProperties.ENCRYPTION_PADDING_NONE};
+                String[] digests;
+                int purposes;
+                if (TestUtils.isHmacAlgorithm(algorithm)) {
+                    String digest = TestUtils.getHmacAlgorithmDigest(algorithm);
+                    String anotherDigest = KeyProperties.DIGEST_SHA256.equalsIgnoreCase(digest)
+                            ? KeyProperties.DIGEST_SHA512 : KeyProperties.DIGEST_SHA256;
+                    digests = new String[] {anotherDigest, digest};
+                    purposes = KeyProperties.PURPOSE_SIGN;
+                } else {
+                    digests = new String[] {KeyProperties.DIGEST_SHA384};
+                    purposes = KeyProperties.PURPOSE_DECRYPT;
+                }
+                KeyGenerator keyGenerator = getKeyGenerator(algorithm);
+                keyGenerator.init(getWorkingSpec(purposes)
+                        .setBlockModes(blockModes)
+                        .setEncryptionPaddings(encryptionPaddings)
+                        .setDigests(digests)
+                        .setKeyValidityStart(keyValidityStart)
+                        .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
+                        .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
+                        .build());
+                SecretKey key = keyGenerator.generateKey();
+                assertEquals(algorithm, key.getAlgorithm());
+
+                KeyInfo keyInfo = TestUtils.getKeyInfo(key);
+                assertEquals(purposes, keyInfo.getPurposes());
+                TestUtils.assertContentsInAnyOrder(
+                        Arrays.asList(blockModes), keyInfo.getBlockModes());
+                TestUtils.assertContentsInAnyOrder(
+                        Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings());
+                TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests());
+                MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+                assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+                assertEquals(keyValidityForOriginationEnd,
+                        keyInfo.getKeyValidityForOriginationEnd());
+                assertEquals(keyValidityForConsumptionEnd,
+                        keyInfo.getKeyValidityForConsumptionEnd());
+                assertFalse(keyInfo.isUserAuthenticationRequired());
+                assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    private static KeyGenParameterSpec.Builder getWorkingSpec() {
+        return getWorkingSpec(0);
+    }
+
+    private static KeyGenParameterSpec.Builder getWorkingSpec(int purposes) {
+        return new KeyGenParameterSpec.Builder("test1", purposes);
+    }
+
+    private static KeyGenerator getKeyGenerator(String algorithm) throws NoSuchAlgorithmException,
+            NoSuchProviderException {
+        return KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+    }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index e3540b4..3d3c909 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -34,7 +34,10 @@
 import java.security.NoSuchProviderException;
 import java.security.Principal;
 import java.security.PrivateKey;
+import java.security.Provider;
 import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Provider.Service;
 import java.security.cert.Certificate;
 import java.security.cert.X509Certificate;
 import java.security.interfaces.ECKey;
@@ -48,7 +51,12 @@
 import java.util.ArrayList;
 import java.util.Arrays;
 import java.util.Date;
+import java.util.HashSet;
 import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Set;
+import java.util.TreeMap;
 import java.util.concurrent.Callable;
 import java.util.concurrent.ExecutorService;
 import java.util.concurrent.Executors;
@@ -99,6 +107,20 @@
     private static final Date DEFAULT_CERT_NOT_BEFORE = new Date(0L); // Jan 1 1970
     private static final Date DEFAULT_CERT_NOT_AFTER = new Date(2461449600000L); // Jan 1 2048
 
+    private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME;
+
+    private static final String[] EXPECTED_ALGORITHMS = {
+        "EC",
+        "RSA",
+    };
+
+    private static final Map<String, Integer> DEFAULT_KEY_SIZES =
+            new TreeMap<>(String.CASE_INSENSITIVE_ORDER);
+    static {
+        DEFAULT_KEY_SIZES.put("EC", 256);
+        DEFAULT_KEY_SIZES.put("RSA", 2048);
+    }
+
     @Override
     protected void setUp() throws Exception {
         super.setUp();
@@ -107,6 +129,29 @@
         mKeyStore.load(null, null);
     }
 
+    public void testAlgorithmList() {
+        // Assert that Android Keystore Provider exposes exactly the expected KeyPairGenerator
+        // algorithms. We don't care whether the algorithms are exposed via aliases, as long as
+        // canonical names of algorithms are accepted. If the Provider exposes extraneous
+        // algorithms, it'll be caught because it'll have to expose at least one Service for such an
+        // algorithm, and this Service's algorithm will not be in the expected set.
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        Set<Service> services = provider.getServices();
+        Set<String> actualAlgsLowerCase = new HashSet<String>();
+        Set<String> expectedAlgsLowerCase = new HashSet<String>(
+                Arrays.asList(TestUtils.toLowerCase(EXPECTED_ALGORITHMS)));
+        for (Service service : services) {
+            if ("KeyPairGenerator".equalsIgnoreCase(service.getType())) {
+                String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+                actualAlgsLowerCase.add(algLowerCase);
+            }
+        }
+
+        TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+                expectedAlgsLowerCase.toArray(new String[0]));
+    }
+
     public void testInitialize_LegacySpec() throws Exception {
         @SuppressWarnings("deprecation")
         KeyPairGeneratorSpec spec = new KeyPairGeneratorSpec.Builder(getContext())
@@ -164,6 +209,143 @@
         }
     }
 
+    public void testDefaultKeySize() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                int expectedSizeBits = DEFAULT_KEY_SIZES.get(algorithm);
+                KeyPairGenerator generator = getGenerator(algorithm);
+                generator.initialize(getWorkingSpec().build());
+                KeyPair keyPair = generator.generateKeyPair();
+                assertEquals(expectedSizeBits,
+                        TestUtils.getKeyInfo(keyPair.getPrivate()).getKeySize());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnknownBlockModeFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyPairGenerator generator = getGenerator(algorithm);
+                try {
+                    generator.initialize(getWorkingSpec().setBlockModes("weird").build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnknownEncryptionPaddingFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyPairGenerator generator = getGenerator(algorithm);
+                try {
+                    generator.initialize(getWorkingSpec().setEncryptionPaddings("weird").build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnknownSignaturePaddingFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyPairGenerator generator = getGenerator(algorithm);
+                try {
+                    generator.initialize(getWorkingSpec().setSignaturePaddings("weird").build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitWithUnknownDigestFails() {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyPairGenerator generator = getGenerator(algorithm);
+                try {
+                    generator.initialize(getWorkingSpec().setDigests("weird").build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitRandomizedEncryptionRequiredButViolatedFails() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyPairGenerator generator = getGenerator(algorithm);
+                try {
+                    generator.initialize(getWorkingSpec(
+                            KeyProperties.PURPOSE_ENCRYPT)
+                            .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+                            .build());
+                    fail();
+                } catch (InvalidAlgorithmParameterException expected) {}
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testGenerateHonorsAuthorizations() throws Exception {
+        Date keyValidityStart = new Date(System.currentTimeMillis() - TestUtils.DAY_IN_MILLIS);
+        Date keyValidityForOriginationEnd =
+                new Date(System.currentTimeMillis() + TestUtils.DAY_IN_MILLIS);
+        Date keyValidityForConsumptionEnd =
+                new Date(System.currentTimeMillis() + 3 * TestUtils.DAY_IN_MILLIS);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                String[] blockModes =
+                        new String[] {KeyProperties.BLOCK_MODE_GCM, KeyProperties.BLOCK_MODE_CBC};
+                String[] encryptionPaddings =
+                        new String[] {KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+                                KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1};
+                String[] digests =
+                        new String[] {KeyProperties.DIGEST_SHA256, KeyProperties.DIGEST_SHA1};
+                int purposes = KeyProperties.PURPOSE_SIGN | KeyProperties.PURPOSE_ENCRYPT;
+                KeyPairGenerator generator = getGenerator(algorithm);
+                generator.initialize(getWorkingSpec(purposes)
+                        .setBlockModes(blockModes)
+                        .setEncryptionPaddings(encryptionPaddings)
+                        .setDigests(digests)
+                        .setKeyValidityStart(keyValidityStart)
+                        .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
+                        .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
+                        .build());
+                KeyPair keyPair = generator.generateKeyPair();
+                assertEquals(algorithm, keyPair.getPrivate().getAlgorithm());
+
+                KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+                assertEquals(purposes, keyInfo.getPurposes());
+                TestUtils.assertContentsInAnyOrder(
+                        Arrays.asList(blockModes), keyInfo.getBlockModes());
+                TestUtils.assertContentsInAnyOrder(
+                        Arrays.asList(encryptionPaddings), keyInfo.getEncryptionPaddings());
+                TestUtils.assertContentsInAnyOrder(Arrays.asList(digests), keyInfo.getDigests());
+                MoreAsserts.assertEmpty(Arrays.asList(keyInfo.getSignaturePaddings()));
+                assertEquals(keyValidityStart, keyInfo.getKeyValidityStart());
+                assertEquals(keyValidityForOriginationEnd,
+                        keyInfo.getKeyValidityForOriginationEnd());
+                assertEquals(keyValidityForConsumptionEnd,
+                        keyInfo.getKeyValidityForConsumptionEnd());
+                assertFalse(keyInfo.isUserAuthenticationRequired());
+                assertFalse(keyInfo.isUserAuthenticationRequirementEnforcedBySecureHardware());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
     @SuppressWarnings("deprecation")
     public void testGenerate_EC_LegacySpec() throws Exception {
         // There are three legacy ways to generate an EC key pair using Android Keystore
@@ -1258,4 +1440,12 @@
                 + "Expected one of " + Arrays.asList(expected)
                 + ", actual: <" + actual + ">");
     }
+
+    private KeyGenParameterSpec.Builder getWorkingSpec() {
+        return getWorkingSpec(0);
+    }
+
+    private KeyGenParameterSpec.Builder getWorkingSpec(int purposes) {
+        return new KeyGenParameterSpec.Builder(TEST_ALIAS_1, purposes);
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/MacTest.java b/tests/tests/keystore/src/android/keystore/cts/MacTest.java
index d654e3a..8dcf47e 100644
--- a/tests/tests/keystore/src/android/keystore/cts/MacTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/MacTest.java
@@ -37,6 +37,9 @@
 import javax.crypto.SecretKey;
 import javax.crypto.spec.SecretKeySpec;
 
+/**
+ * Tests for algorithm-agnostic functionality of MAC implementations backed by Android Keystore.
+ */
 public class MacTest extends TestCase {
 
     private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
@@ -95,7 +98,7 @@
     }
 
 
-    private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+    private static final long DAY_IN_MILLIS = TestUtils.DAY_IN_MILLIS;
 
     public void testAlgorithmList() {
         // Assert that Android Keystore Provider exposes exactly the expected MAC algorithms. We
@@ -131,13 +134,13 @@
                 Mac mac = Mac.getInstance(algorithm);
                 mac.init(key);
                 assertSame(provider, mac.getProvider());
-            } catch (Exception e) {
+            } catch (Throwable e) {
                 throw new RuntimeException(algorithm + " failed", e);
             }
         }
     }
 
-    public void testGeneratedSignatureVerifies() throws Exception {
+    public void testMacGeneratedByAndroidKeyStoreVerifiesByAndroidKeyStore() throws Exception {
         SecretKey key = importDefaultKatKey();
 
         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
@@ -145,18 +148,66 @@
         for (String algorithm : EXPECTED_ALGORITHMS) {
             try {
                 // Generate a MAC
-                Mac mac = Mac.getInstance(algorithm);
+                Mac mac = Mac.getInstance(algorithm, provider);
                 mac.init(key);
                 byte[] message = "This is a test".getBytes("UTF-8");
                 byte[] macBytes = mac.doFinal(message);
 
-                assertMacVerifiesOneShot(algorithm, key, message, macBytes);
-            } catch (Exception e) {
+                assertMacVerifiesOneShot(algorithm, provider, key, message, macBytes);
+            } catch (Throwable e) {
                 throw new RuntimeException(algorithm + " failed", e);
             }
         }
     }
 
+    public void testMacGeneratedByAndroidKeyStoreVerifiesByHighestPriorityProvider()
+            throws Exception {
+        SecretKey key = getDefaultKatKey();
+        SecretKey keystoreKey = importDefaultKatKey();
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                // Generate a MAC
+                Mac mac = Mac.getInstance(algorithm, provider);
+                mac.init(keystoreKey);
+                byte[] message = "This is a test".getBytes("UTF-8");
+                byte[] macBytes = mac.doFinal(message);
+
+                assertMacVerifiesOneShot(algorithm, key, message, macBytes);
+            } catch (Throwable e) {
+                throw new RuntimeException(algorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testMacGeneratedByHighestPriorityProviderVerifiesByAndroidKeyStore()
+            throws Exception {
+        SecretKey key = getDefaultKatKey();
+        SecretKey keystoreKey = importDefaultKatKey();
+
+        Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(keystoreProvider);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            Provider signingProvider = null;
+            try {
+                // Generate a MAC
+                Mac mac = Mac.getInstance(algorithm);
+                mac.init(key);
+                signingProvider = mac.getProvider();
+                byte[] message = "This is a test".getBytes("UTF-8");
+                byte[] macBytes = mac.doFinal(message);
+
+                assertMacVerifiesOneShot(
+                        algorithm, keystoreProvider, keystoreKey, message, macBytes);
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        algorithm + " failed, signing provider: " + signingProvider, e);
+            }
+        }
+    }
+
     public void testSmallMsgKat() throws Exception {
         SecretKey key = importDefaultKatKey();
         byte[] message = SHORT_MSG_KAT_MESSAGE;
@@ -202,44 +253,88 @@
     }
 
     public void testInitFailsWhenNotAuthorizedToSign() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN);
         int badPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
                 | KeyProperties.PURPOSE_VERIFY;
-        String algorithm = "HmacSHA512";
-        assertInitSucceeds(algorithm, algorithm, good.build());
-        assertInitThrowsInvalidKeyException(algorithm, algorithm,
-                TestUtils.buildUpon(good, badPurposes).build());
+
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParams(algorithm);
+                assertInitSucceeds(algorithm, good.build());
+                assertInitThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good, badPurposes).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     public void testInitFailsWhenDigestNotAuthorized() throws Exception {
-        KeyProtection spec = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build();
-        assertInitSucceeds("HmacSHA384", "HmacSHA384", spec);
-        assertInitThrowsInvalidKeyException("HmacSHA256", "HmacSHA384", spec);
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParams(algorithm);
+                assertInitSucceeds(algorithm, good.build());
+
+                String badKeyAlgorithm = ("HmacSHA256".equalsIgnoreCase(algorithm))
+                        ? "HmacSHA384" : "HmacSHA256";
+                assertInitThrowsInvalidKeyException(algorithm, badKeyAlgorithm, good.build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     public void testInitFailsWhenKeyNotYetValid() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setKeyValidityStart(new Date(System.currentTimeMillis() - DAY_IN_MILLIS));
-        String algorithm = "HmacSHA224";
-        assertInitSucceeds(algorithm, algorithm, good.build());
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParams(algorithm)
+                        .setKeyValidityStart(new Date(System.currentTimeMillis() - DAY_IN_MILLIS));
+                assertInitSucceeds(algorithm, good.build());
 
-        Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
-        assertInitThrowsInvalidKeyException(algorithm, algorithm,
-                TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+                Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+                assertInitThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
-    public void testInitFailsWhenKeyNoLongerValid() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setKeyValidityForOriginationEnd(
-                        new Date(System.currentTimeMillis() + DAY_IN_MILLIS));
-        String algorithm = "HmacSHA1";
-        assertInitSucceeds(algorithm, algorithm, good.build());
+    public void testInitFailsWhenKeyNoLongerValidForOrigination() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParams(algorithm)
+                        .setKeyValidityForOriginationEnd(
+                                new Date(System.currentTimeMillis() + DAY_IN_MILLIS));
+                assertInitSucceeds(algorithm, good.build());
 
-        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
-        assertInitThrowsInvalidKeyException(algorithm, algorithm,
-                TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+                Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+                assertInitThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForOriginationEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitIgnoresThatKeyNoLongerValidForConsumption() throws Exception {
+        for (String algorithm : EXPECTED_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParams(algorithm)
+                        .setKeyValidityForConsumptionEnd(
+                                new Date(System.currentTimeMillis() + DAY_IN_MILLIS));
+                assertInitSucceeds(algorithm, good.build());
+
+                Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+                assertInitSucceeds(algorithm,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForConsumptionEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     private void assertMacVerifiesOneShot(
@@ -247,7 +342,17 @@
             SecretKey key,
             byte[] message,
             byte[] mac) throws Exception {
-        Mac m = Mac.getInstance(algorithm);
+        assertMacVerifiesOneShot(algorithm, null, key, message, mac);
+    }
+
+    private void assertMacVerifiesOneShot(
+            String algorithm,
+            Provider provider,
+            SecretKey key,
+            byte[] message,
+            byte[] mac) throws Exception {
+        Mac m = (provider != null)
+                ? Mac.getInstance(algorithm, provider) : Mac.getInstance(algorithm);
         m.init(key);
         byte[] mac2 = m.doFinal(message);
         if (!Arrays.equals(mac, mac2)) {
@@ -273,7 +378,6 @@
         }
     }
 
-
     private void assertMacVerifiesFedOneByteAtATime(
             String algorithm,
             SecretKey key,
@@ -318,6 +422,11 @@
         }
     }
 
+    private void assertInitSucceeds(String algorithm, KeyProtection keyProtection)
+            throws Exception {
+        assertInitSucceeds(algorithm, algorithm, keyProtection);
+    }
+
     private void assertInitSucceeds(
             String macAlgorithm, String keyAlgorithm, KeyProtection keyProtection)
                     throws Exception {
@@ -326,30 +435,33 @@
         mac.init(key);
     }
 
+    private void assertInitThrowsInvalidKeyException(String algorithm, KeyProtection keyProtection)
+                    throws Exception {
+        assertInitThrowsInvalidKeyException(algorithm, algorithm, keyProtection);
+    }
+
     private void assertInitThrowsInvalidKeyException(
             String macAlgorithm, String keyAlgorithm, KeyProtection keyProtection)
                     throws Exception {
-        assertInitThrowsInvalidKeyException(null, macAlgorithm, keyAlgorithm, keyProtection);
-    }
-
-
-    private void assertInitThrowsInvalidKeyException(
-            String message, String macAlgorithm, String keyAlgorithm,
-            KeyProtection keyProtection) throws Exception {
         SecretKey key = importDefaultKatKey(keyAlgorithm, keyProtection);
         Mac mac = Mac.getInstance(macAlgorithm);
         try {
             mac.init(key);
-            fail(message);
+            fail("InvalidKeyException should have been thrown. MAC algorithm: " + macAlgorithm
+                    + ", key algorithm: " + keyAlgorithm);
         } catch (InvalidKeyException expected) {}
     }
 
+    private SecretKey getDefaultKatKey() {
+        return new SecretKeySpec(KAT_KEY, "HmacSHA1");
+    }
+
     private SecretKey importDefaultKatKey() throws Exception {
         return importDefaultKatKey("HmacSHA1",
                 new KeyProtection.Builder(
                         KeyProperties.PURPOSE_SIGN)
-                        .setDigests(KeyProperties.DIGEST_NONE,
-                                KeyProperties.DIGEST_SHA1, // TODO: Remove these digests
+                        .setDigests(
+                                KeyProperties.DIGEST_SHA1,
                                 KeyProperties.DIGEST_SHA224,
                                 KeyProperties.DIGEST_SHA256,
                                 KeyProperties.DIGEST_SHA384,
@@ -364,4 +476,9 @@
                 new SecretKeySpec(KAT_KEY, keyAlgorithm),
                 keyProtection);
     }
+
+    private static KeyProtection.Builder getWorkingImportParams(
+            @SuppressWarnings("unused") String algorithm) {
+        return new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN);
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
index c53e2c3..ff6985a 100644
--- a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
@@ -41,6 +41,10 @@
 import android.test.AndroidTestCase;
 import android.test.MoreAsserts;
 
+/**
+ * Tests for algorithm-agnostic functionality of {@code Signature} implementations backed by Android
+ * Keystore.
+ */
 public class SignatureTest extends AndroidTestCase {
 
     static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
@@ -321,7 +325,7 @@
                 + "f04e3de1460e60e9be7a42b1ddff0c"));
     }
 
-    private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+    private static final long DAY_IN_MILLIS = TestUtils.DAY_IN_MILLIS;
 
     public void testAlgorithmList() {
         // Assert that Android Keystore Provider exposes exactly the expected signature algorithms.
@@ -356,7 +360,43 @@
                 expectedSigAlgsLowerCase.toArray(new String[0]));
     }
 
-    public void testGeneratedSignatureVerifies() throws Exception {
+    public void testAndroidKeyStoreKeysHandledByAndroidKeyStoreProviderWhenSigning()
+            throws Exception {
+        Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String sigAlgorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyPair keyPair = getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs);
+                Signature signature = Signature.getInstance(sigAlgorithm);
+                signature.initSign(keyPair.getPrivate());
+                assertSame(provider, signature.getProvider());
+            } catch (Throwable e) {
+                throw new RuntimeException(sigAlgorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testAndroidKeyStorePublicKeysAcceptedByHighestPriorityProviderWhenVerifying()
+            throws Exception {
+        Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
+
+        Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(provider);
+        for (String sigAlgorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyPair keyPair = getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs);
+                Signature signature = Signature.getInstance(sigAlgorithm);
+                signature.initVerify(keyPair.getPublic());
+            } catch (Throwable e) {
+                throw new RuntimeException(sigAlgorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testSignatureGeneratedByAndroidKeyStoreVerifiesByAndroidKeyStore()
+            throws Exception {
         Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
 
         Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
@@ -366,9 +406,8 @@
                 KeyPair keyPair = getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs);
 
                 // Generate a signature
-                Signature signature = Signature.getInstance(sigAlgorithm);
+                Signature signature = Signature.getInstance(sigAlgorithm, provider);
                 signature.initSign(keyPair.getPrivate());
-                assertSame(provider, signature.getProvider());
                 byte[] message = "This is a test".getBytes("UTF-8");
                 signature.update(message);
                 byte[] sigBytes = signature.sign();
@@ -376,17 +415,92 @@
                 // Assert that it verifies using our own Provider
                 assertSignatureVerifiesOneShot(
                         sigAlgorithm, provider, keyPair.getPublic(), message, sigBytes);
-
-                // Assert that it verifies using whatever Provider is chosen by JCA by
-                // default for this signature algorithm and public key.
-                assertSignatureVerifiesOneShot(
-                        sigAlgorithm, keyPair.getPublic(), message, sigBytes);
-            } catch (Exception e) {
+            } catch (Throwable e) {
                 throw new RuntimeException(sigAlgorithm + " failed", e);
             }
         }
     }
 
+    public void testSignatureGeneratedByAndroidKeyStoreVerifiesByHighestPriorityProvider()
+            throws Exception {
+        Collection<KeyPair> keyPairs = getDefaultKatKeyPairs();
+        Collection<KeyPair> keystoreKeyPairs = importDefaultKatKeyPairs();
+
+        Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(keystoreProvider);
+        for (String sigAlgorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                PrivateKey keystorePrivateKey =
+                        getKeyPairForSignatureAlgorithm(sigAlgorithm, keystoreKeyPairs)
+                                .getPrivate();
+
+                // Generate a signature
+                Signature signature = Signature.getInstance(sigAlgorithm, keystoreProvider);
+                signature.initSign(keystorePrivateKey);
+                byte[] message = "This is a test".getBytes("UTF-8");
+                signature.update(message);
+                byte[] sigBytes = signature.sign();
+
+                // Assert that it verifies using whatever Provider is chosen by JCA by default for
+                // this signature algorithm and public key.
+                PublicKey publicKey =
+                        getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs).getPublic();
+                Provider verificationProvider;
+                try {
+                    signature = Signature.getInstance(sigAlgorithm);
+                    signature.initVerify(publicKey);
+                    verificationProvider = signature.getProvider();
+                } catch (InvalidKeyException e) {
+                    // No providers support verifying signatures using this algorithm and key.
+                    continue;
+                }
+                assertSignatureVerifiesOneShot(
+                        sigAlgorithm, verificationProvider, publicKey, message, sigBytes);
+            } catch (Throwable e) {
+                throw new RuntimeException(sigAlgorithm + " failed", e);
+            }
+        }
+    }
+
+    public void testSignatureGeneratedByHighestPriorityProviderVerifiesByAndroidKeyStore()
+            throws Exception {
+        Collection<KeyPair> keyPairs = getDefaultKatKeyPairs();
+        Collection<KeyPair> keystoreKeyPairs = importDefaultKatKeyPairs();
+
+        Provider keystoreProvider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+        assertNotNull(keystoreProvider);
+        for (String sigAlgorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            Provider signingProvider = null;
+            try {
+                PrivateKey privateKey =
+                        getKeyPairForSignatureAlgorithm(sigAlgorithm, keyPairs).getPrivate();
+
+                // Generate a signature
+                Signature signature;
+                try {
+                    signature = Signature.getInstance(sigAlgorithm);
+                    signature.initSign(privateKey);
+                    signingProvider = signature.getProvider();
+                } catch (InvalidKeyException e) {
+                    // No providers support signing using this algorithm and key.
+                    continue;
+                }
+                byte[] message = "This is a test".getBytes("UTF-8");
+                signature.update(message);
+                byte[] sigBytes = signature.sign();
+
+                // Assert that the signature verifies using the Android Keystore provider.
+                PublicKey keystorePublicKey =
+                        getKeyPairForSignatureAlgorithm(sigAlgorithm, keystoreKeyPairs).getPublic();
+                assertSignatureVerifiesOneShot(
+                        sigAlgorithm, keystoreProvider, keystorePublicKey, message, sigBytes);
+            } catch (Throwable e) {
+                throw new RuntimeException(
+                        sigAlgorithm + " failed, signing provider: " + signingProvider, e);
+            }
+        }
+    }
+
     public void testSmallMsgKat() throws Exception {
         Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
         byte[] message = SHORT_MSG_KAT_MESSAGE;
@@ -417,9 +531,8 @@
                         algorithm, provider, keyPair.getPublic(), message, goodSigWithBitFlip);
 
                 // Sign the message in one go
-                Signature signature = Signature.getInstance(algorithm);
+                Signature signature = Signature.getInstance(algorithm, provider);
                 signature.initSign(keyPair.getPrivate());
-                assertSame(provider, signature.getProvider());
                 signature.update(message);
                 byte[] generatedSigBytes = signature.sign();
                 boolean deterministicSignatureScheme =
@@ -451,7 +564,7 @@
                 }
 
                 // Sign the message by feeding it into the Signature one byte at a time
-                signature = Signature.getInstance(signature.getAlgorithm());
+                signature = Signature.getInstance(signature.getAlgorithm(), provider);
                 signature.initSign(keyPair.getPrivate());
                 for (int i = 0; i < message.length; i++) {
                     signature.update(message[i]);
@@ -499,9 +612,8 @@
             try {
                 if (algorithm.toLowerCase(Locale.US).startsWith("nonewithrsa")) {
                     // This algorithm cannot accept large messages
-                    Signature signature = Signature.getInstance(algorithm);
+                    Signature signature = Signature.getInstance(algorithm, provider);
                     signature.initSign(keyPair.getPrivate());
-                    assertSame(provider, signature.getProvider());
                     try {
                         signature.update(message);
                         signature.sign();
@@ -514,7 +626,7 @@
                     byte[] sigBytes = SHORT_MSG_KAT_SIGNATURES.get(
                             "SHA256" + algorithm.substring("NONE".length()));
                     assertNotNull(sigBytes);
-                    signature = Signature.getInstance(algorithm);
+                    signature = Signature.getInstance(algorithm, provider);
                     signature.initVerify(keyPair.getPublic());
                     try {
                         signature.update(message);
@@ -534,9 +646,8 @@
                         algorithm, provider, keyPair.getPublic(), message, goodSigBytes, 718871);
 
                 // Sign the message in one go
-                Signature signature = Signature.getInstance(algorithm);
+                Signature signature = Signature.getInstance(algorithm, provider);
                 signature.initSign(keyPair.getPrivate());
-                assertSame(provider, signature.getProvider());
                 signature.update(message);
                 byte[] generatedSigBytes = signature.sign();
                 boolean deterministicSignatureScheme =
@@ -566,10 +677,8 @@
                 }
 
                 // Sign the message by feeding it into the Signature one byte at a time
-                signature = Signature.getInstance(signature.getAlgorithm());
                 generatedSigBytes = generateSignatureFedUsingFixedSizeChunks(
                         algorithm, provider, keyPair.getPrivate(), message, 444307);
-                signature.initSign(keyPair.getPrivate());
                 if (deterministicSignatureScheme) {
                     MoreAsserts.assertEquals(goodSigBytes, generatedSigBytes);
                 } else {
@@ -600,114 +709,226 @@
 
     public void testInitVerifySucceedsDespiteMissingAuthorizations() throws Exception {
         KeyProtection spec = new KeyProtection.Builder(0).build();
-        assertInitVerifySucceeds("SHA256withECDSA", spec);
-        assertInitVerifySucceeds("SHA256withRSA", spec);
-        assertInitVerifySucceeds("SHA256withRSA/PSS", spec);
+
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                assertInitVerifySucceeds(algorithm, spec);
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     public void testInitSignFailsWhenNotAuthorizedToSign() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setDigests(KeyProperties.DIGEST_NONE);
         int badPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
                 | KeyProperties.PURPOSE_VERIFY;
 
-        assertInitSignSucceeds("SHA256withECDSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA256withECDSA",
-                TestUtils.buildUpon(good, badPurposes).build());
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForSigning(algorithm);
+                assertInitSignSucceeds(algorithm, good.build());
+                assertInitSignThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good, badPurposes).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-        assertInitSignSucceeds("SHA256withRSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA256withRSA",
-                TestUtils.buildUpon(good, badPurposes).build());
+    public void testInitVerifyIgnoresThatNotAuthorizedToVerify() throws Exception {
+        int badPurposes = KeyProperties.PURPOSE_ENCRYPT | KeyProperties.PURPOSE_DECRYPT
+                | KeyProperties.PURPOSE_SIGN;
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignSucceeds("SHA256withRSA/PSS", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA256withRSA/PSS",
-                TestUtils.buildUpon(good, badPurposes).build());
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForVerifying(algorithm);
+                assertInitVerifySucceeds(algorithm, good.build());
+                assertInitVerifySucceeds(algorithm,
+                        TestUtils.buildUpon(good, badPurposes).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     public void testInitSignFailsWhenDigestNotAuthorized() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setDigests(KeyProperties.DIGEST_SHA384);
-        String badDigest = KeyProperties.DIGEST_SHA256;
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForSigning(algorithm);
+                assertInitSignSucceeds(algorithm, good.build());
 
-        assertInitSignSucceeds("SHA384withECDSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA384withECDSA",
-                TestUtils.buildUpon(good).setDigests(badDigest).build());
+                String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
+                String badDigest =
+                        (algorithmUpperCase.startsWith("SHA256")) ? "SHA-384" : "SHA-256";
+                assertInitSignThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good).setDigests(badDigest).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-        assertInitSignSucceeds("SHA384withRSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA384withRSA",
-                TestUtils.buildUpon(good).setDigests(badDigest).build());
+    public void testInitVerifyIgnoresThatDigestNotAuthorized() throws Exception {
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForVerifying(algorithm);
+                assertInitVerifySucceeds(algorithm, good.build());
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignSucceeds("SHA384withRSA/PSS", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA384withRSA/PSS",
-                TestUtils.buildUpon(good).setDigests(badDigest).build());
+                String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
+                String badDigest =
+                        (algorithmUpperCase.startsWith("SHA256")) ? "SHA-384" : "SHA-256";
+                assertInitVerifySucceeds(algorithm,
+                        TestUtils.buildUpon(good).setDigests(badDigest).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     public void testInitSignFailsWhenPaddingNotAuthorized() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setDigests(KeyProperties.DIGEST_SHA512);
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
+                String badPaddingScheme;
+                if (algorithmUpperCase.endsWith("WITHECDSA")) {
+                    // Test does not apply to ECDSA because ECDSA doesn't any signature padding
+                    // schemes.
+                    continue;
+                } else if (algorithmUpperCase.endsWith("WITHRSA")) {
+                    badPaddingScheme = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+                } else if (algorithmUpperCase.endsWith("WITHRSA/PSS")) {
+                    badPaddingScheme = KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
+                } else {
+                    throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
+                }
 
-        // Does not apply to ECDSA because it doesn't any signature padding schemes
-        // assertInitSignThrowsInvalidKeyException("SHA256withECDSA", builder.build());
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-        assertInitSignSucceeds("SHA512withRSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA512withRSA",
-                TestUtils.buildUpon(good)
-                        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS)
-                        .build());
+                KeyProtection.Builder good = getWorkingImportParamsForSigning(algorithm);
+                assertInitSignSucceeds(algorithm, good.build());
+                assertInitSignThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good).setSignaturePaddings(badPaddingScheme).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignSucceeds("SHA512withRSA/PSS", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA512withRSA/PSS",
-                TestUtils.buildUpon(good)
-                        .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
-                        .build());
+    public void testInitVerifyIgnoresThatPaddingNotAuthorized() throws Exception {
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
+                String badPaddingScheme;
+                if (algorithmUpperCase.endsWith("WITHECDSA")) {
+                    // Test does not apply to ECDSA because ECDSA doesn't any signature padding
+                    // schemes.
+                    continue;
+                } else if (algorithmUpperCase.endsWith("WITHRSA")) {
+                    badPaddingScheme = KeyProperties.SIGNATURE_PADDING_RSA_PSS;
+                } else if (algorithmUpperCase.endsWith("WITHRSA/PSS")) {
+                    badPaddingScheme = KeyProperties.SIGNATURE_PADDING_RSA_PKCS1;
+                } else {
+                    throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
+                }
+
+                KeyProtection.Builder good = getWorkingImportParamsForVerifying(algorithm);
+                assertInitVerifySucceeds(algorithm, good.build());
+                assertInitVerifySucceeds(algorithm,
+                        TestUtils.buildUpon(good).setSignaturePaddings(badPaddingScheme).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     public void testInitSignFailsWhenKeyNotYetValid() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setDigests(KeyProperties.DIGEST_SHA224);
         Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
-        assertInitSignSucceeds("SHA224withECDSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA224withECDSA",
-                TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
-
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-        assertInitSignSucceeds("SHA224withRSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA",
-                TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
-
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
-                TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForSigning(algorithm);
+                assertInitSignSucceeds(algorithm, good.build());
+                assertInitSignThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
-    public void testInitSignFailsWhenKeyNoLongerValid() throws Exception {
-        KeyProtection.Builder good = new KeyProtection.Builder(
-                KeyProperties.PURPOSE_SIGN)
-                .setDigests(KeyProperties.DIGEST_SHA224);
+    public void testInitVerifyIgnoresThatKeyNotYetValid() throws Exception {
+        Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForVerifying(algorithm);
+                assertInitVerifySucceeds(algorithm, good.build());
+                assertInitVerifySucceeds(algorithm,
+                        TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitSignFailsWhenKeyNoLongerValidForOrigination() throws Exception {
         Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
-        assertInitSignSucceeds("SHA224withECDSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA224withECDSA",
-                TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForSigning(algorithm);
+                assertInitSignSucceeds(algorithm, good.build());
+                assertInitSignThrowsInvalidKeyException(algorithm,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForOriginationEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
-        assertInitSignSucceeds("SHA224withRSA", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA",
-                TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+    public void testInitVerifyIgnoresThatKeyNoLongerValidForOrigination() throws Exception {
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForVerifying(algorithm);
+                assertInitVerifySucceeds(algorithm, good.build());
+                assertInitVerifySucceeds(algorithm,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForOriginationEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
 
-        good.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
-        assertInitSignSucceeds("SHA224withRSA/PSS", good.build());
-        assertInitSignThrowsInvalidKeyException("SHA224withRSA/PSS",
-                TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+    public void testInitSignIgnoresThatKeyNoLongerValidForConsumption() throws Exception {
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForSigning(algorithm);
+                assertInitSignSucceeds(algorithm, good.build());
+                assertInitSignSucceeds(algorithm,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForConsumptionEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
+    }
+
+    public void testInitVerifyIgnoresThatKeyNoLongerValidForConsumption() throws Exception {
+        Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+        for (String algorithm : EXPECTED_SIGNATURE_ALGORITHMS) {
+            try {
+                KeyProtection.Builder good = getWorkingImportParamsForVerifying(algorithm);
+                assertInitVerifySucceeds(algorithm, good.build());
+                assertInitVerifySucceeds(algorithm,
+                        TestUtils.buildUpon(good)
+                                .setKeyValidityForConsumptionEnd(badEndDate)
+                                .build());
+            } catch (Throwable e) {
+                throw new RuntimeException("Failed for " + algorithm, e);
+            }
+        }
     }
 
     private void assertInitVerifySucceeds(
@@ -732,7 +953,7 @@
                 TestUtils.getRawResX509Certificate(getContext(), certResId),
                 keyProtection)
                 .getPublic();
-        Signature signature = Signature.getInstance(signatureAlgorithm);
+        Signature signature = Signature.getInstance(signatureAlgorithm, EXPECTED_PROVIDER_NAME);
         signature.initVerify(publicKey);
     }
 
@@ -758,7 +979,7 @@
                 TestUtils.getRawResX509Certificate(getContext(), certResId),
                 keyProtection)
                 .getPrivate();
-        Signature signature = Signature.getInstance(signatureAlgorithm);
+        Signature signature = Signature.getInstance(signatureAlgorithm, EXPECTED_PROVIDER_NAME);
         signature.initSign(privateKey);
     }
 
@@ -793,7 +1014,7 @@
                 TestUtils.getRawResX509Certificate(getContext(), certResId),
                 keyProtection)
                 .getPrivate();
-        Signature signature = Signature.getInstance(signatureAlgorithm);
+        Signature signature = Signature.getInstance(signatureAlgorithm, EXPECTED_PROVIDER_NAME);
         try {
             signature.initSign(privateKey);
             fail(message);
@@ -830,6 +1051,19 @@
                 getKeyAlgorithmForSignatureAlgorithm(signatureAlgorithm), keyPairs);
     }
 
+    private Collection<KeyPair> getDefaultKatKeyPairs() throws Exception {
+        return Arrays.asList(
+                new KeyPair(
+                        TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key1_cert)
+                                .getPublicKey(),
+                        TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key1_pkcs8)),
+                new KeyPair(
+                        TestUtils.getRawResX509Certificate(getContext(), R.raw.ec_key1_cert)
+                                .getPublicKey(),
+                        TestUtils.getRawResPrivateKey(getContext(), R.raw.ec_key1_pkcs8))
+                );
+    }
+
     private Collection<KeyPair> importDefaultKatKeyPairs() throws Exception {
         return Arrays.asList(
                 TestUtils.importIntoAndroidKeyStore(
@@ -963,4 +1197,27 @@
                     + HexEncoding.encode(signature));
         }
     }
+
+    private static KeyProtection.Builder getWorkingImportParamsForSigning(String algorithm) {
+        KeyProtection.Builder result = new KeyProtection.Builder(
+                KeyProperties.PURPOSE_SIGN)
+                .setDigests(KeyProperties.DIGEST_NONE);
+        String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
+        if (algorithmUpperCase.endsWith("WITHECDSA")) {
+            // No need for padding
+        } else if (algorithmUpperCase.endsWith("WITHRSA")) {
+            result.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1);
+        } else if (algorithmUpperCase.endsWith("WITHRSA/PSS")) {
+            result.setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PSS);
+        } else {
+            throw new IllegalArgumentException("Unsupported algorithm: " + algorithm);
+        }
+        return result;
+    }
+
+    private static KeyProtection.Builder getWorkingImportParamsForVerifying(String algorithm) {
+        return TestUtils.buildUpon(
+                getWorkingImportParamsForSigning(algorithm),
+                KeyProperties.PURPOSE_VERIFY);
+    }
 }
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 3448ffc..01ddf34 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -56,6 +56,7 @@
 import java.util.Collections;
 import java.util.HashMap;
 import java.util.List;
+import java.util.Locale;
 import java.util.Map;
 
 import javax.crypto.SecretKey;
@@ -65,6 +66,9 @@
 abstract class TestUtils extends Assert {
 
     static final String EXPECTED_CRYPTO_OP_PROVIDER_NAME = "AndroidKeyStoreBCWorkaround";
+    static final String EXPECTED_PROVIDER_NAME = "AndroidKeyStore";
+
+    static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
 
 
     private TestUtils() {}
@@ -526,6 +530,15 @@
         throw new IllegalArgumentException("No KeyPair for key algorithm " + keyAlgorithm);
     }
 
+    static Key getKeyForKeyAlgorithm(String keyAlgorithm, Iterable<? extends Key> keys) {
+        for (Key key : keys) {
+            if (keyAlgorithm.equalsIgnoreCase(key.getAlgorithm())) {
+                return key;
+            }
+        }
+        throw new IllegalArgumentException("No Key for key algorithm " + keyAlgorithm);
+    }
+
     static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
         byte[] result = new byte[msgSizeBytes];
         MessageDigest digest = MessageDigest.getInstance("SHA-512");
@@ -540,4 +553,38 @@
         }
         return result;
     }
+
+    static byte[] leftPadWithZeroBytes(byte[] array, int length) {
+        if (array.length >= length) {
+            return array;
+        }
+        byte[] result = new byte[length];
+        System.arraycopy(array, 0, result, result.length - array.length, array.length);
+        return result;
+    }
+
+    static boolean contains(int[] array, int value) {
+        for (int element : array) {
+            if (element == value) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    static boolean isHmacAlgorithm(String algorithm) {
+        return algorithm.toUpperCase(Locale.US).startsWith("HMAC");
+    }
+
+    static String getHmacAlgorithmDigest(String algorithm) {
+        String algorithmUpperCase = algorithm.toUpperCase(Locale.US);
+        if (!algorithmUpperCase.startsWith("HMAC")) {
+            return null;
+        }
+        String result = algorithmUpperCase.substring("HMAC".length());
+        if (result.startsWith("SHA")) {
+            result = "SHA-" + result.substring("SHA".length());
+        }
+        return result;
+    }
 }
diff --git a/tests/tests/media/src/android/media/cts/AudioRecordTest.java b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
index 048f956..968a382 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -690,174 +690,192 @@
             listener = new MockOnRecordPositionUpdateListener(record);
         }
 
-        if (markerPeriodsPerSecond != 0) {
-            mMarkerPeriodInFrames = TEST_SR / markerPeriodsPerSecond;
-            mMarkerPosition = mMarkerPeriodInFrames;
-            assertEquals(AudioRecord.SUCCESS,
-                    record.setNotificationMarkerPosition(mMarkerPosition));
-        } else {
-            mMarkerPeriodInFrames = 0;
-        }
         final int updatePeriodInFrames = (periodsPerSecond == 0)
                 ? 0 : TEST_SR / periodsPerSecond;
-        assertEquals(AudioRecord.SUCCESS,
-                record.setPositionNotificationPeriod(updatePeriodInFrames));
-
-        listener.start(TEST_SR);
-        record.startRecording();
-        assertEquals(AudioRecord.RECORDSTATE_RECORDING, record.getRecordingState());
-        long startTime = System.currentTimeMillis();
-
-        // For our tests, we could set test duration by timed sleep or by # frames received.
-        // Since we don't know *exactly* when AudioRecord actually begins recording,
-        // we end the test by # frames read.
-        final int numChannels =  AudioFormat.channelCountFromInChannelMask(TEST_CONF);
-        final int bytesPerSample = AudioFormat.getBytesPerSample(TEST_FORMAT);
-        final int bytesPerFrame = numChannels * bytesPerSample;
-        // careful about integer overflow in the formula below:
-        final int targetSamples = (int)((long)TEST_TIME_MS * TEST_SR * numChannels / 1000);
-        final int BUFFER_FRAMES = 512;
-        final int BUFFER_SAMPLES = BUFFER_FRAMES * numChannels;
-        // TODO: verify behavior when buffer size is not a multiple of frame size.
-
         // After starting, there is no guarantee when the first frame of data is read.
         long firstSampleTime = 0;
-        int samplesRead = 0;
-        if (useByteBuffer) {
-            ByteBuffer byteBuffer = ByteBuffer.allocateDirect(BUFFER_SAMPLES * bytesPerSample);
-            while (samplesRead < targetSamples) {
-                // the first time through, we read a single frame.
-                // this sets the recording anchor position.
-                int amount = samplesRead == 0 ? numChannels :
-                    Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
-                amount *= bytesPerSample;    // in bytes
-                // read always places data at the start of the byte buffer with
-                // position and limit are ignored.  test this by setting
-                // position and limit to arbitrary values here.
-                final int lastPosition = 7;
-                final int lastLimit = 13;
-                byteBuffer.position(lastPosition);
-                byteBuffer.limit(lastLimit);
-                int ret = blocking ? record.read(byteBuffer, amount) :
-                    record.read(byteBuffer, amount, AudioRecord.READ_NON_BLOCKING);
-                // so long as amount requested in bytes is a multiple of the frame size
-                // we expect the byte buffer request to be filled.  Caution: the
-                // byte buffer data will be in native endian order, not Java order.
-                if (blocking) {
-                    assertEquals(amount, ret);
-                } else {
-                    assertTrue("0 <= " + ret + " <= " + amount, 0 <= ret && ret <= amount);
-                }
-                // position, limit are not changed by read().
-                assertEquals(lastPosition, byteBuffer.position());
-                assertEquals(lastLimit, byteBuffer.limit());
-                if (samplesRead == 0 && ret > 0) {
-                    firstSampleTime = System.currentTimeMillis();
-                }
-                samplesRead += ret / bytesPerSample;
+
+        // blank final variables: all successful paths will initialize the times.
+        final long endTime;
+        final long startTime;
+        final long stopTime;
+
+        try {
+            if (markerPeriodsPerSecond != 0) {
+                mMarkerPeriodInFrames = TEST_SR / markerPeriodsPerSecond;
+                mMarkerPosition = mMarkerPeriodInFrames;
+                assertEquals(AudioRecord.SUCCESS,
+                        record.setNotificationMarkerPosition(mMarkerPosition));
+            } else {
+                mMarkerPeriodInFrames = 0;
             }
-        } else {
-            switch (TEST_FORMAT) {
-            case AudioFormat.ENCODING_PCM_8BIT: {
-                // For 8 bit data, use bytes
-                byte[] byteData = new byte[BUFFER_SAMPLES];
+
+            assertEquals(AudioRecord.SUCCESS,
+                    record.setPositionNotificationPeriod(updatePeriodInFrames));
+
+            listener.start(TEST_SR);
+            record.startRecording();
+            assertEquals(AudioRecord.RECORDSTATE_RECORDING, record.getRecordingState());
+            startTime = System.currentTimeMillis();
+
+            // For our tests, we could set test duration by timed sleep or by # frames received.
+            // Since we don't know *exactly* when AudioRecord actually begins recording,
+            // we end the test by # frames read.
+            final int numChannels =  AudioFormat.channelCountFromInChannelMask(TEST_CONF);
+            final int bytesPerSample = AudioFormat.getBytesPerSample(TEST_FORMAT);
+            final int bytesPerFrame = numChannels * bytesPerSample;
+            // careful about integer overflow in the formula below:
+            final int targetSamples = (int)((long)TEST_TIME_MS * TEST_SR * numChannels / 1000);
+            final int BUFFER_FRAMES = 512;
+            final int BUFFER_SAMPLES = BUFFER_FRAMES * numChannels;
+            // TODO: verify behavior when buffer size is not a multiple of frame size.
+
+            int samplesRead = 0;
+            if (useByteBuffer) {
+                ByteBuffer byteBuffer =
+                        ByteBuffer.allocateDirect(BUFFER_SAMPLES * bytesPerSample);
                 while (samplesRead < targetSamples) {
                     // the first time through, we read a single frame.
                     // this sets the recording anchor position.
                     int amount = samplesRead == 0 ? numChannels :
                         Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
-                    int ret = blocking ? record.read(byteData, 0, amount) :
-                        record.read(byteData, 0, amount, AudioRecord.READ_NON_BLOCKING);
+                    amount *= bytesPerSample;    // in bytes
+                    // read always places data at the start of the byte buffer with
+                    // position and limit are ignored.  test this by setting
+                    // position and limit to arbitrary values here.
+                    final int lastPosition = 7;
+                    final int lastLimit = 13;
+                    byteBuffer.position(lastPosition);
+                    byteBuffer.limit(lastLimit);
+                    int ret = blocking ? record.read(byteBuffer, amount) :
+                        record.read(byteBuffer, amount, AudioRecord.READ_NON_BLOCKING);
+                    // so long as amount requested in bytes is a multiple of the frame size
+                    // we expect the byte buffer request to be filled.  Caution: the
+                    // byte buffer data will be in native endian order, not Java order.
                     if (blocking) {
                         assertEquals(amount, ret);
                     } else {
-                        assertTrue("0 <= " + ret + " <= " + amount, 0 <= ret && ret <= amount);
+                        assertTrue("0 <= " + ret + " <= " + amount,
+                                0 <= ret && ret <= amount);
                     }
+                    // position, limit are not changed by read().
+                    assertEquals(lastPosition, byteBuffer.position());
+                    assertEquals(lastLimit, byteBuffer.limit());
                     if (samplesRead == 0 && ret > 0) {
                         firstSampleTime = System.currentTimeMillis();
                     }
-                    samplesRead += ret;
+                    samplesRead += ret / bytesPerSample;
                 }
-            } break;
-            case AudioFormat.ENCODING_PCM_16BIT: {
-                // For 16 bit data, use shorts
-                short[] shortData = new short[BUFFER_SAMPLES];
-                while (samplesRead < targetSamples) {
-                    // the first time through, we read a single frame.
-                    // this sets the recording anchor position.
-                    int amount = samplesRead == 0 ? numChannels :
-                        Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
-                    int ret = blocking ? record.read(shortData, 0, amount) :
-                        record.read(shortData, 0, amount, AudioRecord.READ_NON_BLOCKING);
-                    if (blocking) {
-                        assertEquals(amount, ret);
-                    } else {
-                        assertTrue("0 <= " + ret + " <= " + amount, 0 <= ret && ret <= amount);
+            } else {
+                switch (TEST_FORMAT) {
+                case AudioFormat.ENCODING_PCM_8BIT: {
+                    // For 8 bit data, use bytes
+                    byte[] byteData = new byte[BUFFER_SAMPLES];
+                    while (samplesRead < targetSamples) {
+                        // the first time through, we read a single frame.
+                        // this sets the recording anchor position.
+                        int amount = samplesRead == 0 ? numChannels :
+                            Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
+                        int ret = blocking ? record.read(byteData, 0, amount) :
+                            record.read(byteData, 0, amount, AudioRecord.READ_NON_BLOCKING);
+                        if (blocking) {
+                            assertEquals(amount, ret);
+                        } else {
+                            assertTrue("0 <= " + ret + " <= " + amount,
+                                    0 <= ret && ret <= amount);
+                        }
+                        if (samplesRead == 0 && ret > 0) {
+                            firstSampleTime = System.currentTimeMillis();
+                        }
+                        samplesRead += ret;
                     }
-                    if (samplesRead == 0 && ret > 0) {
-                        firstSampleTime = System.currentTimeMillis();
+                } break;
+                case AudioFormat.ENCODING_PCM_16BIT: {
+                    // For 16 bit data, use shorts
+                    short[] shortData = new short[BUFFER_SAMPLES];
+                    while (samplesRead < targetSamples) {
+                        // the first time through, we read a single frame.
+                        // this sets the recording anchor position.
+                        int amount = samplesRead == 0 ? numChannels :
+                            Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
+                        int ret = blocking ? record.read(shortData, 0, amount) :
+                            record.read(shortData, 0, amount, AudioRecord.READ_NON_BLOCKING);
+                        if (blocking) {
+                            assertEquals(amount, ret);
+                        } else {
+                            assertTrue("0 <= " + ret + " <= " + amount,
+                                    0 <= ret && ret <= amount);
+                        }
+                        if (samplesRead == 0 && ret > 0) {
+                            firstSampleTime = System.currentTimeMillis();
+                        }
+                        samplesRead += ret;
                     }
-                    samplesRead += ret;
+                } break;
+                case AudioFormat.ENCODING_PCM_FLOAT: {
+                    float[] floatData = new float[BUFFER_SAMPLES];
+                    while (samplesRead < targetSamples) {
+                        // the first time through, we read a single frame.
+                        // this sets the recording anchor position.
+                        int amount = samplesRead == 0 ? numChannels :
+                            Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
+                        int ret = record.read(floatData, 0, amount, blocking ?
+                                AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
+                        if (blocking) {
+                            assertEquals(amount, ret);
+                        } else {
+                            assertTrue("0 <= " + ret + " <= " + amount,
+                                    0 <= ret && ret <= amount);
+                        }
+                        if (samplesRead == 0 && ret > 0) {
+                            firstSampleTime = System.currentTimeMillis();
+                        }
+                        samplesRead += ret;
+                    }
+                } break;
                 }
-            } break;
-            case AudioFormat.ENCODING_PCM_FLOAT: {
-                float[] floatData = new float[BUFFER_SAMPLES];
-                while (samplesRead < targetSamples) {
-                    // the first time through, we read a single frame.
-                    // this sets the recording anchor position.
-                    int amount = samplesRead == 0 ? numChannels :
-                        Math.min(BUFFER_SAMPLES, targetSamples - samplesRead);
-                    int ret = record.read(floatData, 0, amount, blocking ?
-                            AudioRecord.READ_BLOCKING : AudioRecord.READ_NON_BLOCKING);
-                    if (blocking) {
-                        assertEquals(amount, ret);
-                    } else {
-                        assertTrue("0 <= " + ret + " <= " + amount, 0 <= ret && ret <= amount);
-                    }
-                    if (samplesRead == 0 && ret > 0) {
-                        firstSampleTime = System.currentTimeMillis();
-                    }
-                    samplesRead += ret;
-                }
-            } break;
             }
+
+            // We've read all the frames, now check the record timing.
+            endTime = System.currentTimeMillis();
+            //Log.d(TAG, "first sample time " + (firstSampleTime - startTime)
+            //        + " test time " + (endTime - firstSampleTime));
+            // Verify recording starts within 200 ms of record.startRecording() (typical 100ms)
+            // Verify recording completes within 50 ms of expected test time (typical 20ms)
+            assertEquals(0, firstSampleTime - startTime, 200);
+            assertEquals(TEST_TIME_MS, endTime - firstSampleTime, auditRecording ? 1000 : 50);
+
+            // Even though we've read all the frames we want, the events may not be sent to
+            // the listeners (events are handled through a separate internal callback thread).
+            // One must sleep to make sure the last event(s) come in.
+            Thread.sleep(30);
+
+            record.stop();
+            assertEquals(AudioRecord.RECORDSTATE_STOPPED, record.getRecordingState());
+
+            stopTime = System.currentTimeMillis();
+
+            // stop listening - we should be done.
+            // Caution M behavior and likely much earlier:
+            // we assume no events can happen after stop(), but this may not
+            // always be true as stop can take 100ms to complete (as it may disable
+            // input recording on the hal); thus the event handler may be block with
+            // valid events, issuing right after stop completes. Except for those events,
+            // no other events should show up after stop.
+            // This behavior may change in the future but we account for it here in testing.
+            listener.stop();
+
+            // clean up
+            if (makeSomething != null) {
+                makeSomething.join();
+            }
+
+        } finally {
+            listener.release();
+            // we must release the record immediately as it is a system-wide
+            // resource needed for other tests.
+            record.release();
         }
-
-        // We've read all the frames, now check the record timing.
-        final long endTime = System.currentTimeMillis();
-        //Log.d(TAG, "first sample time " + (firstSampleTime - startTime)
-        //        + " test time " + (endTime - firstSampleTime));
-        // Verify recording starts within 200 ms of record.startRecording() (typical 100ms)
-        // Verify recording completes within 50 ms of expected test time (typical 20ms)
-        assertEquals(0, firstSampleTime - startTime, 200);
-        assertEquals(TEST_TIME_MS, endTime - firstSampleTime, auditRecording ? 1000 : 50);
-
-        // Even though we've read all the frames we want, the events may not be sent to
-        // the listeners (events are handled through a separate internal callback thread).
-        // One must sleep to make sure the last event(s) come in.
-        Thread.sleep(30);
-
-        record.stop();
-        assertEquals(AudioRecord.RECORDSTATE_STOPPED, record.getRecordingState());
-
-        final long stopTime = System.currentTimeMillis();
-
-        // stop listening - we should be done.
-        // Caution M behavior and likely much earlier:
-        // we assume no events can happen after stop(), but this may not
-        // always be true as stop can take 100ms to complete (as it may disable
-        // input recording on the hal); thus the event handler may be block with
-        // valid events, issuing right after stop completes. Except for those events,
-        // no other events should show up after stop.
-        // This behavior may change in the future but we account for it here in testing.
-        listener.stop();
-
-        // clean up
-        if (makeSomething != null) {
-            makeSomething.join();
-        }
-        listener.release();
-        record.release();
         if (auditRecording) { // don't check timing if auditing (messes up timing)
             return;
         }
@@ -1005,6 +1023,7 @@
         }
 
         public synchronized void release() {
+            stop();
             mAudioRecord.setRecordPositionUpdateListener(null);
             mAudioRecord = null;
         }
diff --git a/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
new file mode 100644
index 0000000..4139059
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2014 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.
+ */
+
+package android.permission.cts;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for TV API related permissions.
+ */
+public class TvPermissionTest extends AndroidTestCase {
+    private static final String DUMMY_INPUT_ID = "dummy";
+
+    private boolean mHasTvInputFramework;
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mHasTvInputFramework = getContext().getPackageManager()
+                .hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        super.tearDown();
+    }
+
+    public void verifyInsert(Uri uri, String tableName) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().insert(uri, values);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyUpdate(Uri uri, String tableName) throws Exception {
+        try {
+            ContentValues values = new ContentValues();
+            getContext().getContentResolver().update(uri, values, null, null);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void verifyDelete(Uri uri, String tableName) throws Exception {
+        try {
+            getContext().getContentResolver().delete(uri, null, null);
+            fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+        } catch (SecurityException e) {
+            // Expected exception
+        }
+    }
+
+    public void testInsertChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyInsert(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testUpdateChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyUpdate(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testDeleteChannels() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyDelete(TvContract.Channels.CONTENT_URI, "channels");
+    }
+
+    public void testInsertPrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyInsert(TvContract.Programs.CONTENT_URI, "programs");
+    }
+
+    public void testUpdatePrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyUpdate(TvContract.Programs.CONTENT_URI, "programs");
+    }
+
+    public void testDeletePrograms() throws Exception {
+        if (!mHasTvInputFramework) return;
+        verifyDelete(TvContract.Programs.CONTENT_URI, "programs");
+    }
+}
diff --git a/tests/tests/tv/AndroidManifest.xml b/tests/tests/tv/AndroidManifest.xml
index 41ec835..79406e0 100644
--- a/tests/tests/tv/AndroidManifest.xml
+++ b/tests/tests/tv/AndroidManifest.xml
@@ -22,6 +22,9 @@
     <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
     <uses-permission android:name="android.permission.INJECT_EVENTS" />
 
+    <uses-permission android:name="com.android.providers.tv.permission.READ_EPG_DATA" />
+    <uses-permission android:name="com.android.providers.tv.permission.WRITE_EPG_DATA" />
+
     <application>
         <uses-library android:name="android.test.runner" />
 
diff --git a/tests/tests/voicesettings/Android.mk b/tests/tests/voicesettings/Android.mk
new file mode 100644
index 0000000..71fead6
--- /dev/null
+++ b/tests/tests/voicesettings/Android.mk
@@ -0,0 +1,33 @@
+# Copyright (C) 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceSettingsCommon ctstestrunner ctsdeviceutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsVoiceSettingsTestCases
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/tests/tests/voicesettings/AndroidManifest.xml b/tests/tests/voicesettings/AndroidManifest.xml
new file mode 100644
index 0000000..bf938f9
--- /dev/null
+++ b/tests/tests/voicesettings/AndroidManifest.xml
@@ -0,0 +1,45 @@
+<!--
+ * Copyright (C) 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.voicesettings.cts">
+
+    <uses-permission android:name="android.permission.DISABLE_KEYGUARD" />
+    <uses-permission android:name="android.permission.BIND_VOICE_INTERACTION" />
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+
+        <activity android:name="TestStartActivity"
+                  android:label="The Target Activity for VoiceSettings CTS Test">
+            <intent-filter>
+                <action android:name="android.intent.action.TEST_START_ACTIVITY_ZEN_MODE" />
+                <action android:name="android.intent.action.TEST_START_ACTIVITY_AIRPLANE_MODE" />
+                <action android:name="android.intent.action.TEST_START_ACTIVITY_BATTERYSAVER_MODE" />
+                <category android:name="android.intent.category.LAUNCHER" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+    </application>
+
+    <instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
+                     android:targetPackage="android.voicesettings.cts"
+                     android:label="CTS tests of android.voicesettings">
+        <meta-data android:name="listener"
+            android:value="com.android.cts.runner.CtsTestRunListener" />
+    </instrumentation>
+</manifest>
+
diff --git a/tests/tests/voicesettings/AndroidTest.xml b/tests/tests/voicesettings/AndroidTest.xml
new file mode 100644
index 0000000..0a3974d
--- /dev/null
+++ b/tests/tests/voicesettings/AndroidTest.xml
@@ -0,0 +1,23 @@
+<!-- Copyright (C) 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.
+-->
+<configuration description="Test module config for VoiceInteraction">
+    <include name="common-config" />
+    <option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsService.apk" />
+    <option name="run-command:run-command"
+         value="settings put secure voice_interaction_service android.voicesettings.service/.MainInteractionService" />
+    <option name="run-command:teardown-command"
+         value="settings put secure voice_interaction_service com.google.android.googlequicksearchbox/com.google.android.voiceinteraction.GsaVoiceInteractionService" />
+    <option name="cts-apk-installer:test-file-name" value="CtsVoiceSettingsTestCases.apk" />
+</configuration>
diff --git a/tests/tests/voicesettings/common/Android.mk b/tests/tests/voicesettings/common/Android.mk
new file mode 100644
index 0000000..1478ef2
--- /dev/null
+++ b/tests/tests/voicesettings/common/Android.mk
@@ -0,0 +1,30 @@
+# Copyright (C) 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE := CtsVoiceSettingsCommon
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_STATIC_JAVA_LIBRARY)
diff --git a/tests/tests/voicesettings/common/src/android/voicesettings/common/Utils.java b/tests/tests/voicesettings/common/src/android/voicesettings/common/Utils.java
new file mode 100644
index 0000000..44514b0
--- /dev/null
+++ b/tests/tests/voicesettings/common/src/android/voicesettings/common/Utils.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 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.
+ */
+package common.src.android.voicesettings.common;
+
+import android.os.Bundle;
+
+public class Utils {
+    public enum TestcaseType {
+        ZEN_MODE_ON,
+        ZEN_MODE_OFF,
+        AIRPLANE_MODE_ON,
+        AIRPLANE_MODE_OFF,
+        BATTERYSAVER_MODE_ON,
+        BATTERYSAVER_MODE_OFF,
+    }
+    public static final String TESTCASE_TYPE = "Testcase_type";
+    public static final String BROADCAST_INTENT =
+            "android.intent.action.FROM_VOICESETTINGS_CTS_TEST_";
+    public static final int NUM_MINUTES_FOR_ZENMODE = 10;
+
+    public static final String toBundleString(Bundle bundle) {
+        if (bundle == null) {
+            return "*** Bundle is null ****";
+        }
+        StringBuilder buf = new StringBuilder();
+        if (bundle != null) {
+            buf.append("extras: ");
+            for (String s : bundle.keySet()) {
+                buf.append("(" + s + " = " + bundle.get(s) + "), ");
+            }
+        }
+        return buf.toString();
+    }
+}
diff --git a/tests/tests/voicesettings/res/xml/interaction_service.xml b/tests/tests/voicesettings/res/xml/interaction_service.xml
new file mode 100644
index 0000000..bf40892
--- /dev/null
+++ b/tests/tests/voicesettings/res/xml/interaction_service.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 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.
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:sessionService="android.voicesettings.service.MainInteractionSessionService"
+    android:recognitionService="android.voicesettings.service.MainRecognitionService"
+    android:settingsActivity="android.voicesettings.service.SettingsActivity"
+    android:supportsAssist="false" />
diff --git a/tests/tests/voicesettings/service/Android.mk b/tests/tests/voicesettings/service/Android.mk
new file mode 100644
index 0000000..97866d5
--- /dev/null
+++ b/tests/tests/voicesettings/service/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 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.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+# don't include this package in any target
+LOCAL_MODULE_TAGS := optional
+# and when built explicitly put it in the data partition
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_STATIC_JAVA_LIBRARIES := CtsVoiceSettingsCommon ctstestrunner ctsdeviceutil
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_PACKAGE_NAME := CtsVoiceSettingsService
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_SUPPORT_PACKAGE)
diff --git a/tests/tests/voicesettings/service/AndroidManifest.xml b/tests/tests/voicesettings/service/AndroidManifest.xml
new file mode 100644
index 0000000..13671b6
--- /dev/null
+++ b/tests/tests/voicesettings/service/AndroidManifest.xml
@@ -0,0 +1,65 @@
+<!--
+ * Copyright (C) 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+    package="android.voicesettings.service">
+
+    <application>
+        <uses-library android:name="android.test.runner" />
+        <service android:name=".MainInteractionService"
+                android:label="CTS test voice interaction service"
+                android:permission="android.permission.BIND_VOICE_INTERACTION"
+                android:process=":interactor"
+                android:exported="true">
+            <meta-data android:name="android.voice_interaction"
+                       android:resource="@xml/interaction_service" />
+            <intent-filter>
+                <action android:name="android.service.voice.VoiceInteractionService" />
+            </intent-filter>
+        </service>
+        <activity android:name=".VoiceInteractionMain" >
+            <intent-filter>
+                <action android:name="android.intent.action.VIMAIN_ZEN_MODE_ON" />
+                <action android:name="android.intent.action.VIMAIN_ZEN_MODE_OFF" />
+                <action android:name="android.intent.action.VIMAIN_AIRPLANE_MODE_ON" />
+                <action android:name="android.intent.action.VIMAIN_AIRPLANE_MODE_OFF" />
+                <action android:name="android.intent.action.VIMAIN_BATTERYSAVER_MODE_ON" />
+                <action android:name="android.intent.action.VIMAIN_BATTERYSAVER_MODE_OFF" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <activity android:name=".SettingsActivity"
+                  android:label="Voice Interaction Settings">
+            <intent-filter>
+                <action android:name="android.intent.action.MAIN" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+        </activity>
+        <service android:name=".MainInteractionSessionService"
+                android:permission="android.permission.BIND_VOICE_INTERACTION"
+                android:process=":session">
+        </service>
+        <service android:name=".MainRecognitionService"
+                android:label="CTS Voice Recognition Service">
+            <intent-filter>
+                <action android:name="android.speech.RecognitionService" />
+                <category android:name="android.intent.category.DEFAULT" />
+            </intent-filter>
+            <meta-data android:name="android.speech" android:resource="@xml/recognition_service" />
+        </service>
+    </application>
+</manifest>
+
diff --git a/tests/tests/voicesettings/service/res/xml/interaction_service.xml b/tests/tests/voicesettings/service/res/xml/interaction_service.xml
new file mode 100644
index 0000000..bf40892
--- /dev/null
+++ b/tests/tests/voicesettings/service/res/xml/interaction_service.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 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.
+-->
+
+<voice-interaction-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:sessionService="android.voicesettings.service.MainInteractionSessionService"
+    android:recognitionService="android.voicesettings.service.MainRecognitionService"
+    android:settingsActivity="android.voicesettings.service.SettingsActivity"
+    android:supportsAssist="false" />
diff --git a/tests/tests/voicesettings/service/res/xml/recognition_service.xml b/tests/tests/voicesettings/service/res/xml/recognition_service.xml
new file mode 100644
index 0000000..9d80f24
--- /dev/null
+++ b/tests/tests/voicesettings/service/res/xml/recognition_service.xml
@@ -0,0 +1,17 @@
+<!-- Copyright (C) 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.
+-->
+
+<recognition-service xmlns:android="http://schemas.android.com/apk/res/android"
+    android:settingsActivity="android.voicesettings.service.SettingsActivity" />
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionService.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionService.java
new file mode 100644
index 0000000..6302b78
--- /dev/null
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionService.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.service;
+
+import android.content.ComponentName;
+import android.content.Intent;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionService;
+import android.util.Log;
+import common.src.android.voicesettings.common.Utils;
+
+public class MainInteractionService extends VoiceInteractionService {
+    static final String TAG = "MainInteractionService";
+    private Intent mIntent;
+    private boolean mReady = false;
+
+    @Override
+    public void onReady() {
+        super.onReady();
+        mReady = true;
+    }
+
+    @Override
+    public int onStartCommand(Intent intent, int flags, int startId) {
+        Log.i(TAG, "onStartCommand received");
+        mIntent = intent;
+        Log.i(TAG, "received_testcasetype = " + mIntent.getStringExtra(Utils.TESTCASE_TYPE));
+        maybeStart();
+        return START_NOT_STICKY;
+    }
+
+    private void maybeStart() {
+       if (mIntent == null || !mReady) {
+            Log.wtf(TAG, "Can't start session because either intent is null or onReady() "
+                    + "is not called yet. mIntent = " + mIntent + ", mReady = " + mReady);
+        } else {
+            Log.i(TAG, "Yay! about to start MainInteractionSession");
+            if (isActiveService(this, new ComponentName(this, getClass()))) {
+                Bundle args = new Bundle();
+                args.putString(Utils.TESTCASE_TYPE, mIntent.getStringExtra(Utils.TESTCASE_TYPE));
+                Log.i(TAG, "xferring_testcasetype = " + args.getString(Utils.TESTCASE_TYPE));
+                showSession(args, 0);
+            } else {
+                Log.wtf(TAG, "**** Not starting MainInteractionService because" +
+                    " it is not set as the current voice interaction service");
+            }
+        }
+    }
+}
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
new file mode 100644
index 0000000..c2b7e18
--- /dev/null
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSession.java
@@ -0,0 +1,190 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.service;
+
+import static android.provider.Settings.ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE;
+import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
+import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_AIRPLANE_MODE;
+import static android.provider.Settings.EXTRA_AIRPLANE_MODE_ENABLED;
+import static android.provider.Settings.ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE;
+import static android.provider.Settings.EXTRA_BATTERY_SAVER_MODE_ENABLED;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+import common.src.android.voicesettings.common.Utils.TestcaseType;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public class MainInteractionSession extends VoiceInteractionSession {
+    static final String TAG = "MainInteractionSession";
+
+    List<MyTask> mUsedTasks = new ArrayList<MyTask>();
+    Context mContext;
+    TestcaseType mTestType;
+
+    MainInteractionSession(Context context) {
+        super(context);
+        mContext = context;
+    }
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+    }
+
+    @Override
+    public void onDestroy() {
+        Log.i(TAG, "Canceling the Asynctasks in onDestroy()");
+        for (MyTask t : mUsedTasks) {
+            t.cancel(true);
+        }
+        super.onDestroy();
+    }
+
+    @Override
+    public void onShow(Bundle args, int showFlags) {
+        super.onShow(args, showFlags);
+        String testCaseType = args.getString(Utils.TESTCASE_TYPE);
+        Log.i(TAG, "received_testcasetype = " + testCaseType);
+        try {
+            mTestType = TestcaseType.valueOf(testCaseType);
+        } catch (IllegalArgumentException e) {
+            Log.wtf(TAG, e);
+            return;
+        } catch (NullPointerException e) {
+            Log.wtf(TAG, e);
+            return;
+        }
+        Intent intent;
+        switch(mTestType) {
+            case ZEN_MODE_ON:
+                intent = new Intent(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE);
+                intent.putExtra(EXTRA_DO_NOT_DISTURB_MODE_ENABLED, true);
+                intent.putExtra(EXTRA_DO_NOT_DISTURB_MODE_MINUTES, Utils.NUM_MINUTES_FOR_ZENMODE);
+                break;
+            case ZEN_MODE_OFF:
+                intent = new Intent(ACTION_VOICE_CONTROL_DO_NOT_DISTURB_MODE);
+                intent.putExtra(EXTRA_DO_NOT_DISTURB_MODE_ENABLED, false);
+                break;
+            case AIRPLANE_MODE_ON:
+                intent = new Intent(ACTION_VOICE_CONTROL_AIRPLANE_MODE);
+                intent.putExtra(EXTRA_AIRPLANE_MODE_ENABLED, true);
+                break;
+            case AIRPLANE_MODE_OFF:
+                intent = new Intent(ACTION_VOICE_CONTROL_AIRPLANE_MODE);
+                intent.putExtra(EXTRA_AIRPLANE_MODE_ENABLED, false);
+                break;
+            case BATTERYSAVER_MODE_ON:
+                intent = new Intent(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE);
+                intent.putExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED, true);
+                break;
+            case BATTERYSAVER_MODE_OFF:
+                intent = new Intent(ACTION_VOICE_CONTROL_BATTERY_SAVER_MODE);
+                intent.putExtra(EXTRA_BATTERY_SAVER_MODE_ENABLED, false);
+                break;
+            default:
+                Log.i(TAG, "Not implemented!");
+                return;
+        }
+        Log.i(TAG, "starting_voiceactivity: " + intent.toString());
+        startVoiceActivity(intent);
+    }
+
+    @Override
+    public void onTaskFinished(Intent intent, int taskId) {
+        // extras contain the info on what the activity started above did.
+        // we probably could verify this also.
+        Bundle extras = intent.getExtras();
+        Log.i(TAG, "in onTaskFinished: testcasetype = " + mTestType + ", intent: " +
+                intent.toString() + Utils.toBundleString(extras));
+        Intent broadcastIntent = new Intent(Utils.BROADCAST_INTENT + mTestType.toString());
+        if (extras == null) {
+            extras = new Bundle();
+        }
+        extras.putString(Utils.TESTCASE_TYPE, mTestType.toString());
+        broadcastIntent.putExtras(extras);
+        Log.i(TAG, "sending_broadcast: Bundle = " + Utils.toBundleString(extras) +
+                ", intent = " + broadcastIntent.toString());
+        mContext.sendBroadcast(broadcastIntent);
+    }
+
+    synchronized MyTask newTask() {
+        MyTask t = new MyTask();
+        mUsedTasks.add(t);
+        return t;
+    }
+
+    @Override
+    public void onRequestCompleteVoice(CompleteVoiceRequest request) {
+        CharSequence prompt = request.getVoicePrompt().getVoicePromptAt(0);
+        Log.i(TAG, "in Session testcasetype = " + mTestType +
+                ", onRequestCompleteVoice recvd. message = " + prompt);
+        AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setCompleteReq(true);
+        newTask().execute(asyncTaskArg);
+    }
+
+    @Override
+    public void onRequestAbortVoice(AbortVoiceRequest request) {
+        AsyncTaskArg asyncTaskArg = new AsyncTaskArg().setRequest(request).setCompleteReq(false);
+        Log.i(TAG, "in Session sending sendAbortResult. ");
+        newTask().execute(asyncTaskArg);
+    }
+
+    private class AsyncTaskArg {
+        CompleteVoiceRequest mCompReq;
+        AbortVoiceRequest mAbortReq;
+        boolean isCompleteRequest = true;
+
+        AsyncTaskArg setRequest(CompleteVoiceRequest r) {
+            mCompReq = r;
+            return this;
+        }
+
+        AsyncTaskArg setRequest(AbortVoiceRequest r) {
+            mAbortReq = r;
+            return this;
+        }
+
+        AsyncTaskArg setCompleteReq(boolean flag) {
+            isCompleteRequest = flag;
+            return this;
+        }
+    }
+
+    private class MyTask extends AsyncTask<AsyncTaskArg, Void, Void> {
+        @Override
+        protected Void doInBackground(AsyncTaskArg... params) {
+            AsyncTaskArg arg = params[0];
+            Log.i(TAG, "in MyTask - doInBackground: testType = " +
+                    MainInteractionSession.this.mTestType);
+            if (arg.isCompleteRequest) {
+                arg.mCompReq.sendCompleteResult(new Bundle());
+            } else {
+                arg.mAbortReq.sendAbortResult(new Bundle());
+            }
+            return null;
+        }
+    }
+}
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSessionService.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSessionService.java
new file mode 100644
index 0000000..2b302b8
--- /dev/null
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainInteractionSessionService.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.service;
+
+import android.os.Bundle;
+import android.service.voice.VoiceInteractionSession;
+import android.service.voice.VoiceInteractionSessionService;
+
+public class MainInteractionSessionService extends VoiceInteractionSessionService {
+    @Override
+    public VoiceInteractionSession onNewSession(Bundle args) {
+        return new MainInteractionSession(this);
+    }
+}
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/MainRecognitionService.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainRecognitionService.java
new file mode 100644
index 0000000..9b0e95d
--- /dev/null
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/MainRecognitionService.java
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.service;
+
+import android.content.Intent;
+import android.speech.RecognitionService;
+import android.util.Log;
+
+/**
+ * Stub recognition service needed to be a complete voice interactor.
+ */
+public class MainRecognitionService extends RecognitionService {
+    private static final String TAG = "MainRecognitionService";
+
+    @Override
+    public void onCreate() {
+        super.onCreate();
+        Log.i(TAG, "onCreate");
+    }
+
+    @Override
+    protected void onStartListening(Intent recognizerIntent, Callback listener) {
+        Log.i(TAG, "onStartListening");
+    }
+
+    @Override
+    protected void onCancel(Callback listener) {
+        Log.i(TAG, "onCancel");
+    }
+
+    @Override
+    protected void onStopListening(Callback listener) {
+        Log.i(TAG, "onStopListening");
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        Log.i(TAG, "onDestroy");
+    }
+}
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/SettingsActivity.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/SettingsActivity.java
new file mode 100644
index 0000000..140bca4
--- /dev/null
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/SettingsActivity.java
@@ -0,0 +1,30 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.service;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+/**
+ * Stub activity to test out settings selection for voice interactor.
+ */
+public class SettingsActivity extends Activity {
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+    }
+}
diff --git a/tests/tests/voicesettings/service/src/android/voicesettings/service/VoiceInteractionMain.java b/tests/tests/voicesettings/service/src/android/voicesettings/service/VoiceInteractionMain.java
new file mode 100644
index 0000000..adc2980
--- /dev/null
+++ b/tests/tests/voicesettings/service/src/android/voicesettings/service/VoiceInteractionMain.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.service;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+
+public class VoiceInteractionMain extends Activity {
+    static final String TAG = "VoiceInteractionMain";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Intent intent = new Intent();
+        String testCaseType = getIntent().getStringExtra(Utils.TESTCASE_TYPE);
+        Log.i(TAG, "received_testcasetype = " + testCaseType);
+        intent.putExtra(Utils.TESTCASE_TYPE, testCaseType);
+        intent.setComponent(new ComponentName(this, MainInteractionService.class));
+        ComponentName serviceName = startService(intent);
+        Log.i(TAG, "Started service: " + serviceName);
+    }
+}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
new file mode 100644
index 0000000..8abe396
--- /dev/null
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/AirplaneModeTest.java
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.cts;
+
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+
+public class AirplaneModeTest extends VoiceSettingsTestBase {
+    static final String TAG = "AirplaneModeTest";
+
+    private static final int AIRPLANE_MODE_IS_OFF = 0;
+    private static final int AIRPLANE_MODE_IS_ON = 1;
+
+    public AirplaneModeTest() {
+        super();
+    }
+
+    public void testAll() throws Exception {
+        startTestActivity("AIRPLANE_MODE");
+        int mode = getMode();
+        Log.i(TAG, "Before testing, AIRPLANE_MODE is set to: " + mode);
+        if (mode == AIRPLANE_MODE_IS_OFF) {
+            // mode is currently OFF.
+            // run a test to turn it on.
+            // After successful run of the test, run a test to turn it back off.
+            if (!runTest(Utils.TestcaseType.AIRPLANE_MODE_ON, AIRPLANE_MODE_IS_ON)) {
+                // the test failed. don't test the next one.
+                return;
+            }
+            runTest(Utils.TestcaseType.AIRPLANE_MODE_OFF, AIRPLANE_MODE_IS_OFF);
+        } else {
+            // mode is currently ON.
+            // run a test to turn it off.
+            // After successful run of the test, run a test to turn it back on.
+            if (!runTest(Utils.TestcaseType.AIRPLANE_MODE_OFF, AIRPLANE_MODE_IS_OFF)) {
+                // the test failed. don't test the next one.
+                return;
+            }
+            runTest(Utils.TestcaseType.AIRPLANE_MODE_ON, AIRPLANE_MODE_IS_ON);
+        }
+    }
+
+    private boolean runTest(Utils.TestcaseType test, int expectedMode) throws Exception {
+        if (!startTestAndWaitForBroadcast(test)) {
+            return false;
+        }
+
+        // verify the test results
+        int mode = getMode();
+        Log.i(TAG, "After testing, AIRPLANE_MODE is set to: " + mode);
+        assertEquals(expectedMode, mode);
+        Log.i(TAG, "Successfully Tested: " + test);
+        return true;
+    }
+
+    private int getMode() throws Exception {
+        return Settings.Global.getInt(mContext.getContentResolver(),
+            Settings.Global.AIRPLANE_MODE_ON);
+    }
+}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
new file mode 100644
index 0000000..3d1357a
--- /dev/null
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/BatterySaverModeTest.java
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.cts;
+
+import android.content.Context;
+import android.os.PowerManager;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+
+public class BatterySaverModeTest extends VoiceSettingsTestBase {
+    static final String TAG = "BatterySaverModeTest";
+
+    public BatterySaverModeTest() {
+        super();
+    }
+
+    public void testAll() throws Exception {
+        startTestActivity("BATTERYSAVER_MODE");
+        boolean modeIsOn = isModeOn();
+        Log.i(TAG, "Before testing, BATTERYSAVER_MODE is set to: " + modeIsOn);
+        if (modeIsOn) {
+            // mode is currently ON.
+            // run a test to turn it off.
+            // After successful run of the test, run a test to turn it back on.
+            if (!runTest(Utils.TestcaseType.BATTERYSAVER_MODE_OFF, false)) {
+                // the test failed. don't test the next one.
+                return;
+            }
+            runTest(Utils.TestcaseType.BATTERYSAVER_MODE_ON, true);
+        } else {
+            // mode is currently OFF.
+            // run a test to turn it on.
+            // After successful run of the test, run a test to turn it back off.
+            if (!runTest(Utils.TestcaseType.BATTERYSAVER_MODE_ON, true)) {
+                // the test failed. don't test the next one.
+                return;
+            }
+            runTest(Utils.TestcaseType.BATTERYSAVER_MODE_OFF, false);
+        }
+    }
+
+    private boolean runTest(Utils.TestcaseType test, boolean expectedMode) throws Exception {
+        if (!startTestAndWaitForBroadcast(test)) {
+            return false;
+        }
+
+        // Verify the test results
+        // Since CTS test needs the device to be connected to the host computer via USB,
+        // Batter Saver mode can't be turned on/off.
+        // The most we can do is that the broadcast frmo MainInteractionSession is received
+        // because that signals the firing and completion of BatterySaverModeVoiceActivity
+        // caused by the intent to set Battery Saver mode.
+        return true;
+    }
+
+    private boolean isModeOn() {
+        PowerManager powerManager = (PowerManager)mContext.getSystemService(Context.POWER_SERVICE);
+        return powerManager.isPowerSaveMode();
+    }
+}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/TestStartActivity.java b/tests/tests/voicesettings/src/android/voicesettings/cts/TestStartActivity.java
new file mode 100644
index 0000000..cef29b1
--- /dev/null
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/TestStartActivity.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.cts;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.ComponentName;
+import android.os.Bundle;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+
+public class TestStartActivity extends Activity {
+    static final String TAG = "TestStartActivity";
+
+    @Override
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        Log.i(TAG, " in onCreate");
+    }
+
+    @Override
+    protected void onResume() {
+        super.onResume();
+        Log.i(TAG, " in onResume");
+    }
+
+    void startTest(String testCaseType) {
+        Intent intent = new Intent();
+        Log.i(TAG, "received_testcasetype = " + testCaseType);
+        intent.putExtra(Utils.TESTCASE_TYPE, testCaseType);
+        intent.setAction("android.intent.action.VIMAIN_" + testCaseType);
+        intent.setComponent(new ComponentName("android.voicesettings.service",
+                "android.voicesettings.service.VoiceInteractionMain"));
+        startActivity(intent);
+    }
+
+    @Override
+    protected void onPause() {
+        Log.i(TAG, " in onPause");
+        super.onPause();
+    }
+
+    @Override
+    protected void onStart() {
+        super.onStart();
+        Log.i(TAG, " in onStart");
+    }
+
+    @Override
+    protected void onRestart() {
+        super.onRestart();
+        Log.i(TAG, " in onRestart");
+    }
+
+    @Override
+    protected void onStop() {
+        Log.i(TAG, " in onStop");
+        super.onStop();
+    }
+
+    @Override
+    protected void onDestroy() {
+        Log.i(TAG, " in onDestroy");
+        super.onDestroy();
+    }
+}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
new file mode 100644
index 0000000..5386497
--- /dev/null
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/VoiceSettingsTestBase.java
@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.cts;
+
+import android.content.BroadcastReceiver;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.os.Bundle;
+import android.test.ActivityInstrumentationTestCase2;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+public class VoiceSettingsTestBase extends ActivityInstrumentationTestCase2<TestStartActivity> {
+    static final String TAG = "VoiceSettingsTestBase";
+    protected static final int TIMEOUT_MS = 20 * 1000;
+
+    protected Context mContext;
+    protected Bundle mResultExtras;
+    private CountDownLatch mLatch;
+    private ActivityDoneReceiver mActivityDoneReceiver = null;
+    private TestStartActivity mActivity;
+    private Utils.TestcaseType mTestCaseType;
+
+    public VoiceSettingsTestBase() {
+        super(TestStartActivity.class);
+    }
+
+    @Override
+    protected void setUp() throws Exception {
+        super.setUp();
+        mContext = getInstrumentation().getTargetContext();
+    }
+
+    @Override
+    protected void tearDown() throws Exception {
+        mContext.unregisterReceiver(mActivityDoneReceiver);
+        super.tearDown();
+    }
+
+    protected void startTestActivity(String intentSuffix) {
+        Intent intent = new Intent();
+        intent.setAction("android.intent.action.TEST_START_ACTIVITY_" + intentSuffix);
+        intent.setComponent(new ComponentName(getInstrumentation().getContext(),
+                TestStartActivity.class));
+        setActivityIntent(intent);
+        mActivity = getActivity();
+    }
+
+    protected void registerBroadcastReceiver(Utils.TestcaseType testCaseType) throws Exception {
+        mTestCaseType = testCaseType;
+        mLatch = new CountDownLatch(1);
+        if (mActivityDoneReceiver != null) {
+            mContext.unregisterReceiver(mActivityDoneReceiver);
+        }
+        mActivityDoneReceiver = new ActivityDoneReceiver();
+        mContext.registerReceiver(mActivityDoneReceiver,
+                new IntentFilter(Utils.BROADCAST_INTENT + testCaseType.toString()));
+    }
+
+    protected boolean startTestAndWaitForBroadcast(Utils.TestcaseType testCaseType)
+            throws Exception {
+        Log.i(TAG, "Begin Testing: " + testCaseType);
+        registerBroadcastReceiver(testCaseType);
+        mActivity.startTest(testCaseType.toString());
+        if (!mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)) {
+            fail("Failed to receive broadcast in " + TIMEOUT_MS + "msec");
+            return false;
+        }
+        return true;
+    }
+
+    class ActivityDoneReceiver extends BroadcastReceiver {
+        @Override
+        public void onReceive(Context context, Intent intent) {
+            if (intent.getAction().equals(
+                    Utils.BROADCAST_INTENT +
+                        VoiceSettingsTestBase.this.mTestCaseType.toString())) {
+                Bundle extras = intent.getExtras();
+                Log.i(TAG, "received_broadcast for " + Utils.toBundleString(extras));
+                VoiceSettingsTestBase.this.mResultExtras = extras;
+                mLatch.countDown();
+            }
+        }
+    }
+}
diff --git a/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
new file mode 100644
index 0000000..8c2efbe
--- /dev/null
+++ b/tests/tests/voicesettings/src/android/voicesettings/cts/ZenModeTest.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 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.
+ */
+
+package android.voicesettings.cts;
+
+import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_ENABLED;
+import static android.provider.Settings.EXTRA_DO_NOT_DISTURB_MODE_MINUTES;
+
+import android.provider.Settings;
+import android.provider.Settings.Global;
+import android.util.Log;
+
+import common.src.android.voicesettings.common.Utils;
+
+public class ZenModeTest extends VoiceSettingsTestBase {
+    static final String TAG = "ZenModeTest";
+
+    // The following are hidden in frameworks/base/core/java/android/provider/Settings.java
+    // If they weren't, we could have used them directly here.
+    private static final String ZEN_MODE = "zen_mode";
+    private static final int ZEN_MODE_IS_OFF = 0;
+    private static final int ZEN_MODE_IS_ALARMS = 3;
+
+    public ZenModeTest() {
+        super();
+    }
+
+    public void testAll() throws Exception {
+        startTestActivity("ZEN_MODE");
+        int mode = getMode();
+        Log.i(TAG, "Before testing, zen-mode is set to: " + mode);
+        if (mode == ZEN_MODE_IS_OFF) {
+            // mode is currently OFF.
+            // run a test to turn it on.
+            // After successful run of the test, run a test to turn it back off.
+            if (!runTest(Utils.TestcaseType.ZEN_MODE_ON, ZEN_MODE_IS_ALARMS)) {
+                // the test failed. don't test the next one.
+                return;
+            }
+            runTest(Utils.TestcaseType.ZEN_MODE_OFF, ZEN_MODE_IS_OFF);
+        } else {
+            // mode is currently ON.
+            // run a test to turn it off.
+            // After successful run of the test, run a test to turn it back on.
+            if (!runTest(Utils.TestcaseType.ZEN_MODE_OFF, ZEN_MODE_IS_OFF)) {
+                // the test failed. don't test the next one.
+                return;
+            }
+            runTest(Utils.TestcaseType.ZEN_MODE_ON, ZEN_MODE_IS_ALARMS);
+        }
+    }
+
+    private boolean runTest(Utils.TestcaseType test, int expectedMode) throws Exception {
+        if (!startTestAndWaitForBroadcast(test)) {
+            return false;
+        }
+
+        // verify the test results
+        int mode = getMode();
+        Log.i(TAG, "After testing, zen-mode is set to: " + mode);
+        assertEquals(expectedMode, mode);
+        Log.i(TAG, "results_received: " + Utils.toBundleString(mResultExtras));
+        assertNotNull(mResultExtras);
+        if (expectedMode == ZEN_MODE_IS_ALARMS) {
+            assertTrue(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED));
+            assertEquals(Utils.NUM_MINUTES_FOR_ZENMODE,
+                    mResultExtras.getInt(EXTRA_DO_NOT_DISTURB_MODE_MINUTES));
+        } else {
+            assertFalse(mResultExtras.getBoolean(EXTRA_DO_NOT_DISTURB_MODE_ENABLED));
+        }
+        Log.i(TAG, "Successfully Tested: " + test);
+        return true;
+    }
+
+    private int getMode() throws Exception {
+        return Settings.Global.getInt(mContext.getContentResolver(), ZEN_MODE);
+    }
+}