Merge "Add: test case for SettingInjectorSerivce" into mnc-dev
diff --git a/CtsTestCaseList.mk b/CtsTestCaseList.mk
index 21b4027..4035af7 100644
--- a/CtsTestCaseList.mk
+++ b/CtsTestCaseList.mk
@@ -67,6 +67,7 @@
cts_support_packages := \
CtsAccelerationTestStubs \
CtsAppTestStubs \
+ CtsCertInstallerApp \
CtsDeviceAdmin \
CtsDeviceOpenGl \
CtsDeviceOwnerApp \
@@ -183,6 +184,7 @@
cts_host_libraries := \
CtsAdbTests \
CtsAppSecurityTests \
+ CtsAtraceHostTestCases \
CtsDevicePolicyManagerTestCases \
CtsDumpsysHostTestCases \
CtsHostJank \
diff --git a/hostsidetests/atrace/Android.mk b/hostsidetests/atrace/Android.mk
new file mode 100644
index 0000000..4bde535
--- /dev/null
+++ b/hostsidetests/atrace/Android.mk
@@ -0,0 +1,32 @@
+# Copyright (C) 2014 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH := $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_MODULE_TAGS := optional
+
+# Must match the package name in CtsTestCaseList.mk
+LOCAL_MODULE := CtsAtraceHostTestCases
+
+LOCAL_JAVA_LIBRARIES := cts-tradefed tradefed-prebuilt
+
+LOCAL_CTS_TEST_PACKAGE := android.host.atrace
+
+include $(BUILD_CTS_HOST_JAVA_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
new file mode 100644
index 0000000..ae4a850
--- /dev/null
+++ b/hostsidetests/atrace/src/android/atrace/cts/AtraceHostTest.java
@@ -0,0 +1,103 @@
+/*
+ * 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.atrace.cts;
+
+import com.android.ddmlib.Log;
+import com.android.tradefed.device.ITestDevice;
+import com.android.tradefed.testtype.DeviceTestCase;
+
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * Test to check that atrace is usable, to enable usage of systrace.
+ */
+public class AtraceHostTest extends DeviceTestCase {
+ private static final String TAG = "AtraceHostTest";
+
+ private ITestDevice mDevice;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDevice = getDevice();
+ }
+
+ // Collection of all userspace tags
+ private static final List<String> sRequiredCategoriesList = Arrays.asList(
+ "gfx",
+ "input",
+ "view",
+ "webview",
+ "wm",
+ "am",
+ "sm",
+ "audio",
+ "video",
+ "camera",
+ "hal",
+ "app",
+ "res",
+ "dalvik",
+ "rs",
+ "bionic",
+ "power"
+ );
+
+ /**
+ * Tests that atrace exists and is runnable with no args
+ */
+ public void testSimpleRun() throws Exception {
+ String output = mDevice.executeShellCommand("atrace");
+ String[] lines = output.split("\\r?\\n");
+
+ // check for expected stdout
+ assertEquals("capturing trace... done", lines[0]);
+ assertEquals("TRACE:", lines[1]);
+
+ // commented trace marker starts here
+ assertEquals("# tracer: nop", lines[2]);
+ }
+
+ /**
+ * Tests the output of "atrace --list_categories" to ensure required categories exist.
+ */
+ public void testCategories() throws Exception {
+ String output = mDevice.executeShellCommand("atrace --list_categories");
+ String[] categories = output.split("\\r?\\n");
+
+ Set<String> requiredCategories = new HashSet<String>(sRequiredCategoriesList);
+
+ for (String category : categories) {
+ int dashIndex = category.indexOf("-");
+
+ assertTrue(dashIndex > 1); // must match category output format
+ category = category.substring(0, dashIndex).trim();
+
+ requiredCategories.remove(category);
+ }
+
+ if (!requiredCategories.isEmpty()) {
+ for (String missingCategory : requiredCategories) {
+ Log.d(TAG, "missing category: " + missingCategory);
+ }
+ fail("Expected categories missing from atrace");
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/Android.mk b/hostsidetests/devicepolicy/app/CertInstaller/Android.mk
new file mode 100644
index 0000000..22a78e2
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CertInstaller/Android.mk
@@ -0,0 +1,31 @@
+# Copyright (C) 2015 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_PACKAGE_NAME := CtsCertInstallerApp
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_DATA_APPS)
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_SDK_VERSION := current
+
+include $(BUILD_CTS_PACKAGE)
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml b/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml
new file mode 100644
index 0000000..c50e5fd
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CertInstaller/AndroidManifest.xml
@@ -0,0 +1,34 @@
+<?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.certinstaller">
+
+ <uses-sdk android:minSdkVersion="22"/>
+
+ <application>
+ <activity android:name="CertInstallerActivity">
+ <intent-filter>
+ <action android:name="com.android.cts.certinstaller.install_cert" />
+ <action android:name="com.android.cts.certinstaller.remove_cert" />
+ <action android:name="com.android.cts.certinstaller.verify_cert" />
+ <action android:name="com.android.cts.certinstaller.install_keypair" />
+ <category android:name="android.intent.category.DEFAULT" />
+ </intent-filter>
+ </activity>
+ </application>
+
+</manifest>
diff --git a/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerActivity.java b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerActivity.java
new file mode 100644
index 0000000..b14822b
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/CertInstaller/src/com/android/cts/certinstaller/CertInstallerActivity.java
@@ -0,0 +1,177 @@
+/*
+ * 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.certinstaller;
+
+import android.app.Activity;
+import android.app.admin.DevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Base64;
+import android.util.Base64InputStream;
+import android.util.Log;
+
+import java.io.ByteArrayInputStream;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.security.KeyFactory;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.util.List;
+
+/**
+ * Delegated certificate installer app that responds to specific intents and executes various DPM
+ * certificate manipulation APIs. The following APIs are exercised:
+ * {@link DevicePolicyManager#installCaCert},
+ * {@link DevicePolicyManager#uninstallCaCert},
+ * {@link DevicePolicyManager#hasCaCertInstalled},
+ * {@link DevicePolicyManager#getInstalledCaCerts},
+ * {@link DevicePolicyManager#installKeyPair}.
+ */
+public class CertInstallerActivity extends Activity {
+
+ private static final String TAG = "DelegatedCertInstaller";
+ // exercises {@link DevicePolicyManager#installCaCert} and
+ // {@link DevicePolicyManager#hasCaCertInstalled},
+ private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert";
+ // exercises {@link DevicePolicyManager#uninstallCaCert} and
+ // {@link DevicePolicyManager#hasCaCertInstalled},
+ private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert";
+ // exercises {@link DevicePolicyManager#getInstalledCaCerts},
+ private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert";
+ // exercises {@link DevicePolicyManager#installKeyPair},
+ private static final String ACTION_INSTALL_KEYPAIR =
+ "com.android.cts.certinstaller.install_keypair";
+
+ private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
+
+ private static final String EXTRA_CERT_DATA = "extra_cert_data";
+ private static final String EXTRA_KEY_DATA = "extra_key_data";
+ private static final String EXTRA_KEY_ALIAS = "extra_key_alias";
+ private static final String EXTRA_RESULT_VALUE = "extra_result_value";
+ private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ Intent intent = getIntent();
+ if (intent == null) {
+ finish();
+ return;
+ }
+
+ String action = intent.getAction();
+ DevicePolicyManager dpm = (DevicePolicyManager) getSystemService(
+ Context.DEVICE_POLICY_SERVICE);
+
+ byte[] certBuffer;
+
+ if (ACTION_INSTALL_CERT.equals(action)) {
+ try {
+ certBuffer = intent.getByteArrayExtra(EXTRA_CERT_DATA);
+ // Verify cert is not currently installed.
+ if (dpm.hasCaCertInstalled(null, certBuffer)) {
+ throw new RuntimeException("Cert already on device?");
+ }
+ if (!dpm.installCaCert(null, certBuffer)) {
+ throw new RuntimeException("installCaCert returned false.");
+ }
+ if (!dpm.hasCaCertInstalled(null, certBuffer)) {
+ throw new RuntimeException("Cannot find cert after installation.");
+ }
+ sendResult(true, null);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception raised duing ACTION_INSTALL_CERT", e);
+ sendResult(false, e);
+ }
+ } else if (ACTION_REMOVE_CERT.equals(action)) {
+ try {
+ certBuffer = intent.getByteArrayExtra(EXTRA_CERT_DATA);
+ if (!dpm.hasCaCertInstalled(null, certBuffer)) {
+ throw new RuntimeException("Trying to uninstall a non-existent cert.");
+ }
+ dpm.uninstallCaCert(null, certBuffer);
+ sendResult(!dpm.hasCaCertInstalled(null, certBuffer), null);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception raised duing ACTION_REMOVE_CERT", e);
+ sendResult(false, e);
+ }
+ } else if (ACTION_VERIFY_CERT.equals(action)) {
+ try {
+ certBuffer = intent.getByteArrayExtra(EXTRA_CERT_DATA);
+ sendResult(containsCertificate(dpm.getInstalledCaCerts(null), certBuffer), null);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception raised duing ACTION_VERIFY_CERT", e);
+ sendResult(false, e);
+ }
+ } else if (ACTION_INSTALL_KEYPAIR.equals(action)) {
+ String alias = intent.getStringExtra(EXTRA_KEY_ALIAS);
+ String key = intent.getStringExtra(EXTRA_KEY_DATA);
+ String cert = intent.getStringExtra(EXTRA_CERT_DATA);
+ try {
+ PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(
+ Base64.decode(key, Base64.DEFAULT));
+ KeyFactory kf = KeyFactory.getInstance("RSA");
+ PrivateKey privatekey = kf.generatePrivate(keySpec);
+
+ Certificate certificate = CertificateFactory.getInstance("X.509")
+ .generateCertificate(
+ new Base64InputStream(new ByteArrayInputStream(cert.getBytes()),
+ Base64.DEFAULT));
+ // Unfortunately there is no programmatically way to check if the given private key
+ // is indeed in the key store as a unprivileged app. So we just rely on
+ // installKeyPair() returning true as the success criteria of this test. Separate
+ // CTS keychain tests will make sure the API's behaviour is correct.
+ // Note: installKeyPair() will silently return false if there is no lockscreen
+ // password, however the test setup should have set one already.
+ sendResult(dpm.installKeyPair(null, privatekey, certificate, alias), null);
+ } catch (Exception e) {
+ Log.e(TAG, "Exception raised duing ACTION_INSTALL_KEYPAIR", e);
+ sendResult(false, e);
+ }
+ }
+ finish();
+ }
+
+ private void sendResult(boolean succeed, Exception e) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_CERT_OPERATION_DONE);
+ intent.putExtra(EXTRA_RESULT_VALUE, succeed);
+ if (e != null) {
+ intent.putExtra(EXTRA_RESULT_EXCEPTION, e);
+ }
+ sendBroadcast(intent);
+ }
+
+ private boolean containsCertificate(List<byte[]> certificates, byte[] toMatch)
+ throws CertificateException {
+ Certificate certificateToMatch = readCertificate(toMatch);
+ for (byte[] certBuffer : certificates) {
+ Certificate cert = readCertificate(certBuffer);
+ if (certificateToMatch.equals(cert)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private Certificate readCertificate(byte[] certBuffer) throws CertificateException {
+ final CertificateFactory certFactory = CertificateFactory.getInstance("X.509");
+ return certFactory.generateCertificate(new ByteArrayInputStream(certBuffer));
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
index aeb6a45..82f6d6d 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/AndroidManifest.xml
@@ -34,6 +34,15 @@
<action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
</intent-filter>
</receiver>
+ <receiver
+ android:name="com.android.cts.managedprofile.PrimaryUserDeviceAdmin"
+ android:permission="android.permission.BIND_DEVICE_ADMIN">
+ <meta-data android:name="android.app.device_admin"
+ android:resource="@xml/primary_device_admin" />
+ <intent-filter>
+ <action android:name="android.app.action.DEVICE_ADMIN_ENABLED" />
+ </intent-filter>
+ </receiver>
<activity android:name=".PrimaryUserFilterSetterActivity">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
new file mode 100644
index 0000000..a6aff49
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/res/xml/primary_device_admin.xml
@@ -0,0 +1,20 @@
+<!-- Copyright (C) 2015 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<device-admin xmlns:android="http://schemas.android.com/apk/res/android" android:visible="false">
+ <uses-policies>
+ <reset-password />
+ <limit-password />
+ </uses-policies>
+</device-admin>
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java
new file mode 100644
index 0000000..de859b4
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/DelegatedCertInstallerTest.java
@@ -0,0 +1,293 @@
+/*
+ * 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.managedprofile;
+
+import android.app.KeyguardManager;
+import android.app.admin.DevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.security.KeyChain;
+import android.security.KeyChainException;
+import android.test.AndroidTestCase;
+import android.util.Base64;
+import android.util.Base64InputStream;
+
+import java.io.ByteArrayInputStream;
+import java.security.KeyFactory;
+import java.security.NoSuchAlgorithmException;
+import java.security.PrivateKey;
+import java.security.cert.Certificate;
+import java.security.cert.CertificateException;
+import java.security.cert.CertificateFactory;
+import java.security.spec.InvalidKeySpecException;
+import java.security.spec.PKCS8EncodedKeySpec;
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+import static com.android.cts.managedprofile.BaseManagedProfileTest.ADMIN_RECEIVER_COMPONENT;
+
+/**
+ * Exercise delegated cert installer APIs in {@link DevicePolicyManager} by setting the test app
+ * (CtsCertInstallerApp) as a delegated cert installer and then asking it to invoke various
+ * cert-related APIs. The expected certificate changes are validated both remotely and locally.
+ */
+public class DelegatedCertInstallerTest extends AndroidTestCase {
+
+ private static final String CERT_INSTALLER_PACKAGE = "com.android.cts.certinstaller";
+
+ private static final String ACTION_INSTALL_CERT = "com.android.cts.certinstaller.install_cert";
+ private static final String ACTION_REMOVE_CERT = "com.android.cts.certinstaller.remove_cert";
+ private static final String ACTION_VERIFY_CERT = "com.android.cts.certinstaller.verify_cert";
+ private static final String ACTION_INSTALL_KEYPAIR =
+ "com.android.cts.certinstaller.install_keypair";
+ private static final String ACTION_CERT_OPERATION_DONE = "com.android.cts.certinstaller.done";
+
+ private static final String EXTRA_CERT_DATA = "extra_cert_data";
+ private static final String EXTRA_KEY_DATA = "extra_key_data";
+ private static final String EXTRA_KEY_ALIAS = "extra_key_alias";
+ private static final String EXTRA_RESULT_VALUE = "extra_result_value";
+ private static final String EXTRA_RESULT_EXCEPTION = "extra_result_exception";
+
+ /*
+ * The CA and keypair below are generated with:
+ *
+ * openssl req -new -x509 -days 3650 -extensions v3_ca -keyout cakey.pem -out cacert.pem
+ * openssl req -newkey rsa:1024 -keyout userkey.pem -nodes -days 3650 -out userkey.req
+ * mkdir -p demoCA/newcerts
+ * touch demoCA/index.txt
+ * echo "01" > demoCA/serial
+ * openssl ca -out usercert.pem -in userkey.req -cert cacert.pem -keyfile cakey.pem -days 3650
+ */
+
+ // Content from cacert.pem
+ private static final String TEST_CA =
+ "-----BEGIN CERTIFICATE-----\n" +
+ "MIIDXTCCAkWgAwIBAgIJAK9Tl/F9V8kSMA0GCSqGSIb3DQEBCwUAMEUxCzAJBgNV\n" +
+ "BAYTAkFVMRMwEQYDVQQIDApTb21lLVN0YXRlMSEwHwYDVQQKDBhJbnRlcm5ldCBX\n" +
+ "aWRnaXRzIFB0eSBMdGQwHhcNMTUwMzA2MTczMjExWhcNMjUwMzAzMTczMjExWjBF\n" +
+ "MQswCQYDVQQGEwJBVTETMBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50\n" +
+ "ZXJuZXQgV2lkZ2l0cyBQdHkgTHRkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB\n" +
+ "CgKCAQEAvItOutsE75WBTgTyNAHt4JXQ3JoseaGqcC3WQij6vhrleWi5KJ0jh1/M\n" +
+ "Rpry7Fajtwwb4t8VZa0NuM2h2YALv52w1xivql88zce/HU1y7XzbXhxis9o6SCI+\n" +
+ "oVQSbPeXRgBPppFzBEh3ZqYTVhAqw451XhwdA4Aqs3wts7ddjwlUzyMdU44osCUg\n" +
+ "kVg7lfPf9sTm5IoHVcfLSCWH5n6Nr9sH3o2ksyTwxuOAvsN11F/a0mmUoPciYPp+\n" +
+ "q7DzQzdi7akRG601DZ4YVOwo6UITGvDyuAAdxl5isovUXqe6Jmz2/myTSpAKxGFs\n" +
+ "jk9oRoG6WXWB1kni490GIPjJ1OceyQIDAQABo1AwTjAdBgNVHQ4EFgQUH1QIlPKL\n" +
+ "p2OQ/AoLOjKvBW4zK3AwHwYDVR0jBBgwFoAUH1QIlPKLp2OQ/AoLOjKvBW4zK3Aw\n" +
+ "DAYDVR0TBAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAcMi4voMMJHeQLjtq8Oky\n" +
+ "Azpyk8moDwgCd4llcGj7izOkIIFqq/lyqKdtykVKUWz2bSHO5cLrtaOCiBWVlaCV\n" +
+ "DYAnnVLM8aqaA6hJDIfaGs4zmwz0dY8hVMFCuCBiLWuPfiYtbEmjHGSmpQTG6Qxn\n" +
+ "ZJlaK5CZyt5pgh5EdNdvQmDEbKGmu0wpCq9qjZImwdyAul1t/B0DrsWApZMgZpeI\n" +
+ "d2od0VBrCICB1K4p+C51D93xyQiva7xQcCne+TAnGNy9+gjQ/MyR8MRpwRLv5ikD\n" +
+ "u0anJCN8pXo6IMglfMAsoton1J6o5/ae5uhC6caQU8bNUsCK570gpNfjkzo6rbP0\n" +
+ "wQ==\n" +
+ "-----END CERTIFICATE-----";
+ // Content from userkey.pem without the private key header and footer.
+ private static final String TEST_KEY =
+ "MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBALCYprGsTU+5L3KM\n" +
+ "fhkm0gXM2xjGUH+543YLiMPGVr3eVS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqU\n" +
+ "mhegxG4s3IvGYVB0KZoRIjDKmnnvlx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T\n" +
+ "9sV6zW2Wzri+f5mvzKjhnArbLktHAgMBAAECgYBlfVVPhtZnmuXJzzQpAEZzTugb\n" +
+ "tN1OimZO0RIocTQoqj4KT+HkiJOLGFQPwbtFpMre+q4SRqNpM/oZnI1yRtKcCmIc\n" +
+ "mZgkwJ2k6pdSxqO0ofxFFTdT9czJ3rCnqBHy1g6BqUQFXT4olcygkxUpKYUwzlz1\n" +
+ "oAl487CoPxyr4sVEAQJBANwiUOHcdGd2RoRILDzw5WOXWBoWPOKzX/K9wt0yL+mO\n" +
+ "wlFNFSymqo9eLheHcEq/VD9qK9rT700dCewJfWj6+bECQQDNXmWNYIxGii5NJilT\n" +
+ "OBOHiMD/F0NE178j+/kmacbhDJwpkbLYXaP8rW4+Iswrm4ORJ59lvjNuXaZ28+sx\n" +
+ "fFp3AkA6Z7Bl/IO135+eATgbgx6ZadIqObQ1wbm3Qbmtzl7/7KyJvZXcnuup1icM\n" +
+ "fxa//jtwB89S4+Ad6ZJ0WaA4dj5BAkEAuG7V9KmIULE388EZy8rIfyepa22Q0/qN\n" +
+ "hdt8XasRGHsio5Jdc0JlSz7ViqflhCQde/aBh/XQaoVgQeO8jKyI8QJBAJHekZDj\n" +
+ "WA0w1RsBVVReN1dVXgjm1CykeAT8Qx8TUmBUfiDX6w6+eGQjKtS7f4KC2IdRTV6+\n" +
+ "bDzDoHBChHNC9ms=\n";
+
+ // Content from usercert.pem without the header and footer.
+ private static final String TEST_CERT =
+ "MIIDEjCCAfqgAwIBAgIBATANBgkqhkiG9w0BAQsFADBFMQswCQYDVQQGEwJBVTET\n" +
+ "MBEGA1UECAwKU29tZS1TdGF0ZTEhMB8GA1UECgwYSW50ZXJuZXQgV2lkZ2l0cyBQ\n" +
+ "dHkgTHRkMB4XDTE1MDUwMTE2NTQwNVoXDTI1MDQyODE2NTQwNVowWzELMAkGA1UE\n" +
+ "BhMCQVUxEzARBgNVBAgMClNvbWUtU3RhdGUxITAfBgNVBAoMGEludGVybmV0IFdp\n" +
+ "ZGdpdHMgUHR5IEx0ZDEUMBIGA1UEAwwLY2xpZW50IGNlcnQwgZ8wDQYJKoZIhvcN\n" +
+ "AQEBBQADgY0AMIGJAoGBALCYprGsTU+5L3KMfhkm0gXM2xjGUH+543YLiMPGVr3e\n" +
+ "VS7biue1/tQlL+fJsw3rqsPKJe71RbVWlpqUmhegxG4s3IvGYVB0KZoRIjDKmnnv\n" +
+ "lx6nngL2ZJ8O27U42pHsw4z4MKlcQlWkjL3T9sV6zW2Wzri+f5mvzKjhnArbLktH\n" +
+ "AgMBAAGjezB5MAkGA1UdEwQCMAAwLAYJYIZIAYb4QgENBB8WHU9wZW5TU0wgR2Vu\n" +
+ "ZXJhdGVkIENlcnRpZmljYXRlMB0GA1UdDgQWBBQ8GL+jKSarvTn9fVNA2AzjY7qq\n" +
+ "gjAfBgNVHSMEGDAWgBRzBBA5sNWyT/fK8GrhN3tOqO5tgjANBgkqhkiG9w0BAQsF\n" +
+ "AAOCAQEAgwQEd2bktIDZZi/UOwU1jJUgGq7NiuBDPHcqgzjxhGFLQ8SQAAP3v3PR\n" +
+ "mLzcfxsxnzGynqN5iHQT4rYXxxaqrp1iIdj9xl9Wl5FxjZgXITxhlRscOd/UOBvG\n" +
+ "oMrazVczjjdoRIFFnjtU3Jf0Mich68HD1Z0S3o7X6sDYh6FTVR5KbLcxbk6RcoG4\n" +
+ "VCI5boR5LUXgb5Ed5UxczxvN12S71fyxHYVpuuI0z0HTIbAxKeRw43I6HWOmR1/0\n" +
+ "G6byGCNL/1Fz7Y+264fGqABSNTKdZwIU2K4ANEH7F+9scnhoO6OBp+gjBe5O+7jb\n" +
+ "wZmUCAoTka4hmoaOCj7cqt/IkmxozQ==\n";
+
+ private DevicePolicyManager mDpm;
+ private volatile boolean mReceivedResult;
+ private volatile Exception mReceivedException;
+ private Semaphore mAvailableResultSemaphore;
+
+ private final BroadcastReceiver receiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (ACTION_CERT_OPERATION_DONE.equals(intent.getAction())) {
+ synchronized (DelegatedCertInstallerTest.this) {
+ mReceivedResult = intent.getBooleanExtra(EXTRA_RESULT_VALUE, false);
+ mReceivedException =
+ (Exception) intent.getSerializableExtra(EXTRA_RESULT_EXCEPTION);
+ mAvailableResultSemaphore.release();
+ }
+ }
+ }
+ };
+
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+ mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ mAvailableResultSemaphore = new Semaphore(0);
+ mReceivedResult = false;
+ mReceivedException = null;
+ IntentFilter filter = new IntentFilter();
+ filter.addAction(ACTION_CERT_OPERATION_DONE);
+ getContext().registerReceiver(receiver, filter);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ getContext().unregisterReceiver(receiver);
+ mDpm.uninstallCaCert(ADMIN_RECEIVER_COMPONENT, TEST_CA.getBytes());
+ // Installed private key pair will be removed once the lockscreen password is cleared,
+ // which is done in the hostside test.
+ mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
+ super.tearDown();
+ }
+
+ public void testCaCertsOperations() throws InterruptedException {
+ byte[] cert = TEST_CA.getBytes();
+
+ mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
+ assertEquals(CERT_INSTALLER_PACKAGE,
+ mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
+
+ // Exercise installCaCert()
+ installCaCert(cert);
+ assertResult("installCaCert", true);
+ assertTrue("Certificate is not installed properly", mDpm.hasCaCertInstalled(
+ ADMIN_RECEIVER_COMPONENT, cert));
+
+ // Exercise getInstalledCaCerts()
+ verifyCaCert(cert);
+ assertResult("getInstalledCaCerts()", true);
+
+ // Exercise uninstallCaCert()
+ removeCaCert(cert);
+ assertResult("uninstallCaCert()", true);
+ assertFalse("Certificate is not removed properly", mDpm.hasCaCertInstalled(
+ ADMIN_RECEIVER_COMPONENT, cert));
+
+ // Clear delegated cert installer.
+ // Tests after this are expected to fail.
+ mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
+
+ installCaCert(cert);
+ assertResult("installCaCert", false);
+ }
+
+ public void testInstallKeyPair() throws InterruptedException, KeyChainException {
+ final String alias = "delegated-cert-installer-test-key";
+
+ // Clear delegated cert installer.
+ mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, null);
+ // The app is not the cert installer , it shouldn't have have privilege to call
+ // installKeyPair().
+ installKeyPair(TEST_KEY, TEST_CERT, alias);
+ assertResult("installKeyPair", false);
+
+ // Set the app to be cert installer.
+ mDpm.setCertInstallerPackage(ADMIN_RECEIVER_COMPONENT, CERT_INSTALLER_PACKAGE);
+ assertEquals(CERT_INSTALLER_PACKAGE,
+ mDpm.getCertInstallerPackage(ADMIN_RECEIVER_COMPONENT));
+
+ // Exercise installKeyPair()
+ checkKeyguardPrecondition();
+ installKeyPair(TEST_KEY, TEST_CERT, alias);
+ assertResult("installKeyPair", true);
+ }
+
+ /**
+ * installKeyPair() requires the system to have a lockscreen password, which should have been
+ * set by the host side test.
+ */
+ private void checkKeyguardPrecondition() throws InterruptedException {
+ KeyguardManager km = (KeyguardManager) mContext.getSystemService(Context.KEYGUARD_SERVICE);
+ if (!km.isKeyguardSecure()) {
+ Thread.sleep(5000);
+ }
+ assertTrue("A lockscreen password is required before keypair can be installed",
+ km.isKeyguardSecure());
+ }
+
+ private void installCaCert(byte[] cert) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_INSTALL_CERT);
+ intent.putExtra(EXTRA_CERT_DATA, cert);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+
+ private void removeCaCert(byte[] cert) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_REMOVE_CERT);
+ intent.putExtra(EXTRA_CERT_DATA, cert);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+
+ private void verifyCaCert(byte[] cert) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_VERIFY_CERT);
+ intent.putExtra(EXTRA_CERT_DATA, cert);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+
+ private void assertResult(String testName, Boolean expectSuccess) throws InterruptedException {
+ assertTrue("Cert installer did not respond in time.",
+ mAvailableResultSemaphore.tryAcquire(5, TimeUnit.SECONDS));
+ synchronized (this) {
+ if (expectSuccess) {
+ assertTrue(testName + " failed unexpectedly.", mReceivedResult);
+ assertNull(testName + " raised exception", mReceivedException);
+ } else {
+ assertFalse(testName + " succeeded unexpectedly.", mReceivedResult);
+ assertTrue(testName + " did not raise SecurityException",
+ mReceivedException != null &&
+ mReceivedException instanceof SecurityException);
+ }
+ }
+ }
+
+ private void installKeyPair(String key, String cert, String alias) {
+ Intent intent = new Intent();
+ intent.setAction(ACTION_INSTALL_KEYPAIR);
+ intent.putExtra(EXTRA_CERT_DATA, cert);
+ intent.putExtra(EXTRA_KEY_DATA, key);
+ intent.putExtra(EXTRA_KEY_ALIAS, alias);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ getContext().startActivity(intent);
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
new file mode 100644
index 0000000..5b8af1f
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserAdminHelper.java
@@ -0,0 +1,79 @@
+/*
+ * 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.managedprofile;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.ComponentName;
+import android.content.Context;
+import android.test.AndroidTestCase;
+
+/**
+ * This test executes helper tasks as active device admin in the primary user. Current tasks are
+ * setting and clearing lockscreen password used by the host side delegated cert installer test.
+ */
+public class PrimaryUserAdminHelper extends AndroidTestCase {
+
+ private DevicePolicyManager mDpm;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mDpm = (DevicePolicyManager) mContext.getSystemService(Context.DEVICE_POLICY_SERVICE);
+ }
+
+ /**
+ * Device admin can only be deactivated by itself and this test should be executed before the
+ * device admin package can be uninstalled.
+ */
+ public void testClearDeviceAdmin() {
+ try {
+ removeActiveAdmin(mDpm, PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT);
+ } catch (InterruptedException e) {
+ fail("Removal of device admin interrupted.");
+ }
+ assertFalse(mDpm.isAdminActive(PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT));
+ }
+
+ /**
+ * Set lockscreen password.
+ */
+ public void testSetPassword() {
+ // Enable credential storage by setting a nonempty password.
+ assertTrue(mDpm.resetPassword("test", 0));
+ }
+
+ /**
+ * Clear lockscreen password.
+ */
+ public void testClearPassword() {
+ mDpm.setPasswordQuality(PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT,
+ DevicePolicyManager.PASSWORD_QUALITY_UNSPECIFIED);
+ mDpm.setPasswordMinimumLength(
+ PrimaryUserDeviceAdmin.ADMIN_RECEIVER_COMPONENT, 0);
+ assertTrue(mDpm.resetPassword("", 0));
+ }
+
+ private void removeActiveAdmin(DevicePolicyManager dpm, ComponentName cn)
+ throws InterruptedException {
+ if (mDpm.isAdminActive(cn)) {
+ mDpm.removeActiveAdmin(cn);
+ // Wait until device admin is not active.
+ for (int i = 0; i < 1000 && dpm.isAdminActive(cn); i++) {
+ Thread.sleep(100);
+ }
+ }
+ }
+}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserDeviceAdmin.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserDeviceAdmin.java
new file mode 100644
index 0000000..8662daf
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/PrimaryUserDeviceAdmin.java
@@ -0,0 +1,29 @@
+/*
+ * 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.managedprofile;
+
+import android.app.admin.DeviceAdminReceiver;
+import android.content.ComponentName;
+
+/**
+ * A device admin class running in the primary user. Currently used by delegated cert installer
+ * test to set a lockscreen password which is prerequisite of installKeyPair().
+ */
+public class PrimaryUserDeviceAdmin extends DeviceAdminReceiver {
+ public static final ComponentName ADMIN_RECEIVER_COMPONENT = new ComponentName(
+ PrimaryUserDeviceAdmin.class.getPackage().getName(),
+ PrimaryUserDeviceAdmin.class.getName());
+}
\ No newline at end of file
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
index 7e0d959..5904a6f 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/BaseDevicePolicyTest.java
@@ -335,4 +335,12 @@
assertTrue(commandOutput + " expected to start with \"Success:\"",
commandOutput.startsWith("Success:"));
}
+
+ protected void setDeviceAdmin(String componentName) throws DeviceNotAvailableException {
+ String command = "dpm set-active-admin '" + componentName + "'";
+ String commandOutput = getDevice().executeShellCommand(command);
+ CLog.logAndDisplay(LogLevel.INFO, "Output for command " + command + ": " + commandOutput);
+ assertTrue(commandOutput + " expected to start with \"Success:\"",
+ commandOutput.startsWith("Success:"));
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 3954bda..5c2048e 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -36,6 +36,9 @@
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 CERT_INSTALLER_PKG = "com.android.cts.certinstaller";
+ private static final String CERT_INSTALLER_APK = "CtsCertInstallerApp.apk";
+
private static final String ADMIN_RECEIVER_TEST_CLASS =
MANAGED_PROFILE_PKG + ".BaseManagedProfileTest$BasicAdminReceiver";
@@ -66,6 +69,7 @@
getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
getDevice().uninstallPackage(INTENT_SENDER_PKG);
getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+ getDevice().uninstallPackage(CERT_INSTALLER_PKG);
}
super.tearDown();
}
@@ -380,6 +384,28 @@
"testSetBluetoothContactSharingDisabled_setterAndGetter", mUserId));
}
+ public void testDelegatedCertInstaller() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
+ installApp(CERT_INSTALLER_APK);
+ setDeviceAdmin(MANAGED_PROFILE_PKG + "/.PrimaryUserDeviceAdmin");
+
+ final String adminHelperClass = ".PrimaryUserAdminHelper";
+ try {
+ assertTrue("Set lockscreen password failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ adminHelperClass, "testSetPassword", 0 /* user 0 */));
+ assertTrue("DelegatedCertInstaller failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ ".DelegatedCertInstallerTest", mUserId));
+ } finally {
+ // Reset lockscreen password and remove device admin.
+ assertTrue("Clear lockscreen password failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ adminHelperClass, "testClearPassword", 0 /* user 0 */));
+ assertTrue("Clear device admin failed", runDeviceTestsAsUser(MANAGED_PROFILE_PKG,
+ adminHelperClass, "testClearDeviceAdmin", 0 /* user 0 */));
+ }
+ }
+
private void disableActivityForUser(String activityName, int userId)
throws DeviceNotAvailableException {
String command = "am start -W --user " + userId
diff --git a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
index 94862e7..fec4b40 100644
--- a/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
+++ b/hostsidetests/dumpsys/src/android/dumpsys/cts/DumpsysHostTest.java
@@ -813,11 +813,76 @@
assertInteger(parts[4]); // chargeTimeRemaining
}
- private static void assertInteger(String input) {
+ /**
+ * Tests the output of "dumpsys gfxinfo framestats".
+ *
+ * @throws Exception
+ */
+ public void testGfxinfoFramestats() throws Exception {
+ final String MARKER = "---PROFILEDATA---";
+
+ String frameinfo = mDevice.executeShellCommand("dumpsys gfxinfo com.android.systemui framestats");
+ assertNotNull(frameinfo);
+ assertTrue(frameinfo.length() > 0);
+ int profileStart = frameinfo.indexOf(MARKER);
+ int profileEnd = frameinfo.indexOf(MARKER, profileStart + 1);
+ assertTrue(profileStart >= 0);
+ assertTrue(profileEnd > profileStart);
+ String profileData = frameinfo.substring(profileStart + MARKER.length(), profileEnd);
+ assertTrue(profileData.length() > 0);
+ boolean foundAtLeastOneRow = false;
+ try (BufferedReader reader = new BufferedReader(
+ new StringReader(profileData))) {
+ String line;
+ // First line needs to be the headers
+ while ((line = reader.readLine()) != null && line.isEmpty()) {}
+
+ assertNotNull(line);
+ assertTrue("First line was not the expected header",
+ line.startsWith("Flags,IntendedVsync,Vsync,OldestInputEvent" +
+ ",NewestInputEvent,HandleInputStart,AnimationStart" +
+ ",PerformTraversalsStart,DrawStart,SyncStart" +
+ ",IssueDrawCommandsStart,SwapBuffers,FrameCompleted"));
+
+ long[] numparts = new long[13];
+ while ((line = reader.readLine()) != null && !line.isEmpty()) {
+
+ String[] parts = line.split(",");
+ assertTrue(parts.length >= 13);
+ for (int i = 0; i < 13; i++) {
+ numparts[i] = assertInteger(parts[i]);
+ }
+ if (numparts[0] != 0) {
+ continue;
+ }
+ // assert VSYNC >= INTENDED_VSYNC
+ assertTrue(numparts[2] >= numparts[1]);
+ // assert time is flowing forwards, skipping index 3 & 4
+ // as those are input timestamps that may or may not be present
+ assertTrue(numparts[5] >= numparts[2]);
+ for (int i = 6; i < 13; i++) {
+ assertTrue("Index " + i + " did not flow forward, " +
+ numparts[i] + " not larger than " + numparts[i - 1],
+ numparts[i] >= numparts[i-1]);
+ }
+ long totalDuration = numparts[12] - numparts[1];
+ assertTrue("Frame did not take a positive amount of time to process",
+ totalDuration > 0);
+ assertTrue("Bogus frame duration, exceeds 100 seconds",
+ totalDuration < 100000000000L);
+ foundAtLeastOneRow = true;
+ }
+ }
+ assertTrue(foundAtLeastOneRow);
+ }
+
+ private static long assertInteger(String input) {
try {
- Long.parseLong(input);
+ return Long.parseLong(input);
} catch (NumberFormatException e) {
fail("Expected an integer but found \"" + input + "\"");
+ // Won't be hit, above throws AssertException
+ return -1;
}
}
diff --git a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
index 08a905b..41332c7 100644
--- a/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
+++ b/tests/tests/admin/src/android/admin/cts/DevicePolicyManagerTest.java
@@ -679,6 +679,19 @@
}
}
+ public void testInstallCaCert_failIfNotCertInstaller() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testInstallCaCert_failIfNotCertInstaller");
+ return;
+ }
+ try {
+ // Delegated cert installer is identified by using null as the first argument.
+ mDevicePolicyManager.installCaCert(null, TEST_CA_STRING1.getBytes());
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
public void testUninstallCaCert_failIfNotProfileOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testUninstallCaCert_failIfNotProfileOwner");
@@ -693,6 +706,19 @@
}
}
+ public void testUninstallCaCert_failIfNotCertInstaller() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testUninstallCaCert_failIfNotCertInstaller");
+ return;
+ }
+ try {
+ // Delegated cert installer is identified by using null as the first argument.
+ mDevicePolicyManager.uninstallCaCert(null, TEST_CA_STRING1.getBytes());
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
public void testGetInstalledCaCerts_failIfNotProfileOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testGetInstalledCaCerts_failIfNotProfileOwner");
@@ -706,6 +732,19 @@
}
}
+ public void testGetInstalledCaCerts_failIfNotCertInstaller() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testGetInstalledCaCerts_failIfNotCertInstaller");
+ return;
+ }
+ try {
+ // Delegated cert installer is identified by using null as the first argument.
+ mDevicePolicyManager.getInstalledCaCerts(null);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
public void testHasCaCertInstalled_failIfNotProfileOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testHasCaCertInstalled_failIfNotProfileOwner");
@@ -720,6 +759,19 @@
}
}
+ public void testHasCaCertInstalled_failIfNotCertInstaller() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testHasCaCertInstalled_failIfNotCertInstaller");
+ return;
+ }
+ try {
+ // Delegated cert installer is identified by using null as the first argument.
+ mDevicePolicyManager.hasCaCertInstalled(null, TEST_CA_STRING1.getBytes());
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
public void testUninstallAllUserCaCerts_failIfNotProfileOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testUninstallAllUserCaCerts_failIfNotProfileOwner");
@@ -733,6 +785,19 @@
}
}
+ public void testUninstallAllUserCaCerts_failIfNotCertInstaller() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testUninstallAllUserCaCerts_failIfNotCertInstaller");
+ return;
+ }
+ try {
+ // Delegated cert installer is identified by using null as the first argument.
+ mDevicePolicyManager.uninstallAllUserCaCerts(null);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException expected) {
+ }
+ }
+
public void testSetScreenCaptureDisabled_failIfNotProfileOwner() {
if (!mDeviceAdmin) {
Log.w(TAG, "Skipping testSetScreenCaptureDisabled_failIfNotProfileOwner");
@@ -961,4 +1026,30 @@
assertTrue("Password '" + password + "' failed on " + restriction, passwordResetResult);
assertTrue(mDevicePolicyManager.isActivePasswordSufficient());
}
+
+ public void testSetDelegatedCertInstaller_failIfNotProfileOwner() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testSetDelegatedCertInstaller_failIfNotProfileOwner");
+ return;
+ }
+ try {
+ mDevicePolicyManager.setCertInstallerPackage(mComponent, "com.test.package");
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ }
+
+ public void testGetDelegatedCertInstaller_failIfNotProfileOwner() {
+ if (!mDeviceAdmin) {
+ Log.w(TAG, "Skipping testGetDelegatedCertInstaller_failIfNotProfileOwner");
+ return;
+ }
+ try {
+ mDevicePolicyManager.getCertInstallerPackage(mComponent);
+ fail("did not throw expected SecurityException");
+ } catch (SecurityException e) {
+ assertProfileOwnerMessage(e.getMessage());
+ }
+ }
}
diff --git a/tests/tests/app/src/android/app/cts/ProgressDialogTest.java b/tests/tests/app/src/android/app/cts/ProgressDialogTest.java
index 038b57b..b2037b6 100644
--- a/tests/tests/app/src/android/app/cts/ProgressDialogTest.java
+++ b/tests/tests/app/src/android/app/cts/ProgressDialogTest.java
@@ -97,8 +97,7 @@
@UiThreadTest
public void testShow2() {
- ProgressDialog dialog = buildDialog();
- dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, false);
+ ProgressDialog dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, false);
/*
* note: the progress bar's style only supports indeterminate mode,
@@ -120,11 +119,11 @@
// cancelable is false
runTestOnUiThread(new Runnable() {
public void run() {
- ProgressDialog dialog = buildDialog();
- dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, true, false);
+ ProgressDialog dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, true, false);
dialog.setOnCancelListener(cL);
dialog.onBackPressed();
+ dialog.dismiss();
}
});
mInstrumentation.waitForIdleSync();
@@ -134,8 +133,7 @@
// cancelable is true
runTestOnUiThread(new Runnable() {
public void run() {
- ProgressDialog dialog = buildDialog();
- dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, true, true);
+ ProgressDialog dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, true, true);
assertFalse(mCanceled);
dialog.setOnCancelListener(cL);
dialog.onBackPressed();
@@ -156,10 +154,11 @@
// cancelable is false
runTestOnUiThread(new Runnable() {
public void run() {
- ProgressDialog dialog = buildDialog();
- dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, true, false, cL);
+ ProgressDialog dialog = ProgressDialog.show(
+ mContext, TITLE, MESSAGE, true, false, cL);
dialog.onBackPressed();
+ dialog.dismiss();;
}
});
mInstrumentation.waitForIdleSync();
@@ -169,8 +168,8 @@
// cancelable is true
runTestOnUiThread(new Runnable() {
public void run() {
- ProgressDialog dialog = buildDialog();
- dialog = ProgressDialog.show(mContext, TITLE, MESSAGE, true, true, cL);
+ ProgressDialog dialog = ProgressDialog.show(
+ mContext, TITLE, MESSAGE, true, true, cL);
assertFalse(mCanceled);
dialog.onBackPressed();
diff --git a/tests/tests/content/src/android/content/cts/ContentResolverTest.java b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
index 4176da3..628f956 100644
--- a/tests/tests/content/src/android/content/cts/ContentResolverTest.java
+++ b/tests/tests/content/src/android/content/cts/ContentResolverTest.java
@@ -58,6 +58,11 @@
private static final Uri SELF_URI = Uri.parse("content://" + AUTHORITY + "/self/");
private static final Uri CRASH_URI = Uri.parse("content://" + AUTHORITY + "/crash/");
+ private static final Uri LEVEL1_URI = Uri.parse("content://" + AUTHORITY + "/level/");
+ private static final Uri LEVEL2_URI = Uri.parse("content://" + AUTHORITY + "/level/child");
+ private static final Uri LEVEL3_URI = Uri.parse("content://" + AUTHORITY
+ + "/level/child/grandchild/");
+
private static final String REMOTE_AUTHORITY = "remotectstest";
private static final Uri REMOTE_TABLE1_URI = Uri.parse("content://"
+ REMOTE_AUTHORITY + "/testtable1/");
@@ -88,7 +93,7 @@
mContext = getContext();
mContentResolver = mContext.getContentResolver();
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+ MockContentProvider.setCrashOnLaunch(mContext, false);
// add three rows to database when every test case start.
ContentValues values = new ContentValues();
@@ -151,13 +156,12 @@
fail("Content provider process is not gone!");
}
try {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+ MockContentProvider.setCrashOnLaunch(mContext, true);
String type1 = mContentResolver.getType(REMOTE_TABLE1_URI);
- assertEquals(android.provider.Settings.System.getInt(mContentResolver,
- "__cts_crash_on_launch", 0), 0);
+ assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
assertTrue(type1.startsWith(ContentResolver.CURSOR_DIR_BASE_TYPE, 0));
} finally {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+ MockContentProvider.setCrashOnLaunch(mContext, false);
}
}
@@ -352,12 +356,11 @@
public void testCrashingQuery() {
try {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+ MockContentProvider.setCrashOnLaunch(mContext, true);
mCursor = mContentResolver.query(REMOTE_CRASH_URI, null, null, null, null);
- assertEquals(android.provider.Settings.System.getInt(mContentResolver,
- "__cts_crash_on_launch", 0), 0);
+ assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
} finally {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+ MockContentProvider.setCrashOnLaunch(mContext, false);
}
assertNotNull(mCursor);
@@ -570,16 +573,15 @@
public void testCrashingOpenAssetFileDescriptor() throws IOException {
AssetFileDescriptor afd = null;
try {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+ MockContentProvider.setCrashOnLaunch(mContext, true);
afd = mContentResolver.openAssetFileDescriptor(REMOTE_CRASH_URI, "rw");
- assertEquals(android.provider.Settings.System.getInt(mContentResolver,
- "__cts_crash_on_launch", 0), 0);
+ assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
assertNotNull(afd);
String str = consumeAssetFileDescriptor(afd);
afd = null;
assertEquals(str, "This is the openAssetFile test data!");
} finally {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+ MockContentProvider.setCrashOnLaunch(mContext, false);
if (afd != null) {
afd.close();
}
@@ -602,17 +604,16 @@
public void testCrashingOpenTypedAssetFileDescriptor() throws IOException {
AssetFileDescriptor afd = null;
try {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 1);
+ MockContentProvider.setCrashOnLaunch(mContext, true);
afd = mContentResolver.openTypedAssetFileDescriptor(
REMOTE_CRASH_URI, "text/plain", null);
- assertEquals(android.provider.Settings.System.getInt(mContentResolver,
- "__cts_crash_on_launch", 0), 0);
+ assertFalse(MockContentProvider.getCrashOnLaunch(mContext));
assertNotNull(afd);
String str = consumeAssetFileDescriptor(afd);
afd = null;
assertEquals(str, "This is the openTypedAssetFile test data!");
} finally {
- android.provider.Settings.System.putInt(mContentResolver, "__cts_crash_on_launch", 0);
+ MockContentProvider.setCrashOnLaunch(mContext, false);
if (afd != null) {
afd.close();
}
@@ -957,6 +958,53 @@
}
}
+ public void testRegisterContentObserverDescendantBehavior() throws Exception {
+ final MockContentObserver mco1 = new MockContentObserver();
+ final MockContentObserver mco2 = new MockContentObserver();
+
+ // Register one content observer with notifyDescendants set to false, and
+ // another with true.
+ mContentResolver.registerContentObserver(LEVEL2_URI, false, mco1);
+ mContentResolver.registerContentObserver(LEVEL2_URI, true, mco2);
+
+ // Initially nothing has happened.
+ assertFalse(mco1.hadOnChanged());
+ assertFalse(mco2.hadOnChanged());
+
+ // Fire a change with the exact URI.
+ // Should signal both observers due to exact match, notifyDescendants doesn't matter.
+ mContentResolver.notifyChange(LEVEL2_URI, null);
+ Thread.sleep(200);
+ assertTrue(mco1.hadOnChanged());
+ assertTrue(mco2.hadOnChanged());
+ mco1.reset();
+ mco2.reset();
+
+ // Fire a change with a descendant URI.
+ // Should only signal observer with notifyDescendants set to true.
+ mContentResolver.notifyChange(LEVEL3_URI, null);
+ Thread.sleep(200);
+ assertFalse(mco1.hadOnChanged());
+ assertTrue(mco2.hadOnChanged());
+ mco2.reset();
+
+ // Fire a change with an ancestor URI.
+ // Should signal both observers due to ancestry, notifyDescendants doesn't matter.
+ mContentResolver.notifyChange(LEVEL1_URI, null);
+ Thread.sleep(200);
+ assertTrue(mco1.hadOnChanged());
+ assertTrue(mco2.hadOnChanged());
+ mco1.reset();
+ mco2.reset();
+
+ // Fire a change with an unrelated URI.
+ // Should signal neither observer.
+ mContentResolver.notifyChange(TABLE1_URI, null);
+ Thread.sleep(200);
+ assertFalse(mco1.hadOnChanged());
+ assertFalse(mco2.hadOnChanged());
+ }
+
public void testNotifyChange1() {
final MockContentObserver mco = new MockContentObserver();
diff --git a/tests/tests/content/src/android/content/cts/MockContentProvider.java b/tests/tests/content/src/android/content/cts/MockContentProvider.java
index de82c0d..bddc82d 100644
--- a/tests/tests/content/src/android/content/cts/MockContentProvider.java
+++ b/tests/tests/content/src/android/content/cts/MockContentProvider.java
@@ -16,6 +16,7 @@
package android.content.cts;
+import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
@@ -28,6 +29,7 @@
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
+import android.content.SharedPreferences;
import android.content.UriMatcher;
import android.content.ContentProvider.PipeDataWriter;
import android.content.res.AssetFileDescriptor;
@@ -122,15 +124,7 @@
@Override
public boolean onCreate() {
mOpenHelper = new DatabaseHelper(getContext(), mDbName);
- if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0) != 0) {
- // The test case wants us to crash our process on first launch.
- // Well, okay then!
- Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
- android.provider.Settings.System.putInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0);
- android.os.Process.killProcess(android.os.Process.myPid());
- }
+ crashOnLaunchIfNeeded();
return true;
}
@@ -277,15 +271,7 @@
break;
case CRASH_ID:
- if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0) != 0) {
- // The test case wants us to crash while querying.
- // Well, okay then!
- Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
- android.provider.Settings.System.putInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0);
- android.os.Process.killProcess(android.os.Process.myPid());
- }
+ crashOnLaunchIfNeeded();
qb.setTables("TestTable1");
qb.setProjectionMap(CTSDBTABLE1_LIST_PROJECTION_MAP);
break;
@@ -351,15 +337,7 @@
public AssetFileDescriptor openAssetFile(Uri uri, String mode) throws FileNotFoundException {
switch (URL_MATCHER.match(uri)) {
case CRASH_ID:
- if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0) != 0) {
- // The test case wants us to crash while querying.
- // Well, okay then!
- Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
- android.provider.Settings.System.putInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0);
- android.os.Process.killProcess(android.os.Process.myPid());
- }
+ crashOnLaunchIfNeeded();
return new AssetFileDescriptor(
openPipeHelper(uri, null, null,
"This is the openAssetFile test data!", this), 0,
@@ -375,15 +353,7 @@
throws FileNotFoundException {
switch (URL_MATCHER.match(uri)) {
case CRASH_ID:
- if (android.provider.Settings.System.getInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0) != 0) {
- // The test case wants us to crash while querying.
- // Well, okay then!
- Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
- android.provider.Settings.System.putInt(getContext().getContentResolver(),
- "__cts_crash_on_launch", 0);
- android.os.Process.killProcess(android.os.Process.myPid());
- }
+ crashOnLaunchIfNeeded();
return new AssetFileDescriptor(
openPipeHelper(uri, null, null,
"This is the openTypedAssetFile test data!", this), 0,
@@ -414,4 +384,36 @@
}
}
}
+
+ private void crashOnLaunchIfNeeded() {
+ if (getCrashOnLaunch(getContext())) {
+ // The test case wants us to crash our process on first launch.
+ // Well, okay then!
+ Log.i("MockContentProvider", "TEST IS CRASHING SELF, CROSS FINGERS!");
+ setCrashOnLaunch(getContext(), false);
+ android.os.Process.killProcess(android.os.Process.myPid());
+ }
+ }
+
+ public static boolean getCrashOnLaunch(Context context) {
+ File file = getCrashOnLaunchFile(context);
+ return file.exists();
+ }
+
+ public static void setCrashOnLaunch(Context context, boolean value) {
+ File file = getCrashOnLaunchFile(context);
+ if (value) {
+ try {
+ file.createNewFile();
+ } catch (IOException ex) {
+ throw new RuntimeException("Could not create crash on launch file.", ex);
+ }
+ } else {
+ file.delete();
+ }
+ }
+
+ private static File getCrashOnLaunchFile(Context context) {
+ return context.getFileStreamPath("MockContentProvider.crashonlaunch");
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
index f062545..423577e 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -32,7 +32,6 @@
import android.hardware.camera2.params.InputConfiguration;
import android.hardware.camera2.TotalCaptureResult;
import android.hardware.cts.helpers.CameraUtils;
-import android.util.Size;
import android.hardware.camera2.params.MeteringRectangle;
import android.hardware.camera2.params.StreamConfigurationMap;
import android.media.Image;
@@ -41,6 +40,8 @@
import android.media.Image.Plane;
import android.os.Handler;
import android.util.Log;
+import android.util.Pair;
+import android.util.Size;
import android.view.Surface;
import com.android.ex.camera2.blocking.BlockingCameraManager;
@@ -315,13 +316,21 @@
new LinkedBlockingQueue<TotalCaptureResult>();
private final LinkedBlockingQueue<CaptureFailure> mFailureQueue =
new LinkedBlockingQueue<>();
+ // Pair<CaptureRequest, Long> is a pair of capture request and timestamp.
+ private final LinkedBlockingQueue<Pair<CaptureRequest, Long>> mCaptureStartQueue =
+ new LinkedBlockingQueue<>();
private AtomicLong mNumFramesArrived = new AtomicLong(0);
@Override
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
- long timestamp, long frameNumber)
- {
+ long timestamp, long frameNumber) {
+ try {
+ mCaptureStartQueue.put(new Pair(request, timestamp));
+ } catch (InterruptedException e) {
+ throw new UnsupportedOperationException(
+ "Can't handle InterruptedException in onCaptureStarted");
+ }
}
@Override
@@ -519,6 +528,37 @@
return failures;
}
+ /**
+ * Wait until the capture start of a request and expected timestamp arrives or it times
+ * out after a number of capture starts.
+ *
+ * @param request The request for the capture start to wait for.
+ * @param timestamp The timestamp for the capture start to wait for.
+ * @param numCaptureStartsWait The number of capture start events to wait for before timing
+ * out.
+ */
+ public void waitForCaptureStart(CaptureRequest request, Long timestamp,
+ int numCaptureStartsWait) throws Exception {
+ Pair<CaptureRequest, Long> expectedShutter = new Pair<>(request, timestamp);
+
+ int i = 0;
+ do {
+ Pair<CaptureRequest, Long> shutter = mCaptureStartQueue.poll(
+ CAPTURE_RESULT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
+
+ if (shutter == null) {
+ throw new TimeoutRuntimeException("Unable to get any more capture start " +
+ "event after waiting for " + CAPTURE_RESULT_TIMEOUT_MS + " ms.");
+ } else if (expectedShutter.equals(shutter)) {
+ return;
+ }
+
+ } while (i++ < numCaptureStartsWait);
+
+ throw new TimeoutRuntimeException("Unable to get the expected capture start " +
+ "event after waiting for " + numCaptureStartsWait + " capture starts");
+ }
+
public boolean hasMoreResults()
{
return mQueue.isEmpty();
@@ -528,6 +568,7 @@
mQueue.clear();
mNumFramesArrived.getAndSet(0);
mFailureQueue.clear();
+ mCaptureStartQueue.clear();
}
}
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
index 4061412..a115fbe 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/ReprocessCaptureTest.java
@@ -77,7 +77,8 @@
SINGLE_SHOT,
BURST,
MIXED_BURST,
- ABORT_CAPTURE
+ ABORT_CAPTURE,
+ TIMESTAMPS
}
/**
@@ -366,6 +367,35 @@
}
/**
+ * Test reprocess timestamps for the largest input and output sizes for each supported format.
+ */
+ public void testReprocessTimestamps() throws Exception {
+ for (String id : mCameraIds) {
+ if (!isYuvReprocessSupported(id) && !isOpaqueReprocessSupported(id)) {
+ continue;
+ }
+
+ try {
+ // open Camera device
+ openDevice(id);
+
+ int[] supportedInputFormats =
+ mStaticInfo.getAvailableFormats(StaticMetadata.StreamDirection.Input);
+ for (int inputFormat : supportedInputFormats) {
+ int[] supportedReprocessOutputFormats =
+ mStaticInfo.getValidOutputFormatsForInput(inputFormat);
+ for (int reprocessOutputFormat : supportedReprocessOutputFormats) {
+ testReprocessingMaxSizes(id, inputFormat, reprocessOutputFormat,
+ /*previewSize*/null, CaptureTestCase.TIMESTAMPS);
+ }
+ }
+ } finally {
+ closeDevice();
+ }
+ }
+ }
+
+ /**
* Test the input format and output format with the largest input and output sizes.
*/
private void testBasicReprocessing(String cameraId, int inputFormat,
@@ -400,6 +430,10 @@
testReprocessAbort(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize,
reprocessOutputFormat);
break;
+ case TIMESTAMPS:
+ testReprocessTimestamps(cameraId, maxInputSize, inputFormat, maxReprocessOutputSize,
+ reprocessOutputFormat);
+ break;
default:
throw new IllegalArgumentException("Invalid test case");
}
@@ -742,6 +776,89 @@
}
/**
+ * Test timestamps for reprocess requests. Reprocess request's shutter timestamp, result's
+ * sensor timestamp, and output image's timestamp should match the reprocess input's timestamp.
+ */
+ private void testReprocessTimestamps(String cameraId, Size inputSize, int inputFormat,
+ Size reprocessOutputSize, int reprocessOutputFormat) throws Exception {
+ if (VERBOSE) {
+ Log.v(TAG, "testReprocessTimestamps: cameraId: " + cameraId + " inputSize: " +
+ inputSize + " inputFormat: " + inputFormat + " reprocessOutputSize: " +
+ reprocessOutputSize + " reprocessOutputFormat: " + reprocessOutputFormat);
+ }
+
+ try {
+ setupImageReaders(inputSize, inputFormat, reprocessOutputSize, reprocessOutputFormat,
+ NUM_REPROCESS_CAPTURES);
+ setupReprocessableSession(/*previewSurface*/null, NUM_REPROCESS_CAPTURES);
+
+ // Prepare reprocess capture requests.
+ ArrayList<CaptureRequest> reprocessRequests = new ArrayList<>(NUM_REPROCESS_CAPTURES);
+ ArrayList<Long> expectedTimestamps = new ArrayList<>(NUM_REPROCESS_CAPTURES);
+
+ for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) {
+ TotalCaptureResult result = submitCaptureRequest(mFirstImageReader.getSurface(),
+ /*inputResult*/null);
+
+ mImageWriter.queueInputImage(
+ mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS));
+ CaptureRequest.Builder builder = mCamera.createReprocessCaptureRequest(result);
+ if (mShareOneImageReader) {
+ builder.addTarget(mFirstImageReader.getSurface());
+ } else {
+ builder.addTarget(mSecondImageReader.getSurface());
+ }
+ reprocessRequests.add(builder.build());
+ // Reprocess result's timestamp should match input image's timestamp.
+ expectedTimestamps.add(result.get(CaptureResult.SENSOR_TIMESTAMP));
+ }
+
+ // Submit reprocess requests.
+ SimpleCaptureCallback captureCallback = new SimpleCaptureCallback();
+ mSession.captureBurst(reprocessRequests, captureCallback, mHandler);
+
+ // Verify we get the expected timestamps.
+ for (int i = 0; i < reprocessRequests.size(); i++) {
+ captureCallback.waitForCaptureStart(reprocessRequests.get(i),
+ expectedTimestamps.get(i), CAPTURE_TIMEOUT_FRAMES);
+ }
+
+ TotalCaptureResult[] reprocessResults =
+ captureCallback.getTotalCaptureResultsForRequests(reprocessRequests,
+ CAPTURE_TIMEOUT_FRAMES);
+
+ for (int i = 0; i < expectedTimestamps.size(); i++) {
+ // Verify the result timestamps match the input image's timestamps.
+ long expected = expectedTimestamps.get(i);
+ long timestamp = reprocessResults[i].get(CaptureResult.SENSOR_TIMESTAMP);
+ assertEquals("Reprocess result timestamp (" + timestamp + ") doesn't match input " +
+ "image's timestamp (" + expected + ")", expected, timestamp);
+
+ // Verify the reprocess output image timestamps match the input image's timestamps.
+ Image image;
+ if (mShareOneImageReader) {
+ image = mFirstImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+ } else {
+ image = mSecondImageReaderListener.getImage(CAPTURE_TIMEOUT_MS);
+ }
+ timestamp = image.getTimestamp();
+ image.close();
+
+ assertEquals("Reprocess output timestamp (" + timestamp + ") doesn't match input " +
+ "image's timestamp (" + expected + ")", expected, timestamp);
+ }
+
+ // Make sure all input surfaces are released.
+ for (int i = 0; i < NUM_REPROCESS_CAPTURES; i++) {
+ mImageWriterListener.waitForImageReleased(CAPTURE_TIMEOUT_MS);
+ }
+ } finally {
+ closeReprossibleSession();
+ closeImageReaders();
+ }
+ }
+
+ /**
* Set up two image readers: one for regular capture (used for reprocess input) and one for
* reprocess capture.
*/
diff --git a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
index bf0d6cf..f513709 100644
--- a/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
+++ b/tests/tests/media/src/android/media/cts/EnumDevicesTest.java
@@ -37,11 +37,6 @@
boolean mAddCallbackCalled = false;
boolean mRemoveCallbackCalled = false;
- static {
- // We're going to use a Handler
- Looper.prepare();
- }
-
@Override
protected void setUp() throws Exception {
super.setUp();
@@ -100,6 +95,10 @@
deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_ALL);
for (int index = 0; index < deviceList.length; index++) {
AudioDeviceInfo deviceInfo = deviceList[index];
+
+ // we don't say anything about the returned value.
+ int id = deviceInfo.getId();
+
// Product Name
CharSequence productName = deviceInfo.getProductName();
assertNotNull(productName);
@@ -152,16 +151,56 @@
}
}
+ /*
+ * tests if the Looper for the current thread has been prepared,
+ * If not, it makes one, prepares it and returns it.
+ * If this returns non-null, the caller is reponsible for calling quit()
+ * on the returned Looper.
+ */
+ private Looper prepareIfNeededLooper() {
+ // non-null Handler
+ Looper myLooper = null;
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ myLooper = Looper.myLooper();
+ assertNotNull(myLooper);
+ }
+ return myLooper;
+ }
+
public void test_deviceCallback() {
+ // null callback?
mAudioManager.registerAudioDeviceCallback(null,null);
- assertTrue(true);
AudioDeviceCallback callback = new EmptyDeviceCallback();
+ AudioDeviceCallback someOtherCallback = new EmptyDeviceCallback();
+ // null Handler
mAudioManager.registerAudioDeviceCallback(callback, null);
- assertTrue(true);
+ // unregister null callback
+ mAudioManager.unregisterAudioDeviceCallback(null);
+ // unregister callback not registered
+ mAudioManager.unregisterAudioDeviceCallback(someOtherCallback);
+ // nominal case
+ mAudioManager.unregisterAudioDeviceCallback(callback);
+ // remove twice
+ mAudioManager.unregisterAudioDeviceCallback(callback);
+
+ Looper myLooper = prepareIfNeededLooper();
+
mAudioManager.registerAudioDeviceCallback(callback, new Handler());
- assertTrue(true);
+ // unregister null callback
+ mAudioManager.unregisterAudioDeviceCallback(null);
+ // unregister callback not registered
+ mAudioManager.unregisterAudioDeviceCallback(someOtherCallback);
+ // nominal case
+ mAudioManager.unregisterAudioDeviceCallback(callback);
+ // remove twice
+ mAudioManager.unregisterAudioDeviceCallback(callback);
+
+ if (myLooper != null) {
+ myLooper.quit();
+ }
}
//TODO - Need tests for device connect/disconnect callbacks
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
index b96d38c..2473078 100644
--- a/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCencPlayer.java
@@ -248,12 +248,14 @@
if (null == mCrypto && (mEncryptedVideo || mEncryptedAudio)) {
try {
- mCrypto = new MediaCrypto(CLEARKEY_SCHEME_UUID, mSessionId);
+ byte[] initData = new byte[0];
+ mCrypto = new MediaCrypto(CLEARKEY_SCHEME_UUID, initData);
} catch (MediaCryptoException e) {
reset();
Log.e(TAG, "Failed to create MediaCrypto instance.");
throw e;
}
+ mCrypto.setMediaDrmSession(mSessionId);
} else {
reset();
mCrypto.release();
diff --git a/tests/tests/media/src/android/media/cts/RoutingTest.java b/tests/tests/media/src/android/media/cts/RoutingTest.java
index fcb61dd..c0e9a3e 100644
--- a/tests/tests/media/src/android/media/cts/RoutingTest.java
+++ b/tests/tests/media/src/android/media/cts/RoutingTest.java
@@ -26,10 +26,15 @@
import android.media.AudioTrack;
import android.media.MediaRecorder;
+import android.os.Handler;
+import android.os.Looper;
+
import android.test.AndroidTestCase;
import android.util.Log;
+import java.lang.Runnable;
+
/**
* TODO: Insert description here. (generated by pmclean)
*/
@@ -47,17 +52,12 @@
assertNotNull(mAudioManager);
}
- public void test_audioTrack_preferredDevice() {
- if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
- // Can't do it so skip this test
- return;
- }
-
+ private AudioTrack allocAudioTrack() {
int bufferSize =
- AudioTrack.getMinBufferSize(
- 41000,
- AudioFormat.CHANNEL_OUT_STEREO,
- AudioFormat.ENCODING_PCM_16BIT);
+ AudioTrack.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
AudioTrack audioTrack =
new AudioTrack(
AudioManager.STREAM_MUSIC,
@@ -66,6 +66,16 @@
AudioFormat.ENCODING_PCM_16BIT,
bufferSize,
AudioTrack.MODE_STREAM);
+ return audioTrack;
+ }
+
+ public void test_audioTrack_preferredDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUDIO_OUTPUT)) {
+ // Can't do it so skip this test
+ return;
+ }
+
+ AudioTrack audioTrack = allocAudioTrack();
assertNotNull(audioTrack);
// None selected (new AudioTrack), so check for default
@@ -76,7 +86,7 @@
// test each device
AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_OUTPUTS);
- for(int index = 0; index < deviceList.length; index++) {
+ for (int index = 0; index < deviceList.length; index++) {
assertTrue(audioTrack.setPreferredDevice(deviceList[index]));
assertTrue(audioTrack.getPreferredDevice() == deviceList[index]);
}
@@ -84,6 +94,104 @@
// Check defaults again
assertTrue(audioTrack.setPreferredDevice(null));
assertNull(audioTrack.getPreferredDevice());
+
+ audioTrack.release();
+ }
+
+ /*
+ * tests if the Looper for the current thread has been prepared,
+ * If not, it makes one, prepares it and returns it.
+ * If this returns non-null, the caller is reponsible for calling quit()
+ * on the returned Looper.
+ */
+ private Looper prepareIfNeededLooper() {
+ // non-null Handler
+ Looper myLooper = null;
+ if (Looper.myLooper() == null) {
+ Looper.prepare();
+ myLooper = Looper.myLooper();
+ assertNotNull(myLooper);
+ }
+ return myLooper;
+ }
+
+ private class AudioTrackRoutingListener implements AudioTrack.OnRoutingChangedListener {
+ public void onRoutingChanged(AudioTrack audioTrack) {}
+ }
+
+ public void test_audioTrack_RoutingListener() {
+ AudioTrack audioTrack = allocAudioTrack();
+
+ audioTrack.addOnRoutingChangedListener(null, null);
+
+ AudioTrackRoutingListener listener = new AudioTrackRoutingListener();
+ AudioTrackRoutingListener someOtherListener = new AudioTrackRoutingListener();
+
+ audioTrack.addOnRoutingChangedListener(listener, null);
+
+ // remove a listener we didn't add
+ audioTrack.removeOnRoutingChangedListener(someOtherListener);
+
+ audioTrack.removeOnRoutingChangedListener(listener);
+
+ Looper myLooper = prepareIfNeededLooper();
+ audioTrack.addOnRoutingChangedListener(listener, new Handler());
+
+ audioTrack.removeOnRoutingChangedListener(listener);
+
+ audioTrack.release();
+ if (myLooper != null) {
+ myLooper.quit();
+ }
+ }
+
+ private AudioRecord allocAudioRecord() {
+ int bufferSize =
+ AudioRecord.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.ENCODING_PCM_16BIT);
+ AudioRecord audioRecord =
+ new AudioRecord(
+ MediaRecorder.AudioSource.DEFAULT,
+ 41000, AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.ENCODING_PCM_16BIT,
+ bufferSize);
+ return audioRecord;
+ }
+
+ private class AudioRecordRoutingListener implements AudioRecord.OnRoutingChangedListener {
+ public void onRoutingChanged(AudioRecord audioRecord) {}
+ }
+
+ public void test_audioRecord_RoutingListener() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ // Can't do it so skip this test
+ return;
+ }
+ AudioRecord audioRecord = allocAudioRecord();
+
+ audioRecord.addOnRoutingChangedListener(null, null);
+
+ AudioRecordRoutingListener listener = new AudioRecordRoutingListener();
+ AudioRecordRoutingListener someOtherListener = new AudioRecordRoutingListener();
+
+ audioRecord.addOnRoutingChangedListener(listener, null);
+
+ // remove a listener we didn't add
+ audioRecord.removeOnRoutingChangedListener(someOtherListener);
+
+ audioRecord.removeOnRoutingChangedListener(listener);
+
+ Looper myLooper = prepareIfNeededLooper();
+ audioRecord.addOnRoutingChangedListener(listener, new Handler());
+
+ audioRecord.removeOnRoutingChangedListener(listener);
+
+ audioRecord.release();
+ if (myLooper != null) {
+ myLooper.quit();
+ }
}
public void test_audioRecord_preferredDevice() {
@@ -92,17 +200,7 @@
return;
}
- int bufferSize =
- AudioRecord.getMinBufferSize(
- 41000,
- AudioFormat.CHANNEL_OUT_DEFAULT,
- AudioFormat.ENCODING_PCM_16BIT);
- AudioRecord audioRecord =
- new AudioRecord(
- MediaRecorder.AudioSource.DEFAULT,
- 41000, AudioFormat.CHANNEL_OUT_DEFAULT,
- AudioFormat.ENCODING_PCM_16BIT,
- bufferSize);
+ AudioRecord audioRecord = allocAudioRecord();
assertNotNull(audioRecord);
// None selected (new AudioRecord), so check for default
@@ -113,7 +211,7 @@
// test each device
AudioDeviceInfo[] deviceList = mAudioManager.getDevices(AudioManager.GET_DEVICES_INPUTS);
- for(int index = 0; index < deviceList.length; index++) {
+ for (int index = 0; index < deviceList.length; index++) {
assertTrue(audioRecord.setPreferredDevice(deviceList[index]));
assertTrue(audioRecord.getPreferredDevice() == deviceList[index]);
}
@@ -121,5 +219,133 @@
// Check defaults again
assertTrue(audioRecord.setPreferredDevice(null));
assertNull(audioRecord.getPreferredDevice());
+
+ audioRecord.release();
+ }
+
+ private class AudioTrackFiller implements Runnable {
+ AudioTrack mAudioTrack;
+ int mBufferSize;
+
+ boolean mPlaying;
+
+ short[] mAudioData;
+
+ public AudioTrackFiller(AudioTrack audioTrack, int bufferSize) {
+ mAudioTrack = audioTrack;
+ mBufferSize = bufferSize;
+ mPlaying = false;
+
+ // setup audio data (silence will suffice)
+ mAudioData = new short[mBufferSize];
+ for (int index = 0; index < mBufferSize; index++) {
+ mAudioData[index] = 0;
+ }
+ }
+
+ public void start() { mPlaying = true; }
+ public void stop() { mPlaying = false; }
+
+ @Override
+ public void run() {
+ while (mAudioTrack != null && mPlaying) {
+ mAudioTrack.write(mAudioData, 0, mBufferSize);
+ }
+ }
+ }
+
+ public void test_audioTrack_getRoutedDevice() {
+ int bufferSize =
+ AudioTrack.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT);
+ AudioTrack audioTrack =
+ new AudioTrack(
+ AudioManager.STREAM_MUSIC,
+ 41000,
+ AudioFormat.CHANNEL_OUT_STEREO,
+ AudioFormat.ENCODING_PCM_16BIT,
+ bufferSize,
+ AudioTrack.MODE_STREAM);
+
+ AudioTrackFiller filler = new AudioTrackFiller(audioTrack, bufferSize);
+ filler.start();
+
+ audioTrack.play();
+
+ Thread fillerThread = new Thread(filler);
+ fillerThread.start();
+
+ try { Thread.sleep(1000); } catch (InterruptedException ex) {}
+
+ // No explicit route
+ AudioDeviceInfo routedDevice = audioTrack.getRoutedDevice();
+ assertNotNull(routedDevice); // we probably can't say anything more than this
+
+ filler.stop();
+ audioTrack.stop();
+ audioTrack.release();
+ }
+
+ private class AudioRecordPuller implements Runnable {
+ AudioRecord mAudioRecord;
+ int mBufferSize;
+
+ boolean mRecording;
+
+ short[] mAudioData;
+
+ public AudioRecordPuller(AudioRecord audioRecord, int bufferSize) {
+ mAudioRecord = audioRecord;
+ mBufferSize = bufferSize;
+ mRecording = false;
+ }
+
+ public void start() { mRecording = true; }
+ public void stop() { mRecording = false; }
+
+ @Override
+ public void run() {
+ while (mAudioRecord != null && mRecording) {
+ mAudioRecord.read(mAudioData, 0, mBufferSize);
+ }
+ }
+ }
+
+ public void test_audioRecord_getRoutedDevice() {
+ if (!mContext.getPackageManager().hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
+ return;
+ }
+
+ int bufferSize =
+ AudioRecord.getMinBufferSize(
+ 41000,
+ AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.ENCODING_PCM_16BIT);
+ AudioRecord audioRecord =
+ new AudioRecord(
+ MediaRecorder.AudioSource.DEFAULT,
+ 41000, AudioFormat.CHANNEL_OUT_DEFAULT,
+ AudioFormat.ENCODING_PCM_16BIT,
+ bufferSize);
+
+ AudioRecordPuller puller = new AudioRecordPuller(audioRecord, bufferSize);
+ puller.start();
+
+ audioRecord.startRecording();
+
+ Thread pullerThread = new Thread(puller);
+ pullerThread.start();
+
+ try { Thread.sleep(1000); } catch (InterruptedException ex) {}
+
+ // No explicit route
+ AudioDeviceInfo routedDevice = audioRecord.getRoutedDevice();
+ assertNotNull(routedDevice); // we probably can't say anything more than this
+
+ puller.stop();
+ audioRecord.stop();
+ audioRecord.release();
}
}
diff --git a/tests/tests/os/src/android/os/cts/MessageQueueTest.java b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
index 8906c42..0c051bf 100644
--- a/tests/tests/os/src/android/os/cts/MessageQueueTest.java
+++ b/tests/tests/os/src/android/os/cts/MessageQueueTest.java
@@ -25,6 +25,8 @@
import android.os.ParcelFileDescriptor;
import android.os.ParcelFileDescriptor.AutoCloseInputStream;
import android.os.ParcelFileDescriptor.AutoCloseOutputStream;
+import android.system.ErrnoException;
+import android.system.Os;
import android.os.SystemClock;
import android.os.MessageQueue.IdleHandler;
import android.test.AndroidTestCase;
@@ -66,7 +68,7 @@
}
}
- private enum Test {ADD_IDLE_HANDLER, REMOVE_IDLE_HANDLER};
+ private enum Test {ADD_IDLE_HANDLER, REMOVE_IDLE_HANDLER}
/**
* {@link HandlerThread} that adds or removes an idle handler depending on the {@link Test}
@@ -91,6 +93,7 @@
super.onLooperPrepared();
IdleHandler idleHandler = new IdleHandler() {
+ @Override
public boolean queueIdle() {
mIdleLatch.countDown();
return false;
@@ -168,6 +171,7 @@
public void testMessageOrder() throws Exception {
OrderTestHelper tester = new OrderTestHelper() {
+ @Override
public void init() {
super.init();
long now = SystemClock.uptimeMillis() + 200;
@@ -191,6 +195,7 @@
OrderTestHelper tester = new OrderTestHelper() {
+ @Override
public void init() {
super.init();
long now = SystemClock.uptimeMillis() + 200;
@@ -200,6 +205,7 @@
mHandler.sendMessageAtFrontOfQueue(mHandler.obtainMessage(0));
}
+ @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 0) {
@@ -386,9 +392,9 @@
* edge cases around closing file descriptors within the callback and adding
* new ones with the same number.
*
- * Register a file descriptor, close it from within the callback before
- * returning, return. Then create a new file descriptor (with the same number),
- * register it. Ensure that we start getting events for the new file descriptor.
+ * Register a file descriptor, close it from within the callback, then return.
+ * Later, create a new file descriptor register it. Ensure that we start getting
+ * events for the new file descriptor.
*
* This test exercises special logic in Looper.cpp for EPOLL_CTL_DEL handling EBADF.
*/
@@ -401,23 +407,19 @@
final Handler handler = new Handler(thread.getLooper());
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- final int oldReaderFd = pipe[0].getFd();
try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
- final boolean[] awoke = new boolean[1];
+ final CountDownLatch awoke = new CountDownLatch(1);
queue.addOnFileDescriptorEventListener(reader.getFD(),
- OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
+ OnFileDescriptorEventListener.EVENT_ERROR,
+ new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke[0] = true;
+ awoke.countDown();
- // Close the file descriptor before we return.
- try {
- reader.close();
- } catch (IOException ex) {
- throw new RuntimeException(ex);
- }
+ // Close the reader before we return.
+ closeQuietly(reader);
// Return 0 to unregister the callback.
return 0;
@@ -428,26 +430,23 @@
writer.close();
// Wait for the looper to catch up and run the callback.
+ assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
syncWait(handler);
- assertTrue(awoke[0]);
}
// At this point, the reader and writer are both closed.
- // If we're lucky, we can create a new pipe with the same file
- // descriptor numbers as before.
+ // Make a new pipe and ensure that things still work as expected.
final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
- assertEquals("Expected new pipe to be created with same fd number as "
- + "previous pipe we just closed for the purpose of this test.",
- oldReaderFd, pipe2[0].getFd());
try (final FileInputStream reader2 = new AutoCloseInputStream(pipe2[0]);
final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) {
// Register the callback.
- final boolean[] awoke = new boolean[1];
+ final CountDownLatch awoke = new CountDownLatch(1);
queue.addOnFileDescriptorEventListener(reader2.getFD(),
- OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
+ OnFileDescriptorEventListener.EVENT_INPUT,
+ new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke[0] = true;
+ awoke.countDown();
// Return 0 to unregister the callback.
return 0;
@@ -458,8 +457,8 @@
writer2.close();
// Wait for the looper to catch up and run the callback.
+ assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
syncWait(handler);
- assertTrue(awoke[0]);
}
} finally {
thread.quitAndRethrow();
@@ -471,10 +470,10 @@
* edge cases around closing file descriptors within the callback and adding
* new ones with the same number.
*
- * Register a file descriptor, close it from within the callback before
- * returning, create a new file descriptor (with the same number) and return.
- * Then register the same file descriptor. Ensure that we start getting events for
- * the new file descriptor.
+ * Register a file descriptor, close it from within the callback, reassign its
+ * number to a different pipe, then return. Later, register the same file descriptor
+ * again (now referring to a new pipe). Ensure that we start getting
+ * events for the new file descriptor.
*
* This test exercises special logic in Looper.cpp for EPOLL_CTL_DEL handling ENOENT.
*/
@@ -487,69 +486,67 @@
final Handler handler = new Handler(thread.getLooper());
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- final int oldReaderFd = pipe[0].getFd();
- try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
- final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
- // Register the callback.
- final boolean[] awoke = new boolean[1];
- queue.addOnFileDescriptorEventListener(reader.getFD(),
- OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
- @Override
- public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke[0] = true;
+ final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
+ try {
+ final int oldReaderFd = pipe[0].getFd();
+ try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
+ final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
+ // Register the callback.
+ final CountDownLatch awoke = new CountDownLatch(1);
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_ERROR,
+ new OnFileDescriptorEventListener() {
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ awoke.countDown();
- try {
- // Close the file descriptor before we return.
- reader.close();
+ // Close the reader before we return and hijack its fd.
+ hijackFd(pipe2, pipe);
- // At this point, the reader and writer are both closed.
- // Assuming no one else has created a file descriptor in the meantime,
- // when we recreate the pipe we will get the same number as before.
- final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
- assertEquals("Expected new pipe to be created with same fd number as "
- + "previous pipe we just closed for the purpose of this test.",
- oldReaderFd, pipe2[0].getFd());
- pipe[0] = pipe2[0];
- pipe[1] = pipe2[1];
- } catch (IOException ex) {
- throw new RuntimeException(ex);
+ // Return 0 to unregister the callback.
+ return 0;
}
+ });
- // Return 0 to unregister the callback.
- return 0;
- }
- });
+ // Close the writer to wake up the callback (due to hangup).
+ writer.close();
- // Close the writer to wake up the callback (due to hangup).
- writer.close();
+ // Wait for the looper to catch up and run the callback.
+ assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
+ syncWait(handler);
+ }
- // Wait for the looper to catch up and run the callback.
- syncWait(handler);
- assertTrue(awoke[0]);
- }
+ // Now we have a new pipe with the same file descriptor, make sure we can
+ // register it successfully.
+ assertEquals(oldReaderFd, pipe2[0].getFd());
+ try (final FileInputStream reader2 = new AutoCloseInputStream(pipe2[0]);
+ final FileOutputStream writer2 = new AutoCloseOutputStream(pipe2[1])) {
+ // Register the callback.
+ final CountDownLatch awoke = new CountDownLatch(1);
+ queue.addOnFileDescriptorEventListener(reader2.getFD(),
+ OnFileDescriptorEventListener.EVENT_INPUT,
+ new OnFileDescriptorEventListener() {
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ awoke.countDown();
- // Now we have a new pipe, make sure we can register it successfully.
- try (final FileInputStream reader2 = new AutoCloseInputStream(pipe[0]);
- final FileOutputStream writer2 = new AutoCloseOutputStream(pipe[1])) {
- // Register the callback.
- final boolean[] awoke = new boolean[1];
- queue.addOnFileDescriptorEventListener(reader2.getFD(),
- OnFileDescriptorEventListener.EVENT_INPUT, new OnFileDescriptorEventListener() {
- @Override
- public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke[0] = true;
+ // Return 0 to unregister the callback.
+ return 0;
+ }
+ });
- // Return 0 to unregister the callback.
- return 0;
- }
- });
+ // Close the writer to wake up the callback (due to hangup).
+ writer2.close();
- // Close the writer to wake up the callback (due to hangup).
- writer2.close();
-
- // Wait for the looper to catch up and run the callback.
- syncWait(handler);
- assertTrue(awoke[0]);
+ // Wait for the looper to catch up and run the callback.
+ assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
+ syncWait(handler);
+ }
+ } finally {
+ closeQuietly(pipe[0]);
+ closeQuietly(pipe[1]);
+ closeQuietly(pipe2[0]);
+ closeQuietly(pipe2[1]);
}
} finally {
thread.quitAndRethrow();
@@ -561,10 +558,9 @@
* edge cases around closing file descriptors within the callback and adding
* new ones with the same number.
*
- * Register a file descriptor, close it from within the callback before
- * returning, create a new file descriptor (with the same number),
- * register it, and return. Ensure that we start getting events for the
- * new file descriptor.
+ * Register a file descriptor, close it from within the callback, reassign its
+ * number to a different pipe, register it, then return.
+ * Ensure that we start getting events for the new file descriptor.
*
* This test exercises special logic in Looper.cpp for EPOLL_CTL_MOD handling
* ENOENT and fallback to EPOLL_CTL_ADD as well as sequence number checks when removing
@@ -579,71 +575,66 @@
final Handler handler = new Handler(thread.getLooper());
final ParcelFileDescriptor[] pipe = ParcelFileDescriptor.createPipe();
- final boolean[] awoke2 = new boolean[1];
- final int oldReaderFd = pipe[0].getFd();
- try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
- final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
- // Register the callback.
- final boolean[] awoke = new boolean[1];
- queue.addOnFileDescriptorEventListener(reader.getFD(),
- OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
- @Override
- public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke[0] = true;
+ final ParcelFileDescriptor[] pipe2 = ParcelFileDescriptor.createPipe();
+ try {
+ final CountDownLatch awoke2 = new CountDownLatch(1);
+ final int oldReaderFd = pipe[0].getFd();
+ try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
+ final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
+ // Register the callback.
+ final CountDownLatch awoke = new CountDownLatch(1);
+ queue.addOnFileDescriptorEventListener(reader.getFD(),
+ OnFileDescriptorEventListener.EVENT_ERROR,
+ new OnFileDescriptorEventListener() {
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ awoke.countDown();
- final ParcelFileDescriptor[] pipe2;
- try {
- // Close the file descriptor before we return.
- reader.close();
+ // Close the reader before we return and hijack its fd.
+ hijackFd(pipe2, pipe);
- // At this point, the reader and writer are both closed.
- // Assuming no one else has created a file descriptor in the meantime,
- // when we recreate the pipe we will get the same number as before.
- pipe2 = ParcelFileDescriptor.createPipe();
- assertEquals("Expected new pipe to be created with same fd number as "
- + "previous pipe we just closed for the purpose of this test.",
- oldReaderFd, pipe2[0].getFd());
- pipe[0] = pipe2[0];
- pipe[1] = pipe2[1];
- } catch (IOException ex) {
- throw new RuntimeException(ex);
+ // Now we have a new pipe, make sure we can register it successfully.
+ queue.addOnFileDescriptorEventListener(pipe2[0].getFileDescriptor(),
+ OnFileDescriptorEventListener.EVENT_INPUT,
+ new OnFileDescriptorEventListener() {
+ @Override
+ public int onFileDescriptorEvents(FileDescriptor fd, int events) {
+ awoke2.countDown();
+
+ // Return 0 to unregister the callback.
+ return 0;
+ }
+ });
+
+ // Return 0 to unregister the callback.
+ return 0;
}
+ });
- // Now we have a new pipe, make sure we can register it successfully.
- queue.addOnFileDescriptorEventListener(pipe[0].getFileDescriptor(),
- OnFileDescriptorEventListener.EVENT_INPUT,
- new OnFileDescriptorEventListener() {
- @Override
- public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke2[0] = true;
+ // Close the writer to wake up the callback (due to hangup).
+ writer.close();
- // Return 0 to unregister the callback.
- return 0;
- }
- });
+ // Wait for the looper to catch up and run the callback.
+ assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
+ syncWait(handler);
+ }
- // Return 0 to unregister the callback.
- return 0;
- }
- });
-
- // Close the writer to wake up the callback (due to hangup).
- writer.close();
+ // Close the second writer to wake up the second callback (due to hangup).
+ pipe2[1].close();
// Wait for the looper to catch up and run the callback.
+ assertTrue("awoke2", awoke2.await(TIMEOUT, TimeUnit.MILLISECONDS));
syncWait(handler);
- assertTrue(awoke[0]);
+
+ // Close the second reader now that we're done with the test.
+ assertEquals(oldReaderFd, pipe2[0].getFd());
+ pipe2[0].close();
+ } finally {
+ closeQuietly(pipe[0]);
+ closeQuietly(pipe[1]);
+ closeQuietly(pipe2[0]);
+ closeQuietly(pipe2[1]);
}
-
- // Close the second writer to wake up the second callback (due to hangup).
- pipe[1].close();
-
- // Wait for the looper to catch up and run the callback.
- syncWait(handler);
- assertTrue(awoke2[0]);
-
- // Close the second reader now that we're done with the test.
- pipe[0].close();
} finally {
thread.quitAndRethrow();
}
@@ -655,7 +646,7 @@
* new ones with the same number.
*
* Register a file descriptor, make a duplicate of it, close it from within the
- * callback before returning, return. Look for signs that the Looper is spinning
+ * callback, then return. Look for signs that the Looper is spinning
* and never getting a chance to block.
*
* This test exercises special logic in Looper.cpp for rebuilding the epoll set
@@ -676,12 +667,12 @@
try (final FileInputStream reader = new AutoCloseInputStream(pipe[0]);
final FileOutputStream writer = new AutoCloseOutputStream(pipe[1])) {
// Register the callback.
- final boolean[] awoke = new boolean[1];
+ final CountDownLatch awoke = new CountDownLatch(1);
queue.addOnFileDescriptorEventListener(reader.getFD(),
OnFileDescriptorEventListener.EVENT_ERROR, new OnFileDescriptorEventListener() {
@Override
public int onFileDescriptorEvents(FileDescriptor fd, int events) {
- awoke[0] = true;
+ awoke.countDown();
// Close the file descriptor before we return.
try {
@@ -699,8 +690,8 @@
writer.close();
// Wait for the looper to catch up and run the callback.
+ assertTrue("awoke", awoke.await(TIMEOUT, TimeUnit.MILLISECONDS));
syncWait(handler);
- assertTrue(awoke[0]);
}
// Wait a little bit before we stop the thread.
@@ -729,6 +720,7 @@
private int mBarrierToken1;
private int mBarrierToken2;
+ @Override
public void init() {
super.init();
mLastMessage = 10;
@@ -741,6 +733,7 @@
mHandler.sendEmptyMessage(6);
}
+ @Override
public void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == 3) {
@@ -797,7 +790,35 @@
latch.countDown();
}
});
- latch.await(TIMEOUT, TimeUnit.MILLISECONDS);
+ assertTrue("Handler got stuck.", latch.await(TIMEOUT, TimeUnit.MILLISECONDS));
+ }
+
+ private static void closeQuietly(AutoCloseable c) {
+ if (c != null) {
+ try {
+ c.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ex) {
+ }
+ }
+ }
+
+ private static void hijackFd(ParcelFileDescriptor[] newPipe, ParcelFileDescriptor[] oldPipe) {
+ // Detach the old pipe's first fd and get its number.
+ int fd = oldPipe[0].detachFd();
+
+ // Assign the new pipe's first fd to the same number as the old pipe's first fd.
+ // This causes the old pipe's first fd to be closed and reassigned.
+ try {
+ Os.dup2(newPipe[0].getFileDescriptor(), fd);
+ } catch (ErrnoException ex) {
+ throw new RuntimeException(ex);
+ }
+
+ // Fix up the new pipe's first fd object.
+ closeQuietly(newPipe[0]);
+ newPipe[0] = ParcelFileDescriptor.adoptFd(fd);
}
/**
@@ -814,6 +835,7 @@
public void init() {
mHandler = new Handler() {
+ @Override
public void handleMessage(Message msg) {
OrderTestHelper.this.handleMessage(msg);
}
@@ -863,6 +885,7 @@
super("MessengerLooperThread");
}
+ @Override
public void onLooperPrepared() {
init();
mLooper = getLooper();
@@ -908,7 +931,7 @@
* A HandlerThread that propagates exceptions out of the event loop
* instead of crashing the process.
*/
- private class AssertableHandlerThread extends HandlerThread {
+ private static class AssertableHandlerThread extends HandlerThread {
private Throwable mThrowable;
private long mRuntime;
diff --git a/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java b/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java
new file mode 100644
index 0000000..9c38ec0
--- /dev/null
+++ b/tests/tests/provider/src/android/provider/cts/ContactsContract_ProviderStatus.java
@@ -0,0 +1,84 @@
+/*
+ * 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.provider.cts;
+
+import android.content.ContentProviderClient;
+import android.content.ContentResolver;
+import android.content.ContentValues;
+import android.database.Cursor;
+import android.provider.ContactsContract;
+import android.provider.ContactsContract.CommonDataKinds.StructuredName;
+import android.provider.ContactsContract.ProviderStatus;
+import android.provider.ContactsContract.RawContacts;
+import android.provider.cts.ContactsContract_TestDataBuilder.TestRawContact;
+import android.provider.cts.contacts.DatabaseAsserts;
+import android.test.AndroidTestCase;
+
+/**
+ * CTS tests for {@link android.provider.ContactsContract.ProviderStatus}.
+ *
+ * Unfortunately, we can't check that the value of ProviderStatus equals
+ * {@link ProviderStatus#STATUS_EMPTY} initially. Some carriers pre-install
+ * accounts. As a result, the value STATUS_EMPTY will never be achieved.
+ */
+public class ContactsContract_ProviderStatus extends AndroidTestCase {
+ private ContentResolver mResolver;
+ private ContactsContract_TestDataBuilder mBuilder;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mResolver = getContext().getContentResolver();
+ ContentProviderClient provider =
+ mResolver.acquireContentProviderClient(ContactsContract.AUTHORITY);
+ mBuilder = new ContactsContract_TestDataBuilder(provider);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ mBuilder.cleanup();
+ }
+
+ public void testProviderStatus_addedContacts() throws Exception {
+ // Setup: add a contact to CP2.
+ TestRawContact rawContact1 = mBuilder.newRawContact()
+ .with(RawContacts.ACCOUNT_TYPE, "test_account")
+ .with(RawContacts.ACCOUNT_NAME, "test_name")
+ .insert();
+ rawContact1.newDataRow(StructuredName.CONTENT_ITEM_TYPE)
+ .with(StructuredName.GIVEN_NAME, "first1")
+ .with(StructuredName.FAMILY_NAME, "last1")
+ .insert();
+
+ // Execute: fetch CP2 status
+ Cursor cursor = mResolver.query(ProviderStatus.CONTENT_URI, null, null, null, null);
+
+ // Verify: CP2 status is normal instead of STATUS_EMPTY.
+ try {
+ assertEquals(1, cursor.getCount());
+ cursor.moveToFirst();
+ ContentValues values = new ContentValues();
+ values.put(ProviderStatus.STATUS, ProviderStatus.STATUS_NORMAL);
+ DatabaseAsserts.assertCursorValuesMatchExactly(cursor, values);
+ } finally {
+ if (cursor != null) {
+ cursor.close();
+ }
+ }
+ }
+}
diff --git a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
index 81006c1..f49a820 100644
--- a/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
+++ b/tests/tests/telephony/src/android/telephony/cts/PhoneNumberUtilsTest.java
@@ -335,7 +335,7 @@
// exercising the underlying PhoneNumberUtil or constraining localization changes.
String phoneNumber = "6512223333";
// Execute
- TtsSpan ttsSpan = PhoneNumberUtils.getPhoneTtsSpan(phoneNumber);
+ TtsSpan ttsSpan = PhoneNumberUtils.createTtsSpan(phoneNumber);
// Verify: the created TtsSpan contains the phone number.
assertEquals("6512223333", ttsSpan.getArgs().get(TtsSpan.ARG_NUMBER_PARTS));
}
@@ -345,7 +345,7 @@
// exercising the underlying PhoneNumberUtil or constraining localization changes.
Spannable spannable = new SpannableString("Hello 6502223333");
// Execute
- PhoneNumberUtils.addPhoneTtsSpan(spannable, 5, spannable.length());
+ PhoneNumberUtils.addTtsSpan(spannable, 5, spannable.length());
// Verify: the Spannable is annotated with a TtsSpan in the correct location.
TtsSpan[] ttsSpans = spannable.getSpans(5, spannable.length() - 1, TtsSpan.class);
assertEquals(1, ttsSpans.length);
@@ -357,7 +357,7 @@
// exercising the underlying PhoneNumberUtil or constraining localization changes.
CharSequence phoneNumber = "6512223333";
// Execute
- Spannable spannable = (Spannable) PhoneNumberUtils.getPhoneTtsSpannable(phoneNumber);
+ Spannable spannable = (Spannable) PhoneNumberUtils.createTtsSpannable(phoneNumber);
// Verify: returned char sequence contains a TtsSpan with the phone number in it
TtsSpan[] ttsSpans = spannable.getSpans(0, spannable.length() - 1, TtsSpan.class);
assertEquals(1, ttsSpans.length);