Merge "Add connection tests" into mnc-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/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
index 48ce03c..0a397e8 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/camera/video/CameraVideoActivity.java
@@ -268,6 +268,7 @@
@Override
public void onCompletion(MediaPlayer mp) {
isPlayingBack = false;
+ mPlaybackView.stopPlayback();
captureButton.setEnabled(true);
mStatusLabel.setText(getResources().getString(R.string.status_ready));
}
@@ -562,7 +563,7 @@
if (matchedSize == null) {
for (int i = mPreviewSizes.size() - 1; i >= 0; i--) {
if (Math.abs((float)mPreviewSizes.get(i).width * recordSize.height /
- mPreviewSizes.get(i).height / recordSize.width - 1) < 0.1) {
+ mPreviewSizes.get(i).height / recordSize.width - 1) < 0.12) {
matchedSize = mCamera.new Size(mPreviewSizes.get(i).width,
mPreviewSizes.get(i).height);
break;
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/AndroidManifest.xml b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
index 9d8f615..98fe92a 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/AndroidManifest.xml
@@ -23,4 +23,6 @@
<instrumentation android:name="android.support.test.runner.AndroidJUnitRunner"
android:targetPackage="com.android.cts.externalstorageapp" />
+ <uses-permission android:name="android.permission.INTERNET" />
+
</manifest>
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/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
index 425815f..5d492b2 100644
--- a/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
+++ b/hostsidetests/appsecurity/test-apps/ExternalStorageApp/src/com/android/cts/externalstorageapp/ExternalStorageTest.java
@@ -27,10 +27,23 @@
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.getAllPackageSpecificPaths;
import static com.android.cts.externalstorageapp.CommonExternalStorageTest.readInt;
+import android.app.DownloadManager;
+import android.app.DownloadManager.Query;
+import android.app.DownloadManager.Request;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.database.Cursor;
+import android.net.Uri;
import android.os.Environment;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
+import android.text.format.DateUtils;
import java.io.File;
+import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
/**
@@ -70,6 +83,43 @@
}
/**
+ * Verify that we can't download things outside package directory.
+ */
+ public void testDownloadManager() throws Exception {
+ final DownloadManager dm = getContext().getSystemService(DownloadManager.class);
+ try {
+ final Uri source = Uri.parse("http://www.example.com");
+ final File target = new File(
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS), "meow");
+ dm.enqueue(new Request(source).setDestinationUri(Uri.fromFile(target)));
+ fail("Unexpected success writing outside package directory");
+ } catch (SecurityException expected) {
+ }
+ }
+
+ /**
+ * Verify that we can download things into our package directory.
+ */
+ public void testDownloadManagerPackage() throws Exception {
+ final DownloadManager dm = getContext().getSystemService(DownloadManager.class);
+ final DownloadCompleteReceiver receiver = new DownloadCompleteReceiver();
+ try {
+ IntentFilter intentFilter = new IntentFilter(DownloadManager.ACTION_DOWNLOAD_COMPLETE);
+ mContext.registerReceiver(receiver, intentFilter);
+
+ final Uri source = Uri.parse("http://www.example.com");
+ final File target = new File(
+ getContext().getExternalFilesDir(Environment.DIRECTORY_DOWNLOADS), "meow");
+
+ final long id = dm.enqueue(new Request(source).setDestinationUri(Uri.fromFile(target)));
+ receiver.waitForDownloadComplete(30 * DateUtils.SECOND_IN_MILLIS, id);
+ assertSuccessfulDownload(id, target);
+ } finally {
+ mContext.unregisterReceiver(receiver);
+ }
+ }
+
+ /**
* Verify we can read only our gifts.
*/
public void doVerifyGifts() throws Exception {
@@ -83,4 +133,69 @@
final File write = buildGiftForPackage(getContext(), PACKAGE_WRITE);
assertFileNoAccess(write);
}
+
+ /**
+ * Shamelessly borrowed from DownloadManagerTest.java
+ */
+ private static class DownloadCompleteReceiver extends BroadcastReceiver {
+ private HashSet<Long> mCompleteIds = new HashSet<>();
+
+ public DownloadCompleteReceiver() {
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ synchronized (mCompleteIds) {
+ mCompleteIds.add(intent.getLongExtra(DownloadManager.EXTRA_DOWNLOAD_ID, -1));
+ mCompleteIds.notifyAll();
+ }
+ }
+
+ private boolean isCompleteLocked(long... ids) {
+ for (long id : ids) {
+ if (!mCompleteIds.contains(id)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ public void waitForDownloadComplete(long timeoutMillis, long... waitForIds)
+ throws InterruptedException {
+ if (waitForIds.length == 0) {
+ throw new IllegalArgumentException("Missing IDs to wait for");
+ }
+
+ final long startTime = SystemClock.elapsedRealtime();
+ do {
+ synchronized (mCompleteIds) {
+ mCompleteIds.wait(timeoutMillis);
+ if (isCompleteLocked(waitForIds)) return;
+ }
+ } while ((SystemClock.elapsedRealtime() - startTime) < timeoutMillis);
+
+ throw new InterruptedException("Timeout waiting for IDs " + Arrays.toString(waitForIds)
+ + "; received " + mCompleteIds.toString()
+ + ". Make sure you have WiFi or some other connectivity for this test.");
+ }
+ }
+
+ private void assertSuccessfulDownload(long id, File location) {
+ final DownloadManager dm = getContext().getSystemService(DownloadManager.class);
+
+ Cursor cursor = null;
+ try {
+ cursor = dm.query(new Query().setFilterById(id));
+ assertTrue(cursor.moveToNext());
+ assertEquals(DownloadManager.STATUS_SUCCESSFUL, cursor.getInt(
+ cursor.getColumnIndex(DownloadManager.COLUMN_STATUS)));
+ assertEquals(Uri.fromFile(location).toString(),
+ cursor.getString(cursor.getColumnIndex(DownloadManager.COLUMN_LOCAL_URI)));
+ assertTrue(location.exists());
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
}
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/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
index a4cf39d..727b47f 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PermissionsTest.java
@@ -80,6 +80,7 @@
public void testPermissionGrantState() throws Exception {
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
assertPermissionGrantState(PackageManager.PERMISSION_DENIED);
+ assertPermissionRequest(PackageManager.PERMISSION_DENIED);
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
// Should stay denied
@@ -87,12 +88,11 @@
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
assertPermissionGrantState(PackageManager.PERMISSION_GRANTED);
+ assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
// Should stay granted
assertPermissionGrantState(PackageManager.PERMISSION_GRANTED);
-
- assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
}
public void testPermissionPolicy() throws Exception {
@@ -102,12 +102,22 @@
assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
assertPermissionRequest(PackageManager.PERMISSION_DENIED);
+ // permission should be locked, so changing the policy should not change the grant state
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_PROMPT);
+ assertPermissionRequest(PackageManager.PERMISSION_DENIED);
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
+ assertPermissionRequest(PackageManager.PERMISSION_DENIED);
// reset permission to denied and unlocked
assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
+ // permission should be locked, so changing the policy should not change the grant state
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_PROMPT);
+ assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
+ assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_PROMPT);
}
@@ -134,6 +144,46 @@
assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
}
+ public void testPermissionUpdate_setDeniedState() throws Exception {
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_DENIED);
+ }
+
+ public void testPermissionUpdate_setAutoDeniedPolicy() throws Exception {
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY);
+ assertPermissionRequest(PackageManager.PERMISSION_DENIED);
+ }
+
+ public void testPermissionUpdate_checkDenied() throws Exception {
+ assertPermissionRequest(PackageManager.PERMISSION_DENIED);
+ assertPermissionGrantState(PackageManager.PERMISSION_DENIED);
+ }
+
+ public void testPermissionUpdate_setGrantedState() throws Exception {
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertSetPermissionGrantState(DevicePolicyManager.PERMISSION_GRANT_STATE_GRANTED);
+ }
+
+ public void testPermissionUpdate_setAutoGrantedPolicy() throws Exception {
+ assertEquals(mDevicePolicyManager.getPermissionGrantState(ADMIN_RECEIVER_COMPONENT,
+ PERMISSION_APP_PACKAGE_NAME, PERMISSION_NAME),
+ DevicePolicyManager.PERMISSION_GRANT_STATE_DEFAULT);
+ assertSetPermissionPolicy(DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT);
+ assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
+ }
+
+ public void testPermissionUpdate_checkGranted() throws Exception {
+ assertPermissionRequest(PackageManager.PERMISSION_GRANTED);
+ assertPermissionGrantState(PackageManager.PERMISSION_GRANTED);
+ }
+
public void testPermissionGrantStatePreMApp() throws Exception {
// These tests are to make sure that pre-M apps are not granted runtime permissions
// by a profile owner
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 5904a6f..374f2f9 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -91,7 +91,7 @@
}
final String result = device.executeShellCommand(
- "pm install --user " + userId + " " + remotePath);
+ "pm install -r --user " + userId + " " + remotePath);
assertTrue(result, result.contains("\nSuccess"));
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index f53e41f..15f5368 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -501,6 +501,50 @@
"testPermissionMixedPolicies", mUserId));
}
+ public void testPermissionAppUpdate() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_setDeniedState", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkDenied", mUserId));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkDenied", mUserId));
+
+ assertNull(getDevice().uninstallPackage(PERMISSIONS_APP_PKG));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_setGrantedState", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkGranted", mUserId));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkGranted", mUserId));
+
+ assertNull(getDevice().uninstallPackage(PERMISSIONS_APP_PKG));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_setAutoDeniedPolicy", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkDenied", mUserId));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkDenied", mUserId));
+
+ assertNull(getDevice().uninstallPackage(PERMISSIONS_APP_PKG));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_setAutoGrantedPolicy", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkGranted", mUserId));
+ installAppAsUser(PERMISSIONS_APP_APK, mUserId);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".PermissionsTest",
+ "testPermissionUpdate_checkGranted", mUserId));
+ }
+
public void testPermissionGrantPreMApp() throws Exception {
if (!mHasFeature) {
return;
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index cb50f16..1542ea7 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -689,7 +689,7 @@
}
private void checkSignalStrength(String[] parts) {
- assertEquals(9, parts.length);
+ assertTrue(parts.length >= 9);
assertInteger(parts[4]); // none
assertInteger(parts[5]); // poor
assertInteger(parts[6]); // moderate
diff --git a/hostsidetests/monkey/test-apps/CtsMonkeyApp/AndroidManifest.xml b/hostsidetests/monkey/test-apps/CtsMonkeyApp/AndroidManifest.xml
index 5584652..efb1288 100644
--- a/hostsidetests/monkey/test-apps/CtsMonkeyApp/AndroidManifest.xml
+++ b/hostsidetests/monkey/test-apps/CtsMonkeyApp/AndroidManifest.xml
@@ -20,7 +20,7 @@
<activity
android:name=".MonkeyActivity"
- android:screenOrientation="nosensor">
+ android:screenOrientation="locked">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
@@ -29,7 +29,7 @@
<activity
android:name=".BaboonActivity"
- android:screenOrientation="nosensor">
+ android:screenOrientation="locked">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.MONKEY" />
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/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index c477128..479f72a 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -174,5 +174,22 @@
"android.hardware.camera2.cts.SurfaceViewPreviewTest#testPreparePerformance"
],
bug: 17989532
+},
+{
+ description: "The new AE/AF trigger tests are not yet passing on all devices",
+ names: [
+ "android.hardware.camera2.cts.RobustnessTest#testBasicTriggerSequence",
+ "android.hardware.camera2.cts.RobustnessTest#testSimultaneousTriggers",
+ "android.hardware.camera2.cts.RobustnessTest#testAfThenAeTrigger",
+ "android.hardware.camera2.cts.RobustnessTest#testAeThenAfTrigger"
+ ],
+ bug: 22180706
+},
+{
+ description: "The new create session test is not yet passing on all devices",
+ names: [
+ "android.hardware.camera2.cts.CameraDeviceTest#testCreateSessions"
+ ],
+ bug: 22092756
}
]
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/camera2/cts/RobustnessTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
index 4b213fe..5e1f399 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RobustnessTest.java
@@ -19,11 +19,13 @@
import static android.hardware.camera2.cts.CameraTestUtils.*;
import static android.hardware.camera2.cts.RobustnessTest.MaxStreamSizes.*;
+import android.content.Context;
import android.graphics.ImageFormat;
import android.graphics.SurfaceTexture;
import android.hardware.camera2.CameraCaptureSession;
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
import android.hardware.camera2.CaptureRequest;
import android.hardware.camera2.CaptureResult;
import android.hardware.camera2.TotalCaptureResult;
@@ -38,7 +40,9 @@
import android.media.ImageWriter;
import android.util.Log;
import android.util.Size;
+import android.view.Display;
import android.view.Surface;
+import android.view.WindowManager;
import com.android.ex.camera2.blocking.BlockingSessionCallback;
@@ -208,7 +212,7 @@
openDevice(id);
// Find the concrete max sizes for each format/resolution combination
- MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id);
+ MaxStreamSizes maxSizes = new MaxStreamSizes(mStaticInfo, id, getContext());
String streamConfigurationMapString =
mStaticInfo.getCharacteristics().get(
@@ -320,7 +324,7 @@
for (String id : mCameraIds) {
CameraCharacteristics cc = mCameraManager.getCameraCharacteristics(id);
StaticMetadata staticInfo = new StaticMetadata(cc);
- MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id);
+ MaxStreamSizes maxSizes = new MaxStreamSizes(staticInfo, id, getContext());
// Skip the test for legacy devices.
if (staticInfo.isHardwareLevelLegacy()) {
@@ -1027,7 +1031,7 @@
static final int VGA = 3;
static final int RESOLUTION_COUNT = 4;
- public MaxStreamSizes(StaticMetadata sm, String cameraId) {
+ public MaxStreamSizes(StaticMetadata sm, String cameraId, Context context) {
Size[] privSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.PRIVATE,
StaticMetadata.StreamDirection.Output);
Size[] yuvSizes = sm.getAvailableSizesForFormatChecked(ImageFormat.YUV_420_888,
@@ -1035,11 +1039,13 @@
Size[] jpegSizes = sm.getJpegOutputSizesChecked();
Size[] rawSizes = sm.getRawOutputSizesChecked();
+ Size maxPreviewSize = getMaxPreviewSize(context, cameraId);
+
maxRawSize = (rawSizes.length != 0) ? CameraTestUtils.getMaxSize(rawSizes) : null;
- maxPrivSizes[PREVIEW] = getMaxSize(privSizes, PREVIEW_SIZE_BOUND);
- maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, PREVIEW_SIZE_BOUND);
- maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, PREVIEW_SIZE_BOUND);
+ maxPrivSizes[PREVIEW] = getMaxSize(privSizes, maxPreviewSize);
+ maxYuvSizes[PREVIEW] = getMaxSize(yuvSizes, maxPreviewSize);
+ maxJpegSizes[PREVIEW] = getMaxSize(jpegSizes, maxPreviewSize);
maxPrivSizes[RECORD] = getMaxRecordingSize(cameraId);
maxYuvSizes[RECORD] = getMaxRecordingSize(cameraId);
@@ -1486,4 +1492,35 @@
return sz;
}
+ private static Size getMaxPreviewSize(Context context, String cameraId) {
+ try {
+ WindowManager windowManager =
+ (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
+ Display display = windowManager.getDefaultDisplay();
+
+ int width = display.getWidth();
+ int height = display.getHeight();
+
+ if (height > width) {
+ height = width;
+ width = display.getHeight();
+ }
+
+ CameraManager camMgr =
+ (CameraManager) context.getSystemService(Context.CAMERA_SERVICE);
+ List<Size> orderedPreviewSizes = CameraTestUtils.getSupportedPreviewSizes(
+ cameraId, camMgr, PREVIEW_SIZE_BOUND);
+
+ if (orderedPreviewSizes != null) {
+ for (Size size : orderedPreviewSizes) {
+ if (width >= size.getWidth() &&
+ height >= size.getHeight())
+ return size;
+ }
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "getMaxPreviewSize Failed. "+e.toString());
+ }
+ return PREVIEW_SIZE_BOUND;
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
index ec9caaf..58e3903 100644
--- a/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/CameraTest.java
@@ -861,15 +861,16 @@
}
private void testJpegExifByCamera(boolean recording) throws Exception {
- Camera.Parameters parameters = mCamera.getParameters();
if (!recording) mCamera.startPreview();
- double focalLength = parameters.getFocalLength();
Date date = new Date(System.currentTimeMillis());
String localDatetime = new SimpleDateFormat("yyyy:MM:dd HH:").format(date);
mCamera.takePicture(mShutterCallback, mRawPictureCallback, mJpegPictureCallback);
waitForSnapshotDone();
+ Camera.Parameters parameters = mCamera.getParameters();
+ double focalLength = parameters.getFocalLength();
+
// Test various exif tags.
ExifInterface exif = new ExifInterface(JPEG_PATH);
StringBuffer failedCause = new StringBuffer("Jpeg exif test failed:\n");
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
index 0a0607d..18796d4 100755
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -92,8 +92,11 @@
lessThanDpi(density, DENSITY_MEDIUM, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
lessThanDpi(density, DENSITY_LOW, screenSize, SCREENLAYOUT_SIZE_XLARGE)) {
- assertFalse("Device is not expected to be 64-bit", supports64Bit);
- assertMinMemoryMb(424);
+ if (supports64Bit) {
+ assertMinMemoryMb(704);
+ } else {
+ assertMinMemoryMb(424);
+ }
} else if (greaterThanDpi(density, DENSITY_XHIGH, screenSize,
SCREENLAYOUT_SIZE_NORMAL, SCREENLAYOUT_SIZE_SMALL) ||
greaterThanDpi(density, DENSITY_TV, screenSize, SCREENLAYOUT_SIZE_LARGE) ||
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/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
index 7b77ead..d69bf31 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -114,9 +114,9 @@
if (mPreviousEvent == null) {
mMaxTimestamp = event.timestamp;
} else {
- if (event.timestamp < mMaxTimestamp) {
+ if (event.timestamp <= mMaxTimestamp) {
mOutOfOrderEvents.add(new IndexedEventPair(mIndex, event, mPreviousEvent));
- } else if (event.timestamp > mMaxTimestamp) {
+ } else {
mMaxTimestamp = event.timestamp;
}
}
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/KeyFactoryTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyFactoryTest.java
new file mode 100644
index 0000000..ebf3688
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyFactoryTest.java
@@ -0,0 +1,464 @@
+/*
+ * 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.AndroidTestCase;
+import android.test.MoreAsserts;
+
+import com.android.cts.keystore.R;
+
+import java.io.InputStream;
+import java.security.InvalidKeyException;
+import java.security.KeyFactory;
+import java.security.KeyPair;
+import java.security.KeyPairGenerator;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.PrivateKey;
+import java.security.Provider;
+import java.security.Security;
+import java.security.interfaces.ECPublicKey;
+import java.security.interfaces.RSAPublicKey;
+import java.security.spec.ECPrivateKeySpec;
+import java.security.spec.ECPublicKeySpec;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.KeySpec;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.spec.RSAPrivateKeySpec;
+import java.security.spec.RSAPublicKeySpec;
+import java.security.spec.X509EncodedKeySpec;
+import java.security.Provider.Service;
+import java.security.PublicKey;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+public class KeyFactoryTest extends AndroidTestCase {
+ private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_PROVIDER_NAME;
+
+ private static final String[] EXPECTED_ALGORITHMS = {
+ "EC",
+ "RSA",
+ };
+
+ public void testAlgorithmList() {
+ // Assert that Android Keystore Provider exposes exactly the expected KeyFactory 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 ("KeyFactory".equalsIgnoreCase(service.getType())) {
+ String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+ actualAlgsLowerCase.add(algLowerCase);
+ }
+ }
+
+ TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+ expectedAlgsLowerCase.toArray(new String[0]));
+ }
+
+ public void testGetKeySpecWithKeystorePrivateKeyAndKeyInfoReflectsAllAuthorizations()
+ 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_ECB};
+ String[] encryptionPaddings =
+ new String[] {KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1,
+ KeyProperties.ENCRYPTION_PADDING_RSA_OAEP};
+ String[] digests = new String[] {KeyProperties.DIGEST_SHA1,
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512};
+ int purposes = KeyProperties.PURPOSE_DECRYPT | KeyProperties.PURPOSE_SIGN;
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", purposes)
+ .setBlockModes(blockModes)
+ .setEncryptionPaddings(encryptionPaddings)
+ .setDigests(digests)
+ .setKeyValidityStart(keyValidityStart)
+ .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
+ .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
+ .build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ KeyInfo keyInfo = keyFactory.getKeySpec(keyPair.getPrivate(), KeyInfo.class);
+ assertEquals("test1", keyInfo.getKeystoreAlias());
+ 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);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePublicKeyRejectsKeyInfo()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.getKeySpec(keyPair.getPublic(), KeyInfo.class);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePrivateKeyRejectsTransparentKeySpecAndEncodedKeySpec()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ Class<? extends KeySpec> transparentKeySpecClass;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ transparentKeySpecClass = ECPrivateKeySpec.class;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ transparentKeySpecClass = RSAPrivateKeySpec.class;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.getKeySpec(keyPair.getPrivate(), transparentKeySpecClass);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+
+ try {
+ keyFactory.getKeySpec(keyPair.getPrivate(), PKCS8EncodedKeySpec.class);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePublicKeyAcceptsX509EncodedKeySpec()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ PublicKey publicKey = keyPair.getPublic();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ X509EncodedKeySpec x509EncodedSpec =
+ keyFactory.getKeySpec(publicKey, X509EncodedKeySpec.class);
+ MoreAsserts.assertEquals(publicKey.getEncoded(), x509EncodedSpec.getEncoded());
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGetKeySpecWithKeystorePublicKeyAcceptsTransparentKeySpec()
+ throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ PublicKey publicKey = keyPair.getPublic();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ ECPublicKey ecPublicKey = (ECPublicKey) publicKey;
+ ECPublicKeySpec spec =
+ keyFactory.getKeySpec(publicKey, ECPublicKeySpec.class);
+ assertEquals(ecPublicKey.getW(), spec.getW());
+ TestUtils.assertECParameterSpecEqualsIgnoreSeedIfNotPresent(
+ ecPublicKey.getParams(), spec.getParams());
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ RSAPublicKey rsaPublicKey = (RSAPublicKey) publicKey;
+ RSAPublicKeySpec spec =
+ keyFactory.getKeySpec(publicKey, RSAPublicKeySpec.class);
+ assertEquals(rsaPublicKey.getModulus(), spec.getModulus());
+ assertEquals(rsaPublicKey.getPublicExponent(), spec.getPublicExponent());
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyWithNullKeyThrowsInvalidKeyException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(null);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyRejectsNonAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKey key = new SecretKeySpec(new byte[16], algorithm);
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(key);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyAcceptsAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ assertSame(keyPair.getPrivate(), keyFactory.translateKey(keyPair.getPrivate()));
+ assertSame(keyPair.getPublic(), keyFactory.translateKey(keyPair.getPublic()));
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateWithNullSpecThrowsInvalidKeySpecException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(null);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePublicWithNullSpecThrowsInvalidKeySpecException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePublic(null);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateRejectsPKCS8EncodedKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key1_pkcs8;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key2_pkcs8;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+
+ byte[] pkcs8EncodedForm;
+ try (InputStream in = getContext().getResources().openRawResource(resId)) {
+ pkcs8EncodedForm = TestUtils.drain(in);
+ }
+ PKCS8EncodedKeySpec spec = new PKCS8EncodedKeySpec(pkcs8EncodedForm);
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePublicRejectsX509EncodedKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key2_cert;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key1_cert;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+
+ byte[] x509EncodedForm;
+ try (InputStream in = getContext().getResources().openRawResource(resId)) {
+ x509EncodedForm = TestUtils.drain(in);
+ }
+ X509EncodedKeySpec spec = new X509EncodedKeySpec(x509EncodedForm);
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePublic(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateRejectsTransparentKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ Class<? extends KeySpec> keySpecClass;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key2_pkcs8;
+ keySpecClass = ECPrivateKeySpec.class;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key2_pkcs8;
+ keySpecClass = RSAPrivateKeySpec.class;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+ PrivateKey key = TestUtils.getRawResPrivateKey(getContext(), resId);
+ KeyFactory anotherKeyFactory = KeyFactory.getInstance(algorithm);
+ KeySpec spec = anotherKeyFactory.getKeySpec(key, keySpecClass);
+
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePublicRejectsTransparentKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ int resId;
+ Class<? extends KeySpec> keySpecClass;
+ if ("EC".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.ec_key2_cert;
+ keySpecClass = ECPublicKeySpec.class;
+ } else if ("RSA".equalsIgnoreCase(algorithm)) {
+ resId = R.raw.rsa_key2_cert;
+ keySpecClass = RSAPublicKeySpec.class;
+ } else {
+ throw new RuntimeException("Unsupported key algorithm: " + algorithm);
+ }
+ PublicKey key = TestUtils.getRawResX509Certificate(getContext(), resId).getPublicKey();
+ KeyFactory anotherKeyFactory = KeyFactory.getInstance(algorithm);
+ KeySpec spec = anotherKeyFactory.getKeySpec(key, keySpecClass);
+
+ try {
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePublic(spec);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGeneratePrivateAndPublicRejectKeyInfo() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyPairGenerator keyGenerator =
+ KeyPairGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.initialize(new KeyGenParameterSpec.Builder("test1", 0).build());
+ KeyPair keyPair = keyGenerator.generateKeyPair();
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keyPair.getPrivate());
+
+ KeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generatePrivate(keyInfo);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+
+ try {
+ keyFactory.generatePublic(keyInfo);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ private KeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ return KeyFactory.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ }
+}
\ No newline at end of file
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/SecretKeyFactoryTest.java b/tests/tests/keystore/src/android/keystore/cts/SecretKeyFactoryTest.java
new file mode 100644
index 0000000..2bb8ecd
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/SecretKeyFactoryTest.java
@@ -0,0 +1,236 @@
+/*
+ * 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.InvalidKeyException;
+import java.security.NoSuchAlgorithmException;
+import java.security.NoSuchProviderException;
+import java.security.Provider;
+import java.security.Security;
+import java.security.spec.InvalidKeySpecException;
+import java.security.Provider.Service;
+import java.util.Arrays;
+import java.util.Date;
+import java.util.HashSet;
+import java.util.Locale;
+import java.util.Set;
+
+import javax.crypto.KeyGenerator;
+import javax.crypto.SecretKey;
+import javax.crypto.SecretKeyFactory;
+import javax.crypto.spec.SecretKeySpec;
+
+public class SecretKeyFactoryTest 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",
+ };
+
+ public void testAlgorithmList() {
+ // Assert that Android Keystore Provider exposes exactly the expected SecretKeyFactory
+ // 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 ("SecretKeyFactory".equalsIgnoreCase(service.getType())) {
+ String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+ actualAlgsLowerCase.add(algLowerCase);
+ }
+ }
+
+ TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+ expectedAlgsLowerCase.toArray(new String[0]));
+ }
+
+ public void testGetKeySpecWithKeystoreKeyAndKeyInfoReflectsAllAuthorizations()
+ 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_CTR, KeyProperties.BLOCK_MODE_ECB};
+ 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 =
+ KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.init(new KeyGenParameterSpec.Builder("test1", purposes)
+ .setBlockModes(blockModes)
+ .setEncryptionPaddings(encryptionPaddings)
+ .setDigests(digests)
+ .setKeyValidityStart(keyValidityStart)
+ .setKeyValidityForOriginationEnd(keyValidityForOriginationEnd)
+ .setKeyValidityForConsumptionEnd(keyValidityForConsumptionEnd)
+ .build());
+ SecretKey key = keyGenerator.generateKey();
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ KeyInfo keyInfo = (KeyInfo) keyFactory.getKeySpec(key, KeyInfo.class);
+ assertEquals("test1", keyInfo.getKeystoreAlias());
+ 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);
+ }
+ }
+ }
+
+ public void testTranslateKeyWithNullKeyThrowsInvalidKeyException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(null);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyRejectsNonAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKey key = new SecretKeySpec(new byte[16], algorithm);
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.translateKey(key);
+ fail();
+ } catch (InvalidKeyException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testTranslateKeyAcceptsAndroidKeystoreKeys() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyGenerator keyGenerator =
+ KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.init(new KeyGenParameterSpec.Builder("test1", 0).build());
+ SecretKey key = keyGenerator.generateKey();
+
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ assertSame(key, keyFactory.translateKey(key));
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGenerateSecretWithNullSpecThrowsInvalidKeySpecException() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generateSecret(null);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGenerateSecretRejectsSecretKeySpec() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generateSecret(new SecretKeySpec(new byte[16], algorithm));
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testGenerateSecretRejectsKeyInfo() throws Exception {
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ KeyGenerator keyGenerator =
+ KeyGenerator.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ keyGenerator.init(new KeyGenParameterSpec.Builder("test1", 0).build());
+ SecretKey keystoreKey = keyGenerator.generateKey();
+ KeyInfo keyInfo = TestUtils.getKeyInfo(keystoreKey);
+
+ SecretKeyFactory keyFactory = getKeyFactory(algorithm);
+ try {
+ keyFactory.generateSecret(keyInfo);
+ fail();
+ } catch (InvalidKeySpecException expected) {}
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ private SecretKeyFactory getKeyFactory(String algorithm) throws NoSuchAlgorithmException,
+ NoSuchProviderException {
+ return SecretKeyFactory.getInstance(algorithm, EXPECTED_PROVIDER_NAME);
+ }
+}
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 6ba1aeb..968a382 100644
--- a/tests/tests/media/src/android/media/cts/AudioRecordTest.java
+++ b/tests/tests/media/src/android/media/cts/AudioRecordTest.java
@@ -321,6 +321,9 @@
// Test AudioRecord.Builder to verify the observed configuration of an AudioRecord built with
// an empty Builder matches the documentation / expected values
public void testAudioRecordBuilderDefault() throws Exception {
+ if (!hasMicrophone()) {
+ return;
+ }
// constants for test
final String TEST_NAME = "testAudioRecordBuilderDefault";
// expected values below match the AudioRecord.Builder documentation
@@ -352,6 +355,9 @@
// Test AudioRecord.Builder to verify the observed configuration of an AudioRecord built with
// an incomplete AudioFormat matches the documentation / expected values
public void testAudioRecordBuilderPartialFormat() throws Exception {
+ if (!hasMicrophone()) {
+ return;
+ }
// constants for test
final String TEST_NAME = "testAudioRecordBuilderPartialFormat";
final int expectedRate = 16000;
@@ -380,6 +386,9 @@
// Test AudioRecord.Builder to verify the observed configuration of an AudioRecord matches
// the parameters used in the builder
public void testAudioRecordBuilderParams() throws Exception {
+ if (!hasMicrophone()) {
+ return;
+ }
// constants for test
final String TEST_NAME = "testAudioRecordBuilderParams";
final int expectedRate = 8000;
@@ -418,6 +427,9 @@
// Test AudioRecord to ensure we can build after a failure.
public void testAudioRecordBufferSize() throws Exception {
+ if (!hasMicrophone()) {
+ return;
+ }
// constants for test
final String TEST_NAME = "testAudioRecordBufferSize";
@@ -678,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;
}
@@ -993,6 +1023,7 @@
}
public synchronized void release() {
+ stop();
mAudioRecord.setRecordPositionUpdateListener(null);
mAudioRecord = null;
}
diff --git a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
index d65bbf6..48fcb65 100755
--- a/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
+++ b/tests/tests/media/src/android/media/cts/EncodeVirtualDisplayTest.java
@@ -509,7 +509,7 @@
* Determines if two color values are approximately equal.
*/
private static boolean approxEquals(int expected, int actual) {
- final int MAX_DELTA = 4;
+ final int MAX_DELTA = 7;
return Math.abs(expected - actual) <= MAX_DELTA;
}
diff --git a/tests/tests/media/src/android/media/cts/MediaSyncTest.java b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
index 41d8d89..b334040 100644
--- a/tests/tests/media/src/android/media/cts/MediaSyncTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaSyncTest.java
@@ -56,7 +56,7 @@
private static final String LOG_TAG = "MediaSyncTest";
private final long NO_TIMESTAMP = -1;
- private final int PLAYBACK_RATE_TOLERANCE_PERCENT = 2;
+ private final float FLOAT_PLAYBACK_RATE_TOLERANCE = .02f;
private final long TIME_MEASUREMENT_TOLERANCE_US = 20000;
final int INPUT_RESOURCE_ID =
R.raw.video_480x360_mp4_h264_1350kbps_30fps_aac_stereo_192kbps_44100hz;
@@ -501,9 +501,9 @@
mediaDurationUs,
playTimeUs * playbackRate,
// sync.getTolerance() is MediaSync's tolerance of the playback rate, whereas
- // PLAYBACK_RATE_TOLERANCE_PERCENT / 100 is our test's tolerance.
+ // FLOAT_PLAYBACK_RATE_TOLERANCE is our test's tolerance.
// We need to add both to get an upperbound for allowable error.
- mediaDurationUs * (sync.getTolerance() + PLAYBACK_RATE_TOLERANCE_PERCENT / 100.)
+ mediaDurationUs * (sync.getTolerance() + FLOAT_PLAYBACK_RATE_TOLERANCE)
+ TIME_MEASUREMENT_TOLERANCE_US);
}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java b/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
index d453e3f..f77b245 100644
--- a/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
@@ -28,6 +28,27 @@
private int[] mRequestCodes = {0, 1};
private boolean[] mResults = {false, false};
private int mNumResults = 0;
+ private int mType1 = ResourceManagerTestActivityBase.TYPE_NONSECURE;
+ private int mType2 = ResourceManagerTestActivityBase.TYPE_NONSECURE;
+ private boolean mWaitForReclaim = true;
+
+ private static final String ERROR_INSUFFICIENT_RESOURCES =
+ "* Please check if the omx component is returning OMX_ErrorInsufficientResources " +
+ "properly when the codec failure is due to insufficient resource.\n";
+ private static final String ERROR_SUPPORTS_MULTIPLE_SECURE_CODECS =
+ "* Please check if this platform supports multiple concurrent secure codec " +
+ "instances. If not, please add below setting in /etc/media_codecs.xml in order " +
+ "to pass the test:\n" +
+ " <Settings>\n" +
+ " <Setting name=\"supports-multiple-secure-codecs\" value=\"false\" />\n" +
+ " </Settings>\n";
+ private static final String ERROR_SUPPORTS_SECURE_WITH_NON_SECURE_CODEC =
+ "* Please check if this platform supports co-exist of secure and non-secure codec. " +
+ "If not, please add below setting in /etc/media_codecs.xml in order to pass the " +
+ "test:\n" +
+ " <Settings>\n" +
+ " <Setting name=\"supports-secure-with-non-secure-codec\" value=\"false\" />\n" +
+ " </Settings>\n";
@Override
protected void onCreate(Bundle savedInstanceState) {
@@ -36,7 +57,7 @@
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
- Log.d(TAG, "Activity " + requestCode + " finished.");
+ Log.d(TAG, "Activity " + requestCode + " finished with resultCode " + resultCode);
mResults[requestCode] = (resultCode == RESULT_OK);
if (++mNumResults == mResults.length) {
synchronized (mFinishEvent) {
@@ -45,17 +66,28 @@
}
}
- public boolean testReclaimResource() throws InterruptedException {
+ public void testReclaimResource(int type1, int type2) throws InterruptedException {
+ mType1 = type1;
+ mType2 = type2;
+ if (type1 != ResourceManagerTestActivityBase.TYPE_MIX && type1 != type2) {
+ // in this case, activity2 may not need to reclaim codec from activity1.
+ mWaitForReclaim = false;
+ } else {
+ mWaitForReclaim = true;
+ }
Thread thread = new Thread() {
@Override
public void run() {
try {
Context context = getApplicationContext();
Intent intent1 = new Intent(context, ResourceManagerTestActivity1.class);
+ intent1.putExtra("test-type", mType1);
+ intent1.putExtra("wait-for-reclaim", mWaitForReclaim);
startActivityForResult(intent1, mRequestCodes[0]);
Thread.sleep(5000); // wait for process to launch and allocate all codecs.
Intent intent2 = new Intent(context, ResourceManagerTestActivity2.class);
+ intent2.putExtra("test-type", mType2);
startActivityForResult(intent2, mRequestCodes[1]);
synchronized (mFinishEvent) {
@@ -67,11 +99,29 @@
}
};
thread.start();
- thread.join(10000);
+ thread.join(20000 /* millis */);
+ Thread.sleep(5000); // give the gc a chance to release test activities.
+ boolean result = true;
for (int i = 0; i < mResults.length; ++i) {
- Assert.assertTrue("Result from activity " + i + " is a fail.", mResults[i]);
+ if (!mResults[i]) {
+ Log.e(TAG, "Result from activity " + i + " is a fail.");
+ result = false;
+ break;
+ }
}
- return true;
+ if (!result) {
+ String failMessage = "The potential reasons for the failure:\n";
+ StringBuilder reasons = new StringBuilder();
+ reasons.append(ERROR_INSUFFICIENT_RESOURCES);
+ if (mType1 != mType2) {
+ reasons.append(ERROR_SUPPORTS_SECURE_WITH_NON_SECURE_CODEC);
+ }
+ if (mType1 == ResourceManagerTestActivityBase.TYPE_MIX &&
+ mType2 == ResourceManagerTestActivityBase.TYPE_SECURE) {
+ reasons.append(ERROR_SUPPORTS_MULTIPLE_SECURE_CODECS);
+ }
+ Assert.assertTrue(failMessage + reasons.toString(), result);
+ }
}
}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTest.java b/tests/tests/media/src/android/media/cts/ResourceManagerTest.java
index 5170aac..7305651 100644
--- a/tests/tests/media/src/android/media/cts/ResourceManagerTest.java
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTest.java
@@ -26,11 +26,41 @@
super("com.android.cts.media", ResourceManagerStubActivity.class);
}
- public void testReclaimResource() throws Exception {
+ private void doTestReclaimResource(int type1, int type2) throws Exception {
Bundle extras = new Bundle();
ResourceManagerStubActivity activity = launchActivity(
"com.android.cts.media", ResourceManagerStubActivity.class, extras);
- activity.testReclaimResource();
+ activity.testReclaimResource(type1, type2);
activity.finish();
}
+
+ public void testReclaimResourceNonsecureVsNonsecure() throws Exception {
+ doTestReclaimResource(ResourceManagerTestActivityBase.TYPE_NONSECURE,
+ ResourceManagerTestActivityBase.TYPE_NONSECURE);
+ }
+
+ public void testReclaimResourceNonsecureVsSecure() throws Exception {
+ doTestReclaimResource(ResourceManagerTestActivityBase.TYPE_NONSECURE,
+ ResourceManagerTestActivityBase.TYPE_SECURE);
+ }
+
+ public void testReclaimResourceSecureVsNonsecure() throws Exception {
+ doTestReclaimResource(ResourceManagerTestActivityBase.TYPE_SECURE,
+ ResourceManagerTestActivityBase.TYPE_NONSECURE);
+ }
+
+ public void testReclaimResourceSecureVsSecure() throws Exception {
+ doTestReclaimResource(ResourceManagerTestActivityBase.TYPE_SECURE,
+ ResourceManagerTestActivityBase.TYPE_SECURE);
+ }
+
+ public void testReclaimResourceMixVsNonsecure() throws Exception {
+ doTestReclaimResource(ResourceManagerTestActivityBase.TYPE_MIX,
+ ResourceManagerTestActivityBase.TYPE_NONSECURE);
+ }
+
+ public void testReclaimResourceMixVsSecure() throws Exception {
+ doTestReclaimResource(ResourceManagerTestActivityBase.TYPE_MIX,
+ ResourceManagerTestActivityBase.TYPE_SECURE);
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java
index aff3f03..938324c 100644
--- a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity1.java
@@ -34,9 +34,23 @@
if (allocateCodecs(MAX_INSTANCES) == MAX_INSTANCES) {
// haven't reached the limit with MAX_INSTANCES, report RESULT_OK directly and
// skip additional test.
- setResult(Activity.RESULT_OK);
- finish();
+ finishWithResult(RESULT_OK);
}
useCodecs();
+
+ boolean waitForReclaim = true;
+ Bundle extras = getIntent().getExtras();
+ if (extras != null) {
+ waitForReclaim = extras.getBoolean("wait-for-reclaim", waitForReclaim);
+ }
+
+ try {
+ Thread.sleep(15000); // timeout to ensure the activity is finished.
+ } catch (InterruptedException e) {
+ }
+ stopUsingCodecs();
+ // if the test is supposed to wait for reclaim event then this is a failure, otherwise
+ // this is a pass.
+ finishWithResult(waitForReclaim ? RESULT_CANCELED : RESULT_OK);
}
}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java
index f4c57f5..79d0d2d 100644
--- a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivity2.java
@@ -28,9 +28,7 @@
Log.d(TAG, "onCreate called.");
super.onCreate(savedInstanceState);
- if (allocateCodecs(1) == 1) {
- setResult(Activity.RESULT_OK);
- finish();
- }
+ int result = (allocateCodecs(1 /* max */) == 1) ? RESULT_OK : RESULT_CANCELED;
+ finishWithResult(result);
}
}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java
index 9c48fc4..3e0b111 100644
--- a/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerTestActivityBase.java
@@ -29,6 +29,10 @@
import java.util.Vector;
public class ResourceManagerTestActivityBase extends Activity {
+ public static final int TYPE_NONSECURE = 0;
+ public static final int TYPE_SECURE = 1;
+ public static final int TYPE_MIX = 2;
+
protected String TAG;
private static final int IFRAME_INTERVAL = 10; // 10 seconds between I-frames
private static final String MIME = MediaFormat.MIMETYPE_VIDEO_AVC;
@@ -60,7 +64,7 @@
private MediaCodec.Callback mCallback = new TestCodecCallback();
- private static MediaFormat getTestFormat(VideoCapabilities vcaps) {
+ private static MediaFormat getTestFormat(VideoCapabilities vcaps, boolean securePlayback) {
int maxWidth = vcaps.getSupportedWidths().getUpper();
int maxHeight = vcaps.getSupportedHeightsFor(maxWidth).getUpper();
int maxBitrate = vcaps.getBitrateRange().getUpper();
@@ -73,14 +77,15 @@
format.setInteger(MediaFormat.KEY_BIT_RATE, maxBitrate);
format.setInteger(MediaFormat.KEY_FRAME_RATE, maxFramerate);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, IFRAME_INTERVAL);
+ format.setFeatureEnabled(CodecCapabilities.FEATURE_SecurePlayback, securePlayback);
return format;
}
- private MediaCodecInfo getTestCodecInfo() {
+ private MediaCodecInfo getTestCodecInfo(boolean securePlayback) {
// Use avc decoder for testing.
boolean isEncoder = false;
- MediaCodecList mcl = new MediaCodecList(MediaCodecList.REGULAR_CODECS);
+ MediaCodecList mcl = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo info : mcl.getCodecInfos()) {
if (info.isEncoder() != isEncoder) {
continue;
@@ -88,6 +93,17 @@
CodecCapabilities caps;
try {
caps = info.getCapabilitiesForType(MIME);
+ boolean securePlaybackSupported =
+ caps.isFeatureSupported(CodecCapabilities.FEATURE_SecurePlayback);
+ boolean securePlaybackRequired =
+ caps.isFeatureRequired(CodecCapabilities.FEATURE_SecurePlayback);
+ if ((securePlayback && securePlaybackSupported) ||
+ (!securePlayback && !securePlaybackRequired) ) {
+ Log.d(TAG, "securePlayback " + securePlayback + " will use " + info.getName());
+ } else {
+ Log.d(TAG, "securePlayback " + securePlayback + " skip " + info.getName());
+ continue;
+ }
} catch (IllegalArgumentException e) {
// mime is not supported
continue;
@@ -99,16 +115,48 @@
}
protected int allocateCodecs(int max) {
- MediaCodecInfo info = getTestCodecInfo();
- if (info == null) {
- // skip the test
- return 0;
+ Bundle extras = getIntent().getExtras();
+ int type = TYPE_NONSECURE;
+ if (extras != null) {
+ type = extras.getInt("test-type", type);
+ Log.d(TAG, "type is: " + type);
}
+ boolean shouldSkip = true;
+ boolean securePlayback;
+ if (type == TYPE_NONSECURE || type == TYPE_MIX) {
+ securePlayback = false;
+ MediaCodecInfo info = getTestCodecInfo(securePlayback);
+ if (info != null) {
+ shouldSkip = false;
+ allocateCodecs(max, info, securePlayback);
+ }
+ }
+
+ if (type == TYPE_SECURE || type == TYPE_MIX) {
+ securePlayback = true;
+ MediaCodecInfo info = getTestCodecInfo(securePlayback);
+ if (info != null) {
+ shouldSkip = false;
+ allocateCodecs(max, info, securePlayback);
+ }
+ }
+
+ if (shouldSkip) {
+ Log.d(TAG, "test skipped as there's no supported codec.");
+ finishWithResult(RESULT_OK);
+ }
+
+ Log.d(TAG, "allocateCodecs returned " + mCodecs.size());
+ return mCodecs.size();
+ }
+
+ protected void allocateCodecs(int max, MediaCodecInfo info, boolean securePlayback) {
String name = info.getName();
- VideoCapabilities vcaps = info.getCapabilitiesForType(MIME).getVideoCapabilities();
- MediaFormat format = getTestFormat(vcaps);
- for (int i = 0; i < max; ++i) {
+ CodecCapabilities caps = info.getCapabilitiesForType(MIME);
+ VideoCapabilities vcaps = caps.getVideoCapabilities();
+ MediaFormat format = getTestFormat(vcaps, securePlayback);
+ for (int i = mCodecs.size(); i < max; ++i) {
try {
Log.d(TAG, "Create codec " + name + " #" + i);
MediaCodec codec = MediaCodec.createByCodecName(name);
@@ -129,8 +177,16 @@
break;
}
}
+ }
- return mCodecs.size();
+ protected void finishWithResult(int result) {
+ for (int i = 0; i < mCodecs.size(); ++i) {
+ Log.d(TAG, "release codec #" + i);
+ mCodecs.get(i).release();
+ }
+ setResult(result);
+ finish();
+ Log.d(TAG, "activity finished");
}
private void doUseCodecs() {
@@ -144,33 +200,45 @@
if (e.getErrorCode() == MediaCodec.CodecException.ERROR_RECLAIMED) {
Log.d(TAG, "Remove codec " + current + " from the list");
mCodecs.remove(current);
- setResult(Activity.RESULT_OK);
- finish();
+ mGotReclaimedException = true;
+ mUseCodecs = false;
}
return;
}
}
private Thread mWorkerThread;
+ private volatile boolean mUseCodecs = true;
+ private volatile boolean mGotReclaimedException = false;
protected void useCodecs() {
mWorkerThread = new Thread(new Runnable() {
@Override
public void run() {
- while (true) {
+ while (mUseCodecs) {
doUseCodecs();
+ try {
+ Thread.sleep(50 /* millis */);
+ } catch (InterruptedException e) {}
+ }
+ if (mGotReclaimedException) {
+ finishWithResult(RESULT_OK);
}
}
});
mWorkerThread.start();
}
+ protected void stopUsingCodecs() {
+ mUseCodecs = false;
+ try {
+ mWorkerThread.join(1000);
+ } catch (InterruptedException e) {
+ }
+ }
+
@Override
protected void onDestroy() {
Log.d(TAG, "onDestroy called.");
super.onDestroy();
-
- for (int i = 0; i < mCodecs.size(); ++i) {
- mCodecs.get(i).release();
- }
}
}
diff --git a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
index 5ec3c45..7b4adb0 100644
--- a/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
+++ b/tests/tests/permission/src/android/permission/cts/FileSystemPermissionTest.java
@@ -19,6 +19,7 @@
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.os.Environment;
+import android.os.SystemProperties;
import android.system.Os;
import android.system.OsConstants;
import android.system.StructStatVfs;
@@ -360,6 +361,19 @@
@MediumTest
public void testDeviceTreeCpuCurrent() throws Exception {
+ String arch = System.getProperty("os.arch");
+ String flavor = SystemProperties.get("ro.build.flavor");
+ String[] osVersion = System.getProperty("os.version").split("\\.");
+ /*
+ * Perform the test for only arm-based architecture and
+ * kernel version 3.10 and above.
+ */
+ if (!arch.contains("arm") ||
+ Integer.parseInt(osVersion[0]) < 2 ||
+ (Integer.parseInt(osVersion[0]) == 3 &&
+ Integer.parseInt(osVersion[1]) < 10) ||
+ flavor.contains("sprout") || flavor.contains("sprout_b"))
+ return;
final File f = new File("/proc/device-tree/cpus");
assertTrue(f.exists());
String[] dir = f.list(new FilenameFilter() {
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/renderscript/src/android/renderscript/cts/ForEachTest.java b/tests/tests/renderscript/src/android/renderscript/cts/ForEachTest.java
index b90633e..38831f4 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/ForEachTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/ForEachTest.java
@@ -475,8 +475,8 @@
s.invoke_verify_foo();
s.invoke_foreach_test();
mRS.finish();
- checkForErrors();
waitForMessage();
+ checkForErrors();
}
public void testNoRoot() {
@@ -494,7 +494,7 @@
s.invoke_verify_foo();
s.invoke_noroot_test();
mRS.finish();
- checkForErrors();
waitForMessage();
+ checkForErrors();
}
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/KernelInputTest.java b/tests/tests/renderscript/src/android/renderscript/cts/KernelInputTest.java
new file mode 100644
index 0000000..ecb1b6d
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/KernelInputTest.java
@@ -0,0 +1,619 @@
+/*
+ * 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.renderscript.cts;
+
+import android.renderscript.Allocation;
+
+import android.renderscript.Byte2;
+import android.renderscript.Byte3;
+import android.renderscript.Byte4;
+
+import android.renderscript.Double2;
+import android.renderscript.Double3;
+import android.renderscript.Double4;
+
+import android.renderscript.Element;
+
+import android.renderscript.Float2;
+import android.renderscript.Float3;
+import android.renderscript.Float4;
+
+import android.renderscript.Int2;
+import android.renderscript.Int3;
+import android.renderscript.Int4;
+
+import android.renderscript.Long2;
+import android.renderscript.Long3;
+import android.renderscript.Long4;
+
+import android.renderscript.Short2;
+import android.renderscript.Short3;
+import android.renderscript.Short4;
+
+import android.renderscript.Element;
+
+/*
+ * This checks that modifications to input arguments done by a kernel
+ * are never reflected back to the input Allocation.
+ *
+ * The test works by launching forEach kernels that take different
+ * types of inputs. Each forEach kernel modifies its input in some way.
+ * After running the forEach kernel, the input Allocation is checked
+ * that it remains unmodified.
+ */
+public class KernelInputTest extends RSBaseCompute {
+
+ void checkForErrorsInScript(ScriptC_kernel_input script) {
+ mRS.finish();
+ script.invoke_checkError();
+ waitForMessage();
+ checkForErrors();
+ }
+
+ public void testInputNotModified_char() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I8(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I8(mRS), 1);
+
+ script.set_initial_value_char((byte) 6);
+ ain.copyFrom(new byte[]{ (byte) 6 });
+ script.forEach_clear_input_char(ain, tmp);
+ script.invoke_verify_input_char(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_char2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I8_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I8_2(mRS), 1);
+
+ script.set_initial_value_char2(new Byte2((byte) 127, (byte) 3));
+ ain.copyFrom(new byte[]{ (byte) 127, (byte) 3 });
+ script.forEach_clear_input_char2(ain, tmp);
+ script.invoke_verify_input_char2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_char3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I8_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I8_3(mRS), 1);
+
+ script.set_initial_value_char3(new Byte3((byte) 127, (byte) 3, (byte) 4));
+ ain.copyFrom(new byte[]{ (byte) 127, (byte) 3, (byte) 4, 0 });
+ script.forEach_clear_input_char3(ain, tmp);
+ script.invoke_verify_input_char3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_char4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I8_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I8_4(mRS), 1);
+
+ script.set_initial_value_char4(new Byte4((byte) 127, (byte) 3, (byte) 4, (byte) 7));
+ ain.copyFrom(new byte[]{ (byte) 127, (byte) 3, (byte) 4, (byte) 7 });
+ script.forEach_clear_input_char4(ain, tmp);
+ script.invoke_verify_input_char4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_double() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F64(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F64(mRS), 1);
+
+ script.set_initial_value_double((double) 6);
+ ain.copyFrom(new double[]{ (double) 6 });
+ script.forEach_clear_input_double(ain, tmp);
+ script.invoke_verify_input_double(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_double2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F64_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F64_2(mRS), 1);
+
+ script.set_initial_value_double2(new Double2((double) 127, (double) 3));
+ ain.copyFrom(new double[]{ (double) 127, (double) 3 });
+ script.forEach_clear_input_double2(ain, tmp);
+ script.invoke_verify_input_double2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_double3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F64_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F64_3(mRS), 1);
+
+ script.set_initial_value_double3(new Double3((double) 127, (double) 3, (double) 4));
+ ain.copyFrom(new double[]{ (double) 127, (double) 3, (double) 4, 0 });
+ script.forEach_clear_input_double3(ain, tmp);
+ script.invoke_verify_input_double3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_double4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F64_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F64_4(mRS), 1);
+
+ script.set_initial_value_double4(new Double4((double) 127, (double) 3, (double) 4, (double) 7));
+ ain.copyFrom(new double[]{ (double) 127, (double) 3, (double) 4, (double) 7 });
+ script.forEach_clear_input_double4(ain, tmp);
+ script.invoke_verify_input_double4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_float() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F32(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F32(mRS), 1);
+
+ script.set_initial_value_float((float) 6);
+ ain.copyFrom(new float[]{ (float) 6 });
+ script.forEach_clear_input_float(ain, tmp);
+ script.invoke_verify_input_float(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_float2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F32_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F32_2(mRS), 1);
+
+ script.set_initial_value_float2(new Float2((float) 127, (float) 3));
+ ain.copyFrom(new float[]{ (float) 127, (float) 3 });
+ script.forEach_clear_input_float2(ain, tmp);
+ script.invoke_verify_input_float2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_float3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F32_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F32_3(mRS), 1);
+
+ script.set_initial_value_float3(new Float3((float) 127, (float) 3, (float) 4));
+ ain.copyFrom(new float[]{ (float) 127, (float) 3, (float) 4, 0 });
+ script.forEach_clear_input_float3(ain, tmp);
+ script.invoke_verify_input_float3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_float4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.F32_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.F32_4(mRS), 1);
+
+ script.set_initial_value_float4(new Float4((float) 127, (float) 3, (float) 4, (float) 7));
+ ain.copyFrom(new float[]{ (float) 127, (float) 3, (float) 4, (float) 7 });
+ script.forEach_clear_input_float4(ain, tmp);
+ script.invoke_verify_input_float4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_int() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I32(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I32(mRS), 1);
+
+ script.set_initial_value_int(6);
+ ain.copyFrom(new int[]{ 6 });
+ script.forEach_clear_input_int(ain, tmp);
+ script.invoke_verify_input_int(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_int2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I32_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I32_2(mRS), 1);
+
+ script.set_initial_value_int2(new Int2(127, 3));
+ ain.copyFrom(new int[]{ 127, 3 });
+ script.forEach_clear_input_int2(ain, tmp);
+ script.invoke_verify_input_int2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_int3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I32_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I32_3(mRS), 1);
+
+ script.set_initial_value_int3(new Int3(127, 3, 4));
+ ain.copyFrom(new int[]{ 127, 3, 4, 0 });
+ script.forEach_clear_input_int3(ain, tmp);
+ script.invoke_verify_input_int3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_int4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I32_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I32_4(mRS), 1);
+
+ script.set_initial_value_int4(new Int4(127, 3, 4, 7));
+ ain.copyFrom(new int[]{ 127, 3, 4, 7 });
+ script.forEach_clear_input_int4(ain, tmp);
+ script.invoke_verify_input_int4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_long() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I64(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I64(mRS), 1);
+
+ script.set_initial_value_long((long) 6);
+ ain.copyFrom(new long[]{ (long) 6 });
+ script.forEach_clear_input_long(ain, tmp);
+ script.invoke_verify_input_long(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_long2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I64_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I64_2(mRS), 1);
+
+ script.set_initial_value_long2(new Long2((long) 127, (long) 3));
+ ain.copyFrom(new long[]{ (long) 127, (long) 3 });
+ script.forEach_clear_input_long2(ain, tmp);
+ script.invoke_verify_input_long2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_long3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I64_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I64_3(mRS), 1);
+
+ script.set_initial_value_long3(new Long3((long) 127, (long) 3, (long) 4));
+ ain.copyFrom(new long[]{ (long) 127, (long) 3, (long) 4, 0 });
+ script.forEach_clear_input_long3(ain, tmp);
+ script.invoke_verify_input_long3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_long4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I64_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I64_4(mRS), 1);
+
+ script.set_initial_value_long4(new Long4((long) 127, (long) 3, (long) 4, (long) 7));
+ ain.copyFrom(new long[]{ (long) 127, (long) 3, (long) 4, (long) 7 });
+ script.forEach_clear_input_long4(ain, tmp);
+ script.invoke_verify_input_long4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_short() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I16(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I16(mRS), 1);
+
+ script.set_initial_value_short((short) 6);
+ ain.copyFrom(new short[]{ (short) 6 });
+ script.forEach_clear_input_short(ain, tmp);
+ script.invoke_verify_input_short(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_short2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I16_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I16_2(mRS), 1);
+
+ script.set_initial_value_short2(new Short2((short) 127, (short) 3));
+ ain.copyFrom(new short[]{ (short) 127, (short) 3 });
+ script.forEach_clear_input_short2(ain, tmp);
+ script.invoke_verify_input_short2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_short3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I16_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I16_3(mRS), 1);
+
+ script.set_initial_value_short3(new Short3((short) 127, (short) 3, (short) 4));
+ ain.copyFrom(new short[]{ (short) 127, (short) 3, (short) 4, 0 });
+ script.forEach_clear_input_short3(ain, tmp);
+ script.invoke_verify_input_short3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_short4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.I16_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.I16_4(mRS), 1);
+
+ script.set_initial_value_short4(new Short4((short) 127, (short) 3, (short) 4, (short) 7));
+ ain.copyFrom(new short[]{ (short) 127, (short) 3, (short) 4, (short) 7 });
+ script.forEach_clear_input_short4(ain, tmp);
+ script.invoke_verify_input_short4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uchar() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U8(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U8(mRS), 1);
+
+ script.set_initial_value_uchar((short) 6);
+ ain.copyFrom(new byte[]{ (byte) 6 });
+ script.forEach_clear_input_uchar(ain, tmp);
+ script.invoke_verify_input_uchar(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uchar2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U8_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U8_2(mRS), 1);
+
+ script.set_initial_value_uchar2(new Short2((short) 127, (short) 3));
+ ain.copyFrom(new byte[]{ (byte) 127, (byte) 3 });
+ script.forEach_clear_input_uchar2(ain, tmp);
+ script.invoke_verify_input_uchar2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uchar3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U8_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U8_3(mRS), 1);
+
+ script.set_initial_value_uchar3(new Short3((short) 127, (short) 3, (short) 4));
+ ain.copyFrom(new byte[]{ (byte) 127, (byte) 3, (byte) 4, 0 });
+ script.forEach_clear_input_uchar3(ain, tmp);
+ script.invoke_verify_input_uchar3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uchar4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U8_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U8_4(mRS), 1);
+
+ script.set_initial_value_uchar4(new Short4((short) 127, (short) 3, (short) 4, (short) 7));
+ ain.copyFrom(new byte[]{ (byte) 127, (byte) 3, (byte) 4, (byte) 7 });
+ script.forEach_clear_input_uchar4(ain, tmp);
+ script.invoke_verify_input_uchar4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uint() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U32(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U32(mRS), 1);
+
+ script.set_initial_value_uint((long) 6);
+ ain.copyFrom(new int[]{ 6 });
+ script.forEach_clear_input_uint(ain, tmp);
+ script.invoke_verify_input_uint(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uint2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U32_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U32_2(mRS), 1);
+
+ script.set_initial_value_uint2(new Long2((long) 127, (long) 3));
+ ain.copyFrom(new int[]{ 127, 3 });
+ script.forEach_clear_input_uint2(ain, tmp);
+ script.invoke_verify_input_uint2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uint3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U32_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U32_3(mRS), 1);
+
+ script.set_initial_value_uint3(new Long3((long) 127, (long) 3, (long) 4));
+ ain.copyFrom(new int[]{ 127, 3, 4, 0 });
+ script.forEach_clear_input_uint3(ain, tmp);
+ script.invoke_verify_input_uint3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_uint4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U32_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U32_4(mRS), 1);
+
+ script.set_initial_value_uint4(new Long4((long) 127, (long) 3, (long) 4, (long) 7));
+ ain.copyFrom(new int[]{ 127, 3, 4, 7 });
+ script.forEach_clear_input_uint4(ain, tmp);
+ script.invoke_verify_input_uint4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ulong() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U64(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U64(mRS), 1);
+
+ script.set_initial_value_ulong((long) 6);
+ ain.copyFrom(new long[]{ (long) 6 });
+ script.forEach_clear_input_ulong(ain, tmp);
+ script.invoke_verify_input_ulong(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ulong2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U64_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U64_2(mRS), 1);
+
+ script.set_initial_value_ulong2(new Long2((long) 127, (long) 3));
+ ain.copyFrom(new long[]{ (long) 127, (long) 3 });
+ script.forEach_clear_input_ulong2(ain, tmp);
+ script.invoke_verify_input_ulong2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ulong3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U64_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U64_3(mRS), 1);
+
+ script.set_initial_value_ulong3(new Long3((long) 127, (long) 3, (long) 4));
+ ain.copyFrom(new long[]{ (long) 127, (long) 3, (long) 4, 0 });
+ script.forEach_clear_input_ulong3(ain, tmp);
+ script.invoke_verify_input_ulong3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ulong4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U64_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U64_4(mRS), 1);
+
+ script.set_initial_value_ulong4(new Long4((long) 127, (long) 3, (long) 4, (long) 7));
+ ain.copyFrom(new long[]{ (long) 127, (long) 3, (long) 4, (long) 7 });
+ script.forEach_clear_input_ulong4(ain, tmp);
+ script.invoke_verify_input_ulong4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ushort() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U16(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U16(mRS), 1);
+
+ script.set_initial_value_ushort(6);
+ ain.copyFrom(new short[]{ (short) 6 });
+ script.forEach_clear_input_ushort(ain, tmp);
+ script.invoke_verify_input_ushort(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ushort2() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U16_2(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U16_2(mRS), 1);
+
+ script.set_initial_value_ushort2(new Int2(127, 3));
+ ain.copyFrom(new short[]{ (short) 127, (short) 3 });
+ script.forEach_clear_input_ushort2(ain, tmp);
+ script.invoke_verify_input_ushort2(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ushort3() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U16_3(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U16_3(mRS), 1);
+
+ script.set_initial_value_ushort3(new Int3(127, 3, 4));
+ ain.copyFrom(new short[]{ (short) 127, (short) 3, (short) 4, 0 });
+ script.forEach_clear_input_ushort3(ain, tmp);
+ script.invoke_verify_input_ushort3(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputNotModified_ushort4() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+ Allocation ain = Allocation.createSized(mRS, Element.U16_4(mRS), 1);
+ Allocation tmp = Allocation.createSized(mRS, Element.U16_4(mRS), 1);
+
+ script.set_initial_value_ushort4(new Int4(127, 3, 4, 7));
+ ain.copyFrom(new short[]{ (short) 127, (short) 3, (short) 4, (short) 7 });
+ script.forEach_clear_input_ushort4(ain, tmp);
+ script.invoke_verify_input_ushort4(ain);
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputsNotModified_small() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+
+ Allocation tmp = Allocation.createSized(mRS, ScriptField_small.createElement(mRS), 1);
+ ScriptField_small item = new ScriptField_small(mRS, 1);
+
+ item.set_x(0, new int[]{6}, true);
+ script.set_initial_value_small(item.get(0));
+ script.forEach_clear_input_small(item.getAllocation(), tmp);
+ script.invoke_verify_input_small(item.getAllocation());
+
+ checkForErrorsInScript(script);
+ }
+
+ public void testInputsNotModified_big() {
+ ScriptC_kernel_input script = new ScriptC_kernel_input(mRS);
+
+ Allocation tmp = Allocation.createSized(mRS, ScriptField_big.createElement(mRS), 1);
+ ScriptField_big item = new ScriptField_big(mRS, 1);
+
+ for (int i = 0; i < 100; i++) {
+ int[] input = new int[100];
+
+ input[i] = 6;
+ item.set_x(0, input, true);
+ script.set_initial_value_big(item.get(0));
+ script.forEach_clear_input_big(item.getAllocation(), tmp);
+ script.invoke_verify_input_big(item.getAllocation());
+ }
+
+ checkForErrorsInScript(script);
+ }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/KernelTest.java b/tests/tests/renderscript/src/android/renderscript/cts/KernelTest.java
index 64368b6..90d4fe9 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/KernelTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/KernelTest.java
@@ -475,8 +475,8 @@
s.invoke_verify_foo();
s.invoke_foreach_test();
mRS.finish();
- checkForErrors();
waitForMessage();
+ checkForErrors();
}
public void testNoRoot() {
@@ -494,7 +494,7 @@
s.invoke_verify_foo();
s.invoke_noroot_test();
mRS.finish();
- checkForErrors();
waitForMessage();
+ checkForErrors();
}
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java b/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java
index 0175c38..2ca4f13 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/RSBase.java
@@ -34,7 +34,9 @@
Resources mRes;
private int result;
- private boolean msgHandled;
+ // msgHandled is used to synchronize between waitForMessage() and the
+ // RSMessageHandler thread.
+ private volatile boolean msgHandled;
protected static final int RS_MSG_TEST_PASSED = 100;
protected static final int RS_MSG_TEST_FAILED = 101;
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/SampleTest.java b/tests/tests/renderscript/src/android/renderscript/cts/SampleTest.java
index bc69b0e..a468b9f 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/SampleTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/SampleTest.java
@@ -92,8 +92,8 @@
public void testNearest() {
mScript.invoke_test_RGBA(mAlloc_RGBA_1D, mAlloc_RGBA_2D);
mRS.finish();
- checkForErrors();
waitForMessage();
+ checkForErrors();
}
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/kernel_input.rs b/tests/tests/renderscript/src/android/renderscript/cts/kernel_input.rs
new file mode 100644
index 0000000..2eaca92
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/kernel_input.rs
@@ -0,0 +1,213 @@
+/*
+ * 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.
+ */
+
+
+#pragma version(1)
+#pragma rs java_package_name(foo)
+
+#include "shared.rsh"
+
+static bool failed = false;
+
+/*
+ * This checks that modifications to input arguments done by a kernel
+ * are never reflected back to the input Allocation. In order to do
+ * this, we create kernels that modify their input arguments (the
+ * clear_input_* kernels).
+ *
+ * When the kernels modify their input arguments, these modifications
+ * should not be visible in the underlying Allocation. The
+ * verify_input_* functions can be passed the same Allocation that was
+ * used as an in input to a clear_input_* kernel. The verify_input_*
+ * functions check their input against a global variable.
+ */
+
+// For clear_input_* kernels, we use a volatile qualified input argument
+// to try to inhibit any optimizations that would result in the write to
+// the input argument being optimized out by the compiler.
+
+#define COMMON_TEST_CODE(type) \
+ type initial_value_##type; \
+ type RS_KERNEL clear_input_##type(volatile type in) { \
+ rsDebug(#type, in); \
+ in -= in; \
+ rsDebug(#type, in); \
+ return in; \
+ }
+
+#define SCALAR_TEST(type) \
+ COMMON_TEST_CODE(type) \
+ void verify_input_##type(rs_allocation alloc) { \
+ type elem = rsGetElementAt_##type(alloc, 0); \
+ _RS_ASSERT(elem == initial_value_##type); \
+ }
+
+#define VEC2_TEST(type) \
+ COMMON_TEST_CODE(type) \
+ void verify_input_##type(rs_allocation alloc) { \
+ type elem = rsGetElementAt_##type(alloc, 0); \
+ _RS_ASSERT(elem[0] == initial_value_##type[0]); \
+ _RS_ASSERT(elem[1] == initial_value_##type[1]); \
+ }
+
+#define VEC3_TEST(type) \
+ COMMON_TEST_CODE(type) \
+ void verify_input_##type(rs_allocation alloc) { \
+ type elem = rsGetElementAt_##type(alloc, 0); \
+ _RS_ASSERT(elem[0] == initial_value_##type[0]); \
+ _RS_ASSERT(elem[1] == initial_value_##type[1]); \
+ _RS_ASSERT(elem[2] == initial_value_##type[2]); \
+ }
+
+#define VEC4_TEST(type) \
+ COMMON_TEST_CODE(type) \
+ void verify_input_##type(rs_allocation alloc) { \
+ type elem = rsGetElementAt_##type(alloc, 0); \
+ _RS_ASSERT(elem[0] == initial_value_##type[0]); \
+ _RS_ASSERT(elem[1] == initial_value_##type[1]); \
+ _RS_ASSERT(elem[2] == initial_value_##type[2]); \
+ _RS_ASSERT(elem[3] == initial_value_##type[3]); \
+ }
+
+SCALAR_TEST(char)
+
+VEC2_TEST(char2)
+
+VEC3_TEST(char3)
+
+VEC4_TEST(char4)
+
+SCALAR_TEST(double)
+
+VEC2_TEST(double2)
+
+VEC3_TEST(double3)
+
+VEC4_TEST(double4)
+
+SCALAR_TEST(float)
+
+VEC2_TEST(float2)
+
+VEC3_TEST(float3)
+
+VEC4_TEST(float4)
+
+SCALAR_TEST(int)
+
+VEC2_TEST(int2)
+
+VEC3_TEST(int3)
+
+VEC4_TEST(int4)
+
+SCALAR_TEST(long)
+
+VEC2_TEST(long2)
+
+VEC3_TEST(long3)
+
+VEC4_TEST(long4)
+
+SCALAR_TEST(short)
+
+VEC2_TEST(short2)
+
+VEC3_TEST(short3)
+
+VEC4_TEST(short4)
+
+SCALAR_TEST(uchar)
+
+VEC2_TEST(uchar2)
+
+VEC3_TEST(uchar3)
+
+VEC4_TEST(uchar4)
+
+SCALAR_TEST(uint)
+
+VEC2_TEST(uint2)
+
+VEC3_TEST(uint3)
+
+VEC4_TEST(uint4)
+
+SCALAR_TEST(ulong)
+
+VEC2_TEST(ulong2)
+
+VEC3_TEST(ulong3)
+
+VEC4_TEST(ulong4)
+
+SCALAR_TEST(ushort)
+
+VEC2_TEST(ushort2)
+
+VEC3_TEST(ushort3)
+
+VEC4_TEST(ushort4)
+
+typedef struct small {
+ int x[1];
+} small;
+
+typedef struct big {
+ int x[100];
+} big;
+
+small initial_value_small;
+
+// See comment on volatile above.
+small RS_KERNEL clear_input_small(volatile small in) {
+ rsDebug("in.x", in.x[0]);
+ in.x[0] = 0;
+ rsDebug("in.x", in.x[0]);
+ return in;
+}
+
+void verify_input_small(rs_allocation alloc) {
+ const small *elem = (const small *) rsGetElementAt(alloc, 0);
+ _RS_ASSERT(elem->x[0] == initial_value_small.x[0]);
+}
+
+big initial_value_big;
+
+// See comment on volatile above.
+big RS_KERNEL clear_input_big(volatile big in) {
+ for (size_t i = 0; i < 100; ++i) {
+ rsDebug("in.x", in.x[i]);
+ in.x[i] = 0;
+ rsDebug("in.x", in.x[i]);
+ }
+ return in;
+}
+
+void verify_input_big(rs_allocation alloc) {
+ const big *elem = (const big *) rsGetElementAt(alloc, 0);
+ for (size_t i = 0; i < 100; ++i) {
+ _RS_ASSERT(elem->x[i] == initial_value_big.x[i]);
+ }
+}
+
+void checkError() {
+ if (failed) {
+ rsSendToClientBlocking(RS_MSG_TEST_FAILED);
+ } else {
+ rsSendToClientBlocking(RS_MSG_TEST_PASSED);
+ }
+}
diff --git a/tests/tests/rscpp/librscpptest/Android.mk b/tests/tests/rscpp/librscpptest/Android.mk
index 65a5e2a..df8ce29f 100644
--- a/tests/tests/rscpp/librscpptest/Android.mk
+++ b/tests/tests/rscpp/librscpptest/Android.mk
@@ -38,7 +38,8 @@
clear_object.rs \
foreach.rs \
fe_all.rs \
- noroot.rs
+ noroot.rs \
+ vector.rs
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
LOCAL_C_INCLUDES += frameworks/rs/cpp
diff --git a/tests/tests/rscpp/librscpptest/rs_jni.cpp b/tests/tests/rscpp/librscpptest/rs_jni.cpp
index b362c99..4e60f4b 100644
--- a/tests/tests/rscpp/librscpptest/rs_jni.cpp
+++ b/tests/tests/rscpp/librscpptest/rs_jni.cpp
@@ -24,8 +24,7 @@
#include <RenderScript.h>
#define LOG_TAG "rscpptest"
-#define LOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
-#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
+#define LOGV(...) __android_log_print(ANDROID_LOG_VERBOSE,LOG_TAG,__VA_ARGS__)
using namespace android::RSC;
@@ -72,7 +71,7 @@
for (int i = 0; i < 1000; i++) {
sp<RS> rs = new RS();
r &= rs->init(path);
- LOGE("Native iteration %i, returned %i", i, (int)r);
+ LOGV("Native iteration %i, returned %i", i, (int)r);
}
env->ReleaseStringUTFChars(pathObj, path);
return r;
diff --git a/tests/tests/rscpp/librscpptest/rs_jni_script.cpp b/tests/tests/rscpp/librscpptest/rs_jni_script.cpp
index fd31112..1888341 100644
--- a/tests/tests/rscpp/librscpptest/rs_jni_script.cpp
+++ b/tests/tests/rscpp/librscpptest/rs_jni_script.cpp
@@ -29,6 +29,7 @@
#include <ScriptC_primitives.h>
#include <ScriptC_instance.h>
+#include <ScriptC_vector.h>
using namespace android::RSC;
@@ -134,3 +135,50 @@
return passed;
}
+// Define some reasonable types for use with the vector invoke testing.
+typedef unsigned char uchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+#define TEST_VECTOR_INVOKE(L, U) \
+L temp_##L = 0; \
+vector->invoke_vector_test_##L(temp_##L); \
+U##2 temp_##L##2; \
+vector->invoke_vector_test_##L##2(temp_##L##2); \
+U##3 temp_##L##3; \
+vector->invoke_vector_test_##L##3(temp_##L##3); \
+U##4 temp_##L##4; \
+vector->invoke_vector_test_##L##4(temp_##L##4);
+
+
+/*
+ * Test that vector invoke C++ reflection is working/present.
+ */
+extern "C" JNIEXPORT jboolean JNICALL Java_android_cts_rscpp_RSScriptTest_testVector(JNIEnv * env,
+ jclass obj,
+ jstring pathObj)
+{
+ const char * path = env->GetStringUTFChars(pathObj, nullptr);
+ sp<RS> mRS = new RS();
+ mRS->init(path);
+ env->ReleaseStringUTFChars(pathObj, path);
+ MessageHandlerFunc_t mHandler = rsMsgHandler;
+ mRS->setMessageHandler(mHandler);
+
+ bool passed = true;
+ sp<ScriptC_vector> vector = new ScriptC_vector(mRS);
+
+ TEST_VECTOR_INVOKE(float, Float)
+ TEST_VECTOR_INVOKE(double, Double)
+ TEST_VECTOR_INVOKE(char, Byte)
+ TEST_VECTOR_INVOKE(uchar, UByte)
+ TEST_VECTOR_INVOKE(short, Short)
+ TEST_VECTOR_INVOKE(ushort, UShort)
+ TEST_VECTOR_INVOKE(int, Int)
+ TEST_VECTOR_INVOKE(uint, UInt)
+ TEST_VECTOR_INVOKE(long, Long)
+ TEST_VECTOR_INVOKE(ulong, ULong)
+
+ return passed;
+}
diff --git a/tests/tests/rscpp/librscpptest/vector.rs b/tests/tests/rscpp/librscpptest/vector.rs
new file mode 100644
index 0000000..cdea9b8
--- /dev/null
+++ b/tests/tests/rscpp/librscpptest/vector.rs
@@ -0,0 +1,19 @@
+#include "shared.rsh"
+
+#define MAKE_TEST(T) \
+void vector_test_##T(T t) {} \
+void vector_test_##T##2(T##2 t) {} \
+void vector_test_##T##3(T##3 t) {} \
+void vector_test_##T##4(T##4 t) {} \
+
+MAKE_TEST(float)
+MAKE_TEST(double)
+MAKE_TEST(char)
+MAKE_TEST(uchar)
+MAKE_TEST(short)
+MAKE_TEST(ushort)
+MAKE_TEST(int)
+MAKE_TEST(uint)
+MAKE_TEST(long)
+MAKE_TEST(ulong)
+
diff --git a/tests/tests/rscpp/src/android/cts/rscpp/RSScriptTest.java b/tests/tests/rscpp/src/android/cts/rscpp/RSScriptTest.java
index 72f4a17..fe45b33 100644
--- a/tests/tests/rscpp/src/android/cts/rscpp/RSScriptTest.java
+++ b/tests/tests/rscpp/src/android/cts/rscpp/RSScriptTest.java
@@ -36,4 +36,9 @@
public void testRSScriptTestInstance() {
assertTrue(testInstance(this.getContext().getCacheDir().toString()));
}
+
+ native boolean testVector(String path);
+ public void testRSScriptTestVector() {
+ assertTrue(testVector(this.getContext().getCacheDir().toString()));
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
index 0625d78..940ce48 100644
--- a/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/CallDetailsTest.java
@@ -117,6 +117,10 @@
public void testCallCapabilities() {
assertThat(mCall.getDetails().getCallCapabilities(), is(Integer.class));
assertEquals(CALL_CAPABILITIES, mCall.getDetails().getCallCapabilities());
+ assertTrue(mCall.getDetails().can(Call.Details.CAPABILITY_HOLD));
+ assertTrue(mCall.getDetails().can(Call.Details.CAPABILITY_MUTE));
+ assertFalse(mCall.getDetails().can(Call.Details.CAPABILITY_MANAGE_CONFERENCE));
+ assertFalse(mCall.getDetails().can(Call.Details.CAPABILITY_RESPOND_VIA_TEXT));
}
/**
diff --git a/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java b/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
index 88babef..74d5b17 100644
--- a/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
+++ b/tests/tests/telecom/src/android/telecom/cts/DataObjectUnitTests.java
@@ -26,6 +26,8 @@
import android.net.Uri;
import android.os.Bundle;
import android.os.Parcel;
+import android.os.Process;
+import android.os.UserHandle;
import android.telecom.CallAudioState;
import android.telecom.ConnectionRequest;
import android.telecom.DisconnectCause;
@@ -43,14 +45,12 @@
import java.util.List;
/**
- * Verifies the parcelable interface of all the telecom objects.
+ * Verifies that the setter, getter and parcelable interfaces of the Telecom data objects are
+ * working as intended.
*/
public class DataObjectUnitTests extends InstrumentationTestCase {
- /**
- * Tests the PhoneAccount object creation and recreation from a Parcel.
- */
public void testPhoneAccount() throws Exception {
Context context = getInstrumentation().getContext();
PhoneAccountHandle accountHandle = new PhoneAccountHandle(
@@ -75,6 +75,7 @@
assertEquals(PhoneAccount.CAPABILITY_CALL_PROVIDER, account.getCapabilities());
assertEquals(Color.RED, account.getHighlightColor());
assertEquals(LABEL, account.getShortDescription());
+ assertEquals(LABEL, account.getLabel());
assertEquals(Arrays.asList("tel"), account.getSupportedUriSchemes());
assertEquals(phoneIcon.toString(), account.getIcon().toString());
assertEquals(0, account.describeContents());
@@ -98,9 +99,32 @@
p.recycle();
}
- /**
- * Tests the ConnectionRequest object creation and recreation from a Parcel.
- */
+ public void testPhoneAccountHandle() throws Exception {
+ final ComponentName component = new ComponentName(PACKAGE, COMPONENT);
+ final UserHandle userHandle = Process.myUserHandle();
+ PhoneAccountHandle accountHandle = new PhoneAccountHandle(
+ component,
+ ACCOUNT_ID,
+ userHandle);
+ assertNotNull(accountHandle);
+ assertEquals(component, accountHandle.getComponentName());
+ assertEquals(ACCOUNT_ID, accountHandle.getId());
+ assertEquals(userHandle, accountHandle.getUserHandle());
+ assertEquals(0, accountHandle.describeContents());
+
+ // Create a parcel of the object and recreate the object back
+ // from the parcel.
+ Parcel p = Parcel.obtain();
+ accountHandle.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ PhoneAccountHandle unparcelled = PhoneAccountHandle.CREATOR.createFromParcel(p);
+ assertEquals(accountHandle, unparcelled);
+ assertEquals(accountHandle.getComponentName(), unparcelled.getComponentName());
+ assertEquals(accountHandle.getId(), unparcelled.getId());
+ assertEquals(accountHandle.getUserHandle(), unparcelled.getUserHandle());
+ p.recycle();
+ }
+
public void testConnectionRequest() throws Exception {
PhoneAccountHandle accountHandle = new PhoneAccountHandle(
new ComponentName(PACKAGE, COMPONENT),
@@ -138,11 +162,7 @@
p.recycle();
}
- /**
- * Tests the DisconnectCause object creation and recreation from a Parcel.
- */
public void testDisconnectCause() throws Exception {
- Context context = getInstrumentation().getContext();
final CharSequence label = "Out of service area";
final CharSequence description = "Mobile network not available";
final String reason = "CTS Testing";
@@ -175,9 +195,6 @@
p.recycle();
}
- /**
- * Tests the StatusHints object creation and recreation from a Parcel.
- */
public void testStatusHints() throws Exception {
Context context = getInstrumentation().getContext();
final CharSequence label = "Wi-Fi call";
@@ -214,9 +231,6 @@
p.recycle();
}
- /**
- * Tests the GatewayInfo object creation and recreation from a Parcel.
- */
public void testGatewayInfo() throws Exception {
final CharSequence label = "Wi-Fi call";
Uri originalAddress = Uri.parse("http://www.google.com");
@@ -229,6 +243,7 @@
assertEquals(gatewayAddress, info.getGatewayAddress());
assertEquals(originalAddress, info.getOriginalAddress());
assertEquals(0, info.describeContents());
+ assertFalse(info.isEmpty());
// Create a parcel of the object and recreate the object back
// from the parcel.
@@ -243,9 +258,6 @@
p.recycle();
}
- /**
- * Tests the CallAudioState object creation and recreation from a Parcel.
- */
public void testCallAudioState() throws Exception {
CallAudioState audioState = new CallAudioState(
true,
@@ -255,6 +267,7 @@
assertEquals(CallAudioState.ROUTE_EARPIECE, audioState.getRoute());
assertEquals(CallAudioState.ROUTE_WIRED_OR_EARPIECE, audioState.getSupportedRouteMask());
assertEquals(0, audioState.describeContents());
+ assertEquals("EARPIECE", CallAudioState.audioRouteToString(audioState.getRoute()));
// Create a parcel of the object and recreate the object back
// from the parcel.
@@ -269,4 +282,40 @@
assertEquals(audioState, parcelAudioState);
p.recycle();
}
+
+ public void testVideoProfile() throws Exception {
+ VideoProfile videoProfile = new VideoProfile(VideoProfile.STATE_BIDIRECTIONAL,
+ VideoProfile.QUALITY_HIGH);
+ assertEquals(VideoProfile.STATE_BIDIRECTIONAL, videoProfile.getVideoState());
+ assertEquals(VideoProfile.QUALITY_HIGH, videoProfile.getQuality());
+ assertEquals(0, videoProfile.describeContents());
+ assertEquals("Audio Tx Rx", VideoProfile.videoStateToString(videoProfile.getVideoState()));
+
+ // Create a parcel of the object and recreate the object back from the parcel.
+ Parcel p = Parcel.obtain();
+ videoProfile.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ VideoProfile unparcelled = VideoProfile.CREATOR.createFromParcel(p);
+ assertEquals(videoProfile.getQuality(), unparcelled.getQuality());
+ assertEquals(videoProfile.getVideoState(), unparcelled.getVideoState());
+ p.recycle();
+ }
+
+ public void testCameraCapabilities() throws Exception {
+ VideoProfile.CameraCapabilities cameraCapabilities =
+ new VideoProfile.CameraCapabilities(500, 1000);
+ assertEquals(500, cameraCapabilities.getWidth());
+ assertEquals(1000, cameraCapabilities.getHeight());
+ assertEquals(0, cameraCapabilities.describeContents());
+
+ // Create a parcel of the object and recreate the object back from the parcel.
+ Parcel p = Parcel.obtain();
+ cameraCapabilities.writeToParcel(p, 0);
+ p.setDataPosition(0);
+ VideoProfile.CameraCapabilities unparcelled =
+ VideoProfile.CameraCapabilities.CREATOR.createFromParcel(p);
+ assertEquals(cameraCapabilities.getWidth(), unparcelled.getWidth());
+ assertEquals(cameraCapabilities.getHeight(), unparcelled.getHeight());
+ p.recycle();
+ }
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
index fbbf3ca..d51b14a 100644
--- a/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
+++ b/tests/tests/telecom/src/android/telecom/cts/ExtendedInCallServiceTest.java
@@ -69,7 +69,8 @@
assertMuteState(connection, false);
- inCallService.setMuted(true);
+ // Explicitly call super implementation to enable detection of CTS coverage
+ ((InCallService) inCallService).setMuted(true);
assertMuteState(connection, true);
assertMuteState(inCallService, true);
@@ -95,7 +96,8 @@
// Only test speaker and earpiece modes because the other modes are dependent on having
// a bluetooth headset or wired headset connected.
- inCallService.setAudioRoute(CallAudioState.ROUTE_SPEAKER);
+ // Explicitly call super implementation to enable detection of CTS coverage
+ ((InCallService) inCallService).setAudioRoute(CallAudioState.ROUTE_SPEAKER);
assertAudioRoute(connection, CallAudioState.ROUTE_SPEAKER);
assertAudioRoute(inCallService, CallAudioState.ROUTE_SPEAKER);
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
index bf9d730..460b060 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockConnection.java
@@ -38,57 +38,67 @@
@Override
public void onAnswer() {
- onAnswer(VideoProfile.STATE_AUDIO_ONLY);
+ super.onAnswer();
}
@Override
public void onAnswer(int videoState) {
+ super.onAnswer(videoState);
this.videoState = videoState;
setActive();
}
@Override
public void onReject() {
+ super.onReject();
setDisconnected(new DisconnectCause(DisconnectCause.REJECTED));
}
@Override
public void onHold() {
+ super.onHold();
setOnHold();
}
@Override
public void onUnhold() {
+ super.onUnhold();
setActive();
}
@Override
public void onDisconnect() {
+ super.onDisconnect();
setDisconnected(new DisconnectCause(DisconnectCause.LOCAL));
destroy();
}
@Override
public void onAbort() {
+ super.onAbort();
}
@Override
public void onPlayDtmfTone(char c) {
+ super.onPlayDtmfTone(c);
mDtmfString += c;
}
@Override
public void onStopDtmfTone() {
+ super.onStopDtmfTone();
mDtmfString += ".";
}
@Override
public void onCallAudioStateChanged(CallAudioState state) {
+ super.onCallAudioStateChanged(state);
mCallAudioState = state;
}
@Override
public void onStateChanged(int state) {
+ super.onStateChanged(state);
mState = state;
}
diff --git a/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
index 8ed422f..a1b6b65 100644
--- a/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
+++ b/tests/tests/telecom/src/android/telecom/cts/MockVideoProvider.java
@@ -87,7 +87,7 @@
*/
@Override
public void onSendSessionModifyRequest(VideoProfile fromProfile, VideoProfile toProfile) {
- receiveSessionModifyResponse(Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS,
+ super.receiveSessionModifyResponse(Connection.VideoProvider.SESSION_MODIFY_REQUEST_SUCCESS,
toProfile, toProfile);
mMockConnection.setVideoState(toProfile.getVideoState());
}
@@ -111,7 +111,7 @@
*/
@Override
public void onRequestConnectionDataUsage() {
- setCallDataUsage(DATA_USAGE);
+ super.setCallDataUsage(DATA_USAGE);
}
@Override
@@ -126,11 +126,11 @@
private void handleCameraChange(String cameraId) {
mCameraId = cameraId;
if (CAMERA_FRONT.equals(mCameraId)) {
- changeCameraCapabilities(new VideoProfile.CameraCapabilities(CAMERA_FRONT_DIMENSIONS,
- CAMERA_FRONT_DIMENSIONS));
+ super.changeCameraCapabilities(new VideoProfile.CameraCapabilities(
+ CAMERA_FRONT_DIMENSIONS, CAMERA_FRONT_DIMENSIONS));
} else if (CAMERA_BACK.equals(mCameraId)) {
- changeCameraCapabilities(new VideoProfile.CameraCapabilities(CAMERA_BACK_DIMENSIONS,
- CAMERA_BACK_DIMENSIONS));
+ super.changeCameraCapabilities(new VideoProfile.CameraCapabilities(
+ CAMERA_BACK_DIMENSIONS, CAMERA_BACK_DIMENSIONS));
}
}
@@ -140,7 +140,7 @@
* @param videoQuality The video quality.
*/
public void sendMockVideoQuality(int videoQuality) {
- changeVideoQuality(videoQuality);
+ super.changeVideoQuality(videoQuality);
}
/**
@@ -149,7 +149,7 @@
* @param event The call session event.
*/
public void sendMockCallSessionEvent(int event) {
- handleCallSessionEvent(event);
+ super.handleCallSessionEvent(event);
}
/**
@@ -158,7 +158,7 @@
* @param width The peer width.
*/
public void sendMockPeerWidth(int width) {
- changePeerDimensions(width, width);
+ super.changePeerDimensions(width, width);
}
/**
@@ -167,7 +167,7 @@
* @param request The requested profile.
*/
public void sendMockSessionModifyRequest(VideoProfile request) {
- receiveSessionModifyRequest(request);
+ super.receiveSessionModifyRequest(request);
}
public int getDeviceOrientation() {
diff --git a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
index 26e0c54..5a80802 100755
--- a/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/SmsManagerTest.java
@@ -84,6 +84,8 @@
"51502", // Globe Telecoms
"51503", // Smart Communications
"51505", // Sun Cellular
+ "53001", // Vodafone New Zealand
+ "53024", // NZC
"311870", // Boost Mobile
"311220", // USCC
"311225", // USCC LTE
@@ -100,6 +102,7 @@
"310600", // Cellcom
"31000", // Republic Wireless US
"310026", // T-Mobile US
+ "330120", // OpenMobile communication
// Verizon
"310004",
"310012",
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/uirendering/src/android/uirendering/cts/testclasses/ShaderTests.java b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShaderTests.java
new file mode 100644
index 0000000..85764a6
--- /dev/null
+++ b/tests/tests/uirendering/src/android/uirendering/cts/testclasses/ShaderTests.java
@@ -0,0 +1,136 @@
+/*
+ * 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.uirendering.cts.testclasses;
+
+import android.graphics.Bitmap;
+import android.graphics.BitmapShader;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ComposeShader;
+import android.graphics.Paint;
+import android.graphics.PorterDuff;
+import android.graphics.RadialGradient;
+import android.graphics.Shader;
+import android.test.suitebuilder.annotation.SmallTest;
+import android.uirendering.cts.bitmapcomparers.MSSIMComparer;
+import android.uirendering.cts.bitmapverifiers.ColorVerifier;
+import android.uirendering.cts.testinfrastructure.ActivityTestBase;
+import android.uirendering.cts.testinfrastructure.CanvasClient;
+import com.android.cts.uirendering.R;
+
+public class ShaderTests extends ActivityTestBase {
+ @SmallTest
+ public void testSinglePixelBitmapShader() {
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ Paint mPaint = new Paint();
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ if (mPaint.getShader() == null) {
+ Bitmap shaderBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ shaderBitmap.eraseColor(Color.BLUE);
+ mPaint.setShader(new BitmapShader(shaderBitmap,
+ Shader.TileMode.REPEAT, Shader.TileMode.REPEAT));
+ }
+ canvas.drawRect(0, 0, width, height, mPaint);
+ }
+ })
+ .runWithVerifier(new ColorVerifier(Color.BLUE));
+ }
+
+ @SmallTest
+ public void testSinglePixelComposeShader() {
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ Paint mPaint = new Paint();
+
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ if (mPaint.getShader() == null) {
+ // BLUE as SRC for Compose
+ Bitmap shaderBitmap = Bitmap.createBitmap(1, 1, Bitmap.Config.ARGB_8888);
+ shaderBitmap.eraseColor(Color.BLUE);
+ BitmapShader bitmapShader = new BitmapShader(shaderBitmap,
+ Shader.TileMode.REPEAT, Shader.TileMode.REPEAT);
+
+ // Fully opaque gradient mask (via DST_IN).
+ // In color array, only alpha channel will matter.
+ RadialGradient gradientShader = new RadialGradient(
+ 10, 10, 10,
+ new int[] { Color.RED, Color.GREEN, Color.BLUE }, null,
+ Shader.TileMode.CLAMP);
+
+ mPaint.setShader(new ComposeShader(
+ bitmapShader, gradientShader, PorterDuff.Mode.DST_IN));
+ }
+ canvas.drawRect(0, 0, width, height, mPaint);
+ }
+ })
+ .runWithVerifier(new ColorVerifier(Color.BLUE));
+ }
+
+ @SmallTest
+ public void testComplexShaderUsage() {
+ /*
+ * This test not only builds a very complex drawing operation, but also tests an
+ * implementation detail of HWUI, using the largest number of texture sample sources
+ * possible - 4.
+ *
+ * 1) Bitmap passed to canvas.drawBitmap
+ * 2) gradient color lookup
+ * 3) gradient dither lookup
+ * 4) Bitmap in BitmapShader
+ */
+ createTest()
+ .addCanvasClient(new CanvasClient() {
+ Paint mPaint = new Paint();
+ Bitmap mBitmap;
+
+ @Override
+ public void draw(Canvas canvas, int width, int height) {
+ if (mBitmap == null) {
+ mBitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ALPHA_8);
+ // Primary content mask
+ Canvas bitmapCanvas = new Canvas(mBitmap);
+ final float radius = width / 2.0f;
+ bitmapCanvas.drawCircle(width / 2, height / 2, radius, mPaint);
+
+ // Bitmap shader mask, partially overlapping content
+ Bitmap shaderBitmap = Bitmap.createBitmap(
+ width, height, Bitmap.Config.ALPHA_8);
+ bitmapCanvas = new Canvas(shaderBitmap);
+ bitmapCanvas.drawCircle(width / 2, 0, radius, mPaint);
+ bitmapCanvas.drawCircle(width / 2, height, radius, mPaint);
+ BitmapShader bitmapShader = new BitmapShader(shaderBitmap,
+ Shader.TileMode.CLAMP, Shader.TileMode.CLAMP);
+
+ // Gradient fill
+ RadialGradient gradientShader = new RadialGradient(
+ width / 2, height / 2, radius,
+ new int[] { Color.RED, Color.BLUE, Color.GREEN },
+ null, Shader.TileMode.CLAMP);
+
+ mPaint.setShader(new ComposeShader(gradientShader, bitmapShader,
+ PorterDuff.Mode.DST_IN));
+ }
+ canvas.drawBitmap(mBitmap, 0, 0, mPaint);
+ }
+ })
+ // expect extremely similar rendering results between SW and HW, since there's no AA
+ .runWithComparer(new MSSIMComparer(0.98f));
+ }
+}
diff --git a/tests/tests/view/AndroidManifest.xml b/tests/tests/view/AndroidManifest.xml
index e5e77bc..65e95d5 100644
--- a/tests/tests/view/AndroidManifest.xml
+++ b/tests/tests/view/AndroidManifest.xml
@@ -175,6 +175,14 @@
</intent-filter>
</activity>
+ <activity android:name="android.view.cts.SearchEventActivity"
+ android:label="SearchEventActivity">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.FRAMEWORK_INSTRUMENTATION_TEST" />
+ </intent-filter>
+ </activity>
+
<activity android:name="android.view.cts.CtsActivity"
android:label="CtsActivity">
<intent-filter>
diff --git a/tests/tests/view/src/android/view/cts/SearchEventActivity.java b/tests/tests/view/src/android/view/cts/SearchEventActivity.java
new file mode 100644
index 0000000..6cc8c85
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/SearchEventActivity.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.view.cts;
+
+import com.android.cts.view.R;
+
+import android.app.Activity;
+import android.os.Bundle;
+import android.view.SearchEvent;
+
+public class SearchEventActivity extends Activity {
+
+ private static SearchEvent mSearchEvent;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.windowstub_layout);
+ }
+
+ @Override
+ public boolean onSearchRequested() {
+ mSearchEvent = getSearchEvent();
+ return true;
+ }
+
+ public SearchEvent getTestSearchEvent() {
+ return mSearchEvent;
+ }
+
+ public void reset() {
+ mSearchEvent = null;
+ }
+}
diff --git a/tests/tests/view/src/android/view/cts/SearchEventTest.java b/tests/tests/view/src/android/view/cts/SearchEventTest.java
new file mode 100644
index 0000000..4df52a1
--- /dev/null
+++ b/tests/tests/view/src/android/view/cts/SearchEventTest.java
@@ -0,0 +1,51 @@
+/*
+ * 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.view.cts;
+
+import com.android.cts.view.R;
+
+import android.app.Instrumentation;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.InputDevice;
+import android.view.KeyEvent;
+import android.view.SearchEvent;
+
+public class SearchEventTest extends ActivityInstrumentationTestCase2<SearchEventActivity> {
+
+ private Instrumentation mInstrumentation;
+ private SearchEventActivity mActivity;
+
+ public SearchEventTest() {
+ super(SearchEventActivity.class);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mInstrumentation = getInstrumentation();
+ mActivity = getActivity();
+ }
+
+ public void testTest() throws Exception {
+ mInstrumentation.sendKeyDownUpSync(KeyEvent.KEYCODE_SEARCH);
+ SearchEvent se = mActivity.getTestSearchEvent();
+ assertNotNull(se);
+ InputDevice id = se.getInputDevice();
+ assertNotNull(id);
+ assertEquals(-1, id.getId());
+ }
+}
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);
+ }
+}
diff --git a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
index 9d8c7d2..70c31d6 100644
--- a/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/AbsListViewTest.java
@@ -194,7 +194,6 @@
assertSame(mListView, onScrollListener.getView());
assertEquals(mListView.getChildCount(), onScrollListener.getVisibleItemCount());
assertEquals(mCountryList.length, onScrollListener.getTotalItemCount());
- assertEquals(OnScrollListener.SCROLL_STATE_IDLE, onScrollListener.getScrollState());
assertTrue(onScrollListener.isOnScrollCalled());
assertTrue(onScrollListener.isOnScrollStateChangedCalled());
diff --git a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
index 7e44e49..7e4c367 100644
--- a/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
+++ b/tests/uiautomator/src/com/android/cts/uiautomatortest/CtsUiAutomatorTest.java
@@ -827,17 +827,17 @@
assertTrue("Pinch must be in center of target view", p2s.y == screenRect.centerY());
assertTrue("Touch-down X coordinate for pointer 1 is invalid",
- withinMarginOfError(0.1f, screenRect.centerX(), p1s.x));
+ withinMarginOfError(0.125f, screenRect.centerX(), p1s.x));
assertTrue("Touch-down X coordinate for pointer 2 is invalid",
- withinMarginOfError(0.1f, screenRect.centerX(), p2s.x));
+ withinMarginOfError(0.125f, screenRect.centerX(), p2s.x));
assertTrue("Touch-up X coordinate for pointer 1 is invalid",
- withinMarginOfError(0.1f, screenRect.centerX() - screenRect.left,
+ withinMarginOfError(0.125f, screenRect.centerX() - screenRect.left,
screenRect.centerX() - p1e.x));
assertTrue("Touch-up X coordinate for pointer 2 is invalid",
- withinMarginOfError(0.1f, screenRect.right, p2e.x));
+ withinMarginOfError(0.125f, screenRect.right, p2e.x));
}
/**
@@ -881,17 +881,17 @@
assertTrue("Pinch must be in center of target view", p2s.y == screenRect.centerY());
assertTrue("Touch-down X coordinate for pointer 1 is invalid",
- withinMarginOfError(0.1f, screenRect.centerX() - screenRect.left,
+ withinMarginOfError(0.125f, screenRect.centerX() - screenRect.left,
screenRect.centerX() - p1s.x));
assertTrue("Touch-down X coordinate for pointer 2 is invalid",
- withinMarginOfError(0.1f, screenRect.right, p2s.x));
+ withinMarginOfError(0.125f, screenRect.right, p2s.x));
assertTrue("Touch-up X coordinate for pointer 1 is invalid",
- withinMarginOfError(0.1f, screenRect.centerX() - FINGER_TOUCH_HALF_WIDTH, p1e.x));
+ withinMarginOfError(0.125f, screenRect.centerX() - FINGER_TOUCH_HALF_WIDTH, p1e.x));
assertTrue("Touch-up X coordinate for pointer 2 is invalid",
- withinMarginOfError(0.1f, screenRect.centerX() + FINGER_TOUCH_HALF_WIDTH, p2e.x));
+ withinMarginOfError(0.125f, screenRect.centerX() + FINGER_TOUCH_HALF_WIDTH, p2e.x));
}
/**