am b087665b: Clean up ManifestTestListAdapter\'s filterTests loop.
automerge: ad1d56e
* commit 'ad1d56eb6190b9429953f739b4c1d5eb14080878':
Clean up ManifestTestListAdapter's filterTests loop.
diff --git a/apps/CameraITS/CameraITS.pdf b/apps/CameraITS/CameraITS.pdf
index 5e5fd29..b1d933d 100644
--- a/apps/CameraITS/CameraITS.pdf
+++ b/apps/CameraITS/CameraITS.pdf
Binary files differ
diff --git a/apps/CameraITS/build/envsetup.sh b/apps/CameraITS/build/envsetup.sh
index a95c445..6069341 100644
--- a/apps/CameraITS/build/envsetup.sh
+++ b/apps/CameraITS/build/envsetup.sh
@@ -29,7 +29,7 @@
python -V 2>&1 | grep -q "Python 2.7" || \
echo ">> Require python 2.7" >&2
-for M in numpy PIL Image matplotlib pylab
+for M in numpy PIL Image matplotlib pylab cv2 scipy.stats scipy.spatial
do
python -c "import $M" >/dev/null 2>&1 || \
echo ">> Require Python $M module" >&2
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
index b4863fa..3d0e0d7 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/notifications/MockListener.java
@@ -31,9 +31,7 @@
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
-import java.util.HashSet;
import java.util.List;
-import java.util.Set;
public class MockListener extends NotificationListenerService {
static final String TAG = "MockListener";
@@ -69,10 +67,8 @@
private ArrayList<String> mPosted = new ArrayList<String>();
private ArrayMap<String, JSONObject> mNotifications = new ArrayMap<>();
- private ArrayMap<String, String> mNotificationKeys = new ArrayMap<>();
private ArrayList<String> mRemoved = new ArrayList<String>();
private ArrayList<String> mOrder = new ArrayList<>();
- private Set<String> mTestPackages = new HashSet<>();
private BroadcastReceiver mReceiver;
private int mDND = -1;
@@ -81,8 +77,6 @@
super.onCreate();
Log.d(TAG, "created");
- mTestPackages.add("com.android.cts.verifier");
-
mPosted = new ArrayList<String>();
mRemoved = new ArrayList<String>();
@@ -129,13 +123,10 @@
setResultCode(Activity.RESULT_OK);
} else if (SERVICE_CLEAR_ONE.equals(action)) {
Log.d(TAG, "SERVICE_CLEAR_ONE");
- String tag = intent.getStringExtra(EXTRA_TAG);
- String key = mNotificationKeys.get(tag);
- if (key != null) {
- MockListener.this.cancelNotification(key);
- } else {
- Log.w(TAG, "Notification does not exist: " + tag);
- }
+ MockListener.this.cancelNotification(
+ context.getApplicationInfo().packageName,
+ intent.getStringExtra(EXTRA_TAG),
+ intent.getIntExtra(EXTRA_CODE, 0));
} else if (SERVICE_CLEAR_ALL.equals(action)) {
Log.d(TAG, "SERVICE_CLEAR_ALL");
MockListener.this.cancelAllNotifications();
@@ -214,7 +205,6 @@
@Override
public void onNotificationPosted(StatusBarNotification sbn, RankingMap rankingMap) {
- if (!mTestPackages.contains(sbn.getPackageName())) { return; }
Log.d(TAG, "posted: " + sbn.getTag());
mPosted.add(sbn.getTag());
JSONObject notification = new JSONObject();
@@ -226,7 +216,6 @@
notification.put(JSON_ICON, sbn.getNotification().icon);
notification.put(JSON_FLAGS, sbn.getNotification().flags);
mNotifications.put(sbn.getKey(), notification);
- mNotificationKeys.put(sbn.getTag(), sbn.getKey());
} catch (JSONException e) {
Log.e(TAG, "failed to pack up notification payload", e);
}
@@ -239,7 +228,6 @@
mRemoved.add(sbn.getTag());
mNotifications.remove(sbn.getKey());
onNotificationRankingUpdate(rankingMap);
- mNotificationKeys.remove(sbn.getTag());
}
public static void resetListenerData(Context context) {
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
index dfcf120..4a0c135 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/AccelerometerMeasurementTestActivity.java
@@ -97,7 +97,7 @@
Sensor.TYPE_ACCELEROMETER,
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation verifyMeasurements =
- new TestSensorOperation(environment, 100 /* event count */);
+ TestSensorOperation.createOperation(environment, 100 /* event count */);
verifyMeasurements.addVerification(new MeanVerification(
expectations,
new float[]{1.95f, 1.95f, 1.95f} /* m / s^2 */));
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
index 6f0a7aa..2d58c64 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/BatchingTestActivity.java
@@ -22,9 +22,7 @@
import android.hardware.Sensor;
import android.hardware.SensorManager;
import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
import java.util.concurrent.TimeUnit;
@@ -128,7 +126,7 @@
int testDurationSec = maxBatchReportLatencySec + BATCHING_PADDING_TIME_S;
TestSensorOperation operation =
- new TestSensorOperation(environment, testDurationSec,TimeUnit.SECONDS);
+ TestSensorOperation.createOperation(environment, testDurationSec,TimeUnit.SECONDS);
return executeTest(operation);
}
@@ -145,12 +143,12 @@
maxBatchReportLatencyUs);
int flushDurationSec = maxBatchReportLatencySec / 2;
- TestSensorFlushOperation operation =
- new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+ TestSensorOperation operation = TestSensorOperation
+ .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
return executeTest(operation);
}
- private String executeTest(VerifiableSensorOperation operation) throws InterruptedException {
+ private String executeTest(TestSensorOperation operation) throws InterruptedException {
operation.addDefaultVerifications();
operation.setLogEvents(true);
operation.execute();
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
index 4b2a7f4..d6ec951 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/GyroscopeMeasurementTestActivity.java
@@ -156,8 +156,8 @@
getApplicationContext(),
Sensor.TYPE_GYROSCOPE,
SensorManager.SENSOR_DELAY_FASTEST);
- TestSensorOperation sensorOperation =
- new TestSensorOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
+ TestSensorOperation sensorOperation = TestSensorOperation
+ .createOperation(environment, ROTATION_COLLECTION_SEC, TimeUnit.SECONDS);
int gyroscopeAxes = environment.getSensorAxesCount();
int[] expectationsDeg = getExpectationsDeg(gyroscopeAxes, rotationAxis, expectationDeg);
diff --git a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
index 553147b..1ec5dc1 100644
--- a/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
+++ b/apps/CtsVerifier/src/com/android/cts/verifier/sensors/MagneticFieldMeasurementTestActivity.java
@@ -79,7 +79,7 @@
Sensor.TYPE_MAGNETIC_FIELD,
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation verifyNorm =
- new TestSensorOperation(environment, 100 /* event count */);
+ TestSensorOperation.createOperation(environment, 100 /* event count */);
float expectedMagneticFieldEarth =
(SensorManager.MAGNETIC_FIELD_EARTH_MAX + SensorManager.MAGNETIC_FIELD_EARTH_MIN) / 2;
@@ -124,7 +124,7 @@
Sensor.TYPE_MAGNETIC_FIELD,
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation verifyStdDev =
- new TestSensorOperation(environment, 100 /* event count */);
+ TestSensorOperation.createOperation(environment, 100 /* event count */);
verifyStdDev.addVerification(new StandardDeviationVerification(
new float[]{2f, 2f, 2f} /* uT */));
diff --git a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
index fe8f9ee..18ee963 100644
--- a/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
+++ b/hostsidetests/appsecurity/test-apps/DocumentClient/src/com/android/cts/documentclient/DocumentsClientTest.java
@@ -27,6 +27,8 @@
import android.provider.DocumentsProvider;
import android.support.test.uiautomator.UiDevice;
import android.support.test.uiautomator.UiObject;
+import android.support.test.uiautomator.UiObjectNotFoundException;
+import android.support.test.uiautomator.UiScrollable;
import android.support.test.uiautomator.UiSelector;
import android.test.InstrumentationTestCase;
import android.test.MoreAsserts;
@@ -66,6 +68,20 @@
mActivity.finish();
}
+ private UiObject findRoot(String label) throws UiObjectNotFoundException {
+ final UiSelector rootsList = new UiSelector().resourceId(
+ "com.android.documentsui:id/container_roots").childSelector(
+ new UiSelector().resourceId("android:id/list"));
+
+ // Wait for the first list item to appear
+ assertTrue("First list item",
+ new UiObject(rootsList.childSelector(new UiSelector())).waitForExists(TIMEOUT));
+
+ // Now scroll around to find our item
+ new UiScrollable(rootsList).scrollIntoView(new UiSelector().text(label));
+ return new UiObject(rootsList.childSelector(new UiSelector().text(label)));
+ }
+
public void testOpenSimple() throws Exception {
if (!supportedHardware()) return;
@@ -83,13 +99,13 @@
// Ensure that we see both of our roots
mDevice.waitForIdle();
- assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).waitForExists(TIMEOUT));
- assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
- assertFalse("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
+ assertTrue("CtsLocal root", findRoot("CtsLocal").exists());
+ assertTrue("CtsCreate root", findRoot("CtsCreate").exists());
+ assertFalse("CtsGetContent root", findRoot("CtsGetContent").exists());
// Pick a specific file from our test provider
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("CtsLocal")).click();
+ findRoot("CtsLocal").click();
mDevice.waitForIdle();
new UiObject(new UiSelector().text("FILE1")).click();
@@ -118,7 +134,8 @@
mActivity.startActivityForResult(intent, 42);
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("CtsCreate")).click();
+ findRoot("CtsCreate").click();
+
mDevice.waitForIdle();
new UiObject(new UiSelector().resourceId("com.android.documentsui:id/container_save")
.childSelector(new UiSelector().resourceId("android:id/button1"))).click();
@@ -142,7 +159,7 @@
mActivity.startActivityForResult(intent, 42);
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("CtsCreate")).click();
+ findRoot("CtsCreate").click();
// Pick file2, which should be selected since MIME matches, then try
// picking a non-matching MIME, which should leave file2 selected.
@@ -168,7 +185,8 @@
mActivity.startActivityForResult(intent, 42);
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("CtsCreate")).click();
+ findRoot("CtsCreate").click();
+
mDevice.waitForIdle();
new UiObject(new UiSelector().text("DIR2")).click();
mDevice.waitForIdle();
@@ -244,12 +262,12 @@
// Look around, we should be able to see both DocumentsProviders and
// other GET_CONTENT sources.
mDevice.waitForIdle();
- assertTrue("CtsLocal root", new UiObject(new UiSelector().text("CtsLocal")).waitForExists(TIMEOUT));
- assertTrue("CtsCreate root", new UiObject(new UiSelector().text("CtsCreate")).exists());
- assertTrue("CtsGetContent", new UiObject(new UiSelector().text("CtsGetContent")).exists());
+ assertTrue("CtsLocal root", findRoot("CtsLocal").exists());
+ assertTrue("CtsCreate root", findRoot("CtsCreate").exists());
+ assertTrue("CtsGetContent root", findRoot("CtsGetContent").exists());
mDevice.waitForIdle();
- new UiObject(new UiSelector().text("CtsGetContent")).click();
+ findRoot("CtsGetContent").click();
final Result result = mActivity.getResult();
assertEquals("ReSuLt", result.data.getAction());
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
index e1f6886..5bbdf76 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/AndroidManifest.xml
@@ -24,9 +24,10 @@
<application>
<activity android:name="com.android.cts.intent.receiver.IntentReceiverActivity">
<intent-filter>
+ <action android:name="com.android.cts.action.COPY_TO_CLIPBOARD" />
<action android:name="com.android.cts.action.READ_FROM_URI" />
- <action android:name="com.android.cts.action.WRITE_TO_URI" />
<action android:name="com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION" />
+ <action android:name="com.android.cts.action.WRITE_TO_URI" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</activity>
diff --git a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
index 59f0752..294c678 100644
--- a/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentReceiver/src/com/android/cts/intent/receiver/IntentReceiverActivity.java
@@ -16,6 +16,8 @@
package com.android.cts.intent.receiver;
import android.app.Activity;
+import android.content.ClipboardManager;
+import android.content.ClipData;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
@@ -35,21 +37,36 @@
private static final String TAG = "IntentReceiverActivity";
- private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
+ private static final String ACTION_COPY_TO_CLIPBOARD =
+ "com.android.cts.action.COPY_TO_CLIPBOARD";
- private static final String ACTION_WRITE_TO_URI = "com.android.cts.action.WRITE_TO_URI";
+ private static final String ACTION_READ_FROM_URI =
+ "com.android.cts.action.READ_FROM_URI";
private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
"com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
+ private static final String ACTION_WRITE_TO_URI =
+ "com.android.cts.action.WRITE_TO_URI";
+
+
private static final String EXTRA_CAUGHT_SECURITY_EXCEPTION = "extra_caught_security_exception";
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
- Intent received = getIntent();
- String action = received.getAction();
- Uri uri = getIntent().getClipData().getItemAt(0).getUri();
- if (ACTION_READ_FROM_URI.equals(action)) {
+ final Intent received = getIntent();
+ final String action = received.getAction();
+ final ClipData clipData = getIntent().getClipData();
+ final Uri uri = clipData != null ? clipData.getItemAt(0).getUri() : null;
+ if (ACTION_COPY_TO_CLIPBOARD.equals(action)) {
+ String text = received.getStringExtra("extra_text");
+ Log.i(TAG, "Copying \"" + text + "\" to the clipboard");
+ ClipData clip = ClipData.newPlainText("", text);
+ ClipboardManager clipboard =
+ (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ clipboard.setPrimaryClip(clip);
+ setResult(Activity.RESULT_OK);
+ } else if (ACTION_READ_FROM_URI.equals(action)) {
Intent result = new Intent();
String message = null;
try {
@@ -63,6 +80,11 @@
Log.i(TAG, "Message received in reading test: " + message);
result.putExtra("extra_response", message);
setResult(Activity.RESULT_OK, result);
+ } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
+ Log.i(TAG, "Taking persistable uri permission to " + uri);
+ getContentResolver().takePersistableUriPermission(uri,
+ Intent.FLAG_GRANT_READ_URI_PERMISSION);
+ setResult(Activity.RESULT_OK);
} else if (ACTION_WRITE_TO_URI.equals(action)) {
Intent result = new Intent();
String message = received.getStringExtra("extra_message");
@@ -76,11 +98,6 @@
Log.i(TAG, "Caught a IOException while trying to write to " + uri, e);
}
setResult(Activity.RESULT_OK, result);
- } else if (ACTION_TAKE_PERSISTABLE_URI_PERMISSION.equals(action)) {
- Log.i(TAG, "Taking persistable uri permission to " + uri);
- getContentResolver().takePersistableUriPermission(uri,
- Intent.FLAG_GRANT_READ_URI_PERMISSION);
- setResult(Activity.RESULT_OK);
}
finish();
}
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
similarity index 94%
rename from hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
rename to hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
index 47de0da..1aaa5ab 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderTest.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/ContentTest.java
@@ -32,7 +32,7 @@
import java.io.InputStream;
import java.io.InputStreamReader;
-public class IntentSenderTest extends InstrumentationTestCase {
+public class ContentTest extends InstrumentationTestCase {
private static final String MESSAGE = "Sample Message";
@@ -57,8 +57,8 @@
@Override
public void tearDown() throws Exception {
- super.tearDown();
mActivity.finish();
+ super.tearDown();
}
/**
@@ -73,7 +73,7 @@
intent.setClipData(ClipData.newRawUri("", uri));
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
- final Intent result = mActivity.getResult(intent);
+ final Intent result = mActivity.getCrossProfileResult(intent);
assertNotNull(result);
assertEquals(MESSAGE, result.getStringExtra("extra_response"));
}
@@ -95,7 +95,7 @@
intent.addFlags(Intent.FLAG_GRANT_WRITE_URI_PERMISSION
| Intent.FLAG_GRANT_READ_URI_PERMISSION);
- mActivity.getResult(intent);
+ mActivity.getCrossProfileResult(intent);
assertEquals(MESSAGE, getFirstLineFromUri(uri));
}
@@ -107,7 +107,7 @@
Intent intent = new Intent(ACTION_READ_FROM_URI);
intent.setClipData(ClipData.newRawUri("", uri));
- final Intent result = mActivity.getResult(intent);
+ final Intent result = mActivity.getCrossProfileResult(intent);
assertNotNull(result);
assertEquals(MESSAGE, result.getStringExtra("extra_response"));
}
@@ -136,7 +136,7 @@
Intent notGrant = new Intent(ACTION_READ_FROM_URI);
notGrant.setClipData(ClipData.newRawUri("", uriNotGranted));
- final Intent result = mActivity.getResult(notGrant);
+ final Intent result = mActivity.getCrossProfileResult(notGrant);
assertNotNull(result);
// The receiver did not have permission to read the uri. So it should have caught a security
// exception.
@@ -155,7 +155,7 @@
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
// We're expecting to run into a security exception
- final Intent result = mActivity.getResult(intent);
+ final Intent result = mActivity.getCrossProfileResult(intent);
if (result == null) {
// This is fine; probably of a SecurityException when off in the
// system somewhere.
@@ -170,7 +170,7 @@
grantPersistable.setClipData(ClipData.newRawUri("", uri));
grantPersistable.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION
| Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION);
- mActivity.getResult(grantPersistable);
+ mActivity.getCrossProfileResult(grantPersistable);
}
private Uri getBasicContentProviderUri(String path) {
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java
new file mode 100644
index 0000000..a5d83db
--- /dev/null
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/CopyPasteTest.java
@@ -0,0 +1,119 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.cts.intent.sender;
+
+import android.content.ClipboardManager;
+import android.content.ClipData;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.test.InstrumentationTestCase;
+import android.util.Log;
+
+import java.util.concurrent.Semaphore;
+import java.util.concurrent.TimeUnit;
+
+public class CopyPasteTest extends InstrumentationTestCase
+ implements ClipboardManager.OnPrimaryClipChangedListener {
+
+ private IntentSenderActivity mActivity;
+ private ClipboardManager mClipboard;
+ private Semaphore mNotified;
+
+ private static String ACTION_COPY_TO_CLIPBOARD = "com.android.cts.action.COPY_TO_CLIPBOARD";
+
+ private static String INITIAL_TEXT = "initial text";
+ private static String NEW_TEXT = "sample text";
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ Context context = getInstrumentation().getTargetContext();
+ mActivity = launchActivity(context.getPackageName(), IntentSenderActivity.class, null);
+ mClipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
+ }
+
+ @Override
+ public void tearDown() throws Exception {
+ mActivity.finish();
+ super.tearDown();
+ }
+
+ public void testCanReadAcrossProfiles() throws Exception {
+ ClipData clip = ClipData.newPlainText(""/*label*/, INITIAL_TEXT);
+ mClipboard.setPrimaryClip(clip);
+ assertEquals(INITIAL_TEXT , getTextFromClipboard());
+
+ askCrossProfileReceiverToCopy(NEW_TEXT);
+
+ assertEquals(NEW_TEXT, getTextFromClipboard());
+ }
+
+ public void testCannotReadAcrossProfiles() throws Exception {
+ ClipData clip = ClipData.newPlainText(""/*label*/, INITIAL_TEXT);
+ mClipboard.setPrimaryClip(clip);
+ assertEquals(INITIAL_TEXT , getTextFromClipboard());
+
+ askCrossProfileReceiverToCopy(NEW_TEXT);
+
+ String clipboardText = getTextFromClipboard();
+ assertTrue("The clipboard text is " + clipboardText + " but should be <null> or "
+ + INITIAL_TEXT, clipboardText == null || clipboardText.equals(INITIAL_TEXT));
+ }
+
+ public void testIsNotified() throws Exception {
+ try {
+ mNotified = new Semaphore(0);
+ mActivity.addPrimaryClipChangedListener(this);
+
+ askCrossProfileReceiverToCopy(NEW_TEXT);
+
+ assertTrue(mNotified.tryAcquire(5, TimeUnit.SECONDS));
+ } finally {
+ mActivity.removePrimaryClipChangedListener(this);
+ }
+ }
+
+ private void askCrossProfileReceiverToCopy(String text) throws Exception {
+ Intent intent = new Intent(ACTION_COPY_TO_CLIPBOARD);
+ intent.putExtra("extra_text", text);
+ mActivity.getCrossProfileResult(intent);
+ }
+
+ private String getTextFromClipboard() {
+ ClipData clip = mClipboard.getPrimaryClip();
+ if (clip == null) {
+ return null;
+ }
+ ClipData.Item item = clip.getItemAt(0);
+ if (item == null) {
+ return null;
+ }
+ CharSequence text = item.getText();
+ if (text == null) {
+ return null;
+ }
+ return text.toString();
+ }
+
+
+ @Override
+ public void onPrimaryClipChanged() {
+ mNotified.release();
+ }
+
+}
diff --git a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
index 00fa6b7..fd421ac 100644
--- a/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
+++ b/hostsidetests/devicepolicy/app/IntentSender/src/com/android/cts/intent/sender/IntentSenderActivity.java
@@ -17,15 +17,27 @@
package com.android.cts.intent.sender;
import android.app.Activity;
+import android.content.ClipboardManager;
+import android.content.ClipboardManager.OnPrimaryClipChangedListener;
+import android.content.ComponentName;
import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.os.Bundle;
+import android.util.Log;
import java.util.concurrent.SynchronousQueue;
import java.util.concurrent.TimeUnit;
+import java.util.List;
public class IntentSenderActivity extends Activity {
+ private static String TAG = "IntentSenderActivity";
+
private final SynchronousQueue<Result> mResult = new SynchronousQueue<>();
+ private ClipboardManager mClipboardManager;
+
public static class Result {
public final int resultCode;
public final Intent data;
@@ -37,6 +49,12 @@
}
@Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mClipboardManager = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
+ }
+
+ @Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
if (resultCode == Activity.RESULT_OK) {
try {
@@ -52,4 +70,38 @@
final Result result = mResult.poll(30, TimeUnit.SECONDS);
return (result != null) ? result.data : null;
}
+
+ /**
+ * This method will send an intent accross profiles to IntentReceiverActivity, and return the
+ * result intent set by IntentReceiverActivity.
+ */
+ public Intent getCrossProfileResult(Intent intent)
+ throws Exception {
+ PackageManager pm = getPackageManager();
+ List<ResolveInfo> ris = pm.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY);
+ // There should be two matches:
+ // - com.android.cts.intent.receiver (on the current profile).
+ // - One that will send the intent to the other profile.
+ // It's the second one we want to send the intent to.
+
+ for (ResolveInfo ri : ris) {
+ if (!ri.activityInfo.packageName.equals("com.android.cts.intent.receiver")) {
+ intent.setComponent(new ComponentName(ri.activityInfo.packageName,
+ ri.activityInfo.name));
+ return getResult(intent);
+ }
+ }
+ Log.e(TAG, "The intent " + intent + " cannot be sent accross profiles");
+ return null;
+ }
+
+ public void addPrimaryClipChangedListener(OnPrimaryClipChangedListener listener) {
+ mClipboardManager.addPrimaryClipChangedListener(listener);
+ }
+
+ public void removePrimaryClipChangedListener(
+ OnPrimaryClipChangedListener listener) {
+ mClipboardManager.removePrimaryClipChangedListener(listener);
+ }
+
}
diff --git a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
index 9615991..49001e9 100644
--- a/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
+++ b/hostsidetests/devicepolicy/app/ManagedProfile/src/com/android/cts/managedprofile/CrossProfileUtils.java
@@ -21,8 +21,15 @@
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.content.IntentFilter;
+import android.os.UserManager;
import android.test.AndroidTestCase;
+/**
+ * The methods in this class are not really tests.
+ * They are just performing an action that is needed for a test.
+ * But we're still using an AndroidTestCase because it's an easy way to call
+ * device-side methods from the host.
+ */
public class CrossProfileUtils extends AndroidTestCase {
private static final String ACTION_READ_FROM_URI = "com.android.cts.action.READ_FROM_URI";
@@ -31,16 +38,14 @@
private static final String ACTION_TAKE_PERSISTABLE_URI_PERMISSION =
"com.android.cts.action.TAKE_PERSISTABLE_URI_PERMISSION";
+ private static String ACTION_COPY_TO_CLIPBOARD = "com.android.cts.action.COPY_TO_CLIPBOARD";
+
public void addParentCanAccessManagedFilters() {
removeAllFilters();
final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
Context.DEVICE_POLICY_SERVICE);
- IntentFilter intentFilter = new IntentFilter();
- intentFilter.addAction(ACTION_READ_FROM_URI);
- intentFilter.addAction(ACTION_WRITE_TO_URI);
- intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
- dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
+ dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, getIntentFilter(),
DevicePolicyManager.FLAG_PARENT_CAN_ACCESS_MANAGED);
}
@@ -49,12 +54,17 @@
final DevicePolicyManager dpm = (DevicePolicyManager) getContext().getSystemService(
Context.DEVICE_POLICY_SERVICE);
+ dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, getIntentFilter(),
+ DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+ }
+
+ public IntentFilter getIntentFilter() {
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(ACTION_READ_FROM_URI);
intentFilter.addAction(ACTION_WRITE_TO_URI);
intentFilter.addAction(ACTION_TAKE_PERSISTABLE_URI_PERMISSION);
- dpm.addCrossProfileIntentFilter(ADMIN_RECEIVER_COMPONENT, intentFilter,
- DevicePolicyManager.FLAG_MANAGED_CAN_ACCESS_PARENT);
+ intentFilter.addAction(ACTION_COPY_TO_CLIPBOARD);
+ return intentFilter;
}
public void removeAllFilters() {
@@ -62,4 +72,18 @@
Context.DEVICE_POLICY_SERVICE);
dpm.clearCrossProfileIntentFilters(ADMIN_RECEIVER_COMPONENT);
}
+
+ public void disallowCrossProfileCopyPaste() {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.addUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ }
+
+ public void allowCrossProfileCopyPaste() {
+ DevicePolicyManager dpm = (DevicePolicyManager)
+ getContext().getSystemService(Context.DEVICE_POLICY_SERVICE);
+ dpm.clearUserRestriction(ADMIN_RECEIVER_COMPONENT,
+ UserManager.DISALLOW_CROSS_PROFILE_COPY_PASTE);
+ }
}
diff --git a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
index 6ece85c..51b7930 100644
--- a/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
+++ b/hostsidetests/devicepolicy/src/com/android/cts/devicepolicy/ManagedProfileTest.java
@@ -49,6 +49,8 @@
if (mHasFeature) {
mUserId = createManagedProfile();
installApp(MANAGED_PROFILE_APK);
+ installApp(INTENT_RECEIVER_APK);
+ installApp(INTENT_SENDER_APK);
setProfileOwner(MANAGED_PROFILE_PKG + "/" + ADMIN_RECEIVER_TEST_CLASS, mUserId);
startUser(mUserId);
}
@@ -59,8 +61,9 @@
if (mHasFeature) {
removeUser(mUserId);
getDevice().uninstallPackage(MANAGED_PROFILE_PKG);
+ getDevice().uninstallPackage(INTENT_SENDER_PKG);
+ getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
}
-
super.tearDown();
}
@@ -114,30 +117,58 @@
return;
}
- try {
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
- installAppAsUser(INTENT_SENDER_APK, 0);
- installAppAsUser(INTENT_RECEIVER_APK, mUserId);
+ // Test from parent to managed
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "removeAllFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "addManagedCanAccessParentFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", 0));
- // Test from parent to managed
- assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "addManagedCanAccessParentFilters", mUserId));
- assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", 0));
+ // Test from managed to parent
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "removeAllFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "addParentCanAccessManagedFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".ContentTest", mUserId));
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
- installAppAsUser(INTENT_SENDER_APK, mUserId);
- installAppAsUser(INTENT_RECEIVER_APK, 0);
+ }
- // Test from managed to parent
- assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
- "addParentCanAccessManagedFilters", mUserId));
- assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".IntentSenderTest", mUserId));
+ public void testCrossProfileCopyPaste() throws Exception {
+ if (!mHasFeature) {
+ return;
+ }
- } finally {
- getDevice().uninstallPackage(INTENT_SENDER_PKG);
- getDevice().uninstallPackage(INTENT_RECEIVER_PKG);
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "allowCrossProfileCopyPaste", mUserId));
+ // Test that managed can see what is copied in the parent.
+ testCrossProfileCopyPasteInternal(mUserId, true);
+ // Test that the parent can see what is copied in managed.
+ testCrossProfileCopyPasteInternal(0, true);
+
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "disallowCrossProfileCopyPaste", mUserId));
+ // Test that managed can still see what is copied in the parent.
+ testCrossProfileCopyPasteInternal(mUserId, true);
+ // Test that the parent cannot see what is copied in managed.
+ testCrossProfileCopyPasteInternal(0, false);
+ }
+
+ private void testCrossProfileCopyPasteInternal(int userId, boolean shouldSucceed)
+ throws DeviceNotAvailableException {
+ final String direction = (userId == 0) ? "addManagedCanAccessParentFilters"
+ : "addParentCanAccessManagedFilters";
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ "removeAllFilters", mUserId));
+ assertTrue(runDeviceTestsAsUser(MANAGED_PROFILE_PKG, ".CrossProfileUtils",
+ direction, mUserId));
+ if (shouldSucceed) {
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testCanReadAcrossProfiles", userId));
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testIsNotified", userId));
+ } else {
+ assertTrue(runDeviceTestsAsUser(INTENT_SENDER_PKG, ".CopyPasteTest",
+ "testCannotReadAcrossProfiles", userId));
}
}
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index dd8660f..3ab571d 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -142,7 +142,8 @@
"com.android.cts.devicepolicy.ManagedProfileTest#testWipeData",
"com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileIntentFilters",
"com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileContent",
- "com.android.cts.devicepolicy.ManagedProfileTest#testNoDebuggingFeaturesRestriction"
+ "com.android.cts.devicepolicy.ManagedProfileTest#testNoDebuggingFeaturesRestriction",
+ "com.android.cts.devicepolicy.ManagedProfileTest#testCrossProfileCopyPaste"
]
},
{
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
index 35a0b4c..306428f 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorSetTest.java
@@ -16,8 +16,11 @@
package android.animation.cts;
import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.Set;
import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.animation.TimeInterpolator;
@@ -25,6 +28,7 @@
import android.test.ActivityInstrumentationTestCase2;
import android.view.animation.AccelerateDecelerateInterpolator;
import android.view.animation.AccelerateInterpolator;
+import android.view.animation.LinearInterpolator;
public class AnimatorSetTest extends
ActivityInstrumentationTestCase2<AnimationActivity> {
@@ -34,6 +38,7 @@
private Object object;
private ObjectAnimator yAnimator;
private ObjectAnimator xAnimator;
+ Set<Integer> identityHashes = new HashSet<Integer>();
public AnimatorSetTest() {
super(AnimationActivity.class);
@@ -158,4 +163,79 @@
}
});
}
+
+ private void assertUnique(Object object) {
+ assertUnique(object, "");
+ }
+
+ private void assertUnique(Object object, String msg) {
+ final int code = System.identityHashCode(object);
+ assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+ }
+
+ public void testClone() {
+ AnimatorSet set1 = new AnimatorSet();
+ final AnimatorListenerAdapter setListener = new AnimatorListenerAdapter() {};
+ set1.addListener(setListener);
+ ObjectAnimator animator1 = new ObjectAnimator();
+ animator1.setDuration(100);
+ animator1.setPropertyName("x");
+ animator1.setIntValues(5);
+ animator1.setInterpolator(new LinearInterpolator());
+ AnimatorListenerAdapter listener1 = new AnimatorListenerAdapter(){};
+ AnimatorListenerAdapter listener2 = new AnimatorListenerAdapter(){};
+ animator1.addListener(listener1);
+
+ ObjectAnimator animator2 = new ObjectAnimator();
+ animator2.setDuration(100);
+ animator2.setInterpolator(new LinearInterpolator());
+ animator2.addListener(listener2);
+ animator2.setPropertyName("y");
+ animator2.setIntValues(10);
+
+ set1.playTogether(animator1, animator2);
+
+ AnimateObject target = new AnimateObject();
+ set1.setTarget(target);
+ set1.start();
+ assertTrue(set1.isStarted());
+
+ animator1.getListeners();
+ AnimatorSet set2 = set1.clone();
+ assertFalse(set2.isStarted());
+
+ assertUnique(set1);
+ assertUnique(animator1);
+ assertUnique(animator2);
+
+ assertUnique(set2);
+ assertEquals(2, set2.getChildAnimations().size());
+
+ Animator clone1 = set2.getChildAnimations().get(0);
+ Animator clone2 = set2.getChildAnimations().get(1);
+
+ for (Animator animator : set2.getChildAnimations()) {
+ assertUnique(animator);
+ }
+
+ assertTrue(clone1.getListeners().contains(listener1));
+ assertTrue(clone2.getListeners().contains(listener2));
+
+ assertTrue(set2.getListeners().contains(setListener));
+
+ for (Animator.AnimatorListener listener : set1.getListeners()) {
+ assertTrue(set2.getListeners().contains(listener));
+ }
+
+ assertEquals(animator1.getDuration(), clone1.getDuration());
+ assertEquals(animator2.getDuration(), clone2.getDuration());
+ assertSame(animator1.getInterpolator(), clone1.getInterpolator());
+ assertSame(animator2.getInterpolator(), clone2.getInterpolator());
+ }
+
+ class AnimateObject {
+ int x = 1;
+ int y = 2;
+ }
}
diff --git a/tests/tests/animation/src/android/animation/cts/AnimatorTest.java b/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
index fac9ff9..a08a5eb 100644
--- a/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
+++ b/tests/tests/animation/src/android/animation/cts/AnimatorTest.java
@@ -182,6 +182,27 @@
assertNull(listListenersTwo);
}
+ public void testNullObjectAnimator() throws Throwable {
+ Object object = mActivity.view.newBall;
+ final ObjectAnimator animator = ObjectAnimator.ofFloat(object, "y", 0, 100);
+ MyListener listener = new MyListener();
+ animator.addListener(listener);
+ mActivity.view.newBall.setY(0);
+ startAnimation(animator);
+ int sleepCount = 0;
+ while (mActivity.view.newBall.getY() == 0 && sleepCount++ < 50) {
+ Thread.sleep(1);
+ }
+ assertNotSame(0, mActivity.view.newBall.getY());
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ animator.setTarget(null);
+ }
+ });
+ assertTrue(listener.mCancel);
+ }
+
class MyListener implements Animator.AnimatorListener{
boolean mStart = false;
boolean mEnd = false;
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
index 192fb85..32a6880 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraManagerTest.java
@@ -41,7 +41,9 @@
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.HashSet;
import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
/**
* <p>Basic test for CameraManager class.</p>
@@ -485,7 +487,127 @@
mCameraManager.registerAvailabilityCallback(mListener, mHandler);
mCameraManager.unregisterAvailabilityCallback(mListener);
mCameraManager.unregisterAvailabilityCallback(mListener);
-
- // TODO: test the listener callbacks
}
+
+ /**
+ * Test that the availability callbacks fire when expected
+ */
+ public void testCameraManagerListenerCallbacks() throws Exception {
+ final int AVAILABILITY_TIMEOUT_MS = 10;
+
+ final LinkedBlockingQueue<String> availableEventQueue = new LinkedBlockingQueue<>();
+ final LinkedBlockingQueue<String> unavailableEventQueue = new LinkedBlockingQueue<>();
+
+ CameraManager.AvailabilityCallback ac = new CameraManager.AvailabilityCallback() {
+ @Override
+ public void onCameraAvailable(String cameraId) {
+ availableEventQueue.offer(cameraId);
+ }
+
+ @Override
+ public void onCameraUnavailable(String cameraId) {
+ unavailableEventQueue.offer(cameraId);
+ }
+ };
+
+ mCameraManager.registerAvailabilityCallback(ac, mHandler);
+ String[] cameras = mCameraManager.getCameraIdList();
+
+ // Verify we received available for all cameras' initial state in a reasonable amount of time
+ HashSet<String> expectedAvailableCameras = new HashSet<String>(Arrays.asList(cameras));
+ while (expectedAvailableCameras.size() > 0) {
+ String id = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue("Did not receive initial availability notices for some cameras",
+ id != null);
+ expectedAvailableCameras.remove(id);
+ }
+ // Verify no unavailable cameras were reported
+ assertTrue("Some camera devices are initially unavailable",
+ unavailableEventQueue.size() == 0);
+
+ // Verify transitions for individual cameras
+ for (String id : cameras) {
+ MockStateCallback mockListener = MockStateCallback.mock();
+ mCameraListener = new BlockingStateCallback(mockListener);
+
+ mCameraManager.openCamera(id, mCameraListener, mHandler);
+
+ // Block until opened
+ mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
+ CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+ // Then verify only open happened, and get the camera handle
+ CameraDevice camera = verifyCameraStateOpened(id, mockListener);
+
+ // Verify that we see the expected 'unavailable' event.
+ String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received unavailability notice for wrong ID " +
+ "(expected %s, got %s)", id, candidateId),
+ id == candidateId);
+ assertTrue("Availability events received unexpectedly",
+ availableEventQueue.size() == 0);
+
+ // Verify that we see the expected 'available' event after closing the camera
+
+ camera.close();
+
+ mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
+ CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
+
+ candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received availability notice for wrong ID " +
+ "(expected %s, got %s)", id, candidateId),
+ id == candidateId);
+ assertTrue("Unavailability events received unexpectedly",
+ unavailableEventQueue.size() == 0);
+
+ }
+
+ // Verify that we can unregister the listener and see no more events
+ assertTrue("Availability events received unexpectedly",
+ availableEventQueue.size() == 0);
+ assertTrue("Unavailability events received unexpectedly",
+ unavailableEventQueue.size() == 0);
+
+ mCameraManager.unregisterAvailabilityCallback(ac);
+
+ {
+ // Open an arbitrary camera and make sure we don't hear about it
+
+ MockStateCallback mockListener = MockStateCallback.mock();
+ mCameraListener = new BlockingStateCallback(mockListener);
+
+ mCameraManager.openCamera(cameras[0], mCameraListener, mHandler);
+
+ // Block until opened
+ mCameraListener.waitForState(BlockingStateCallback.STATE_OPENED,
+ CameraTestUtils.CAMERA_IDLE_TIMEOUT_MS);
+ // Then verify only open happened, and close the camera
+ CameraDevice camera = verifyCameraStateOpened(cameras[0], mockListener);
+
+ camera.close();
+
+ mCameraListener.waitForState(BlockingStateCallback.STATE_CLOSED,
+ CameraTestUtils.CAMERA_CLOSE_TIMEOUT_MS);
+
+ // No unavailability or availability callback should have occured
+ String candidateId = unavailableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received unavailability notice for ID %s unexpectedly ",
+ candidateId),
+ candidateId == null);
+
+ candidateId = availableEventQueue.poll(AVAILABILITY_TIMEOUT_MS,
+ java.util.concurrent.TimeUnit.MILLISECONDS);
+ assertTrue(String.format("Received availability notice for ID %s unexpectedly ",
+ candidateId),
+ candidateId == null);
+
+
+ }
+
+ } // testCameraManagerListenerCallbacks
+
}
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 0c36832..303099a 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -63,6 +63,7 @@
import java.util.List;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
/**
* A package private utility class for wrapping up the camera2 cts test common utility functions
@@ -199,6 +200,7 @@
public static class SimpleCaptureCallback extends CameraCaptureSession.CaptureCallback {
private final LinkedBlockingQueue<CaptureResult> mQueue =
new LinkedBlockingQueue<CaptureResult>();
+ private AtomicLong mNumFramesArrived = new AtomicLong(0);
@Override
public void onCaptureStarted(CameraCaptureSession session, CaptureRequest request,
@@ -210,6 +212,7 @@
public void onCaptureCompleted(CameraCaptureSession session, CaptureRequest request,
TotalCaptureResult result) {
try {
+ mNumFramesArrived.incrementAndGet();
mQueue.put(result);
} catch (InterruptedException e) {
throw new UnsupportedOperationException(
@@ -227,6 +230,10 @@
long frameNumber) {
}
+ public long getTotalNumFrames() {
+ return mNumFramesArrived.get();
+ }
+
public CaptureResult getCaptureResult(long timeout) {
try {
CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
index 669de2d..400be6f 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/RecordingTest.java
@@ -287,8 +287,9 @@
updatePreviewSurfaceWithVideoSize(size);
// Start recording
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
startSlowMotionRecording(/*useMediaRecorder*/true, videoFramerate, captureRate,
- fpsRange);
+ fpsRange, resultListener);
long startTime = SystemClock.elapsedRealtime();
// Record certain duration.
@@ -296,10 +297,12 @@
// Stop recording and preview
stopRecording(/*useMediaRecorder*/true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ // Convert number of frames camera produced into the duration in unit of ms.
+ int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ videoFramerate);
// Validation.
- validateRecording(size, duration * SLOWMO_SLOW_FACTOR);
+ validateRecording(size, durationMs);
}
@@ -329,7 +332,8 @@
}
private void startSlowMotionRecording(boolean useMediaRecorder, int videoFrameRate,
- int captureRate, Range<Integer> fpsRange) throws Exception {
+ int captureRate, Range<Integer> fpsRange,
+ CameraCaptureSession.CaptureCallback listener) throws Exception {
List<Surface> outputSurfaces = new ArrayList<Surface>(2);
assertTrue("Both preview and recording surfaces should be valid",
mPreviewSurface.isValid() && mRecordingSurface.isValid());
@@ -370,7 +374,7 @@
for (int i = 0; i < slowMotionFactor - 1; i++) {
slowMoRequests.add(recordingOnlyBuilder.build()); // Recording only.
}
- mSession.setRepeatingBurst(slowMoRequests, null, null);
+ mSession.setRepeatingBurst(slowMoRequests, listener, mHandler);
if (useMediaRecorder) {
mMediaRecorder.start();
@@ -415,18 +419,25 @@
updatePreviewSurfaceWithVideoSize(videoSz);
// Start recording
- startRecording(/* useMediaRecorder */true);
- long startTime = SystemClock.elapsedRealtime();
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ startRecording(/* useMediaRecorder */true, resultListener);
// Record certain duration.
SystemClock.sleep(RECORDING_DURATION_MS);
// Stop recording and preview
stopRecording(/* useMediaRecorder */true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ // Convert number of frames camera produced into the duration in unit of ms.
+ int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ profile.videoFrameRate);
+
+ if (VERBOSE) {
+ Log.v(TAG, "video frame rate: " + profile.videoFrameRate +
+ ", num of frames produced: " + resultListener.getTotalNumFrames());
+ }
// Validation.
- validateRecording(videoSz, duration);
+ validateRecording(videoSz, durationMs);
}
}
@@ -458,18 +469,20 @@
updatePreviewSurfaceWithVideoSize(sz);
// Start recording
- startRecording(/* useMediaRecorder */true);
- long startTime = SystemClock.elapsedRealtime();
+ SimpleCaptureCallback resultListener = new SimpleCaptureCallback();
+ startRecording(/* useMediaRecorder */true, resultListener);
// Record certain duration.
SystemClock.sleep(RECORDING_DURATION_MS);
// Stop recording and preview
stopRecording(/* useMediaRecorder */true);
- int duration = (int) (SystemClock.elapsedRealtime() - startTime);
+ // Convert number of frames camera produced into the duration in unit of ms.
+ int durationMs = (int) (resultListener.getTotalNumFrames() * 1000.0f /
+ VIDEO_FRAME_RATE);
// Validation.
- validateRecording(sz, duration);
+ validateRecording(sz, durationMs);
}
}
@@ -861,7 +874,8 @@
// TODO: Don't skip this for video snapshot
if (!mStaticInfo.isHardwareLevelLegacy()) {
assertTrue(String.format(
- "Video duration doesn't match: recorded %dms, expected %dms", duration,
+ "Camera %s: Video duration doesn't match: recorded %dms, expected %dms",
+ mCamera.getId(), duration,
durationMs), Math.abs(duration - durationMs) < DURATION_MARGIN_MS);
}
} finally {
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
index f18a1cf..1f6bc2c 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/StillCaptureTest.java
@@ -1253,6 +1253,8 @@
CaptureRequest.Builder stillRequest =
mCamera.createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE);
stillRequest.set(CaptureRequest.CONTROL_AE_LOCK, true);
+ CaptureResult normalResult;
+ CaptureResult compensatedResult;
// The following variables should only be read under the MANUAL_SENSOR capability guard:
long minExposureValue = -1;
@@ -1284,13 +1286,13 @@
// Wait for AE to be stabilized before capture: CONVERGED or FLASH_REQUIRED.
waitForAeStable(resultListener, NUM_FRAMES_WAITED_FOR_UNKNOWN_LATENCY);
- CaptureResult result = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
+ normalResult = resultListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS);
long normalExposureValue = -1;
if (mStaticInfo.isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
// get and check if current exposure value is valid
- normalExposureValue = getExposureValue(result);
+ normalExposureValue = getExposureValue(normalResult);
mCollector.expectInRange("Exposure setting out of bound", normalExposureValue,
minExposureValue, maxExposureValuePreview);
@@ -1300,6 +1302,12 @@
expectedExposureValue > maxExposureValueStill) {
continue;
}
+ Log.v(TAG, "Expect ratio: " + expectedRatio +
+ " normalExposureValue: " + normalExposureValue +
+ " expectedExposureValue: " + expectedExposureValue +
+ " minExposureValue: " + minExposureValue +
+ " maxExposureValuePreview: " + maxExposureValuePreview +
+ " maxExposureValueStill: " + maxExposureValueStill);
}
// Now issue exposure compensation and wait for AE locked. AE could take a few
@@ -1320,29 +1328,39 @@
CaptureRequest request = stillRequest.build();
mSession.capture(request, resultListener, mHandler);
- result = resultListener.getCaptureResultForRequest(request, WAIT_FOR_RESULT_TIMEOUT_MS);
+ compensatedResult = resultListener.getCaptureResultForRequest(
+ request, WAIT_FOR_RESULT_TIMEOUT_MS);
if (mStaticInfo.isCapabilitySupported(
CameraCharacteristics.REQUEST_AVAILABLE_CAPABILITIES_MANUAL_SENSOR)) {
// Verify the exposure value compensates as requested
- long compensatedExposureValue = getExposureValue(result);
+ long compensatedExposureValue = getExposureValue(compensatedResult);
mCollector.expectInRange("Exposure setting out of bound", compensatedExposureValue,
minExposureValue, maxExposureValueStill);
double observedRatio = (double) compensatedExposureValue / normalExposureValue;
double error = observedRatio / expectedRatio;
- mCollector.expectInRange(String.format(
- "Exposure compensation ratio exceeds error tolerence:"
- + " expected(%f) observed(%f) ", expectedRatio, observedRatio),
- error,
+ String errorString = String.format(
+ "Exposure compensation ratio exceeds error tolerence:" +
+ " expected(%f) observed(%f)." +
+ " Normal exposure time %d us, sensitivity %d." +
+ " Compensated exposure time %d us, sensitivity %d",
+ expectedRatio, observedRatio,
+ (int) (getValueNotNull(
+ normalResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
+ getValueNotNull(normalResult, CaptureResult.SENSOR_SENSITIVITY),
+ (int) (getValueNotNull(
+ compensatedResult, CaptureResult.SENSOR_EXPOSURE_TIME) / 1000),
+ getValueNotNull(compensatedResult, CaptureResult.SENSOR_SENSITIVITY));
+ mCollector.expectInRange(errorString, error,
1.0 - AE_COMPENSATION_ERROR_TOLERANCE,
1.0 + AE_COMPENSATION_ERROR_TOLERANCE);
}
mCollector.expectEquals("Exposure compensation result should match requested value.",
exposureCompensation,
- result.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
+ compensatedResult.get(CaptureResult.CONTROL_AE_EXPOSURE_COMPENSATION));
mCollector.expectTrue("Exposure lock should be set",
- result.get(CaptureResult.CONTROL_AE_LOCK));
+ compensatedResult.get(CaptureResult.CONTROL_AE_LOCK));
Image image = imageListener.getImage(CAPTURE_IMAGE_TIMEOUT_MS);
validateJpegCapture(image, maxStillSz);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
index 687826c..bd47024 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorBatchingTests.java
@@ -20,9 +20,7 @@
import android.hardware.SensorManager;
import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.sensoroperations.TestSensorFlushOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
import android.hardware.cts.helpers.sensorverification.ISensorVerification;
import java.util.concurrent.TimeUnit;
@@ -263,7 +261,7 @@
rateUs,
maxBatchReportLatencyUs);
TestSensorOperation operation =
- new TestSensorOperation(environment, testDurationSec, TimeUnit.SECONDS);
+ TestSensorOperation.createOperation(environment, testDurationSec, TimeUnit.SECONDS);
executeTest(environment, operation, false /* flushExpected */);
}
@@ -279,15 +277,15 @@
shouldEmulateSensorUnderLoad(),
rateUs,
maxBatchReportLatencyUs);
- TestSensorFlushOperation operation =
- new TestSensorFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
+ TestSensorOperation operation = TestSensorOperation
+ .createFlushOperation(environment, flushDurationSec, TimeUnit.SECONDS);
executeTest(environment, operation, true /* flushExpected */);
}
private void executeTest(
TestSensorEnvironment environment,
- VerifiableSensorOperation operation,
+ TestSensorOperation operation,
boolean flushExpected) throws Throwable {
operation.addDefaultVerifications();
operation.setLogEvents(true);
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
index 50cb12d..4dfa16e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -24,7 +24,6 @@
import android.hardware.cts.helpers.sensoroperations.RepeatingSensorOperation;
import android.hardware.cts.helpers.sensoroperations.SequentialSensorOperation;
import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
-import android.hardware.cts.helpers.sensoroperations.VerifiableSensorOperation;
import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
import java.util.Random;
@@ -81,7 +80,7 @@
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation continuousOperation =
- new TestSensorOperation(environment, 100 /* eventCount */);
+ TestSensorOperation.createOperation(environment, 100 /* eventCount */);
continuousOperation.addVerification(new EventOrderingVerification());
operation.add(new RepeatingSensorOperation(continuousOperation, ITERATIONS));
@@ -93,7 +92,7 @@
sensor.getMinDelay(),
MAX_REPORTING_LATENCY_US);
TestSensorOperation batchingOperation =
- new TestSensorOperation(batchingEnvironment, 100 /* eventCount */);
+ TestSensorOperation.createOperation(batchingEnvironment, 100 /* eventCount */);
batchingOperation.addVerification(new EventOrderingVerification());
operation.add(new RepeatingSensorOperation(batchingOperation, ITERATIONS));
}
@@ -145,7 +144,7 @@
generateSamplingRateInUs(sensorType),
generateReportLatencyInUs());
TestSensorOperation sensorOperation =
- new TestSensorOperation(environment, 100 /* eventCount */);
+ TestSensorOperation.createOperation(environment, 100 /* eventCount */);
sensorOperation.addVerification(new EventOrderingVerification());
sequentialOperation.add(sensorOperation);
}
@@ -229,7 +228,7 @@
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST);
TestSensorOperation tester =
- new TestSensorOperation(testerEnvironment, 100 /* event count */);
+ TestSensorOperation.createOperation(testerEnvironment, 100 /* event count */);
tester.addVerification(new EventOrderingVerification());
TestSensorEnvironment testeeEnvironment = new TestSensorEnvironment(
@@ -237,8 +236,8 @@
sensorTypeTestee,
shouldEmulateSensorUnderLoad(),
SensorManager.SENSOR_DELAY_FASTEST);
- VerifiableSensorOperation testee =
- new TestSensorOperation(testeeEnvironment, 100 /* event count */);
+ TestSensorOperation testee =
+ TestSensorOperation.createOperation(testeeEnvironment, 100 /* event count */);
testee.addVerification(new EventOrderingVerification());
ParallelSensorOperation operation = new ParallelSensorOperation();
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index d8b8e51..08d06c6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -18,6 +18,8 @@
import com.android.cts.util.TimeoutReq;
+import junit.framework.Assert;
+
import android.content.Context;
import android.content.pm.PackageManager;
import android.hardware.Sensor;
@@ -27,44 +29,70 @@
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.SensorNotSupportedException;
+import android.hardware.cts.helpers.SensorTestStateNotSupportedException;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.sensoroperations.ParallelSensorOperation;
+import android.hardware.cts.helpers.sensoroperations.TestSensorOperation;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.PowerManager;
import android.os.SystemClock;
import android.util.Log;
+import java.util.ArrayList;
import java.util.List;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
public class SensorTest extends SensorTestCase {
- private SensorManager mSensorManager;
- private TriggerListener mTriggerListener;
- private SensorListener mSensorListener;
- private List<Sensor> mSensorList;
private static final String TAG = "SensorTest";
+
// Test only SDK defined sensors. Any sensors with type > 100 are ignored.
private static final int MAX_OFFICIAL_ANDROID_SENSOR_TYPE = 100;
- private static final long TIMEOUT_TOLERANCE_US = TimeUnit.SECONDS.toMicros(5);
- private static final double MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE = 0.9;
+
private PowerManager.WakeLock mWakeLock;
+ private SensorManager mSensorManager;
+ private NullTriggerEventListener mNullTriggerEventListener;
+ private NullSensorEventListener mNullSensorEventListener;
+ private List<Sensor> mSensorList;
@Override
protected void setUp() throws Exception {
- super.setUp();
- mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
- mTriggerListener = new TriggerListener();
- mSensorListener = new SensorListener();
- mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
+ Context context = getContext();
+ PowerManager pm = (PowerManager) context.getSystemService(Context.POWER_SERVICE);
mWakeLock = pm.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, TAG);
+
+ mSensorManager = (SensorManager) context.getSystemService(Context.SENSOR_SERVICE);
+ mNullTriggerEventListener = new NullTriggerEventListener();
+ mNullSensorEventListener = new NullSensorEventListener();
+
+ mSensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ assertNotNull("SensorList was null.", mSensorList);
+ if (mSensorList.isEmpty()) {
+ // several devices will not have sensors, so we need to skip the tests in those cases
+ throw new SensorTestStateNotSupportedException(
+ "Sensors are not available in the system.");
+ }
+
+ mWakeLock.acquire();
}
+ @Override
+ protected void tearDown(){
+ if (mWakeLock != null && mWakeLock.isHeld()) {
+ mWakeLock.release();
+ }
+ }
+
+ @SuppressWarnings("deprecation")
public void testSensorOperations() {
// Because we can't know every sensors unit details, so we can't assert
// get values with specified values.
- List<Sensor> sensors = mSensorManager.getSensorList(Sensor.TYPE_ALL);
- assertNotNull(sensors);
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
boolean hasAccelerometer = getContext().getPackageManager().hasSystemFeature(
PackageManager.FEATURE_SENSOR_ACCELEROMETER);
@@ -149,7 +177,6 @@
}
assertTrue(sensors.get(0).getName() + " defined as non-wake-up sensor",
sensors.get(0).isWakeUpSensor());
- return;
}
// Some sensors like proximity, significant motion etc. are defined as wake-up sensors by
@@ -207,372 +234,137 @@
public void testRequestTriggerWithNonTriggerSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.requestTriggerSensor(mTriggerListener, sensor);
- assertFalse(result);
+ if (sensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
+ boolean result = mSensorManager.requestTriggerSensor(mNullTriggerEventListener, sensor);
+ assertFalse(result);
}
public void testCancelTriggerWithNonTriggerSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.cancelTriggerSensor(mTriggerListener, sensor);
- assertFalse(result);
+ if (sensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
+ boolean result = mSensorManager.cancelTriggerSensor(mNullTriggerEventListener, sensor);
+ assertFalse(result);
}
public void testRegisterWithTriggerSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_SIGNIFICANT_MOTION);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.registerListener(mSensorListener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertFalse(result);
+ if (sensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_SIGNIFICANT_MOTION);
}
+ boolean result = mSensorManager.registerListener(
+ mNullSensorEventListener,
+ sensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ assertFalse(result);
}
public void testRegisterTwiceWithSameSensor() {
Sensor sensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
- boolean result;
- if (sensor != null) {
- result = mSensorManager.registerListener(mSensorListener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertTrue(result);
- result = mSensorManager.registerListener(mSensorListener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertFalse(result);
+ if (sensor == null) {
+ throw new SensorNotSupportedException(Sensor.TYPE_ACCELEROMETER);
}
+
+ boolean result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ assertTrue(result);
+
+ result = mSensorManager.registerListener(mNullSensorEventListener, sensor,
+ SensorManager.SENSOR_DELAY_NORMAL);
+ assertFalse(result);
}
- class SensorEventTimeStampListener implements SensorEventListener {
- SensorEventTimeStampListener(long eventReportLatencyNs, CountDownLatch latch) {
- mEventReportLatencyNs = eventReportLatencyNs;
- mPrevTimeStampNs = -1;
- mLatch = latch;
- numErrors = 0;
- }
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- if (mPrevTimeStampNs == -1) {
- mPrevTimeStampNs = event.timestamp;
- return;
- }
- long currTimeStampNs = event.timestamp;
- if (currTimeStampNs <= mPrevTimeStampNs) {
- Log.w(TAG, "Timestamps not monotonically increasing curr_ts_ns=" +
- event.timestamp + " prev_ts_ns=" + mPrevTimeStampNs);
- numErrors++;
- mPrevTimeStampNs = currTimeStampNs;
- return;
- }
- mLatch.countDown();
-
- final long elapsedRealtimeNs = SystemClock.elapsedRealtimeNanos();
-
- if (elapsedRealtimeNs <= currTimeStampNs) {
- Log.w(TAG, "Timestamps into the future curr elapsedRealTimeNs=" + elapsedRealtimeNs
- + " current sensor ts_ns=" + currTimeStampNs);
- ++numErrors;
- } else if (elapsedRealtimeNs-currTimeStampNs > SYNC_TOLERANCE + mEventReportLatencyNs) {
- Log.w(TAG, "Timestamp sync error elapsedRealTimeNs=" + elapsedRealtimeNs +
- " curr_ts_ns=" + currTimeStampNs +
- " diff_ns=" + (elapsedRealtimeNs - currTimeStampNs) +
- " SYNC_TOLERANCE_NS=" + SYNC_TOLERANCE +
- " eventReportLatencyNs=" + mEventReportLatencyNs);
- ++numErrors;
- }
- mPrevTimeStampNs = currTimeStampNs;
- }
-
- public int getNumErrors() {
- return numErrors;
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- private int numErrors;
- private long mEventReportLatencyNs;
- private long mPrevTimeStampNs;
- private final CountDownLatch mLatch;
- private final long SYNC_TOLERANCE = 500000000L; // 500 milli seconds approx.
- }
-
- // Register for each sensor and compare the timestamps of SensorEvents that you get with
- // elapsedRealTimeNano.
+ // TODO: remove when parametized tests are supported and EventTimestampSynchronization
+ // verification is added to default verifications
@TimeoutReq(minutes=60)
public void testSensorTimeStamps() throws Exception {
- final int numEvents = 2000;
- try {
- mWakeLock.acquire();
- int numErrors = 0;
- for (Sensor sensor : mSensorList) {
- // Skip OEM defined sensors and non continuous sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
- continue;
- }
-
- for (int iterations = 0; iterations < 2; ++iterations) {
- // Test in both batch mode and non-batch mode for every sensor.
- long maxBatchReportLatencyNs = 10000000000L; // 10 secs
- if (iterations % 2 == 0) maxBatchReportLatencyNs = 0;
-
- final long samplingPeriodNs = (long)(TimeUnit.MICROSECONDS.toNanos(
- sensor.getMinDelay())/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE);
- // If there is a FIFO and a wake-lock is held, events will be reported when
- // the batch timeout expires or when the FIFO is full which ever occurs
- // earlier.
- final long eventReportLatencyNs = Math.min(maxBatchReportLatencyNs,
- sensor.getFifoMaxEventCount() * samplingPeriodNs);
-
- final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
- SensorEventTimeStampListener listener = new SensorEventTimeStampListener(
- eventReportLatencyNs, eventsRemaining);
-
- Log.i(TAG, "Running timeStamp test on " + sensor.getName());
- boolean result = mSensorManager.registerListener(listener, sensor,
- SensorManager.SENSOR_DELAY_FASTEST,
- (int)maxBatchReportLatencyNs/1000);
- assertTrue("Sensor registerListener failed ", result);
-
- long timeToWaitUs = samplingPeriodNs/1000 + eventReportLatencyNs/1000 +
- TIMEOUT_TOLERANCE_US;
- long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
- (int)(eventReportLatencyNs/1000), eventsRemaining);
-
- mSensorManager.unregisterListener(listener);
- if (eventsRemaining.getCount() > 0) {
- failTimedOut(sensor.getName(), (double) totalTimeWaitedUs/1000,
- numEvents, (double) sensor.getMinDelay()/1000,
- eventsRemaining.getCount(),
- numEvents - eventsRemaining.getCount());
- }
- if (listener.getNumErrors() > 5) {
- fail("Check logcat. Timestamp test failed. numErrors=" +
- listener.getNumErrors() + " " + sensor.getName() +
- " maxBatchReportLatencyNs=" + maxBatchReportLatencyNs +
- " samplingPeriodNs=" + sensor.getMinDelay());
- numErrors += listener.getNumErrors();
- } else {
- Log.i(TAG, "TimeStamp test PASS'd on " + sensor.getName());
- }
- }
- }
- } finally {
- mWakeLock.release();
+ ArrayList<Throwable> errorsFound = new ArrayList<>();
+ for (Sensor sensor : mSensorList) {
+ // test both continuous and batching mode sensors
+ verifyLongActivation(sensor, 0 /* maxReportLatencyUs */, errorsFound);
+ verifyLongActivation(sensor, (int) TimeUnit.SECONDS.toMicros(10), errorsFound);
}
+ assertOnErrors(errorsFound);
}
- private void failTimedOut(String sensorName, double totalTimeWaitedMs, int numEvents,
- double minDelayMs, long eventsRemaining, long eventsReceived) {
- final String TIMED_OUT_FORMAT = "Timed out waiting for events from %s " +
- "waited for time=%.1fms to receive totalEvents=%d at samplingRate=%.1fHz" +
- " remainingEvents=%d received events=%d";
- fail(String.format(TIMED_OUT_FORMAT, sensorName, totalTimeWaitedMs, numEvents,
- 1000/minDelayMs, eventsRemaining, eventsReceived));
- }
-
- // Register for updates from each continuous mode sensor, wait for N events, call flush and
- // wait for flushCompleteEvent before unregistering for the sensor.
+ // TODO: remove when parameterized tests are supported (see SensorBatchingTests.java)
@TimeoutReq(minutes=20)
public void testBatchAndFlush() throws Exception {
- try {
- mWakeLock.acquire();
- for (Sensor sensor : mSensorList) {
- // Skip ONLY one-shot sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
- registerListenerCallFlush(sensor, null);
- }
- }
- } finally {
- mWakeLock.release();
+ ArrayList<Throwable> errorsFound = new ArrayList<>();
+ for (Sensor sensor : mSensorList) {
+ verifyRegisterListenerCallFlush(sensor, null /* handler */, errorsFound);
}
+ assertOnErrors(errorsFound);
}
- // Same as testBatchAndFlush but using Handler version of the API to register for sensors.
- // onSensorChanged is now called on a background thread.
+ /**
+ * Verifies that sensor events arrive in the given message queue (Handler).
+ */
@TimeoutReq(minutes=10)
public void testBatchAndFlushWithHandler() throws Exception {
- try {
- mWakeLock.acquire();
- HandlerThread handlerThread = new HandlerThread("sensorThread");
- handlerThread.start();
- Handler handler = new Handler(handlerThread.getLooper());
- for (Sensor sensor : mSensorList) {
- // Skip ONLY one-shot sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_ONE_SHOT) {
- registerListenerCallFlush(sensor, handler);
- }
+ Sensor sensor = null;
+ for (Sensor s : mSensorList) {
+ if (s.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+ sensor = s;
+ break;
}
- } finally {
- mWakeLock.release();
}
+ if (sensor == null) {
+ throw new SensorTestStateNotSupportedException(
+ "There are no Continuous sensors in the device.");
+ }
+
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ SensorManager.SENSOR_DELAY_FASTEST,
+ (int) TimeUnit.SECONDS.toMicros(5));
+ TestSensorManager sensorManager = new TestSensorManager(environment);
+
+ HandlerThread handlerThread = new HandlerThread("sensorThread");
+ handlerThread.start();
+ Handler handler = new Handler(handlerThread.getLooper());
+ TestSensorEventListener listener = new TestSensorEventListener(handler);
+
+ sensorManager.registerListener(listener);
+ listener.waitForEvents(1);
+ sensorManager.requestFlush();
+ listener.waitForFlushComplete();
+ listener.assertEventsReceivedInHandler();
}
- private void registerListenerCallFlush(Sensor sensor, Handler handler)
- throws InterruptedException {
- if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
- return;
- }
- final int numEvents = 500;
- final int rateUs = 0; // DELAY_FASTEST
- final int maxBatchReportLatencyUs = 10000000;
- final CountDownLatch eventsRemaining = new CountDownLatch(numEvents);
- final CountDownLatch flushReceived = new CountDownLatch(1);
- SensorEventListener2 listener = new SensorEventListener2() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- eventsRemaining.countDown();
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public void onFlushCompleted(Sensor sensor) {
- flushReceived.countDown();
- }
- };
- // Consider only continuous mode sensors for testing registerListener.
- // For on-change sensors, call registerListener() so that the listener is associated
- // with the sensor so that flush(listener) can be called on it.
- try {
- Log.i(TAG, "testBatch " + sensor.getName());
- boolean result = mSensorManager.registerListener(listener, sensor,
- rateUs, maxBatchReportLatencyUs, handler);
- assertTrue("registerListener failed " + sensor.getName(), result);
-
- // Wait for 500 events or N seconds before the test times out.
- if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
- // Wait for approximately the time required to generate these events + a tolerance
- // of 10 seconds.
- long timeToWaitUs =
- numEvents*(long)(sensor.getMinDelay()/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE)
- + maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
-
- long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs,
- maxBatchReportLatencyUs, eventsRemaining);
- if (eventsRemaining.getCount() > 0) {
- failTimedOut(sensor.getName(), (double)totalTimeWaitedUs/1000, numEvents,
- (double)sensor.getMinDelay()/1000,
- eventsRemaining.getCount(), numEvents - eventsRemaining.getCount());
- }
- }
- Log.i(TAG, "testFlush " + sensor.getName());
- result = mSensorManager.flush(listener);
- assertTrue("flush failed " + sensor.getName(), result);
- boolean collectedAllEvents = flushReceived.await(TIMEOUT_TOLERANCE_US,
- TimeUnit.MICROSECONDS);
- if (!collectedAllEvents) {
- fail("Timed out waiting for flushCompleteEvent from " + sensor.getName() +
- " waitedFor="+ TIMEOUT_TOLERANCE_US/1000 + "ms");
- }
- Log.i(TAG, "testBatchAndFlush PASS " + sensor.getName());
- } finally {
- mSensorManager.unregisterListener(listener);
- }
- }
-
- // Wait till the CountDownLatch counts down to zero. If the events are not delivered within
- // timetoWaitUs, wait an additional maxReportLantencyUs and check if the sensor is streaming
- // data or not. If the sensor is not streaming at all, fail the test or else wait in increments
- // of maxReportLantencyUs to collect sensor events.
- private long waitToCollectAllEvents(long timeToWaitUs, int maxReportLatencyUs,
- CountDownLatch eventsRemaining)
- throws InterruptedException {
- boolean collectedAllEvents = false;
- long totalTimeWaitedUs = 0;
- long remainingEvents;
- final long INCREMENTAL_WAIT_US = maxReportLatencyUs + TimeUnit.SECONDS.toMicros(1);
- do {
- totalTimeWaitedUs += timeToWaitUs;
- remainingEvents = eventsRemaining.getCount();
- collectedAllEvents = eventsRemaining.await(timeToWaitUs, TimeUnit.MICROSECONDS);
- timeToWaitUs = INCREMENTAL_WAIT_US;
- } while (!collectedAllEvents &&
- (remainingEvents - eventsRemaining.getCount() >=(long)INCREMENTAL_WAIT_US/1000000));
- return totalTimeWaitedUs;
- }
-
- // Call registerListener for multiple sensors at a time and call flush.
+ // TODO: after L release move to SensorBatchingTests and run in all sensors with default
+ // verifications enabled
public void testBatchAndFlushWithMutipleSensors() throws Exception {
- final int MAX_SENSORS = 3;
- int numSensors = mSensorList.size() < MAX_SENSORS ? mSensorList.size() : MAX_SENSORS;
- if (numSensors == 0) {
- return;
+ final int maxSensors = 3;
+ final int maxReportLatencyUs = (int) TimeUnit.SECONDS.toMicros(10);
+ int sensorsCount = mSensorList.size();
+ int numSensors = sensorsCount < maxSensors ? sensorsCount : maxSensors;
+
+ StringBuilder builder = new StringBuilder();
+ ParallelSensorOperation parallelSensorOperation = new ParallelSensorOperation();
+ for (int i = 0; i < sensorsCount && numSensors > 0; ++i) {
+ Sensor sensor = mSensorList.get(i);
+ // skip all non-continuous sensors
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_CONTINUOUS) {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ maxReportLatencyUs);
+ FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+ parallelSensorOperation.add(new TestSensorOperation(environment, executor));
+ --numSensors;
+ builder.append(sensor.getName()).append(", ");
+ }
}
- final int numEvents = 500;
- int rateUs = 0; // DELAY_FASTEST
- final int maxBatchReportLatencyUs = 10000000;
- final CountDownLatch eventsRemaining = new CountDownLatch(numSensors * numEvents);
- final CountDownLatch flushReceived = new CountDownLatch(numSensors);
- SensorEventListener2 listener = new SensorEventListener2() {
- @Override
- public void onSensorChanged(SensorEvent event) {
- eventsRemaining.countDown();
- }
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- @Override
- public void onFlushCompleted(Sensor sensor) {
- flushReceived.countDown();
- }
- };
-
- try {
- mWakeLock.acquire();
- StringBuilder registeredSensors = new StringBuilder(30);
- for (Sensor sensor : mSensorList) {
- // Skip all non-continuous sensors.
- if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
- continue;
- }
- rateUs = Math.max(sensor.getMinDelay(), rateUs);
- boolean result = mSensorManager.registerListener(listener, sensor,
- SensorManager.SENSOR_DELAY_FASTEST, maxBatchReportLatencyUs);
- assertTrue("registerListener failed for " + sensor.getName(), result);
- registeredSensors.append(sensor.getName());
- registeredSensors.append(" ");
- if (--numSensors == 0) {
- break;
- }
- }
- if (registeredSensors.toString().isEmpty()) {
- return;
- }
-
- Log.i(TAG, "testBatchAndFlushWithMutipleSensors " + registeredSensors);
- long timeToWaitUs =
- numEvents*(long)(rateUs/MIN_SAMPLING_FREQUENCY_MULTIPLIER_TOLERANCE) +
- maxBatchReportLatencyUs + TIMEOUT_TOLERANCE_US;
- long totalTimeWaitedUs = waitToCollectAllEvents(timeToWaitUs, maxBatchReportLatencyUs,
- eventsRemaining);
- if (eventsRemaining.getCount() > 0) {
- failTimedOut(registeredSensors.toString(), (double)totalTimeWaitedUs/1000,
- numEvents, (double)rateUs/1000, eventsRemaining.getCount(),
- numEvents - eventsRemaining.getCount());
- }
- boolean result = mSensorManager.flush(listener);
- assertTrue("flush failed " + registeredSensors.toString(), result);
- boolean collectedFlushEvent =
- flushReceived.await(TIMEOUT_TOLERANCE_US, TimeUnit.MICROSECONDS);
- if (!collectedFlushEvent) {
- fail("Timed out waiting for flushCompleteEvent from " +
- registeredSensors.toString() + " waited for=" + timeToWaitUs/1000 + "ms");
- }
- Log.i(TAG, "testBatchAndFlushWithMutipleSensors PASS'd");
- } finally {
- mSensorManager.unregisterListener(listener);
- mWakeLock.release();
- }
+ Log.i(TAG, "Testing batch/flush for sensors: " + builder);
+ parallelSensorOperation.execute();
}
private void assertSensorValues(Sensor sensor) {
@@ -583,8 +375,8 @@
assertTrue("Max resolution must be positive. Resolution=" + sensor.getResolution() +
" " + sensor.getName(), sensor.getResolution() >= 0);
assertNotNull("Vendor name must not be null " + sensor.getName(), sensor.getVendor());
- assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
- sensor.getName(), sensor.getVersion() > 0);
+ assertTrue("Version must be positive version=" + sensor.getVersion() + " " +
+ sensor.getName(), sensor.getVersion() > 0);
int fifoMaxEventCount = sensor.getFifoMaxEventCount();
int fifoReservedEventCount = sensor.getFifoReservedEventCount();
assertTrue(fifoMaxEventCount >= 0);
@@ -617,19 +409,142 @@
assertEquals(sensors, mSensorManager.getSensors());
}
- class TriggerListener extends TriggerEventListener {
- @Override
- public void onTrigger(TriggerEvent event) {
+ /**
+ * Verifies that a continuous sensor produces events that have timestamps synchronized with
+ * {@link SystemClock#elapsedRealtimeNanos()}.
+ */
+ private void verifyLongActivation(
+ Sensor sensor,
+ int maxReportLatencyUs,
+ ArrayList<Throwable> errorsFound) throws InterruptedException {
+ if (sensor.getReportingMode() != Sensor.REPORTING_MODE_CONTINUOUS) {
+ return;
+ }
+
+ try {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ maxReportLatencyUs);
+ TestSensorOperation operation =
+ TestSensorOperation.createOperation(environment, 20, TimeUnit.SECONDS);
+ operation.addVerification(EventGapVerification.getDefault(environment));
+ operation.addVerification(EventOrderingVerification.getDefault(environment));
+ operation.addVerification(
+ EventTimestampSynchronizationVerification.getDefault(environment));
+
+ Log.i(TAG, "Running timestamp test on: " + sensor.getName());
+ operation.execute();
+ } catch (InterruptedException e) {
+ // propagate so the test can stop
+ throw e;
+ } catch (Throwable e) {
+ errorsFound.add(e);
+ Log.e(TAG, e.getMessage());
}
}
- class SensorListener implements SensorEventListener {
- @Override
- public void onSensorChanged(SensorEvent event) {
+ /**
+ * Verifies that a client can listen for events, and that
+ * {@link SensorManager#flush(SensorEventListener)} will trigger the appropriate notification
+ * for {@link SensorEventListener2#onFlushCompleted(Sensor)}.
+ */
+ private void verifyRegisterListenerCallFlush(
+ Sensor sensor,
+ Handler handler,
+ ArrayList<Throwable> errorsFound)
+ throws InterruptedException {
+ if (sensor.getReportingMode() == Sensor.REPORTING_MODE_ONE_SHOT) {
+ return;
}
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ try {
+ TestSensorEnvironment environment = new TestSensorEnvironment(
+ getContext(),
+ sensor,
+ shouldEmulateSensorUnderLoad(),
+ SensorManager.SENSOR_DELAY_FASTEST,
+ (int) TimeUnit.SECONDS.toMicros(10));
+ FlushExecutor executor = new FlushExecutor(environment, 500 /* eventCount */);
+ TestSensorOperation operation = new TestSensorOperation(environment, executor, handler);
+
+ Log.i(TAG, "Running flush test on: " + sensor.getName());
+ operation.execute();
+ } catch (InterruptedException e) {
+ // propagate so the test can stop
+ throw e;
+ } catch (Throwable e) {
+ errorsFound.add(e);
+ Log.e(TAG, e.getMessage());
}
}
+
+ private void assertOnErrors(List<Throwable> errorsFound) {
+ if (!errorsFound.isEmpty()) {
+ StringBuilder builder = new StringBuilder();
+ for (Throwable error : errorsFound) {
+ builder.append(error.getMessage()).append("\n");
+ }
+ Assert.fail(builder.toString());
+ }
+ }
+
+ /**
+ * A delegate that drives the execution of Batch/Flush tests.
+ * It performs several operations in order:
+ * - registration
+ * - for continuous sensors it first ensures that the FIFO is filled
+ * - if events do not arrive on time, an assert will be triggered
+ * - requests flush of sensor data
+ * - waits for {@link SensorEventListener2#onFlushCompleted(Sensor)}
+ * - if the event does not arrive, an assert will be triggered
+ */
+ private class FlushExecutor implements TestSensorOperation.Executor {
+ private final TestSensorEnvironment mEnvironment;
+ private final int mEventCount;
+
+ public FlushExecutor(TestSensorEnvironment environment, int eventCount) {
+ mEnvironment = environment;
+ mEventCount = eventCount;
+ }
+
+ /**
+ * Consider only continuous mode sensors for testing register listener.
+ *
+ * For on-change sensors, we only use
+ * {@link TestSensorManager#registerListener(TestSensorEventListener)} to associate the
+ * listener with the sensor. So that {@link TestSensorManager#requestFlush()} can be
+ * invoked on it.
+ */
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ int sensorReportingMode = mEnvironment.getSensor().getReportingMode();
+ try {
+ sensorManager.registerListener(listener);
+ if (sensorReportingMode == Sensor.REPORTING_MODE_CONTINUOUS) {
+ listener.waitForEvents(mEventCount);
+ }
+ sensorManager.requestFlush();
+ listener.waitForFlushComplete();
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ }
+
+ private class NullTriggerEventListener extends TriggerEventListener {
+ @Override
+ public void onTrigger(TriggerEvent event) {}
+ }
+
+ private class NullSensorEventListener implements SensorEventListener {
+ @Override
+ public void onSensorChanged(SensorEvent event) {}
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {}
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
index 3bd4a03..a98dc59 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SingleSensorTests.java
@@ -541,7 +541,8 @@
sensorType,
shouldEmulateSensorUnderLoad(),
rateUs);
- TestSensorOperation op = new TestSensorOperation(environment, 5, TimeUnit.SECONDS);
+ TestSensorOperation op =
+ TestSensorOperation.createOperation(environment, 5, TimeUnit.SECONDS);
op.addDefaultVerifications();
op.setLogEvents(true);
try {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
index ca7d133..3bedc05 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/CollectingSensorEventListener.java
@@ -17,7 +17,6 @@
package android.hardware.cts.helpers;
import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
import java.util.ArrayList;
import java.util.Collections;
@@ -32,21 +31,6 @@
private final ArrayList<TestSensorEvent> mSensorEventsList = new ArrayList<TestSensorEvent>();
/**
- * Constructs a {@link CollectingSensorEventListener} with an additional
- * {@link SensorEventListener2}.
- */
- public CollectingSensorEventListener(SensorEventListener2 listener) {
- super(listener);
- }
-
- /**
- * Constructs a {@link CollectingSensorEventListener}.
- */
- public CollectingSensorEventListener() {
- this(null);
- }
-
- /**
* {@inheritDoc}
*/
@Override
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
index b3b8559..0f84ee6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCalibratedUncalibratedVerifier.java
@@ -51,7 +51,8 @@
*/
public void execute() throws Throwable {
CollectingSensorEventListener calibratedTestListener = new CollectingSensorEventListener();
- CollectingSensorEventListener uncalibratedTestListener = new CollectingSensorEventListener();
+ CollectingSensorEventListener uncalibratedTestListener =
+ new CollectingSensorEventListener();
mCalibratedSensorManager.registerListener(calibratedTestListener);
mUncalibratedSensorManager.registerListener(uncalibratedTestListener);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
index a79e5b1..e89b473 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -192,7 +192,7 @@
TestSensorEnvironment environment,
String extras) {
return String.format(
- "%s | sensor='%s', samplingPeriodUs=%d, maxReportLatencyUs=%d | %s",
+ "%s | sensor='%s', samplingPeriod=%dus, maxReportLatency=%dus | %s",
label,
environment.getSensor().getName(),
environment.getRequestedSamplingPeriodUs(),
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
index 7be6c0c..6f98e86 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorStats.java
@@ -40,14 +40,15 @@
public class SensorStats {
public static final String DELIMITER = "__";
- public static final String FIRST_TIMESTAMP_KEY = "first_timestamp";
- public static final String LAST_TIMESTAMP_KEY = "last_timestamp";
public static final String ERROR = "error";
- public static final String EVENT_COUNT_KEY = "event_count";
public static final String EVENT_GAP_COUNT_KEY = "event_gap_count";
public static final String EVENT_GAP_POSITIONS_KEY = "event_gap_positions";
public static final String EVENT_OUT_OF_ORDER_COUNT_KEY = "event_out_of_order_count";
public static final String EVENT_OUT_OF_ORDER_POSITIONS_KEY = "event_out_of_order_positions";
+ public static final String EVENT_TIME_SYNCHRONIZATION_COUNT_KEY =
+ "event_time_synchronization_count";
+ public static final String EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY =
+ "event_time_synchronization_positions";
public static final String FREQUENCY_KEY = "frequency";
public static final String JITTER_95_PERCENTILE_PERCENT_KEY = "jitter_95_percentile_percent";
public static final String MEAN_KEY = "mean";
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
index 8f33f92..5be30a4 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEnvironment.java
@@ -28,6 +28,13 @@
* The environment is self contained and carries its state around all the sensor test framework.
*/
public class TestSensorEnvironment {
+
+ /**
+ * It represents the fraction of the expected sampling frequency, at which the sensor can
+ * actually produce events.
+ */
+ private static final float MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER = 0.9f;
+
private final Context mContext;
private final Sensor mSensor;
private final boolean mSensorMightHaveMoreListeners;
@@ -40,7 +47,10 @@
* @param context The context for the test
* @param sensorType The type of the sensor under test
* @param samplingPeriodUs The requested collection period for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(Context context, int sensorType, int samplingPeriodUs) {
this(context, sensorType, false /* sensorMightHaveMoreListeners */, samplingPeriodUs);
}
@@ -52,7 +62,10 @@
* @param sensorType The type of the sensor under test
* @param samplingPeriodUs The requested collection period for the sensor under test
* @param maxReportLatencyUs The requested collection report latency for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(
Context context,
int sensorType,
@@ -72,7 +85,10 @@
* @param sensorType The type of the sensor under test
* @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
* @param samplingPeriodUs The requested collection period for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(
Context context,
int sensorType,
@@ -93,7 +109,10 @@
* @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load
* @param samplingPeriodUs The requested collection period for the sensor under test
* @param maxReportLatencyUs The requested collection report latency for the sensor under test
+ *
+ * @deprecated Use variants with {@link Sensor} objects.
*/
+ @Deprecated
public TestSensorEnvironment(
Context context,
int sensorType,
@@ -112,6 +131,26 @@
*
* @param context The context for the test
* @param sensor The sensor under test
+ * @param samplingPeriodUs The requested collection period for the sensor under test
+ * @param maxReportLatencyUs The requested collection report latency for the sensor under test
+ */
+ public TestSensorEnvironment(
+ Context context,
+ Sensor sensor,
+ int samplingPeriodUs,
+ int maxReportLatencyUs) {
+ this(context,
+ sensor,
+ false /* sensorMightHaveMoreListeners */,
+ samplingPeriodUs,
+ maxReportLatencyUs);
+ }
+
+ /**
+ * Constructs an environment for sensor testing.
+ *
+ * @param context The context for the test
+ * @param sensor The sensor under test
* @param sensorMightHaveMoreListeners Whether the sensor under test is acting under load (this
* usually implies that there are several listeners
* requesting different sampling periods)
@@ -171,7 +210,8 @@
* data at different sampling rates (the rates are unknown); false otherwise.
*/
public boolean isSensorSamplingRateOverloaded() {
- return mSensorMightHaveMoreListeners && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
+ return mSensorMightHaveMoreListeners
+ && mSamplingPeriodUs != SensorManager.SENSOR_DELAY_FASTEST;
}
/**
@@ -197,6 +237,15 @@
}
/**
+ * @return The actual sampling period at which a sensor can sample data. This value is a
+ * fraction of {@link #getExpectedSamplingPeriodUs()}.
+ */
+ public int getMaximumExpectedSamplingPeriodUs() {
+ int expectedSamplingPeriodUs = getExpectedSamplingPeriodUs();
+ return (int) (expectedSamplingPeriodUs / MAXIMUM_EXPECTED_SAMPLING_FREQUENCY_MULTIPLIER);
+ }
+
+ /**
* @return The number of axes in the coordinate system of the sensor under test.
*/
public int getSensorAxesCount() {
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
index 9b3a5e4..ae36e6a 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorEventListener.java
@@ -21,9 +21,12 @@
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener2;
+import android.os.Handler;
+import android.os.Looper;
import android.os.SystemClock;
import android.util.Log;
+import java.util.ArrayList;
import java.util.Arrays;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
@@ -36,13 +39,16 @@
*/
public class TestSensorEventListener implements SensorEventListener2 {
public static final String LOG_TAG = "TestSensorEventListener";
- private static final long EVENT_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
- private static final long FLUSH_TIMEOUT_US = TimeUnit.MICROSECONDS.convert(5, TimeUnit.SECONDS);
+ private static final long EVENT_TIMEOUT_US = TimeUnit.SECONDS.toMicros(5);
+ private static final long FLUSH_TIMEOUT_US = TimeUnit.SECONDS.toMicros(10);
+
+ private final ArrayList<CountDownLatch> mEventLatches = new ArrayList<CountDownLatch>();
+ private final ArrayList<CountDownLatch> mFlushLatches = new ArrayList<CountDownLatch>();
private final SensorEventListener2 mListener;
+ private final Handler mHandler;
- private volatile CountDownLatch mEventLatch;
- private volatile CountDownLatch mFlushLatch = new CountDownLatch(1);
+ private volatile boolean mEventsReceivedInHandler = true;
private volatile TestSensorEnvironment mEnvironment;
private volatile boolean mLogEvents;
@@ -50,13 +56,28 @@
* Construct a {@link TestSensorEventListener}.
*/
public TestSensorEventListener() {
- this(null);
+ this(null /* listener */, null /* handler */);
+ }
+
+ /**
+ * Construct a {@link TestSensorEventListener} with a {@link Handler}.
+ */
+ public TestSensorEventListener(Handler handler) {
+ this(null /* listener */, handler);
}
/**
* Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}.
*/
public TestSensorEventListener(SensorEventListener2 listener) {
+ this(listener, null /* handler */);
+ }
+
+ /**
+ * Construct a {@link TestSensorEventListener} that wraps a {@link SensorEventListener2}, and it
+ * has a {@link Handler}.
+ */
+ public TestSensorEventListener(SensorEventListener2 listener, Handler handler) {
if (listener != null) {
mListener = listener;
} else {
@@ -67,6 +88,14 @@
public void onAccuracyChanged(Sensor sensor, int i) {}
};
}
+ mHandler = handler;
+ }
+
+ /**
+ * @return The handler (if any) associated with the instance.
+ */
+ public Handler getHandler() {
+ return mHandler;
}
/**
@@ -88,6 +117,7 @@
*/
@Override
public void onSensorChanged(SensorEvent event) {
+ checkHandler();
mListener.onSensorChanged(event);
if (mLogEvents) {
Log.v(LOG_TAG, String.format(
@@ -98,9 +128,10 @@
Arrays.toString(event.values)));
}
- CountDownLatch eventLatch = mEventLatch;
- if(eventLatch != null) {
- eventLatch.countDown();
+ synchronized (mEventLatches) {
+ for (CountDownLatch latch : mEventLatches) {
+ latch.countDown();
+ }
}
}
@@ -109,6 +140,7 @@
*/
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ checkHandler();
mListener.onAccuracyChanged(sensor, accuracy);
}
@@ -117,12 +149,14 @@
*/
@Override
public void onFlushCompleted(Sensor sensor) {
- CountDownLatch latch = mFlushLatch;
- mFlushLatch = new CountDownLatch(1);
- if(latch != null) {
- latch.countDown();
- }
+ checkHandler();
mListener.onFlushCompleted(sensor);
+
+ synchronized (mFlushLatches) {
+ for (CountDownLatch latch : mFlushLatches) {
+ latch.countDown();
+ }
+ }
}
/**
@@ -131,13 +165,23 @@
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
public void waitForFlushComplete() throws InterruptedException {
- CountDownLatch latch = mFlushLatch;
- if(latch == null) {
- return;
+ CountDownLatch latch = new CountDownLatch(1);
+ synchronized (mFlushLatches) {
+ mFlushLatches.add(latch);
}
- Assert.assertTrue(
- SensorCtsHelper.formatAssertionMessage("WaitForFlush", mEnvironment),
- latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+
+ try {
+ String message = SensorCtsHelper.formatAssertionMessage(
+ "WaitForFlush",
+ mEnvironment,
+ "timeout=%dus",
+ FLUSH_TIMEOUT_US);
+ Assert.assertTrue(message, latch.await(FLUSH_TIMEOUT_US, TimeUnit.MICROSECONDS));
+ } finally {
+ synchronized (mFlushLatches) {
+ mFlushLatches.remove(latch);
+ }
+ }
}
/**
@@ -146,22 +190,34 @@
* @throws AssertionError if there was a timeout after {@link #FLUSH_TIMEOUT_US} µs
*/
public void waitForEvents(int eventCount) throws InterruptedException {
- mEventLatch = new CountDownLatch(eventCount);
+ CountDownLatch eventLatch = new CountDownLatch(eventCount);
+ synchronized (mEventLatches) {
+ mEventLatches.add(eventLatch);
+ }
try {
- int rateUs = mEnvironment.getExpectedSamplingPeriodUs();
- // Timeout is 2 * event count * expected period + batch timeout + default wait
- long timeoutUs = (2 * eventCount * rateUs)
+ long samplingPeriodUs = mEnvironment.getMaximumExpectedSamplingPeriodUs();
+ // timeout is 2 * event count * expected period + batch timeout + default wait
+ // we multiply by two as not to raise an error in this function even if the events are
+ // streaming at a lower rate than expected, as long as it's not streaming twice as slow
+ // as expected
+ long timeoutUs = (2 * eventCount * samplingPeriodUs)
+ mEnvironment.getMaxReportLatencyUs()
+ EVENT_TIMEOUT_US;
- String message = SensorCtsHelper.formatAssertionMessage(
- "WaitForEvents",
- mEnvironment,
- "requested:%d, received:%d",
- eventCount,
- eventCount - mEventLatch.getCount());
- Assert.assertTrue(message, mEventLatch.await(timeoutUs, TimeUnit.MICROSECONDS));
+ boolean success = eventLatch.await(timeoutUs, TimeUnit.MICROSECONDS);
+ if (!success) {
+ String message = SensorCtsHelper.formatAssertionMessage(
+ "WaitForEvents",
+ mEnvironment,
+ "requested=%d, received=%d, timeout=%dus",
+ eventCount,
+ eventCount - eventLatch.getCount(),
+ timeoutUs);
+ Assert.fail(message);
+ }
} finally {
- mEventLatch = null;
+ synchronized (mEventLatches) {
+ mEventLatches.remove(eventLatch);
+ }
}
}
@@ -171,4 +227,22 @@
public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
SensorCtsHelper.sleep(duration, timeUnit);
}
+
+ /**
+ * Asserts that sensor events arrived in the proper thread if a {@link Handler} was associated
+ * with the current instance.
+ *
+ * If no events were received this assertion will be evaluated to {@code true}.
+ */
+ public void assertEventsReceivedInHandler() {
+ Assert.assertTrue(
+ "Events did not arrive in the Looper associated with the given Handler.",
+ mEventsReceivedInHandler);
+ }
+
+ private void checkHandler() {
+ if (mHandler != null) {
+ mEventsReceivedInHandler &= (mHandler.getLooper() == Looper.myLooper());
+ }
+ }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
index dc40ff4..a611bfc 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -19,35 +19,18 @@
import junit.framework.Assert;
import android.content.Context;
-import android.hardware.Sensor;
import android.hardware.SensorEventListener;
-import android.hardware.SensorEventListener2;
import android.hardware.SensorManager;
import android.util.Log;
-import java.util.concurrent.TimeUnit;
-
/**
- * A test class that performs the actions of {@link SensorManager} on a single sensor. This
- * class allows for a single sensor to be registered and unregistered as well as performing
- * operations such as flushing the sensor events and gathering events. This class also manages
- * performing the test verifications for the sensor manager.
- * <p>
- * This class requires that operations are performed in the following order:
- * <p><ul>
- * <li>{@link #registerListener(TestSensorEventListener)}</li>
- * <li>{@link #startFlush()}, {@link #waitForFlushCompleted()}, or {@link #flush()}.
- * <li>{@link #unregisterListener()}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, int)}</li>
- * </ul><p>Or:</p><ul>
- * <li>{@link #runSensor(TestSensorEventListener, long, TimeUnit)}</li>
- * </ul><p>
- * If methods are called outside of this order, they will print a warning to the log and then
- * return. Both {@link #runSensor(TestSensorEventListener, int)}} and
- * {@link #runSensor(TestSensorEventListener, long, TimeUnit)} will perform the appropriate
- * set up and tear down.
- * <p>
+ * A test class that performs the actions of {@link SensorManager} on a single sensor.
+ * This class allows for a single sensor to be registered and unregistered as well as performing
+ * operations such as flushing the sensor events and gathering events.
+ * This class also manages performing the test verifications for the sensor manager.
+ *
+ * NOTE: this class is expected to mirror {@link SensorManager} operations, and perform the
+ * required test verifications along with them.
*/
public class TestSensorManager {
private static final String LOG_TAG = "TestSensorManager";
@@ -55,7 +38,7 @@
private final SensorManager mSensorManager;
private final TestSensorEnvironment mEnvironment;
- private TestSensorEventListener mTestSensorEventListener;
+ private volatile TestSensorEventListener mTestSensorEventListener;
/**
* @deprecated Use {@link #TestSensorManager(TestSensorEnvironment)} instead.
@@ -90,7 +73,7 @@
return;
}
- mTestSensorEventListener = listener != null ? listener : new TestSensorEventListener();
+ mTestSensorEventListener = listener;
mTestSensorEventListener.setEnvironment(mEnvironment);
String message = SensorCtsHelper.formatAssertionMessage("registerListener", mEnvironment);
@@ -98,7 +81,8 @@
mTestSensorEventListener,
mEnvironment.getSensor(),
mEnvironment.getRequestedSamplingPeriodUs(),
- mEnvironment.getMaxReportLatencyUs());
+ mEnvironment.getMaxReportLatencyUs(),
+ listener.getHandler());
Assert.assertTrue(message, result);
}
@@ -110,136 +94,24 @@
Log.w(LOG_TAG, "No listener registered, returning.");
return;
}
-
- mSensorManager.unregisterListener(
- mTestSensorEventListener,
- mEnvironment.getSensor());
+ mSensorManager.unregisterListener(mTestSensorEventListener, mEnvironment.getSensor());
+ mTestSensorEventListener.assertEventsReceivedInHandler();
mTestSensorEventListener = null;
}
/**
- * Wait for a specific number of events.
- */
- public void waitForEvents(int eventCount) throws InterruptedException {
- if (mTestSensorEventListener == null) {
- Log.w(LOG_TAG, "No listener registered, returning.");
- return;
- }
- mTestSensorEventListener.waitForEvents(eventCount);
- }
-
- /**
- * Wait for a specific duration.
- */
- public void waitForEvents(long duration, TimeUnit timeUnit) throws InterruptedException {
- if (mTestSensorEventListener == null) {
- Log.w(LOG_TAG, "No listener registered, returning.");
- return;
- }
- mTestSensorEventListener.waitForEvents(duration, timeUnit);
- }
-
- /**
* Call {@link SensorManager#flush(SensorEventListener)}. This method will perform a no-op if
* the sensor is not registered.
*
- * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false
+ * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} fails.
*/
- public void startFlush() {
+ public void requestFlush() {
if (mTestSensorEventListener == null) {
+ Log.w(LOG_TAG, "No listener registered, returning.");
return;
}
-
Assert.assertTrue(
SensorCtsHelper.formatAssertionMessage("Flush", mEnvironment),
mSensorManager.flush(mTestSensorEventListener));
}
-
- /**
- * Wait for {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will
- * perform a no-op if the sensor is not registered.
- *
- * @throws AssertionError if there is a time out
- * @throws InterruptedException if the thread was interrupted
- */
- public void waitForFlushCompleted() throws InterruptedException {
- if (mTestSensorEventListener == null) {
- return;
- }
- mTestSensorEventListener.waitForFlushComplete();
- }
-
- /**
- * Call {@link SensorManager#flush(SensorEventListener)} and wait for
- * {@link SensorEventListener2#onFlushCompleted(Sensor)} to be called. This method will perform
- * a no-op if the sensor is not registered.
- *
- * @throws AssertionError if {@link SensorManager#flush(SensorEventListener)} returns false or
- * if there is a time out
- * @throws InterruptedException if the thread was interrupted
- */
- public void flush() throws InterruptedException {
- if (mTestSensorEventListener == null) {
- return;
- }
- startFlush();
- waitForFlushCompleted();
- }
-
- /**
- * Register a listener, wait for a specific number of events, and then unregister the listener.
- */
- public void runSensor(TestSensorEventListener listener, int eventCount)
- throws InterruptedException {
- if (mTestSensorEventListener != null) {
- Log.w(LOG_TAG, "Listener already registered, returning.");
- return;
- }
- try {
- registerListener(listener);
- waitForEvents(eventCount);
- } finally {
- unregisterListener();
- }
- }
-
- /**
- * Register a listener, wait for a specific duration, and then unregister the listener.
- */
- public void runSensor(TestSensorEventListener listener, long duration, TimeUnit timeUnit)
- throws InterruptedException {
- if (mTestSensorEventListener != null) {
- Log.w(LOG_TAG, "Listener already registered, returning.");
- return;
- }
- try {
- registerListener(listener);
- waitForEvents(duration, timeUnit);
- } finally {
- unregisterListener();
- }
- }
-
- /**
- * Registers a listener, waits for a specific duration, calls flush, and waits for flush to
- * complete.
- */
- public void runSensorAndFlush(
- TestSensorEventListener listener,
- long duration,
- TimeUnit timeUnit) throws InterruptedException {
- if (mTestSensorEventListener != null) {
- Log.w(LOG_TAG, "Listener already registered, returning.");
- return;
- }
-
- try {
- registerListener(listener);
- SensorCtsHelper.sleep(duration, timeUnit);
- startFlush();
- listener.waitForFlushComplete();
- } finally {
- unregisterListener();
- }
- }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
index 299f470..7572dc7 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/ValidatingSensorEventListener.java
@@ -17,11 +17,11 @@
package android.hardware.cts.helpers;
import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener2;
import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.os.Handler;
+import java.util.ArrayList;
import java.util.Collection;
-import java.util.LinkedList;
/**
* A {@link TestSensorEventListener} which performs validations on the received events on the fly.
@@ -30,42 +30,17 @@
*/
public class ValidatingSensorEventListener extends TestSensorEventListener {
- private final Collection<ISensorVerification> mVerifications =
- new LinkedList<ISensorVerification>();
-
- /**
- * Construct a {@link ValidatingSensorEventListener} with an additional
- * {@link SensorEventListener2}.
- */
- public ValidatingSensorEventListener(SensorEventListener2 listener,
- ISensorVerification ... verifications) {
- super(listener);
- for (ISensorVerification verification : verifications) {
- mVerifications.add(verification);
- }
- }
-
- /**
- * Construct a {@link ValidatingSensorEventListener} with an additional
- * {@link SensorEventListener2}.
- */
- public ValidatingSensorEventListener(SensorEventListener2 listener,
- Collection<ISensorVerification> verifications) {
- this(listener, verifications.toArray(new ISensorVerification[0]));
- }
+ private final ArrayList<ISensorVerification> mVerifications =
+ new ArrayList<ISensorVerification>();
/**
* Construct a {@link ValidatingSensorEventListener}.
*/
- public ValidatingSensorEventListener(ISensorVerification ... verifications) {
- this(null, verifications);
- }
-
- /**
- * Construct a {@link ValidatingSensorEventListener}.
- */
- public ValidatingSensorEventListener(Collection<ISensorVerification> verifications) {
- this(null, verifications);
+ public ValidatingSensorEventListener(
+ Collection<ISensorVerification> verifications,
+ Handler handler) {
+ super(handler);
+ mVerifications.addAll(verifications);
}
/**
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
deleted file mode 100644
index d5aa4b9..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorFlushOperation.java
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
-
-import java.util.concurrent.TimeUnit;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type. When {{@link #execute()} is called, the sensor will collect the
- * events, call flush, and then run all the tests.
- * </p>
- */
-public class TestSensorFlushOperation extends VerifiableSensorOperation {
- private final Long mDuration;
- private final TimeUnit mTimeUnit;
-
- /**
- * Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- * @param duration the duration to gather events before calling {@code SensorManager.flush()}
- * @param timeUnit the time unit of the duration
- */
- public TestSensorFlushOperation(
- TestSensorEnvironment environment,
- long duration,
- TimeUnit timeUnit) {
- super(environment);
- mDuration = duration;
- mTimeUnit = timeUnit;
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
- mSensorManager.runSensorAndFlush(listener, mDuration, mTimeUnit);
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- protected VerifiableSensorOperation doClone() {
- return new TestSensorFlushOperation(mEnvironment, mDuration,mTimeUnit);
- }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
index 695e1a7..6e53dbb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/TestSensorOperation.java
@@ -16,9 +16,27 @@
package android.hardware.cts.helpers.sensoroperations;
+import junit.framework.Assert;
+
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.SensorStats;
import android.hardware.cts.helpers.TestSensorEnvironment;
import android.hardware.cts.helpers.TestSensorEventListener;
+import android.hardware.cts.helpers.TestSensorManager;
+import android.hardware.cts.helpers.ValidatingSensorEventListener;
+import android.hardware.cts.helpers.sensorverification.EventGapVerification;
+import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
+import android.hardware.cts.helpers.sensorverification.EventTimestampSynchronizationVerification;
+import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
+import android.hardware.cts.helpers.sensorverification.ISensorVerification;
+import android.hardware.cts.helpers.sensorverification.JitterVerification;
+import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
+import android.hardware.cts.helpers.sensorverification.MeanVerification;
+import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
+import android.os.Handler;
+import java.util.Collection;
+import java.util.HashSet;
import java.util.concurrent.TimeUnit;
/**
@@ -29,70 +47,204 @@
* events and then run all the tests.
* </p>
*/
-public class TestSensorOperation extends VerifiableSensorOperation {
- private final Integer mEventCount;
- private final Long mDuration;
- private final TimeUnit mTimeUnit;
+public class TestSensorOperation extends AbstractSensorOperation {
+ private final Collection<ISensorVerification> mVerifications =
+ new HashSet<ISensorVerification>();
+
+ private final TestSensorManager mSensorManager;
+ private final TestSensorEnvironment mEnvironment;
+ private final Executor mExecutor;
+ private final Handler mHandler;
+
+ private boolean mLogEvents;
/**
- * Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- * @param eventCount the number of events to gather
+ * An interface that defines an abstraction for operations to be performed by the
+ * {@link TestSensorOperation}.
*/
- public TestSensorOperation(TestSensorEnvironment environment, int eventCount) {
- this(environment, eventCount, null /* duration */, null /* timeUnit */);
+ public interface Executor {
+ void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException;
}
/**
* Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- * @param duration the duration to gather events for
- * @param timeUnit the time unit of the duration
+ */
+ public TestSensorOperation(TestSensorEnvironment environment, Executor executor) {
+ this(environment, executor, null /* handler */);
+ }
+
+ /**
+ * Create a {@link TestSensorOperation}.
*/
public TestSensorOperation(
TestSensorEnvironment environment,
- long duration,
- TimeUnit timeUnit) {
- this(environment, null /* eventCount */, duration, timeUnit);
+ Executor executor,
+ Handler handler) {
+ mEnvironment = environment;
+ mExecutor = executor;
+ mHandler = handler;
+ mSensorManager = new TestSensorManager(mEnvironment);
}
/**
- * Private helper constructor.
+ * Set whether to log events.
*/
- private TestSensorOperation(
+ public void setLogEvents(boolean logEvents) {
+ mLogEvents = logEvents;
+ }
+
+ /**
+ * Set all of the default test expectations.
+ */
+ public void addDefaultVerifications() {
+ addVerification(EventGapVerification.getDefault(mEnvironment));
+ addVerification(EventOrderingVerification.getDefault(mEnvironment));
+ addVerification(FrequencyVerification.getDefault(mEnvironment));
+ addVerification(JitterVerification.getDefault(mEnvironment));
+ addVerification(MagnitudeVerification.getDefault(mEnvironment));
+ addVerification(MeanVerification.getDefault(mEnvironment));
+ addVerification(StandardDeviationVerification.getDefault(mEnvironment));
+ addVerification(EventTimestampSynchronizationVerification.getDefault(mEnvironment));
+ }
+
+ public void addVerification(ISensorVerification verification) {
+ if (verification != null) {
+ mVerifications.add(verification);
+ }
+ }
+
+ /**
+ * Collect the specified number of events from the sensor and run all enabled verifications.
+ */
+ @Override
+ public void execute() throws InterruptedException {
+ getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
+
+ ValidatingSensorEventListener listener =
+ new ValidatingSensorEventListener(mVerifications, mHandler);
+ listener.setLogEvents(mLogEvents);
+
+ mExecutor.execute(mSensorManager, listener);
+
+ boolean failed = false;
+ StringBuilder sb = new StringBuilder();
+ for (ISensorVerification verification : mVerifications) {
+ failed |= evaluateResults(verification, sb);
+ }
+
+ if (failed) {
+ String msg = SensorCtsHelper
+ .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
+ getStats().addValue(SensorStats.ERROR, msg);
+ Assert.fail(msg);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public TestSensorOperation clone() {
+ TestSensorOperation operation = new TestSensorOperation(mEnvironment, mExecutor);
+ for (ISensorVerification verification : mVerifications) {
+ operation.addVerification(verification.clone());
+ }
+ return operation;
+ }
+
+ /**
+ * Evaluate the results of a test, aggregate the stats, and build the error message.
+ */
+ private boolean evaluateResults(ISensorVerification verification, StringBuilder sb) {
+ try {
+ verification.verify(mEnvironment, getStats());
+ } catch (AssertionError e) {
+ if (sb.length() > 0) {
+ sb.append(", ");
+ }
+ sb.append(e.getMessage());
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Creates an operation that will wait for a given amount of events to arrive.
+ *
+ * @param environment The test environment.
+ * @param eventCount The number of events to wait for.
+ */
+ public static TestSensorOperation createOperation(
TestSensorEnvironment environment,
- Integer eventCount,
- Long duration,
- TimeUnit timeUnit) {
- super(environment);
- mEventCount = eventCount;
- mDuration = duration;
- mTimeUnit = timeUnit;
+ final int eventCount) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ sensorManager.registerListener(listener);
+ listener.waitForEvents(eventCount);
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
}
/**
- * {@inheritDoc}
+ * Creates an operation that will wait for a given amount of time to collect events.
+ *
+ * @param environment The test environment.
+ * @param duration The duration to wait for events.
+ * @param timeUnit The time unit for {@code duration}.
*/
- @Override
- protected void doExecute(TestSensorEventListener listener) throws InterruptedException {
- if (mEventCount != null) {
- mSensorManager.runSensor(listener, mEventCount);
- } else {
- mSensorManager.runSensor(listener, mDuration, mTimeUnit);
- }
+ public static TestSensorOperation createOperation(
+ TestSensorEnvironment environment,
+ final long duration,
+ final TimeUnit timeUnit) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ sensorManager.registerListener(listener);
+ listener.waitForEvents(duration, timeUnit);
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
}
/**
- * {@inheritDoc}
+ * Creates an operation that will wait for a given amount of time before calling
+ * {@link TestSensorManager#requestFlush()}.
+ *
+ * @param environment The test environment.
+ * @param duration The duration to wait before calling {@link TestSensorManager#requestFlush()}.
+ * @param timeUnit The time unit for {@code duration}.
*/
- @Override
- protected VerifiableSensorOperation doClone() {
- if (mEventCount != null) {
- return new TestSensorOperation(mEnvironment, mEventCount);
- } else {
- return new TestSensorOperation(mEnvironment, mDuration, mTimeUnit);
- }
+ public static TestSensorOperation createFlushOperation(
+ TestSensorEnvironment environment,
+ final long duration,
+ final TimeUnit timeUnit) {
+ Executor executor = new Executor() {
+ @Override
+ public void execute(TestSensorManager sensorManager, TestSensorEventListener listener)
+ throws InterruptedException {
+ try {
+ sensorManager.registerListener(listener);
+ SensorCtsHelper.sleep(duration, timeUnit);
+ sensorManager.requestFlush();
+ listener.waitForFlushComplete();
+ } finally {
+ sensorManager.unregisterListener();
+ }
+ }
+ };
+ return new TestSensorOperation(environment, executor);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
deleted file mode 100644
index 57018eb..0000000
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensoroperations/VerifiableSensorOperation.java
+++ /dev/null
@@ -1,156 +0,0 @@
-/*
- * Copyright (C) 2014 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.hardware.cts.helpers.sensoroperations;
-
-import junit.framework.Assert;
-
-import android.hardware.cts.helpers.SensorCtsHelper;
-import android.hardware.cts.helpers.SensorStats;
-import android.hardware.cts.helpers.TestSensorEnvironment;
-import android.hardware.cts.helpers.TestSensorEventListener;
-import android.hardware.cts.helpers.TestSensorManager;
-import android.hardware.cts.helpers.ValidatingSensorEventListener;
-import android.hardware.cts.helpers.sensorverification.EventGapVerification;
-import android.hardware.cts.helpers.sensorverification.EventOrderingVerification;
-import android.hardware.cts.helpers.sensorverification.FrequencyVerification;
-import android.hardware.cts.helpers.sensorverification.ISensorVerification;
-import android.hardware.cts.helpers.sensorverification.JitterVerification;
-import android.hardware.cts.helpers.sensorverification.MagnitudeVerification;
-import android.hardware.cts.helpers.sensorverification.MeanVerification;
-import android.hardware.cts.helpers.sensorverification.StandardDeviationVerification;
-
-import java.util.Collection;
-import java.util.HashSet;
-
-/**
- * A {@link ISensorOperation} used to verify that sensor events and sensor values are correct.
- * <p>
- * Provides methods to set test expectations as well as providing a set of default expectations
- * depending on sensor type. When {{@link #execute()} is called, the sensor will collect the
- * events and then run all the tests.
- * </p>
- */
-public abstract class VerifiableSensorOperation extends AbstractSensorOperation {
- protected final TestSensorManager mSensorManager;
- protected final TestSensorEnvironment mEnvironment;
-
- private final Collection<ISensorVerification> mVerifications =
- new HashSet<ISensorVerification>();
-
- private boolean mLogEvents = false;
-
- /**
- * Create a {@link TestSensorOperation}.
- *
- * @param environment the test environment
- */
- public VerifiableSensorOperation(TestSensorEnvironment environment) {
- mEnvironment = environment;
- mSensorManager = new TestSensorManager(mEnvironment);
- }
-
- /**
- * Set whether to log events.
- */
- public void setLogEvents(boolean logEvents) {
- mLogEvents = logEvents;
- }
-
- /**
- * Set all of the default test expectations.
- */
- public void addDefaultVerifications() {
- addVerification(EventGapVerification.getDefault(mEnvironment));
- addVerification(EventOrderingVerification.getDefault(mEnvironment));
- addVerification(FrequencyVerification.getDefault(mEnvironment));
- addVerification(JitterVerification.getDefault(mEnvironment));
- addVerification(MagnitudeVerification.getDefault(mEnvironment));
- addVerification(MeanVerification.getDefault(mEnvironment));
- // Skip SigNumVerification since it has no default
- addVerification(StandardDeviationVerification.getDefault(mEnvironment));
- }
-
- public void addVerification(ISensorVerification verification) {
- if (verification != null) {
- mVerifications.add(verification);
- }
- }
-
- /**
- * Collect the specified number of events from the sensor and run all enabled verifications.
- */
- @Override
- public void execute() throws InterruptedException {
- getStats().addValue("sensor_name", mEnvironment.getSensor().getName());
-
- ValidatingSensorEventListener listener = new ValidatingSensorEventListener(mVerifications);
- listener.setLogEvents(mLogEvents);
-
- doExecute(listener);
-
- boolean failed = false;
- StringBuilder sb = new StringBuilder();
- for (ISensorVerification verification : mVerifications) {
- failed |= evaluateResults(verification, sb);
- }
-
- if (failed) {
- String msg = SensorCtsHelper
- .formatAssertionMessage("VerifySensorOperation", mEnvironment, sb.toString());
- getStats().addValue(SensorStats.ERROR, msg);
- Assert.fail(msg);
- }
- }
-
- /**
- * {@inheritDoc}
- */
- @Override
- public VerifiableSensorOperation clone() {
- VerifiableSensorOperation operation = doClone();
- for (ISensorVerification verification : mVerifications) {
- operation.addVerification(verification.clone());
- }
- return operation;
- }
-
- /**
- * Execute operations in a {@link TestSensorManager}.
- */
- protected abstract void doExecute(TestSensorEventListener listener) throws InterruptedException;
-
- /**
- * Clone the subclass operation.
- */
- protected abstract VerifiableSensorOperation doClone();
-
- /**
- * Evaluate the results of a test, aggregate the stats, and build the error message.
- */
- private boolean evaluateResults(ISensorVerification verification, StringBuilder sb) {
- try {
- verification.verify(mEnvironment, getStats());
- } catch (AssertionError e) {
- if (sb.length() > 0) {
- sb.append(", ");
- }
- sb.append(e.getMessage());
- return true;
- }
- return false;
- }
-}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
index 911ae3a..acf71bb 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/AbstractSensorVerification.java
@@ -18,6 +18,8 @@
import android.hardware.cts.helpers.TestSensorEvent;
+import java.util.List;
+
/**
* Abstract class that deals with the synchronization of the sensor verifications.
*/
@@ -52,18 +54,38 @@
*/
protected abstract void addSensorEventInternal(TestSensorEvent event);
+ protected <TEvent extends IndexedEvent> int[] getIndexArray(List<TEvent> indexedEvents) {
+ int eventsCount = indexedEvents.size();
+ int[] indices = new int[eventsCount];
+ for (int i = 0; i < eventsCount; i++) {
+ indices[i] = indexedEvents.get(i).index;
+ }
+ return indices;
+ }
+
+ /**
+ * Helper class to store the index and current event.
+ * Events are added to the verification in the order they are generated, the index represents
+ * the position of the given event, in the list of added events.
+ */
+ protected class IndexedEvent {
+ public final int index;
+ public final TestSensorEvent event;
+
+ public IndexedEvent(int index, TestSensorEvent event) {
+ this.index = index;
+ this.event = event;
+ }
+ }
+
/**
* Helper class to store the index, previous event, and current event.
*/
- protected class IndexedEventPair {
- public final int index;
- public final TestSensorEvent event;
+ protected class IndexedEventPair extends IndexedEvent {
public final TestSensorEvent previousEvent;
- public IndexedEventPair(int index, TestSensorEvent event,
- TestSensorEvent previousEvent) {
- this.index = index;
- this.event = event;
+ public IndexedEventPair(int index, TestSensorEvent event, TestSensorEvent previousEvent) {
+ super(index, event);
this.previousEvent = previousEvent;
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
index 156afa8..b692f0f 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventGapVerification.java
@@ -63,19 +63,14 @@
public void verify(TestSensorEnvironment environment, SensorStats stats) {
if (environment.isSensorSamplingRateOverloaded()) {
// the verification is not reliable on environments under load
- stats.addValue(PASSED_KEY, true);
+ stats.addValue(PASSED_KEY, "skipped (under load)");
return;
}
final int count = mEventGaps.size();
stats.addValue(PASSED_KEY, count == 0);
stats.addValue(SensorStats.EVENT_GAP_COUNT_KEY, count);
-
- final int[] indices = new int[count];
- for (int i = 0; i < indices.length; i++) {
- indices[i] = mEventGaps.get(i).index;
- }
- stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, indices);
+ stats.addValue(SensorStats.EVENT_GAP_POSITIONS_KEY, getIndexArray(mEventGaps));
if (count > 0) {
StringBuilder sb = new StringBuilder();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
index 6598725..7b77ead 100644
--- a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventOrderingVerification.java
@@ -75,12 +75,9 @@
final int count = mOutOfOrderEvents.size();
stats.addValue(PASSED_KEY, count == 0);
stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_COUNT_KEY, count);
-
- final int[] indices = new int[count];
- for (int i = 0; i < indices.length; i++) {
- indices[i] = mOutOfOrderEvents.get(i).index;
- }
- stats.addValue(SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY, indices);
+ stats.addValue(
+ SensorStats.EVENT_OUT_OF_ORDER_POSITIONS_KEY,
+ getIndexArray(mOutOfOrderEvents));
if (count > 0) {
StringBuilder sb = new StringBuilder();
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
new file mode 100644
index 0000000..d4a1f38
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/sensorverification/EventTimestampSynchronizationVerification.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package android.hardware.cts.helpers.sensorverification;
+
+import junit.framework.Assert;
+
+import android.hardware.SensorEvent;
+import android.hardware.cts.helpers.SensorStats;
+import android.hardware.cts.helpers.TestSensorEnvironment;
+import android.hardware.cts.helpers.TestSensorEvent;
+import android.os.SystemClock;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * A {@link ISensorVerification} which verifies that the timestamp of the {@link SensorEvent} is
+ * synchronized with {@link SystemClock#elapsedRealtimeNanos()}, based on a given threshold.
+ */
+public class EventTimestampSynchronizationVerification extends AbstractSensorVerification {
+ public static final String PASSED_KEY = "timestamp_synchronization_passed";
+
+ // number of indices to print in assertion message before truncating
+ private static final int TRUNCATE_MESSAGE_LENGTH = 3;
+
+ private static final long DEFAULT_THRESHOLD_NS = TimeUnit.MILLISECONDS.toNanos(500);
+
+ private final ArrayList<TestSensorEvent> mCollectedEvents = new ArrayList<TestSensorEvent>();
+
+ private final long mMaximumSynchronizationErrorNs;
+ private final long mReportLatencyNs;
+
+ /**
+ * Constructs an instance of {@link EventTimestampSynchronizationVerification}.
+ *
+ * @param maximumSynchronizationErrorNs The valid threshold for timestamp synchronization.
+ * @param reportLatencyNs The latency on which batching events are received
+ */
+ public EventTimestampSynchronizationVerification(
+ long maximumSynchronizationErrorNs,
+ long reportLatencyNs) {
+ mMaximumSynchronizationErrorNs = maximumSynchronizationErrorNs;
+ mReportLatencyNs = reportLatencyNs;
+ }
+
+ /**
+ * Gets a default {@link EventTimestampSynchronizationVerification}.
+ *
+ * @param environment The test environment
+ * @return The verification or null if the verification is not supported in the given
+ * environment.
+ */
+ public static EventTimestampSynchronizationVerification getDefault(
+ TestSensorEnvironment environment) {
+ int reportLatencyUs = environment.getMaxReportLatencyUs();
+ int fifoMaxEventCount = environment.getSensor().getFifoMaxEventCount();
+ if (fifoMaxEventCount > 0) {
+ int fifoBasedReportLatencyUs =
+ fifoMaxEventCount * environment.getMaximumExpectedSamplingPeriodUs();
+ reportLatencyUs = Math.min(reportLatencyUs, fifoBasedReportLatencyUs);
+
+ }
+ long reportLatencyNs = TimeUnit.MICROSECONDS.toNanos(reportLatencyUs);
+ return new EventTimestampSynchronizationVerification(DEFAULT_THRESHOLD_NS, reportLatencyNs);
+ }
+
+ @Override
+ public void verify(TestSensorEnvironment environment, SensorStats stats) {
+ StringBuilder errorMessageBuilder =
+ new StringBuilder(" event timestamp synchronization failures: ");
+ List<IndexedEvent> failures = verifyTimestampSynchronization(errorMessageBuilder);
+
+ int failuresCount = failures.size();
+ stats.addValue(SensorStats.EVENT_TIME_SYNCHRONIZATION_COUNT_KEY, failuresCount);
+ stats.addValue(
+ SensorStats.EVENT_TIME_SYNCHRONIZATION_POSITIONS_KEY,
+ getIndexArray(failures));
+
+ boolean success = failures.isEmpty();
+ stats.addValue(PASSED_KEY, success);
+ errorMessageBuilder.insert(0, failuresCount);
+ Assert.assertTrue(errorMessageBuilder.toString(), success);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ public EventTimestampSynchronizationVerification clone() {
+ return new EventTimestampSynchronizationVerification(
+ mMaximumSynchronizationErrorNs,
+ mReportLatencyNs);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ @Override
+ protected void addSensorEventInternal(TestSensorEvent event) {
+ mCollectedEvents.add(event);
+ }
+
+ /**
+ * Verifies timestamp synchronization for all sensor events.
+ * The verification accounts for a lower and upper threshold, such thresholds are adjusted for
+ * batching cases.
+ *
+ * @param builder A string builder to store error messaged found in the collected sensor events.
+ * @return A list of events tha failed the verification.
+ */
+ private List<IndexedEvent> verifyTimestampSynchronization(StringBuilder builder) {
+ int collectedEventsCount = mCollectedEvents.size();
+ ArrayList<IndexedEvent> failures = new ArrayList<IndexedEvent>();
+
+ for (int i = 0; i < collectedEventsCount; ++i) {
+ TestSensorEvent event = mCollectedEvents.get(i);
+ long eventTimestampNs = event.timestamp;
+ long receivedTimestampNs = event.receivedTimestamp;
+ long upperThresholdNs = receivedTimestampNs;
+ long lowerThresholdNs = receivedTimestampNs - mMaximumSynchronizationErrorNs
+ - mReportLatencyNs;
+
+ if (eventTimestampNs < lowerThresholdNs || eventTimestampNs > upperThresholdNs) {
+ if (failures.size() < TRUNCATE_MESSAGE_LENGTH) {
+ builder.append("position=").append(i);
+ builder.append(", timestamp=").append(eventTimestampNs).append("ns");
+ builder.append(", expected=[").append(lowerThresholdNs);
+ builder.append(", ").append(upperThresholdNs).append("]ns; ");
+ }
+ failures.add(new IndexedEvent(i, event));
+ }
+ }
+ if (failures.size() >= TRUNCATE_MESSAGE_LENGTH) {
+ builder.append("more; ");
+ }
+ return failures;
+ }
+}
diff --git a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
index 3765809..b298b97 100644
--- a/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
+++ b/tests/tests/location2/src/android/location2/cts/LocationManagerTest.java
@@ -26,6 +26,7 @@
import android.location.Criteria;
import android.location.GpsStatus;
import android.location.GpsStatus.Listener;
+import android.location.GpsStatus.NmeaListener;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
@@ -227,18 +228,32 @@
@UiThreadTest
public void testGpsStatusListener() {
try {
- // .addGpsStatusListener returns true if the listener added successfully
- if (mManager.addGpsStatusListener(new MockGpsStatusListener())) {
- fail("Should have failed to add a gps status listener");
- }
+ mManager.addGpsStatusListener(new MockGpsStatusListener());
+ fail("Should have failed to add a gps status listener");
} catch (SecurityException e) {
// expected
}
try {
- if (mManager.addGpsStatusListener(null)) {
- fail("Should have failed to add null as a gps status listener");
- }
+ mManager.addGpsStatusListener(null);
+ fail("Should have failed to add null as a gps status listener");
+ } catch (SecurityException e) {
+ // expected
+ }
+ }
+
+ @UiThreadTest
+ public void testGpsStatusNmeaListener() {
+ try {
+ mManager.addNmeaListener(new MockGpsStatusNmeaListener());
+ fail("Should have failed to add a gps status nmea listener");
+ } catch (SecurityException e) {
+ // expected
+ }
+
+ try {
+ mManager.addNmeaListener(null);
+ fail("Should have failed to add null as a gps status nmea listener");
} catch (SecurityException e) {
// expected
}
@@ -478,4 +493,20 @@
mHasCallOnGpsStatusChanged = true;
}
}
+
+ private static class MockGpsStatusNmeaListener implements NmeaListener {
+ private boolean mHasCallOnNmeaReceived;
+
+ public boolean hasCallOnNmeaReceived() {
+ return mHasCallOnNmeaReceived;
+ }
+
+ public void reset(){
+ mHasCallOnNmeaReceived = false;
+ }
+
+ public void onNmeaReceived(long timestamp, String nmea) {
+ mHasCallOnNmeaReceived = true;
+ }
+ }
}
diff --git a/tests/tests/media/res/raw/sine1khzm40db.wav b/tests/tests/media/res/raw/sine1khzm40db.wav
new file mode 100644
index 0000000..ba541c4
--- /dev/null
+++ b/tests/tests/media/res/raw/sine1khzm40db.wav
Binary files differ
diff --git a/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
new file mode 100644
index 0000000..9515c22
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/LoudnessEnhancerTest.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.media.cts;
+
+import com.android.cts.media.R;
+
+import android.content.Context;
+import android.media.audiofx.AudioEffect;
+import android.media.AudioFormat;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.media.audiofx.LoudnessEnhancer;
+import android.os.Looper;
+import android.test.AndroidTestCase;
+import android.util.Log;
+
+public class LoudnessEnhancerTest extends PostProcTestBase {
+
+ private String TAG = "LoudnessEnhancerTest";
+ private LoudnessEnhancer mLE;
+
+ //-----------------------------------------------------------------
+ // LOUDNESS ENHANCER TESTS:
+ //----------------------------------
+
+ //-----------------------------------------------------------------
+ // 0 - constructor
+ //----------------------------------
+
+ //Test case 0.0: test constructor and release
+ public void test0_0ConstructorAndRelease() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("null AudioManager", am);
+ getLoudnessEnhancer(0);
+ releaseLoudnessEnhancer();
+
+ int session = am.generateAudioSessionId();
+ assertTrue("cannot generate new session", session != AudioManager.ERROR);
+ getLoudnessEnhancer(session);
+ releaseLoudnessEnhancer();
+ }
+
+ //-----------------------------------------------------------------
+ // 1 - get/set parameters
+ //----------------------------------
+
+ //Test case 1.0: test set/get target gain
+ public void test1_0TargetGain() throws Exception {
+ if (!hasAudioOutput()) {
+ return;
+ }
+ getLoudnessEnhancer(0);
+ try {
+ mLE.setTargetGain(0);
+ assertEquals("target gain differs from value set", 0, mLE.getTargetGain());
+ mLE.setTargetGain(800);
+ assertEquals("target gain differs from value set", 800, mLE.getTargetGain());
+ } catch (IllegalArgumentException e) {
+ fail("target gain illegal argument");
+ } catch (UnsupportedOperationException e) {
+ fail("target gain unsupported operation");
+ } catch (IllegalStateException e) {
+ fail("target gain operation called in wrong state");
+ } finally {
+ releaseLoudnessEnhancer();
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // private methods
+ //----------------------------------
+ private void getLoudnessEnhancer(int session) {
+ releaseLoudnessEnhancer();
+ try {
+ mLE = new LoudnessEnhancer(session);
+ } catch (IllegalArgumentException e) {
+ Log.e(TAG, "getLoudnessEnhancer() LoudnessEnhancer not found exception: ", e);
+ } catch (UnsupportedOperationException e) {
+ Log.e(TAG, "getLoudnessEnhancer() Effect library not loaded exception: ", e);
+ }
+ assertNotNull("could not create LoudnessEnhancer", mLE);
+ }
+
+ private void releaseLoudnessEnhancer() {
+ if (mLE != null) {
+ mLE.release();
+ mLE = null;
+ }
+ }
+}
\ No newline at end of file
diff --git a/tests/tests/media/src/android/media/cts/VisualizerTest.java b/tests/tests/media/src/android/media/cts/VisualizerTest.java
index 8c91e9b..871f602 100644
--- a/tests/tests/media/src/android/media/cts/VisualizerTest.java
+++ b/tests/tests/media/src/android/media/cts/VisualizerTest.java
@@ -16,10 +16,15 @@
package android.media.cts;
+import com.android.cts.media.R;
+
+import android.content.Context;
import android.media.audiofx.AudioEffect;
import android.media.AudioFormat;
import android.media.AudioManager;
+import android.media.MediaPlayer;
import android.media.audiofx.Visualizer;
+import android.media.audiofx.Visualizer.MeasurementPeakRms;
import android.os.Looper;
import android.test.AndroidTestCase;
import android.util.Log;
@@ -126,7 +131,7 @@
// 2 - check capture
//----------------------------------
- //Test case 2.0: test cature in polling mode
+ //Test case 2.0: test capture in polling mode
public void test2_0PollingCapture() throws Exception {
if (!hasAudioOutput()) {
return;
@@ -217,6 +222,113 @@
}
//-----------------------------------------------------------------
+ // 3 - check measurement mode MEASUREMENT_MODE_NONE
+ //----------------------------------
+
+ //Test case 3.0: test setting NONE measurement mode
+ public void test3_0MeasurementModeNone() throws Exception {
+ try {
+ getVisualizer(0);
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_NONE);
+ assertEquals("setMeasurementMode for NONE doesn't report success",
+ Visualizer.SUCCESS, status);
+
+ int mode = mVisualizer.getMeasurementMode();
+ assertEquals("getMeasurementMode reports NONE",
+ Visualizer.MEASUREMENT_MODE_NONE, mode);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ releaseVisualizer();
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // 4 - check measurement mode MEASUREMENT_MODE_PEAK_RMS
+ //----------------------------------
+
+ //Test case 4.0: test setting peak / RMS measurement mode
+ public void test4_0MeasurementModePeakRms() throws Exception {
+ try {
+ getVisualizer(0);
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ assertEquals("setMeasurementMode for PEAK_RMS doesn't report success",
+ Visualizer.SUCCESS, status);
+
+ int mode = mVisualizer.getMeasurementMode();
+ assertEquals("getMeasurementMode doesn't report PEAK_RMS",
+ Visualizer.MEASUREMENT_MODE_PEAK_RMS, mode);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ releaseVisualizer();
+ }
+ }
+
+ //Test case 4.1: test measurement of peak / RMS
+ public void test4_1MeasurePeakRms() throws Exception {
+ try {
+ // this test will play a 1kHz sine wave with peaks at -40dB
+ MediaPlayer mp = MediaPlayer.create(getContext(), R.raw.sine1khzm40db);
+ final int EXPECTED_PEAK_MB = -4015;
+ final int EXPECTED_RMS_MB = -4300;
+ final int MAX_MEASUREMENT_ERROR_MB = 1500;
+ assertNotNull("null MediaPlayer", mp);
+
+ AudioManager am = (AudioManager) getContext().getSystemService(Context.AUDIO_SERVICE);
+ assertNotNull("null AudioManager", am);
+ int originalVolume = am.getStreamVolume(AudioManager.STREAM_MUSIC);
+ am.setStreamVolume(AudioManager.STREAM_MUSIC,
+ am.getStreamMaxVolume(AudioManager.STREAM_MUSIC), 0);
+ getVisualizer(mp.getAudioSessionId());
+ mp.setLooping(true);
+ mp.start();
+
+ mVisualizer.setEnabled(true);
+ assertTrue("visualizer not enabled", mVisualizer.getEnabled());
+ Thread.sleep(100);
+ int status = mVisualizer.setMeasurementMode(Visualizer.MEASUREMENT_MODE_PEAK_RMS);
+ // make sure we're playing long enough so the measurement is valid
+ Thread.sleep(500);
+ assertEquals("setMeasurementMode() for PEAK_RMS doesn't report success",
+ Visualizer.SUCCESS, status);
+ MeasurementPeakRms measurement = new MeasurementPeakRms();
+ status = mVisualizer.getMeasurementPeakRms(measurement);
+ assertEquals("getMeasurementPeakRms() reports failure",
+ Visualizer.SUCCESS, status);
+ Log.i("VisTest", "peak="+measurement.mPeak+" rms="+measurement.mRms);
+ int deltaPeak = Math.abs(measurement.mPeak - EXPECTED_PEAK_MB);
+ int deltaRms = Math.abs(measurement.mRms - EXPECTED_RMS_MB);
+ assertTrue("peak deviation in mB=" + deltaPeak, deltaPeak < MAX_MEASUREMENT_ERROR_MB);
+ assertTrue("RMS deviation in mB=" + deltaRms, deltaRms < MAX_MEASUREMENT_ERROR_MB);
+ mp.stop();
+ mp.release();
+ am.setStreamVolume(AudioManager.STREAM_MUSIC, originalVolume, 0);
+
+ } catch (IllegalStateException e) {
+ fail("method called in wrong state");
+ } catch (InterruptedException e) {
+ fail("sleep() interrupted");
+ } finally {
+ releaseVisualizer();
+ }
+ }
+
+ //-----------------------------------------------------------------
// private methods
//----------------------------------
diff --git a/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
new file mode 100644
index 0000000..0c648c5
--- /dev/null
+++ b/tests/tests/permission/src/android/permission/cts/TvPermissionTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.permission.cts;
+
+import android.content.ContentValues;
+import android.content.pm.PackageManager;
+import android.media.tv.TvContract;
+import android.net.Uri;
+import android.test.AndroidTestCase;
+
+/**
+ * Tests for TV API related permissions.
+ */
+public class TvPermissionTest extends AndroidTestCase {
+ private static final String DUMMY_INPUT_ID = "dummy";
+
+ private boolean mHasTvInputFramework;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mHasTvInputFramework = getContext().getPackageManager()
+ .hasSystemFeature(PackageManager.FEATURE_LIVE_TV);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ }
+
+ private void verifyQuery(Uri uri, final String[] projection,
+ String tableName) throws Exception {
+ try {
+ getContext().getContentResolver().query(uri, projection, null, null, null);
+ fail("Accessing " + tableName + " table should require READ_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void verifyInsert(Uri uri, String tableName) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().insert(uri, values);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void verifyUpdate(Uri uri, String tableName) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().update(uri, values, null, null);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void verifyDelete(Uri uri, String tableName) throws Exception {
+ try {
+ getContext().getContentResolver().delete(uri, null, null);
+ fail("Accessing " + tableName + " table should require WRITE_EPG_DATA permission.");
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void testQueryChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQuery(TvContract.Channels.CONTENT_URI, projection, "channels");
+ }
+
+ public void testInsertChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyInsert(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ public void testUpdateChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyUpdate(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ public void testDeleteChannels() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyDelete(TvContract.Channels.CONTENT_URI, "channels");
+ }
+
+ public void testQueryPrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ final String[] projection = { TvContract.Programs._ID };
+ verifyQuery(TvContract.Programs.CONTENT_URI, projection, "programs");
+ }
+
+ public void testInsertPrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyInsert(TvContract.Programs.CONTENT_URI, "programs");
+ }
+
+ public void testUpdatePrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyUpdate(TvContract.Programs.CONTENT_URI, "programs");
+ }
+
+ public void testDeletePrograms() throws Exception {
+ if (!mHasTvInputFramework) return;
+ verifyDelete(TvContract.Programs.CONTENT_URI, "programs");
+ }
+}
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
new file mode 100644
index 0000000..a2d22d9
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/TestVLoad.java
@@ -0,0 +1,371 @@
+ /*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+package android.renderscript.cts;
+
+import android.renderscript.*;
+
+public class TestVLoad extends RSBaseCompute {
+
+ private ScriptC_vload script;
+ private ScriptC_vload_relaxed scriptRelaxed;
+ Allocation walkAlloc;
+ Allocation inAlloc;
+ Allocation outAlloc;
+ private static java.util.Random random = new java.util.Random();
+
+ final int w = 253;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ random.setSeed(10);
+ script = new ScriptC_vload(mRS);
+ scriptRelaxed = new ScriptC_vload_relaxed(mRS);
+ }
+
+
+
+ protected void createWalk() {
+ int tmp[] = new int[w];
+ boolean b[] = new boolean[w];
+ int toCopy = w;
+ int i = 0;
+
+ while (toCopy > 0) {
+ int x = random.nextInt(w);
+
+ //android.util.Log.v("rs", "x " + x + ", y " + y + ", toCopy " + toCopy);
+ while ((x < w) && b[x]) {
+ x++;
+ if (x >= w) {
+ x = 0;
+ }
+ }
+
+ int maxsize = 1;
+ b[x] = true;
+ if ((x+1 < w) && !b[x+1]) {
+ maxsize ++;
+ b[x+1] = true;
+ if ((x+2 < w) && !b[x+2]) {
+ maxsize ++;
+ b[x+2] = true;
+ if ((x+3 < w) && !b[x+3]) {
+ maxsize ++;
+ b[x+3] = true;
+ }
+ }
+ }
+
+ toCopy -= maxsize;
+ tmp[i] = x | (maxsize << 16);
+ android.util.Log.v("rs", "x " + x + ", vec " + maxsize);
+ i++;
+ }
+
+ walkAlloc = Allocation.createSized(mRS, Element.I32(mRS), i);
+ walkAlloc.copy1DRangeFrom(0, i, tmp);
+ }
+
+ private void testSetup(Type t) {
+ createWalk();
+
+ inAlloc = Allocation.createTyped(mRS, t);
+ outAlloc = Allocation.createTyped(mRS, t);
+ script.set_gAllocIn(inAlloc);
+ script.set_gAllocOut(outAlloc);
+ scriptRelaxed.set_gAllocIn(inAlloc);
+ scriptRelaxed.set_gAllocOut(outAlloc);
+ }
+
+ private void verify(byte[] a1, byte[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(short[] a1, short[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(int[] a1, int[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(long[] a1, long[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(float[] a1, float[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private void verify(double[] a1, double[] a2, String s) {
+ outAlloc.copyTo(a2);
+ for (int i=0; i < w; i++) {
+ if (a1[i] != a2[i]) {
+ throw new RSRuntimeException(s + a1[i] + ", " + a2[i] + ", at " + i);
+ }
+ a2[i] = 0;
+ }
+ outAlloc.copyFrom(a2);
+ }
+
+ private byte[] randomByteArray(int len) {
+ byte t[] = new byte[len];
+ random.nextBytes(t);
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ private short[] randomShortArray(int len) {
+ short t[] = new short[len];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = (short)(random.nextInt() & 0xffff);
+ }
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ private int[] randomIntArray(int len) {
+ int t[] = new int[len];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = random.nextInt();
+ }
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ private long[] randomLongArray(int len) {
+ long t[] = new long[len];
+ for (int i = 0; i < t.length; i++) {
+ t[i] = random.nextLong();
+ }
+ inAlloc.copyFrom(t);
+ return t;
+ }
+
+ public void testVload_char() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ script.forEach_copy2d_char(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch char: ");
+ }
+
+ public void testVload_uchar() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ script.forEach_copy2d_uchar(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch uchar: ");
+ }
+
+ public void testVload_char_relaxed() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ scriptRelaxed.forEach_copy2d_char(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed char: ");
+ }
+
+ public void testVload_uchar_relaxed() {
+ testSetup(Type.createX(mRS, Element.I8(mRS), w));
+ byte tmp[] = randomByteArray(w);
+ byte tmp2[] = new byte[w];
+ scriptRelaxed.forEach_copy2d_uchar(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed uchar: ");
+ }
+
+ public void testVload_short() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ script.forEach_copy2d_short(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch short: ");
+ }
+
+ public void testVload_ushort() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ script.forEach_copy2d_ushort(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ushort: ");
+ }
+
+ public void testVload_short_relaxed() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ scriptRelaxed.forEach_copy2d_short(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed short: ");
+ }
+
+ public void testVload_ushort_relaxed() {
+ testSetup(Type.createX(mRS, Element.I16(mRS), w));
+ short tmp[] = randomShortArray(w);
+ short tmp2[] = new short[w];
+ scriptRelaxed.forEach_copy2d_ushort(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ushort: ");
+ }
+
+ public void testVload_int() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ script.forEach_copy2d_int(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch int: ");
+ }
+
+ public void testVload_uint() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ script.forEach_copy2d_uint(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch uint: ");
+ }
+
+ public void testVload_int_relaxed() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ scriptRelaxed.forEach_copy2d_int(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed int: ");
+ }
+
+ public void testVload_uint_relaxed() {
+ testSetup(Type.createX(mRS, Element.I32(mRS), w));
+ int tmp[] = randomIntArray(w);
+ int tmp2[] = new int[w];
+ scriptRelaxed.forEach_copy2d_uint(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch uint: ");
+ }
+
+ public void testVload_long() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ script.forEach_copy2d_long(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch long: ");
+ }
+
+ public void testVload_ulong() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ script.forEach_copy2d_ulong(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ulong: ");
+ }
+ public void testVload_long_relaxed() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ scriptRelaxed.forEach_copy2d_long(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed long: ");
+ }
+ public void testVload_ulong_relaxed() {
+ testSetup(Type.createX(mRS, Element.I64(mRS), w));
+ long tmp[] = randomLongArray(w);
+ long tmp2[] = new long[w];
+ scriptRelaxed.forEach_copy2d_ulong(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch ulong: ");
+ }
+
+ public void testVload_float() {
+ testSetup(Type.createX(mRS, Element.F32(mRS), w));
+ float tmp[] = new float[w];
+ float tmp2[] = new float[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextFloat();
+ }
+ inAlloc.copyFrom(tmp);
+ script.forEach_copy2d_float(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch float: ");
+ }
+
+ public void testVload_float_relaxed() {
+ testSetup(Type.createX(mRS, Element.F32(mRS), w));
+ float tmp[] = new float[w];
+ float tmp2[] = new float[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextFloat();
+ }
+ inAlloc.copyFrom(tmp);
+ scriptRelaxed.forEach_copy2d_float(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed float: ");
+ }
+
+ public void testVload_double() {
+ testSetup(Type.createX(mRS, Element.F64(mRS), w));
+ double tmp[] = new double[w];
+ double tmp2[] = new double[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextDouble();
+ }
+ inAlloc.copyFrom(tmp);
+ script.forEach_copy2d_double(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch double: ");
+ }
+
+ public void testVload_double_relaxed() {
+ testSetup(Type.createX(mRS, Element.F64(mRS), w));
+ double tmp[] = new double[w];
+ double tmp2[] = new double[w];
+ for (int i=0; i < w; i++) {
+ tmp[i] = random.nextDouble();
+ }
+ inAlloc.copyFrom(tmp);
+ scriptRelaxed.forEach_copy2d_double(walkAlloc);
+ verify(tmp, tmp2, "Data mismatch relaxed double: ");
+ }
+
+}
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
new file mode 100644
index 0000000..cdf5fd1
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload.rs
@@ -0,0 +1,60 @@
+ /*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty) \
+ void __attribute__((kernel)) copy2d_##ty(int xy_v) { \
+ int lx = xy_v & 0xff; \
+ int vecsize = (xy_v & 0xff0000) >> 16; \
+ switch(vecsize) { \
+ case 1: { \
+ ty i = rsGetElementAt_##ty(gAllocIn, lx); \
+ rsSetElementAt_##ty(gAllocOut, i, lx); \
+ } break; \
+ case 2: { \
+ ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##2(gAllocOut, i, lx); \
+ } break; \
+ case 3: { \
+ ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##3(gAllocOut, i, lx); \
+ } break; \
+ case 4: { \
+ ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##4(gAllocOut, i, lx); \
+ } break; \
+ } \
+ }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
new file mode 100644
index 0000000..61940ba
--- /dev/null
+++ b/tests/tests/renderscript/src/android/renderscript/cts/vload_relaxed.rs
@@ -0,0 +1,62 @@
+ /*
+ * 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.
+ */
+
+#pragma version(1)
+#pragma rs java_package_name(android.renderscript.cts)
+
+#pragma rs_fp_relaxed
+
+rs_allocation gAllocIn;
+rs_allocation gAllocOut;
+
+
+
+#define COPY_2D(ty) \
+ void __attribute__((kernel)) copy2d_##ty(int xy_v) { \
+ int lx = xy_v & 0xff; \
+ int vecsize = (xy_v & 0xff0000) >> 16; \
+ switch(vecsize) { \
+ case 1: { \
+ ty i = rsGetElementAt_##ty(gAllocIn, lx); \
+ rsSetElementAt_##ty(gAllocOut, i, lx); \
+ } break; \
+ case 2: { \
+ ty##2 i = rsAllocationVLoadX_##ty##2(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##2(gAllocOut, i, lx); \
+ } break; \
+ case 3: { \
+ ty##3 i = rsAllocationVLoadX_##ty##3(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##3(gAllocOut, i, lx); \
+ } break; \
+ case 4: { \
+ ty##4 i = rsAllocationVLoadX_##ty##4(gAllocIn, lx); \
+ rsAllocationVStoreX_##ty##4(gAllocOut, i, lx); \
+ } break; \
+ } \
+ }
+
+COPY_2D(char)
+COPY_2D(uchar)
+COPY_2D(short)
+COPY_2D(ushort)
+COPY_2D(int)
+COPY_2D(uint)
+COPY_2D(long)
+COPY_2D(ulong)
+
+COPY_2D(float)
+COPY_2D(double)
+
diff --git a/tests/tests/security/jni/Android.mk b/tests/tests/security/jni/Android.mk
index fa862c1..46d0868 100644
--- a/tests/tests/security/jni/Android.mk
+++ b/tests/tests/security/jni/Android.mk
@@ -31,7 +31,8 @@
android_security_cts_SeccompDeathTestService.cpp \
android_security_cts_SELinuxTest.cpp \
android_security_cts_MMapExecutableTest.cpp \
- android_security_cts_NetlinkSocket.cpp
+ android_security_cts_NetlinkSocket.cpp \
+ android_security_cts_AudioPolicyBinderTest.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
diff --git a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
index 0e91b4e..ca8e841 100644
--- a/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
+++ b/tests/tests/security/jni/CtsSecurityJniOnLoad.cpp
@@ -26,6 +26,7 @@
extern int register_android_security_cts_SeccompDeathTestService(JNIEnv*);
extern int register_android_security_cts_SELinuxTest(JNIEnv*);
extern int register_android_security_cts_MMapExecutableTest(JNIEnv* env);
+extern int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env);
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
JNIEnv *env = NULL;
@@ -70,5 +71,9 @@
return JNI_ERR;
}
+ if (register_android_security_cts_AudioPolicyBinderTest(env)) {
+ return JNI_ERR;
+ }
+
return JNI_VERSION_1_4;
}
diff --git a/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
new file mode 100644
index 0000000..7e24a27
--- /dev/null
+++ b/tests/tests/security/jni/android_security_cts_AudioPolicyBinderTest.cpp
@@ -0,0 +1,202 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "AudioPolicyBinderTest-JNI"
+
+#include <jni.h>
+#include <binder/IServiceManager.h>
+#include <media/IAudioPolicyService.h>
+#include <media/AudioSystem.h>
+#include <system/audio.h>
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+using namespace android;
+
+/*
+ * Native methods used by
+ * cts/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
+ */
+
+static bool init(sp<IAudioPolicyService>& aps, audio_io_handle_t *output, int *session)
+{
+ aps = 0;
+ if (output != NULL) {
+ *output = AUDIO_IO_HANDLE_NONE;
+ }
+ if (session != NULL) {
+ *session = AUDIO_UNIQUE_ID_ALLOCATE;
+ }
+
+ int64_t startTime = 0;
+ sp<IServiceManager> sm = defaultServiceManager();
+ while (aps == 0) {
+ sp<IBinder> binder = defaultServiceManager()->checkService(String16("media.audio_policy"));
+ if (binder == 0) {
+ if (startTime == 0) {
+ startTime = uptimeMillis();
+ } else if ((uptimeMillis()-startTime) > 10000) {
+ ALOGE("timeout while getting audio policy service");
+ return false;
+ }
+ sleep(1);
+ } else {
+ aps = interface_cast<IAudioPolicyService>(binder);
+ }
+ }
+
+ if (output != NULL) {
+ // get a valid output. Any use case will do.
+ for (int stream = AUDIO_STREAM_MIN; stream < AUDIO_STREAM_CNT; stream++) {
+ *output = AudioSystem::getOutput((audio_stream_type_t)stream);
+ if (*output != AUDIO_IO_HANDLE_NONE) {
+ break;
+ }
+ }
+ if (*output == AUDIO_IO_HANDLE_NONE) {
+ ALOGE("cannot get valid audio output");
+ return false;
+ }
+ }
+ if (session != NULL) {
+ //get a valid session
+ *session = AudioSystem::newAudioUniqueId();
+ if (*session == AUDIO_UNIQUE_ID_ALLOCATE) {
+ ALOGE("cannot get valid audio session");
+ return false;
+ }
+ }
+ return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::startOutput() cannot be called with an
+ * invalid stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_startOutput(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+ audio_io_handle_t output;
+ int session;
+
+ if (!init(aps, &output, &session)) {
+ return false;
+ }
+
+ status_t status = aps->startOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1), session);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ status = aps->startOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::stopOutput() cannot be called with an
+ * invalid stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_stopOutput(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+ audio_io_handle_t output;
+ int session;
+
+ if (!init(aps, &output, &session)) {
+ return false;
+ }
+
+ status_t status = aps->stopOutput(output, (audio_stream_type_t)(AUDIO_STREAM_MIN -1), session);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ status = aps->stopOutput(output, (audio_stream_type_t)AUDIO_STREAM_CNT, session);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::isStreamActive() cannot be called with an
+ * invalid stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_isStreamActive(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+
+ if (!init(aps, NULL, NULL)) {
+ return false;
+ }
+
+ status_t status = aps->isStreamActive((audio_stream_type_t)(AUDIO_STREAM_MIN -1), 0);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ status = aps->isStreamActive((audio_stream_type_t)AUDIO_STREAM_CNT, 0);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ return true;
+}
+
+/*
+ * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+ * invalid stream type.
+ * Test with NUM_RANDOM_TESTS random values for stream type.
+ */
+jboolean android_security_cts_AudioPolicy_test_isStreamActiveRemotely(JNIEnv* env __unused,
+ jobject thiz __unused)
+{
+ sp<IAudioPolicyService> aps;
+
+ if (!init(aps, NULL, NULL)) {
+ return false;
+ }
+
+ status_t status = aps->isStreamActiveRemotely((audio_stream_type_t)(AUDIO_STREAM_MIN -1), 0);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ status = aps->isStreamActiveRemotely((audio_stream_type_t)AUDIO_STREAM_CNT, 0);
+ if (status == NO_ERROR) {
+ return false;
+ }
+ return true;
+}
+
+static JNINativeMethod gMethods[] = {
+ { "native_test_startOutput", "()Z",
+ (void *) android_security_cts_AudioPolicy_test_startOutput },
+ { "native_test_stopOutput", "()Z",
+ (void *) android_security_cts_AudioPolicy_test_stopOutput },
+ { "native_test_isStreamActive", "()Z",
+ (void *) android_security_cts_AudioPolicy_test_isStreamActive },
+ { "native_test_isStreamActiveRemotely", "()Z",
+ (void *) android_security_cts_AudioPolicy_test_isStreamActiveRemotely },
+};
+
+int register_android_security_cts_AudioPolicyBinderTest(JNIEnv* env)
+{
+ jclass clazz = env->FindClass("android/security/cts/AudioPolicyBinderTest");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
new file mode 100644
index 0000000..b307247
--- /dev/null
+++ b/tests/tests/security/src/android/security/cts/AudioPolicyBinderTest.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.security.cts;
+
+import junit.framework.TestCase;
+
+public class AudioPolicyBinderTest extends TestCase {
+
+ static {
+ System.loadLibrary("ctssecurity_jni");
+ }
+
+ /**
+ * Checks that IAudioPolicyService::startOutput() cannot be called with an
+ * invalid stream type.
+ */
+ public void test_startOutput() throws Exception {
+ assertTrue(native_test_startOutput());
+ }
+
+ /**
+ * Checks that IAudioPolicyService::stopOutput() cannot be called with an
+ * invalid stream type.
+ */
+ public void test_stopOutput() throws Exception {
+ assertTrue(native_test_stopOutput());
+ }
+
+ /**
+ * Checks that IAudioPolicyService::isStreamActive() cannot be called with an
+ * invalid stream type.
+ */
+ public void test_isStreamActive() throws Exception {
+ assertTrue(native_test_isStreamActive());
+ }
+
+ /**
+ * Checks that IAudioPolicyService::isStreamActiveRemotely() cannot be called with an
+ * invalid stream type.
+ */
+ public void test_isStreamActiveRemotely() throws Exception {
+ assertTrue(native_test_isStreamActiveRemotely());
+ }
+
+ private static native boolean native_test_startOutput();
+ private static native boolean native_test_stopOutput();
+ private static native boolean native_test_isStreamActive();
+ private static native boolean native_test_isStreamActiveRemotely();
+}
diff --git a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
index 651a199..857e489 100644
--- a/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
+++ b/tests/tests/tv/src/android/media/tv/cts/TvContractTest.java
@@ -395,4 +395,111 @@
verifyOverlap(programEndMillis + hour, programEndMillis + hour * 2, 0,
channelId, channelUri);
}
+
+ private void verifyQueryWithSortOrder(Uri uri, final String[] projection,
+ String sortOrder) throws Exception {
+ try {
+ getContext().getContentResolver().query(uri, projection, null, null, sortOrder);
+ fail("Setting sortOrder should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ private void verifyQueryWithSelection(Uri uri, final String[] projection,
+ String selection) throws Exception {
+ try {
+ getContext().getContentResolver().query(uri, projection, selection, null, null);
+ fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ private void verifyUpdateWithSelection(Uri uri, String selection) throws Exception {
+ try {
+ ContentValues values = new ContentValues();
+ getContext().getContentResolver().update(uri, values, selection, null);
+ fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ private void verifyDeleteWithSelection(Uri uri, String selection) throws Exception {
+ try {
+ getContext().getContentResolver().delete(uri, selection, null);
+ fail("Setting selection should fail without ACCESS_ALL_EPG_DATA permission for " + uri);
+ } catch (SecurityException e) {
+ // Expected exception
+ }
+ }
+
+ public void testAllEpgPermissionBlocksSortOrderOnQuery_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQueryWithSortOrder(TvContract.Channels.CONTENT_URI, projection,
+ TvContract.Channels._ID + " ASC");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnQuery_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQueryWithSelection(TvContract.Channels.CONTENT_URI, projection,
+ TvContract.Channels._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnUpdate_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyUpdateWithSelection(TvContract.Channels.CONTENT_URI,
+ TvContract.Channels._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnDelete_Channels() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyDeleteWithSelection(TvContract.Channels.CONTENT_URI,
+ TvContract.Channels._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSortOrderOnQuery_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Programs._ID };
+ verifyQueryWithSortOrder(TvContract.Programs.CONTENT_URI, projection,
+ TvContract.Programs._ID + " ASC");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnQuery_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ final String[] projection = { TvContract.Channels._ID };
+ verifyQueryWithSelection(TvContract.Programs.CONTENT_URI, projection,
+ TvContract.Programs._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnUpdate_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyUpdateWithSelection(TvContract.Programs.CONTENT_URI,
+ TvContract.Programs._ID + ">0");
+ }
+
+ public void testAllEpgPermissionBlocksSelectionOnDelete_Programs() throws Exception {
+ if (!Utils.hasTvInputFramework(getContext())) {
+ return;
+ }
+ verifyDeleteWithSelection(TvContract.Programs.CONTENT_URI,
+ TvContract.Programs._ID + ">0");
+ }
}
diff --git a/tests/tests/view/res/anim-land/changing_reset_state_anim.xml b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
new file mode 100644
index 0000000..0b3279a
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_state_list_animator.xml b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
new file mode 100644
index 0000000..4c1c41e
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ </item>
+ <!-- base state-->
+ <item>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="200dp" android:valueType="floatType"/>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim-land/changing_test_animator.xml b/tests/tests/view/res/anim-land/changing_test_animator.xml
new file mode 100644
index 0000000..d5c83cd
--- /dev/null
+++ b/tests/tests/view/res/anim-land/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="x" android:duration="100"
+ android:interpolator="@android:anim/bounce_interpolator"
+ android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_reset_state_anim.xml b/tests/tests/view/res/anim/changing_reset_state_anim.xml
new file mode 100644
index 0000000..dd2b233
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="50dp" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_state_list_animator.xml b/tests/tests/view/res/anim/changing_state_list_animator.xml
new file mode 100644
index 0000000..6bcc0d9
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_state_list_animator.xml
@@ -0,0 +1,24 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ </item>
+ <!-- base state-->
+ <item>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="100dp" android:valueType="floatType"/>
+ </item>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/changing_test_animator.xml b/tests/tests/view/res/anim/changing_test_animator.xml
new file mode 100644
index 0000000..739a71b
--- /dev/null
+++ b/tests/tests/view/res/anim/changing_test_animator.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+<objectAnimator xmlns:android="http://schemas.android.com/apk/res/android"
+ android:propertyName="x" android:duration="100"
+ android:interpolator="@android:anim/accelerate_decelerate_interpolator"
+ android:valueTo="1" android:valueType="floatType"/>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/reset_state_anim.xml b/tests/tests/view/res/anim/reset_state_anim.xml
new file mode 100644
index 0000000..4bbbe62
--- /dev/null
+++ b/tests/tests/view/res/anim/reset_state_anim.xml
@@ -0,0 +1,20 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_animator.xml b/tests/tests/view/res/anim/test_animator.xml
new file mode 100644
index 0000000..94e9ec8
--- /dev/null
+++ b/tests/tests/view/res/anim/test_animator.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<set xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- if you change this, you should also change AnimatorInflaterTest#testLoadAnimator-->
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="@dimen/test_animator_target_x" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="@dimen/test_animator_target_y" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="left" android:duration="100" android:valueTo="2" android:valueType="intType"/>
+</set>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator.xml b/tests/tests/view/res/anim/test_state_list_animator.xml
new file mode 100644
index 0000000..b6a4822
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator.xml
@@ -0,0 +1,33 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <set>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="20" android:valueType="floatType"/>
+ </set>
+ </item>
+ <item android:state_enabled="true" android:state_pressed="false">
+ <set>
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="y" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ <objectAnimator android:propertyName="z" android:duration="100" android:valueTo="0" android:valueType="floatType"/>
+ </set>
+ </item>
+ <!-- base state-->
+ <item android:animation="@anim/reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/anim/test_state_list_animator_2.xml b/tests/tests/view/res/anim/test_state_list_animator_2.xml
new file mode 100644
index 0000000..6aeb41f
--- /dev/null
+++ b/tests/tests/view/res/anim/test_state_list_animator_2.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true">
+ <objectAnimator android:propertyName="x" android:duration="100" android:valueTo="10" android:valueType="floatType"/>
+ </item>
+ <!-- base state-->
+ <item android:animation="@anim/changing_reset_state_anim"/>
+</selector>
\ No newline at end of file
diff --git a/tests/tests/view/res/values-land/dimens.xml b/tests/tests/view/res/values-land/dimens.xml
new file mode 100644
index 0000000..17b5395
--- /dev/null
+++ b/tests/tests/view/res/values-land/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<resources>
+ <dimen name="test_animator_target_y">30dp</dimen>
+ <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced
+ in the XML on purpose-->
+ <dimen name="reset_state_value">100dp</dimen>
+ <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+ <dimen name="changing_state_list_anim_target_x_value">200dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/res/values/dimens.xml b/tests/tests/view/res/values/dimens.xml
new file mode 100644
index 0000000..16e5084
--- /dev/null
+++ b/tests/tests/view/res/values/dimens.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0"?>
+<!-- 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.
+-->
+<resources>
+ <dimen name="test_animator_target_x">10dp</dimen>
+ <dimen name="test_animator_target_y">20dp</dimen>
+ <!-- this value is equal to the value in changing_reset_state_anim. It is NOT referenced in the XML on purpose-->
+ <dimen name="reset_state_value">50dp</dimen>
+ <!-- this value is equal to the value in changing_state_list_animator. It is NOT referenced in the XML on purpose-->
+ <dimen name="changing_state_list_anim_target_x_value">100dp</dimen>
+</resources>
\ No newline at end of file
diff --git a/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
new file mode 100644
index 0000000..eeda5a3
--- /dev/null
+++ b/tests/tests/view/src/android/view/animation/cts/AnimatorInflaterTest.java
@@ -0,0 +1,271 @@
+/*
+* Copyright (C) 2014 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+package android.view.animation.cts;
+
+import android.animation.Animator;
+import android.animation.AnimatorInflater;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.animation.StateListAnimator;
+import android.app.Activity;
+import android.app.Instrumentation;
+import android.content.pm.ActivityInfo;
+import android.content.res.Configuration;
+import android.os.Debug;
+import android.test.ActivityInstrumentationTestCase2;
+import android.view.View;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import com.android.cts.view.R;
+
+public class AnimatorInflaterTest
+ extends ActivityInstrumentationTestCase2<AnimationTestCtsActivity> {
+
+ Set<Integer> identityHashes = new HashSet<Integer>();
+
+ public AnimatorInflaterTest() {
+ super("com.android.cts.view", AnimationTestCtsActivity.class);
+ }
+
+ private void assertUnique(Object object) {
+ assertUnique(object, "");
+ }
+
+ private void assertUnique(Object object, String msg) {
+ final int code = System.identityHashCode(object);
+ assertTrue("object should be unique " + msg + ", obj:" + object, identityHashes.add(code));
+
+ }
+
+ public void testLoadAnimatorWithDifferentInterpolators() throws Throwable {
+ Animator anim1 = AnimatorInflater
+ .loadAnimator(getActivity(), R.anim.changing_test_animator);
+ rotate();
+ Animator anim2 = AnimatorInflater
+ .loadAnimator(getActivity(), R.anim.changing_test_animator);
+ assertNotSame(anim1, anim2);
+ assertNotSame("interpolater is orientation dependent, should change",
+ anim1.getInterpolator(), anim2.getInterpolator());
+ }
+
+ /**
+ * Tests animators with dimension references.
+ */
+ public void testLoadAnimator() throws Throwable {
+ // to identify objects
+ Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ assertNotSame("a different animation should be returned", anim1, anim2);
+ assertSame("interpolator should be shallow cloned", anim1.getInterpolator(),
+ anim2.getInterpolator());
+ for (int i = 0; i < 2; i++) {
+ float targetX = getActivity().getResources()
+ .getDimension(R.dimen.test_animator_target_x);
+ // y value changes in landscape orientation
+ float targetY = getActivity().getResources()
+ .getDimension(R.dimen.test_animator_target_y);
+ for (Animator anim : new Animator[]{anim1, anim2}) {
+ assertTrue(anim instanceof AnimatorSet);
+ assertUnique(anim);
+ AnimatorSet set = (AnimatorSet) anim;
+ assertEquals("should have 3 sub animations", 3, set.getChildAnimations().size());
+ for (Animator subAnim : set.getChildAnimations()) {
+ assertUnique(subAnim);
+ assertTrue(subAnim instanceof ObjectAnimator);
+ }
+ final ObjectAnimator child1 = (ObjectAnimator) set.getChildAnimations().get(0);
+ final ObjectAnimator child2 = (ObjectAnimator) set.getChildAnimations().get(1);
+ final DummyObject dummyObject = new DummyObject();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ for (ObjectAnimator animator : new ObjectAnimator[]{child1, child2}) {
+ animator.setTarget(dummyObject);
+ animator.setupStartValues();
+ animator.start();
+ animator.end();
+ }
+ }
+ });
+ assertEquals(targetX, dummyObject.x);
+ assertEquals(targetY, dummyObject.y);
+ }
+ if (i == 0) {
+ rotate();
+ }
+ anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+
+ }
+ }
+
+ private void rotate() throws Throwable {
+ final Activity activity = getActivity();
+ int orientation = activity.getResources().getConfiguration().orientation;
+ final int nextOrientation = orientation == Configuration.ORIENTATION_LANDSCAPE
+ ? ActivityInfo.SCREEN_ORIENTATION_PORTRAIT
+ : ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE;
+ Instrumentation.ActivityMonitor monitor = new Instrumentation.ActivityMonitor(
+ activity.getClass().getName(), null, false);
+ getInstrumentation().addMonitor(monitor);
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ activity.setRequestedOrientation(nextOrientation);
+ }
+ });
+ getInstrumentation().waitForIdleSync();
+ Activity newAct = getInstrumentation()
+ .waitForMonitorWithTimeout(monitor, TimeUnit.SECONDS.toMillis(2));
+ if (newAct != null) {
+ setActivity(newAct);
+ }
+ }
+
+ /**
+ * Simple state list animator test that checks for cloning
+ */
+ public void testLoadStateListAnimator() {
+ StateListAnimator sla1 = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.test_state_list_animator);
+ StateListAnimator sla2 = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.test_state_list_animator);
+ assertUnique(sla1);
+ assertUnique(sla2);
+ }
+
+ /**
+ * Tests a state list animator which has an @anim reference that has different xmls per
+ * orientation
+ */
+ public void testLoadStateListAnimatorWithChangingResetState() throws Throwable {
+ loadStateListAnimatorWithChangingResetStateTest();
+ rotate();
+ loadStateListAnimatorWithChangingResetStateTest();
+ }
+
+ private void loadStateListAnimatorWithChangingResetStateTest() throws Throwable {
+ final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.test_state_list_animator_2);
+ final View testView = getTestView();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ testView.setStateListAnimator(sla);
+ testView.jumpDrawablesToCurrentState();
+ }
+ });
+ float resetValue = getActivity().getResources().getDimension(R.dimen.reset_state_value);
+ getInstrumentation().waitForIdleSync();
+ assertEquals(resetValue, testView.getX());
+ assertEquals(resetValue, testView.getY());
+ assertEquals(resetValue, testView.getZ());
+ }
+
+ /**
+ * Tests a state list animator which has different xml descriptions per orientation.
+ */
+ public void testLoadChangingStateListAnimator() throws Throwable {
+ loadChangingStateListAnimatorTest();
+ rotate();
+ loadChangingStateListAnimatorTest();
+ }
+
+ private void loadChangingStateListAnimatorTest() throws Throwable {
+ final StateListAnimator sla = AnimatorInflater.loadStateListAnimator(getActivity(),
+ R.anim.changing_state_list_animator);
+ final View testView = getTestView();
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ testView.setStateListAnimator(sla);
+ testView.jumpDrawablesToCurrentState();
+ }
+ });
+ float targetValue = getActivity().getResources()
+ .getDimension(R.dimen.changing_state_list_anim_target_x_value);
+ getInstrumentation().waitForIdleSync();
+ assertEquals(targetValue, testView.getX());
+ }
+
+ /**
+ * Tests that makes sure that reloaded animator is not affected by previous changes
+ */
+ public void testReloadedAnimatorIsNotModified() throws Throwable {
+ final Animator anim1 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ final CountDownLatch mStarted = new CountDownLatch(1);
+ final AnimatorListenerAdapter listener = new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ super.onAnimationEnd(animation);
+ }
+
+ @Override
+ public void onAnimationStart(Animator animation) {
+ super.onAnimationStart(animation);
+ mStarted.countDown();
+ }
+ };
+ runTestOnUiThread(new Runnable() {
+ @Override
+ public void run() {
+ anim1.setTarget(getTestView());
+ anim1.addListener(listener);
+ anim1.start();
+ }
+ });
+ Animator anim2 = AnimatorInflater.loadAnimator(getActivity(), R.anim.test_animator);
+ assertTrue(anim1.isStarted());
+ assertFalse(anim2.isStarted());
+ assertFalse("anim2 should not include the listener",
+ anim2.getListeners() != null && anim2.getListeners().contains(listener));
+ assertTrue("animator should start", mStarted.await(10, TimeUnit.SECONDS));
+ assertFalse(anim2.isRunning());
+
+ }
+
+ public View getTestView() {
+ return getActivity().findViewById(R.id.anim_window);
+ }
+
+ class DummyObject {
+
+ float x;
+ float y;
+
+ public float getX() {
+ return x;
+ }
+
+ public void setX(float x) {
+ this.x = x;
+ }
+
+ public float getY() {
+ return y;
+ }
+
+ public void setY(float y) {
+ this.y = y;
+ }
+ }
+}
+