Merge "Additional failure tests for KeyChain" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 3fc5086..c189c12 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -73,6 +73,7 @@
CtsCertInstallerApp \
CtsDeviceAdmin \
CtsDeviceOpenGl \
+ CtsWifiConfigCreator \
CtsDeviceOwnerApp \
CtsDeviceTaskswitchingAppA \
CtsDeviceTaskswitchingAppB \
diff --git a/apps/CameraITS/pymodules/its/image.py b/apps/CameraITS/pymodules/its/image.py
index 8e76c6f..4769bb7 100644
--- a/apps/CameraITS/pymodules/its/image.py
+++ b/apps/CameraITS/pymodules/its/image.py
@@ -211,6 +211,9 @@
if cap["format"] == "raw10":
assert(props is not None)
cap = unpack_raw10_capture(cap, props)
+ if cap["format"] == "raw12":
+ assert(props is not None)
+ cap = unpack_raw12_capture(cap, props)
if cap["format"] == "yuv":
y = cap["data"][0:w*h]
u = cap["data"][w*h:w*h*5/4]
@@ -229,6 +232,36 @@
img = numpy.ndarray(shape=(h*w,), dtype='<u2',
buffer=cap["data"][0:w*h*2])
img = img.astype(numpy.float32).reshape(h,w) / white_level
+ # Crop the raw image to the active array region.
+ if props.has_key("android.sensor.info.activeArraySize") \
+ and props["android.sensor.info.activeArraySize"] is not None \
+ and props.has_key("android.sensor.info.pixelArraySize") \
+ and props["android.sensor.info.pixelArraySize"] is not None:
+ # Note that the Rect class is defined such that the left,top values
+ # are "inside" while the right,bottom values are "outside"; that is,
+ # it's inclusive of the top,left sides only. So, the width is
+ # computed as right-left, rather than right-left+1, etc.
+ wfull = props["android.sensor.info.pixelArraySize"]["width"]
+ hfull = props["android.sensor.info.pixelArraySize"]["height"]
+ xcrop = props["android.sensor.info.activeArraySize"]["left"]
+ ycrop = props["android.sensor.info.activeArraySize"]["top"]
+ wcrop = props["android.sensor.info.activeArraySize"]["right"]-xcrop
+ hcrop = props["android.sensor.info.activeArraySize"]["bottom"]-ycrop
+ assert(wfull >= wcrop >= 0)
+ assert(hfull >= hcrop >= 0)
+ assert(wfull - wcrop >= xcrop >= 0)
+ assert(hfull - hcrop >= ycrop >= 0)
+ if w == wfull and h == hfull:
+ # Crop needed; extract the center region.
+ img = img[ycrop:ycrop+hcrop,xcrop:xcrop+wcrop]
+ w = wcrop
+ h = hcrop
+ elif w == wcrop and h == hcrop:
+ # No crop needed; image is already cropped to the active array.
+ None
+ else:
+ raise its.error.Error('Invalid image size metadata')
+ # Separate the image planes.
imgs = [img[::2].reshape(w*h/2)[::2].reshape(h/2,w/2,1),
img[::2].reshape(w*h/2)[1::2].reshape(h/2,w/2,1),
img[1::2].reshape(w*h/2)[::2].reshape(h/2,w/2,1),
diff --git a/apps/CtsVerifier/Android.mk b/apps/CtsVerifier/Android.mk
index 142172b..dd85a19 100644
--- a/apps/CtsVerifier/Android.mk
+++ b/apps/CtsVerifier/Android.mk
@@ -34,8 +34,10 @@
androidplot \
ctsverifier-opencv \
core-tests \
+ android-support-v4 \
mockito-target \
mockwebserver \
+ compatibility-device-util_v2 \
LOCAL_PACKAGE_NAME := CtsVerifier
diff --git a/apps/CtsVerifier/AndroidManifest.xml b/apps/CtsVerifier/AndroidManifest.xml
index 070c174..0f4cb6b 100644
--- a/apps/CtsVerifier/AndroidManifest.xml
+++ b/apps/CtsVerifier/AndroidManifest.xml
@@ -1200,6 +1200,23 @@
android:value="android.hardware.type.television:android.software.leanback" />
</activity>
-->
+ <activity
+ android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity"
+ android:label="@string/snsr_device_suspend_test"
+ android:screenOrientation="nosensor" >
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_sensors" />
+ </activity>
+
+ <receiver android:name="com.android.cts.verifier.sensors.DeviceSuspendTestActivity$AlarmReceiver">
+ </receiver>
+
+ <receiver android:name="com.android.cts.verifier.sensors.SignificantMotionTestActivity$AlarmReceiver">
+ </receiver>
+
<activity
android:name="com.android.cts.verifier.sensors.SignificantMotionTestActivity"
android:label="@string/snsr_significant_motion_test"
@@ -1300,16 +1317,36 @@
android:label="@string/projection_service_name"
android:process=":projectionservice" />
- <activity android:name=".managedprovisioning.DeviceOwnerTestActivity"
+ <activity android:name=".managedprovisioning.DeviceOwnerNegativeTestActivity"
android:label="@string/provisioning_device_owner">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.cts.intent.category.MANUAL_TEST" />
</intent-filter>
<meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
- <meta-data android:name="test_required_features" android:value="android.software.managed_users:android.software.device_admin" />
+ <meta-data android:name="test_required_features" android:value="android.software.device_admin" />
</activity>
+ <activity android:name=".managedprovisioning.DeviceOwnerPositiveTestActivity"
+ android:label="@string/positive_device_owner">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.cts.intent.category.MANUAL_TEST" />
+ </intent-filter>
+ <meta-data android:name="test_category" android:value="@string/test_category_managed_provisioning" />
+ <meta-data android:name="test_required_features" android:value="android.software.device_admin" />
+ </activity>
+
+ <activity android:name=".managedprovisioning.DeviceOwnerPositiveTestActivity$CommandReceiver"
+ android:exported="false"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:noHistory="true"
+ android:autoRemoveFromRecents="true"
+ android:stateNotNeeded="true"/>
+
+ <activity android:name=".managedprovisioning.WifiLockdownTestActivity"
+ android:label="@string/device_owner_wifi_lockdown_test">
+ </activity>
<activity android:name=".managedprovisioning.ByodFlowTestActivity"
android:launchMode="singleTask"
@@ -1380,7 +1417,7 @@
</activity>
<receiver android:name=".managedprovisioning.DeviceAdminTestReceiver"
- android:label="@string/provisioning_byod_device_admin"
+ android:label="@string/afw_device_admin"
android:permission="android.permission.BIND_DEVICE_ADMIN">
<meta-data android:name="android.app.device_admin"
android:resource="@xml/device_admin_byod" />
diff --git a/apps/CtsVerifier/res/layout/positive_device_owner.xml b/apps/CtsVerifier/res/layout/positive_device_owner.xml
new file mode 100644
index 0000000..f5d10e0
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/positive_device_owner.xml
@@ -0,0 +1,49 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="320dp"
+ android:layout_weight="2">
+ <TextView
+ android:id="@+id/positive_device_owner_instructions"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:text="@string/device_owner_positive_tests_instructions"
+ android:textSize="18dip" />
+ </ScrollView>
+
+ <Button
+ android:id="@+id/set_device_owner_button"
+ android:layout_width="204dp"
+ android:layout_height="wrap_content"
+ android:text="@string/set_device_owner_button_label" />
+
+ <ListView
+ android:id="@+id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3" />
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/layout/wifi_lockdown.xml b/apps/CtsVerifier/res/layout/wifi_lockdown.xml
new file mode 100644
index 0000000..ae6ea0c
--- /dev/null
+++ b/apps/CtsVerifier/res/layout/wifi_lockdown.xml
@@ -0,0 +1,77 @@
+<?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.
+-->
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+ android:orientation="vertical"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ >
+
+ <ScrollView
+ android:layout_width="match_parent"
+ android:layout_height="320dp"
+ android:layout_weight="2">
+ <TextView
+ android:id="@+id/device_owner_wifi_lockdown_info"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:padding="10dip"
+ android:text="@string/device_owner_wifi_lockdown_info"
+ android:textSize="18dip" />
+ </ScrollView>
+
+ <EditText
+ android:id="@+id/device_owner_wifi_ssid"
+ android:hint="(SSID)"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content" />
+
+ <RadioGroup
+ android:id="@+id/device_owner_keyManagementMethods"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:orientation="horizontal">
+ <RadioButton
+ android:id="@+id/device_owner_keymgmnt_none"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/device_owner_wifi_key_management_none_button" />
+ <RadioButton
+ android:id="@+id/device_owner_keymgmnt_wpa"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/device_owner_wifi_key_management_wpa_button" />
+ <RadioButton
+ android:id="@+id/device_owner_keymgmnt_wep"
+ android:layout_width="wrap_content"
+ android:layout_height="wrap_content"
+ android:text="@string/device_owner_wifi_key_management_wep_button" />
+ </RadioGroup>
+
+ <Button
+ android:id="@+id/create_wifi_config_button"
+ android:layout_width="204dp"
+ android:layout_height="wrap_content"
+ android:text="@string/create_wifi_config_button_label" />
+
+ <ListView
+ android:id="@+id/android:list"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ android:layout_weight="3" />
+
+ <include layout="@layout/pass_fail_buttons" />
+
+</LinearLayout>
\ No newline at end of file
diff --git a/apps/CtsVerifier/res/values/strings.xml b/apps/CtsVerifier/res/values/strings.xml
index 772fc96..800deb6 100644
--- a/apps/CtsVerifier/res/values/strings.xml
+++ b/apps/CtsVerifier/res/values/strings.xml
@@ -649,6 +649,14 @@
<string name="snsr_step_counter_event">%1$d | Step Counter event. count=%2$d.</string>
<string name="snsr_step_detector_event">%1$d | Step Detector event.</string>
+ <!-- Device suspend tests -->
+ <string name="snsr_device_suspend_test">Device Suspend Tests</string>
+ <string name="snsr_device_did_not_go_into_suspend">Device did not go into suspend mode during test execution </string>
+ <string name="snsr_batch_did_not_arrive_at_expected_time">Batch did not arrive at the expected time estimatedBatchArrivalMs=%1$d
+ firstEventReceivedMs=%2$d diffMs=%3$d toleranceMs=%4$d </string>
+ <string name="snsr_device_suspend_test_instr">One you begin the test, disconnect USB, turn off the display and allow
+ the device to go into suspend mode. The screen will turn on and a sound will be played once all the tests are completed.</string>
+
<!-- Significant Motion -->
<string name="snsr_significant_motion_test">Significant Motion Tests</string>
<string name="snsr_significant_motion_event_arrival">Event expected to trigger. Triggered=%1$s.</string>
@@ -663,6 +671,9 @@
<string name="snsr_significant_motion_test_deactivation">Once you begin the test, you will need to walk to ensure Significant Motion triggers only once.</string>
<string name="snsr_significant_motion_registration">Expected to be able to register for TriggerSensor. Found=%1$b.</string>
<string name="snsr_significant_motion_cancelation">Expected to be able to cancel TriggerSensor. Found=%b.</string>
+ <string name="snsr_significant_motion_ap_suspend">One you begin the test, disconnect USB, turn off the display and allow the device to go into suspend.
+ You will need to walk to ensure that Significant Motion triggers. The screen will turn on and a sound will be played once the test completes.</string>
+ <string name="snsr_device_did_not_wake_up_at_trigger">Device did not wakeup at tigger time. wakeTime=%1$d ms triggerTime=%2$d ms</string>
<!-- Strings for Sensor CTS tests inside CtsVerifier -->
<string name="snsr_single_sensor_tests">CTS Single Sensor Tests</string>
@@ -1366,9 +1377,11 @@
<string name="snsr_rotation_vector_set_final">Place the device back to the reference position.</string>
<string name="snsr_rotation_vector_verification">Angular deviation [%1$4.1f %2$4.1f %3$4.1f]. Current: %4$f deg. Max tolerated: %5$f.</string>
+ <!-- Strings common for BYOD and DO managed provisioning tests. -->
+ <string name="afw_device_admin">CTS Verifier - AfW Admin</string>
+
<!-- Strings for BYOD managed provisioning tests (ByodFlowTestActivity) -->
<string name="test_category_managed_provisioning">Managed Provisioning</string>
- <string name="provisioning_byod_device_admin">CTS Verifier - BYOD Admin</string>
<string name="provisioning_byod">BYOD Managed Provisioning</string>
<string name="provisioning_byod_info">
This test exercises the BYOD managed provisioning flow.
@@ -1454,7 +1467,7 @@
Navigate to Device administrators and confirm that:\n
\n
- Both Personal and Work categories exist.\n
- - \"CTS Verifier - BYOD Admin\" exists under the Work category, and is activated.\n
+ - \"CTS Verifier - AfW Admin\" exists under the Work category, and is activated.\n
\n
Use the Back button to return to this page.
</string>
@@ -1525,6 +1538,104 @@
<string name="device_owner_negative_test">Device owner negative test</string>
<string name="device_owner_negative_test_info">Please click the "Start provisioning" button, and when you see a warning dialog telling the device is already set up, select "pass". Otherwise, select "fail".</string>
<string name="start_device_owner_provisioning_button">Start provisioning</string>
+ <string name="positive_device_owner">Device Owner Tests</string>
+ <string name="device_owner_positive_tests">Device Owner positive tests</string>
+ <string name="device_owner_positive_tests_instructions">
+ The positive device owner tests verify policies on a corporate owned device.\n
+ Press below button first, follow steps described in the dialog that pops up,
+ then proceed to the test cases.\n
+ Pressing \'back\', \'pass\' or \'fail\' on this test page will remove the device owner.\n
+ Alternatively, you can run the \'Remove device owner\' test. Ideally, that test should
+ be run last so that it does not interfere with other tests.
+ </string>
+ <string name="device_owner_positive_tests_info">
+ The positive device owner tests verify policies on a corporate owned device.\n
+ Press below button first, follow steps described in the dialog that pops up,
+ then proceed to the test cases.\n
+ Pressing \'back\', \'pass\' or \'fail\' on this test page will remove the device owner.\n
+ Alternatively, you can run the \'Remove device owner\' test. Ideally, that test should
+ be run last so that it does not interfere with other tests.
+ </string>
+ <string name="device_owner_positive_category">Device Owner Tests</string>
+ <string name="set_device_owner_button_label">Set up device owner</string>
+ <string name="set_device_owner_dialog_title">Set up device owner</string>
+ <string name="set_device_owner_dialog_text">
+ Please set the device owner by enabling USB debugging on the device and issuing the following command on the host:\n
+ adb shell dpm set-device-owner \'com.android.cts.verifier/com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver\'
+ </string>
+ <string name="device_owner_remove_device_owner_test">Remove device owner</string>
+ <string name="device_owner_remove_device_owner_test_info">
+ Please check in Settings > Security > Device Administrators if CTSVerifier is
+ Device Owner. Then press the button below, and check that CTSVerifier is NOT Device
+ Owner anymore.
+ </string>
+ <string name="remove_device_owner_button">Remove device owner</string>
+ <string name="device_owner_check_device_owner_test">Check device owner</string>
+ <string name="device_owner_incorrect_device_owner">Missing or incorrect device owner: CTSVerifier is not DO!</string>
+ <string name="device_owner_wifi_lockdown_test">WiFi configuration lockdown</string>
+ <string name="device_owner_wifi_lockdown_info">
+ Please enter the SSID and auth method of an available WiFi Access Point and press the button to create a
+ WiFi configuration. This configuration can be seen on Settings > WiFi. The test cases
+ are going use this config. Please go through test cases in order (from top to bottom).
+ </string>
+ <string name="switch_wifi_lockdown_off_button">WiFi config lockdown off</string>
+ <string name="switch_wifi_lockdown_on_button">WiFi config lockdown on</string>
+ <string name="wifi_lockdown_go_settings_wifi_button">Go to WiFi Settings</string>
+ <string name="device_owner_wifi_key_management_none_button">None</string>
+ <string name="device_owner_wifi_key_management_wpa_button">WPA</string>
+ <string name="device_owner_wifi_key_management_wep_button">WEP</string>
+ <string name="create_wifi_config_button_label">Create WiFi configuration</string>
+ <string name="wifi_lockdown_add_network_failed_dialog_title">WiFi configuration could not be created</string>
+ <string name="wifi_lockdown_add_network_failed_dialog_text">
+ There was an error during creation of WiFi configuration. Check if WiFi is switched on.
+ </string>
+ <string name="device_owner_wifi_config_unlocked_modification_test">Unlocked config is modifiable in Settings</string>
+ <string name="device_owner_wifi_config_unlocked_modification_test_info">
+ Please press the button to ensure WiFi config lockdown is NOT in effect. Then go to
+ Settings > WiFi and see if the CTSVerifier created WiFi configuration can be edited.
+ Please make sure you can connect to it. The test is successful if the config is editable
+ and can be connected to.
+ </string>
+ <string name="device_owner_wifi_config_locked_modification_test">Locked config is not modifiable in Settings</string>
+ <string name="device_owner_wifi_config_locked_modification_test_info">
+ Please press the button to ensure WiFi config lockdown is in effect. Then go to
+ Settings > WiFi and see if the CTSVerifier created WiFi configuration can NOT be edited
+ or removed. The test is successful if the config is NOT modifiable.
+ </string>
+ <string name="device_owner_wifi_config_locked_connection_test">Locked config can be connected to</string>
+ <string name="device_owner_wifi_config_locked_connection_test_info">
+ Please press the button to ensure WiFi config lockdown is in effect. Then go to
+ Settings > WiFi and see if the CTSVerifier created WiFi configuration can be connected
+ to manually. The test is successful if the connection can be established.
+ </string>
+ <string name="device_owner_wifi_config_unlocked_removal_test">Unlocked config can be forgotten in Settings</string>
+ <string name="device_owner_wifi_config_unlocked_removal_test_info">
+ Please press the button to ensure WiFi config lockdown is NOT in effect. Then go to
+ Settings > WiFi and see if the CTSVerifier created WiFi configuration can be forgotten.
+ The test is successful if the config could be forgotten and is removed from the list of saved configs.
+ </string>
+ <string name="device_owner_disable_statusbar_test">Disable status bar</string>
+ <string name="device_owner_disable_statusbar_test_info">
+ Please press the below button to disable the status bar and verify that quick settings, notifications
+ and the assist gesture are no longer available.\n
+ Next, press the button to reenable the status bar and verify that quick settings, notification
+ and the assist gesture are available again.\n
+ Please mark the test accordingly.
+ </string>
+ <string name="device_owner_disable_statusbar_button">Disable status bar</string>
+ <string name="device_owner_reenable_statusbar_button">Reenable status bar</string>
+ <string name="device_owner_disable_keyguard_test">Disable keyguard</string>
+ <string name="device_owner_disable_keyguard_test_info">
+ Note that any device passwords that you might have set will be deleted during this test.\n
+ Please press the below button to disable the keyguard. Press the power button on your device to
+ switch off the screen. Then press the power button to switch the screen back on and verify that
+ no keyguard was shown.\n
+ Next, press the button to reenable the keyguard and repeat the above steps, this time verifying that
+ a keyguard was shown again.\n
+ Please mark the test accordingly.
+ </string>
+ <string name="device_owner_disable_keyguard_button">Disable keyguard</string>
+ <string name="device_owner_reenable_keyguard_button">Reenable keyguard</string>
<!-- Strings for JobScheduler Tests -->
<string name="js_test_description">This test is mostly automated, but requires some user interaction. You can pass this test once the list items below are checked.</string>
diff --git a/apps/CtsVerifier/res/xml/device_admin_byod.xml b/apps/CtsVerifier/res/xml/device_admin_byod.xml
index 0408ce2..61238aa 100644
--- a/apps/CtsVerifier/res/xml/device_admin_byod.xml
+++ b/apps/CtsVerifier/res/xml/device_admin_byod.xml
@@ -19,6 +19,7 @@
<uses-policies>
<encrypted-storage />
<wipe-data />
+ <reset-password />
</uses-policies>
</device-admin>
<!-- END_INCLUDE(meta_data) -->
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
index 9154307..ef5d62d 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceAdminTestReceiver.java
@@ -22,9 +22,7 @@
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
-import android.content.pm.PackageManager;
import android.util.Log;
-import android.widget.Toast;
/**
* Profile owner receiver for BYOD flow test.
@@ -32,6 +30,16 @@
*/
public class DeviceAdminTestReceiver extends DeviceAdminReceiver {
private static final String TAG = "DeviceAdminTestReceiver";
+ private static final String DEVICE_OWNER_PKG =
+ "com.android.cts.verifier";
+ private static final String ADMIN_RECEIVER_TEST_CLASS =
+ DEVICE_OWNER_PKG + ".managedprovisioning.DeviceAdminTestReceiver";
+ private static final ComponentName RECEIVER_COMPONENT_NAME = new ComponentName(
+ DEVICE_OWNER_PKG, ADMIN_RECEIVER_TEST_CLASS);
+
+ public static ComponentName getReceiverComponentName() {
+ return RECEIVER_COMPONENT_NAME;
+ }
@Override
public void onProfileProvisioningComplete(Context context, Intent intent) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
similarity index 80%
rename from apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerTestActivity.java
rename to apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
index 7cb3825..c7e785c 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerNegativeTestActivity.java
@@ -1,22 +1,24 @@
+/*
+ * 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.verifier.managedprovisioning;
-import android.app.AlertDialog;
-import android.app.admin.DevicePolicyManager;
-import android.content.ComponentName;
-import android.content.Context;
-import android.content.DialogInterface;
import android.content.Intent;
import android.database.DataSetObserver;
import android.os.Bundle;
-import android.provider.Settings;
-import android.util.Log;
-import android.view.View;
-import android.view.ViewGroup;
-import android.view.View.OnClickListener;
-import android.widget.ArrayAdapter;
-import android.widget.ListView;
-import android.widget.TextView;
-import android.widget.Toast;
import com.android.cts.verifier.ArrayTestListAdapter;
import com.android.cts.verifier.IntentDrivenTestActivity;
@@ -26,13 +28,10 @@
import com.android.cts.verifier.R;
import com.android.cts.verifier.TestListAdapter.TestListItem;
-import java.util.ArrayList;
-import java.util.List;
-
/**
- * Activity that lists all device owner provisioning tests.
+ * Activity that lists all device owner negative tests.
*/
-public class DeviceOwnerTestActivity extends PassFailButtons.TestListActivity {
+public class DeviceOwnerNegativeTestActivity extends PassFailButtons.TestListActivity {
private static final String ACTION_PROVISION_MANAGED_DEVICE
= "com.android.managedprovisioning.ACTION_PROVISION_MANAGED_DEVICE";
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
new file mode 100644
index 0000000..1a5e73c
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/DeviceOwnerPositiveTestActivity.java
@@ -0,0 +1,275 @@
+/*
+ * 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.verifier.managedprovisioning;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.database.DataSetObserver;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.IntentDrivenTestActivity;
+import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.TestListAdapter.TestListItem;
+import com.android.cts.verifier.TestResult;
+
+/**
+ * Activity that lists all positive device owner tests. Requires the following adb command be issued
+ * by the user prior to starting the tests:
+ *
+ * adb shell dpm set-device-owner
+ * 'com.android.cts.verifier/com.android.cts.verifier.managedprovisioning.DeviceAdminTestReceiver'
+ */
+public class DeviceOwnerPositiveTestActivity extends PassFailButtons.TestListActivity {
+ private static final String TAG = "DeviceOwnerPositiveTestActivity";
+
+ static final String EXTRA_COMMAND = "extra-command";
+ static final String EXTRA_TEST_ID = "extra-test-id";
+ static final String COMMAND_SET_POLICY = "set-policy";
+ static final String EXTRA_POLICY = "extra-policy";
+ static final String EXTRA_PARAMETER_1 = "extra_parameter_1";
+ static final String EXTRA_PARAMETER_2 = "extra_parameter_2";
+ static final String COMMAND_ADD_USER_RESTRICTION = "add-user-restriction";
+ static final String COMMAND_CLEAR_USER_RESTRICTION = "clear-user-restriction";
+ static final String EXTRA_RESTRICTION = "extra-restriction";
+ static final String COMMAND_TEAR_DOWN = "tear-down";
+ static final String COMMAND_CHECK_DEVICE_OWNER = "check-device-owner";
+ static final String COMMAND_SET_GLOBAL_SETTING = "set-global-setting";
+ static final String COMMAND_SET_STATUSBAR_DISABLED = "set-statusbar-disabled";
+ static final String COMMAND_SET_KEYGUARD_DISABLED = "set-keyguard-disabled";
+ static final String EXTRA_SETTING = "extra-setting";
+
+ private static final String CHECK_DEVICE_OWNER_TEST_ID = "CHECK_DEVICE_OWNER";
+ private static final String WIFI_LOCKDOWN_TEST_ID = WifiLockdownTestActivity.class.getName();
+ private static final String DISABLE_STATUS_BAR_TEST_ID = "DISABLE_STATUS_BAR";
+ private static final String DISABLE_KEYGUARD_TEST_ID = "DISABLE_KEYGUARD";
+ private static final String REMOVE_DEVICE_OWNER_TEST_ID = "REMOVE_DEVICE_OWNER";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setContentView(R.layout.positive_device_owner);
+ setInfoResources(R.string.device_owner_positive_tests,
+ R.string.device_owner_positive_tests_info, 0);
+ setPassFailButtonClickListeners();
+
+ final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+ adapter.add(TestListItem.newCategory(this, R.string.device_owner_positive_category));
+
+ addTestsToAdapter(adapter);
+
+ adapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ updatePassButton();
+ }
+ });
+
+ setTestListAdapter(adapter);
+
+ View setDeviceOwnerButton = findViewById(R.id.set_device_owner_button);
+ setDeviceOwnerButton.setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ new AlertDialog.Builder(
+ DeviceOwnerPositiveTestActivity.this)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(R.string.set_device_owner_dialog_title)
+ .setMessage(R.string.set_device_owner_dialog_text)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+ });
+
+ }
+
+ @Override
+ public void finish() {
+ // Pass and fail buttons are known to call finish() when clicked, and this is when we want
+ // to remove the device owner.
+ startActivity(createTearDownIntent());
+ super.finish();
+ }
+
+ /**
+ * Enable Pass Button when all tests passed.
+ */
+ private void updatePassButton() {
+ getPassButton().setEnabled(mAdapter.allTestsPassed());
+ }
+
+ private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+ adapter.add(createTestItem(this, CHECK_DEVICE_OWNER_TEST_ID,
+ R.string.device_owner_check_device_owner_test,
+ new Intent(this, CommandReceiver.class)
+ .putExtra(EXTRA_COMMAND, COMMAND_CHECK_DEVICE_OWNER)
+ ));
+ PackageManager packageManager = getPackageManager();
+ if (packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) {
+ adapter.add(createTestItem(this, WIFI_LOCKDOWN_TEST_ID,
+ R.string.device_owner_wifi_lockdown_test,
+ new Intent(this, WifiLockdownTestActivity.class)));
+ }
+
+ // setStatusBarDisabled
+ adapter.add(createInteractiveTestItem(this, DISABLE_STATUS_BAR_TEST_ID,
+ R.string.device_owner_disable_statusbar_test,
+ R.string.device_owner_disable_statusbar_test_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.device_owner_disable_statusbar_button,
+ createDeviceOwnerIntentWithBooleanParameter(
+ COMMAND_SET_STATUSBAR_DISABLED, true)),
+ new ButtonInfo(
+ R.string.device_owner_reenable_statusbar_button,
+ createDeviceOwnerIntentWithBooleanParameter(
+ COMMAND_SET_STATUSBAR_DISABLED, false))}));
+
+ // setKeyguardDisabled
+ adapter.add(createInteractiveTestItem(this, DISABLE_KEYGUARD_TEST_ID,
+ R.string.device_owner_disable_keyguard_test,
+ R.string.device_owner_disable_keyguard_test_info,
+ new ButtonInfo[] {
+ new ButtonInfo(
+ R.string.device_owner_disable_keyguard_button,
+ createDeviceOwnerIntentWithBooleanParameter(
+ COMMAND_SET_KEYGUARD_DISABLED, true)),
+ new ButtonInfo(
+ R.string.device_owner_reenable_keyguard_button,
+ createDeviceOwnerIntentWithBooleanParameter(
+ COMMAND_SET_KEYGUARD_DISABLED, false))}));
+
+ // removeDeviceOwner
+ adapter.add(createInteractiveTestItem(this, REMOVE_DEVICE_OWNER_TEST_ID,
+ R.string.device_owner_remove_device_owner_test,
+ R.string.device_owner_remove_device_owner_test_info,
+ new ButtonInfo(
+ R.string.remove_device_owner_button,
+ createTearDownIntent())));
+ }
+
+ static TestListItem createInteractiveTestItem(Activity activity, String id, int titleRes,
+ int infoRes, ButtonInfo buttonInfo) {
+ return createInteractiveTestItem(activity, id, titleRes, infoRes,
+ new ButtonInfo[] { buttonInfo });
+ }
+
+ static TestListItem createInteractiveTestItem(Activity activity, String id, int titleRes,
+ int infoRes, ButtonInfo[] buttonInfos) {
+ return TestListItem.newTest(activity, titleRes,
+ id, new Intent(activity, IntentDrivenTestActivity.class)
+ .putExtra(IntentDrivenTestActivity.EXTRA_ID, id)
+ .putExtra(IntentDrivenTestActivity.EXTRA_TITLE, titleRes)
+ .putExtra(IntentDrivenTestActivity.EXTRA_INFO, infoRes)
+ .putExtra(IntentDrivenTestActivity.EXTRA_BUTTONS, buttonInfos),
+ null);
+ }
+
+ static TestListItem createTestItem(Activity activity, String id, int titleRes,
+ Intent intent) {
+ return TestListItem.newTest(activity, titleRes, id, intent.putExtra(EXTRA_TEST_ID, id),
+ null);
+ }
+
+ private Intent createTearDownIntent() {
+ return new Intent(this, CommandReceiver.class)
+ .putExtra(EXTRA_COMMAND, COMMAND_TEAR_DOWN);
+ }
+
+ private Intent createDeviceOwnerIntentWithBooleanParameter(String command, boolean value) {
+ return new Intent(this, CommandReceiver.class)
+ .putExtra(EXTRA_COMMAND, command)
+ .putExtra(EXTRA_PARAMETER_1, value);
+ }
+
+ public static class CommandReceiver extends Activity {
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ String command = intent.getStringExtra(EXTRA_COMMAND);
+ try {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getSystemService(Context.DEVICE_POLICY_SERVICE);
+ ComponentName admin = DeviceAdminTestReceiver.getReceiverComponentName();
+ Log.i(TAG, "Command: " + command);
+
+ if (COMMAND_ADD_USER_RESTRICTION.equals(command)) {
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION);
+ dpm.addUserRestriction(admin, restrictionKey);
+ Log.i(TAG, "Added user restriction " + restrictionKey);
+ } else if (COMMAND_CLEAR_USER_RESTRICTION.equals(command)) {
+ String restrictionKey = intent.getStringExtra(EXTRA_RESTRICTION);
+ dpm.clearUserRestriction(admin, restrictionKey);
+ Log.i(TAG, "Cleared user restriction " + restrictionKey);
+ } else if (COMMAND_TEAR_DOWN.equals(command)) {
+ tearDown(dpm, admin);
+ } else if (COMMAND_SET_GLOBAL_SETTING.equals(command)) {
+ final String setting = intent.getStringExtra(EXTRA_SETTING);
+ final String value = intent.getStringExtra(EXTRA_PARAMETER_1);
+ dpm.setGlobalSetting(admin, setting, value);
+ } else if (COMMAND_SET_STATUSBAR_DISABLED.equals(command)) {
+ final boolean value = intent.getBooleanExtra(EXTRA_PARAMETER_1, false);
+ dpm.setStatusBarDisabled(admin, value);
+ } else if (COMMAND_SET_KEYGUARD_DISABLED.equals(command)) {
+ final boolean value = intent.getBooleanExtra(EXTRA_PARAMETER_1, false);
+ if (value) {
+ dpm.resetPassword(null, 0);
+ }
+ dpm.setKeyguardDisabled(admin, value);
+ } else if (COMMAND_CHECK_DEVICE_OWNER.equals(command)) {
+ if (dpm.isDeviceOwnerApp(getPackageName())) {
+ TestResult.setPassedResult(this, intent.getStringExtra(EXTRA_TEST_ID),
+ null, null);
+ } else {
+ TestResult.setFailedResult(this, intent.getStringExtra(EXTRA_TEST_ID),
+ getString(R.string.device_owner_incorrect_device_owner), null);
+ }
+ } else {
+ Log.e(TAG, "Invalid command: " + command);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Command " + command + " failed with exception " + e);
+ } finally {
+ // No matter what happened, don't let the activity run
+ finish();
+ }
+ }
+
+ private void tearDown(DevicePolicyManager dpm, ComponentName admin) {
+ if (dpm == null || !dpm.isDeviceOwnerApp(getPackageName())) {
+ return;
+ }
+
+ dpm.setStatusBarDisabled(admin, false);
+ dpm.setKeyguardDisabled(admin, false);
+ dpm.clearDeviceOwnerApp(getPackageName());
+ }
+ }
+}
+
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java
new file mode 100644
index 0000000..4a292eb
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/managedprovisioning/WifiLockdownTestActivity.java
@@ -0,0 +1,173 @@
+/*
+ * 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.verifier.managedprovisioning;
+
+import android.app.AlertDialog;
+import android.content.Intent;
+import android.database.DataSetObserver;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.EditText;
+import android.widget.RadioGroup;
+
+import com.android.cts.verifier.ArrayTestListAdapter;
+import com.android.cts.verifier.PassFailButtons;
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.IntentDrivenTestActivity.ButtonInfo;
+
+import com.android.compatibility.common.util.WifiConfigCreator;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_NONE;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_WPA;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_WEP;
+
+/**
+ * Activity to test WiFi configuration lockdown functionality. A locked down WiFi config
+ * must not be editable/forgettable in Settings.
+ */
+public class WifiLockdownTestActivity extends PassFailButtons.TestListActivity {
+ private static final String TAG = "WifiLockdownTestActivity";
+
+ private static final int NONE = R.id.device_owner_keymgmnt_none;
+ private static final int WPA = R.id.device_owner_keymgmnt_wpa;
+ private static final int WEP = R.id.device_owner_keymgmnt_wep;
+
+ private static final String CONFIG_MODIFIABLE_WHEN_UNLOCKED_TEST_ID = "UNLOCKED_MODIFICATION";
+ private static final String CONFIG_NOT_MODIFIABLE_WHEN_LOCKED_TEST_ID = "LOCKED_MODIFICATION";
+ private static final String CONFIG_CONNECTABLE_WHEN_LOCKED_TEST_ID = "LOCKED_CONNECT";
+ private static final String CONFIG_REMOVABLE_WHEN_UNLOCKED_TEST_ID = "UNLOCKED_REMOVE";
+
+ private WifiConfigCreator mConfigCreator;
+ private ButtonInfo[] mSwitchLockdownOffButtonInfos;
+ private ButtonInfo[] mSwitchLockdownOnButtonInfos;
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mConfigCreator = new WifiConfigCreator(this);
+ setContentView(R.layout.wifi_lockdown);
+ setInfoResources(R.string.device_owner_wifi_lockdown_test,
+ R.string.device_owner_wifi_lockdown_info, 0);
+ setPassFailButtonClickListeners();
+
+ final ArrayTestListAdapter adapter = new ArrayTestListAdapter(this);
+
+ final ButtonInfo goToWifiSettings = new ButtonInfo(
+ R.string.wifi_lockdown_go_settings_wifi_button,
+ new Intent(Settings.ACTION_WIFI_SETTINGS));
+ mSwitchLockdownOffButtonInfos = new ButtonInfo[] { new ButtonInfo(
+ R.string.switch_wifi_lockdown_off_button,
+ new Intent(this, DeviceOwnerPositiveTestActivity.CommandReceiver.class)
+ .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_COMMAND,
+ DeviceOwnerPositiveTestActivity.COMMAND_SET_GLOBAL_SETTING)
+ .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_SETTING,
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN)
+ .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_PARAMETER_1, "0"
+ )), goToWifiSettings };
+ mSwitchLockdownOnButtonInfos = new ButtonInfo[] { new ButtonInfo(
+ R.string.switch_wifi_lockdown_on_button,
+ new Intent(this, DeviceOwnerPositiveTestActivity.CommandReceiver.class)
+ .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_COMMAND,
+ DeviceOwnerPositiveTestActivity.COMMAND_SET_GLOBAL_SETTING)
+ .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_SETTING,
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN)
+ .putExtra(DeviceOwnerPositiveTestActivity.EXTRA_PARAMETER_1, "1"
+ )), goToWifiSettings };
+
+ addTestsToAdapter(adapter);
+
+ adapter.registerDataSetObserver(new DataSetObserver() {
+ @Override
+ public void onChanged() {
+ updatePassButton();
+ }
+ });
+
+ setTestListAdapter(adapter);
+
+ View createConfigButton = findViewById(R.id.create_wifi_config_button);
+ createConfigButton .setOnClickListener(new OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ EditText ssidEditor = (EditText) findViewById(R.id.device_owner_wifi_ssid);
+ RadioGroup authMethods = (RadioGroup) findViewById(
+ R.id.device_owner_keyManagementMethods);
+ int checkedRadioId = authMethods.getCheckedRadioButtonId();
+ if (checkedRadioId == -1) {
+ checkedRadioId = NONE;
+ }
+ int netId = mConfigCreator.addNetwork(ssidEditor.getText().toString(), false,
+ convertKeyManagement(checkedRadioId), "defaultpassword");
+ if (netId == -1) {
+ new AlertDialog.Builder(
+ WifiLockdownTestActivity.this)
+ .setIcon(android.R.drawable.ic_dialog_info)
+ .setTitle(R.string.wifi_lockdown_add_network_failed_dialog_title)
+ .setMessage(R.string.wifi_lockdown_add_network_failed_dialog_text)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ }
+ }
+ });
+ }
+
+ /**
+ * Enable Pass Button when all tests passed.
+ */
+ private void updatePassButton() {
+ getPassButton().setEnabled(mAdapter.allTestsPassed());
+ }
+
+ private void addTestsToAdapter(final ArrayTestListAdapter adapter) {
+ adapter.add(DeviceOwnerPositiveTestActivity.createInteractiveTestItem(this,
+ CONFIG_MODIFIABLE_WHEN_UNLOCKED_TEST_ID,
+ R.string.device_owner_wifi_config_unlocked_modification_test,
+ R.string.device_owner_wifi_config_unlocked_modification_test_info,
+ mSwitchLockdownOffButtonInfos));
+ adapter.add(DeviceOwnerPositiveTestActivity.createInteractiveTestItem(this,
+ CONFIG_NOT_MODIFIABLE_WHEN_LOCKED_TEST_ID,
+ R.string.device_owner_wifi_config_locked_modification_test,
+ R.string.device_owner_wifi_config_locked_modification_test_info,
+ mSwitchLockdownOnButtonInfos));
+ adapter.add(DeviceOwnerPositiveTestActivity.createInteractiveTestItem(this,
+ CONFIG_CONNECTABLE_WHEN_LOCKED_TEST_ID,
+ R.string.device_owner_wifi_config_locked_connection_test,
+ R.string.device_owner_wifi_config_locked_connection_test_info,
+ mSwitchLockdownOnButtonInfos));
+ adapter.add(DeviceOwnerPositiveTestActivity.createInteractiveTestItem(this,
+ CONFIG_REMOVABLE_WHEN_UNLOCKED_TEST_ID,
+ R.string.device_owner_wifi_config_unlocked_removal_test,
+ R.string.device_owner_wifi_config_unlocked_removal_test_info,
+ mSwitchLockdownOffButtonInfos));
+ }
+
+ private int convertKeyManagement(int radioButtonId) {
+ switch (radioButtonId) {
+ case NONE: {
+ return SECURITY_TYPE_NONE;
+ }
+ case WPA: {
+ return SECURITY_TYPE_WPA;
+ }
+ case WEP: {
+ return SECURITY_TYPE_WEP;
+ }
+ }
+ return SECURITY_TYPE_NONE;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
new file mode 100644
index 0000000..d066fce
--- /dev/null
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/DeviceSuspendTestActivity.java
@@ -0,0 +1,263 @@
+package com.android.cts.verifier.sensors;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.cts.verifier.R;
+import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
+import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
+
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.MovementDetectorHelper;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.sensorverification.BatchArrivalVerification;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.os.SystemClock;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+
+import junit.framework.Assert;
+
+public class DeviceSuspendTestActivity
+ extends SensorCtsVerifierTestActivity {
+ public DeviceSuspendTestActivity() {
+ super(DeviceSuspendTestActivity.class);
+ }
+
+ private SensorTestScreenManipulator mScreenManipulator;
+ private PowerManager.WakeLock mDeviceSuspendLock;
+ private PendingIntent mPendingIntent;
+ private AlarmManager mAlarmManager;
+ private static String ACTION_ALARM = "DeviceSuspendTestActivity.ACTION_ALARM";
+ private static String TAG = "DeviceSuspendTestActivity";
+ private SensorManager mSensorManager;
+
+ @Override
+ protected void activitySetUp() throws InterruptedException {
+ mSensorManager = (SensorManager) getSystemService(Context.SENSOR_SERVICE);
+ mScreenManipulator = new SensorTestScreenManipulator(this);
+ mScreenManipulator.initialize(this);
+ LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
+ new IntentFilter(ACTION_ALARM));
+
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ mPendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+
+ mAlarmManager = (AlarmManager) getSystemService(ALARM_SERVICE);
+
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ mDeviceSuspendLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "DeviceSuspendTestActivity");
+ mDeviceSuspendLock.acquire();
+ SensorTestLogger logger = getTestLogger();
+ logger.logInstructions(R.string.snsr_device_suspend_test_instr);
+ waitForUserToBegin();
+ }
+
+ @Override
+ protected void activityCleanUp() {
+ mScreenManipulator.turnScreenOn();
+ try {
+ playSound();
+ } catch(InterruptedException e) {
+ // Ignore.
+ }
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ if (mScreenManipulator != null) {
+ mScreenManipulator.releaseScreenOn();
+ mScreenManipulator.close();
+ }
+ if (mDeviceSuspendLock.isHeld()) {
+ mDeviceSuspendLock.release();
+ }
+ }
+
+ public static class AlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent alarm_intent = new Intent(context, DeviceSuspendTestActivity.class);
+ alarm_intent.setAction(DeviceSuspendTestActivity.ACTION_ALARM);
+ LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
+ }
+ }
+
+ public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (!mDeviceSuspendLock.isHeld()) {
+ mDeviceSuspendLock.acquire();
+ }
+ }
+ };
+
+ public String testAPWakeUpWhenReportLatencyExpiresAccel() throws Throwable {
+ Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER, true);
+ if (wakeUpSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER, true);
+ }
+ return runAPWakeUpWhenReportLatencyExpires(wakeUpSensor);
+ }
+
+ public String testAPWakeUpWhenReportLatencyExpiresGyro() throws Throwable {
+ Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE, true);
+ if (wakeUpSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_GYROSCOPE, true);
+ }
+ return runAPWakeUpWhenReportLatencyExpires(wakeUpSensor);
+ }
+
+ public String testAPWakeUpWhenReportLatencyExpiresMag() throws Throwable {
+ Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD,true);
+ if (wakeUpSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_MAGNETIC_FIELD, true);
+ }
+ return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+ }
+
+ public String testAPWakeUpWhenFIFOFullAccel() throws Throwable {
+ Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER, true);
+ if (wakeUpSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER, true);
+ }
+ return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+ }
+
+ public String testAPWakeUpWhenFIFOFullGyro() throws Throwable {
+ Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_GYROSCOPE, true);
+ if (wakeUpSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_GYROSCOPE, true);
+ }
+ return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+ }
+
+ public String testAPWakeUpWhenFIFOFullMag() throws Throwable {
+ Sensor wakeUpSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_MAGNETIC_FIELD,true);
+ if (wakeUpSensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_MAGNETIC_FIELD, true);
+ }
+ return runAPWakeUpWhenFIFOFull(wakeUpSensor);
+ }
+
+ public String runAPWakeUpWhenReportLatencyExpires(Sensor sensor) throws Throwable {
+ int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+ if (fifoMaxEventCount == 0) {
+ throw new SensorTestStateNotSupportedException("Batching not supported.");
+ }
+ int maximumExpectedSamplingPeriodUs = sensor.getMaxDelay();
+ if (maximumExpectedSamplingPeriodUs == 0) {
+ // If maxDelay is not defined, set the value for 5 Hz.
+ maximumExpectedSamplingPeriodUs = 200000;
+ }
+ int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+
+ // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
+ // seconds of time to allow the device to be in suspend state.
+ if (fifoBasedReportLatencyUs < 20000000L) {
+ throw new SensorTestStateNotSupportedException("FIFO too small to test reliably");
+ }
+
+ final int MAX_REPORT_LATENCY_US = 15000000; // 15 seconds
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ this,
+ sensor,
+ false,
+ maximumExpectedSamplingPeriodUs,
+ MAX_REPORT_LATENCY_US,
+ true /*isDeviceSuspendTest*/);
+
+ int numEventsToWaitFor = MAX_REPORT_LATENCY_US/maximumExpectedSamplingPeriodUs;
+ TestSensorOperation op = TestSensorOperation.createOperation(environment,
+ numEventsToWaitFor,
+ mDeviceSuspendLock,
+ false);
+ final int ALARM_WAKE_UP_DELAY_MS = MAX_REPORT_LATENCY_US/1000 +
+ (int)TimeUnit.SECONDS.toMillis(10);
+ op.addVerification(BatchArrivalVerification.getDefault(environment));
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS,
+ mPendingIntent);
+ try {
+ op.execute(getCurrentTestNode());
+ } finally {
+ mAlarmManager.cancel(mPendingIntent);
+ }
+ return null;
+ }
+
+ public String runAPWakeUpWhenFIFOFull(Sensor sensor) throws Throwable {
+ int fifoMaxEventCount = sensor.getFifoMaxEventCount();
+ if (fifoMaxEventCount == 0) {
+ throw new SensorTestStateNotSupportedException("Batching not supported.");
+ }
+ // Try to fill the FIFO at the fastest rate and check if the time is enough to run
+ // the manual test.
+ int maximumExpectedSamplingPeriodUs = sensor.getMinDelay();
+ int fifoBasedReportLatencyUs = fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+
+ final int MIN_LATENCY_US = (int)TimeUnit.SECONDS.toMicros(20);
+ // Ensure that FIFO based report latency is at least 20 seconds, we need at least 10
+ // seconds of time to allow the device to be in suspend state.
+ if (fifoBasedReportLatencyUs < MIN_LATENCY_US) {
+ maximumExpectedSamplingPeriodUs = MIN_LATENCY_US/fifoMaxEventCount;
+ fifoBasedReportLatencyUs = MIN_LATENCY_US;
+ }
+
+ final int MAX_REPORT_LATENCY_US = Integer.MAX_VALUE;
+ final int ALARM_WAKE_UP_DELAY_MS = fifoBasedReportLatencyUs/1000 +
+ (int)TimeUnit.SECONDS.toMillis(10);
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ this,
+ sensor,
+ false,
+ maximumExpectedSamplingPeriodUs,
+ MAX_REPORT_LATENCY_US,
+ true /*isDeviceSuspendTest*/);
+
+ int numEventsToWaitFor = fifoMaxEventCount;
+ TestSensorOperation op = TestSensorOperation.createOperation(environment,
+ numEventsToWaitFor,
+ mDeviceSuspendLock,
+ true);
+ mAlarmManager.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + ALARM_WAKE_UP_DELAY_MS,
+ mPendingIntent);
+ op.addDefaultVerifications();
+ try {
+ op.execute(getCurrentTestNode());
+ } finally {
+ mAlarmManager.cancel(mPendingIntent);
+ }
+ return null;
+ }
+}
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
index faba445..f8f1a9a 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/SignificantMotionTestActivity.java
@@ -16,22 +16,36 @@
package com.android.cts.verifier.sensors;
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
import com.android.cts.verifier.R;
import com.android.cts.verifier.sensors.base.SensorCtsVerifierTestActivity;
+import com.android.cts.verifier.sensors.helpers.SensorTestScreenManipulator;
-import junit.framework.Assert;
-
+import android.app.AlarmManager;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
import android.hardware.cts.helpers.SensorNotSupportedException;
import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.SuspendStateMonitor;
+import android.os.Handler;
+import android.os.Looper;
+import android.os.PowerManager;
import android.os.SystemClock;
+import android.support.v4.content.LocalBroadcastManager;
+import android.util.Log;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
/**
* Test cases for Significant Motion sensor.
@@ -46,20 +60,30 @@
private static final long MAX_ACCEPTABLE_EVENT_TIME_DELAY_NANOS =
TimeUnit.MILLISECONDS.toNanos(500);
+ // acceptable time difference between event time and AP wake up time.
+ private static final long MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS =
+ TimeUnit.MILLISECONDS.toNanos(2000);
+
+ // time to wait for SMD after the device has gone into suspend. Even after
+ // 45 secs if SMD does not trigger, the test will fail.
+ private static final long ALARM_WAKE_TIME_DELAY_MS = TimeUnit.SECONDS.toMillis(45);
+
// time for the test to wait for a trigger
private static final int TRIGGER_MAX_DELAY_SECONDS = 30;
private static final int VIBRATE_DURATION_MILLIS = 10000;
private static final int EVENT_VALUES_LENGTH = 1;
private static final float EXPECTED_EVENT_VALUE = 1.0f;
+ private static String ACTION_ALARM = "SignificantMotionTestActivity.ACTION_ALARM";
private SensorManager mSensorManager;
private Sensor mSensorSignificantMotion;
+ private TriggerVerifier mVerifier;
+ private SensorTestScreenManipulator mScreenManipulator;
/**
* Test cases.
*/
-
@SuppressWarnings("unused")
public String testTrigger() throws Throwable {
return runTest(
@@ -131,6 +155,69 @@
return result;
}
+ public static class AlarmReceiver extends BroadcastReceiver {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ Intent alarm_intent = new Intent(context, SignificantMotionTestActivity.class);
+ alarm_intent.setAction(SignificantMotionTestActivity.ACTION_ALARM);
+ LocalBroadcastManager.getInstance(context).sendBroadcastSync(alarm_intent);
+ }
+ }
+
+ public BroadcastReceiver myBroadCastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mVerifier.releaseLatch();
+ mScreenManipulator.turnScreenOn();
+ try {
+ playSound();
+ } catch (InterruptedException e) {
+ // Ignore ...
+ }
+ }
+ };
+
+ @SuppressWarnings("unused")
+ public String testAPWakeUpOnSMDTrigger() throws Throwable {
+ SensorTestLogger logger = getTestLogger();
+ logger.logInstructions(R.string.snsr_significant_motion_ap_suspend);
+ waitForUserToBegin();
+ mVerifier = new TriggerVerifier();
+ mSensorManager.requestTriggerSensor(mVerifier, mSensorSignificantMotion);
+ long testStartTimeNs = SystemClock.elapsedRealtimeNanos();
+ Handler handler = new Handler(Looper.getMainLooper());
+ SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
+
+ Intent intent = new Intent(this, AlarmReceiver.class);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, 0, intent, 0);
+
+ AlarmManager am = (AlarmManager) getSystemService(ALARM_SERVICE);
+ am.setExact(AlarmManager.ELAPSED_REALTIME_WAKEUP,
+ SystemClock.elapsedRealtime() + ALARM_WAKE_TIME_DELAY_MS, pendingIntent);
+ try {
+ // Wait for the first event to trigger. Device is expected to go into suspend here.
+ mVerifier.verifyEventTriggered();
+ long eventTimeStampNs = mVerifier.getTimeStampForTriggerEvent();
+ long endTimeNs = SystemClock.elapsedRealtimeNanos();
+ long lastWakeupTimeNs = TimeUnit.MILLISECONDS.toNanos(
+ suspendStateMonitor.getLastWakeUpTime());
+ Assert.assertTrue(getString(R.string.snsr_device_did_not_go_into_suspend),
+ testStartTimeNs < lastWakeupTimeNs && lastWakeupTimeNs < endTimeNs);
+ long timestampDelta = Math.abs(lastWakeupTimeNs - eventTimeStampNs);
+ Assert.assertTrue(
+ String.format(getString(R.string.snsr_device_did_not_wake_up_at_trigger),
+ TimeUnit.NANOSECONDS.toMillis(lastWakeupTimeNs),
+ TimeUnit.NANOSECONDS.toMillis(eventTimeStampNs)),
+ timestampDelta < MAX_ACCEPTABLE_DELAY_EVENT_AP_WAKE_UP_NS);
+ } finally {
+ am.cancel(pendingIntent);
+ suspendStateMonitor.cancel();
+ mScreenManipulator.turnScreenOn();
+ playSound();
+ }
+ return null;
+ }
+
/**
* @param instructionsResId Instruction to be shown to testers
* @param isMotionExpected Should the device detect significant motion event
@@ -187,6 +274,27 @@
if (mSensorSignificantMotion == null) {
throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
}
+
+ mScreenManipulator = new SensorTestScreenManipulator(this);
+ try {
+ mScreenManipulator.initialize(this);
+ } catch (InterruptedException e) {
+ }
+ PowerManager pm = (PowerManager)getSystemService(Context.POWER_SERVICE);
+ LocalBroadcastManager.getInstance(this).registerReceiver(myBroadCastReceiver,
+ new IntentFilter(ACTION_ALARM));
+ }
+
+ @Override
+ protected void activityCleanUp() {
+ mScreenManipulator.turnScreenOff();
+ LocalBroadcastManager.getInstance(this).unregisterReceiver(myBroadCastReceiver);
+ }
+
+ @Override
+ protected void onDestroy() {
+ super.onDestroy();
+ mScreenManipulator.close();
}
/**
@@ -194,7 +302,7 @@
* It cannot be reused.
*/
private class TriggerVerifier extends TriggerEventListener {
- private volatile CountDownLatch mCountDownLatch;
+ private volatile CountDownLatch mCountDownLatch = new CountDownLatch(1);
private volatile TriggerEventRegistry mEventRegistry;
// TODO: refactor out if needed
@@ -214,6 +322,19 @@
mCountDownLatch.countDown();
}
+ public void releaseLatch() {
+ if (mCountDownLatch != null) {
+ mCountDownLatch.countDown();
+ }
+ }
+
+ public long getTimeStampForTriggerEvent() {
+ if (mEventRegistry != null && mEventRegistry.triggerEvent != null) {
+ return mEventRegistry.triggerEvent.timestamp;
+ }
+ return 0;
+ }
+
public String verifyEventTriggered() throws Throwable {
TriggerEventRegistry registry = awaitForEvent();
@@ -267,11 +388,8 @@
}
private TriggerEventRegistry awaitForEvent() throws InterruptedException {
- mCountDownLatch = new CountDownLatch(1);
mCountDownLatch.await(TRIGGER_MAX_DELAY_SECONDS, TimeUnit.SECONDS);
-
TriggerEventRegistry registry = mEventRegistry;
- mEventRegistry = null;
playSound();
return registry != null ? registry : new TriggerEventRegistry(null, 0);
diff --git a/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java b/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
new file mode 100644
index 0000000..1cea80c
--- /dev/null
+++ b/common/device-side/util/src/com/android/compatibility/common/util/WifiConfigCreator.java
@@ -0,0 +1,182 @@
+/*
+ * 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.compatibility.common.util;
+
+import android.content.Context;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.util.List;
+
+/**
+ * A simple activity to create and manage wifi configurations.
+ */
+public class WifiConfigCreator {
+ public static final String CREATE_WIFI_CONFIG_ACTION =
+ "com.android.compatibility.common.util.CREATE_WIFI_CONFIG";
+ public static final String UPDATE_WIFI_CONFIG_ACTION =
+ "com.android.compatibility.common.util.UPDATE_WIFI_CONFIG";
+ public static final String REMOVE_WIFI_CONFIG_ACTION =
+ "com.android.compatibility.common.util.REMOVE_WIFI_CONFIG";
+ public static final String EXTRA_NETID = "extra-netid";
+ public static final String EXTRA_SSID = "extra-ssid";
+ public static final String EXTRA_SECURITY_TYPE = "extra-security-type";
+ public static final String EXTRA_PASSWORD = "extra-password";
+
+ public static final int SECURITY_TYPE_NONE = 1;
+ public static final int SECURITY_TYPE_WPA = 2;
+ public static final int SECURITY_TYPE_WEP = 3;
+
+ private static final String TAG = "WifiConfigCreator";
+
+ private final WifiManager mWifiManager;
+
+ public WifiConfigCreator(Context context) {
+ mWifiManager = (WifiManager) context.getSystemService(Context.WIFI_SERVICE);
+ }
+
+ /**
+ * Adds a new WiFi network.
+ * @return network id or -1 in case of error
+ */
+ public int addNetwork(String ssid, boolean hidden, int securityType,
+ String password) throws SecurityException {
+ if (!mWifiManager.isWifiEnabled()) {
+ mWifiManager.setWifiEnabled(true);
+ }
+
+ WifiConfiguration wifiConf = createConfig(ssid, hidden, securityType, password);
+
+ int netId = mWifiManager.addNetwork(wifiConf);
+
+ if (netId != -1) {
+ mWifiManager.enableNetwork(netId, true);
+ } else {
+ Log.w(TAG, "Unable to add SSID '" + ssid + "': netId = " + netId);
+ }
+ return netId;
+ }
+
+ /**
+ * Updates a new WiFi network.
+ * @return network id (may differ from original) or -1 in case of error
+ */
+ public int updateNetwork(WifiConfiguration wifiConf, String ssid, boolean hidden,
+ int securityType, String password) throws SecurityException {
+ if (!mWifiManager.isWifiEnabled()) {
+ mWifiManager.setWifiEnabled(true);
+ }
+ if (wifiConf == null) {
+ return -1;
+ }
+
+ WifiConfiguration conf = createConfig(ssid, hidden, securityType, password);
+ conf.networkId = wifiConf.networkId;
+
+ int newNetId = mWifiManager.updateNetwork(conf);
+
+ if (newNetId != -1) {
+ mWifiManager.saveConfiguration();
+ mWifiManager.enableNetwork(newNetId, true);
+ } else {
+ Log.w(TAG, "Unable to update SSID '" + ssid + "': netId = " + newNetId);
+ }
+ return newNetId;
+ }
+
+ /**
+ * Updates a new WiFi network.
+ * @return network id (may differ from original) or -1 in case of error
+ */
+ public int updateNetwork(int netId, String ssid, boolean hidden,
+ int securityType, String password) throws SecurityException {
+ if (!mWifiManager.isWifiEnabled()) {
+ mWifiManager.setWifiEnabled(true);
+ }
+
+ WifiConfiguration wifiConf = null;
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ for (WifiConfiguration config : configs) {
+ if (config.networkId == netId) {
+ wifiConf = config;
+ break;
+ }
+ }
+ return updateNetwork(wifiConf, ssid, hidden, securityType, password);
+ }
+
+ public boolean removeNetwork(int netId) {
+ return mWifiManager.removeNetwork(netId);
+ }
+
+ /**
+ * Creates a WifiConfiguration set up according to given parameters
+ * @param ssid SSID of the network
+ * @param hidden Is SSID not broadcast?
+ * @param securityType One of {@link #SECURITY_TYPE_NONE}, {@link #SECURITY_TYPE_WPA} or
+ * {@link #SECURITY_TYPE_WEP}
+ * @param password Password for WPA or WEP
+ * @return Created configuration object
+ */
+ private WifiConfiguration createConfig(String ssid, boolean hidden, int securityType,
+ String password) {
+ WifiConfiguration wifiConf = new WifiConfiguration();
+ if (!TextUtils.isEmpty(ssid)) {
+ wifiConf.SSID = '"' + ssid + '"';
+ }
+ wifiConf.status = WifiConfiguration.Status.ENABLED;
+ wifiConf.hiddenSSID = hidden;
+ switch (securityType) {
+ case SECURITY_TYPE_NONE:
+ wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ break;
+ case SECURITY_TYPE_WPA:
+ updateForWPAConfiguration(wifiConf, password);
+ break;
+ case SECURITY_TYPE_WEP:
+ updateForWEPConfiguration(wifiConf, password);
+ break;
+ }
+ return wifiConf;
+ }
+
+ private void updateForWPAConfiguration(WifiConfiguration wifiConf, String wifiPassword) {
+ wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.WPA_PSK);
+ if (!TextUtils.isEmpty(wifiPassword)) {
+ wifiConf.preSharedKey = '"' + wifiPassword + '"';
+ }
+ }
+
+ private void updateForWEPConfiguration(WifiConfiguration wifiConf, String password) {
+ wifiConf.allowedKeyManagement.set(WifiConfiguration.KeyMgmt.NONE);
+ wifiConf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.OPEN);
+ wifiConf.allowedAuthAlgorithms.set(WifiConfiguration.AuthAlgorithm.SHARED);
+ if (!TextUtils.isEmpty(password)) {
+ int length = password.length();
+ if ((length == 10 || length == 26
+ || length == 58) && password.matches("[0-9A-Fa-f]*")) {
+ wifiConf.wepKeys[0] = password;
+ } else {
+ wifiConf.wepKeys[0] = '"' + password + '"';
+ }
+ wifiConf.wepTxKeyIndex = 0;
+ }
+ }
+}
+
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
index bd0089f..314f996 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/Android.mk
@@ -26,7 +26,7 @@
LOCAL_JAVA_LIBRARIES := android.test.runner cts-junit
-LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner
+LOCAL_STATIC_JAVA_LIBRARIES := ctstestrunner compatibility-device-util_v2
LOCAL_SDK_VERSION := current
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
index 3b90ef9..64c7262 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/AndroidManifest.xml
@@ -21,6 +21,8 @@
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.WRITE_SETTINGS" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<application>
<uses-library android:name="android.test.runner" />
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
index dcb5bbc..b6815fd 100644
--- a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/BaseDeviceOwnerTest.java
@@ -59,6 +59,7 @@
}
static void assertDeviceOwner(DevicePolicyManager dpm) {
+ assertNotNull(dpm);
assertTrue(dpm.isAdminActive(getWho()));
assertTrue(dpm.isDeviceOwnerApp(PACKAGE_NAME));
}
diff --git a/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
new file mode 100644
index 0000000..ef1d8f7
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/DeviceOwner/src/com/android/cts/deviceowner/WifiConfigLockdownTest.java
@@ -0,0 +1,185 @@
+/*
+ * 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.deviceowner;
+
+import android.content.Context;
+import android.content.Intent;
+import android.net.wifi.WifiConfiguration;
+import android.net.wifi.WifiManager;
+import android.provider.Settings;
+
+import com.android.compatibility.common.util.WifiConfigCreator;
+
+import java.util.List;
+
+import static com.android.compatibility.common.util.WifiConfigCreator.CREATE_WIFI_CONFIG_ACTION;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_NETID;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_PASSWORD;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SECURITY_TYPE;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SSID;
+import static com.android.compatibility.common.util.WifiConfigCreator.REMOVE_WIFI_CONFIG_ACTION;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_NONE;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_WPA;
+import static com.android.compatibility.common.util.WifiConfigCreator.UPDATE_WIFI_CONFIG_ACTION;
+
+/**
+ * Testing WiFi configuration lockdown by Device Owner
+ */
+public class WifiConfigLockdownTest extends BaseDeviceOwnerTest {
+ private static final String TAG = "WifiConfigLockdownTest";
+ private static final String ORIGINAL_DEVICE_OWNER_SSID = "DOCTSTest";
+ private static final String CHANGED_DEVICE_OWNER_SSID = "DOChangedCTSTest";
+ private static final String ORIGINAL_REGULAR_SSID = "RegularCTSTest";
+ private static final String CHANGED_REGULAR_SSID = "RegularChangedCTSTest";
+ private static final String ORIGINAL_PASSWORD = "originalpassword";
+ private WifiManager mWifiManager;
+ private WifiConfigCreator mConfigCreator;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mWifiManager = (WifiManager) mContext.getSystemService(Context.WIFI_SERVICE);
+ mConfigCreator = new WifiConfigCreator(mContext);
+ mDevicePolicyManager.setGlobalSetting(getWho(),
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, "1");
+ mConfigCreator.addNetwork(ORIGINAL_DEVICE_OWNER_SSID, true, SECURITY_TYPE_WPA,
+ ORIGINAL_PASSWORD);
+ startRegularActivity(CREATE_WIFI_CONFIG_ACTION, -1, ORIGINAL_REGULAR_SSID,
+ SECURITY_TYPE_WPA, ORIGINAL_PASSWORD);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mDevicePolicyManager.setGlobalSetting(getWho(),
+ Settings.Global.WIFI_DEVICE_OWNER_CONFIGS_LOCKDOWN, "0");
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ for (WifiConfiguration config : configs) {
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID) ||
+ areMatchingSsids(CHANGED_DEVICE_OWNER_SSID, config.SSID) ||
+ areMatchingSsids(ORIGINAL_REGULAR_SSID, config.SSID) ||
+ areMatchingSsids(CHANGED_REGULAR_SSID, config.SSID)) {
+ mWifiManager.removeNetwork(config.networkId);
+ }
+ }
+ super.tearDown();
+ }
+
+ public void testDeviceOwnerCanUpdateConfig() throws Exception {
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ int updateCount = 0;
+ for (WifiConfiguration config : configs) {
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)) {
+ assertFalse(-1 == mConfigCreator.updateNetwork(config,
+ CHANGED_DEVICE_OWNER_SSID, true, SECURITY_TYPE_NONE, null));
+ ++updateCount;
+ }
+ if (areMatchingSsids(ORIGINAL_REGULAR_SSID, config.SSID)) {
+ assertFalse(-1 == mConfigCreator.updateNetwork(config,
+ CHANGED_REGULAR_SSID, true, SECURITY_TYPE_NONE, null));
+ ++updateCount;
+ }
+ }
+ assertEquals("Expected to update two configs: the DO created one and the regular one." +
+ " Instead updated: " + updateCount, 2, updateCount);
+ }
+
+ public void testDeviceOwnerCanRemoveConfig() throws Exception {
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ int removeCount = 0;
+ for (WifiConfiguration config : configs) {
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID) ||
+ areMatchingSsids(ORIGINAL_REGULAR_SSID, config.SSID)) {
+ assertTrue(mWifiManager.removeNetwork(config.networkId));
+ ++removeCount;
+ }
+ }
+ assertEquals("Expected to remove two configs: the DO created one and the regular one." +
+ " Instead removed: " + removeCount, 2, removeCount);
+ }
+
+ public void testRegularAppCannotUpdateDeviceOwnerConfig() throws Exception {
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ int updateCount = 0;
+ for (WifiConfiguration config : configs) {
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)) {
+ startRegularActivity(UPDATE_WIFI_CONFIG_ACTION, config.networkId,
+ CHANGED_DEVICE_OWNER_SSID, SECURITY_TYPE_NONE, null);
+ ++updateCount;
+ }
+ }
+ assertEquals("Expected to have tried to update one config: the DO created one" +
+ " Instead tried to update: " + updateCount, 1, updateCount);
+
+ // Assert nothing has changed
+ configs = mWifiManager.getConfiguredNetworks();
+ int notChangedCount = 0;
+ for (WifiConfiguration config : configs) {
+ assertFalse(areMatchingSsids(CHANGED_DEVICE_OWNER_SSID, config.SSID));
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)) {
+ ++notChangedCount;
+ }
+ }
+ assertEquals("Expected to see one unchanged config, saw instead: " + notChangedCount, 1,
+ notChangedCount);
+ }
+
+ public void testRegularAppCannotRemoveDeviceOwnerConfig() throws Exception {
+ List<WifiConfiguration> configs = mWifiManager.getConfiguredNetworks();
+ int removeCount = 0;
+ for (WifiConfiguration config : configs) {
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)) {
+ startRegularActivity(REMOVE_WIFI_CONFIG_ACTION, config.networkId,
+ null, SECURITY_TYPE_NONE, null);
+ ++removeCount;
+ }
+ }
+ assertEquals("Expected to try to remove one config: the DO created one." +
+ " Instead tried to remove: " + removeCount, 1, removeCount);
+
+ // Assert nothing has changed
+ configs = mWifiManager.getConfiguredNetworks();
+ int notChangedCount = 0;
+ for (WifiConfiguration config : configs) {
+ if (areMatchingSsids(ORIGINAL_DEVICE_OWNER_SSID, config.SSID)) {
+ ++notChangedCount;
+ }
+ }
+ assertEquals("Expected to see one unchanged config, saw instead: " + notChangedCount, 1,
+ notChangedCount);
+ }
+
+ private void startRegularActivity(String action, int netId, String ssid, int securityType,
+ String password) throws InterruptedException {
+ Intent createRegularConfig = new Intent(action);
+ createRegularConfig.putExtra(EXTRA_NETID, netId);
+ createRegularConfig.putExtra(EXTRA_SSID, ssid);
+ createRegularConfig.putExtra(EXTRA_SECURITY_TYPE, securityType);
+ createRegularConfig.putExtra(EXTRA_PASSWORD, password);
+ createRegularConfig.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ mContext.startActivity(createRegularConfig);
+
+ // Give some time for the other app to finish the action
+ Thread.sleep(2000);
+ }
+
+ private boolean areMatchingSsids(String s1, String s2) {
+ if (s1 == null || s2 == null) {
+ return false;
+ }
+ return s1.replace("\"", "").equals(s2.replace("\"", ""));
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/Android.mk b/hostsidetests/devicepolicy/app/WifiConfigCreator/Android.mk
new file mode 100644
index 0000000..47db44e
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/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
+
+# 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_PACKAGE_NAME := CtsWifiConfigCreator
+
+LOCAL_STATIC_JAVA_LIBRARIES := compatibility-device-util_v2
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml b/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
new file mode 100644
index 0000000..1b98259
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/AndroidManifest.xml
@@ -0,0 +1,47 @@
+<?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.deviceowner.wificonfigcreator">
+
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
+
+ <application>
+ <activity android:name=".WifiConfigCreatorActivity"
+ android:exported="true"
+ android:theme="@android:style/Theme.NoDisplay"
+ android:noHistory="true"
+ android:autoRemoveFromRecents="true"
+ android:stateNotNeeded="true" >
+ <intent-filter>
+ <action android:name="com.android.compatibility.common.util.CREATE_WIFI_CONFIG" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="com.android.compatibility.common.util.UPDATE_WIFI_CONFIG" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ <intent-filter>
+ <action android:name="com.android.compatibility.common.util.REMOVE_WIFI_CONFIG" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
+
diff --git a/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java b/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java
new file mode 100644
index 0000000..7958cb1
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/WifiConfigCreator/src/com/android/cts/deviceowner/wificonfigcreator/WifiConfigCreatorActivity.java
@@ -0,0 +1,71 @@
+/*
+ * 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.deviceowner.wificonfigcreator;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+
+import com.android.compatibility.common.util.WifiConfigCreator;
+import static com.android.compatibility.common.util.WifiConfigCreator.CREATE_WIFI_CONFIG_ACTION;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_NETID;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_PASSWORD;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SECURITY_TYPE;
+import static com.android.compatibility.common.util.WifiConfigCreator.EXTRA_SSID;
+import static com.android.compatibility.common.util.WifiConfigCreator.REMOVE_WIFI_CONFIG_ACTION;
+import static com.android.compatibility.common.util.WifiConfigCreator.SECURITY_TYPE_NONE;
+import static com.android.compatibility.common.util.WifiConfigCreator.UPDATE_WIFI_CONFIG_ACTION;
+
+/**
+ * A simple activity to create and manage wifi configurations.
+ */
+public class WifiConfigCreatorActivity extends Activity {
+ private static final String TAG = "WifiConfigCreatorActivity";
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ Log.i(TAG, "Created for user " + android.os.Process.myUserHandle());
+ WifiConfigCreator configCreator = new WifiConfigCreator(this);
+ try {
+ Intent intent = getIntent();
+ String action = intent.getAction();
+ if (CREATE_WIFI_CONFIG_ACTION.equals(action)) {
+ String ssid = intent.getStringExtra(EXTRA_SSID);
+ int securityType = intent.getIntExtra(EXTRA_SECURITY_TYPE, SECURITY_TYPE_NONE);
+ String password = intent.getStringExtra(EXTRA_PASSWORD);
+ configCreator.addNetwork(ssid, false, securityType, password);
+ } else if (UPDATE_WIFI_CONFIG_ACTION.equals(action)) {
+ int netId = intent.getIntExtra(EXTRA_NETID, -1);
+ String ssid = intent.getStringExtra(EXTRA_SSID);
+ int securityType = intent.getIntExtra(EXTRA_SECURITY_TYPE, SECURITY_TYPE_NONE);
+ String password = intent.getStringExtra(EXTRA_PASSWORD);
+ configCreator.updateNetwork(netId, ssid, false, securityType, password);
+ } else if (REMOVE_WIFI_CONFIG_ACTION.equals(action)) {
+ int netId = intent.getIntExtra(EXTRA_NETID, -1);
+ if (netId != -1) {
+ configCreator.removeNetwork(netId);
+ }
+ } else {
+ Log.i(TAG, "Unknown command: " + action);
+ }
+ } finally {
+ finish();
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
index ebdbcc7..66a123a1 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/DeviceOwnerTest.java
@@ -37,6 +37,10 @@
private static final String INTENT_RECEIVER_PKG = "com.android.cts.intent.receiver";
private static final String INTENT_RECEIVER_APK = "CtsIntentReceiverApp.apk";
+ private static final String WIFI_CONFIG_CREATOR_PKG =
+ "com.android.cts.deviceowner.wificonfigcreator";
+ private static final String WIFI_CONFIG_CREATOR_APK = "CtsWifiConfigCreator.apk";
+
private static final String ADMIN_RECEIVER_TEST_CLASS =
DEVICE_OWNER_PKG + ".BaseDeviceOwnerTest$BasicAdminReceiver";
private static final String CLEAR_DEVICE_OWNER_TEST_CLASS =
@@ -112,6 +116,18 @@
executeDeviceOwnerTest("SystemUpdatePolicyTest");
}
+ public void testWifiConfigLockdown() throws Exception {
+ final boolean hasWifi = hasDeviceFeature("android.hardware.wifi");
+ if (hasWifi && mHasFeature) {
+ try {
+ installApp(WIFI_CONFIG_CREATOR_APK);
+ executeDeviceOwnerTest("WifiConfigLockdownTest");
+ } finally {
+ getDevice().uninstallPackage(WIFI_CONFIG_CREATOR_PKG);
+ }
+ }
+ }
+
private void executeDeviceOwnerTest(String testClassName) throws Exception {
if (!mHasFeature) {
return;
diff --git a/tests/tests/accessibility/Android.mk b/tests/tests/accessibility/Android.mk
index bb943ee..263c47b 100644
--- a/tests/tests/accessibility/Android.mk
+++ b/tests/tests/accessibility/Android.mk
@@ -26,4 +26,6 @@
LOCAL_STATIC_JAVA_LIBRARIES := ctsdeviceutil ctstestrunner
+LOCAL_SDK_VERSION := current
+
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/app/Android.mk b/tests/tests/app/Android.mk
index 4a3d31f..301f931 100644
--- a/tests/tests/app/Android.mk
+++ b/tests/tests/app/Android.mk
@@ -31,4 +31,6 @@
LOCAL_INSTRUMENTATION_FOR := CtsAppTestStubs
+LOCAL_SDK_VERSION := current
+
include $(BUILD_CTS_PACKAGE)
diff --git a/tests/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java b/tests/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
index 9554438..58e69b8 100644
--- a/tests/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
+++ b/tests/tests/app/src/android/app/cts/AlertDialog_BuilderTest.java
@@ -577,26 +577,6 @@
assertEquals(view, mView);
}
- public void testSetViewCustom() throws Throwable {
- final int viewSpacingLeft = 10;
- final int viewSpacingTop = 20;
- final int viewSpacingRight = 30;
- final int viewSpacingBottom = 40;
- final View view = new View(mContext);
- view.setId(100);
- runTestOnUiThread(new Runnable() {
- public void run() {
- mBuilder = new AlertDialog.Builder(mContext);
- mBuilder.setView(view, viewSpacingLeft, viewSpacingTop, viewSpacingRight,
- viewSpacingBottom);
- mDialog = mBuilder.show();
- mView = mDialog.getWindow().findViewById(100);
- }
- });
- mInstrumentation.waitForIdleSync();
- assertEquals(view, mView);
- }
-
public void testSetInverseBackgroundForced() throws Throwable {
runTestOnUiThread(new Runnable() {
public void run() {
diff --git a/tests/tests/app/src/android/app/cts/InstrumentationTest.java b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
index b21148e..25403f3 100644
--- a/tests/tests/app/src/android/app/cts/InstrumentationTest.java
+++ b/tests/tests/app/src/android/app/cts/InstrumentationTest.java
@@ -540,7 +540,6 @@
public void openPanel(int featureId, KeyEvent event) {
}
- @Override
public void alwaysReadCloseOnTouchAttr() {
}
diff --git a/tests/tests/app/src/android/app/cts/NotificationTest.java b/tests/tests/app/src/android/app/cts/NotificationTest.java
index 17f433e..6179922 100644
--- a/tests/tests/app/src/android/app/cts/NotificationTest.java
+++ b/tests/tests/app/src/android/app/cts/NotificationTest.java
@@ -166,16 +166,6 @@
assertNotNull(mNotification.contentView);
}
- public void testSetLatestEventInfo() {
- mNotification = new Notification();
- mNotification.icon = 1;
- final Intent intent = new Intent();
- final PendingIntent contentIntent = PendingIntent.getBroadcast(mContext, 0, intent, 0);
- mNotification.setLatestEventInfo(mContext, CONTENT_TITLE, CONTENT_TEXT, contentIntent);
- assertTrue(mNotification.contentView instanceof RemoteViews);
- assertNotNull(mNotification.contentView);
- }
-
public void testToString() {
mNotification = new Notification();
assertNotNull(mNotification.toString());
diff --git a/tests/tests/app/src/android/app/cts/SearchDialogTest.java b/tests/tests/app/src/android/app/cts/SearchDialogTest.java
deleted file mode 100644
index 1cf1ebd..0000000
--- a/tests/tests/app/src/android/app/cts/SearchDialogTest.java
+++ /dev/null
@@ -1,95 +0,0 @@
-/*
- * 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.app.cts;
-
-import android.app.SearchDialog;
-import android.content.Context;
-import android.test.ActivityInstrumentationTestCase2;
-import android.test.InstrumentationTestCase;
-import android.view.ActionMode;
-import android.view.View;
-import android.view.ViewGroup;
-
-/**
- * Test {@link SearchDialog}.
- */
-public class SearchDialogTest extends InstrumentationTestCase {
-
- private Context mContext;
-
- @Override
- protected void setUp() throws Exception {
- super.setUp();
- mContext = getInstrumentation().getTargetContext();
- }
-
- public void testPrimaryActionModesAreStopped() {
- SearchDialog.SearchBar searchBar = new SearchDialog.SearchBar(mContext);
- MockViewGroup viewGroup = new MockViewGroup(mContext);
- viewGroup.addView(searchBar);
-
- ActionMode mode = searchBar.startActionModeForChild(null, null, ActionMode.TYPE_PRIMARY);
-
- assertNull(mode);
- // Should not bubble up.
- assertFalse(viewGroup.isStartActionModeForChildTypedCalled);
- assertFalse(viewGroup.isStartActionModeForChildTypelessCalled);
-
- mode = searchBar.startActionModeForChild(null, null);
-
- assertNull(mode);
- // Should not bubble up.
- assertFalse(viewGroup.isStartActionModeForChildTypedCalled);
- assertFalse(viewGroup.isStartActionModeForChildTypelessCalled);
- }
-
- public void testFloatingActionModesAreBubbledUp() {
- SearchDialog.SearchBar searchBar = new SearchDialog.SearchBar(mContext);
- MockViewGroup viewGroup = new MockViewGroup(mContext);
- viewGroup.addView(searchBar);
-
- searchBar.startActionModeForChild(null, null, ActionMode.TYPE_FLOATING);
-
- // Should bubble up.
- assertTrue(viewGroup.isStartActionModeForChildTypedCalled);
- }
-
- private static class MockViewGroup extends ViewGroup {
- boolean isStartActionModeForChildTypedCalled = false;
- boolean isStartActionModeForChildTypelessCalled = false;
-
- public MockViewGroup(Context context) {
- super(context);
- }
-
- @Override
- public ActionMode startActionModeForChild(View originalView, ActionMode.Callback callback) {
- isStartActionModeForChildTypelessCalled = true;
- return super.startActionModeForChild(originalView, callback);
- }
-
- @Override
- public ActionMode startActionModeForChild(
- View originalView, ActionMode.Callback callback, int type) {
- isStartActionModeForChildTypedCalled = true;
- return super.startActionModeForChild(originalView, callback, type);
- }
-
- @Override
- protected void onLayout(boolean changed, int l, int t, int r, int b) {}
- }
-}
diff --git a/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java b/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
index f3a4ba1..187fe06 100644
--- a/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
+++ b/tests/tests/content/src/android/content/cts/HighPriorityBroadcastReceiver.java
@@ -22,13 +22,11 @@
public class HighPriorityBroadcastReceiver extends ResultReceiver {
@Override
- public void onReceive(Context context, Intent intent) {
+ public synchronized void onReceive(Context context, Intent intent) {
super.onReceive(context, intent);
try {
- synchronized (this) {
- wait();
- }
+ wait();
} catch (InterruptedException e) {
throw new RuntimeException("Got interrupted during wait()", e);
}
diff --git a/tests/tests/graphics/src/android/graphics/cts/VulkanReservedTest.java b/tests/tests/graphics/src/android/graphics/cts/VulkanReservedTest.java
new file mode 100644
index 0000000..bfd520e
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/cts/VulkanReservedTest.java
@@ -0,0 +1,43 @@
+/*
+ * 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.graphics.cts;
+
+import android.cts.util.FileUtils;
+
+import junit.framework.TestCase;
+
+import java.io.File;
+
+public class VulkanReservedTest extends TestCase {
+
+ /**
+ * Assert that file with given path does not exist.
+ */
+ private static void assertNoFile(String filename) {
+ assertFalse(filename + " must not exist", new File(filename).exists());
+ }
+
+ /**
+ * Test that no vendor ships libvulkan.so before ratification and
+ * appropriate CTS coverage.
+ */
+ public void testNoVulkan() {
+ assertNoFile("/system/lib/libvulkan.so");
+ assertNoFile("/system/lib64/libvulkan.so");
+ assertNoFile("/vendor/lib/libvulkan.so");
+ assertNoFile("/vendor/lib64/libvulkan.so");
+ }
+}
diff --git a/tests/tests/graphics/src/android/graphics/drawable/cts/IconTest.java b/tests/tests/graphics/src/android/graphics/drawable/cts/IconTest.java
new file mode 100644
index 0000000..d89ce7c
--- /dev/null
+++ b/tests/tests/graphics/src/android/graphics/drawable/cts/IconTest.java
@@ -0,0 +1,200 @@
+/*
+ * 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.graphics.drawable.cts;
+
+import com.android.cts.graphics.R;
+
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.res.ColorStateList;
+import android.graphics.Bitmap;
+import android.graphics.cts.ImageViewCtsActivity;
+import android.graphics.drawable.Icon;
+import android.graphics.drawable.Drawable;
+import android.graphics.Color;
+import android.graphics.PorterDuff;
+import android.net.Uri;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Parcel;
+import android.test.ActivityInstrumentationTestCase2;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+public class IconTest extends ActivityInstrumentationTestCase2<ImageViewCtsActivity> {
+ static final long TIMEOUT = 1000;
+
+ Activity mActivity;
+ Instrumentation mInstrumentation;
+ Icon mIcon;
+
+ MockOnDrawableLoadedListener mListener;
+ MockRunner mRunner;
+
+ public IconTest() {
+ super("com.android.cts.graphics", ImageViewCtsActivity.class);
+ }
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mActivity = getActivity();
+ mInstrumentation = getInstrumentation();
+ }
+
+ public void testBitmapIcon() {
+ checkIconValidity(
+ Icon.createWithBitmap(Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888)));
+ }
+
+ public void testDataIcon() {
+ byte[] data = new byte[4];
+ data[0] = data[1] = data[2] = data[3] = (byte)255;
+ checkIconValidity(Icon.createWithData(data, 0, 4));
+ }
+
+ public void testFileIcon() throws IOException {
+ File file = new File(mActivity.getFilesDir(), "testimage.jpg");
+ try {
+ writeSampleImage(file);
+ assertTrue(file.exists());
+
+ checkIconValidity(Icon.createWithFilePath(file.getPath()));
+
+ checkIconValidity(Icon.createWithContentUri(Uri.fromFile(file)));
+
+ checkIconValidity(Icon.createWithContentUri(file.toURI().toString()));
+ } finally {
+ file.delete();
+ }
+ }
+
+ public void testResourceIcon() {
+ checkIconValidity(Icon.createWithResource(mActivity, R.drawable.bmp_test));
+
+ checkIconValidity(Icon.createWithResource(mActivity.getPackageName(), R.drawable.bmp_test));
+ }
+
+ public void testLoadDrawableAsync() {
+ mIcon = Icon.createWithBitmap(Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888));
+
+ mListener = new MockOnDrawableLoadedListener();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mIcon.loadDrawableAsync(mActivity, mListener, new Handler());
+ }
+ });
+ sleep(TIMEOUT);
+
+ assertEquals(1, mListener.getLoadedCount());
+ }
+
+ public void testLoadDrawableAsyncWithMessage() {
+ mIcon = Icon.createWithBitmap(Bitmap.createBitmap(16, 16, Bitmap.Config.ARGB_8888));
+
+ mRunner = new MockRunner();
+ mInstrumentation.runOnMainSync(new Runnable() {
+ @Override
+ public void run() {
+ mIcon.loadDrawableAsync(mActivity, Message.obtain(new Handler(), mRunner));
+ }
+ });
+ sleep(TIMEOUT);
+
+ assertEquals(1, mRunner.getRunCount());
+ }
+
+ class MockOnDrawableLoadedListener implements Icon.OnDrawableLoadedListener {
+ int mLoadedCount;
+
+ @Override
+ public void onDrawableLoaded(Drawable d) {
+ assertNotNull(d);
+ ++mLoadedCount;
+ }
+
+ int getLoadedCount() { return mLoadedCount; }
+ }
+
+ class MockRunner implements Runnable {
+ int mRun;
+
+ @Override
+ public void run() {
+ ++mRun;
+ }
+
+ int getRunCount() { return mRun; }
+ };
+
+ private void sleep(long time) {
+ try {
+ Thread.sleep(time);
+ } catch (InterruptedException e) {
+ fail(e.getMessage());
+ }
+ }
+
+ private void writeSampleImage(File imagefile) throws IOException {
+ InputStream source = null;
+ OutputStream target = null;
+
+ try {
+ source = mActivity.getResources().openRawResource(R.drawable.testimage);
+ target = new FileOutputStream(imagefile);
+
+ byte[] buffer = new byte[1024];
+ for (int len = source.read(buffer); len >= 0; len = source.read(buffer)) {
+ target.write(buffer, 0, len);
+ }
+ } finally {
+ if (target != null) {
+ target.close();
+ }
+
+ if (source != null) {
+ source.close();
+ }
+ }
+ }
+
+ // Check if the created icon is valid and doesn't cause crashes for the public methods.
+ private void checkIconValidity(Icon icon) {
+ assertNotNull(icon);
+
+ // tint properties.
+ icon.setTint(Color.BLUE);
+ icon.setTintList(ColorStateList.valueOf(Color.RED));
+ icon.setTintMode(PorterDuff.Mode.XOR);
+
+ // Parcelable methods.
+ icon.describeContents();
+ Parcel parcel = Parcel.obtain();
+ icon.writeToParcel(parcel, 0);
+
+ parcel.setDataPosition(0);
+ assertNotNull(Icon.CREATOR.createFromParcel(parcel));
+
+ // loading drawable synchronously.
+ assertNotNull(icon.loadDrawable(mActivity));
+ }
+}
diff --git a/tests/tests/graphics/src/android/opengl/cts/EglConfigGLSurfaceView.java b/tests/tests/graphics/src/android/opengl/cts/EglConfigGLSurfaceView.java
index 03e8d94..eb36166 100644
--- a/tests/tests/graphics/src/android/opengl/cts/EglConfigGLSurfaceView.java
+++ b/tests/tests/graphics/src/android/opengl/cts/EglConfigGLSurfaceView.java
@@ -101,7 +101,7 @@
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
gl.glColor4f(1.0f, 0, 0, 0);
gl.glVertexPointer(3, GL10.GL_FLOAT, 0, mFloatBuffer);
- gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 9);
+ gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 3);
if (++mNumFrames == 10) {
post(mCallback);
diff --git a/tests/tests/hardware/AndroidTest.xml b/tests/tests/hardware/AndroidTest.xml
index 783eafe..4ddf28c 100644
--- a/tests/tests/hardware/AndroidTest.xml
+++ b/tests/tests/hardware/AndroidTest.xml
@@ -17,6 +17,6 @@
<include name="common-config" />
<!-- Put SensorService in restricted mode so that only CTS tests will be able to get access to
sensors -->
- <option name="run-command:run-command" value="dumpsys sensorservice restrict" />
+ <option name="run-command:run-command" value="dumpsys sensorservice restrict .cts." />
<option name="run-command:teardown-command" value="dumpsys sensorservice enable" />
</configuration>
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index ad1951c..cd4b731 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -60,7 +60,7 @@
private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
private static final boolean DEBUG_DUMP = Log.isLoggable(TAG, Log.DEBUG);
private static final int RECORDING_DURATION_MS = 3000;
- private static final int DURATION_MARGIN_MS = 600;
+ private static final float DURATION_MARGIN = 0.2f;
private static final double FRAME_DURATION_ERROR_TOLERANCE_MS = 3.0;
private static final int BIT_RATE_1080P = 16000000;
private static final int BIT_RATE_MIN = 64000;
@@ -1080,7 +1080,7 @@
}
}
- private void validateRecording(Size sz, int durationMs) throws Exception {
+ private void validateRecording(Size sz, int expectedDurationMs) throws Exception {
File outFile = new File(mOutMediaFileName);
assertTrue("No video is recorded", outFile.exists());
@@ -1108,15 +1108,16 @@
int duration = (int) (durationUs / 1000);
if (VERBOSE) {
Log.v(TAG, String.format("Video duration: recorded %dms, expected %dms",
- duration, durationMs));
+ duration, expectedDurationMs));
}
// TODO: Don't skip this for video snapshot
if (!mStaticInfo.isHardwareLevelLegacy()) {
assertTrue(String.format(
"Camera %s: Video duration doesn't match: recorded %dms, expected %dms.",
- mCamera.getId(), duration, durationMs),
- Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
+ mCamera.getId(), duration, expectedDurationMs),
+ Math.abs(duration - expectedDurationMs) <
+ DURATION_MARGIN * expectedDurationMs);
}
} finally {
extractor.release();
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index ee4ddd9..d8dda8f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -341,6 +341,12 @@
Log.i(TAG, "Testing AE precapture cancel for jpeg capture for Camera " + id);
openDevice(id);
+ // Legacy device doesn't support AE precapture trigger
+ if (mStaticInfo.isHardwareLevelLegacy()) {
+ Log.i(TAG, "Skipping AE precapture trigger cancel test on legacy devices");
+ continue;
+ }
+
takePictureTestByCamera(/*aeRegions*/null, /*awbRegions*/null, /*afRegions*/null,
/*addAeTriggerCancel*/true);
} finally {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
index f97c08f..818b6c0 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/rs/RawConverter.java
@@ -608,8 +608,11 @@
float[] referenceNeutral = new float[3];
map(inverseInterpolatedCC, cameraNeutral, /*out*/referenceNeutral);
if (DEBUG) Log.d(TAG, "Reference neutral: " + Arrays.toString(referenceNeutral));
- float[] D = new float[] { 1/referenceNeutral[0], 0, 0, 0, 1/referenceNeutral[1], 0, 0, 0,
- 1/referenceNeutral[2] };
+ float maxNeutral = Math.max(Math.max(referenceNeutral[0], referenceNeutral[1]),
+ referenceNeutral[2]);
+ float[] D = new float[] { maxNeutral/referenceNeutral[0], 0, 0,
+ 0, maxNeutral/referenceNeutral[1], 0,
+ 0, 0, maxNeutral/referenceNeutral[2] };
if (DEBUG) Log.d(TAG, "Reference Neutral Diagonal: " + Arrays.toString(D));
float[] intermediate = new float[9];
diff --git a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
old mode 100644
new mode 100755
index 0e09e8c..0a0607d
--- a/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/LowRamDeviceTest.java
@@ -152,7 +152,7 @@
private void assertMinMemoryMb(long minMb) {
long totalMemoryMb = getTotalMemory() / ONE_MEGABYTE;
- boolean lowRam = totalMemoryMb <= minMb * 1.5;
+ boolean lowRam = totalMemoryMb <= 512;
boolean lowRamDevice = mActivityManager.isLowRamDevice();
Log.i(TAG, String.format("minMb=%,d", minMb));
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 4450339..6beeec8 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -348,9 +348,9 @@
TestSensorEventListener listener = new TestSensorEventListener(environment, handler);
CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1);
- listener.waitForEvents(eventLatch, 1);
+ listener.waitForEvents(eventLatch, 1, true);
CountDownLatch flushLatch = mTestSensorManager.requestFlush();
- listener.waitForFlushComplete(flushLatch);
+ listener.waitForFlushComplete(flushLatch, true);
listener.assertEventsReceivedInHandler();
}
@@ -382,9 +382,9 @@
// specifyHandler <= false, use the SensorManager API without Handler parameter
CountDownLatch eventLatch = mTestSensorManager.registerListener(listener, 1, false);
- listener.waitForEvents(eventLatch, 1);
+ listener.waitForEvents(eventLatch, 1, true);
CountDownLatch flushLatch = mTestSensorManager.requestFlush();
- listener.waitForFlushComplete(flushLatch);
+ listener.waitForFlushComplete(flushLatch, true);
listener.assertEventsReceivedInHandler();
}
@@ -581,10 +581,10 @@
try {
CountDownLatch eventLatch = sensorManager.registerListener(listener, mEventCount);
if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
- listener.waitForEvents(eventLatch, mEventCount);
+ listener.waitForEvents(eventLatch, mEventCount, true);
}
CountDownLatch flushLatch = sensorManager.requestFlush();
- listener.waitForFlushComplete(flushLatch);
+ listener.waitForFlushComplete(flushLatch, true);
} finally {
sensorManager.unregisterListener();
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java
index e727092..0d90957 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorNotSupportedException.java
@@ -26,6 +26,11 @@
super("Sensor '%s' of type %d is not supported.", getSensorName(sensorType), sensorType);
}
+ public SensorNotSupportedException(int sensorType, boolean wakeup) {
+ super("Sensor '%s' of type %d and %s is not supported.", getSensorName(sensorType),
+ sensorType, wakeup ? "wake-up" : "non wake-up");
+ }
+
private static String getSensorName(int sensorType) {
return String.format("%s (%d)", getSimpleSensorName(sensorType), sensorType);
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 8067aed..f0f0186 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -54,6 +54,7 @@
public static final String MEAN_KEY = "mean";
public static final String STANDARD_DEVIATION_KEY = "standard_deviation";
public static final String MAGNITUDE_KEY = "magnitude";
+ public static final String DELAYED_BATCH_DELIVERY = "delayed_batch_delivery";
private final Map<String, Object> mValues = new HashMap<>();
private final Map<String, SensorStats> mSensorStats = new HashMap<>();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SuspendStateMonitor.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SuspendStateMonitor.java
new file mode 100644
index 0000000..4426967
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SuspendStateMonitor.java
@@ -0,0 +1,57 @@
+package android.hardware.cts.helpers;
+
+import java.util.Timer;
+import java.util.TimerTask;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.Sensor;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.util.Log;
+
+import junit.framework.Assert;
+
+public class SuspendStateMonitor {
+ private final double firstRealTimeMillis;
+ private final double firstUpTimeMillis;
+ private double lastSleepTimeSeconds = 0;
+ private volatile long lastWakeUpTime = 0;
+ Timer sleepMonitoringTimer = new Timer();
+
+ /**
+ * Returns the time the device slept since the start of the application,
+ * in seconds.
+ */
+ public double getSleepTimeSeconds() {
+ double totalSinceStart = android.os.SystemClock.elapsedRealtime() - firstRealTimeMillis;
+ double upTimeSinceStart = android.os.SystemClock.uptimeMillis() - firstUpTimeMillis;
+ return (totalSinceStart - upTimeSinceStart) / 1000;
+ }
+
+ public long getLastWakeUpTime() {
+ return lastWakeUpTime;
+ }
+
+ public void cancel() {
+ sleepMonitoringTimer.cancel();
+ }
+
+ public SuspendStateMonitor() {
+ firstRealTimeMillis = android.os.SystemClock.elapsedRealtime();
+ firstUpTimeMillis = android.os.SystemClock.uptimeMillis();
+ // Every 100 miliseconds, check whether the device has slept.
+ TimerTask sleepMonitoringTask = new TimerTask() {
+ @Override
+ public void run() {
+ if (getSleepTimeSeconds() - lastSleepTimeSeconds > 0.1) {
+ lastSleepTimeSeconds = getSleepTimeSeconds();
+ lastWakeUpTime = SystemClock.elapsedRealtime();
+ }
+ }
+ };
+ sleepMonitoringTimer.schedule(sleepMonitoringTask, 0, 100);
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 143f0a1..6156d3d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -40,6 +40,7 @@
private final boolean mSensorMightHaveMoreListeners;
private final int mSamplingPeriodUs;
private final int mMaxReportLatencyUs;
+ private final boolean mIsDeviceSuspendTest;
/**
* Constructs an environment for sensor testing.
@@ -163,11 +164,27 @@
boolean sensorMightHaveMoreListeners,
int samplingPeriodUs,
int maxReportLatencyUs) {
+ this(context,
+ sensor,
+ sensorMightHaveMoreListeners,
+ samplingPeriodUs,
+ maxReportLatencyUs,
+ false /* isDeviceSuspendTest */);
+ }
+
+ public TestSensorEnvironment(
+ Context context,
+ Sensor sensor,
+ boolean sensorMightHaveMoreListeners,
+ int samplingPeriodUs,
+ int maxReportLatencyUs,
+ boolean isDeviceSuspendTest) {
mContext = context;
mSensor = sensor;
mSensorMightHaveMoreListeners = sensorMightHaveMoreListeners;
mSamplingPeriodUs = samplingPeriodUs;
mMaxReportLatencyUs = maxReportLatencyUs;
+ mIsDeviceSuspendTest = isDeviceSuspendTest;
}
/**
@@ -357,4 +374,9 @@
&& mSamplingPeriodUs != SensorManager.SENSOR_DELAY_UI
&& mSamplingPeriodUs != SensorManager.SENSOR_DELAY_NORMAL);
}
+
+ public boolean isDeviceSuspendTest() {
+ return mIsDeviceSuspendTest;
+ }
}
+
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 662c3ce..23effb9 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -18,12 +18,16 @@
import junit.framework.Assert;
+import android.content.Context;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener2;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
+import android.os.PowerManager;
+import android.os.PowerManager.WakeLock;
+import android.util.Log;
import java.io.BufferedWriter;
import java.io.File;
@@ -50,12 +54,14 @@
private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<>();
+ private final ArrayList<Long> mTimeStampFlushCompleteEvents = new ArrayList<>();
private final List<CountDownLatch> mEventLatches = new ArrayList<>();
private final List<CountDownLatch> mFlushLatches = new ArrayList<>();
private final AtomicInteger mEventsReceivedOutsideHandler = new AtomicInteger();
private final Handler mHandler;
private final TestSensorEnvironment mEnvironment;
+ private final PowerManager.WakeLock mTestSensorEventListenerWakeLock;
/**
* @deprecated Use {@link TestSensorEventListener(TestSensorEnvironment)}.
@@ -78,6 +84,10 @@
public TestSensorEventListener(TestSensorEnvironment environment, Handler handler) {
mEnvironment = environment;
mHandler = handler;
+ PowerManager pm = (PowerManager) environment.getContext().getSystemService(
+ Context.POWER_SERVICE);
+ mTestSensorEventListenerWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK,
+ "TestSensorEventListenerWakeLock");
}
/**
@@ -93,6 +103,9 @@
synchronized (mEventLatches) {
for (CountDownLatch latch : mEventLatches) {
latch.countDown();
+ if (latch.getCount() == 0 && !mTestSensorEventListenerWakeLock.isHeld()) {
+ mTestSensorEventListenerWakeLock.acquire();
+ }
}
}
}
@@ -136,6 +149,10 @@
@Override
public void onFlushCompleted(Sensor sensor) {
checkHandler();
+ long timestampNs = SystemClock.elapsedRealtimeNanos();
+ synchronized (mTimeStampFlushCompleteEvents) {
+ mTimeStampFlushCompleteEvents.add(timestampNs);
+ }
synchronized (mFlushLatches) {
for (CountDownLatch latch : mFlushLatches) {
latch.countDown();
@@ -175,7 +192,8 @@
* It will overwrite the file if it already exists, the file is created in a relative directory
* named 'events' under the sensor test directory (part of external storage).
*/
- public void logCollectedEventsToFile(String fileName) throws IOException {
+ public void logCollectedEventsToFile(String fileName, long deviceWakeUpTimeMs)
+ throws IOException {
StringBuilder builder = new StringBuilder();
builder.append("Sensor='").append(mEnvironment.getSensor()).append("', ");
builder.append("SamplingRateOverloaded=")
@@ -184,15 +202,54 @@
.append(mEnvironment.getRequestedSamplingPeriodUs()).append("us, ");
builder.append("MaxReportLatency=")
.append(mEnvironment.getMaxReportLatencyUs()).append("us");
-
synchronized (mCollectedEvents) {
- for (TestSensorEvent event : mCollectedEvents) {
+ int i = 0, j = 0;
+ while (i < mCollectedEvents.size() && j < mTimeStampFlushCompleteEvents.size()) {
+ if (mCollectedEvents.get(i).receivedTimestamp <
+ mTimeStampFlushCompleteEvents.get(j)) {
+ TestSensorEvent event = mCollectedEvents.get(i);
+ if (deviceWakeUpTimeMs != -1 && deviceWakeUpTimeMs <
+ event.receivedTimestamp/1000000) {
+ builder.append("\n");
+ builder.append("AP wake-up time=").append(deviceWakeUpTimeMs).append("ms");
+ deviceWakeUpTimeMs = -1;
+ }
+ builder.append("\n");
+ builder.append("Timestamp=").append(event.timestamp/1000000).append("ms, ");
+ builder.append("ReceivedTimestamp=").append(event.receivedTimestamp/1000000).
+ append("ms, ");
+ builder.append("Accuracy=").append(event.accuracy).append(", ");
+ builder.append("Values=").append(Arrays.toString(event.values));
+ ++i;
+ } else {
+ builder.append("\n");
+ builder.append("ReceivedTimestamp=")
+ .append(mTimeStampFlushCompleteEvents.get(j)/1000000)
+ .append(" Flush complete Event");
+ ++j;
+ }
+ }
+ for (;i < mCollectedEvents.size(); ++i) {
+ TestSensorEvent event = mCollectedEvents.get(i);
+ if (deviceWakeUpTimeMs != -1 && deviceWakeUpTimeMs <
+ event.receivedTimestamp/1000000) {
+ builder.append("\n");
+ builder.append("AP wake-up time=").append(deviceWakeUpTimeMs).append("ms");
+ deviceWakeUpTimeMs = -1;
+ }
builder.append("\n");
- builder.append("Timestamp=").append(event.timestamp).append("ns, ");
- builder.append("ReceivedTimestamp=").append(event.receivedTimestamp).append("ns, ");
+ builder.append("Timestamp=").append(event.timestamp/1000000).append("ms, ");
+ builder.append("ReceivedTimestamp=").append(event.receivedTimestamp/1000000).
+ append("ms, ");
builder.append("Accuracy=").append(event.accuracy).append(", ");
builder.append("Values=").append(Arrays.toString(event.values));
}
+ for (;j < mTimeStampFlushCompleteEvents.size(); ++j) {
+ builder.append("\n");
+ builder.append("ReceivedTimestamp=")
+ .append(mTimeStampFlushCompleteEvents.get(j)/1000000)
+ .append("ms Flush complete Event");
+ }
}
File eventsDirectory = SensorCtsHelper.getSensorTestDataDirectory("events/");
@@ -208,8 +265,11 @@
*
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
- public void waitForFlushComplete(CountDownLatch latch) throws InterruptedException {
- clearEvents();
+ public void waitForFlushComplete(CountDownLatch latch,
+ boolean clearCollectedEvents) throws InterruptedException {
+ if (clearCollectedEvents) {
+ clearEvents();
+ }
try {
String message = SensorCtsHelper.formatAssertionMessage(
"WaitForFlush",
@@ -229,8 +289,11 @@
*
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
- public void waitForEvents(CountDownLatch latch, int eventCount) throws InterruptedException {
- clearEvents();
+ public void waitForEvents(CountDownLatch latch, int eventCount,
+ boolean clearCollectedEvents) throws InterruptedException {
+ if (clearCollectedEvents) {
+ clearEvents();
+ }
try {
long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
// timeout is 2 * event count * expected period + batch timeout + default wait
@@ -279,6 +342,12 @@
Assert.assertEquals(message, 0 /* expected */, eventsOutsideHandler);
}
+ public void releaseWakeLock() {
+ if (mTestSensorEventListenerWakeLock.isHeld()) {
+ mTestSensorEventListenerWakeLock.release();
+ }
+ }
+
/**
* Keeps track of the number of events that arrived in a different {@link Looper} than the one
* associated with the {@link TestSensorEventListener}.
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 3b90b15..d6cc54e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -16,7 +16,11 @@
package android.hardware.cts.helpers.sensoroperations;
-import junit.framework.Assert;
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
import android.hardware.cts.helpers.SensorCtsHelper;
import android.hardware.cts.helpers.SensorStats;
@@ -25,6 +29,7 @@
import android.hardware.cts.helpers.TestSensorEvent;
import android.hardware.cts.helpers.TestSensorEventListener;
import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.SuspendStateMonitor;
import android.hardware.cts.helpers.reporting.ISensorTestNode;
import android.hardware.cts.helpers.sensorverification.EventGapVerification;
import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
@@ -36,13 +41,11 @@
import android.hardware.cts.helpers.sensorverification.MeanVerification;
import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
import android.os.Handler;
+import android.os.SystemClock;
+import android.os.PowerManager.WakeLock;
import android.util.Log;
-import java.io.IOException;
-import java.util.HashSet;
-import java.util.List;
-import java.util.concurrent.CountDownLatch;
-import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
/**
* A {@link SensorOperation} used to verify that sensor events and sensor values are correct.
@@ -61,6 +64,7 @@
private final TestSensorEnvironment mEnvironment;
private final Executor mExecutor;
private final Handler mHandler;
+ private long mDeviceWakeUpTimeMs = -1;
/**
* An interface that defines an abstraction for operations to be performed by the
@@ -118,7 +122,22 @@
public void execute(ISensorTestNode parent) throws InterruptedException {
getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
TestSensorEventListener listener = new TestSensorEventListener(mEnvironment, mHandler);
- mExecutor.execute(mSensorManager, listener);
+
+ if (mEnvironment.isDeviceSuspendTest()) {
+ SuspendStateMonitor suspendStateMonitor = new SuspendStateMonitor();
+ long startTimeMs = SystemClock.elapsedRealtime();
+ // Device should go into suspend here.
+ mExecutor.execute(mSensorManager, listener);
+ long endTimeMs = SystemClock.elapsedRealtime();
+ // Check if the device has gone into suspend during test execution.
+ mDeviceWakeUpTimeMs = suspendStateMonitor.getLastWakeUpTime();
+ suspendStateMonitor.cancel();
+ Assert.assertTrue("Device did not go into suspend during test execution",
+ startTimeMs < mDeviceWakeUpTimeMs &&
+ mDeviceWakeUpTimeMs < endTimeMs);
+ } else {
+ mExecutor.execute(mSensorManager, listener);
+ }
boolean failed = false;
StringBuilder sb = new StringBuilder();
@@ -193,7 +212,7 @@
}
try {
- listener.logCollectedEventsToFile(sanitizedFileName);
+ listener.logCollectedEventsToFile(sanitizedFileName, mDeviceWakeUpTimeMs);
} catch (IOException e) {
Log.w(TAG, "Unable to save collected events to file: " + sanitizedFileName, e);
}
@@ -214,7 +233,7 @@
throws InterruptedException {
try {
CountDownLatch latch = sensorManager.registerListener(listener, eventCount);
- listener.waitForEvents(latch, eventCount);
+ listener.waitForEvents(latch, eventCount, true);
} finally {
sensorManager.unregisterListener();
}
@@ -224,6 +243,62 @@
}
/**
+ * Creates an operation that will wait for a given amount of events to arrive.
+ *
+ * @param environment The test environment.
+ * @param eventCount The number of events to wait for.
+ */
+ public static TestSensorOperation createOperation(
+ final TestSensorEnvironment environment,
+ final int eventCount,
+ final WakeLock wakeLock,
+ final boolean flushRequested) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ int eventCountForLatch = eventCount;
+ if (flushRequested) {
+ eventCountForLatch = eventCount + (int)environment.getFrequencyHz();
+ }
+ CountDownLatch latch = sensorManager.registerListener(listener,
+ eventCountForLatch);
+ if (flushRequested) {
+ SensorCtsHelper.sleep(1, TimeUnit.SECONDS);
+ CountDownLatch flushLatch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(flushLatch, false);
+ }
+ if (wakeLock.isHeld()) {
+ wakeLock.release();
+ }
+ listener.releaseWakeLock();
+ Log.v("TestSensorOperation", "waitForEvents " +
+ environment.getSensor().getName()
+ + " " + latch.getCount());
+ listener.waitForEvents(latch, eventCount, false);
+ Log.v("TestSensorOperation", "waitForEvents DONE " + environment.getSensor().getName());
+ if (!wakeLock.isHeld()) {
+ wakeLock.acquire();
+ }
+ if (flushRequested) {
+ SensorCtsHelper.sleep(1, TimeUnit.SECONDS);
+ CountDownLatch flushLatch = sensorManager.requestFlush();
+ listener.waitForFlushComplete(flushLatch, false);
+ }
+ } finally {
+ if(!wakeLock.isHeld()) {
+ wakeLock.acquire();
+ }
+ listener.releaseWakeLock();
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
+ }
+
+ /**
* Creates an operation that will wait for a given amount of time to collect events.
*
* @param environment The test environment.
@@ -269,7 +344,7 @@
sensorManager.registerListener(listener);
SensorCtsHelper.sleep(duration, timeUnit);
CountDownLatch latch = sensorManager.requestFlush();
- listener.waitForFlushComplete(latch);
+ listener.waitForFlushComplete(latch, true);
} finally {
sensorManager.unregisterListener();
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/BatchArrivalVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/BatchArrivalVerification.java
new file mode 100644
index 0000000..5e50c3a
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/BatchArrivalVerification.java
@@ -0,0 +1,139 @@
+
+package android.hardware.cts.helpers.sensorverification;
+
+import java.util.LinkedList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+import android.hardware.Sensor;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.hardware.cts.helpers.sensorverification.AbstractSensorVerification.IndexedEventPair;
+import android.os.SystemClock;
+import android.provider.Settings.System;
+
+import junit.framework.Assert;
+
+/**
+ * A {@link ISensorVerification} which verifies that there are no missing events. This is done by
+ * checking the last received sensor timestamp and checking that it is within 1.8 * the expected
+ * period.
+ */
+public class BatchArrivalVerification extends AbstractSensorVerification {
+ public static final String PASSED_KEY = "missing_event_passed";
+
+ // Batch arrival tolerance is 5 seconds.
+ private static final int BATCH_ARRIVAL_TOLERANCE_US = 5000000;
+
+ // Number of indices to print in assert message before truncating
+ private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+ // Number of events to truncate (discard) from the initial events received
+ private static final int TRUNCATE_EVENTS_COUNT = 100;
+
+ private final long mExpectedBatchArrivalTimeUs;
+
+ private final List<IndexedEventPair> mFailures = new LinkedList<IndexedEventPair>();
+ private TestSensorEvent mFirstEvent = null;
+ private int mIndex = 0;
+
+ /**
+ * Construct a {@link EventGapVerification}
+ *
+ * @param expectedDelayUs the expected period in us.
+ */
+ public BatchArrivalVerification(long expectedBatchArrivalTimeUs) {
+ mExpectedBatchArrivalTimeUs = expectedBatchArrivalTimeUs;
+ }
+
+ /**
+ * Get the default {@link EventGapVerification}.
+ *
+ * @param environment the test environment
+ * @return the verification or null if the verification is not a continuous mode sensor.
+ */
+ public static BatchArrivalVerification getDefault(TestSensorEnvironment environment) {
+ if (environment.getSensor().getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+ return null;
+ }
+ long fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+ int maximumExpectedSamplingPeriodUs = environment.getMaximumExpectedSamplingPeriodUs();
+ long reportLatencyUs = environment.getMaxReportLatencyUs();
+ if (fifoMaxEventCount > 0 && maximumExpectedSamplingPeriodUs != Integer.MAX_VALUE) {
+ long fifoBasedReportLatencyUs =
+ fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
+ // If the device goes into suspend mode during the test and the sensor under test is
+ // a non wake-up sensor, the FIFO will keep overwriting itself and the reportLatency
+ // of each event will be equal to the time it takes to fill up the FIFO.
+ if (environment.isDeviceSuspendTest() && !environment.getSensor().isWakeUpSensor()) {
+ reportLatencyUs = fifoBasedReportLatencyUs;
+ } else {
+ // In this case the sensor under test is either a wake-up sensor OR it
+ // is a non wake-up sensor but the device does not go into suspend.
+ // So the expected delay of a sensor_event is the minimum of the
+ // fifoBasedReportLatencyUs and the requested latency by the application.
+ reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+ }
+ }
+ long expectedBatchArrivalTimeUs = reportLatencyUs + SystemClock.elapsedRealtime() * 1000;
+ return new BatchArrivalVerification(expectedBatchArrivalTimeUs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public void verify(TestSensorEnvironment environment, SensorStats stats) {
+ final int count = mFailures.size();
+ stats.addValue(PASSED_KEY, count == 0);
+ stats.addValue(SensorStats.DELAYED_BATCH_DELIVERY, count);
+
+ if (count > 0) {
+ StringBuilder sb = new StringBuilder();
+ sb.append(count).append(" batch delayed: ");
+ for (int i = 0; i < Math.min(count, TRUNCATE_MESSAGE_LENGTH); i++) {
+ IndexedEventPair info = mFailures.get(i);
+ sb.append(String.format("expectedBatchArrival=%dms actualBatchArrivalTime=%dms "+
+ "diff=%dms tolerance=%dms",
+ (mExpectedBatchArrivalTimeUs)/1000,
+ info.event.receivedTimestamp/(1000 * 1000),
+ (mExpectedBatchArrivalTimeUs -
+ info.event.receivedTimestamp/1000)/1000,
+ BATCH_ARRIVAL_TOLERANCE_US/1000)
+
+ );
+
+ }
+ if (count > TRUNCATE_MESSAGE_LENGTH) {
+ sb.append(count - TRUNCATE_MESSAGE_LENGTH).append(" more; ");
+ }
+ Assert.fail(sb.toString());
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public BatchArrivalVerification clone() {
+ return new BatchArrivalVerification(mExpectedBatchArrivalTimeUs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void addSensorEventInternal(TestSensorEvent event) {
+ if (mFirstEvent == null) {
+ mFirstEvent = event;
+ }
+ if (mIndex == 1) {
+ if (Math.abs(mFirstEvent.receivedTimestamp/1000 - mExpectedBatchArrivalTimeUs) >
+ BATCH_ARRIVAL_TOLERANCE_US) {
+ mFailures.add(new IndexedEventPair(1, mFirstEvent, null));
+ }
+ }
+ ++mIndex;
+ }
+}
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 a90725f..3af3f03 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
@@ -75,6 +75,16 @@
fifoMaxEventCount * maximumExpectedSamplingPeriodUs;
reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs) +
(long)(2.5 * maximumExpectedSamplingPeriodUs);
+ // of each event will be equal to the time it takes to fill up the FIFO.
+ if (environment.isDeviceSuspendTest() && !environment.getSensor().isWakeUpSensor()) {
+ reportLatencyUs = fifoBasedReportLatencyUs;
+ } else {
+ // In this case the sensor under test is either a wake-up sensor OR it
+ // is a non wake-up sensor but the device does not go into suspend.
+ // So the expected delay of a sensor_event is the minimum of the
+ // fifoBasedReportLatencyUs and the requested latency by the application.
+ reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+ }
}
long expectedSyncLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS,
diff --git a/tests/tests/keystore/res/raw/ec_key2_cert.der b/tests/tests/keystore/res/raw/ec_key2_cert.der
new file mode 100644
index 0000000..576d8f9
--- /dev/null
+++ b/tests/tests/keystore/res/raw/ec_key2_cert.der
Binary files differ
diff --git a/tests/tests/keystore/res/raw/ec_key2_pkcs8.der b/tests/tests/keystore/res/raw/ec_key2_pkcs8.der
new file mode 100644
index 0000000..26f4f05
--- /dev/null
+++ b/tests/tests/keystore/res/raw/ec_key2_pkcs8.der
Binary files differ
diff --git a/tests/tests/keystore/res/raw/rsa_key2_cert.der b/tests/tests/keystore/res/raw/rsa_key2_cert.der
new file mode 100644
index 0000000..b00495c
--- /dev/null
+++ b/tests/tests/keystore/res/raw/rsa_key2_cert.der
Binary files differ
diff --git a/tests/tests/keystore/res/raw/rsa_key2_pkcs8.der b/tests/tests/keystore/res/raw/rsa_key2_pkcs8.der
new file mode 100644
index 0000000..c687f7d
--- /dev/null
+++ b/tests/tests/keystore/res/raw/rsa_key2_pkcs8.der
Binary files differ
diff --git a/tests/tests/keystore/src/android/keystore/cts/AES128CBCNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128CBCNoPaddingCipherTest.java
new file mode 100644
index 0000000..e56049c
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128CBCNoPaddingCipherTest.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 AES128CBCNoPaddingCipherTest extends AESCBCNoPaddingCipherTestBase {
+
+ private static final byte[] KAT_KEY = HexEncoding.decode("7E3D723C09A9852B24F584F9D916F6A8");
+ private static final byte[] KAT_IV = HexEncoding.decode("944AE274D983892EADE422274858A96A");
+ private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+ "044E15899A080AADEB6778F64323B64D2CBCBADB338DF93B9AC459D4F41029809FFF37081C22EF278F896A"
+ + "B213A2A631");
+ private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+ "B419293FCBD686F2913D1CF947E510D42FAFEDE5593C98AFD6AEE272596A56FE42C22F2A5E3B6A02BA9D8D"
+ + "0DE1E9A810");
+
+ @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/AES128CBCPKCS7PaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128CBCPKCS7PaddingCipherTest.java
new file mode 100644
index 0000000..d8254c1
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128CBCPKCS7PaddingCipherTest.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 AES128CBCPKCS7PaddingCipherTest extends AESCBCPKCS7PaddingCipherTestBase {
+
+ private static final byte[] KAT_KEY = HexEncoding.decode("F16E698472578E919D92806262C5169F");
+ private static final byte[] KAT_IV = HexEncoding.decode("EF743540F8421ACA128A3247521F3E7D");
+ private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+ "5BEBF33569D90BF5E853814E12E7C7AA5758013F755773E29F4A25EC26EEB765F7F2DC251F7DC62AEFCA1E"
+ + "8A5A11A1DCD44F0BD8FB593A5AE3");
+ private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+ "3197CF6DB9466188B5FED375329324EE7D6092A8C0E41DFAF49E3724271427896D56A6243C0D59D6639722"
+ + "AF93CD53449BDDABF9C5F153EBDBFED9ED98C8CC37");
+
+ @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/AES128CTRNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128CTRNoPaddingCipherTest.java
new file mode 100644
index 0000000..7ffca13
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128CTRNoPaddingCipherTest.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 AES128CTRNoPaddingCipherTest extends AESCTRNoPaddingCipherTestBase {
+
+ private static final byte[] KAT_KEY = HexEncoding.decode("4713a7b2f93efe809b42ecc45213ef9f");
+ private static final byte[] KAT_IV = HexEncoding.decode("ebfa19b0ebf3d57feabd4c4bd04bea01");
+ private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+ "6d2c07e1fc86f99c6e2a8f6567828b4262a9c23d0f3ed8ab32482283c79796f0adba1bcd3736084996452a"
+ +"917fae98005aebe61f9e91c3");
+ private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+ "345deb1d67b95e600e05cad4c32ec381aadb3e2c1ec7e0fb956dc38e6860cf0553535566e1b12fa9f87d29"
+ + "266ca26df427233df035df28");
+
+ @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/AES128ECBNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128ECBNoPaddingCipherTest.java
new file mode 100644
index 0000000..100700c
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128ECBNoPaddingCipherTest.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 AES128ECBNoPaddingCipherTest extends AESECBNoPaddingCipherTestBase {
+
+ private static final byte[] KAT_KEY = HexEncoding.decode("7DA2467F068854B3CB36E5C333A16619");
+ private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+ "9A07C9575AD9CE209DF9F3953965CEBE8208587C7AE575A1904BF25048946D7B6168A9A27BCE554BEA94EF"
+ + "26E6C742A0");
+ private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+ "8C47E49420FC92AC4CA2C601BC3F8AC31D01B260B7B849F2B8EEDFFFED8F36C31CBDA0D22F95C9C2A48C34"
+ + "7E8C77AC82");
+
+ @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/AES128ECBPKCS7PaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128ECBPKCS7PaddingCipherTest.java
new file mode 100644
index 0000000..c834ddf
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128ECBPKCS7PaddingCipherTest.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 AES128ECBPKCS7PaddingCipherTest extends AESECBPKCS7PaddingCipherTestBase {
+
+ private static final byte[] KAT_KEY = HexEncoding.decode("C3BE04BCCB3D99B85290F113FE7AF194");
+ private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+ "348C213FD8DF3F990C20C5ACBF07B34B6264AE245784A5A6176DBFB1C2E7DD27E52CC92B8EEE40614F05B5"
+ + "07B355F6354A2705BD86");
+ private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+ "07CD05C41FEDEDDC5DB4B3E35E676153184A119AA4DFDDC290616F1FA600931DE6BEA9BDB90D1D73389994"
+ + "6F8C8E5C0C4383F99F5D88E27F3EBC0C6E52759ED3");
+
+ @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/AES128GCMNoPaddingCipherTest.java b/tests/tests/keystore/src/android/keystore/cts/AES128GCMNoPaddingCipherTest.java
new file mode 100644
index 0000000..6ae13ce
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AES128GCMNoPaddingCipherTest.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 AES128GCMNoPaddingCipherTest extends AESGCMNoPaddingCipherTestBase {
+
+ private static final byte[] KAT_KEY = HexEncoding.decode("ba76354f0aed6e8d91f45c4ff5a062db");
+ private static final byte[] KAT_IV = HexEncoding.decode("b79437ae08ff355d7d8a4d0f");
+ private static final byte[] KAT_PLAINTEXT = HexEncoding.decode(
+ "6d7596a8fd56ceaec61de7940984b7736fec44f572afc3c8952e4dc6541e2bc6a702c440a37610989543f6"
+ + "3fedb047ca2173bc18581944");
+ private static final byte[] KAT_CIPHERTEXT = HexEncoding.decode(
+ "b3f6799e8f9326f2df1e80fcd2cb16d78c9dc7cc14bb677862dc6c639b3a6338d24b312d3989e5920b5dbf"
+ + "c976765efbfe57bb385940a7a43bdf05bddae3c9d6a2fbbdfcc0cba0");
+
+ @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/AESCBCCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESCBCCipherTestBase.java
new file mode 100644
index 0000000..8f1aed69
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESCBCCipherTestBase.java
@@ -0,0 +1,57 @@
+/*
+ * 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 java.security.AlgorithmParameters;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.spec.IvParameterSpec;
+
+abstract class AESCBCCipherTestBase extends BlockCipherTestBase {
+
+ @Override
+ protected boolean isStreamCipher() {
+ return false;
+ }
+
+ @Override
+ protected boolean isAuthenticatedCipher() {
+ return false;
+ }
+
+ @Override
+ protected int getKatAuthenticationTagLengthBytes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected int getBlockSize() {
+ return 16;
+ }
+
+ @Override
+ protected AlgorithmParameterSpec getKatAlgorithmParameterSpec() {
+ return new IvParameterSpec(getKatIv());
+ }
+
+ @Override
+ protected byte[] getIv(AlgorithmParameters params) throws InvalidParameterSpecException {
+ IvParameterSpec spec = params.getParameterSpec(IvParameterSpec.class);
+ return spec.getIV();
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESCBCNoPaddingCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESCBCNoPaddingCipherTestBase.java
new file mode 100644
index 0000000..8e51d04
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESCBCNoPaddingCipherTestBase.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+abstract class AESCBCNoPaddingCipherTestBase extends AESCBCCipherTestBase {
+
+ @Override
+ protected String getTransformation() {
+ return "AES/CBC/NoPadding";
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESCBCPKS7PaddingCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESCBCPKS7PaddingCipherTestBase.java
new file mode 100644
index 0000000..bd2c5bd
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESCBCPKS7PaddingCipherTestBase.java
@@ -0,0 +1,25 @@
+/*
+ * 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;
+
+abstract class AESCBCPKCS7PaddingCipherTestBase extends AESCBCCipherTestBase {
+
+ @Override
+ protected String getTransformation() {
+ return "AES/CBC/PKCS7Padding";
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESCTRCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESCTRCipherTestBase.java
new file mode 100644
index 0000000..8bf6ac1
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESCTRCipherTestBase.java
@@ -0,0 +1,57 @@
+/*
+ * 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 java.security.AlgorithmParameters;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.spec.IvParameterSpec;
+
+abstract class AESCTRCipherTestBase extends BlockCipherTestBase {
+
+ @Override
+ protected boolean isStreamCipher() {
+ return true;
+ }
+
+ @Override
+ protected boolean isAuthenticatedCipher() {
+ return false;
+ }
+
+ @Override
+ protected int getKatAuthenticationTagLengthBytes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected int getBlockSize() {
+ return 16;
+ }
+
+ @Override
+ protected AlgorithmParameterSpec getKatAlgorithmParameterSpec() {
+ return new IvParameterSpec(getKatIv());
+ }
+
+ @Override
+ protected byte[] getIv(AlgorithmParameters params) throws InvalidParameterSpecException {
+ IvParameterSpec spec = params.getParameterSpec(IvParameterSpec.class);
+ return spec.getIV();
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESCTRNoPaddingCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESCTRNoPaddingCipherTestBase.java
new file mode 100644
index 0000000..e504310
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESCTRNoPaddingCipherTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+abstract class AESCTRNoPaddingCipherTestBase extends AESCTRCipherTestBase {
+ @Override
+ protected String getTransformation() {
+ return "AES/CTR/NoPadding";
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java
new file mode 100644
index 0000000..5ecf22f
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESECBCipherTestBase.java
@@ -0,0 +1,57 @@
+/*
+ * 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 java.security.AlgorithmParameters;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+abstract class AESECBCipherTestBase extends BlockCipherTestBase {
+
+ @Override
+ protected boolean isStreamCipher() {
+ return false;
+ }
+
+ @Override
+ protected boolean isAuthenticatedCipher() {
+ return false;
+ }
+
+ @Override
+ protected int getKatAuthenticationTagLengthBytes() {
+ throw new UnsupportedOperationException();
+ }
+
+ @Override
+ protected int getBlockSize() {
+ return 16;
+ }
+
+ @Override
+ protected AlgorithmParameterSpec getKatAlgorithmParameterSpec() {
+ return null;
+ }
+
+ @Override
+ protected byte[] getIv(AlgorithmParameters params) throws InvalidParameterSpecException {
+ if (params != null) {
+ fail("ECB does not use IV");
+ }
+ return null;
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESECBNoPaddingCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESECBNoPaddingCipherTestBase.java
new file mode 100644
index 0000000..8c38015
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESECBNoPaddingCipherTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+abstract class AESECBNoPaddingCipherTestBase extends AESECBCipherTestBase {
+ @Override
+ protected String getTransformation() {
+ return "AES/ECB/NoPadding";
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESECBPKCS7PaddingCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESECBPKCS7PaddingCipherTestBase.java
new file mode 100644
index 0000000..67e659c
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESECBPKCS7PaddingCipherTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+abstract class AESECBPKCS7PaddingCipherTestBase extends AESECBCipherTestBase {
+ @Override
+ protected String getTransformation() {
+ return "AES/ECB/PKCS7Padding";
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java
new file mode 100644
index 0000000..d901674
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESGCMCipherTestBase.java
@@ -0,0 +1,57 @@
+/*
+ * 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 java.security.AlgorithmParameters;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+
+import javax.crypto.spec.GCMParameterSpec;
+
+abstract class AESGCMCipherTestBase extends BlockCipherTestBase {
+
+ @Override
+ protected boolean isStreamCipher() {
+ return true;
+ }
+
+ @Override
+ protected boolean isAuthenticatedCipher() {
+ return true;
+ }
+
+ @Override
+ protected int getKatAuthenticationTagLengthBytes() {
+ return getKatCiphertext().length - getKatPlaintext().length;
+ }
+
+ @Override
+ protected int getBlockSize() {
+ return 16;
+ }
+
+ @Override
+ protected AlgorithmParameterSpec getKatAlgorithmParameterSpec() {
+ return new GCMParameterSpec(getKatAuthenticationTagLengthBytes() * 8, getKatIv());
+ }
+
+ @Override
+ protected byte[] getIv(AlgorithmParameters params) throws InvalidParameterSpecException {
+ GCMParameterSpec spec = params.getParameterSpec(GCMParameterSpec.class);
+ return spec.getIV();
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AESGCMNoPaddingCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/AESGCMNoPaddingCipherTestBase.java
new file mode 100644
index 0000000..c29e644
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/AESGCMNoPaddingCipherTestBase.java
@@ -0,0 +1,24 @@
+/*
+ * 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;
+
+abstract class AESGCMNoPaddingCipherTestBase extends AESGCMCipherTestBase {
+ @Override
+ protected String getTransformation() {
+ return "AES/GCM/NoPadding";
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
index 11769ad..6b81285 100644
--- a/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/AndroidKeyStoreTest.java
@@ -18,12 +18,19 @@
import android.security.KeyPairGeneratorSpec;
import android.security.KeyStoreParameter;
+import android.security.keystore.KeyProperties;
+import android.security.keystore.KeyProtection;
import android.test.AndroidTestCase;
+import android.test.MoreAsserts;
+import android.test.suitebuilder.annotation.LargeTest;
+
+import com.android.cts.keystore.R;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.OutputStream;
import java.math.BigInteger;
+import java.security.AlgorithmParameters;
import java.security.Key;
import java.security.KeyFactory;
import java.security.KeyPairGenerator;
@@ -52,7 +59,9 @@
import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
+import javax.crypto.Mac;
import javax.crypto.SecretKey;
+import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.security.auth.x500.X500Principal;
@@ -706,6 +715,8 @@
@Override
protected void setUp() throws Exception {
+ super.setUp();
+
// Wipe any existing entries in the KeyStore
KeyStore ksTemp = KeyStore.getInstance("AndroidKeyStore");
ksTemp.load(null, null);
@@ -719,6 +730,21 @@
mKeyStore = KeyStore.getInstance("AndroidKeyStore");
}
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null, null);
+ Enumeration<String> aliases = keyStore.aliases();
+ while (aliases.hasMoreElements()) {
+ String alias = aliases.nextElement();
+ keyStore.deleteEntry(alias);
+ }
+ } finally {
+ super.tearDown();
+ }
+ }
+
private PrivateKey generatePrivateKey(String keyType, byte[] fakeKey1) throws Exception {
KeyFactory kf = KeyFactory.getInstance(keyType);
return kf.generatePrivate(new PKCS8EncodedKeySpec(fakeKey1));
@@ -1980,4 +2006,283 @@
Signature.getInstance("SHA256withECDSA").initVerify(publicKey);
Signature.getInstance("NONEwithECDSA").initVerify(publicKey);
}
+
+ private static final int MIN_SUPPORTED_KEY_COUNT = 10000;
+
+ @LargeTest
+ public void testKeyStore_LargeNumberOfKeysSupported_RSA() throws Exception {
+ // This test imports key1, then lots of other keys, then key2, and then confirms that
+ // key1 and key2 backed by Android Keystore work fine. The assumption is that if the
+ // underlying implementation has a limit on the number of keys, it'll either delete the
+ // oldest key (key1), or will refuse to add keys (key2).
+
+ Certificate cert1 = TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key1_cert);
+ PrivateKey privateKey1 = TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key1_pkcs8);
+ String entryName1 = "test0";
+
+ Certificate cert2 = TestUtils.getRawResX509Certificate(getContext(), R.raw.rsa_key2_cert);
+ PrivateKey privateKey2 = TestUtils.getRawResPrivateKey(getContext(), R.raw.rsa_key2_pkcs8);
+ String entryName2 = "test" + MIN_SUPPORTED_KEY_COUNT;
+
+ Certificate cert3 = generateCertificate(FAKE_RSA_USER_1);
+ PrivateKey privateKey3 = generatePrivateKey("RSA", FAKE_RSA_KEY_1);
+
+ mKeyStore.load(null);
+ try {
+ KeyProtection protectionParams = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .setSignaturePaddings(KeyProperties.SIGNATURE_PADDING_RSA_PKCS1)
+ .build();
+ mKeyStore.setEntry(entryName1,
+ new KeyStore.PrivateKeyEntry(privateKey1, new Certificate[] {cert1}),
+ protectionParams);
+
+ // Import key3 many of times, under different aliases.
+ for (int i = 1; i < MIN_SUPPORTED_KEY_COUNT; i++) {
+ String entryAlias = "test" + i;
+ try {
+ mKeyStore.setEntry(entryAlias,
+ new KeyStore.PrivateKeyEntry(privateKey3, new Certificate[] {cert3}),
+ protectionParams);
+ } catch (Throwable e) {
+ throw new RuntimeException("Entry " + entryAlias + " import failed", e);
+ }
+ }
+
+ mKeyStore.setEntry(entryName2,
+ new KeyStore.PrivateKeyEntry(privateKey2, new Certificate[] {cert2}),
+ protectionParams);
+ PrivateKey keystorePrivateKey2 = (PrivateKey) mKeyStore.getKey(entryName2, null);
+ PrivateKey keystorePrivateKey1 = (PrivateKey) mKeyStore.getKey(entryName1, null);
+
+ byte[] message = "This is a test".getBytes("UTF-8");
+
+ Signature sig = Signature.getInstance("SHA256withRSA");
+ sig.initSign(keystorePrivateKey1);
+ sig.update(message);
+ byte[] signature = sig.sign();
+ sig = Signature.getInstance(sig.getAlgorithm());
+ sig.initVerify(cert1.getPublicKey());
+ sig.update(message);
+ assertTrue(sig.verify(signature));
+
+ sig = Signature.getInstance(sig.getAlgorithm());
+ sig.initSign(keystorePrivateKey2);
+ sig.update(message);
+ signature = sig.sign();
+ sig = Signature.getInstance(sig.getAlgorithm());
+ sig.initVerify(cert2.getPublicKey());
+ sig.update(message);
+ assertTrue(sig.verify(signature));
+ } finally {
+ // Clean up Keystore without using KeyStore.aliases() which can't handle this many
+ // entries.
+ for (int i = 0; i <= MIN_SUPPORTED_KEY_COUNT; i++) {
+ mKeyStore.deleteEntry("test" + i);
+ }
+ }
+ }
+
+ @LargeTest
+ public void testKeyStore_LargeNumberOfKeysSupported_EC() throws Exception {
+ // This test imports key1, then lots of other keys, then key2, and then confirms that
+ // key1 and key2 backed by Android Keystore work fine. The assumption is that if the
+ // underlying implementation has a limit on the number of keys, it'll either delete the
+ // oldest key (key1), or will refuse to add keys (key2).
+
+ Certificate cert1 = TestUtils.getRawResX509Certificate(getContext(), R.raw.ec_key1_cert);
+ PrivateKey privateKey1 = TestUtils.getRawResPrivateKey(getContext(), R.raw.ec_key1_pkcs8);
+ String entryName1 = "test0";
+
+ Certificate cert2 = TestUtils.getRawResX509Certificate(getContext(), R.raw.ec_key2_cert);
+ PrivateKey privateKey2 = TestUtils.getRawResPrivateKey(getContext(), R.raw.ec_key2_pkcs8);
+ String entryName2 = "test" + MIN_SUPPORTED_KEY_COUNT;
+
+ Certificate cert3 = generateCertificate(FAKE_EC_USER_1);
+ PrivateKey privateKey3 = generatePrivateKey("EC", FAKE_EC_KEY_1);
+
+ mKeyStore.load(null);
+ try {
+ KeyProtection protectionParams = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .setDigests(KeyProperties.DIGEST_SHA256)
+ .build();
+ mKeyStore.setEntry(entryName1,
+ new KeyStore.PrivateKeyEntry(privateKey1, new Certificate[] {cert1}),
+ protectionParams);
+
+ // Import key3 many of times, under different aliases.
+ for (int i = 1; i < MIN_SUPPORTED_KEY_COUNT; i++) {
+ String entryAlias = "test" + i;
+ try {
+ mKeyStore.setEntry(entryAlias,
+ new KeyStore.PrivateKeyEntry(privateKey3, new Certificate[] {cert3}),
+ protectionParams);
+ } catch (Throwable e) {
+ throw new RuntimeException("Entry " + entryAlias + " import failed", e);
+ }
+ }
+
+ mKeyStore.setEntry(entryName2,
+ new KeyStore.PrivateKeyEntry(privateKey2, new Certificate[] {cert2}),
+ protectionParams);
+ PrivateKey keystorePrivateKey2 = (PrivateKey) mKeyStore.getKey(entryName2, null);
+ PrivateKey keystorePrivateKey1 = (PrivateKey) mKeyStore.getKey(entryName1, null);
+
+ byte[] message = "This is a test".getBytes("UTF-8");
+
+ Signature sig = Signature.getInstance("SHA256withECDSA");
+ sig.initSign(keystorePrivateKey1);
+ sig.update(message);
+ byte[] signature = sig.sign();
+ sig = Signature.getInstance(sig.getAlgorithm());
+ sig.initVerify(cert1.getPublicKey());
+ sig.update(message);
+ assertTrue(sig.verify(signature));
+
+ sig = Signature.getInstance(sig.getAlgorithm());
+ sig.initSign(keystorePrivateKey2);
+ sig.update(message);
+ signature = sig.sign();
+ sig = Signature.getInstance(sig.getAlgorithm());
+ sig.initVerify(cert2.getPublicKey());
+ sig.update(message);
+ assertTrue(sig.verify(signature));
+ } finally {
+ // Clean up Keystore without using KeyStore.aliases() which can't handle this many
+ // entries.
+ for (int i = 0; i <= MIN_SUPPORTED_KEY_COUNT; i++) {
+ mKeyStore.deleteEntry("test" + i);
+ }
+ }
+ }
+
+ @LargeTest
+ public void testKeyStore_LargeNumberOfKeysSupported_AES() throws Exception {
+ // This test imports key1, then lots of other keys, then key2, and then confirms that
+ // key1 and key2 backed by Android Keystore work fine. The assumption is that if the
+ // underlying implementation has a limit on the number of keys, it'll either delete the
+ // oldest key (key1), or will refuse to add keys (key2).
+
+ SecretKey key1 =
+ new SecretKeySpec(HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "AES");
+ String entryName1 = "test0";
+
+ SecretKey key2 =
+ new SecretKeySpec(HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "AES");
+ String entryName2 = "test" + MIN_SUPPORTED_KEY_COUNT;
+
+ SecretKey key3 =
+ new SecretKeySpec(HexEncoding.decode("33333333333333333333777777777777"), "AES");
+
+ mKeyStore.load(null);
+ try {
+ KeyProtection protectionParams = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT)
+ .setBlockModes(KeyProperties.BLOCK_MODE_GCM)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build();
+ mKeyStore.setEntry(entryName1, new KeyStore.SecretKeyEntry(key1), protectionParams);
+
+ // Import key3 many of times, under different aliases.
+ for (int i = 1; i < MIN_SUPPORTED_KEY_COUNT; i++) {
+ String entryAlias = "test" + i;
+ try {
+ mKeyStore.setEntry(entryAlias, new KeyStore.SecretKeyEntry(key3), protectionParams);
+ } catch (Throwable e) {
+ throw new RuntimeException("Entry " + entryAlias + " import failed", e);
+ }
+ }
+
+ mKeyStore.setEntry(entryName2, new KeyStore.SecretKeyEntry(key2), protectionParams);
+ SecretKey keystoreKey2 = (SecretKey) mKeyStore.getKey(entryName2, null);
+ SecretKey keystoreKey1 = (SecretKey) mKeyStore.getKey(entryName1, null);
+
+ byte[] plaintext = "This is a test".getBytes("UTF-8");
+ Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
+ cipher.init(Cipher.ENCRYPT_MODE, keystoreKey1);
+ byte[] ciphertext = cipher.doFinal(plaintext);
+ AlgorithmParameters cipherParams = cipher.getParameters();
+ cipher = Cipher.getInstance(cipher.getAlgorithm());
+ cipher.init(Cipher.DECRYPT_MODE, key1, cipherParams);
+ MoreAsserts.assertEquals(plaintext, cipher.doFinal(ciphertext));
+
+ cipher = Cipher.getInstance(cipher.getAlgorithm());
+ cipher.init(Cipher.ENCRYPT_MODE, keystoreKey2);
+ ciphertext = cipher.doFinal(plaintext);
+ cipherParams = cipher.getParameters();
+ cipher = Cipher.getInstance(cipher.getAlgorithm());
+ cipher.init(Cipher.DECRYPT_MODE, key2, cipherParams);
+ MoreAsserts.assertEquals(plaintext, cipher.doFinal(ciphertext));
+ } finally {
+ // Clean up Keystore without using KeyStore.aliases() which can't handle this many
+ // entries.
+ for (int i = 0; i <= MIN_SUPPORTED_KEY_COUNT; i++) {
+ mKeyStore.deleteEntry("test" + i);
+ }
+ }
+ }
+
+ @LargeTest
+ public void testKeyStore_LargeNumberOfKeysSupported_HMAC() throws Exception {
+ // This test imports key1, then lots of other keys, then key2, and then confirms that
+ // key1 and key2 backed by Android Keystore work fine. The assumption is that if the
+ // underlying implementation has a limit on the number of keys, it'll either delete the
+ // oldest key (key1), or will refuse to add keys (key2).
+
+ SecretKey key1 = new SecretKeySpec(
+ HexEncoding.decode("010203040506070809fafbfcfdfeffcc"), "HmacSHA256");
+ String entryName1 = "test0";
+
+ SecretKey key2 = new SecretKeySpec(
+ HexEncoding.decode("808182838485868788897a7b7c7d7e7f"), "HmacSHA256");
+ String entryName2 = "test" + MIN_SUPPORTED_KEY_COUNT;
+
+ SecretKey key3 = new SecretKeySpec(
+ HexEncoding.decode("33333333333333333333777777777777"), "HmacSHA256");
+
+ mKeyStore.load(null);
+ try {
+ KeyProtection protectionParams = new KeyProtection.Builder(
+ KeyProperties.PURPOSE_SIGN)
+ .build();
+ mKeyStore.setEntry(entryName1, new KeyStore.SecretKeyEntry(key1), protectionParams);
+
+ // Import key3 many of times, under different aliases.
+ for (int i = 1; i < MIN_SUPPORTED_KEY_COUNT; i++) {
+ String entryAlias = "test" + i;
+ try {
+ mKeyStore.setEntry(entryAlias, new KeyStore.SecretKeyEntry(key3), protectionParams);
+ } catch (Throwable e) {
+ throw new RuntimeException("Entry " + entryAlias + " import failed", e);
+ }
+ }
+
+ mKeyStore.setEntry(entryName2, new KeyStore.SecretKeyEntry(key2), protectionParams);
+ SecretKey keystoreKey2 = (SecretKey) mKeyStore.getKey(entryName2, null);
+ SecretKey keystoreKey1 = (SecretKey) mKeyStore.getKey(entryName1, null);
+
+ byte[] message = "This is a test".getBytes("UTF-8");
+ Mac mac = Mac.getInstance(key1.getAlgorithm());
+ mac.init(keystoreKey1);
+ MoreAsserts.assertEquals(
+ HexEncoding.decode(
+ "905e36f5a175f4ca54ad56b860b46f6502f883a90628dca2d33a953fb7224eaf"),
+ mac.doFinal(message));
+
+ mac = Mac.getInstance(key2.getAlgorithm());
+ mac.init(keystoreKey2);
+ MoreAsserts.assertEquals(
+ HexEncoding.decode(
+ "59b57e77e4e2cb36b5c7b84af198ac004327bc549de6931a1b5505372dd8c957"),
+ mac.doFinal(message));
+ } finally {
+ // Clean up Keystore without using KeyStore.aliases() which can't handle this many
+ // entries.
+ for (int i = 0; i <= MIN_SUPPORTED_KEY_COUNT; i++) {
+ mKeyStore.deleteEntry("test" + i);
+ }
+ }
+ }
}
diff --git a/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
new file mode 100644
index 0000000..398d373
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/BlockCipherTestBase.java
@@ -0,0 +1,1575 @@
+/*
+ * 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.KeyProperties;
+import android.security.keystore.KeyProtection;
+import android.test.AndroidTestCase;
+
+import junit.framework.AssertionFailedError;
+
+import java.nio.Buffer;
+import java.nio.ByteBuffer;
+import java.security.AlgorithmParameters;
+import java.security.InvalidAlgorithmParameterException;
+import java.security.InvalidKeyException;
+import java.security.Key;
+import java.security.KeyStore;
+import java.security.NoSuchAlgorithmException;
+import java.security.Provider;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.spec.AlgorithmParameterSpec;
+import java.security.spec.InvalidParameterSpecException;
+import java.util.Arrays;
+import java.util.Enumeration;
+import java.util.Locale;
+
+import javax.crypto.BadPaddingException;
+import javax.crypto.Cipher;
+import javax.crypto.IllegalBlockSizeException;
+import javax.crypto.NoSuchPaddingException;
+import javax.crypto.SecretKey;
+import javax.crypto.ShortBufferException;
+import javax.crypto.spec.SecretKeySpec;
+
+abstract class BlockCipherTestBase extends AndroidTestCase {
+
+ private KeyStore mAndroidKeyStore;
+ private int mNextKeyId;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mAndroidKeyStore = KeyStore.getInstance("AndroidKeyStore");
+ mAndroidKeyStore.load(null);
+ for (Enumeration<String> e = mAndroidKeyStore.aliases(); e.hasMoreElements();) {
+ mAndroidKeyStore.deleteEntry(e.nextElement());
+ }
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ try {
+ for (Enumeration<String> e = mAndroidKeyStore.aliases(); e.hasMoreElements();) {
+ mAndroidKeyStore.deleteEntry(e.nextElement());
+ }
+ } finally {
+ super.tearDown();
+ }
+ }
+
+ protected abstract String getTransformation();
+ protected abstract int getBlockSize();
+
+ protected abstract byte[] getKatKey();
+ protected abstract byte[] getKatIv();
+ protected abstract AlgorithmParameterSpec getKatAlgorithmParameterSpec();
+ protected abstract byte[] getKatPlaintext();
+ protected abstract byte[] getKatCiphertext();
+ protected abstract int getKatAuthenticationTagLengthBytes();
+ protected abstract boolean isStreamCipher();
+ protected abstract boolean isAuthenticatedCipher();
+
+ protected abstract byte[] getIv(AlgorithmParameters params)
+ throws InvalidParameterSpecException;
+
+ private byte[] getKatInput(int opmode) {
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ return getKatPlaintext();
+ case Cipher.DECRYPT_MODE:
+ return getKatCiphertext();
+ default:
+ throw new IllegalArgumentException("Invalid opmode: " + opmode);
+ }
+ }
+
+ private byte[] getKatOutput(int opmode) {
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ return getKatCiphertext();
+ case Cipher.DECRYPT_MODE:
+ return getKatPlaintext();
+ default:
+ throw new IllegalArgumentException("Invalid opmode: " + opmode);
+ }
+ }
+
+ private Cipher mCipher;
+ private int mOpmode;
+
+ public void testGetAlgorithm() throws Exception {
+ createCipher();
+ assertEquals(getTransformation(), mCipher.getAlgorithm());
+ }
+
+ public void testGetProvider() throws Exception {
+ createCipher();
+ Provider expectedProvider = Security.getProvider("AndroidKeyStoreBCWorkaround");
+ assertSame(expectedProvider, mCipher.getProvider());
+ }
+
+ public void testGetBlockSize() throws Exception {
+ createCipher();
+ assertEquals(getBlockSize(), mCipher.getBlockSize());
+ }
+
+ public void testGetExemptionMechanism() throws Exception {
+ createCipher();
+ assertNull(mCipher.getExemptionMechanism());
+ }
+
+ public void testGetParameters() throws Exception {
+ createCipher();
+ assertAlgoritmParametersIv(null);
+
+ initKat(Cipher.ENCRYPT_MODE);
+ assertAlgoritmParametersIv(getKatIv());
+ doFinal(getKatPlaintext());
+ assertAlgoritmParametersIv(getKatIv());
+
+ initKat(Cipher.DECRYPT_MODE);
+ assertAlgoritmParametersIv(getKatIv());
+ doFinal(getKatCiphertext());
+ assertAlgoritmParametersIv(getKatIv());
+ }
+
+ private void assertAlgoritmParametersIv(byte[] expectedIv)
+ throws InvalidParameterSpecException {
+ AlgorithmParameters actualParameters = mCipher.getParameters();
+ if (expectedIv == null) {
+ assertNull(actualParameters);
+ } else {
+ byte[] actualIv = getIv(actualParameters);
+ assertEquals(expectedIv, actualIv);
+ }
+ }
+
+ public void testGetOutputSizeInEncryptionMode() throws Exception {
+ int blockSize = getBlockSize();
+ createCipher();
+ try {
+ mCipher.getOutputSize(blockSize);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ initKat(Cipher.ENCRYPT_MODE);
+ if (isAuthenticatedCipher()) {
+ // Authenticated ciphers do not return any output when decrypting until doFinal where
+ // ciphertext is authenticated.
+ for (int input = 0; input <= blockSize * 2; input++) {
+ int actualOutputSize = mCipher.getOutputSize(input);
+ int expectedOutputSize = input + getKatAuthenticationTagLengthBytes();
+ if (actualOutputSize < expectedOutputSize) {
+ fail("getOutputSize(" + expectedOutputSize + ") underestimated output size"
+ + ". min expected: <" + expectedOutputSize
+ + ">, actual: <" + actualOutputSize + ">");
+ }
+ }
+ return;
+ } else if (isStreamCipher()) {
+ // Unauthenticated stream ciphers do not buffer input or output.
+ for (int input = 0; input <= blockSize * 2; input++) {
+ int actualOutputSize = mCipher.getOutputSize(input);
+ if (actualOutputSize < input) {
+ fail("getOutputSize(" + input + ") underestimated output size. min expected: <"
+ + input + ">, actual: <" + actualOutputSize + ">");
+ }
+ }
+ return;
+ }
+ // Not a stream cipher -- input may be buffered.
+
+ for (int buffered = 0; buffered < blockSize; buffered++) {
+ // Assert that the output of getOutputSize is not lower than the minimum expected.
+ for (int input = 0; input <= blockSize * 2; input++) {
+ int inputInclBuffered = buffered + input;
+ // doFinal dominates the output size.
+ // One full plaintext block results in one ciphertext block.
+ int minExpectedOutputSize = inputInclBuffered - (inputInclBuffered % blockSize);
+ if (isPaddingEnabled()) {
+ // Regardless of whether there is a partial input block, an additional block of
+ // ciphertext should be output.
+ minExpectedOutputSize += blockSize;
+ } else {
+ // When no padding is enabled, any remaining partial block of plaintext will
+ // cause an error. Thus, there's no need to account for its ciphertext.
+ }
+ int actualOutputSize = mCipher.getOutputSize(input);
+ System.out.println("*** buffered: " + buffered + ", input: " + input + ", min: " + minExpectedOutputSize + ", actual: " + actualOutputSize);
+ if (actualOutputSize < minExpectedOutputSize) {
+ fail("getOutputSize(" + input + ") underestimated output size when buffered == "
+ + buffered + ". min expected: <"
+ + minExpectedOutputSize + ">, actual: <" + actualOutputSize + ">");
+ }
+ }
+
+ if (buffered == blockSize - 1) {
+ break;
+ }
+ // Buffer one more byte of input.
+ assertNull("buffered: " + buffered, update(new byte[1]));
+ }
+ }
+
+ public void testGetOutputSizeInDecryptionMode() throws Exception {
+ int blockSize = getBlockSize();
+ createCipher();
+ try {
+ mCipher.getOutputSize(blockSize);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ initKat(Cipher.DECRYPT_MODE);
+ if ((!isAuthenticatedCipher()) && (isStreamCipher())) {
+ // Unauthenticated stream ciphers do not buffer input or output.
+ for (int input = 0; input <= blockSize * 2; input++) {
+ int actualOutputSize = mCipher.getOutputSize(input);
+ int expectedOutputSize = input;
+ if (actualOutputSize < expectedOutputSize) {
+ fail("getOutputSize(" + expectedOutputSize + ") underestimated output size"
+ + ". min expected: <" + expectedOutputSize
+ + ">, actual: <" + actualOutputSize + ">");
+ }
+ }
+ return;
+ }
+ // Input may be buffered.
+
+ for (int buffered = 0; buffered < blockSize; buffered++) {
+ // Assert that the output of getOutputSize is not lower than the minimum expected.
+ for (int input = 0; input <= blockSize * 2; input++) {
+ int inputInclBuffered = buffered + input;
+ // doFinal dominates the output size.
+ int minExpectedOutputSize;
+ if (isAuthenticatedCipher()) {
+ // Non-stream authenticated ciphers not supported
+ assertTrue(isStreamCipher());
+
+ // Authenticated stream cipher
+ minExpectedOutputSize =
+ inputInclBuffered - getKatAuthenticationTagLengthBytes();
+ } else {
+ // Unauthenticated block cipher.
+
+ // One full ciphertext block results in one ciphertext block.
+ minExpectedOutputSize = inputInclBuffered - (inputInclBuffered % blockSize);
+ if (isPaddingEnabled()) {
+ if ((inputInclBuffered % blockSize) == 0) {
+ // No more ciphertext remaining. Thus, the last plaintext block is at
+ // most blockSize - 1 bytes long.
+ minExpectedOutputSize--;
+ } else {
+ // Partial ciphertext block cannot be decrypted. Thus, the last
+ // plaintext block would not have been output.
+ minExpectedOutputSize -= blockSize;
+ }
+ } else {
+ // When no padding is enabled, any remaining ciphertext will cause a error
+ // because only full blocks can be decrypted. Thus, there's no need to
+ // account for its plaintext.
+ }
+ }
+ if (minExpectedOutputSize < 0) {
+ minExpectedOutputSize = 0;
+ }
+ int actualOutputSize = mCipher.getOutputSize(input);
+ System.out.println("*** buffered: " + buffered + ", input: " + input + ", min: " + minExpectedOutputSize + ", actual: " + actualOutputSize);
+ if (actualOutputSize < minExpectedOutputSize) {
+ fail("getOutputSize(" + input + ") underestimated output size when buffered == "
+ + buffered + ". min expected: <"
+ + minExpectedOutputSize + ">, actual: <" + actualOutputSize + ">");
+ }
+ }
+
+ if (buffered == blockSize - 1) {
+ break;
+ }
+ // Buffer one more byte of input.
+ assertNull("buffered: " + buffered, update(new byte[1]));
+ }
+ }
+
+ public void testInitRequiresIvInDecryptMode() throws Exception {
+ if (getKatIv() == null) {
+ // IV not used in this transformation.
+ return;
+ }
+
+ createCipher();
+ try {
+ init(Cipher.DECRYPT_MODE, getKey());
+ fail();
+ } catch (InvalidKeyException expected) {}
+
+ createCipher();
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (SecureRandom) null);
+ fail();
+ } catch (InvalidKeyException expected) {}
+
+ createCipher();
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null, null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+
+ createCipher();
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null, null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+
+ createCipher();
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null, null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+
+ createCipher();
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null, null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+ }
+
+ public void testGetIV() throws Exception {
+ createCipher();
+ assertNull(mCipher.getIV());
+
+ initKat(Cipher.ENCRYPT_MODE);
+ assertEquals(getKatIv(), mCipher.getIV());
+
+ byte[] ciphertext = doFinal(new byte[getBlockSize()]);
+ assertEquals(getKatIv(), mCipher.getIV());
+
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ assertEquals(getKatIv(), mCipher.getIV());
+
+ doFinal(ciphertext);
+ assertEquals(getKatIv(), mCipher.getIV());
+ }
+
+ public void testIvGeneratedAndUsedWhenEncryptingWithoutExplicitIv() throws Exception {
+ createCipher();
+ SecretKey key = getKey();
+ init(Cipher.ENCRYPT_MODE, key);
+ byte[] generatedIv = mCipher.getIV();
+ AlgorithmParameters generatedParams = mCipher.getParameters();
+ if (getKatIv() == null) {
+ // IV not needed by this transformation -- shouldn't have been generated by Cipher.init
+ assertNull(generatedIv);
+ assertNull(generatedParams);
+ } else {
+ // IV is needed by this transformation -- should've been generated by Cipher.init
+ assertNotNull(generatedIv);
+ assertEquals(getKatIv().length, generatedIv.length);
+ assertNotNull(generatedParams);
+ assertEquals(generatedIv, getIv(generatedParams));
+ }
+
+ // Assert that encrypting then decrypting using the above IV (or null) results in the
+ // original plaintext.
+ byte[] plaintext = new byte[getBlockSize()];
+ byte[] ciphertext = doFinal(plaintext);
+ createCipher();
+ init(Cipher.DECRYPT_MODE, key, generatedParams);
+ byte[] decryptedPlaintext = mCipher.doFinal(ciphertext);
+ assertEquals(plaintext, decryptedPlaintext);
+ }
+
+ public void testGeneratedIvSurvivesReset() throws Exception {
+ if (getKatIv() == null) {
+ // This transformation does not use an IV
+ return;
+ }
+
+ createCipher();
+ init(Cipher.ENCRYPT_MODE, getKey());
+ byte[] iv = mCipher.getIV();
+ AlgorithmParameters generatedParams = mCipher.getParameters();
+ byte[] ciphertext = mCipher.doFinal(getKatPlaintext());
+ // Assert that the IV is still there
+ assertEquals(iv, mCipher.getIV());
+ assertAlgoritmParametersIv(iv);
+
+ if (getKatIv() != null) {
+ // We try to prevent IV reuse by not letting the Cipher be reused.
+ return;
+ }
+
+ // Assert that encrypting the same input after the above reset produces the same ciphertext.
+ assertEquals(ciphertext, mCipher.doFinal(getKatPlaintext()));
+
+ assertEquals(iv, mCipher.getIV());
+ assertAlgoritmParametersIv(iv);
+
+ // Just in case, test with a new instance of Cipher with the same parameters
+ createCipher();
+ init(Cipher.ENCRYPT_MODE, getKey(), generatedParams);
+ assertEquals(ciphertext, mCipher.doFinal(getKatPlaintext()));
+ }
+
+ public void testGeneratedIvDoesNotSurviveReinitialization() throws Exception {
+ if (getKatIv() == null) {
+ // This transformation does not use an IV
+ return;
+ }
+
+ createCipher();
+ init(Cipher.ENCRYPT_MODE, getKey());
+ byte[] ivBeforeReinitialization = mCipher.getIV();
+
+ init(Cipher.ENCRYPT_MODE, getKey());
+ // A new IV should've been generated
+ if (Arrays.equals(ivBeforeReinitialization, mCipher.getIV())) {
+ fail("Same auto-generated IV after Cipher reinitialized."
+ + " Broken implementation or you're very unlucky (p: 2^{-"
+ + (ivBeforeReinitialization.length * 8) + "})");
+ }
+ }
+
+ public void testExplicitlySetIvDoesNotSurviveReinitialization() throws Exception {
+ if (getKatIv() == null) {
+ // This transformation does not use an IV
+ return;
+ }
+
+ createCipher();
+ initKat(Cipher.ENCRYPT_MODE);
+ init(Cipher.ENCRYPT_MODE, getKey());
+ // A new IV should've been generated
+ if (Arrays.equals(getKatIv(), mCipher.getIV())) {
+ fail("Auto-generated IV after Cipher reinitialized is the same as previous IV."
+ + " Broken implementation or you're very unlucky (p: 2^{-"
+ + (getKatIv().length * 8) + "})");
+ }
+ }
+
+ public void testReinitializingInDecryptModeDoesNotUsePreviouslyUsedIv() throws Exception {
+ if (getKatIv() == null) {
+ // This transformation does not use an IV
+ return;
+ }
+
+ createCipher();
+ // Initialize with explicitly provided IV
+ init(Cipher.ENCRYPT_MODE, getKey(), getKatAlgorithmParameterSpec());
+ // Make sure the IV has been used, just in case it's set/cached lazily.
+ mCipher.update(new byte[getBlockSize() * 2]);
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey());
+ fail();
+ } catch (InvalidKeyException expected) {}
+
+ createCipher();
+ // Initialize with a generated IV
+ init(Cipher.ENCRYPT_MODE, getKey());
+ mCipher.doFinal(getKatPlaintext());
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey());
+ fail();
+ } catch (InvalidKeyException expected) {}
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (SecureRandom) null);
+ fail();
+ } catch (InvalidKeyException expected) {}
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameterSpec) null, null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+
+ // IV required but not provided
+ try {
+ init(Cipher.DECRYPT_MODE, getKey(), (AlgorithmParameters) null, null);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+ }
+
+ public void testKeyDoesNotSurviveReinitialization() throws Exception {
+ assertKeyDoesNotSurviveReinitialization(Cipher.ENCRYPT_MODE);
+ assertKeyDoesNotSurviveReinitialization(Cipher.DECRYPT_MODE);
+ }
+
+ private void assertKeyDoesNotSurviveReinitialization(int opmode) throws Exception {
+ byte[] input = getKatInput(opmode);
+ createCipher();
+ byte[] katKeyBytes = getKatKey();
+ SecretKey key1 = importKey(katKeyBytes);
+ init(opmode, key1, getKatAlgorithmParameterSpec());
+ byte[] output1 = doFinal(input);
+
+ // Create a different key by flipping a bit in the KAT key.
+ katKeyBytes[0] ^= 1;
+ SecretKey key2 = importKey(katKeyBytes);
+
+ init(opmode, key2, getKatAlgorithmParameterSpec());
+ byte[] output2;
+ try {
+ output2 = doFinal(input);
+ } catch (BadPaddingException expected) {
+ // Padding doesn't decode probably because the new key is being used. This can only
+ // occur is padding is used.
+ return;
+ }
+
+ // Either padding wasn't used or the old key was used.
+ if (Arrays.equals(output1, output2)) {
+ fail("Same output when reinitialized with a different key. opmode: " + opmode);
+ }
+ }
+
+ public void testDoFinalResets() throws Exception {
+ assertDoFinalResetsCipher(Cipher.DECRYPT_MODE);
+ assertDoFinalResetsCipher(Cipher.ENCRYPT_MODE);
+ }
+
+ private void assertDoFinalResetsCipher(int opmode) throws Exception {
+ byte[] input = getKatInput(opmode);
+ byte[] expectedOutput = getKatOutput(opmode);
+
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput, doFinal(input));
+
+ if ((opmode == Cipher.ENCRYPT_MODE) && (getKatIv() != null)) {
+ // Assert that this cipher cannot be reused (thus making IV reuse harder)
+ try {
+ doFinal(input);
+ fail();
+ } catch (IllegalStateException expected) {}
+ return;
+ }
+
+ // Assert that the same output is produced after the above reset
+ assertEquals(expectedOutput, doFinal(input));
+
+ // Assert that the same output is produced after the above reset. This time, make update()
+ // buffer half a block of input.
+ if (input.length < getBlockSize() * 2) {
+ fail("This test requires an input which is at least two blocks long");
+ }
+ assertEquals(expectedOutput, concat(
+ update(subarray(input, 0, getBlockSize() * 3 / 2)),
+ doFinal(subarray(input, getBlockSize() * 3 / 2, input.length))));
+
+ // Assert that the same output is produced after the above reset, despite half of the block
+ // having been buffered prior to the reset. This is in case the implementation does not
+ // empty that buffer when resetting.
+ assertEquals(expectedOutput, doFinal(input));
+
+ // Assert that the IV with which the cipher was initialized is still there after the resets.
+ assertEquals(getKatIv(), mCipher.getIV());
+ assertAlgoritmParametersIv(getKatIv());
+ }
+
+ public void testUpdateWithEmptyInputReturnsCorrectValue() throws Exception {
+ // Test encryption
+ createCipher();
+ initKat(Cipher.ENCRYPT_MODE);
+ assertUpdateWithEmptyInputReturnsNull();
+
+ // Test decryption
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ assertUpdateWithEmptyInputReturnsNull();
+ }
+
+ private void assertUpdateWithEmptyInputReturnsNull() {
+ assertEquals(null, update(new byte[0]));
+ assertEquals(null, update(new byte[getBlockSize() * 2], getBlockSize(), 0));
+ assertEquals(null, update(new byte[getBlockSize()], 0, 0));
+
+ // Feed two blocks through the Cipher, so that it's in a state where a block of input
+ // produces a block of output.
+ // Two blocks are used instead of one because when decrypting with padding enabled, output
+ // lags behind input by a block because the Cipher doesn't know whether the most recent
+ // input block was supposed to contain padding.
+ update(new byte[getBlockSize() * 2]);
+
+ assertEquals(null, update(new byte[0]));
+ assertEquals(null, update(new byte[getBlockSize() * 2], getBlockSize(), 0));
+ assertEquals(null, update(new byte[getBlockSize()], 0, 0));
+ }
+
+ public void testUpdateDoesNotProduceOutputWhenInsufficientInput() throws Exception {
+ if (isStreamCipher()) {
+ // Stream ciphers always produce output for non-empty input.
+ return;
+ }
+
+ // Test encryption
+ createCipher();
+ initKat(Cipher.ENCRYPT_MODE);
+ assertUpdateDoesNotProduceOutputWhenInsufficientInput();
+
+ // Test decryption
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ assertUpdateDoesNotProduceOutputWhenInsufficientInput();
+ }
+
+ private void assertUpdateDoesNotProduceOutputWhenInsufficientInput() throws Exception {
+ if (getBlockSize() < 8) {
+ fail("This test isn't designed for small block size: " + getBlockSize());
+ }
+
+ assertEquals(null, update(new byte[1]));
+ assertEquals(null, update(new byte[1], 0, 1));
+ assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()]));
+ assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()], 0));
+ assertEquals(0, update(ByteBuffer.allocate(1), ByteBuffer.allocate(getBlockSize())));
+
+ // Feed a block through the Cipher, so that it's potentially no longer in an initial state.
+ byte[] output = update(new byte[getBlockSize()]);
+ assertEquals(getBlockSize(), output.length);
+
+ assertEquals(null, update(new byte[1]));
+ assertEquals(null, update(new byte[1], 0, 1));
+ assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()]));
+ assertEquals(0, update(new byte[1], 0, 1, new byte[getBlockSize()], 0));
+ assertEquals(0, update(ByteBuffer.allocate(1), ByteBuffer.allocate(getBlockSize())));
+ }
+
+ public void testKatOneShotEncryptUsingDoFinal() throws Exception {
+ createCipher();
+ assertKatOneShotTransformUsingDoFinal(
+ Cipher.ENCRYPT_MODE, getKatPlaintext(), getKatCiphertext());
+ }
+
+ public void testKatOneShotDecryptUsingDoFinal() throws Exception {
+ createCipher();
+ assertKatOneShotTransformUsingDoFinal(
+ Cipher.DECRYPT_MODE, getKatCiphertext(), getKatPlaintext());
+ }
+
+ private void assertKatOneShotTransformUsingDoFinal(
+ int opmode, byte[] input, byte[] expectedOutput) throws Exception {
+ int bufferWithInputInTheMiddleCleartextOffset = 5;
+ byte[] bufferWithInputInTheMiddle = concat(
+ new byte[bufferWithInputInTheMiddleCleartextOffset],
+ input,
+ new byte[4]);
+
+ initKat(opmode);
+ assertEquals(expectedOutput, doFinal(input));
+ initKat(opmode);
+ assertEquals(expectedOutput, doFinal(input, 0, input.length));
+ initKat(opmode);
+ assertEquals(expectedOutput,
+ doFinal(bufferWithInputInTheMiddle,
+ bufferWithInputInTheMiddleCleartextOffset,
+ input.length));
+
+ ByteBuffer inputBuffer = ByteBuffer.wrap(
+ bufferWithInputInTheMiddle,
+ bufferWithInputInTheMiddleCleartextOffset,
+ input.length);
+ ByteBuffer actualOutputBuffer = ByteBuffer.allocate(expectedOutput.length);
+ initKat(opmode);
+ assertEquals(expectedOutput.length, doFinal(inputBuffer, actualOutputBuffer));
+ assertEquals(0, inputBuffer.remaining());
+ assertByteBufferEquals(
+ (ByteBuffer) ByteBuffer.wrap(expectedOutput).position(expectedOutput.length),
+ actualOutputBuffer);
+ }
+
+ public void testKatEncryptOneByteAtATime() throws Exception {
+ createCipher();
+ initKat(Cipher.ENCRYPT_MODE);
+ byte[] plaintext = getKatPlaintext();
+ byte[] expectedCiphertext = getKatCiphertext();
+ int blockSize = getBlockSize();
+ if (isStreamCipher()) {
+ // Stream cipher -- one byte in, one byte out
+ for (int plaintextIndex = 0; plaintextIndex < plaintext.length; plaintextIndex++) {
+ byte[] output = update(new byte[] {plaintext[plaintextIndex]});
+ assertEquals("plaintext index: " + plaintextIndex, 1, output.length);
+ assertEquals("plaintext index: " + plaintextIndex,
+ expectedCiphertext[plaintextIndex], output[0]);
+ }
+ byte[] finalOutput = doFinal();
+ byte[] expectedFinalOutput;
+ if (isAuthenticatedCipher()) {
+ expectedFinalOutput =
+ subarray(expectedCiphertext, plaintext.length, expectedCiphertext.length);
+ } else {
+ expectedFinalOutput = EmptyArray.BYTE;
+ }
+ assertEquals(expectedFinalOutput, finalOutput);
+ } else {
+ // Not a stream cipher -- operates on full blocks only.
+
+ // Assert that a block of output is produced once a full block of input is provided.
+ // Every input block produces an output block.
+ int ciphertextIndex = 0;
+ for (int plaintextIndex = 0; plaintextIndex < plaintext.length; plaintextIndex++) {
+ byte[] output = update(new byte[] {plaintext[plaintextIndex]});
+ if ((plaintextIndex % blockSize) == blockSize - 1) {
+ // Cipher.update is expected to have output a new block
+ assertEquals(
+ "plaintext index: " + plaintextIndex,
+ subarray(
+ expectedCiphertext,
+ ciphertextIndex,
+ ciphertextIndex + blockSize),
+ output);
+ } else {
+ // Cipher.update is expected to have produced no output
+ assertEquals("plaintext index: " + plaintextIndex, null, output);
+ }
+ if (output != null) {
+ ciphertextIndex += output.length;
+ }
+ }
+
+ byte[] actualFinalOutput = doFinal();
+ byte[] expectedFinalOutput =
+ subarray(expectedCiphertext, ciphertextIndex, expectedCiphertext.length);
+ assertEquals(expectedFinalOutput, actualFinalOutput);
+ }
+ }
+
+ public void testKatDecryptOneByteAtATime() throws Exception {
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ byte[] ciphertext = getKatCiphertext();
+ int plaintextIndex = 0;
+ int blockSize = getBlockSize();
+ byte[] expectedPlaintext = getKatPlaintext();
+ boolean paddingEnabled = isPaddingEnabled();
+ if (isAuthenticatedCipher()) {
+ // Authenticated cipher -- no output until doFinal where ciphertext is authenticated.
+ for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) {
+ byte[] output = update(new byte[] {ciphertext[ciphertextIndex]});
+ assertEquals("ciphertext index: " + ciphertextIndex,
+ 0, (output != null) ? output.length : 0);
+ }
+ byte[] finalOutput = doFinal();
+ assertEquals(expectedPlaintext, finalOutput);
+ } else if (isStreamCipher()) {
+ // Unauthenticated stream cipher -- one byte in, one byte out
+ for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) {
+ byte[] output = update(new byte[] {ciphertext[ciphertextIndex]});
+ assertEquals("ciphertext index: " + ciphertextIndex, 1, output.length);
+ assertEquals("ciphertext index: " + ciphertextIndex,
+ expectedPlaintext[ciphertextIndex], output[0]);
+ }
+ byte[] finalOutput = doFinal();
+ assertEquals(0, finalOutput.length);
+ } else {
+ // Unauthenticated block cipher -- operates in full blocks only
+
+ // Assert that a block of output is produced once a full block of input is provided.
+ // When padding is used, output is produced one input byte later: once the first byte of the
+ // next input block is provided.
+ for (int ciphertextIndex = 0; ciphertextIndex < ciphertext.length; ciphertextIndex++) {
+ byte[] output = update(new byte[] {ciphertext[ciphertextIndex]});
+ boolean outputExpected =
+ ((paddingEnabled)
+ && (ciphertextIndex > 0) && ((ciphertextIndex % blockSize) == 0))
+ || ((!paddingEnabled) && ((ciphertextIndex % blockSize) == blockSize - 1));
+
+ if (outputExpected) {
+ assertEquals(
+ "ciphertext index: " + ciphertextIndex,
+ subarray(expectedPlaintext, plaintextIndex, plaintextIndex + blockSize),
+ output);
+ } else {
+ assertEquals("ciphertext index: " + ciphertextIndex, null, output);
+ }
+
+ if (output != null) {
+ plaintextIndex += output.length;
+ }
+ }
+
+ byte[] actualFinalOutput = doFinal();
+ byte[] expectedFinalOutput =
+ subarray(expectedPlaintext, plaintextIndex, expectedPlaintext.length);
+ assertEquals(expectedFinalOutput, actualFinalOutput);
+ }
+ }
+
+ public void testUpdateAADNotSupported() throws Exception {
+ createCipher();
+ initKat(Cipher.ENCRYPT_MODE);
+ if (isAuthenticatedCipher()) {
+ assertUpdateAADSupported();
+ } else {
+ assertUpdateAADNotSupported();
+ }
+
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ if (isAuthenticatedCipher()) {
+ assertUpdateAADSupported();
+ } else {
+ assertUpdateAADNotSupported();
+ }
+ }
+
+ private void assertUpdateAADNotSupported() throws Exception {
+ try {
+ mCipher.updateAAD(new byte[getBlockSize()]);
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ } catch (IllegalStateException expected) {}
+
+ try {
+ mCipher.updateAAD(new byte[getBlockSize()], 0, getBlockSize());
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ } catch (IllegalStateException expected) {}
+
+ try {
+ mCipher.updateAAD(ByteBuffer.allocate(getBlockSize()));
+ fail();
+ } catch (UnsupportedOperationException expected) {
+ } catch (IllegalStateException expected) {}
+ }
+
+ private void assertUpdateAADSupported() throws Exception {
+ mCipher.updateAAD(new byte[getBlockSize()]);
+ mCipher.updateAAD(new byte[getBlockSize()], 0, getBlockSize());
+ mCipher.updateAAD(ByteBuffer.allocate(getBlockSize()));
+ }
+
+ // TODO: Add tests for WRAP and UNWRAP
+
+ public void testUpdateAndDoFinalNotSupportedInWrapAndUnwrapModes() throws Exception {
+ createCipher();
+ assertUpdateAndDoFinalThrowIllegalStateExceprtion(
+ Cipher.WRAP_MODE, getKey(), getKatAlgorithmParameterSpec());
+
+ createCipher();
+ assertUpdateAndDoFinalThrowIllegalStateExceprtion(
+ Cipher.UNWRAP_MODE, getKey(), getKatAlgorithmParameterSpec());
+ }
+
+ private void assertUpdateAndDoFinalThrowIllegalStateExceprtion(
+ int opmode, SecretKey key, AlgorithmParameterSpec paramSpec)
+ throws Exception {
+ try {
+ init(opmode, key, paramSpec);
+ } catch (UnsupportedOperationException e) {
+ // Skip this test because wrap/unwrap is not supported by this Cipher
+ return;
+ }
+
+ try {
+ update(new byte[getBlockSize()]);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ update(new byte[getBlockSize()], 0, getBlockSize());
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ update(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2]);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ update(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2], 0);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ update(ByteBuffer.allocate(getBlockSize()), ByteBuffer.allocate(getBlockSize() * 2));
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal();
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal(new byte[getBlockSize()]);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal(new byte[getBlockSize()], 0, getBlockSize());
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal(new byte[getBlockSize() * 2], 0);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2]);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal(new byte[getBlockSize()], 0, getBlockSize(), new byte[getBlockSize() * 2], 0);
+ fail();
+ } catch (IllegalStateException expected) {}
+
+ init(opmode, key, paramSpec);
+ try {
+ doFinal(ByteBuffer.allocate(getBlockSize()), ByteBuffer.allocate(getBlockSize() * 2));
+ fail();
+ } catch (IllegalStateException expected) {}
+ }
+
+ public void testGeneratedPadding() throws Exception {
+ // Assert that the Cipher under test correctly handles plaintexts of various lengths.
+ if (isStreamCipher()) {
+ // Not applicable to stream ciphers
+ return;
+ }
+
+ // Encryption of basePlaintext and additional data should result in baseCiphertext and some
+ // data (some of which may be padding).
+ int blockSize = getBlockSize();
+ byte[] basePlaintext = subarray(getKatPlaintext(), 0, blockSize);
+ byte[] baseCiphertext = subarray(getKatCiphertext(), 0, blockSize);
+ boolean paddingEnabled = isPaddingEnabled();
+
+ for (int lastInputBlockUnusedByteCount = 0;
+ lastInputBlockUnusedByteCount < blockSize;
+ lastInputBlockUnusedByteCount++) {
+ byte[] plaintext = concat(basePlaintext, new byte[lastInputBlockUnusedByteCount]);
+ createCipher();
+ initKat(Cipher.ENCRYPT_MODE);
+
+ if ((!paddingEnabled) && ((lastInputBlockUnusedByteCount % blockSize) != 0)) {
+ // Without padding, plaintext which does not end with a full block should be
+ // rejected.
+ try {
+ doFinal(plaintext);
+ fail();
+ } catch (IllegalBlockSizeException expected) {}
+ continue;
+ }
+ byte[] ciphertext = doFinal(plaintext);
+
+ assertEquals(
+ "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
+ baseCiphertext,
+ subarray(ciphertext, 0, baseCiphertext.length));
+
+ int expectedCiphertextLength = getExpectedCiphertextLength(plaintext.length);
+ int expectedDecryptedPlaintextLength =
+ (paddingEnabled) ? plaintext.length : expectedCiphertextLength;
+ assertEquals(
+ "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
+ expectedCiphertextLength,
+ ciphertext.length);
+ initKat(Cipher.DECRYPT_MODE);
+ byte[] decryptedPlaintext = doFinal(ciphertext);
+ assertEquals(
+ "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
+ expectedDecryptedPlaintextLength,
+ decryptedPlaintext.length);
+ assertEquals(
+ "lastInputBlockUnusedByteCount: " + lastInputBlockUnusedByteCount,
+ basePlaintext,
+ subarray(decryptedPlaintext, 0, basePlaintext.length));
+ }
+ }
+
+ public void testDecryptWithMangledPadding() throws Exception {
+ if (!isPaddingEnabled()) {
+ // Test not applicable when padding not in use
+ return;
+ }
+
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ byte[] ciphertext = getKatCiphertext();
+ // Flip a bit in the last byte of ciphertext -- this should result in the last plaintext
+ // block getting mangled. In turn, this should result in bad padding.
+ ciphertext[ciphertext.length - 1] ^= 1;
+ try {
+ doFinal(ciphertext);
+ fail();
+ } catch (BadPaddingException expected) {}
+ }
+
+ public void testDecryptWithMissingPadding() throws Exception {
+ if (!isPaddingEnabled()) {
+ // Test not applicable when padding not in use
+ return;
+ }
+
+ createCipher();
+ initKat(Cipher.DECRYPT_MODE);
+ byte[] ciphertext = subarray(getKatCiphertext(), 0, getBlockSize());
+ try {
+ doFinal(ciphertext);
+ fail();
+ } catch (BadPaddingException expected) {}
+ }
+
+ public void testUpdateCopySafe() throws Exception {
+ // Assert that when input and output buffers passed to Cipher.update reference the same
+ // byte array, then no input data is overwritten before it's consumed.
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, 0);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, 1);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 1, 0);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() - 1);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize());
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() + 1);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 - 1, 0);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2, 0);
+ assertUpdateCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 + 1, 0);
+
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, 0);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, 1);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, 1, 0);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() - 1);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize());
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() + 1);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 - 1, 0);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2, 0);
+ assertUpdateCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 + 1, 0);
+ }
+
+ private void assertUpdateCopySafe(
+ int opmode, int inputOffsetInBuffer, int outputOffsetInBuffer)
+ throws Exception {
+ int blockSize = getBlockSize();
+ byte[] input;
+ byte[] expectedOutput;
+ switch (opmode) {
+ case Cipher.ENCRYPT_MODE:
+ input = getKatPlaintext();
+ if (isStreamCipher()) {
+ if (isAuthenticatedCipher()) {
+ expectedOutput = subarray(getKatCiphertext(), 0, input.length);
+ } else {
+ expectedOutput = getKatCiphertext();
+ }
+ } else {
+ // Update outputs exactly one block of ciphertext for one block of plaintext,
+ // excluding padding.
+ expectedOutput = subarray(
+ getKatCiphertext(), 0, (input.length / blockSize) * blockSize);
+ }
+ break;
+ case Cipher.DECRYPT_MODE:
+ input = getKatCiphertext();
+ if (isAuthenticatedCipher()) {
+ expectedOutput = EmptyArray.BYTE;
+ } else if (isStreamCipher()) {
+ expectedOutput = getKatPlaintext();
+ } else {
+ expectedOutput = getKatPlaintext();
+ if (isPaddingEnabled()) {
+ // When padding is enabled, update will not output the last block of
+ // plaintext because it doesn't know whether more ciphertext will be
+ // provided.
+ expectedOutput = subarray(
+ expectedOutput, 0, ((input.length / blockSize) - 1) * blockSize);
+ } else {
+ // When no padding is used, one block of ciphertext results in one block of
+ // plaintext.
+ expectedOutput = subarray(
+ expectedOutput, 0, (input.length - (input.length % blockSize)));
+ }
+ }
+ break;
+ default:
+ throw new AssertionFailedError("Unsupported opmode: " + opmode);
+ }
+
+ int inputEndIndexInBuffer = inputOffsetInBuffer + input.length;
+ int outputEndIndexInBuffer = outputOffsetInBuffer + expectedOutput.length;
+
+ // Test the update(byte[], int, int, byte[], int) variant
+ byte[] buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
+ System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput.length,
+ update(buffer, inputOffsetInBuffer, input.length,
+ buffer, outputOffsetInBuffer));
+ assertEquals(expectedOutput,
+ subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
+
+ if (outputOffsetInBuffer == 0) {
+ // We can use the update variant which assumes that output offset is 0.
+ buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
+ System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput.length,
+ update(buffer, inputOffsetInBuffer, input.length, buffer));
+ assertEquals(expectedOutput,
+ subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
+ }
+
+ // Test the update(ByteBuffer, ByteBuffer) variant
+ buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
+ System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
+ ByteBuffer inputBuffer = ByteBuffer.wrap(buffer, inputOffsetInBuffer, input.length);
+ ByteBuffer outputBuffer =
+ ByteBuffer.wrap(buffer, outputOffsetInBuffer, expectedOutput.length);
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput.length, update(inputBuffer, outputBuffer));
+ assertEquals(expectedOutput,
+ subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
+ }
+
+ public void testDoFinalCopySafe() throws Exception {
+ // Assert that when input and output buffers passed to Cipher.doFinal reference the same
+ // byte array, then no input data is overwritten before it's consumed.
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, 0);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, 1);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 1, 0);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() - 1);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize());
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, 0, getBlockSize() + 1);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 - 1, 0);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2, 0);
+ assertDoFinalCopySafe(Cipher.ENCRYPT_MODE, getBlockSize() * 2 + 1, 0);
+
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, 0);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, 1);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 1, 0);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() - 1);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize());
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, 0, getBlockSize() + 1);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 - 1, 0);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2, 0);
+ assertDoFinalCopySafe(Cipher.DECRYPT_MODE, getBlockSize() * 2 + 1, 0);
+ }
+
+ private void assertDoFinalCopySafe(
+ int opmode, int inputOffsetInBuffer, int outputOffsetInBuffer)
+ throws Exception {
+ byte[] input = getKatInput(opmode);
+ byte[] expectedOutput = getKatOutput(opmode);
+
+ int inputEndIndexInBuffer = inputOffsetInBuffer + input.length;
+ int outputEndIndexInBuffer = outputOffsetInBuffer + expectedOutput.length;
+
+ // Test the doFinal(byte[], int, int, byte[], int) variant
+ byte[] buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
+ System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput.length,
+ doFinal(buffer, inputOffsetInBuffer, input.length,
+ buffer, outputOffsetInBuffer));
+ assertEquals(expectedOutput,
+ subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
+
+ if (outputOffsetInBuffer == 0) {
+ // We can use the doFinal variant which assumes that output offset is 0.
+ buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
+ System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput.length,
+ doFinal(buffer, inputOffsetInBuffer, input.length, buffer));
+ assertEquals(expectedOutput,
+ subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
+ }
+
+ // Test the doFinal(ByteBuffer, ByteBuffer) variant
+ buffer = new byte[Math.max(inputEndIndexInBuffer, outputEndIndexInBuffer)];
+ System.arraycopy(input, 0, buffer, inputOffsetInBuffer, input.length);
+ ByteBuffer inputBuffer = ByteBuffer.wrap(buffer, inputOffsetInBuffer, input.length);
+ ByteBuffer outputBuffer =
+ ByteBuffer.wrap(buffer, outputOffsetInBuffer, expectedOutput.length);
+ createCipher();
+ initKat(opmode);
+ assertEquals(expectedOutput.length, doFinal(inputBuffer, outputBuffer));
+ assertEquals(expectedOutput,
+ subarray(buffer, outputOffsetInBuffer, outputEndIndexInBuffer));
+ }
+
+ private void createCipher() throws NoSuchAlgorithmException,
+ NoSuchPaddingException {
+ mCipher = Cipher.getInstance(getTransformation());
+ }
+
+ private String getKeyAlgorithm() {
+ String transformation = getTransformation();
+ int delimiterIndex = transformation.indexOf('/');
+ if (delimiterIndex == -1) {
+ fail("Unexpected transformation: " + transformation);
+ }
+ return transformation.substring(0, delimiterIndex);
+ }
+
+ private String getBlockMode() {
+ String transformation = getTransformation();
+ int delimiterIndex = transformation.indexOf('/');
+ if (delimiterIndex == -1) {
+ fail("Unexpected transformation: " + transformation);
+ }
+ int nextDelimiterIndex = transformation.indexOf('/', delimiterIndex + 1);
+ if (nextDelimiterIndex == -1) {
+ fail("Unexpected transformation: " + transformation);
+ }
+ return transformation.substring(delimiterIndex + 1, nextDelimiterIndex);
+ }
+
+ private String getPadding() {
+ String transformation = getTransformation();
+ int delimiterIndex = transformation.indexOf('/');
+ if (delimiterIndex == -1) {
+ fail("Unexpected transformation: " + transformation);
+ }
+ int nextDelimiterIndex = transformation.indexOf('/', delimiterIndex + 1);
+ if (nextDelimiterIndex == -1) {
+ fail("Unexpected transformation: " + transformation);
+ }
+ return transformation.substring(nextDelimiterIndex + 1);
+ }
+
+ private SecretKey getKey() {
+ return importKey(getKatKey());
+ }
+
+ private SecretKey importKey(byte[] keyMaterial) {
+ try {
+ int keyId = mNextKeyId++;
+ String keyAlias = "key" + keyId;
+ mAndroidKeyStore.setEntry(
+ keyAlias,
+ new KeyStore.SecretKeyEntry(new SecretKeySpec(keyMaterial, getKeyAlgorithm())),
+ new KeyProtection.Builder(
+ KeyProperties.PURPOSE_ENCRYPT
+ | KeyProperties.PURPOSE_DECRYPT)
+ .setBlockModes(getBlockMode())
+ .setEncryptionPaddings(getPadding())
+ .setRandomizedEncryptionRequired(false)
+ .build());
+ return (SecretKey) mAndroidKeyStore.getKey(keyAlias, null);
+ } catch (Exception e) {
+ throw new RuntimeException("Failed to import key into AndroidKeyStore", e);
+ }
+ }
+
+ private boolean isPaddingEnabled() {
+ return !getTransformation().toLowerCase(Locale.US).endsWith("/nopadding");
+ }
+
+ private int getExpectedCiphertextLength(int plaintextLength) {
+ int blockSize = getBlockSize();
+ if (isStreamCipher()) {
+ // Padding not supported for stream ciphers
+ assertFalse(isPaddingEnabled());
+ return plaintextLength;
+ } else {
+ if (isPaddingEnabled()) {
+ return ((plaintextLength / blockSize) + 1) * blockSize;
+ } else {
+ return ((plaintextLength + blockSize - 1) / blockSize) * blockSize;
+ }
+ }
+ }
+
+ private void initKat(int opmode)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ init(opmode, getKey(), getKatAlgorithmParameterSpec());
+ }
+
+ private 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)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ mCipher.init(opmode, key, spec, random);
+ mOpmode = opmode;
+ }
+
+ private 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)
+ throws InvalidKeyException, InvalidAlgorithmParameterException {
+ mCipher.init(opmode, key, spec, random);
+ mOpmode = opmode;
+ }
+
+ private 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 {
+ mCipher.init(opmode, key, random);
+ mOpmode = opmode;
+ }
+
+ private byte[] doFinal() throws IllegalBlockSizeException, BadPaddingException {
+ return mCipher.doFinal();
+ }
+
+ private byte[] doFinal(byte[] input) throws IllegalBlockSizeException, BadPaddingException {
+ return mCipher.doFinal(input);
+ }
+
+ private 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)
+ throws ShortBufferException, IllegalBlockSizeException, BadPaddingException {
+ return mCipher.doFinal(input, inputOffset, inputLen, output);
+ }
+
+ private 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,
+ ShortBufferException, BadPaddingException {
+ return mCipher.doFinal(output, outputOffset);
+ }
+
+ private int doFinal(ByteBuffer input, ByteBuffer output) throws ShortBufferException,
+ IllegalBlockSizeException, BadPaddingException {
+ return mCipher.doFinal(input, output);
+ }
+
+ private boolean isEncrypting() {
+ return (mOpmode == Cipher.ENCRYPT_MODE) || (mOpmode == Cipher.WRAP_MODE);
+ }
+
+ private void assertUpdateOutputSize(int inputLength, int outputLength) {
+ if ((isAuthenticatedCipher()) && (!isEncrypting())) {
+ assertEquals("Output of update must be empty for authenticated cipher when decrypting",
+ 0, outputLength);
+ return;
+ }
+
+ if (isStreamCipher()) {
+ if (outputLength != inputLength) {
+ fail("Output of update (" + outputLength + ") not same size as input ("
+ + inputLength + ")");
+ }
+ } else {
+ if ((outputLength % getBlockSize()) != 0) {
+ fail("Output of update (" + outputLength + ") not a multiple of block size ("
+ + getBlockSize() + ")");
+ }
+ }
+ }
+
+ private 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) {
+ 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)
+ throws ShortBufferException {
+ int outputLen = mCipher.update(input, offset, len, output);
+ assertUpdateOutputSize(len, outputLen);
+
+ return outputLen;
+ }
+
+ private 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);
+
+ return outputLen;
+ }
+
+ private int update(ByteBuffer input, ByteBuffer output) throws ShortBufferException {
+ int inputLimitBefore = input.limit();
+ int outputLimitBefore = output.limit();
+ int inputLen = input.remaining();
+ int outputPosBefore = output.position();
+
+ int outputLen = mCipher.update(input, output);
+
+ assertUpdateOutputSize(inputLen, outputLen);
+ assertEquals(inputLimitBefore, input.limit());
+ assertEquals(input.limit(), input.position());
+
+ assertEquals(outputLimitBefore, output.limit());
+ assertEquals(outputPosBefore + outputLen, output.position());
+
+ return outputLen;
+ }
+
+ @SuppressWarnings("unused")
+ private static void assertEquals(Buffer expected, Buffer actual) {
+ throw new RuntimeException(
+ "Comparing ByteBuffers using their .equals is probably not what you want"
+ + " -- use assertByteBufferEquals instead.");
+ }
+
+ /**
+ * 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) {
+ if (expected == null) {
+ if (actual == null) {
+ return;
+ } else {
+ fail("Expected: null, actual: " + bufferToString(actual));
+ }
+ } else {
+ if (actual == null) {
+ fail("Expected: " + bufferToString(expected) + ", actual: null");
+ } else {
+ if ((expected.capacity() != actual.capacity())
+ || (expected.position() != actual.position())
+ || (expected.limit() != actual.limit())
+ || (!equals(expected.array(), expected.arrayOffset(), expected.capacity(),
+ actual.array(), actual.arrayOffset(), actual.capacity()))) {
+ fail("Expected: " + bufferToString(expected)
+ + ", actual: " + bufferToString(actual));
+ }
+ }
+ }
+ }
+
+ private static String bufferToString(ByteBuffer buffer) {
+ return "ByteBuffer[pos: " + buffer.position() + ", limit: " + buffer.limit()
+ + ", capacity: " + buffer.capacity()
+ + ", backing array: " + HexEncoding.encode(
+ buffer.array(), buffer.arrayOffset(), buffer.capacity()) + "]";
+ }
+
+ private static boolean equals(byte[] arr1, int offset1, int len1, byte[] arr2, int offset2,
+ int len2) {
+ if (arr1 == null) {
+ return (arr2 == null);
+ } else if (arr2 == null) {
+ return (arr1 == null);
+ } else {
+ if (len1 != len2) {
+ return false;
+ }
+ for (int i = 0; i < len1; i++) {
+ if (arr1[i + offset1] != arr2[i + offset2]) {
+ return false;
+ }
+ }
+ return true;
+ }
+ }
+
+ private 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) {
+ int resultLength = 0;
+ for (byte[] array : arrays) {
+ resultLength += (array != null) ? array.length : 0;
+ }
+
+ byte[] result = new byte[resultLength];
+ int resultOffset = 0;
+ for (byte[] array : arrays) {
+ if (array != null) {
+ System.arraycopy(array, 0, result, resultOffset, array.length);
+ resultOffset += array.length;
+ }
+ }
+ return result;
+ }
+
+ private static void assertEquals(byte[] expected, byte[] actual) {
+ assertEquals(null, expected, actual);
+ }
+
+ private static void assertEquals(String message, byte[] expected, byte[] actual) {
+ if (!Arrays.equals(expected, actual)) {
+ StringBuilder detail = new StringBuilder();
+ if (expected != null) {
+ detail.append("Expected (" + expected.length + " bytes): <"
+ + HexEncoding.encode(expected) + ">");
+ } else {
+ detail.append("Expected: null");
+ }
+ if (actual != null) {
+ detail.append(", actual (" + actual.length + " bytes): <"
+ + HexEncoding.encode(actual) + ">");
+ } else {
+ detail.append(", actual: null");
+ }
+ if (message != null) {
+ fail(message + ": " + detail);
+ } else {
+ fail(detail.toString());
+ }
+ }
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java b/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
index a93cc35..ea4d0d1 100644
--- a/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
+++ b/tests/tests/keystore/src/android/keystore/cts/CountingSecureRandom.java
@@ -1,3 +1,19 @@
+/*
+ * 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 java.security.SecureRandom;
diff --git a/tests/tests/keystore/src/android/keystore/cts/EmptyArray.java b/tests/tests/keystore/src/android/keystore/cts/EmptyArray.java
new file mode 100644
index 0000000..90ac2c4
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/EmptyArray.java
@@ -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.
+ */
+
+package android.keystore.cts;
+
+abstract class EmptyArray {
+ private EmptyArray() {}
+
+ public static final byte[] BYTE = new byte[0];
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
index db7f539..e3540b4 100644
--- a/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/KeyPairGeneratorTest.java
@@ -40,6 +40,7 @@
import java.security.interfaces.ECKey;
import java.security.interfaces.ECPublicKey;
import java.security.interfaces.RSAPublicKey;
+import java.security.spec.AlgorithmParameterSpec;
import java.security.spec.ECGenParameterSpec;
import java.security.spec.ECParameterSpec;
import java.security.spec.RSAKeyGenParameterSpec;
@@ -354,7 +355,7 @@
| KeyProperties.PURPOSE_ENCRYPT
| KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_NONE)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setCertificateSubject(TEST_DN_1)
.setCertificateSerialNumber(TEST_SERIAL_1)
.setCertificateNotBefore(NOW)
@@ -381,7 +382,7 @@
| KeyProperties.PURPOSE_ENCRYPT
| KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_NONE)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setCertificateSubject(TEST_DN_2)
.setCertificateSerialNumber(TEST_SERIAL_2)
.setCertificateNotBefore(NOW)
@@ -410,7 +411,7 @@
| KeyProperties.PURPOSE_ENCRYPT
| KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_NONE)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setCertificateSubject(TEST_DN_1)
.setCertificateSerialNumber(TEST_SERIAL_1)
.setCertificateNotBefore(NOW)
@@ -435,7 +436,7 @@
| KeyProperties.PURPOSE_ENCRYPT
| KeyProperties.PURPOSE_DECRYPT)
.setDigests(KeyProperties.DIGEST_NONE)
- .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1)
.setCertificateSubject(TEST_DN_2)
.setCertificateSerialNumber(TEST_SERIAL_2)
.setCertificateNotBefore(NOW)
@@ -839,6 +840,75 @@
2048, RSAKeyGenParameterSpec.F0));
}
+ public void testGenerate_RSA_IndCpaEnforced() throws Exception {
+ KeyGenParameterSpec.Builder goodBuilder = new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_1, KeyProperties.PURPOSE_ENCRYPT)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_RSA_OAEP,
+ KeyProperties.ENCRYPTION_PADDING_RSA_PKCS1);
+ assertKeyGenInitSucceeds("RSA", goodBuilder.build());
+
+ // Should be fine because IND-CPA restriction applies only to encryption keys
+ assertKeyGenInitSucceeds("RSA",
+ TestUtils.buildUpon(goodBuilder, KeyProperties.PURPOSE_DECRYPT)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+
+ assertKeyGenInitThrowsInvalidAlgorithmParameterException("RSA",
+ TestUtils.buildUpon(goodBuilder)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+
+ assertKeyGenInitSucceeds("RSA",
+ TestUtils.buildUpon(goodBuilder)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setRandomizedEncryptionRequired(false)
+ .build());
+
+ // Should fail because PKCS#7 padding doesn't work with RSA
+ assertKeyGenInitThrowsInvalidAlgorithmParameterException("RSA",
+ TestUtils.buildUpon(goodBuilder)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_PKCS7)
+ .build());
+ }
+
+ public void testGenerate_EC_IndCpaEnforced() throws Exception {
+ KeyGenParameterSpec.Builder goodBuilder = new KeyGenParameterSpec.Builder(
+ TEST_ALIAS_2, KeyProperties.PURPOSE_ENCRYPT);
+ assertKeyGenInitSucceeds("EC", goodBuilder.build());
+
+ // Should be fine because IND-CPA restriction applies only to encryption keys
+ assertKeyGenInitSucceeds("EC",
+ TestUtils.buildUpon(goodBuilder, KeyProperties.PURPOSE_DECRYPT)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+
+ assertKeyGenInitThrowsInvalidAlgorithmParameterException("EC",
+ TestUtils.buildUpon(goodBuilder)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .build());
+
+ assertKeyGenInitSucceeds("EC",
+ TestUtils.buildUpon(goodBuilder)
+ .setEncryptionPaddings(KeyProperties.ENCRYPTION_PADDING_NONE)
+ .setRandomizedEncryptionRequired(false)
+ .build());
+ }
+
+ private void assertKeyGenInitSucceeds(String algorithm, AlgorithmParameterSpec params)
+ throws Exception {
+ KeyPairGenerator generator = getGenerator(algorithm);
+ generator.initialize(params);
+ }
+
+ private void assertKeyGenInitThrowsInvalidAlgorithmParameterException(
+ String algorithm, AlgorithmParameterSpec params) throws Exception {
+ KeyPairGenerator generator = getGenerator(algorithm);
+ try {
+ generator.initialize(params);
+ fail();
+ } catch (InvalidAlgorithmParameterException expected) {}
+ }
+
private void assertKeyGenUsingECSizeOnlyUsesCorrectCurve(
int keySizeBits, ECParameterSpec expectedParams) throws Exception {
KeyPairGenerator generator = getEcGenerator();
@@ -1161,12 +1231,17 @@
private KeyPairGenerator getRsaGenerator()
throws NoSuchAlgorithmException, NoSuchProviderException {
- return KeyPairGenerator.getInstance("RSA", "AndroidKeyStore");
+ return getGenerator("RSA");
}
private KeyPairGenerator getEcGenerator()
throws NoSuchAlgorithmException, NoSuchProviderException {
- return KeyPairGenerator.getInstance("EC", "AndroidKeyStore");
+ return getGenerator("EC");
+ }
+
+ private KeyPairGenerator getGenerator(String algorithm)
+ throws NoSuchAlgorithmException, NoSuchProviderException {
+ return KeyPairGenerator.getInstance(algorithm, "AndroidKeyStore");
}
private static void assertOneOf(int actual, int... expected) {
diff --git a/tests/tests/keystore/src/android/keystore/cts/MacTest.java b/tests/tests/keystore/src/android/keystore/cts/MacTest.java
new file mode 100644
index 0000000..d654e3a
--- /dev/null
+++ b/tests/tests/keystore/src/android/keystore/cts/MacTest.java
@@ -0,0 +1,367 @@
+/*
+ * 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 junit.framework.TestCase;
+
+import java.security.InvalidKeyException;
+import java.security.Provider;
+import java.security.Provider.Service;
+import java.security.Security;
+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.Mac;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.SecretKeySpec;
+
+public class MacTest extends TestCase {
+
+ private static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
+
+ private static final String[] EXPECTED_ALGORITHMS = {
+ "HmacSHA1",
+ "HmacSHA224",
+ "HmacSHA256",
+ "HmacSHA384",
+ "HmacSHA512",
+ };
+
+ private static final byte[] KAT_KEY = HexEncoding.decode(
+ "227b212bebd775493929ef626729a587d3f81b8e18a3ed482d403910e184479b448cfa79b62bd90595efdd"
+ + "15f87bd7b2d2dac480c61e969ba90a7b8ceadd3284");
+
+ private static final byte[] SHORT_MSG_KAT_MESSAGE = HexEncoding.decode("a16037e3c901c9a1ab");
+
+ private static final Map<String, byte[]> SHORT_MSG_KAT_MACS =
+ new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
+ static {
+ // From RI
+ SHORT_MSG_KAT_MACS.put("HmacSHA1", HexEncoding.decode(
+ "47d5677267f0efe1f7416bf504b210765674ef50"));
+ SHORT_MSG_KAT_MACS.put("HmacSHA224", HexEncoding.decode(
+ "03a8bbcd05e7166bff5b0b2368709a0c61c0b9d94f1b4d65c0e04948"));
+ SHORT_MSG_KAT_MACS.put("HmacSHA256", HexEncoding.decode(
+ "17feed3de0b2d53b69b228c2d9d26e9d57b314c50d36662596777f49445df729"));
+ SHORT_MSG_KAT_MACS.put("HmacSHA384", HexEncoding.decode(
+ "26e034c6696d28722ffc446ff0994f835e616cc704517d283a29648aee1eca5569c792ada8a176cdc7"
+ + "813a87437e4ea0"));
+ SHORT_MSG_KAT_MACS.put("HmacSHA512", HexEncoding.decode(
+ "15cff189d754d6612bd18d157c8e59ac2ecd9a4b97b2ef343b7778130f7741795d5d2dc2b7addb9a36"
+ + "7677ad57833b42bfa0733f49b57afd6bc32cddc0dcebec"));
+ }
+
+ private static final byte[] LONG_MSG_KAT_SEED = SHORT_MSG_KAT_MESSAGE;
+ private static final int LONG_MSG_KAT_SIZE_BYTES = 3 * 1024 * 1024 + 149;
+
+ private static final Map<String, byte[]> LONG_MSG_KAT_MACS =
+ new TreeMap<String, byte[]>(String.CASE_INSENSITIVE_ORDER);
+ static {
+ // From RI
+ LONG_MSG_KAT_MACS.put("HmacSHA1", HexEncoding.decode(
+ "2a89d12da79f541512db9c35c0a1e76750e01d48"));
+ LONG_MSG_KAT_MACS.put("HmacSHA224", HexEncoding.decode(
+ "5fef55c822f9b931c1b4ad7142e0a74ceaddf03f0a6533155cc06871"));
+ LONG_MSG_KAT_MACS.put("HmacSHA256", HexEncoding.decode(
+ "0bc25f22b8993d003a95a88c6cfa1c5a7b067a8aae1064ef897712418569bfe9"));
+ LONG_MSG_KAT_MACS.put("HmacSHA384", HexEncoding.decode(
+ "595a616295123966126102c06d69f8bb06c11090490186243420c2c4692877d75752b220c1b0447320"
+ + "959e28345523fc"));
+ LONG_MSG_KAT_MACS.put("HmacSHA512", HexEncoding.decode(
+ "aa97d594d799164d56e6652578f7884d1198bb2663641ad7903e3c0bda4c136e9f94ca0d16c3504302"
+ + "2944224e538e88a5410adb38eaa5169b3125738990e6d0"));
+ }
+
+
+ private static final long DAY_IN_MILLIS = 1000 * 60 * 60 * 24;
+
+ public void testAlgorithmList() {
+ // Assert that Android Keystore Provider exposes exactly the expected MAC algorithms. We
+ // don't care whether the algorithms are exposed via aliases, as long as the 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 ("Mac".equalsIgnoreCase(service.getType())) {
+ String algLowerCase = service.getAlgorithm().toLowerCase(Locale.US);
+ actualAlgsLowerCase.add(algLowerCase);
+ }
+ }
+
+ TestUtils.assertContentsInAnyOrder(actualAlgsLowerCase,
+ expectedAlgsLowerCase.toArray(new String[0]));
+ }
+
+ public void testAndroidKeyStoreKeysHandledByAndroidKeyStoreProvider() throws Exception {
+ SecretKey key = importDefaultKatKey();
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ assertNotNull(provider);
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ // Generate a MAC
+ Mac mac = Mac.getInstance(algorithm);
+ mac.init(key);
+ assertSame(provider, mac.getProvider());
+ } catch (Exception e) {
+ throw new RuntimeException(algorithm + " failed", e);
+ }
+ }
+ }
+
+ public void testGeneratedSignatureVerifies() throws Exception {
+ SecretKey key = importDefaultKatKey();
+
+ Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
+ assertNotNull(provider);
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ // Generate a MAC
+ Mac mac = Mac.getInstance(algorithm);
+ 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) {
+ throw new RuntimeException(algorithm + " failed", e);
+ }
+ }
+ }
+
+ public void testSmallMsgKat() throws Exception {
+ SecretKey key = importDefaultKatKey();
+ byte[] message = SHORT_MSG_KAT_MESSAGE;
+
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ byte[] goodMacBytes = SHORT_MSG_KAT_MACS.get(algorithm);
+ assertNotNull(goodMacBytes);
+ assertMacVerifiesOneShot(algorithm, key, message, goodMacBytes);
+ assertMacVerifiesFedOneByteAtATime(algorithm, key, message, goodMacBytes);
+ assertMacVerifiesFedUsingFixedSizeChunks(algorithm, key, message, goodMacBytes, 3);
+
+ byte[] messageWithBitFlip = message.clone();
+ messageWithBitFlip[messageWithBitFlip.length / 2] ^= 1;
+ assertMacDoesNotVerifyOneShot(algorithm, key, messageWithBitFlip, goodMacBytes);
+
+ byte[] goodMacWithBitFlip = goodMacBytes.clone();
+ goodMacWithBitFlip[goodMacWithBitFlip.length / 2] ^= 1;
+ assertMacDoesNotVerifyOneShot(algorithm, key, message, goodMacWithBitFlip);
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ public void testLargeMsgKat() throws Exception {
+ SecretKey key = importDefaultKatKey();
+ byte[] message = TestUtils.generateLargeKatMsg(LONG_MSG_KAT_SEED, LONG_MSG_KAT_SIZE_BYTES);
+
+ for (String algorithm : EXPECTED_ALGORITHMS) {
+ try {
+ byte[] goodMacBytes = LONG_MSG_KAT_MACS.get(algorithm);
+ assertNotNull(goodMacBytes);
+ assertMacVerifiesOneShot(algorithm, key, message, goodMacBytes);
+ assertMacVerifiesFedUsingFixedSizeChunks(
+ algorithm, key, message, goodMacBytes, 20389);
+ assertMacVerifiesFedUsingFixedSizeChunks(
+ algorithm, key, message, goodMacBytes, 393571);
+ } catch (Throwable e) {
+ throw new RuntimeException("Failed for " + algorithm, e);
+ }
+ }
+ }
+
+ 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());
+ }
+
+ public void testInitFailsWhenDigestNotAuthorized() throws Exception {
+ KeyProtection spec = new KeyProtection.Builder(KeyProperties.PURPOSE_SIGN).build();
+ assertInitSucceeds("HmacSHA384", "HmacSHA384", spec);
+ assertInitThrowsInvalidKeyException("HmacSHA256", "HmacSHA384", spec);
+ }
+
+ 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());
+
+ Date badStartDate = new Date(System.currentTimeMillis() + DAY_IN_MILLIS);
+ assertInitThrowsInvalidKeyException(algorithm, algorithm,
+ TestUtils.buildUpon(good).setKeyValidityStart(badStartDate).build());
+ }
+
+ 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());
+
+ Date badEndDate = new Date(System.currentTimeMillis() - DAY_IN_MILLIS);
+ assertInitThrowsInvalidKeyException(algorithm, algorithm,
+ TestUtils.buildUpon(good).setKeyValidityForOriginationEnd(badEndDate).build());
+ }
+
+ private void assertMacVerifiesOneShot(
+ String algorithm,
+ SecretKey key,
+ byte[] message,
+ byte[] mac) throws Exception {
+ Mac m = Mac.getInstance(algorithm);
+ m.init(key);
+ byte[] mac2 = m.doFinal(message);
+ if (!Arrays.equals(mac, mac2)) {
+ fail("MAC did not verify. algorithm: " + algorithm
+ + ", provider: " + m.getProvider().getName()
+ + ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac)
+ + ", actual MAC (" + mac2.length + " bytes): " + HexEncoding.encode(mac2));
+ }
+ }
+
+ private void assertMacDoesNotVerifyOneShot(
+ String algorithm,
+ SecretKey key,
+ byte[] message,
+ byte[] mac) throws Exception {
+ Mac m = Mac.getInstance(algorithm);
+ m.init(key);
+ byte[] mac2 = m.doFinal(message);
+ if (Arrays.equals(mac, mac2)) {
+ fail("MAC verifies unexpectedly. algorithm: " + algorithm
+ + ", provider: " + m.getProvider().getName()
+ + ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac));
+ }
+ }
+
+
+ private void assertMacVerifiesFedOneByteAtATime(
+ String algorithm,
+ SecretKey key,
+ byte[] message,
+ byte[] mac) throws Exception {
+ Mac m = Mac.getInstance(algorithm);
+ m.init(key);
+ for (int i = 0; i < message.length; i++) {
+ m.update(message[i]);
+ }
+ byte[] mac2 = m.doFinal();
+ if (!Arrays.equals(mac, mac2)) {
+ fail("MAC did not verify. algorithm: " + algorithm
+ + ", provider: " + m.getProvider().getName()
+ + ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac)
+ + ", actual MAC (" + mac2.length + " bytes): " + HexEncoding.encode(mac2));
+ }
+ }
+
+ private void assertMacVerifiesFedUsingFixedSizeChunks(
+ String algorithm,
+ SecretKey key,
+ byte[] message,
+ byte[] mac,
+ int chunkSizeBytes) throws Exception {
+ Mac m = Mac.getInstance(algorithm);
+ m.init(key);
+ int messageRemaining = message.length;
+ int messageOffset = 0;
+ while (messageRemaining > 0) {
+ int actualChunkSizeBytes = Math.min(chunkSizeBytes, messageRemaining);
+ m.update(message, messageOffset, actualChunkSizeBytes);
+ messageOffset += actualChunkSizeBytes;
+ messageRemaining -= actualChunkSizeBytes;
+ }
+ byte[] mac2 = m.doFinal();
+ if (!Arrays.equals(mac, mac2)) {
+ fail("MAC did not verify. algorithm: " + algorithm
+ + ", provider: " + m.getProvider().getName()
+ + ", MAC (" + mac.length + " bytes): " + HexEncoding.encode(mac)
+ + ", actual MAC (" + mac2.length + " bytes): " + HexEncoding.encode(mac2));
+ }
+ }
+
+ private void assertInitSucceeds(
+ String macAlgorithm, String keyAlgorithm, KeyProtection keyProtection)
+ throws Exception {
+ SecretKey key = importDefaultKatKey(keyAlgorithm, keyProtection);
+ Mac mac = Mac.getInstance(macAlgorithm);
+ mac.init(key);
+ }
+
+ 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);
+ } catch (InvalidKeyException expected) {}
+ }
+
+ 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
+ KeyProperties.DIGEST_SHA224,
+ KeyProperties.DIGEST_SHA256,
+ KeyProperties.DIGEST_SHA384,
+ KeyProperties.DIGEST_SHA512)
+ .build());
+ }
+
+ private SecretKey importDefaultKatKey(
+ String keyAlgorithm, KeyProtection keyProtection) throws Exception {
+ return TestUtils.importIntoAndroidKeyStore(
+ "test1",
+ new SecretKeySpec(KAT_KEY, keyAlgorithm),
+ keyProtection);
+ }
+}
diff --git a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
index 9e8678b..c53e2c3 100644
--- a/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
+++ b/tests/tests/keystore/src/android/keystore/cts/SignatureTest.java
@@ -20,7 +20,6 @@
import java.security.InvalidKeyException;
import java.security.KeyPair;
-import java.security.MessageDigest;
import java.security.PrivateKey;
import java.security.Provider;
import java.security.Provider.Service;
@@ -43,7 +42,8 @@
import android.test.MoreAsserts;
public class SignatureTest extends AndroidTestCase {
- static final String EXPECTED_PROVIDER_NAME = "AndroidKeyStoreBCWorkaround";
+
+ static final String EXPECTED_PROVIDER_NAME = TestUtils.EXPECTED_CRYPTO_OP_PROVIDER_NAME;
private static final String[] EXPECTED_SIGNATURE_ALGORITHMS = {
"NONEwithRSA",
@@ -487,24 +487,9 @@
}
}
- private static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
- byte[] result = new byte[msgSizeBytes];
- MessageDigest digest = MessageDigest.getInstance("SHA-512");
- int resultOffset = 0;
- int resultRemaining = msgSizeBytes;
- while (resultRemaining > 0) {
- seed = digest.digest(seed);
- int chunkSize = Math.min(seed.length, resultRemaining);
- System.arraycopy(seed, 0, result, resultOffset, chunkSize);
- resultOffset += chunkSize;
- resultRemaining -= chunkSize;
- }
- return result;
- }
-
public void testLongMsgKat() throws Exception {
Collection<KeyPair> keyPairs = importDefaultKatKeyPairs();
- byte[] message = generateLargeKatMsg(LONG_MSG_KAT_SEED, LONG_MSG_KAT_SIZE_BYTES);
+ byte[] message = TestUtils.generateLargeKatMsg(LONG_MSG_KAT_SEED, LONG_MSG_KAT_SIZE_BYTES);
Provider provider = Security.getProvider(EXPECTED_PROVIDER_NAME);
assertNotNull(provider);
@@ -952,6 +937,7 @@
}
return signature.sign();
}
+
private void assertSignatureVerifiesFedUsingFixedSizeChunks(
String algorithm,
Provider provider,
diff --git a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
index 02fbd1e..3448ffc 100644
--- a/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
+++ b/tests/tests/keystore/src/android/keystore/cts/TestUtils.java
@@ -33,6 +33,7 @@
import java.security.KeyPairGenerator;
import java.security.KeyStore;
import java.security.KeyStoreException;
+import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;
@@ -62,6 +63,10 @@
import javax.crypto.spec.SecretKeySpec;
abstract class TestUtils extends Assert {
+
+ static final String EXPECTED_CRYPTO_OP_PROVIDER_NAME = "AndroidKeyStoreBCWorkaround";
+
+
private TestUtils() {}
/**
@@ -417,6 +422,18 @@
(PrivateKey) keyStore.getKey(alias, null));
}
+ static SecretKey importIntoAndroidKeyStore(
+ String alias,
+ SecretKey key,
+ KeyProtection keyProtection) throws Exception {
+ KeyStore keyStore = KeyStore.getInstance("AndroidKeyStore");
+ keyStore.load(null);
+ keyStore.setEntry(alias,
+ new KeyStore.SecretKeyEntry(key),
+ keyProtection);
+ return (SecretKey) keyStore.getKey(alias, null);
+ }
+
static byte[] drain(InputStream in) throws IOException {
ByteArrayOutputStream result = new ByteArrayOutputStream();
byte[] buffer = new byte[16 * 1024];
@@ -458,6 +475,48 @@
return result;
}
+ static KeyGenParameterSpec.Builder buildUpon(
+ KeyGenParameterSpec.Builder builder) {
+ return buildUponInternal(builder, null);
+ }
+
+ static KeyGenParameterSpec.Builder buildUpon(
+ KeyGenParameterSpec.Builder builder, int newPurposes) {
+ return buildUponInternal(builder, newPurposes);
+ }
+
+ private static KeyGenParameterSpec.Builder buildUponInternal(
+ KeyGenParameterSpec.Builder builder, Integer newPurposes) {
+ KeyGenParameterSpec spec = builder.build();
+ int purposes = (newPurposes == null) ? spec.getPurposes() : newPurposes;
+ KeyGenParameterSpec.Builder result =
+ new KeyGenParameterSpec.Builder(spec.getKeystoreAlias(), purposes);
+ if (spec.getKeySize() >= 0) {
+ result.setKeySize(spec.getKeySize());
+ }
+ if (spec.getAlgorithmParameterSpec() != null) {
+ result.setAlgorithmParameterSpec(spec.getAlgorithmParameterSpec());
+ }
+ result.setCertificateNotBefore(spec.getCertificateNotBefore());
+ result.setCertificateNotAfter(spec.getCertificateNotAfter());
+ result.setCertificateSerialNumber(spec.getCertificateSerialNumber());
+ result.setCertificateSubject(spec.getCertificateSubject());
+ result.setBlockModes(spec.getBlockModes());
+ if (spec.isDigestsSpecified()) {
+ result.setDigests(spec.getDigests());
+ }
+ result.setEncryptionPaddings(spec.getEncryptionPaddings());
+ result.setSignaturePaddings(spec.getSignaturePaddings());
+ result.setKeyValidityStart(spec.getKeyValidityStart());
+ result.setKeyValidityForOriginationEnd(spec.getKeyValidityForOriginationEnd());
+ result.setKeyValidityForConsumptionEnd(spec.getKeyValidityForConsumptionEnd());
+ result.setRandomizedEncryptionRequired(spec.isRandomizedEncryptionRequired());
+ result.setUserAuthenticationRequired(spec.isUserAuthenticationRequired());
+ result.setUserAuthenticationValidityDurationSeconds(
+ spec.getUserAuthenticationValidityDurationSeconds());
+ return result;
+ }
+
static KeyPair getKeyPairForKeyAlgorithm(String keyAlgorithm, Iterable<KeyPair> keyPairs) {
for (KeyPair keyPair : keyPairs) {
if (keyAlgorithm.equalsIgnoreCase(keyPair.getPublic().getAlgorithm())) {
@@ -466,4 +525,19 @@
}
throw new IllegalArgumentException("No KeyPair for key algorithm " + keyAlgorithm);
}
+
+ static byte[] generateLargeKatMsg(byte[] seed, int msgSizeBytes) throws Exception {
+ byte[] result = new byte[msgSizeBytes];
+ MessageDigest digest = MessageDigest.getInstance("SHA-512");
+ int resultOffset = 0;
+ int resultRemaining = msgSizeBytes;
+ while (resultRemaining > 0) {
+ seed = digest.digest(seed);
+ int chunkSize = Math.min(seed.length, resultRemaining);
+ System.arraycopy(seed, 0, result, resultOffset, chunkSize);
+ resultOffset += chunkSize;
+ resultRemaining -= chunkSize;
+ }
+ return result;
+ }
}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
index daf55a7..15438e8 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -587,6 +587,7 @@
}
public void testGetMaxSupportedInstances() {
+ StringBuilder xmlOverrides = new StringBuilder();
MediaCodecList allCodecs = new MediaCodecList(MediaCodecList.ALL_CODECS);
for (MediaCodecInfo info : allCodecs.getCodecInfos()) {
Log.d(TAG, "codec: " + info.getName());
@@ -604,9 +605,25 @@
int actualMax = getActualMax(
info.isEncoder(), info.getName(), types[j], caps, max + 1);
Log.d(TAG, "actualMax " + actualMax + " vs reported max " + max);
- assertTrue(actualMax >= (int)(max * 0.9));
+ if (actualMax < (int)(max * 0.9) || actualMax > (int) Math.ceil(max * 1.1)) {
+ String codec = "<MediaCodec name=\"" + info.getName() +
+ "\" type=\"" + types[j] + "\" >";
+ String limit = " <Limit name=\"concurrent-instances\" max=\"" +
+ actualMax + "\" />";
+ xmlOverrides.append(codec);
+ xmlOverrides.append("\n");
+ xmlOverrides.append(limit);
+ xmlOverrides.append("\n");
+ xmlOverrides.append("</MediaCodec>\n");
+ }
}
}
}
+
+ if (xmlOverrides.length() > 0) {
+ String failMessage = "In order to pass the test, please publish following " +
+ "codecs' concurrent instances limit in /etc/media_codecs.xml: \n";
+ fail(failMessage + xmlOverrides.toString());
+ }
}
}
diff --git a/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java b/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
index 214ced4..d453e3f 100644
--- a/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
+++ b/tests/tests/media/src/android/media/cts/ResourceManagerStubActivity.java
@@ -53,7 +53,7 @@
Context context = getApplicationContext();
Intent intent1 = new Intent(context, ResourceManagerTestActivity1.class);
startActivityForResult(intent1, mRequestCodes[0]);
- Thread.sleep(2000); // wait for process to launch.
+ Thread.sleep(5000); // wait for process to launch and allocate all codecs.
Intent intent2 = new Intent(context, ResourceManagerTestActivity2.class);
startActivityForResult(intent2, mRequestCodes[1]);
diff --git a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
index 6e395aa..6c416ca 100644
--- a/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
+++ b/tests/tests/net/src/android/net/wifi/cts/WifiEnterpriseConfigTest.java
@@ -32,6 +32,10 @@
private static final String IDENTITY = "identity";
private static final String PASSWORD = "password";
private static final String SUBJECT_MATCH = "subjectmatch";
+ private static final String ALT_SUBJECT_MATCH = "altsubjectmatch";
+ private static final String DOM_SUBJECT_MATCH = "domsubjectmatch";
+ private static final String PLMN = "plmn";
+ private static final String REALM = "realm";
private static final String ANON_IDENTITY = "anonidentity";
private static final int ENABLE_DELAY = 10000;
@@ -87,6 +91,15 @@
config.setClientKeyEntry(null, null);
config.setSubjectMatch(SUBJECT_MATCH);
assertTrue(config.getSubjectMatch().equals(SUBJECT_MATCH));
+ // Hotspot 2.0 related attributes
+ config.setPlmn(PLMN);
+ assertTrue(config.getPlmn().equals(PLMN));
+ config.setPlmn(REALM);
+ assertTrue(config.getRealm().equals(REALM));
+ config.setAltSubjectMatch(ALT_SUBJECT_MATCH);
+ assertTrue(config.getAltSubjectMatch().equals(ALT_SUBJECT_MATCH));
+ config.setDomainSuffixMatch(DOM_SUBJECT_MATCH);
+ assertTrue(config.getDomainSuffixMatch().equals(DOM_SUBJECT_MATCH));
}
public void testAddEapNetwork() {
diff --git a/tests/tests/os/jni/Android.mk b/tests/tests/os/jni/Android.mk
index 24a0651..c125f5c 100644
--- a/tests/tests/os/jni/Android.mk
+++ b/tests/tests/os/jni/Android.mk
@@ -38,6 +38,12 @@
ARCH_SUPPORTS_SECCOMP = 1
endif
+ifeq ($(strip $(TARGET_ARCH)),arm64)
+ ARCH_SUPPORTS_SECCOMP = 1
+ # Required for __NR_poll definition.
+ LOCAL_CFLAGS += -D__ARCH_WANT_SYSCALL_DEPRECATED
+endif
+
ifeq ($(strip $(TARGET_ARCH)),x86)
ARCH_SUPPORTS_SECCOMP = 1
endif
diff --git a/tests/tests/os/jni/seccomp_sample_program.cpp b/tests/tests/os/jni/seccomp_sample_program.cpp
index e291e8a..3c90196 100644
--- a/tests/tests/os/jni/seccomp_sample_program.cpp
+++ b/tests/tests/os/jni/seccomp_sample_program.cpp
@@ -355,8 +355,6 @@
{0x6, 0, 0, 0x30001},
};
#elif defined(__aarch64__)
-// Note: aarch64 is not required to support seccomp-bpf yet, but some Nexus
-// devices do support it. For completeness, this test BPF program is provided.
struct sock_filter kTestSeccompFilter[] = {
{0x20, 0, 0, 0x4},
{0x15, 1, 0, 0xc00000b7},
diff --git a/tests/tests/os/src/android/os/cts/SeccompTest.java b/tests/tests/os/src/android/os/cts/SeccompTest.java
index 4c2f78f..0340580 100644
--- a/tests/tests/os/src/android/os/cts/SeccompTest.java
+++ b/tests/tests/os/src/android/os/cts/SeccompTest.java
@@ -51,9 +51,6 @@
}
public void testSeccomp() {
- if (CpuFeatures.isArm64Cpu() || CpuFeatures.isArm64CpuIn32BitMode()) {
- return; // seccomp not yet supported on arm64
- }
if (OSFeatures.needsSeccompSupport()) {
assertTrue("Please enable seccomp support "
+ "in your kernel (CONFIG_SECCOMP_FILTER=y)",
diff --git a/tests/tests/os/src/android/os/cts/StrictModeTest.java b/tests/tests/os/src/android/os/cts/StrictModeTest.java
index 7ebe817..d63f8bc 100644
--- a/tests/tests/os/src/android/os/cts/StrictModeTest.java
+++ b/tests/tests/os/src/android/os/cts/StrictModeTest.java
@@ -84,7 +84,7 @@
}
private String readLogSince(long millis) throws Exception {
- final SimpleDateFormat format = new SimpleDateFormat("MM-DD HH:mm:ss.SSS");
+ final SimpleDateFormat format = new SimpleDateFormat("MM-dd HH:mm:ss.SSS");
final Process proc = new ProcessBuilder("logcat", "-t", format.format(new Date(millis)))
.redirectErrorStream(true).start();
diff --git a/tests/tests/permission2/src/android/permission2/cts/NoWriteSecureSettingsPermissionTest.java b/tests/tests/permission2/src/android/permission2/cts/NoWriteSecureSettingsPermissionTest.java
index eb476c7..b46c45d 100644
--- a/tests/tests/permission2/src/android/permission2/cts/NoWriteSecureSettingsPermissionTest.java
+++ b/tests/tests/permission2/src/android/permission2/cts/NoWriteSecureSettingsPermissionTest.java
@@ -16,6 +16,9 @@
package android.permission2.cts;
+import android.Manifest;
+import android.content.ContentValues;
+import android.provider.Settings;
import android.test.AndroidTestCase;
/**
@@ -30,7 +33,15 @@
* {@link android.Manifest.permission#WRITE_SECURE_SETTINGS}
*/
public void testWriteSecureSettings() {
- assertWritingContentUriRequiresPermission(android.provider.Settings.Secure.CONTENT_URI,
- android.Manifest.permission.WRITE_SECURE_SETTINGS);
+ try {
+ ContentValues values = new ContentValues();
+ values.put(Settings.Secure.NAME, Settings.Secure.ACCESSIBILITY_ENABLED);
+ values.put(Settings.Secure.VALUE, Boolean.TRUE);
+ getContext().getContentResolver().insert(Settings.Secure.CONTENT_URI, values);
+ fail("expected SecurityException requiring "
+ + Manifest.permission.WRITE_SECURE_SETTINGS);
+ } catch (SecurityException expected) {
+ /* do nothing */
+ }
}
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/BNNMTest.java b/tests/tests/renderscript/src/android/renderscript/cts/BNNMTest.java
index b36175c..1822f6a 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/BNNMTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/BNNMTest.java
@@ -338,4 +338,163 @@
assertTrue(testWithTolerance(c_byte, c_byte_output));
}
+
+ // This test multiplies matrices where the results are expected to fall
+ // slightly outside the 0 to 255 valid output range. This test ensures the
+ // values get clamped to that range, rather than wrapping around.
+ public void testClamping() {
+ // The A matrix is:
+ // | 1 | 4 |
+ // | 2 | 5 |
+ // | 3 | 6 |
+ byte[] a_data = unsignedToSignedByte(new int[] {
+ 1, 2, 3,
+ 4, 5, 6,
+ });
+ final int a_rows = 3;
+ final int a_cols = 2;
+ final int a_offset = 0;
+ // The B matrix is:
+ // | -1 | -2 | -3 | -4 |
+ // | -5 | -6 | -7 | -8 |
+ // | 99 | -40 | -11 | -15 |
+ byte[] b_data = unsignedToSignedByte(new int[] {
+ 126, 122, 226,
+ 125, 121, 87,
+ 124, 120, 116,
+ 123, 119, 112,
+ });
+ final int b_cols = 4;
+ final int b_offset = 127;
+ // EightBitGemm implements C = B.transposed() * A,
+ // so we expect to get these results:
+ // 1*-1 + 2*-5 + 3* 99 + 128 = 414 (clamped to 255)
+ // 1*-2 + 2*-6 + 3*-40 + 128 = -6 (clamped to 0)
+ // 1*-3 + 2*-7 + 3*-11 + 128 = 78
+ // 1*-4 + 2*-8 + 3*-15 + 128 = 63
+ // 4*-1 + 5*-5 + 6* 99 + 128 = 693 (clamped to 255)
+ // 4*-2 + 5*-6 + 6*-40 + 128 = -150 (clamped to 0)
+ // 4*-3 + 5*-7 + 6*-11 + 128 = 15
+ // 4*-4 + 5*-8 + 6*-15 + 128 = -18 (clamped to 0)
+ // | 255 | 255 |
+ // | 0 | 0 |
+ // | 78 | 15 |
+ // | 63 | 0 |
+ final int c_offset = 128;
+ final int c_shift = 21;
+ final int c_mult_int = (1 << c_shift);
+ byte[] expected_data = unsignedToSignedByte(new int[] {
+ 255, 0, 78, 63,
+ 255, 0, 15, 0,
+ });
+
+ final int m = a_cols;
+ final int n = b_cols;
+ final int k = a_rows;
+
+ byte[] c_byte_output = runBNNM(m, n, k, a_data, a_offset, b_data, b_offset,
+ c_offset, c_mult_int);
+ assertTrue(testWithTolerance(expected_data, c_byte_output));
+ }
+
+ // This tests the exception handling for a_offset and b_offset.
+ public void testExceptionHandling() {
+ // The A matrix is:
+ // | 1 | 4 |
+ // | 2 | 5 |
+ // | 3 | 6 |
+ byte[] a_data = unsignedToSignedByte(new int[] {
+ 1, 2, 3,
+ 4, 5, 6,
+ });
+ final int a_rows = 3;
+ final int a_cols = 2;
+ // The B matrix is:
+ // | -1 | -2 | -3 | -4 |
+ // | -5 | -6 | -7 | -8 |
+ // | -9 | -10 | -11 | -12 |
+ byte[] b_data = unsignedToSignedByte(new int[] {
+ 11, 7, 3,
+ 10, 6, 2,
+ 9, 5, 1,
+ 8, 4, 0,
+ });
+ final int b_cols = 4;
+ // EightBitGemm implements C = B.transposed() * A,
+ // so we expect to get these results:
+ // 1*-1 + 2*-5 + 3*-9 + 128 = 90
+ // 1*-2 + 2*-6 + 3*-10 + 128 = 84
+ // 1*-3 + 2*-7 + 3*-11 + 128 = 78
+ // 1*-4 + 2*-8 + 3*-12 + 128 = 72
+ // 4*-1 + 5*-5 + 6*-9 + 128 = 45
+ // 4*-2 + 5*-6 + 6*-10 + 128 = 30
+ // 4*-3 + 5*-7 + 6*-11 + 128 = 15
+ // 4*-4 + 5*-8 + 6*-12 + 128 = 0
+ // | 90 | 45 |
+ // | 84 | 30 |
+ // | 78 | 15 |
+ // | 72 | 0 |
+ final int c_offset = 128;
+ final int c_shift = 21;
+ final int c_mult_int = (1 << c_shift);
+ byte[] expected_data = unsignedToSignedByte(new int[] {
+ 90, 84, 78, 72,
+ 45, 30, 15, 0,
+ });
+
+ final int m = a_cols;
+ final int n = b_cols;
+ final int k = a_rows;
+
+ Allocation A, B, C;
+ Type.Builder builder = new Type.Builder(mRS, Element.U8(mRS));
+ Type a_type = builder.setX(k).setY(m).create();
+ Type b_type = builder.setX(k).setY(n).create();
+ Type c_type = builder.setX(n).setY(m).create();
+
+ A = Allocation.createTyped(mRS, a_type);
+ B = Allocation.createTyped(mRS, b_type);
+ C = Allocation.createTyped(mRS, c_type);
+
+ A.copyFrom(a_data);
+ B.copyFrom(b_data);
+ // C doesn't matter, is output only
+
+ ScriptIntrinsicBLAS blas = ScriptIntrinsicBLAS.create(mRS);
+ try {
+ int a_offset = 0;
+ int b_offset = 12;
+ blas.BNNM(A, a_offset, B, b_offset, C, c_offset, c_mult_int);
+ } catch (RSRuntimeException e) {
+ fail("should NOT throw RSRuntimeException for valid offsets");
+ }
+ try {
+ int a_offset = -23;
+ int b_offset = 12;
+ blas.BNNM(A, a_offset, B, b_offset, C, c_offset, c_mult_int);
+ fail("should throw RSRuntimeException for invalid offsets: a_offset < 0");
+ } catch (RSRuntimeException e) {
+ }
+ try {
+ int a_offset = 888;
+ int b_offset = 12;
+ blas.BNNM(A, a_offset, B, b_offset, C, c_offset, c_mult_int);
+ fail("should throw RSRuntimeException for invalid offsets: a_offset > 255");
+ } catch (RSRuntimeException e) {
+ }
+ try {
+ int a_offset = 0;
+ int b_offset = -1;
+ blas.BNNM(A, a_offset, B, b_offset, C, c_offset, c_mult_int);
+ fail("should throw RSRuntimeException for invalid offsets: b_offset < 0");
+ } catch (RSRuntimeException e) {
+ }
+ try {
+ int a_offset = 0;
+ int b_offset = 256;
+ blas.BNNM(A, a_offset, B, b_offset, C, c_offset, c_mult_int);
+ fail("should throw RSRuntimeException for invalid offsets: b_offset > 255");
+ } catch (RSRuntimeException e) {
+ }
+ }
}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java b/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
index 5cdfe95..e162d1d 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
+++ b/tests/tests/renderscript/src/android/renderscript/cts/YuvTest.java
@@ -97,11 +97,11 @@
Allocation ta = Allocation.createSized(mRS, Element.U8(mRS), tmp.length);
ta.copyFrom(tmp);
-
ScriptIntrinsicYuvToRGB syuv = ScriptIntrinsicYuvToRGB.create(mRS, Element.U8(mRS));
syuv.setInput(ta);
syuv.forEach(aout);
-
+
+ mVerify.set_gAllowedIntError(2); // this will allow for less strict implementation
ScriptC_yuv script = new ScriptC_yuv(mRS);
script.invoke_makeRef(ay, au, av, aref);
@@ -141,7 +141,8 @@
}
ta.copyFrom(tmp);
script.invoke_makeRef(ay, au, av, aref);
-
+
+ mVerify.set_gAllowedIntError(2); // this will allow for less strict implementation
syuv.setInput(ta);
syuv.forEach(aout);
mVerify.invoke_verify(aref, aout, ay);
@@ -186,6 +187,7 @@
}
ta.copyFrom(tmp);
script.invoke_makeRef(ay, au, av, aref);
+ mVerify.set_gAllowedIntError(2); // this will allow for less strict implementation
syuv.setInput(ta);
syuv.forEach(aout);
@@ -236,6 +238,7 @@
mVerify.invoke_verify(aref, aout, ay);
mRS.finish();
+ mVerify.set_gAllowedFloatError(0.01f); // this will allow for less strict implementation
mVerify.invoke_checkError();
waitForMessage();
checkForErrors();
@@ -266,11 +269,13 @@
tmp[i++] = bv[j];
tmp[i++] = bu[j];
}
+
ta.copyFrom(tmp);
script.invoke_makeRef_f4(ay, au, av, aref);
script.set_mInput(ta);
script.forEach_cvt_f4(aout);
+ mVerify.set_gAllowedFloatError(0.01f); // this will allow for less strict implementation
mVerify.invoke_verify(aref, aout, ay);
mRS.finish();
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/verify.rs b/tests/tests/renderscript/src/android/renderscript/cts/verify.rs
index f6d625b..c3ae802 100644
--- a/tests/tests/renderscript/src/android/renderscript/cts/verify.rs
+++ b/tests/tests/renderscript/src/android/renderscript/cts/verify.rs
@@ -17,13 +17,14 @@
#include "shared.rsh"
int gAllowedIntError = 0;
-double gAllowedFloatError = 0.00000001;
-double gAllowedDoubleError = 0.00000000001;
+float gAllowedFloatError = 0.0001f;
+double gAllowedFloatMatError = 0.00000001;
+double gAllowedDoubleMatError = 0.00000000001;
static bool hadError = false;
static int2 errorLoc = {0,0};
static bool compare_float(float f1, float f2) {
- if (fabs(f1-f2) > 0.0001f) {
+ if (fabs(f1-f2) > gAllowedFloatError) {
hadError = true;
return false;
}
@@ -316,7 +317,7 @@
float2 pref = rsGetElementAt_float2(in1, x, y);
float2 ptst = rsGetElementAt_float2(in2, x, y);
double absErr = (pref.x - ptst.x) * (pref.x - ptst.x) + (pref.y - ptst.y) * (pref.y - ptst.y);
- if (absErr > l2Norm * gAllowedFloatError) {
+ if (absErr > l2Norm * gAllowedFloatMatError) {
errorLoc.x = x;
errorLoc.y = y;
hadError = true;
@@ -340,7 +341,7 @@
float pref = rsGetElementAt_float(in1, x, y);
float ptst = rsGetElementAt_float(in2, x, y);
double absErr = (pref - ptst) * (pref - ptst);
- if (absErr > l2Norm * gAllowedFloatError) {
+ if (absErr > l2Norm * gAllowedFloatMatError) {
errorLoc.x = x;
errorLoc.y = y;
hadError = true;
@@ -364,7 +365,7 @@
double2 pref = rsGetElementAt_double2(in1, x, y);
double2 ptst = rsGetElementAt_double2(in2, x, y);
double absErr = (pref.x - ptst.x) * (pref.x - ptst.x) + (pref.y - ptst.y) * (pref.y - ptst.y);
- if (absErr > l2Norm * gAllowedDoubleError) {
+ if (absErr > l2Norm * gAllowedDoubleMatError) {
errorLoc.x = x;
errorLoc.y = y;
hadError = true;
@@ -388,7 +389,7 @@
double pref = rsGetElementAt_double(in1, x, y);
double ptst = rsGetElementAt_double(in2, x, y);
double absErr = (pref - ptst) * (pref - ptst);
- if (absErr > l2Norm * gAllowedDoubleError) {
+ if (absErr > l2Norm * gAllowedDoubleMatError) {
errorLoc.x = x;
errorLoc.y = y;
hadError = true;
diff --git a/tests/tests/text/src/android/text/format/cts/FormatterTest.java b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
index bf4a684..9c3c45d 100644
--- a/tests/tests/text/src/android/text/format/cts/FormatterTest.java
+++ b/tests/tests/text/src/android/text/format/cts/FormatterTest.java
@@ -32,24 +32,24 @@
BigDecimal bd = new BigDecimal((long) 1024, mc);
// test different long values with various length
- assertEquals("0.00B", Formatter.formatFileSize(mContext, 0));
+ assertEquals("0.00 B", Formatter.formatFileSize(mContext, 0));
- assertEquals("899B", Formatter.formatFileSize(mContext, 899));
+ assertEquals("899 B", Formatter.formatFileSize(mContext, 899));
- assertEquals("1.00KB", Formatter.formatFileSize(mContext, bd.pow(1).longValue()));
+ assertEquals("1.00 KB", Formatter.formatFileSize(mContext, bd.pow(1).longValue()));
- assertEquals("1.00MB", Formatter.formatFileSize(mContext, bd.pow(2).longValue()));
+ assertEquals("1.00 MB", Formatter.formatFileSize(mContext, bd.pow(2).longValue()));
- assertEquals("1.00GB", Formatter.formatFileSize(mContext, bd.pow(3).longValue()));
+ assertEquals("1.00 GB", Formatter.formatFileSize(mContext, bd.pow(3).longValue()));
- assertEquals("1.00TB", Formatter.formatFileSize(mContext, bd.pow(4).longValue()));
+ assertEquals("1.00 TB", Formatter.formatFileSize(mContext, bd.pow(4).longValue()));
- assertEquals("1.00PB", Formatter.formatFileSize(mContext, bd.pow(5).longValue()));
+ assertEquals("1.00 PB", Formatter.formatFileSize(mContext, bd.pow(5).longValue()));
- assertEquals("1024PB", Formatter.formatFileSize(mContext, bd.pow(6).longValue()));
+ assertEquals("1024 PB", Formatter.formatFileSize(mContext, bd.pow(6).longValue()));
// test Negative value
- assertEquals("-1.00B", Formatter.formatFileSize(mContext, -1));
+ assertEquals("-1.00 B", Formatter.formatFileSize(mContext, -1));
}
public void testFormatIpAddress() {
diff --git a/tests/tests/view/src/android/view/cts/ActionModeTest.java b/tests/tests/view/src/android/view/cts/ActionModeTest.java
index 6898d3c..534db311 100644
--- a/tests/tests/view/src/android/view/cts/ActionModeTest.java
+++ b/tests/tests/view/src/android/view/cts/ActionModeTest.java
@@ -67,6 +67,57 @@
assertTrue(callback.mIsOnGetContentRectCalled);
}
+ public void testSetAndGetTitleOptionalHint() {
+ MockActionMode actionMode = new MockActionMode();
+
+ // Check default value.
+ assertFalse(actionMode.getTitleOptionalHint());
+ // Test set and get.
+ actionMode.setTitleOptionalHint(true);
+ assertTrue(actionMode.getTitleOptionalHint());
+ actionMode.setTitleOptionalHint(false);
+ assertFalse(actionMode.getTitleOptionalHint());
+ }
+
+ public void testSetAndGetTag() {
+ MockActionMode actionMode = new MockActionMode();
+ Object tag = new Object();
+
+ // Check default value.
+ assertNull(actionMode.getTag());
+
+ actionMode.setTag(tag);
+ assertSame(tag, actionMode.getTag());
+ }
+
+ public void testIsTitleOptional() {
+ MockActionMode actionMode = new MockActionMode();
+
+ // Check default value.
+ assertFalse(actionMode.isTitleOptional());
+ }
+
+ public void testIsUiFocusable() {
+ MockActionMode actionMode = new MockActionMode();
+
+ // Check default value.
+ assertTrue(actionMode.isUiFocusable());
+ }
+
+ public void testHide() {
+ MockActionMode actionMode = new MockActionMode();
+
+ actionMode.hide(0);
+ actionMode.hide(ActionMode.DEFAULT_HIDE_DURATION);
+ }
+
+ public void testOnWindowFocusChanged() {
+ MockActionMode actionMode = new MockActionMode();
+
+ actionMode.onWindowFocusChanged(true);
+ actionMode.onWindowFocusChanged(false);
+ }
+
private static class MockActionModeCallback2 extends ActionMode.Callback2 {
boolean mIsOnGetContentRectCalled = false;
diff --git a/tests/tests/view/src/android/view/cts/MockTextView.java b/tests/tests/view/src/android/view/cts/MockTextView.java
index dc9420d..0c73614 100644
--- a/tests/tests/view/src/android/view/cts/MockTextView.java
+++ b/tests/tests/view/src/android/view/cts/MockTextView.java
@@ -179,19 +179,19 @@
}
public int getFrameLeft() {
- return mLeft;
+ return getLeft();
}
public int getFrameTop() {
- return mTop;
+ return getTop();
}
public int getFrameRight() {
- return mRight;
+ return getRight();
}
public int getFrameBottom() {
- return mBottom;
+ return getBottom();
}
public int getBottomPaddingOffset() {
diff --git a/tests/tests/view/src/android/view/cts/MockView.java b/tests/tests/view/src/android/view/cts/MockView.java
index 9093089..579d4fb 100644
--- a/tests/tests/view/src/android/view/cts/MockView.java
+++ b/tests/tests/view/src/android/view/cts/MockView.java
@@ -29,6 +29,7 @@
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.ContextMenu.ContextMenuInfo;
@@ -104,8 +105,8 @@
return mCalledInvalidate;
}
- public void setParent(ViewParent parent) {
- mParent = parent;
+ public void setParent(ViewGroup parent) {
+ parent.addView(this);
}
public static int[] getEnabledStateSet() {
@@ -162,16 +163,6 @@
}
@Override
- protected void initializeFadingEdge(TypedArray a) {
- super.initializeFadingEdge(a);
- }
-
- @Override
- protected void initializeScrollbars(TypedArray a) {
- super.initializeScrollbars(a);
- }
-
- @Override
protected int getHorizontalScrollbarHeight() {
return super.getHorizontalScrollbarHeight();
}
diff --git a/tests/tests/view/src/android/view/cts/ViewConfigurationTest.java b/tests/tests/view/src/android/view/cts/ViewConfigurationTest.java
index 38e9130..5fe4aea 100644
--- a/tests/tests/view/src/android/view/cts/ViewConfigurationTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewConfigurationTest.java
@@ -42,11 +42,17 @@
ViewConfiguration.getTouchSlop();
ViewConfiguration.getWindowTouchSlop();
ViewConfiguration.getMinimumFlingVelocity();
+ ViewConfiguration.getMaximumFlingVelocity();
ViewConfiguration.getMaximumDrawingCacheSize();
ViewConfiguration.getZoomControlsTimeout();
ViewConfiguration.getGlobalActionKeyTimeout();
ViewConfiguration.getScrollFriction();
+ ViewConfiguration.getScrollBarFadeDuration();
+ ViewConfiguration.getScrollDefaultDelay();
ViewConfiguration.getDoubleTapTimeout();
+ ViewConfiguration.getKeyRepeatTimeout();
+ ViewConfiguration.getKeyRepeatDelay();
+ ViewConfiguration.getDefaultActionModeHideDuration();
}
@SuppressWarnings("deprecation")
@@ -61,9 +67,14 @@
vc.getScaledEdgeSlop();
vc.getScaledFadingEdgeLength();
vc.getScaledMaximumDrawingCacheSize();
+ vc.getScaledMaximumFlingVelocity();
vc.getScaledMinimumFlingVelocity();
+ vc.getScaledOverflingDistance();
+ vc.getScaledOverscrollDistance();
+ vc.getScaledPagingTouchSlop();
vc.getScaledScrollBarSize();
vc.getScaledTouchSlop();
vc.getScaledWindowTouchSlop();
+ vc.hasPermanentMenuKey();
}
}
diff --git a/tests/tests/view/src/android/view/cts/ViewGroupTest.java b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
index b98ac4e..7fc5579 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroupTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroupTest.java
@@ -458,7 +458,7 @@
MockTextView textView = new MockTextView(mContext);
vg.addView(textView);
vg.requestChildFocus(textView, null);
- textView.setFrame(1, 1, 100, 100);
+ textView.layout(1, 1, 100, 100);
assertTrue(vg.dispatchKeyEvent(event));
}
@@ -529,7 +529,7 @@
d.getMetrics(metrics);
int screenWidth = metrics.widthPixels;
int screenHeight = metrics.heightPixels;
- vg.setFrame(0, 0, screenWidth, screenHeight);
+ vg.layout(0, 0, screenWidth, screenHeight);
vg.setLayoutParams(new ViewGroup.LayoutParams(screenWidth, screenHeight));
MockTextView textView = new MockTextView(mContext);
@@ -554,7 +554,7 @@
assertFalse(vg.dispatchTouchEvent(me));
assertNull(mMotionEvent);
- textView.setFrame(0, 0, screenWidth, screenHeight);
+ textView.layout(0, 0, screenWidth, screenHeight);
assertTrue(vg.dispatchTouchEvent(me));
assertSame(me, mMotionEvent);
}
@@ -568,7 +568,7 @@
MockTextView textView = new MockTextView(mContext);
vg.addView(textView);
- textView.setFrame(1, 1, 100, 100);
+ textView.layout(1, 1, 100, 100);
vg.requestChildFocus(textView, null);
assertTrue(vg.dispatchTrackballEvent(me));
}
@@ -579,7 +579,7 @@
assertFalse(vg.dispatchUnhandledMove(textView, View.FOCUS_DOWN));
vg.addView(textView);
- textView.setFrame(1, 1, 100, 100);
+ textView.layout(1, 1, 100, 100);
vg.requestChildFocus(textView, null);
assertTrue(vg.dispatchUnhandledMove(textView, View.FOCUS_DOWN));
}
@@ -769,13 +769,13 @@
MockViewGroup vg = new MockViewGroup(mContext);
MockTextView textView = new MockTextView(mContext);
- textView.setFrame(1, 1, 100, 100);
+ textView.layout(1, 1, 100, 100);
Rect rect = new Rect(1, 1, 50, 50);
Point p = new Point();
assertFalse(vg.getChildVisibleRect(textView, rect, p));
- textView.setFrame(0, 0, 0, 0);
- vg.setFrame(20, 20, 60, 60);
+ textView.layout(0, 0, 0, 0);
+ vg.layout(20, 20, 60, 60);
rect = new Rect(10, 10, 40, 40);
p = new Point();
assertTrue(vg.getChildVisibleRect(textView, rect, p));
@@ -998,7 +998,7 @@
// expected
}
vg.addView(textView);
- textView.setFrame(1, 2, 3, 4);
+ textView.layout(1, 2, 3, 4);
Rect rect = new Rect();
vg.offsetDescendantRectToMyCoords(textView, rect);
assertEquals(2, rect.bottom);
@@ -1009,7 +1009,7 @@
public void testOffsetRectIntoDescendantCoords() {
MockViewGroup vg = new MockViewGroup(mContext);
- vg.setFrame(10, 20, 30, 40);
+ vg.layout(10, 20, 30, 40);
MockTextView textView = new MockTextView(mContext);
try {
@@ -1019,7 +1019,7 @@
} catch (RuntimeException e) {
// expected
}
- textView.setFrame(1, 2, 3, 4);
+ textView.layout(1, 2, 3, 4);
vg.addView(textView);
Rect rect = new Rect(5, 6, 7, 8);
@@ -1376,8 +1376,8 @@
MockViewGroup vg = new MockViewGroup(mContext);
MockTextView textView = new MockTextView(mContext);
- textView.setFrame(1, 2, 30, 40);
- vg.setFrame(1, 1, 100, 200);
+ textView.layout(1, 2, 30, 40);
+ vg.layout(1, 1, 100, 200);
vg.setClipChildren(true);
MockCanvas canvas = new MockCanvas(bitmap);
@@ -1434,7 +1434,7 @@
final int frameRight = 100;
final int frameBottom = 200;
MockViewGroup vg = new MockViewGroup(mContext);
- vg.setFrame(frameLeft, frameTop, frameRight, frameBottom);
+ vg.layout(frameLeft, frameTop, frameRight, frameBottom);
vg.setClipToPadding(true);
MockCanvas canvas = new MockCanvas();
@@ -1911,11 +1911,6 @@
}
@Override
- public boolean setFrame(int l, int t, int r, int b) {
- return super.setFrame(l, t, r, b);
- }
-
- @Override
public void dispatchRestoreInstanceState(
SparseArray<Parcelable> container) {
isDispatchRestoreInstanceStateCalled = true;
@@ -1945,11 +1940,6 @@
}
@Override
- public boolean gatherTransparentRegion(Region region) {
- return false;
- }
-
- @Override
public boolean dispatchTouchEvent(MotionEvent event) {
super.dispatchTouchEvent(event);
return true;
@@ -2162,21 +2152,6 @@
}
@Override
- public boolean setFrame(int left, int top, int right, int bottom) {
- return super.setFrame(left, top, right, bottom);
- }
-
- @Override
- public boolean isChildrenDrawnWithCacheEnabled() {
- return super.isChildrenDrawnWithCacheEnabled();
- }
-
- @Override
- public void setChildrenDrawnWithCacheEnabled(boolean enabled) {
- super.setChildrenDrawnWithCacheEnabled(enabled);
- }
-
- @Override
public void measureChild(View child, int parentWidthMeasureSpec,
int parentHeightMeasureSpec) {
measureChildCalledTime++;
@@ -2327,6 +2302,21 @@
super.resetResolvedDrawables();
resetResolvedDrawablesCount++;
}
+
+ @Override
+ public boolean setFrame(int left, int top, int right, int bottom) {
+ return super.setFrame(left, top, right, bottom);
+ }
+
+ @Override
+ public void setChildrenDrawnWithCacheEnabled(boolean enabled) {
+ super.setChildrenDrawnWithCacheEnabled(enabled);
+ }
+
+ @Override
+ public boolean isChildrenDrawnWithCacheEnabled() {
+ return super.isChildrenDrawnWithCacheEnabled();
+ }
}
static class MockView2 extends View {
diff --git a/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java b/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java
index e837b23..e53cba2 100644
--- a/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewGroup_MarginLayoutParamsTest.java
@@ -89,7 +89,9 @@
public void testSetMarginsRelative() {
// create a new MarginLayoutParams instance
mMarginLayoutParams = new ViewGroup.MarginLayoutParams(320, 480);
- mMarginLayoutParams.setMarginsRelative(20, 30, 120, 140);
+ mMarginLayoutParams.setMargins(0, 30, 0, 140);
+ mMarginLayoutParams.setMarginStart(20);
+ mMarginLayoutParams.setMarginEnd(120);
assertEquals(20, mMarginLayoutParams.getMarginStart());
assertEquals(30, mMarginLayoutParams.topMargin);
assertEquals(120, mMarginLayoutParams.getMarginEnd());
@@ -120,7 +122,9 @@
assertEquals(false, mMarginLayoutParams.isMarginRelative());
// LTR / relative margin case
- mMarginLayoutParams.setMarginsRelative(20, 30, 120, 140);
+ mMarginLayoutParams.setMargins(0, 30, 0, 140);
+ mMarginLayoutParams.setMarginStart(20);
+ mMarginLayoutParams.setMarginEnd(120);
vg.setLayoutParams(mMarginLayoutParams);
assertEquals(20, mMarginLayoutParams.getMarginStart());
@@ -151,7 +155,9 @@
assertEquals(false, mMarginLayoutParams.isMarginRelative());
// RTL / relative margin case
- mMarginLayoutParams.setMarginsRelative(20, 30, 120, 140);
+ mMarginLayoutParams.setMargins(0, 30, 0, 140);
+ mMarginLayoutParams.setMarginStart(20);
+ mMarginLayoutParams.setMarginEnd(120);
vg.setLayoutParams(mMarginLayoutParams);
assertEquals(20, mMarginLayoutParams.getMarginStart());
diff --git a/tests/tests/view/src/android/view/cts/ViewTest.java b/tests/tests/view/src/android/view/cts/ViewTest.java
index ea289ce..e95236d 100644
--- a/tests/tests/view/src/android/view/cts/ViewTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTest.java
@@ -16,28 +16,26 @@
package android.view.cts;
-import android.content.res.ColorStateList;
-import android.graphics.Canvas;
-import android.graphics.Color;
-import android.graphics.ColorFilter;
-import android.graphics.PorterDuff;
-
-import android.os.Bundle;
import com.android.cts.view.R;
import com.android.internal.view.menu.ContextMenuBuilder;
-import com.google.android.collect.Lists;
import android.content.Context;
+import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.content.res.XmlResourceParser;
import android.cts.util.PollingCheck;
import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.ColorFilter;
import android.graphics.Point;
+import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.StateListDrawable;
+import android.os.Bundle;
import android.os.Parcelable;
import android.os.SystemClock;
import android.os.Vibrator;
@@ -64,6 +62,7 @@
import android.view.View;
import android.view.View.BaseSavedState;
import android.view.View.OnClickListener;
+import android.view.View.OnContextClickListener;
import android.view.View.OnCreateContextMenuListener;
import android.view.View.OnFocusChangeListener;
import android.view.View.OnKeyListener;
@@ -425,7 +424,9 @@
assertTrue(view.isLayoutRequested());
view.setParent(mMockParent);
- assertFalse(mMockParent.hasRequestLayout());
+ assertTrue(mMockParent.hasRequestLayout());
+
+ mMockParent.reset();
view.requestLayout();
assertTrue(view.isLayoutRequested());
assertTrue(mMockParent.hasRequestLayout());
@@ -883,6 +884,18 @@
assertFalse(view.isClickable());
}
+ public void testAccessContextClickable() {
+ View view = new View(mActivity);
+
+ assertFalse(view.isContextClickable());
+
+ view.setContextClickable(true);
+ assertTrue(view.isContextClickable());
+
+ view.setContextClickable(false);
+ assertFalse(view.isContextClickable());
+ }
+
public void testGetContextMenuInfo() {
MockView view = new MockView(mActivity);
@@ -1131,8 +1144,8 @@
mockView2.setParent(mMockParent);
mMockParent.dispatchSetSelected(true);
- assertFalse(mockView1.isSelected());
- assertFalse(mockView2.isSelected());
+ assertTrue(mockView1.isSelected());
+ assertTrue(mockView2.isSelected());
mMockParent.dispatchSetSelected(false);
assertFalse(mockView1.isSelected());
@@ -1155,8 +1168,8 @@
mockView2.setParent(mMockParent);
mMockParent.dispatchSetPressed(true);
- assertFalse(mockView1.isPressed());
- assertFalse(mockView2.isPressed());
+ assertTrue(mockView1.isPressed());
+ assertTrue(mockView2.isPressed());
mMockParent.dispatchSetPressed(false);
assertFalse(mockView1.isPressed());
@@ -1317,6 +1330,30 @@
assertTrue(view.isLongClickable());
}
+ public void testPerformContextClick() {
+ MockView view = new MockView(mActivity);
+ view.setParent(mMockParent);
+ OnContextClickListenerImpl listener = new OnContextClickListenerImpl();
+
+ view.setOnContextClickListener(listener);
+ assertFalse(listener.hasOnContextClick());
+
+ assertTrue(view.performContextClick());
+ assertTrue(listener.hasOnContextClick());
+ }
+
+ public void testSetOnContextClickListener() {
+ MockView view = new MockView(mActivity);
+ view.setParent(mMockParent);
+
+ assertFalse(view.performContextClick());
+ assertFalse(view.isContextClickable());
+
+ view.setOnContextClickListener(new OnContextClickListenerImpl());
+ assertTrue(view.performContextClick());
+ assertTrue(view.isContextClickable());
+ }
+
public void testAccessOnFocusChangeListener() {
View view = new View(mActivity);
OnFocusChangeListener listener = new OnFocusChangeListenerImpl();
@@ -3017,16 +3054,13 @@
@UiThreadTest
public void testScrollbarStyle() {
- MockView view = (MockView) mActivity.findViewById(R.id.mock_view);
+ MockView view = (MockView) mActivity.findViewById(R.id.scroll_view);
Bitmap bitmap = Bitmap.createBitmap(200, 300, Bitmap.Config.RGB_565);
BitmapDrawable d = new BitmapDrawable(bitmap);
view.setBackgroundDrawable(d);
view.setHorizontalFadingEdgeEnabled(true);
view.setVerticalFadingEdgeEnabled(true);
- view.setHorizontalScrollBarEnabled(true);
- view.setVerticalScrollBarEnabled(true);
- view.initializeScrollbars(mActivity.obtainStyledAttributes(android.R.styleable.View));
assertTrue(view.isHorizontalScrollBarEnabled());
assertTrue(view.isVerticalScrollBarEnabled());
int verticalScrollBarWidth = view.getVerticalScrollbarWidth();
@@ -3156,7 +3190,10 @@
public void testOnStartAndFinishTemporaryDetach() throws Throwable {
final MockListView listView = new MockListView(mActivity);
- List<String> items = Lists.newArrayList("1", "2", "3");
+ List<String> items = new ArrayList<>();
+ items.add("1");
+ items.add("2");
+ items.add("3");
final Adapter<String> adapter = new Adapter<String>(mActivity, 0, items);
runTestOnUiThread(new Runnable() {
@@ -3638,7 +3675,7 @@
}
}
- private final static class MockViewParent extends View implements ViewParent {
+ private final static class MockViewParent extends ViewGroup {
private boolean mHasClearChildFocus = false;
private boolean mHasRequestLayout = false;
private boolean mHasCreateContextMenu = false;
@@ -3678,12 +3715,12 @@
}
@Override
- protected void dispatchSetPressed(boolean pressed) {
+ public void dispatchSetPressed(boolean pressed) {
super.dispatchSetPressed(pressed);
}
@Override
- protected void dispatchSetSelected(boolean selected) {
+ public void dispatchSetSelected(boolean selected) {
super.dispatchSetSelected(selected);
}
@@ -3716,23 +3753,15 @@
return false;
}
+ @Override
+ protected void onLayout(boolean changed, int l, int t, int r, int b) {
+
+ }
+
public boolean hasGetChildVisibleRect() {
return mHasGetChildVisibleRect;
}
- public void invalidateChild(View child, Rect r) {
- mTempRect = new Rect(r);
- mHasInvalidateChild = true;
- }
-
- public Rect getTempRect() {
- return mTempRect;
- }
-
- public boolean hasInvalidateChild() {
- return mHasInvalidateChild;
- }
-
public ViewParent invalidateChildInParent(int[] location, Rect r) {
return null;
}
@@ -3753,6 +3782,7 @@
}
+ @Override
public void requestLayout() {
mHasRequestLayout = true;
}
@@ -3955,6 +3985,23 @@
}
}
+ private static final class OnContextClickListenerImpl implements OnContextClickListener {
+ private boolean mHasContextClick = false;
+
+ public boolean hasOnContextClick() {
+ return mHasContextClick;
+ }
+
+ public void reset() {
+ mHasContextClick = false;
+ }
+
+ public boolean onContextClick(View v) {
+ mHasContextClick = true;
+ return true;
+ }
+ }
+
private static final class OnFocusChangeListenerImpl implements OnFocusChangeListener {
private boolean mHasOnFocusChange = false;
diff --git a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
index 1b21dac..7071808 100644
--- a/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
+++ b/tests/tests/view/src/android/view/cts/ViewTreeObserverTest.java
@@ -25,8 +25,6 @@
import android.test.TouchUtils;
import android.view.View;
import android.view.ViewTreeObserver;
-import android.view.ViewTreeObserver.InternalInsetsInfo;
-import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
import android.view.ViewTreeObserver.OnGlobalFocusChangeListener;
import android.view.ViewTreeObserver.OnGlobalLayoutListener;
import android.view.ViewTreeObserver.OnPreDrawListener;
@@ -155,22 +153,6 @@
}.run();
}
- public void testAddOnComputeInternalInsetsListener() {
- final View view1 = mActivity.findViewById(R.id.view1);
- mViewTreeObserver = view1.getViewTreeObserver();
-
- MockOnComputeInternalInsetsListener listener = new MockOnComputeInternalInsetsListener();
- mViewTreeObserver.addOnComputeInternalInsetsListener(listener);
- }
-
- public void testRemoveOnComputeInternalInsetsListener() {
- final View view1 = mActivity.findViewById(R.id.view1);
- mViewTreeObserver = view1.getViewTreeObserver();
-
- MockOnComputeInternalInsetsListener listener = new MockOnComputeInternalInsetsListener();
- mViewTreeObserver.removeOnComputeInternalInsetsListener(listener);
- }
-
public void testDispatchOnGlobalLayout() {
final LinearLayout layout = (LinearLayout) mActivity.findViewById(R.id.linearlayout);
mViewTreeObserver = layout.getViewTreeObserver();
@@ -434,12 +416,6 @@
}
}
- private class MockOnComputeInternalInsetsListener implements OnComputeInternalInsetsListener {
- @Override
- public void onComputeInternalInsets(InternalInsetsInfo inoutInfo) {
- }
- }
-
private static class MockOnScrollChangedListener implements OnScrollChangedListener {
private boolean mCalledOnScrollChanged = false;
diff --git a/tests/tests/view/src/android/view/cts/util/XmlUtils.java b/tests/tests/view/src/android/view/cts/util/XmlUtils.java
index 4a13d54..f1df4ff 100644
--- a/tests/tests/view/src/android/view/cts/util/XmlUtils.java
+++ b/tests/tests/view/src/android/view/cts/util/XmlUtils.java
@@ -16,8 +16,6 @@
package android.view.cts.util;
-import com.android.internal.util.FastXmlSerializer;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -44,7 +42,6 @@
import java.util.Map;
import java.util.Set;
-/** {@hide} */
public class XmlUtils {
public static void skipCurrentTag(XmlPullParser parser)
@@ -171,28 +168,6 @@
}
/**
- * Flatten a Map into an output stream as XML. The map can later be
- * read back with readMapXml().
- *
- * @param val The map to be flattened.
- * @param out Where to write the XML data.
- *
- * @see #writeMapXml(Map, String, XmlSerializer)
- * @see #writeListXml
- * @see #writeValueXml
- * @see #readMapXml
- */
- public static final void writeMapXml(Map val, OutputStream out)
- throws XmlPullParserException, IOException {
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(out, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- writeMapXml(val, null, serializer);
- serializer.endDocument();
- }
-
- /**
* Flatten a List into an output stream as XML. The list can later be
* read back with readListXml().
*
@@ -224,7 +199,6 @@
* none.
* @param out XmlSerializer to write the map into.
*
- * @see #writeMapXml(Map, OutputStream)
* @see #writeListXml
* @see #writeValueXml
* @see #readMapXml
@@ -244,7 +218,6 @@
* @param out XmlSerializer to write the map into.
* @param callback Method to call when an Object type is not recognized.
*
- * @see #writeMapXml(Map, OutputStream)
* @see #writeListXml
* @see #writeValueXml
* @see #readMapXml
@@ -278,7 +251,6 @@
* @param val The map to be flattened.
* @param out XmlSerializer to write the map into.
*
- * @see #writeMapXml(Map, OutputStream)
* @see #writeListXml
* @see #writeValueXml
* @see #readMapXml
diff --git a/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java
index 7fe016f..fc5eb08 100644
--- a/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/CursorAdapterTest.java
@@ -21,7 +21,6 @@
import android.content.Context;
import android.content.res.Resources.Theme;
import android.cts.util.PollingCheck;
-import android.cts.util.ReadElf;
import android.cts.util.TestThread;
import android.database.ContentObserver;
import android.database.Cursor;
@@ -40,7 +39,6 @@
import com.android.cts.widget.R;
-
/**
* Test {@link CursorAdapter}.
*/
@@ -347,14 +345,23 @@
}
private final class MockCursorAdapter extends CursorAdapter {
+ private final Context mContext;
+ private final boolean mAutoRequery;
+
private boolean mContentChanged = false;
public MockCursorAdapter(Context context, Cursor c) {
super(context, c);
+
+ mContext = context;
+ mAutoRequery = false;
}
public MockCursorAdapter(Context context, Cursor c, boolean autoRequery) {
super(context, c, autoRequery);
+
+ mContext = context;
+ mAutoRequery = autoRequery;
}
public Context getContext() {
diff --git a/tests/tests/widget/src/android/widget/cts/GalleryTest.java b/tests/tests/widget/src/android/widget/cts/GalleryTest.java
index 2813965..a3ac16c 100644
--- a/tests/tests/widget/src/android/widget/cts/GalleryTest.java
+++ b/tests/tests/widget/src/android/widget/cts/GalleryTest.java
@@ -17,16 +17,17 @@
package android.widget.cts;
import com.android.cts.widget.R;
-import com.android.internal.view.menu.ContextMenuBuilder;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import android.app.Activity;
import android.app.Instrumentation;
+import android.content.ComponentName;
import android.content.Context;
+import android.content.Intent;
import android.cts.util.WidgetTestUtils;
+import android.graphics.drawable.Drawable;
import android.os.SystemClock;
import android.test.ActivityInstrumentationTestCase2;
import android.test.UiThreadTest;
@@ -36,7 +37,8 @@
import android.view.ContextMenu;
import android.view.Gravity;
import android.view.KeyEvent;
-import android.view.MotionEvent;
+import android.view.MenuItem;
+import android.view.SubMenu;
import android.view.View;
import android.view.ViewGroup;
import android.view.ContextMenu.ContextMenuInfo;
@@ -311,11 +313,165 @@
MyGallery gallery = new MyGallery(mContext);
gallery.setOnCreateContextMenuListener(listener);
assertFalse(listener.hasCreatedContextMenu());
- gallery.createContextMenu(new ContextMenuBuilder(mContext));
+ gallery.createContextMenu(new MockContextMenu());
assertTrue(listener.hasCreatedContextMenu());
assertSame(gallery.getContextMenuInfo(), listener.getContextMenuInfo());
}
+ private static class MockContextMenu implements ContextMenu {
+ @Override
+ public ContextMenu setHeaderTitle(int titleRes) {
+ return null;
+ }
+
+ @Override
+ public ContextMenu setHeaderTitle(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public ContextMenu setHeaderIcon(int iconRes) {
+ return null;
+ }
+
+ @Override
+ public ContextMenu setHeaderIcon(Drawable icon) {
+ return null;
+ }
+
+ @Override
+ public ContextMenu setHeaderView(View view) {
+ return null;
+ }
+
+ @Override
+ public void clearHeader() {
+
+ }
+
+ @Override
+ public MenuItem add(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public MenuItem add(int titleRes) {
+ return null;
+ }
+
+ @Override
+ public MenuItem add(int groupId, int itemId, int order, CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public MenuItem add(int groupId, int itemId, int order, int titleRes) {
+ return null;
+ }
+
+ @Override
+ public SubMenu addSubMenu(CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public SubMenu addSubMenu(int titleRes) {
+ return null;
+ }
+
+ @Override
+ public SubMenu addSubMenu(int groupId, int itemId, int order,
+ CharSequence title) {
+ return null;
+ }
+
+ @Override
+ public SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes) {
+ return null;
+ }
+
+ @Override
+ public int addIntentOptions(int groupId, int itemId, int order, ComponentName caller,
+ Intent[] specifics, Intent intent, int flags, MenuItem[] outSpecificItems) {
+ return 0;
+ }
+
+ @Override
+ public void removeItem(int id) {
+
+ }
+
+ @Override
+ public void removeGroup(int groupId) {
+
+ }
+
+ @Override
+ public void clear() {
+
+ }
+
+ @Override
+ public void setGroupCheckable(int group, boolean checkable, boolean exclusive) {
+
+ }
+
+ @Override
+ public void setGroupVisible(int group, boolean visible) {
+
+ }
+
+ @Override
+ public void setGroupEnabled(int group, boolean enabled) {
+
+ }
+
+ @Override
+ public boolean hasVisibleItems() {
+ return false;
+ }
+
+ @Override
+ public MenuItem findItem(int id) {
+ return null;
+ }
+
+ @Override
+ public int size() {
+ return 0;
+ }
+
+ @Override
+ public MenuItem getItem(int index) {
+ return null;
+ }
+
+ @Override
+ public void close() {
+
+ }
+
+ @Override
+ public boolean performShortcut(int keyCode, KeyEvent event, int flags) {
+ return false;
+ }
+
+ @Override
+ public boolean isShortcutKey(int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ @Override
+ public boolean performIdentifierAction(int id, int flags) {
+ return false;
+ }
+
+ @Override
+ public void setQwertyMode(boolean isQwerty) {
+
+ }
+ }
+
private static class MockOnCreateContextMenuListener implements OnCreateContextMenuListener {
private boolean hasCreatedContextMenu;
private ContextMenuInfo mContextMenuInfo;
diff --git a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
index c93d4a1..aa1df34 100644
--- a/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ImageViewTest.java
@@ -38,6 +38,7 @@
import android.graphics.PorterDuff;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
import android.graphics.drawable.PaintDrawable;
import android.net.Uri;
import android.test.ActivityInstrumentationTestCase;
@@ -173,6 +174,23 @@
}
@UiThreadTest
+ public void testSetImageIcon() {
+ mImageView = findImageViewById(R.id.imageview);
+ mImageView.setImageIcon(null);
+ assertNull(mImageView.getDrawable());
+
+ Icon icon = Icon.createWithResource(mActivity, R.drawable.testimage);
+ mImageView.setImageIcon(icon);
+ assertTrue(mImageView.isLayoutRequested());
+ assertNotNull(mImageView.getDrawable());
+ Drawable drawable = mActivity.getDrawable(R.drawable.testimage);
+ BitmapDrawable testimageBitmap = (BitmapDrawable) drawable;
+ Drawable imageViewDrawable = mImageView.getDrawable();
+ BitmapDrawable imageViewBitmap = (BitmapDrawable) imageViewDrawable;
+ WidgetTestUtils.assertEquals(testimageBitmap.getBitmap(), imageViewBitmap.getBitmap());
+ }
+
+ @UiThreadTest
public void testSetImageResource() {
mImageView = findImageViewById(R.id.imageview);
mImageView.setImageResource(-1);
@@ -181,7 +199,7 @@
mImageView.setImageResource(R.drawable.testimage);
assertTrue(mImageView.isLayoutRequested());
assertNotNull(mImageView.getDrawable());
- Drawable drawable = mActivity.getResources().getDrawable(R.drawable.testimage);
+ Drawable drawable = mActivity.getDrawable(R.drawable.testimage);
BitmapDrawable testimageBitmap = (BitmapDrawable) drawable;
Drawable imageViewDrawable = mImageView.getDrawable();
BitmapDrawable imageViewBitmap = (BitmapDrawable) imageViewDrawable;
diff --git a/tests/tests/widget/src/android/widget/cts/MyGallery.java b/tests/tests/widget/src/android/widget/cts/MyGallery.java
index 91665ee..27b5d45 100644
--- a/tests/tests/widget/src/android/widget/cts/MyGallery.java
+++ b/tests/tests/widget/src/android/widget/cts/MyGallery.java
@@ -49,10 +49,6 @@
return super.getChildStaticTransformation(child, t);
}
- protected void setParent(ViewParent v) {
- mParent = v;
- }
-
@Override
protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
return super.checkLayoutParams(p);
diff --git a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
index 24596a4..bb638e4 100644
--- a/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
+++ b/tests/tests/widget/src/android/widget/cts/PopupWindowTest.java
@@ -390,7 +390,11 @@
assertTrue(maxAvailableHeightWithOffset <= avaliable);
anchorView = mActivity.findViewById(R.id.anchor_lower);
- avaliable = getDisplay().getHeight() - anchorView.getHeight();
+ // On some devices the view might actually have larger size than the physical display
+ // due to chin and content will be laid out as if outside of the display. We need to use
+ // larger from the display height and the main view height.
+ avaliable = Math.max(getDisplay().getHeight(),
+ mActivity.findViewById(android.R.id.content).getHeight()) - anchorView.getHeight();
maxAvailableHeight = mPopupWindow.getMaxAvailableHeight(anchorView);
assertTrue(maxAvailableHeight > 0);
assertTrue(maxAvailableHeight <= avaliable);
diff --git a/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java b/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
index cfd61a2..25d8e5e 100644
--- a/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
+++ b/tests/tests/widget/src/android/widget/cts/QuickContactBadgeTest.java
@@ -21,8 +21,6 @@
import android.content.ContextWrapper;
import android.content.Intent;
import android.net.Uri;
-import android.os.Bundle;
-import android.os.UserHandle;
import android.provider.ContactsContract;
import android.provider.ContactsContract.Contacts;
import android.test.InstrumentationTestCase;
@@ -39,22 +37,12 @@
final String plainMimeType = "text/plain";
final Uri nonExistentContactUri = ContentUris.withAppendedId(Contacts.CONTENT_URI, 0);
final CountDownLatch latch = new CountDownLatch(1);
- Context context = new ContextWrapper(getInstrumentation().getContext()) {
+ final Context context = new ContextWrapper(getInstrumentation().getContext()) {
@Override
public void startActivity(Intent intent) {
testCallback(intent);
}
- @Override
- public void startActivityAsUser(Intent intent, UserHandle user) {
- testCallback(intent);
- }
-
- @Override
- public void startActivityAsUser(Intent intent, Bundle options, UserHandle user) {
- testCallback(intent);
- }
-
private void testCallback(Intent intent) {
assertEquals(plainMimeType, intent.getStringExtra(
ContactsContract.QuickContact.EXTRA_PRIORITIZED_MIMETYPE));
diff --git a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
index 328f9f3..58738a1 100644
--- a/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/RemoteViewsTest.java
@@ -16,6 +16,7 @@
package android.widget.cts;
+import android.graphics.drawable.Icon;
import android.test.UiThreadTest;
import com.android.cts.widget.R;
@@ -152,6 +153,33 @@
}
}
+ public void testSetIcon() {
+ ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
+ assertNull(image.getDrawable());
+
+ Icon iconBlack = Icon.createWithResource(mActivity, R.drawable.icon_black);
+ mRemoteViews.setIcon(R.id.remoteView_image, "setImageIcon", iconBlack);
+ mRemoteViews.reapply(mActivity, mResult);
+ assertNotNull(image.getDrawable());
+ BitmapDrawable dBlack = (BitmapDrawable) mActivity.getDrawable(R.drawable.icon_black);
+ WidgetTestUtils.assertEquals(dBlack.getBitmap(),
+ ((BitmapDrawable) image.getDrawable()).getBitmap());
+ }
+
+ public void testSetImageViewIcon() {
+ ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
+ assertNull(image.getDrawable());
+
+ Icon iconBlue = Icon.createWithResource(mActivity, R.drawable.icon_blue);
+ mRemoteViews.setImageViewIcon(R.id.remoteView_image, iconBlue);
+ mRemoteViews.reapply(mActivity, mResult);
+ assertNotNull(image.getDrawable());
+ BitmapDrawable dBlue = (BitmapDrawable) mActivity.getDrawable(R.drawable.icon_blue);
+ WidgetTestUtils.assertEquals(dBlue.getBitmap(),
+ ((BitmapDrawable) image.getDrawable()).getBitmap());
+
+ }
+
public void testSetImageViewResource() {
ImageView image = (ImageView) mResult.findViewById(R.id.remoteView_image);
assertNull(image.getDrawable());
@@ -159,8 +187,7 @@
mRemoteViews.setImageViewResource(R.id.remoteView_image, R.drawable.testimage);
mRemoteViews.reapply(mActivity, mResult);
assertNotNull(image.getDrawable());
- BitmapDrawable d = (BitmapDrawable) mActivity
- .getResources().getDrawable(R.drawable.testimage);
+ BitmapDrawable d = (BitmapDrawable) mActivity.getDrawable(R.drawable.testimage);
WidgetTestUtils.assertEquals(d.getBitmap(),
((BitmapDrawable) image.getDrawable()).getBitmap());
diff --git a/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java b/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
index 28bfd06..52d699b5 100644
--- a/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
+++ b/tests/tests/widget/src/android/widget/cts/ResourceCursorAdapterTest.java
@@ -170,13 +170,19 @@
}
private static class MockResourceCursorAdapter extends ResourceCursorAdapter {
+ private final boolean mAutoRequery;
+
public MockResourceCursorAdapter(Context context, int layout, Cursor c) {
super(context, layout, c);
+
+ mAutoRequery = false;
}
public MockResourceCursorAdapter(Context context, int layout,
Cursor c, boolean autoRequery) {
super(context, layout, c, autoRequery);
+
+ mAutoRequery = autoRequery;
}
@Override
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
index 5259736..b9f0d1f 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayoutTest.java
@@ -18,7 +18,6 @@
import com.android.cts.widget.R;
-
import android.content.Context;
import android.content.res.XmlResourceParser;
import android.test.ActivityInstrumentationTestCase2;
diff --git a/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java
index 8a38c81..cbc41ce 100644
--- a/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableLayout_LayoutParamsTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-import com.android.internal.R;
+import com.android.cts.widget.R;
import org.xmlpull.v1.XmlPullParser;
@@ -140,8 +140,7 @@
XmlResourceParser parser = null;
AttributeSet attrs = null;
try {
- parser = mTargetContext.getResources()
- .getXml(com.android.cts.widget.R.xml.base_attributes);
+ parser = mTargetContext.getResources().getXml(R.xml.base_attributes);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java b/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java
index 06f851d..8308414 100644
--- a/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TableRow_LayoutParamsTest.java
@@ -16,7 +16,7 @@
package android.widget.cts;
-import com.android.internal.R;
+import com.android.cts.widget.R;
import org.xmlpull.v1.XmlPullParser;
@@ -91,8 +91,8 @@
assertEquals(0, layoutParams.span);
TableCtsActivity activity = getActivity();
- activity.setContentView(com.android.cts.widget.R.layout.table_layout_2);
- int idTable = com.android.cts.widget.R.id.table2;
+ activity.setContentView(R.layout.table_layout_2);
+ int idTable = R.id.table2;
TableLayout tableLayout = (TableLayout) activity.findViewById(idTable);
View vVitural1 = ((TableRow) tableLayout.getChildAt(0)).getVirtualChildAt(1);
layoutParams = (TableRow.LayoutParams) vVitural1.getLayoutParams();
@@ -186,8 +186,7 @@
XmlResourceParser parser = null;
AttributeSet attrs = null;
try {
- parser = mTargetContext.getResources()
- .getXml(com.android.cts.widget.R.xml.base_attributes);
+ parser = mTargetContext.getResources().getXml(R.xml.base_attributes);
int type;
while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
diff --git a/tests/tests/widget/src/android/widget/cts/TextViewTest.java b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
index 31a4636..936f38f 100644
--- a/tests/tests/widget/src/android/widget/cts/TextViewTest.java
+++ b/tests/tests/widget/src/android/widget/cts/TextViewTest.java
@@ -17,7 +17,6 @@
package android.widget.cts;
import com.android.cts.widget.R;
-import com.android.internal.util.FastMath;
import org.xmlpull.v1.XmlPullParserException;
@@ -786,37 +785,37 @@
float add = 1.2f;
float mult = 1.4f;
setLineSpacing(add, mult);
- assertEquals(FastMath.round(originalLineHeight * mult + add), mTextView.getLineHeight());
+ assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight());
add = 0.0f;
mult = 1.4f;
setLineSpacing(add, mult);
- assertEquals(FastMath.round(originalLineHeight * mult + add), mTextView.getLineHeight());
+ assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight());
// abnormal
add = -1.2f;
mult = 1.4f;
setLineSpacing(add, mult);
- assertEquals(FastMath.round(originalLineHeight * mult + add), mTextView.getLineHeight());
+ assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight());
add = -1.2f;
mult = -1.4f;
setLineSpacing(add, mult);
- assertEquals(FastMath.round(originalLineHeight * mult + add), mTextView.getLineHeight());
+ assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight());
add = 1.2f;
mult = 0.0f;
setLineSpacing(add, mult);
- assertEquals(FastMath.round(originalLineHeight * mult + add), mTextView.getLineHeight());
+ assertEquals(Math.round(originalLineHeight * mult + add), mTextView.getLineHeight());
// edge
add = Float.MIN_VALUE;
mult = Float.MIN_VALUE;
setLineSpacing(add, mult);
float expected = originalLineHeight * mult + add;
- assertEquals(FastMath.round(expected), mTextView.getLineHeight());
+ assertEquals(Math.round(expected), mTextView.getLineHeight());
add = Float.MAX_VALUE;
mult = Float.MAX_VALUE;
setLineSpacing(add, mult);
expected = originalLineHeight * mult + add;
- assertEquals(FastMath.round(expected), mTextView.getLineHeight());
+ assertEquals(Math.round(expected), mTextView.getLineHeight());
}
public void testInstanceState() {
diff --git a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
index 11d670d..b61673c 100644
--- a/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
+++ b/tests/tests/widget/src/android/widget/cts/util/ListScenario.java
@@ -17,6 +17,7 @@
package android.widget.cts.util;
import java.util.ArrayList;
+import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
@@ -35,8 +36,6 @@
import android.widget.ListView;
import android.widget.TextView;
-import com.google.android.collect.Maps;
-
/**
* Utility base class for creating various List scenarios. Configurable by the number
* of items, how tall each item should be (in relation to the screen height), and
@@ -52,7 +51,7 @@
private int mStartingSelectionPosition;
private double mItemScreenSizeFactor;
- private Map<Integer, Double> mOverrideItemScreenSizeFactors = Maps.newHashMap();
+ private Map<Integer, Double> mOverrideItemScreenSizeFactors = new HashMap<>();
private int mScreenHeight;
@@ -102,7 +101,7 @@
private double mItemScreenSizeFactor = 1 / 5;
private Double mFadingEdgeScreenSizeFactor = null;
- private Map<Integer, Double> mOverrideItemScreenSizeFactors = Maps.newHashMap();
+ private Map<Integer, Double> mOverrideItemScreenSizeFactors = new HashMap<>();
// separators
private List<Integer> mUnselectableItems = new ArrayList<Integer>(8);
diff --git a/tests/tests/widget/src/android/widget/cts/util/XmlUtils.java b/tests/tests/widget/src/android/widget/cts/util/XmlUtils.java
index c357e5e..1ff922f 100644
--- a/tests/tests/widget/src/android/widget/cts/util/XmlUtils.java
+++ b/tests/tests/widget/src/android/widget/cts/util/XmlUtils.java
@@ -16,8 +16,6 @@
package android.widget.cts.util;
-import com.android.internal.util.FastXmlSerializer;
-
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
import org.xmlpull.v1.XmlSerializer;
@@ -171,28 +169,6 @@
}
/**
- * Flatten a Map into an output stream as XML. The map can later be
- * read back with readMapXml().
- *
- * @param val The map to be flattened.
- * @param out Where to write the XML data.
- *
- * @see #writeMapXml(Map, String, XmlSerializer)
- * @see #writeListXml
- * @see #writeValueXml
- * @see #readMapXml
- */
- public static final void writeMapXml(Map val, OutputStream out)
- throws XmlPullParserException, IOException {
- XmlSerializer serializer = new FastXmlSerializer();
- serializer.setOutput(out, StandardCharsets.UTF_8.name());
- serializer.startDocument(null, true);
- serializer.setFeature("http://xmlpull.org/v1/doc/features.html#indent-output", true);
- writeMapXml(val, null, serializer);
- serializer.endDocument();
- }
-
- /**
* Flatten a List into an output stream as XML. The list can later be
* read back with readListXml().
*
@@ -224,7 +200,6 @@
* none.
* @param out XmlSerializer to write the map into.
*
- * @see #writeMapXml(Map, OutputStream)
* @see #writeListXml
* @see #writeValueXml
* @see #readMapXml
@@ -244,7 +219,6 @@
* @param out XmlSerializer to write the map into.
* @param callback Method to call when an Object type is not recognized.
*
- * @see #writeMapXml(Map, OutputStream)
* @see #writeListXml
* @see #writeValueXml
* @see #readMapXml
@@ -278,7 +252,6 @@
* @param val The map to be flattened.
* @param out XmlSerializer to write the map into.
*
- * @see #writeMapXml(Map, OutputStream)
* @see #writeListXml
* @see #writeValueXml
* @see #readMapXml