Merge "Adding support for GLES3" into klp-dev
diff --git a/tests/expectations/knownfailures.txt b/tests/expectations/knownfailures.txt
index d7ef39f..6b0f214 100644
--- a/tests/expectations/knownfailures.txt
+++ b/tests/expectations/knownfailures.txt
@@ -6,5 +6,13 @@
{
name: "android.holo.cts.HoloTest",
bug: 8148617
+},
+{
+ name: "android.hardware.camera2.cts.ImageReaderTest",
+ name: "android.hardware.camera2.cts.CameraCharacteristicsTest",
+ name: "android.hardware.camera2.cts.CameraCaptureResultTest",
+ name: "android.hardware.camera2.cts.CameraDeviceTest",
+ name: "android.hardware.camera2.cts.CameraManagerTest",
+ bug: 11141002
}
]
diff --git a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
new file mode 100644
index 0000000..6a708e3
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraCaptureResultTest.java
@@ -0,0 +1,336 @@
+/*
+ * Copyright 2013 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.camera2.cts;
+
+import android.content.Context;
+import android.graphics.ImageFormat;
+import android.hardware.camera2.CameraCharacteristics;
+import android.hardware.camera2.CameraDevice;
+import android.hardware.camera2.CameraManager;
+import android.hardware.camera2.CameraMetadata;
+import android.hardware.camera2.CaptureFailure;
+import android.hardware.camera2.CaptureRequest;
+import android.hardware.camera2.CaptureResult;
+import android.hardware.camera2.Size;
+import static android.hardware.camera2.cts.CameraTestUtils.*;
+import android.media.ImageReader;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.test.AndroidTestCase;
+import android.util.Log;
+import android.view.Surface;
+
+import com.android.ex.camera2.blocking.BlockingStateListener;
+import static com.android.ex.camera2.blocking.BlockingStateListener.*;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.TimeUnit;
+
+public class CameraCaptureResultTest extends AndroidTestCase {
+ private static final String TAG = "CameraCaptureResultTest";
+ private static final boolean VERBOSE = Log.isLoggable(TAG, Log.VERBOSE);
+
+ private CameraManager mCameraManager;
+ private HandlerThread mHandlerThread;
+ private Handler mHandler;
+ private ImageReader mImageReader;
+ private Surface mSurface;
+ private BlockingStateListener mCameraListener;
+
+ private static final int MAX_NUM_IMAGES = 5;
+ private static final int NUM_FRAMES_VERIFIED = 300;
+ private static final long WAIT_FOR_RESULT_TIMEOUT_MS = 3000;
+
+ // List that includes all public keys from CaptureResult
+ List<CameraMetadata.Key<?>> mAllKeys;
+
+ // List tracking the failed test keys.
+ List<CameraMetadata.Key<?>> mFailedKeys = new ArrayList<CameraMetadata.Key<?>>();
+
+ @Override
+ public void setContext(Context context) {
+ mAllKeys = getAllCaptureResultKeys();
+ super.setContext(context);
+ }
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mCameraManager = (CameraManager) mContext.getSystemService(Context.CAMERA_SERVICE);
+ assertNotNull("Can't connect to camera manager", mCameraManager);
+ mHandlerThread = new HandlerThread(TAG);
+ mHandlerThread.start();
+ mHandler = new Handler(mHandlerThread.getLooper());
+ mCameraListener = new BlockingStateListener();
+ mFailedKeys.clear();
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mHandlerThread.quitSafely();
+ super.tearDown();
+ }
+
+ public void testCameraCaptureResultAllKeys() throws Exception {
+ /**
+ * Hardcode a key waiver list for the keys we want to skip the sanity check.
+ * FIXME: We need get ride of this list, see bug 11116270.
+ */
+ List<CameraMetadata.Key<?>> waiverkeys = new ArrayList<CameraMetadata.Key<?>>();
+ waiverkeys.add(CaptureResult.EDGE_MODE);
+ waiverkeys.add(CaptureResult.JPEG_GPS_COORDINATES);
+ waiverkeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
+ waiverkeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
+ waiverkeys.add(CaptureResult.JPEG_ORIENTATION);
+ waiverkeys.add(CaptureResult.JPEG_QUALITY);
+ waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
+ waiverkeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
+ waiverkeys.add(CaptureResult.SENSOR_TEMPERATURE);
+ waiverkeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
+ waiverkeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
+ waiverkeys.add(CaptureResult.TONEMAP_CURVE_RED);
+ waiverkeys.add(CaptureResult.TONEMAP_MODE);
+ waiverkeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_GAINS);
+ waiverkeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_TRANSFORM);
+ waiverkeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
+
+ String[] ids = mCameraManager.getCameraIdList();
+ for (int i = 0; i < ids.length; i++) {
+ CameraCharacteristics props = mCameraManager.getCameraCharacteristics(ids[i]);
+ assertNotNull("CameraCharacteristics shouldn't be null", props);
+ Integer hwLevel = props.get(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL);
+ if (hwLevel != CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_FULL) {
+ continue;
+ }
+ // TODO: check for LIMITED keys
+
+ CameraDevice camera = null;
+ try {
+ Size[] sizes = CameraTestUtils.getSupportedSizeForFormat(
+ ImageFormat.YUV_420_888, ids[i], mCameraManager);
+ CameraTestUtils.assertArrayNotEmpty(sizes, "Available sizes shouldn't be empty");
+ createDefaultSurface(sizes[0]);
+
+ if (VERBOSE) {
+ Log.v(TAG, "Testing camera " + ids[i] + "for size " + sizes[0].toString());
+ }
+
+ camera = CameraTestUtils.openCamera(
+ mCameraManager, ids[i], mCameraListener, mHandler);
+ assertNotNull(
+ String.format("Failed to open camera device %s", ids[i]), camera);
+ mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_OPEN_TIMEOUT_MS);
+
+ List<Surface> outputSurfaces = new ArrayList<Surface>(1);
+ outputSurfaces.add(mSurface);
+ camera.configureOutputs(outputSurfaces);
+ mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+ mCameraListener.waitForState(STATE_IDLE, CAMERA_IDLE_TIMEOUT_MS);
+
+ CaptureRequest.Builder requestBuilder =
+ camera.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW);
+ assertNotNull("Failed to create capture request", requestBuilder);
+ requestBuilder.addTarget(mSurface);
+
+ // Enable face detection if supported
+ byte[] faceModes = props.get(
+ CameraCharacteristics.STATISTICS_INFO_AVAILABLE_FACE_DETECT_MODES);
+ assertNotNull("Available face detection modes shouldn't be null", faceModes);
+ for (int m = 0; m < faceModes.length; m++) {
+ if (faceModes[m] == CameraMetadata.STATISTICS_FACE_DETECT_MODE_FULL) {
+ if (VERBOSE) {
+ Log.v(TAG, "testCameraCaptureResultAllKeys - " +
+ "setting facedetection mode to full");
+ }
+ requestBuilder.set(CaptureRequest.STATISTICS_FACE_DETECT_MODE,
+ (int)faceModes[m]);
+ }
+ }
+
+ // Enable lensShading mode, it should be supported by full mode device.
+ requestBuilder.set(CaptureRequest.STATISTICS_LENS_SHADING_MAP_MODE,
+ CameraMetadata.STATISTICS_LENS_SHADING_MAP_MODE_ON);
+
+ SimpleCaptureListener captureListener = new SimpleCaptureListener();
+ camera.setRepeatingRequest(requestBuilder.build(), captureListener, mHandler);
+
+ for (int m = 0; m < NUM_FRAMES_VERIFIED; m++) {
+ if(VERBOSE) {
+ Log.v(TAG, "Testing frame " + m);
+ }
+ validateCaptureResult(
+ captureListener.getCaptureResult(WAIT_FOR_RESULT_TIMEOUT_MS),
+ waiverkeys);
+ }
+
+ // Stop repeat, wait for captures to complete, and disconnect from surfaces
+ camera.configureOutputs(/*outputs*/ null);
+ mCameraListener.waitForState(STATE_BUSY, CAMERA_BUSY_TIMEOUT_MS);
+ mCameraListener.waitForState(STATE_UNCONFIGURED, CAMERA_IDLE_TIMEOUT_MS);
+ // Camera has disconnected, clear out the reader
+ mSurface.release();
+ mImageReader.close();
+ } finally {
+ if (camera != null) {
+ camera.close();
+ }
+ }
+
+ }
+ }
+
+ private void validateCaptureResult(CaptureResult result,
+ List<CameraMetadata.Key<?>> skippedKeys) throws Exception {
+ for (CameraMetadata.Key<?> key : mAllKeys) {
+ if (!skippedKeys.contains(key) && result.get(key) == null) {
+ mFailedKeys.add(key);
+ }
+ }
+
+ StringBuffer failedKeyNames = new StringBuffer("Below Keys have null values:\n");
+ for (CameraMetadata.Key<?> key : mFailedKeys) {
+ failedKeyNames.append(key.getName() + "\n");
+ }
+
+ assertTrue("Some keys have null values, " + failedKeyNames.toString(),
+ mFailedKeys.isEmpty());
+ }
+
+ private static class SimpleCaptureListener extends CameraDevice.CaptureListener {
+ LinkedBlockingQueue<CaptureResult> mQueue = new LinkedBlockingQueue<CaptureResult>();
+
+ @Override
+ public void onCaptureStarted(CameraDevice camera, CaptureRequest request, long timestamp)
+ {
+ }
+
+ @Override
+ public void onCaptureCompleted(CameraDevice camera, CaptureRequest request,
+ CaptureResult result) {
+ try {
+ mQueue.put(result);
+ } catch (InterruptedException e) {
+ throw new UnsupportedOperationException(
+ "Can't handle InterruptedException in onCaptureCompleted");
+ }
+ }
+
+ @Override
+ public void onCaptureFailed(CameraDevice camera, CaptureRequest request,
+ CaptureFailure failure) {
+ }
+
+ @Override
+ public void onCaptureSequenceCompleted(CameraDevice camera, int sequenceId,
+ int frameNumber) {
+ }
+
+ public CaptureResult getCaptureResult(long timeout) throws InterruptedException {
+ CaptureResult result = mQueue.poll(timeout, TimeUnit.MILLISECONDS);
+ assertNotNull("Wait for a capture result timed out in " + timeout + "ms", result);
+ return result;
+ }
+ }
+
+ private void createDefaultSurface(Size sz) {
+ mImageReader =
+ ImageReader.newInstance(sz.getWidth(),
+ sz.getHeight(),
+ ImageFormat.YUV_420_888,
+ MAX_NUM_IMAGES);
+ mImageReader.setOnImageAvailableListener(new ImageDropperListener(), mHandler);
+ mSurface = mImageReader.getSurface();
+ }
+
+ /**
+ * TODO: Use CameraCharacteristics.getAvailableCaptureResultKeys() once we can filter out
+ * @hide keys.
+ *
+ */
+
+ /*@O~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+ * The key entries below this point are generated from metadata
+ * definitions in /system/media/camera/docs. Do not modify by hand or
+ * modify the comment blocks at the start or end.
+ *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~*/
+
+ private static List<CameraMetadata.Key<?>> getAllCaptureResultKeys() {
+ ArrayList<CameraMetadata.Key<?>> resultKeys = new ArrayList<CameraMetadata.Key<?>>();
+ resultKeys.add(CaptureResult.COLOR_CORRECTION_TRANSFORM);
+ resultKeys.add(CaptureResult.COLOR_CORRECTION_GAINS);
+ resultKeys.add(CaptureResult.CONTROL_AE_REGIONS);
+ resultKeys.add(CaptureResult.CONTROL_AF_MODE);
+ resultKeys.add(CaptureResult.CONTROL_AF_REGIONS);
+ resultKeys.add(CaptureResult.CONTROL_AWB_MODE);
+ resultKeys.add(CaptureResult.CONTROL_AWB_REGIONS);
+ resultKeys.add(CaptureResult.CONTROL_MODE);
+ resultKeys.add(CaptureResult.CONTROL_AE_STATE);
+ resultKeys.add(CaptureResult.CONTROL_AF_STATE);
+ resultKeys.add(CaptureResult.CONTROL_AWB_STATE);
+ resultKeys.add(CaptureResult.EDGE_MODE);
+ resultKeys.add(CaptureResult.FLASH_MODE);
+ resultKeys.add(CaptureResult.FLASH_STATE);
+ resultKeys.add(CaptureResult.JPEG_GPS_COORDINATES);
+ resultKeys.add(CaptureResult.JPEG_GPS_PROCESSING_METHOD);
+ resultKeys.add(CaptureResult.JPEG_GPS_TIMESTAMP);
+ resultKeys.add(CaptureResult.JPEG_ORIENTATION);
+ resultKeys.add(CaptureResult.JPEG_QUALITY);
+ resultKeys.add(CaptureResult.JPEG_THUMBNAIL_QUALITY);
+ resultKeys.add(CaptureResult.JPEG_THUMBNAIL_SIZE);
+ resultKeys.add(CaptureResult.LENS_APERTURE);
+ resultKeys.add(CaptureResult.LENS_FILTER_DENSITY);
+ resultKeys.add(CaptureResult.LENS_FOCAL_LENGTH);
+ resultKeys.add(CaptureResult.LENS_FOCUS_DISTANCE);
+ resultKeys.add(CaptureResult.LENS_OPTICAL_STABILIZATION_MODE);
+ resultKeys.add(CaptureResult.LENS_FOCUS_RANGE);
+ resultKeys.add(CaptureResult.LENS_STATE);
+ resultKeys.add(CaptureResult.NOISE_REDUCTION_MODE);
+ resultKeys.add(CaptureResult.REQUEST_FRAME_COUNT);
+ resultKeys.add(CaptureResult.SCALER_CROP_REGION);
+ resultKeys.add(CaptureResult.SENSOR_EXPOSURE_TIME);
+ resultKeys.add(CaptureResult.SENSOR_FRAME_DURATION);
+ resultKeys.add(CaptureResult.SENSOR_SENSITIVITY);
+ resultKeys.add(CaptureResult.SENSOR_TIMESTAMP);
+ resultKeys.add(CaptureResult.SENSOR_TEMPERATURE);
+ resultKeys.add(CaptureResult.STATISTICS_FACE_DETECT_MODE);
+ resultKeys.add(CaptureResult.STATISTICS_FACE_IDS);
+ resultKeys.add(CaptureResult.STATISTICS_FACE_LANDMARKS);
+ resultKeys.add(CaptureResult.STATISTICS_FACE_RECTANGLES);
+ resultKeys.add(CaptureResult.STATISTICS_FACE_SCORES);
+ resultKeys.add(CaptureResult.STATISTICS_LENS_SHADING_MAP);
+ resultKeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_GAINS);
+ resultKeys.add(CaptureResult.STATISTICS_PREDICTED_COLOR_TRANSFORM);
+ resultKeys.add(CaptureResult.STATISTICS_SCENE_FLICKER);
+ resultKeys.add(CaptureResult.TONEMAP_CURVE_BLUE);
+ resultKeys.add(CaptureResult.TONEMAP_CURVE_GREEN);
+ resultKeys.add(CaptureResult.TONEMAP_CURVE_RED);
+ resultKeys.add(CaptureResult.TONEMAP_MODE);
+ resultKeys.add(CaptureResult.BLACK_LEVEL_LOCK);
+
+ // Add STATISTICS_FACES key separately here because it is not
+ // defined in metadata xml file.
+ resultKeys.add(CaptureResult.STATISTICS_FACES);
+
+ return resultKeys;
+ }
+
+ /*~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~
+ * End generated code
+ *~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~@~O@*/
+}
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 a2d965c..7f10cb8 100644
--- a/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
+++ b/tests/tests/hardware/src/android/hardware/camera2/cts/CameraTestUtils.java
@@ -24,6 +24,7 @@
import android.hardware.camera2.CameraCharacteristics;
import android.hardware.camera2.Size;
import android.media.Image;
+import android.media.ImageReader;
import android.media.Image.Plane;
import android.os.Handler;
import android.util.Log;
@@ -52,6 +53,20 @@
public static final int CAMERA_ACTIVE_TIMEOUT_MS = 500;
public static final int CAMERA_BUSY_TIMEOUT_MS = 500;
+ public static class ImageDropperListener implements ImageReader.OnImageAvailableListener {
+ @Override
+ public void onImageAvailable(ImageReader reader) {
+ Image image = null;
+ try {
+ image = reader.acquireNextImage();
+ } finally {
+ if (image != null) {
+ image.close();
+ }
+ }
+ }
+ }
+
/**
* Block until the camera is opened.
*
@@ -214,21 +229,21 @@
int format = image.getFormat();
Plane[] planes = image.getPlanes();
switch (format) {
- case ImageFormat.YUV_420_888:
- case ImageFormat.NV21:
- case ImageFormat.YV12:
- assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
- break;
- case ImageFormat.Y8:
- case ImageFormat.Y16:
- assertEquals("Y8/Y16 Image should have 1 plane", 1, planes.length);
- break;
- case ImageFormat.JPEG:
- assertEquals("Jpeg Image should have one plane", 1, planes.length);
- break;
- default:
- fail("Unsupported Image Format: " + format);
- }
+ case ImageFormat.YUV_420_888:
+ case ImageFormat.NV21:
+ case ImageFormat.YV12:
+ assertEquals("YUV420 format Images should have 3 planes", 3, planes.length);
+ break;
+ case ImageFormat.Y8:
+ case ImageFormat.Y16:
+ assertEquals("Y8/Y16 Image should have 1 plane", 1, planes.length);
+ break;
+ case ImageFormat.JPEG:
+ assertEquals("Jpeg Image should have one plane", 1, planes.length);
+ break;
+ default:
+ fail("Unsupported Image Format: " + format);
+ }
}
public static void dumpFile(String fileName, byte[] data) {
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java
index 51fd5f6..2e33a5d 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorAccelerometerTest.java
@@ -20,6 +20,8 @@
import android.hardware.SensorManager;
public class SensorAccelerometerTest extends SensorCommonTests {
+ private final int AXIS_COUNT = 3;
+
@Override
protected int getMaxFrequencySupportedInuS() {
return 10000; // 100Hz
@@ -37,21 +39,13 @@
*/
@Override
public void testEventValidity() {
- final float THRESHOLD = 0.25f; // m / s^2
- validateSensorEvent(
- 0 /*x-axis*/,
- 0 /*y-axis*/,
- SensorManager.STANDARD_GRAVITY /*z-axis*/,
- THRESHOLD);
+ final float THRESHOLD = 0.5f; // m / s^2
+ validateNormForSensorEvent(SensorManager.STANDARD_GRAVITY, THRESHOLD, AXIS_COUNT);
}
@Override
- public void testVarianceWhileStatic() {
- final float THRESHOLD = 0.25f; // m / s^2
- validateVarianceWhileStatic(
- 0 /*x-axis*/,
- 0 /*y-axis*/,
- SensorManager.STANDARD_GRAVITY /*z-axis*/,
- THRESHOLD);
+ public void testStandardDeviationWhileStatic() {
+ final float STANDARD_DEVIATION = 1f; // m / s^2
+ validateStandardDeviationWhileStatic(STANDARD_DEVIATION, AXIS_COUNT);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java
index 5cc65bb..326963c 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorCommonTests.java
@@ -18,45 +18,34 @@
import android.content.Context;
-import android.hardware.FlushCompleteListener;
import android.hardware.Sensor;
-import android.hardware.SensorEvent;
-import android.hardware.SensorEventListener;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.TestSensorManager;
+
import android.os.PowerManager;
+import android.os.SystemClock;
import android.test.AndroidTestCase;
-import java.util.List;
import android.util.Log;
-import java.io.DataOutputStream;
-import java.io.IOException;
-
-import java.text.SimpleDateFormat;
-
import java.util.ArrayList;
-import java.util.Collections;
-import java.util.Date;
+import java.util.List;
import java.util.Random;
-import java.util.concurrent.ConcurrentLinkedDeque;
-import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
+import junit.framework.Assert;
+
/**
* Class is not marked public to avoid TestRunner to pick the tests in it
*/
abstract class SensorCommonTests extends AndroidTestCase {
protected final String LOG_TAG = "TestRunner";
-
- protected SensorManager mSensorManager;
- protected Sensor mSensorUnderTest;
- protected TestSensorListener mEventListener;
-
- private FlushCompleteListener mFlushListener;
+ protected TestSensorManager mTestSensorManager;
private PowerManager.WakeLock mWakeLock;
protected SensorCommonTests() {}
@@ -71,15 +60,11 @@
* Abstract test methods that sensors need to verify
*/
public abstract void testEventValidity();
- public abstract void testVarianceWhileStatic();
+ public abstract void testStandardDeviationWhileStatic();
/**
* Methods to control the behavior of the tests by concrete sensor tests
*/
- protected int getWaitTimeoutInSeconds() {
- return 30;
- }
-
protected int getHighNumberOfIterationsToExecute() {
return 100;
}
@@ -101,31 +86,27 @@
Context.POWER_SERVICE);
mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, this.getLogTag());
mWakeLock.acquire();
-
- mEventListener = new TestSensorListener();
- mFlushListener = new TestFlushListener();
}
@Override
protected void tearDown() throws Exception {
- mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
-
- mFlushListener = null;
- mEventListener = null;
- mSensorUnderTest = null;
+ if(mTestSensorManager != null) {
+ mTestSensorManager.close();
+ mTestSensorManager = null;
+ }
releaseWakeLock();
}
@Override
public void runBare() throws Throwable {
- mSensorManager = (SensorManager) this.getContext().getSystemService(Context.SENSOR_SERVICE);
- assertNotNull("getSystemService#Sensor_Service", mSensorManager);
+ SensorManager sensorManager = (SensorManager) this.getContext().getSystemService(Context.SENSOR_SERVICE);
+ assertNotNull("getSystemService#Sensor_Service", sensorManager);
- List<Sensor> availableSensors = mSensorManager.getSensorList(this.getSensorType());
+ List<Sensor> availableSensors = sensorManager.getSensorList(this.getSensorType());
// it is OK if there are no sensors available
for(Sensor sensor : availableSensors) {
- mSensorUnderTest = sensor;
+ mTestSensorManager = new TestSensorManager(this, sensorManager, sensor);
super.runBare();
}
}
@@ -134,49 +115,34 @@
* Test cases continuous mode.
*/
public void testCanRegisterListener() {
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertTrue("registerListener", result);
+ mTestSensorManager.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
}
public void testNotTriggerSensor() {
TestTriggerListener listener = new TestTriggerListener();
- assertFalse(
- "requestTriggerSensor",
- mSensorManager.requestTriggerSensor(listener, mSensorUnderTest));
+ boolean result = mTestSensorManager.getUnderlyingSensorManager().requestTriggerSensor(
+ listener,
+ mTestSensorManager.getSensorUnderTest());
+ assertFalse("requestTriggerSensor", result);
}
public void testCanReceiveEvents() {
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_NORMAL);
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(5);
+ mTestSensorManager.collectEvents(SensorManager.SENSOR_DELAY_NORMAL, 5);
}
public void testMaxFrequency() {
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- this.getMaxFrequencySupportedInuS());
- assertTrue("registerListener", result);
+ // TODO: verify that events do arrive at the proper rate
+ mTestSensorManager.registerListener(this.getMaxFrequencySupportedInuS());
}
public void testEventsArriveInOrder() {
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_FASTEST);
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(100);
-
- SensorEventForTest[] events = mEventListener.getAllEvents();
+ // TODO: test for other sensor frequencies, rely on helper test classes for sensors
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+ SensorManager.SENSOR_DELAY_FASTEST,
+ 100);
for(int i = 1; i < events.length; ++i) {
- long previousTimestamp = events[i-1].getTimestamp();
- long timestamp = events[i].getTimestamp();
+ long previousTimestamp = events[i-1].timestamp;
+ long timestamp = events[i].timestamp;
assertTrue(
String.format("[timestamp:%d] %d >= %d", i, previousTimestamp, timestamp),
previousTimestamp < timestamp);
@@ -184,17 +150,7 @@
}
public void testStartStopRepeatedly() {
- for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
- String iterationInfo = String.format("registerListener:%d", i);
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_FASTEST);
- assertTrue(iterationInfo, result);
- mEventListener.waitForEvents(1, iterationInfo);
-
- mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
- }
+ validateRegisterUnregisterRepeteadly(mTestSensorManager);
}
public void testUpdateRate() {
@@ -221,33 +177,87 @@
rate = this.getMaxFrequencySupportedInuS() * generator.nextInt(10);
}
- String iterationInfo = String.format("registerListener:%d, rate:%d", i, rate);
- assertTrue(
- iterationInfo,
- mSensorManager.registerListener(mEventListener, mSensorUnderTest, rate));
-
- mEventListener.waitForEvents(generator.nextInt(5) + 1, iterationInfo);
- mEventListener.clearEvents();
-
- mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
+ // TODO: check that the rate has indeed changed
+ mTestSensorManager.collectEvents(
+ rate,
+ generator.nextInt(5) + 1,
+ String.format("iteration:%d, rate:%d", i, rate));
}
}
- public void testSeveralClients() throws InterruptedException {
- ArrayList<Thread> threads = new ArrayList<Thread>();
- for(int i = 0; i < this.getNumberOfThreadsToUse(); ++i) {
- threads.add(new Thread() {
- @Override
- public void run() {
- testStartStopRepeatedly();
- }
- });
- }
+ public void testOneClientSeveralThreads() throws InterruptedException {
+ Runnable operation = new Runnable() {
+ @Override
+ public void run() {
+ validateRegisterUnregisterRepeteadly(mTestSensorManager);
+ }
+ };
+ SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+ }
- while(!threads.isEmpty()) {
- Thread thread = threads.remove(0);
- thread.join();
- }
+ public void testSeveralClients() throws InterruptedException {
+ final Assert assertionObject = this;
+ Runnable operation = new Runnable() {
+ @Override
+ public void run() {
+ TestSensorManager testSensorManager = new TestSensorManager(
+ assertionObject,
+ mTestSensorManager.getUnderlyingSensorManager(),
+ mTestSensorManager.getSensorUnderTest());
+ validateRegisterUnregisterRepeteadly(testSensorManager);
+ }
+ };
+ SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+ }
+
+ public void testStoppingOtherClients() {
+ // TODO: use a higher test abstraction and move these to integration tests
+ final int EVENT_COUNT = 1;
+ final int SECOND_EVENT_COUNT = 5;
+ TestSensorManager sensorManager2 = new TestSensorManager(
+ this,
+ mTestSensorManager.getUnderlyingSensorManager(),
+ mTestSensorManager.getSensorUnderTest());
+
+ mTestSensorManager.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
+
+ // is receiving events
+ mTestSensorManager.getEvents(EVENT_COUNT);
+
+ // operate in a different client
+ sensorManager2.collectEvents(SensorManager.SENSOR_DELAY_FASTEST, SECOND_EVENT_COUNT);
+
+ // verify first client is still operating
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+ assertTrue(
+ String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+ events.length >= EVENT_COUNT);
+ }
+
+ public void testStoppingOtherClientsBatching() {
+ final int EVENT_COUNT = 1;
+ final int SECOND_EVENT_COUNT = 5;
+ TestSensorManager sensorManager2 = new TestSensorManager(
+ this,
+ mTestSensorManager.getUnderlyingSensorManager(),
+ mTestSensorManager.getSensorUnderTest());
+
+ mTestSensorManager.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
+
+ // is receiving events
+ mTestSensorManager.getEvents(EVENT_COUNT);
+
+ // operate in a different client
+ sensorManager2.collectBatchEvents(
+ SensorManager.SENSOR_DELAY_FASTEST,
+ SensorCtsHelper.getSecondsAsMicroSeconds(1),
+ SECOND_EVENT_COUNT);
+
+ // verify first client is still operating
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+ assertTrue(
+ String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+ events.length >= EVENT_COUNT);
}
/**
@@ -255,50 +265,52 @@
*/
public void testRegisterForBatchingZeroReport() {
releaseWakeLock();
-
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_NORMAL,
- 0 /*maxBatchReportLatencyUs*/,
- 0 /*reservedFlags*/,
- mFlushListener);
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(10);
+ // TODO: use test wrappers to verify for reportLatency ==0 !=0
+ mTestSensorManager.collectBatchEvents(SensorManager.SENSOR_DELAY_NORMAL, 0, 10);
}
public void testCanReceiveBatchEvents() {
releaseWakeLock();
-
- // TODO: refactor out common code across tests that register for events and do post-process
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
+ mTestSensorManager.collectBatchEvents(
SensorManager.SENSOR_DELAY_NORMAL,
- 5 * 1000000 /*maxBatchReportLatencyUs*/,
- 0 /*reservedFlags*/,
- mFlushListener);
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(10);
+ SensorCtsHelper.getSecondsAsMicroSeconds(5),
+ 10 /*eventCount*/);
+ }
+
+ /**
+ * Regress:
+ * -b/10790905
+ */
+ public void ignore_testBatchingReportLatency() {
+ long startTime = SystemClock.elapsedRealtimeNanos();
+ // TODO: define the sensor frequency per sensor
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectBatchEvents(
+ SensorCtsHelper.getSecondsAsMicroSeconds(1),
+ SensorCtsHelper.getSecondsAsMicroSeconds(5),
+ 1 /*eventCount*/);
+ long elapsedTime = SystemClock.elapsedRealtimeNanos() - startTime;
+ long expectedTime =
+ TimeUnit.NANOSECONDS.convert(5, TimeUnit.SECONDS) +
+ TimeUnit.NANOSECONDS.convert(500, TimeUnit.MILLISECONDS);
+
+ // TODO: ensure the proper batching time considers the size of the FIFO (fifoMaxEventCount),
+ // and make sure that no other application is registered
+ assertTrue(
+ String.format("WaitTime| expected:%d, actual:%d", expectedTime, elapsedTime),
+ elapsedTime <= expectedTime);
}
public void testBatchEventsArriveInOrder() {
releaseWakeLock();
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
+ // TODO: identify if we can reuse code from the non-batching case, same for other batch tests
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectBatchEvents(
SensorManager.SENSOR_DELAY_NORMAL,
- 5 * 1000000 /*maxBatchReportLatencyUs*/,
- 0 /*reservedFlags*/,
- mFlushListener);
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(100);
-
- SensorEventForTest[] events = mEventListener.getAllEvents();
+ SensorCtsHelper.getSecondsAsMicroSeconds(5),
+ 100);
for(int i = 1; i < events.length; ++i) {
- long previousTimestamp = events[i-1].getTimestamp();
- long timestamp = events[i].getTimestamp();
+ long previousTimestamp = events[i-1].timestamp;
+ long timestamp = events[i].timestamp;
assertTrue(
String.format("[timestamp:%d] %d >= %d", i, previousTimestamp, timestamp),
previousTimestamp < timestamp);
@@ -307,22 +319,7 @@
public void testStartStopBatchingRepeatedly() {
releaseWakeLock();
-
- for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
- String iterationInfo = String.format("registerListener:%d", i);
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_FASTEST,
- 5 * 1000000 /*maxBatchReportLatencyUs*/,
- 0 /*reservedFlags*/,
- mFlushListener);
-
- assertTrue(iterationInfo, result);
- mEventListener.waitForEvents(5, iterationInfo);
-
- mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
- }
+ validateRegisterUnregisterRepeteadlyBatching(mTestSensorManager);
}
public void testUpdateBatchRate() {
@@ -351,41 +348,91 @@
rate = this.getMaxFrequencySupportedInuS() * generator.nextInt(10);
}
- String iterationInfo = String.format("registerListener:%d, rate:%d", i, rate);
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
+ String iterationInfo = String.format("iteration:%d, rate:%d", i, rate);
+ mTestSensorManager.collectBatchEvents(
rate,
- generator.nextInt(5 * 1000000),
- 0 /*reservedFlags*/,
- mFlushListener);
- assertTrue(iterationInfo, result);
-
- mEventListener.waitForEvents(generator.nextInt(5) + 1, iterationInfo);
- mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
- mEventListener.clearEvents();
+ generator.nextInt(SensorCtsHelper.getSecondsAsMicroSeconds(5)),
+ generator.nextInt(5) + 1,
+ iterationInfo);
}
}
- public void testSeveralClientsBatching() {
- ArrayList<Thread> threads = new ArrayList<Thread>();
- for(int i = 0; i < this.getNumberOfThreadsToUse(); ++i) {
- threads.add(new Thread() {
- @Override
- public void run() {
- testStartStopBatchingRepeatedly();
- }
- });
- }
-
- while(!threads.isEmpty()) {
- Thread thread = threads.remove(0);
- try {
- thread.join();
- } catch(InterruptedException e) {
- // just continue
+ public void testOneClientSeveralThreadsBatching() throws InterruptedException {
+ Runnable operation = new Runnable() {
+ @Override
+ public void run() {
+ validateRegisterUnregisterRepeteadlyBatching(mTestSensorManager);
}
- }
+ };
+ SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+ }
+
+ public void testSeveralClientsBatching() throws InterruptedException {
+ final Assert assertionObject = this;
+ Runnable operation = new Runnable() {
+ @Override
+ public void run() {
+ TestSensorManager testSensorManager = new TestSensorManager(
+ assertionObject,
+ mTestSensorManager.getUnderlyingSensorManager(),
+ mTestSensorManager.getSensorUnderTest());
+ validateRegisterUnregisterRepeteadlyBatching(testSensorManager);
+ }
+ };
+ SensorCtsHelper.performOperationInThreads(this.getNumberOfThreadsToUse(), operation);
+ }
+
+ public void testBatchingStoppingOtherClients() {
+ final int EVENT_COUNT = 1;
+ final int SECOND_EVENT_COUNT = 5;
+ TestSensorManager sensorManager2 = new TestSensorManager(
+ this,
+ mTestSensorManager.getUnderlyingSensorManager(),
+ mTestSensorManager.getSensorUnderTest());
+
+ mTestSensorManager.registerBatchListener(
+ SensorManager.SENSOR_DELAY_NORMAL,
+ SensorCtsHelper.getSecondsAsMicroSeconds(5));
+
+ // is receiving events
+ mTestSensorManager.getEvents(EVENT_COUNT);
+
+ // operate in a different client
+ sensorManager2.collectEvents(SensorManager.SENSOR_DELAY_FASTEST, SECOND_EVENT_COUNT);
+
+ // verify first client is still operating
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+ assertTrue(
+ String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+ events.length >= EVENT_COUNT);
+ }
+
+ public void testBatchingStoppingOtherClientsBatching() {
+ final int EVENT_COUNT = 1;
+ final int SECOND_EVENT_COUNT = 5;
+ TestSensorManager sensorManager2 = new TestSensorManager(
+ this,
+ mTestSensorManager.getUnderlyingSensorManager(),
+ mTestSensorManager.getSensorUnderTest());
+
+ mTestSensorManager.registerBatchListener(
+ SensorManager.SENSOR_DELAY_NORMAL,
+ SensorCtsHelper.getSecondsAsMicroSeconds(5));
+
+ // is receiving events
+ mTestSensorManager.getEvents(EVENT_COUNT);
+
+ // operate in a different client
+ sensorManager2.collectBatchEvents(
+ SensorManager.SENSOR_DELAY_FASTEST,
+ SensorCtsHelper.getSecondsAsMicroSeconds(1),
+ SECOND_EVENT_COUNT);
+
+ // verify first client is still operating
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.getEvents(EVENT_COUNT);
+ assertTrue(
+ String.format("Events| expected:%d, actual:%d", EVENT_COUNT, events.length),
+ events.length >= EVENT_COUNT);
}
/**
@@ -394,33 +441,21 @@
public void testEventJittering() {
final long EXPECTED_TIMESTAMP_NS = this.getMaxFrequencySupportedInuS() * 1000;
final long THRESHOLD_IN_NS = EXPECTED_TIMESTAMP_NS / 10; // 10%
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- this.getMaxFrequencySupportedInuS());
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(100);
- SensorEventForTest[] events = mEventListener.getAllEvents();
- ArrayList<Long> timestampDeltas = new ArrayList<Long>();
- for(int i = 1; i < events.length; ++i) {
- long previousTimestamp = events[i-1].getTimestamp();
- long timestamp = events[i].getTimestamp();
- long delta = timestamp - previousTimestamp;
- long jitterValue = Math.abs(EXPECTED_TIMESTAMP_NS - delta);
- timestampDeltas.add(jitterValue);
- }
-
- Collections.sort(timestampDeltas);
- long percentile95InNs = timestampDeltas.get(95);
- long actualPercentValue = (percentile95InNs * 100) / EXPECTED_TIMESTAMP_NS;
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+ this.getMaxFrequencySupportedInuS(),
+ 100);
+ ArrayList<Double> jitterValues = new ArrayList<Double>();
+ double jitterMean = SensorCtsHelper.getJitterMean(events, jitterValues);
+ double percentile95InNs = SensorCtsHelper.get95PercentileValue(jitterValues);
if(percentile95InNs > THRESHOLD_IN_NS) {
- for(long jitter : timestampDeltas) {
- Log.e(LOG_TAG, "Jittering delta: " + jitter);
+ for(double jitter : jitterValues) {
+ Log.e(LOG_TAG, "Jitter: " + jitter);
}
+ double actualPercentValue = (percentile95InNs * 100) / jitterMean;
String message = String.format(
- "95%%Jitter| 10%%:%dns, observed:%dns(%d%%)",
+ "95%% Jitter| 10%%:%dns, actual:%fns(%.2f%%)",
THRESHOLD_IN_NS,
percentile95InNs,
actualPercentValue);
@@ -428,6 +463,29 @@
}
}
+ public void testFrequencyAccuracy() {
+ final long EXPECTED_TIMESTAMP_NS = this.getMaxFrequencySupportedInuS() * 1000;
+ final long THRESHOLD_IN_NS = EXPECTED_TIMESTAMP_NS / 10; // 10%
+
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+ this.getMaxFrequencySupportedInuS(),
+ 100);
+ ArrayList<Long> timestampDelayValues = new ArrayList<Long>();
+ Double frequencyMean = SensorCtsHelper.getAverageTimestampDelayWithValues(
+ events,
+ timestampDelayValues);
+ if(Math.abs(EXPECTED_TIMESTAMP_NS - frequencyMean) > THRESHOLD_IN_NS) {
+ for(long value : timestampDelayValues) {
+ Log.e(LOG_TAG, "TimestampDelay: " + value);
+ }
+ String message = String.format(
+ "Frequency| expected:%d, actual:%f",
+ EXPECTED_TIMESTAMP_NS,
+ frequencyMean);
+ fail(message);
+ }
+ }
+
/**
* Private helpers.
*/
@@ -444,252 +502,84 @@
}
}
- private void collectBugreport() {
- String commands[] = new String[] {
- "dumpstate",
- "dumpsys",
- "logcat -d -v threadtime",
- "exit"
- };
-
- SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
- String outputFile = String.format(
- "%s/%s_%s",
- this.getLogTag(),
- "/sdcard/Download",
- dateFormat.format(new Date()));
-
- DataOutputStream processOutput = null;
- try {
- Process process = Runtime.getRuntime().exec("/system/bin/sh -");
- processOutput = new DataOutputStream(process.getOutputStream());
-
- for(String command : commands) {
- processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
- }
-
- processOutput.flush();
- process.waitFor();
-
- Log.d(this.getLogTag(), String.format("Bug-Report collected at: %s", outputFile));
- } catch (IOException e) {
- fail("Unable to collect Bug Report. " + e.toString());
- } catch (InterruptedException e) {
- fail("Unable to collect Bug Report. " + e.toString());
- } finally {
- if(processOutput != null) {
- try {
- processOutput.close();
- } catch(IOException e) {}
- }
- }
- }
-
/**
* Test method helper implementations
*/
- protected void validateSensorEvent(
- float expectedX,
- float expectedY,
- float expectedZ,
- float threshold) {
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- SensorManager.SENSOR_DELAY_FASTEST);
+ protected void validateNormForSensorEvent(float reference, float threshold, int axisCount) {
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+ SensorManager.SENSOR_DELAY_FASTEST,
+ 1);
+ TestSensorManager.SensorEventForTest event = events[0];
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(1);
- SensorEventForTest event = mEventListener.getLastEvent();
+ StringBuilder valuesBuilder = new StringBuilder();
+ double norm = 0.0;
+ for(int i = 0; i < axisCount; ++i) {
+ float value = event.values[i];
+ norm += Math.pow(value, 2);
- float xValue = event.getX();
- assertTrue(
- String.format("x-axis| expected:%f, actual:%f, threshold:%f", expectedX, xValue, threshold),
- Math.abs(expectedX - xValue) <= threshold);
+ valuesBuilder.append(value);
+ valuesBuilder.append(", ");
+ }
+ norm = Math.sqrt(norm);
- float yValue = event.getY();
- assertTrue(
- String.format("y-axis| expected:%f, actual:%f, threshold:%f", expectedY, yValue, threshold),
- Math.abs(expectedY - yValue) <= threshold);
-
- float zValue = event.getZ();
- assertTrue(
- String.format("z-axis| expected:%f, actual:%f, threshold:%f", expectedZ, zValue, threshold),
- Math.abs(expectedZ - zValue) <= threshold);
+ String message = String.format(
+ "Norm| expected:%f, threshold:%f, actual:%f (%s)",
+ reference,
+ threshold,
+ norm,
+ valuesBuilder.toString());
+ assertTrue(message, Math.abs(reference - norm) <= threshold);
}
- protected void validateVarianceWhileStatic(
- float referenceX,
- float referenceY,
- float referenceZ,
- float threshold) {
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- this.getMaxFrequencySupportedInuS());
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(100);
-
- SensorEventForTest[] events = mEventListener.getAllEvents();
- ArrayList<Float> deltaValuesX = new ArrayList<Float>();
- ArrayList<Float> deltaValuesY = new ArrayList<Float>();
- ArrayList<Float> deltaValuesZ = new ArrayList<Float>();
- for(int i = 0; i < events.length; ++i) {
- SensorEventForTest event = events[i];
- deltaValuesX.add(Math.abs(event.getX() - referenceX));
- deltaValuesY.add(Math.abs(event.getY() - referenceY));
- deltaValuesZ.add(Math.abs(event.getZ() - referenceZ));
+ protected void validateRegisterUnregisterRepeteadly(TestSensorManager testSensorManager) {
+ for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
+ String iterationInfo = String.format("iteration:%d", i);
+ testSensorManager.collectEvents(SensorManager.SENSOR_DELAY_FASTEST, 1, iterationInfo);
}
+ }
- Collections.sort(deltaValuesX);
- float percentile95X = deltaValuesX.get(95);
- if(percentile95X > threshold) {
- for(float valueX : deltaValuesX) {
- Log.e(LOG_TAG, "Variance|X delta: " + valueX);
- }
- String message = String.format(
- "95%%Variance|X expected:%f, observed:%f",
- threshold,
- percentile95X);
- fail(message);
+ protected void validateRegisterUnregisterRepeteadlyBatching(
+ TestSensorManager testSensorManager) {
+ // TODO: refactor if allowed with test wrapper abstractions
+ for(int i = 0; i < this.getLowNumberOfIterationsToExecute(); ++i) {
+ testSensorManager.collectBatchEvents(
+ SensorManager.SENSOR_DELAY_FASTEST,
+ SensorCtsHelper.getSecondsAsMicroSeconds(5),
+ 5 /*eventCont*/,
+ String.format("iteration:%d", i));
}
+ }
- Collections.sort(deltaValuesY);
- float percentile95Y = deltaValuesY.get(95);
- if(percentile95Y > threshold) {
- for(float valueY : deltaValuesY) {
- Log.e(LOG_TAG, "Variance|Y delta: " + valueY);
- }
- String message = String.format(
- "95%%Variance|Y expected:%f, observed:%f",
- threshold,
- percentile95Y);
- fail(message);
- }
+ protected void validateStandardDeviationWhileStatic(
+ float expectedStandardDeviation,
+ int axisCount) {
+ // TODO: refactor the report parameter with test wrappers if available
+ TestSensorManager.SensorEventForTest[] events = mTestSensorManager.collectEvents(
+ this.getMaxFrequencySupportedInuS(),
+ 100);
- Collections.sort(deltaValuesZ);
- float percentile95Z = deltaValuesZ.get(95);
- if(percentile95Z > threshold) {
- for(float valueZ : deltaValuesZ) {
- Log.e(LOG_TAG, "Variance|Z delta: " + valueZ);
+ for(int i = 0; i < axisCount; ++i) {
+ ArrayList<Float> values = new ArrayList<Float>();
+ for(TestSensorManager.SensorEventForTest event : events) {
+ values.add(event.values[i]);
}
+
+ double standardDeviation = SensorCtsHelper.getStandardDeviation(values);
String message = String.format(
- "95%%Variance|Z expected:%f, observed:%f",
- threshold,
- percentile95Z);
- fail(message);
+ "StandardDeviation| axis:%d, expected:%f, actual:%f",
+ i,
+ expectedStandardDeviation,
+ standardDeviation);
+ assertTrue(message, standardDeviation <= expectedStandardDeviation);
}
}
/**
* Private class definitions to support test of event handlers.
*/
- protected class SensorEventForTest {
- private Sensor mSensor;
- private long mTimestamp;
- private int mAccuracy;
-
- private float mValueX;
- private float mValueY;
- private float mValueZ;
-
- public SensorEventForTest(SensorEvent event) {
- mSensor = event.sensor;
- mTimestamp = event.timestamp;
- mAccuracy = event.accuracy;
- mValueX = event.values[0];
- mValueY = event.values[1];
- mValueZ = event.values[2];
- }
-
- public Sensor getSensor() {
- return mSensor;
- }
-
- public int getAccuracy() {
- return mAccuracy;
- }
-
- public float getX() {
- return mValueX;
- }
-
- public float getY() {
- return mValueY;
- }
-
- public float getZ() {
- return mValueZ;
- }
-
- public long getTimestamp() {
- return mTimestamp;
- }
- }
-
- protected class TestSensorListener implements SensorEventListener {
- private final ConcurrentLinkedDeque<SensorEventForTest> mSensorEventsList =
- new ConcurrentLinkedDeque<SensorEventForTest>();
- private volatile CountDownLatch mEventLatch;
-
- @Override
- public void onSensorChanged(SensorEvent event) {
- // copy the event because there is no better way to do this in the platform
- mSensorEventsList.addLast(new SensorEventForTest(event));
-
- CountDownLatch latch = mEventLatch;
- if(latch != null) {
- latch.countDown();
- }
- }
-
- @Override
- public void onAccuracyChanged(Sensor sensor, int accuracy) {
- }
-
- public void waitForEvents(int eventCount) {
- waitForEvents(eventCount, null);
- }
-
- public void waitForEvents(int eventCount, String timeoutInfo) {
- mEventLatch = new CountDownLatch(eventCount);
- try {
- boolean awaitCompleted = mEventLatch.await(getWaitTimeoutInSeconds(), TimeUnit.SECONDS);
- if(!awaitCompleted) {
- collectBugreport();
- }
-
- String assertMessage = String.format(
- "WaitForEvents:%d, available:%d, %s",
- eventCount,
- mSensorEventsList.size(),
- timeoutInfo);
- assertTrue(assertMessage, awaitCompleted);
- } catch(InterruptedException e) { }
- }
-
- public SensorEventForTest getLastEvent() {
- return mSensorEventsList.getLast();
- }
-
- public SensorEventForTest[] getAllEvents() {
- return mSensorEventsList.toArray(new SensorEventForTest[0]);
- }
-
- public void clearEvents() {
- mSensorEventsList.clear();
- }
- }
-
private class TestTriggerListener extends TriggerEventListener {
@Override
public void onTrigger(TriggerEvent event) {
}
}
-
- private class TestFlushListener implements FlushCompleteListener {
- @Override
- public void onFlushCompleted(Sensor sensor) {
- }
- }
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java
index d3d72a3..b7c082e 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorGyroscopeTest.java
@@ -19,6 +19,8 @@
import android.hardware.Sensor;
public class SensorGyroscopeTest extends SensorCommonTests {
+ private final int AXIS_COUNT = 3;
+
@Override
protected int getMaxFrequencySupportedInuS() {
return 10000; // 100Hz
@@ -32,20 +34,12 @@
@Override
public void testEventValidity() {
final float THRESHOLD = 0.1f; // dps
- validateSensorEvent(
- 0 /*x-axis*/,
- 0 /*y-axis*/,
- 0 /*z-axis*/,
- THRESHOLD);
+ validateNormForSensorEvent(0 /*reference*/, THRESHOLD, AXIS_COUNT);
}
@Override
- public void testVarianceWhileStatic() {
- final float THRESHOLD = 0.1f; // dps
- validateVarianceWhileStatic(
- 0 /*x-axis*/,
- 0 /*y-axis*/,
- 0 /*z-axis*/,
- THRESHOLD);
+ public void testStandardDeviationWhileStatic() {
+ final float STANDARD_DEVIATION = 0.5f; // dps
+ validateStandardDeviationWhileStatic(STANDARD_DEVIATION, AXIS_COUNT);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
new file mode 100644
index 0000000..f764c6c
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorIntegrationTests.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.content.Context;
+
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+
+import android.hardware.cts.helpers.SensorCtsHelper;
+import android.hardware.cts.helpers.TestSensorManager;
+
+import android.os.PowerManager;
+
+import android.test.AndroidTestCase;
+
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+
+public class SensorIntegrationTests extends AndroidTestCase {
+ protected final String LOG_TAG = "SensorIntegrationTests";
+ private PowerManager.WakeLock mWakeLock;
+ private SensorManager mSensorManager;
+
+ /**
+ * Test execution methods
+ */
+ @Override
+ protected void setUp() throws Exception {
+ PowerManager powerManager = (PowerManager) this.getContext().getSystemService(
+ Context.POWER_SERVICE);
+ mWakeLock = powerManager.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, LOG_TAG);
+ mWakeLock.acquire();
+
+ mSensorManager = (SensorManager) this.getContext().getSystemService(
+ Context.SENSOR_SERVICE);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ mSensorManager = null;
+
+ mWakeLock.release();
+ mWakeLock = null;
+ }
+
+ /**
+ * Test cases.
+ */
+ public void testBatchAndFlush() throws InterruptedException {
+ List<Sensor> sensorList = mSensorManager.getSensorList(Sensor.TYPE_ALL);
+ for(Sensor sensor : sensorList) {
+ // skip all non-continuous mode sensors.
+ switch(sensor.getType()) {
+ case Sensor.TYPE_SIGNIFICANT_MOTION:
+ case Sensor.TYPE_STEP_COUNTER:
+ case Sensor.TYPE_STEP_DETECTOR:
+ case Sensor.TYPE_LIGHT:
+ case Sensor.TYPE_PROXIMITY:
+ case Sensor.TYPE_AMBIENT_TEMPERATURE:
+ continue;
+ }
+
+ TestSensorManager sensorManager = new TestSensorManager(this, mSensorManager, sensor);
+ sensorManager.registerBatchListener(SensorManager.SENSOR_DELAY_NORMAL, 0);
+
+ // wait for 25 events and call flush
+ sensorManager.getEvents(25);
+ sensorManager.waitForFlush();
+
+ sensorManager.unregisterListener();
+ }
+ }
+
+ /**
+ * Regress:
+ * - b/10641388
+ */
+ public void testAccelerometerDoesNotStopGyroscope() {
+ validateSensorCanBeStoppedIndependently(Sensor.TYPE_ACCELEROMETER, Sensor.TYPE_GYROSCOPE);
+ }
+
+ public void testAccelerometerDoesNotStopMagnetometer() {
+ validateSensorCanBeStoppedIndependently(
+ Sensor.TYPE_ACCELEROMETER,
+ Sensor.TYPE_MAGNETIC_FIELD);
+ }
+
+ public void testGyroscopeDoesNotStopAccelerometer() {
+ validateSensorCanBeStoppedIndependently(Sensor.TYPE_GYROSCOPE, Sensor.TYPE_ACCELEROMETER);
+ }
+
+ public void testGyroscopeDoesNotStopMagnetometer() {
+ validateSensorCanBeStoppedIndependently(Sensor.TYPE_GYROSCOPE, Sensor.TYPE_MAGNETIC_FIELD);
+ }
+
+ public void testMagnetometerDoesNotStopAccelerometer() {
+ validateSensorCanBeStoppedIndependently(
+ Sensor.TYPE_MAGNETIC_FIELD,
+ Sensor.TYPE_ACCELEROMETER);
+ }
+
+ public void testMagnetometerDoesNotStopGyroscope() {
+ validateSensorCanBeStoppedIndependently(Sensor.TYPE_MAGNETIC_FIELD, Sensor.TYPE_GYROSCOPE);
+ }
+
+ /**
+ * Private methods for sensor validation.
+ */
+ public void validateSensorCanBeStoppedIndependently(int sensorTypeTester, int sensorTypeTestee) {
+ // if any of the required sensors is not supported, skip the test
+ Sensor sensorTester = mSensorManager.getDefaultSensor(sensorTypeTester);
+ if(sensorTester == null) {
+ return;
+ }
+ Sensor sensorTestee = mSensorManager.getDefaultSensor(sensorTypeTestee);
+ if(sensorTestee == null) {
+ return;
+ }
+
+ TestSensorManager tester = new TestSensorManager(this, mSensorManager, sensorTester);
+ tester.registerListener(SensorManager.SENSOR_DELAY_NORMAL);
+
+ TestSensorManager testee = new TestSensorManager(this, mSensorManager, sensorTestee);
+ testee.registerBatchListener(
+ (int) TimeUnit.MICROSECONDS.convert(200, TimeUnit.MILLISECONDS),
+ SensorCtsHelper.getSecondsAsMicroSeconds(10));
+
+ testee.getEvents(10);
+ tester.getEvents(5);
+
+ tester.unregisterListener();
+ testee.getEvents(5);
+
+ // clean up
+ tester.close();
+ testee.close();
+ }
+}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java
index 10cabb8..e4041d6 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorMagneticFieldTest.java
@@ -19,12 +19,9 @@
import android.hardware.Sensor;
import android.hardware.SensorManager;
-import android.util.Log;
-
-import java.util.ArrayList;
-import java.util.Collections;
-
public class SensorMagneticFieldTest extends SensorCommonTests {
+ private final int AXIS_COUNT = 3;
+
@Override
protected int getMaxFrequencySupportedInuS() {
return 100000; // 10Hz
@@ -37,73 +34,15 @@
@Override
public void testEventValidity() {
- validateSensorEvent(
- 0 /*x-axis*/,
- 0 /*y-axis*/,
- 0 /*z-axis*/,
- SensorManager.MAGNETIC_FIELD_EARTH_MAX);
+ validateNormForSensorEvent(
+ SensorManager.MAGNETIC_FIELD_EARTH_MAX,
+ SensorManager.MAGNETIC_FIELD_EARTH_MIN,
+ AXIS_COUNT);
}
@Override
- public void testVarianceWhileStatic() {
- float THRESHOLD_IN_UT = SensorManager.MAGNETIC_FIELD_EARTH_MAX / 10;
- boolean result = mSensorManager.registerListener(
- mEventListener,
- mSensorUnderTest,
- this.getMaxFrequencySupportedInuS());
- assertTrue("registerListener", result);
- mEventListener.waitForEvents(100);
-
- SensorEventForTest[] events = mEventListener.getAllEvents();
- ArrayList<Float> deltaValuesX = new ArrayList<Float>();
- ArrayList<Float> deltaValuesY = new ArrayList<Float>();
- ArrayList<Float> deltaValuesZ = new ArrayList<Float>();
- for(int i = 1; i < events.length; ++i) {
- SensorEventForTest previousEvent = events[i-1];
- SensorEventForTest event = events[i];
-
- deltaValuesX.add(Math.abs(event.getX() - previousEvent.getX()));
- deltaValuesY.add(Math.abs(event.getY() - previousEvent.getY()));
- deltaValuesZ.add(Math.abs(event.getZ() - previousEvent.getZ()));
- }
-
- Collections.sort(deltaValuesX);
- float percentile95X = deltaValuesX.get(95);
- if(percentile95X > THRESHOLD_IN_UT) {
- for(float valueX : deltaValuesX) {
- Log.e(LOG_TAG, "Variance|X delta: " + valueX);
- }
- String message = String.format(
- "95%%Variance|X expected:%f, observed:%f",
- THRESHOLD_IN_UT,
- percentile95X);
- fail(message);
- }
-
- Collections.sort(deltaValuesY);
- float percentile95Y = deltaValuesY.get(95);
- if(percentile95Y > THRESHOLD_IN_UT) {
- for(float valueY : deltaValuesY) {
- Log.e(LOG_TAG, "Variance|Y delta: " + valueY);
- }
- String message = String.format(
- "95%%Variance|Y expected:%f, observed:%f",
- THRESHOLD_IN_UT,
- percentile95Y);
- fail(message);
- }
-
- Collections.sort(deltaValuesZ);
- float percentile95Z = deltaValuesZ.get(95);
- if(percentile95Z > THRESHOLD_IN_UT) {
- for(float valueZ : deltaValuesZ) {
- Log.e(LOG_TAG, "Variance|Z delta: " + valueZ);
- }
- String message = String.format(
- "95%%Variance|Z expected:%f, observed:%f",
- THRESHOLD_IN_UT,
- percentile95Z);
- fail(message);
- }
+ public void testStandardDeviationWhileStatic() {
+ final float STANDARD_DEVIATION = 2f; // uT
+ validateStandardDeviationWhileStatic(STANDARD_DEVIATION, AXIS_COUNT);
}
}
diff --git a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
index 4af2a45..a0f1a96 100644
--- a/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
+++ b/tests/tests/hardware/src/android/hardware/cts/SensorTest.java
@@ -23,10 +23,10 @@
import android.content.Context;
import android.content.pm.PackageManager;
-import android.hardware.FlushCompleteListener;
import android.hardware.Sensor;
import android.hardware.SensorEvent;
import android.hardware.SensorEventListener;
+import android.hardware.SensorEventListener2;
import android.hardware.SensorManager;
import android.hardware.TriggerEvent;
import android.hardware.TriggerEventListener;
@@ -149,7 +149,8 @@
}
final CountDownLatch eventReceived = new CountDownLatch(25);
- SensorEventListener listener = new SensorEventListener() {
+ final CountDownLatch flushReceived = new CountDownLatch(1);
+ SensorEventListener2 listener = new SensorEventListener2() {
@Override
public void onSensorChanged(SensorEvent event) {
eventReceived.countDown();
@@ -158,22 +159,18 @@
@Override
public void onAccuracyChanged(Sensor sensor, int accuracy) {
}
- };
- final CountDownLatch flushReceived = new CountDownLatch(1);
- FlushCompleteListener flushCompleteListener = new FlushCompleteListener() {
@Override
public void onFlushCompleted(Sensor sensor) {
flushReceived.countDown();
}
};
boolean result = mSensorManager.registerListener(listener, sensor,
- SensorManager.SENSOR_DELAY_NORMAL, 10000000, 0,
- flushCompleteListener);
+ SensorManager.SENSOR_DELAY_NORMAL, 10000000);
assertTrue(result);
// Wait for 25 events and call flush.
eventReceived.await();
- result = mSensorManager.flush(sensor);
+ result = mSensorManager.flush(listener);
assertTrue(result);
flushReceived.await();
mSensorManager.unregisterListener(listener);
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
new file mode 100644
index 0000000..5abdd06
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/SensorCtsHelper.java
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.os.Environment;
+import android.util.Log;
+
+import java.io.DataOutputStream;
+import java.io.IOException;
+
+import java.text.SimpleDateFormat;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Date;
+
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Set of static helper methods for CTS tests.
+ */
+public class SensorCtsHelper {
+ /**
+ * This is an static class.
+ */
+ private SensorCtsHelper() {}
+
+ public static <TValue extends Comparable> TValue get95PercentileValue(
+ Collection<TValue> collection) {
+ validateCollection(collection);
+
+ ArrayList<TValue> arrayCopy = new ArrayList<TValue>(collection);
+ Collections.sort(arrayCopy);
+
+ // zero-based array index
+ int arrayIndex = (int)(arrayCopy.size() * 0.95) - 1;
+ if(arrayIndex < 0) {
+ arrayIndex = 0;
+ }
+
+ return arrayCopy.get(arrayIndex);
+ }
+
+ // TODO: are there any internal libraries for this?
+ public static <TValue extends Number> double getMean(Collection<TValue> collection) {
+ validateCollection(collection);
+
+ double sum = 0.0;
+ for(TValue value : collection) {
+ sum += value.doubleValue();
+ }
+ return sum / collection.size();
+ }
+
+ public static <TValue extends Number> double getVariance(Collection<TValue> collection) {
+ validateCollection(collection);
+
+ double mean = getMean(collection);
+ ArrayList<Double> squaredDifferences = new ArrayList<Double>();
+ for(TValue value : collection) {
+ double difference = mean - value.doubleValue();
+ squaredDifferences.add(Math.pow(difference, 2));
+ }
+
+ double variance = getMean(squaredDifferences);
+ return variance;
+ }
+
+ public static <TValue extends Number> double getStandardDeviation(Collection<TValue> collection) {
+ validateCollection(collection);
+
+ double variance = getVariance(collection);
+ return Math.sqrt(variance);
+ }
+
+ /**
+ * Gets the jitter values associated with a set of sensor events.
+ *
+ * @param events The events to use to obtain the jittering information.
+ * @param jitterValues The Collection that will contain the computed jitter values.
+ * @return The mean of the jitter Values.
+ */
+ public static double getJitterMean(
+ TestSensorManager.SensorEventForTest events[],
+ Collection<Double> jitterValues) {
+ ArrayList<Long> timestampDelayValues = new ArrayList<Long>();
+ double averageTimestampDelay = SensorCtsHelper.getAverageTimestampDelayWithValues(events,
+ timestampDelayValues);
+ for(long frequency : timestampDelayValues) {
+ jitterValues.add(Math.abs(averageTimestampDelay - frequency));
+ }
+
+ double jitterMean = SensorCtsHelper.getMean(timestampDelayValues);
+ return jitterMean;
+ }
+
+ /**
+ * Gets the frequency values associated with a set of sensor events.
+ *
+ * @param events The events to use to obtain the frequency information.
+ * @param timestampDelayValues The Collection that will contain the computed frequency values.
+ * @return The mean of the frequency values.
+ */
+ public static double getAverageTimestampDelayWithValues(
+ TestSensorManager.SensorEventForTest events[],
+ Collection<Long> timestampDelayValues) {
+ for(int i = 1; i < events.length; ++i) {
+ long previousTimestamp = events[i-1].timestamp;
+ long timestamp = events[i].timestamp;
+ timestampDelayValues.add(timestamp - previousTimestamp);
+ }
+
+ double timestampDelayMean = SensorCtsHelper.getMean(timestampDelayValues);
+ return timestampDelayMean;
+ }
+
+ public static int getSecondsAsMicroSeconds(int seconds) {
+ return (int) TimeUnit.MICROSECONDS.convert(seconds, TimeUnit.SECONDS);
+ }
+
+ /**
+ * NOTE: The bug report is usually written to /sdcard/Downloads
+ */
+ public static void collectBugreport(String collectorId)
+ throws IOException, InterruptedException {
+ String commands[] = new String[] {
+ "dumpstate",
+ "dumpsys",
+ "logcat -d -v threadtime",
+ "exit"
+ };
+
+ SimpleDateFormat dateFormat = new SimpleDateFormat("M-d-y_H:m:s.S");
+ String outputFile = String.format(
+ "%s/%s_%s",
+ collectorId,
+ Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS),
+ dateFormat.format(new Date()));
+
+ DataOutputStream processOutput = null;
+ try {
+ Process process = Runtime.getRuntime().exec("/system/bin/sh -");
+ processOutput = new DataOutputStream(process.getOutputStream());
+
+ for(String command : commands) {
+ processOutput.writeBytes(String.format("%s >> %s\n", command, outputFile));
+ }
+
+ processOutput.flush();
+ process.waitFor();
+
+ Log.d(collectorId, String.format("Bug-Report collected at: %s", outputFile));
+ } finally {
+ if(processOutput != null) {
+ try {
+ processOutput.close();
+ } catch(IOException e) {}
+ }
+ }
+ }
+
+ public static void performOperationInThreads(int numberOfThreadsToUse, Runnable operation)
+ throws InterruptedException {
+ ArrayList<Thread> threads = new ArrayList<Thread>();
+ for(int i = 0; i < numberOfThreadsToUse; ++i) {
+ threads.add(new Thread(operation));
+ }
+
+ while(!threads.isEmpty()) {
+ Thread thread = threads.remove(0);
+ thread.join();
+ }
+ }
+
+ /**
+ * Private helpers
+ */
+ private static void validateCollection(Collection collection) {
+ if(collection == null || collection.size() == 0) {
+ throw new IllegalStateException("Collection cannot be null or empty");
+ }
+ }
+}
+
+
+
+
diff --git a/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
new file mode 100644
index 0000000..9a95e9b
--- /dev/null
+++ b/tests/tests/hardware/src/android/hardware/cts/helpers/TestSensorManager.java
@@ -0,0 +1,247 @@
+/*
+ * Copyright (C) 2008 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;
+
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener2;
+import android.hardware.SensorManager;
+import android.hardware.TriggerEvent;
+import android.hardware.TriggerEventListener;
+
+import java.io.Closeable;
+
+import java.util.concurrent.ConcurrentLinkedDeque;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+import junit.framework.Assert;
+
+/**
+ * Test class to wrap SensorManager with verifications and test checks.
+ * This class allows to perform operations in the Sensor Manager and performs all the expected test
+ * verification on behalf of th owner.
+ * An object can be used to quickly writing tests that focus on the scenario that needs to be verified,
+ * and not in the implicit verifications that need to take place at any step.
+ */
+public class TestSensorManager implements Closeable {
+ private final int WAIT_TIMEOUT_IN_SECONDS = 30;
+
+ private Assert mAssert;
+ private SensorManager mSensorManager;
+ private Sensor mSensorUnderTest;
+ private TestSensorListener mEventListener;
+
+ public TestSensorManager(Assert assertionObject, SensorManager sensorManager, Sensor sensor) {
+ mAssert = assertionObject;
+ mSensorManager = sensorManager;
+ mSensorUnderTest = sensor;
+
+ mEventListener = new TestSensorListener();
+ }
+
+ public void close() {
+ this.unregisterListener();
+ mEventListener = null;
+ mSensorUnderTest = null;
+ }
+
+ public SensorManager getUnderlyingSensorManager() {
+ return mSensorManager;
+ }
+
+ public Sensor getSensorUnderTest() {
+ return mSensorUnderTest;
+ }
+
+ public void registerListener(int delay, String debugInfo) {
+ mAssert.assertTrue(
+ "registerListener| " + debugInfo,
+ mSensorManager.registerListener(mEventListener, mSensorUnderTest, delay));
+ }
+
+ public void registerListener(int delay) {
+ registerListener(delay, "");
+ }
+
+ public void registerBatchListener(int delay, int reportLatency, String debugInfo) {
+ boolean result = mSensorManager.registerListener(
+ mEventListener,
+ mSensorUnderTest,
+ delay,
+ reportLatency);
+ mAssert.assertTrue("registerBatchListener| " + debugInfo, result);
+ }
+
+ public void registerBatchListener(int delay, int reportLatency) {
+ registerBatchListener(delay, reportLatency, "");
+ }
+
+ public void unregisterListener() {
+ mSensorManager.unregisterListener(mEventListener, mSensorUnderTest);
+ }
+
+ public SensorEventForTest[] getEvents(int count, String debugInfo) {
+ mEventListener.waitForEvents(count, debugInfo);
+ SensorEventForTest[] events = mEventListener.getAllEvents();
+ mEventListener.clearEvents();
+
+ return events;
+ }
+
+ public SensorEventForTest[] getEvents(int count) {
+ return this.getEvents(count, "");
+ }
+
+ public SensorEventForTest[] collectEvents(
+ int collectionDelay,
+ int eventCount,
+ String debugInfo) {
+ this.registerListener(collectionDelay, debugInfo);
+ SensorEventForTest[] events = this.getEvents(eventCount, debugInfo);
+ this.unregisterListener();
+
+ return events;
+ }
+
+ public SensorEventForTest[] collectEvents(int collectionDelay, int eventCount) {
+ return this.collectEvents(collectionDelay, eventCount, "");
+ }
+
+ public SensorEventForTest[] collectBatchEvents(
+ int collectionDelay,
+ int batchReportLatency,
+ int eventCount,
+ String debugInfo) {
+ this.registerBatchListener(collectionDelay, batchReportLatency, debugInfo);
+ SensorEventForTest[] events = this.getEvents(eventCount, debugInfo);
+ this.unregisterListener();
+
+ return events;
+ }
+
+ public SensorEventForTest[] collectBatchEvents(
+ int collectionDelay,
+ int batchReportLatency,
+ int eventCount) {
+ return this.collectBatchEvents(collectionDelay, batchReportLatency, eventCount, "");
+ }
+
+ public void waitForFlush() throws InterruptedException {
+ mAssert.assertTrue(
+ String.format("flush| sensorType:%d", mSensorUnderTest.getType()),
+ mSensorManager.flush(mEventListener));
+ mEventListener.waitForFlushComplete();
+ }
+
+ /**
+ * Definition of support test classes.
+ */
+ public class SensorEventForTest {
+ public final Sensor sensor;
+ public final long timestamp;
+ public final int accuracy;
+ public final float values[];
+
+ public SensorEventForTest(SensorEvent event) {
+ values = new float[event.values.length];
+ System.arraycopy(event.values, 0, values, 0, event.values.length);
+
+ sensor = event.sensor;
+ timestamp = event.timestamp;
+ accuracy = event.accuracy;
+ }
+ }
+
+ private class TestSensorListener implements SensorEventListener2 {
+ private final ConcurrentLinkedDeque<SensorEventForTest> mSensorEventsList =
+ new ConcurrentLinkedDeque<SensorEventForTest>();
+ private volatile CountDownLatch mEventLatch;
+ private volatile CountDownLatch mFlushLatch;
+
+ @Override
+ public void onSensorChanged(SensorEvent event) {
+ CountDownLatch latch = mEventLatch;
+ if(latch != null) {
+ // copy the event because there is no better way to do this in the platform
+ mSensorEventsList.addLast(new SensorEventForTest(event));
+ latch.countDown();
+ }
+ }
+
+ @Override
+ public void onAccuracyChanged(Sensor sensor, int accuracy) {
+ }
+
+ @Override
+ public void onFlushCompleted(Sensor sensor) {
+ CountDownLatch latch = mFlushLatch;
+ mFlushLatch = new CountDownLatch(1);
+
+ if(latch != null) {
+ latch.countDown();
+ }
+ }
+
+ public void waitForFlushComplete() throws InterruptedException {
+ CountDownLatch latch = mFlushLatch;
+ mAssert.assertTrue(
+ "WaitForFlush",
+ latch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS));
+ }
+
+ public void waitForEvents(int eventCount) {
+ waitForEvents(eventCount, "");
+ }
+
+ public void waitForEvents(int eventCount, String timeoutInfo) {
+ mEventLatch = new CountDownLatch(eventCount);
+ try {
+ boolean awaitCompleted = mEventLatch.await(WAIT_TIMEOUT_IN_SECONDS, TimeUnit.SECONDS);
+ // TODO: can we collect bug reports on error based only if needed? env var?
+
+ String assertMessage = String.format(
+ "WaitForEvents| count:%d, available:%d, %s",
+ eventCount,
+ mSensorEventsList.size(),
+ timeoutInfo);
+ mAssert.assertTrue(assertMessage, awaitCompleted);
+ } catch(InterruptedException e) {
+ } finally {
+ mEventLatch = null;
+ }
+ }
+
+ public SensorEventForTest getLastEvent() {
+ return mSensorEventsList.getLast();
+ }
+
+ public SensorEventForTest[] getAllEvents() {
+ return mSensorEventsList.toArray(new SensorEventForTest[0]);
+ }
+
+ public void clearEvents() {
+ mSensorEventsList.clear();
+ }
+ }
+
+ public class TestTriggerListener extends TriggerEventListener {
+ @Override
+ public void onTrigger(TriggerEvent event) {
+ }
+ }
+}
diff --git a/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
new file mode 100644
index 0000000..4f8fb62
--- /dev/null
+++ b/tests/tests/media/src/android/media/cts/MediaCodecCapabilitiesTest.java
@@ -0,0 +1,125 @@
+/*
+ * Copyright (C) 2013 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 android.media.MediaCodecInfo;
+import android.media.MediaCodecInfo.CodecCapabilities;
+import android.media.MediaCodecInfo.CodecProfileLevel;
+import android.media.MediaCodecList;
+import android.media.MediaPlayer;
+
+import android.util.Log;
+
+/**
+ * Basic sanity test of data returned by MediaCodeCapabilities.
+ */
+public class MediaCodecCapabilitiesTest extends MediaPlayerTestBase {
+
+ private static final String TAG = "MediaCodecCapabilitiesTest";
+ private static final String AVC_MIME = "video/avc";
+ private static final int PLAY_TIME_MS = 30000;
+
+ public void testAvcBaseline1() throws Exception {
+ if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
+ CodecProfileLevel.AVCLevel1)) {
+ throw new RuntimeException("AVCLevel1 support is required by CDD");
+ }
+ // We don't have a test stream, but at least we're testing
+ // that supports() returns true for something.
+ }
+
+ public void testAvcBaseline12() throws Exception {
+ if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
+ CodecProfileLevel.AVCLevel12)) {
+ Log.i(TAG, "AvcBaseline12 not supported");
+ return; // TODO: Can we make this mandatory?
+ }
+ playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ + "&itag=160&source=youtube&user=android-device-test"
+ + "&sparams=ip,ipbits,expire,id,itag,source,user"
+ + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ + "&signature=341692D20FACCAE25B90EA2C131EA6ADCD8E2384."
+ + "9EB08C174BE401AAD20FB85EE4DBA51A2882BB60"
+ + "&key=test_key1", 256, 144, PLAY_TIME_MS);
+ }
+
+ public void testAvcBaseline30() throws Exception {
+ if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileBaseline,
+ CodecProfileLevel.AVCLevel3)) {
+ Log.i(TAG, "AvcBaseline30 not supported");
+ return;
+ }
+ playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ + "&itag=18&source=youtube&user=android-device-test"
+ + "&sparams=ip,ipbits,expire,id,itag,source,user"
+ + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ + "&signature=8701A45F6422229D46ABB25A22E2C00C94024606."
+ + "08BCDF16C3F744C49D4C8A8AD1C38B3DC1810918"
+ + "&key=test_key1", 640, 360, PLAY_TIME_MS);
+ }
+
+ public void testAvcHigh31() throws Exception {
+ if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
+ CodecProfileLevel.AVCLevel31)) {
+ Log.i(TAG, "AvcHigh31 not supported");
+ return;
+ }
+ playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ + "&itag=22&source=youtube&user=android-device-test"
+ + "&sparams=ip,ipbits,expire,id,itag,source,user"
+ + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ + "&signature=42969CA8F7FFAE432B7135BC811F96F7C4172C3F."
+ + "1A8A92EA714C1B7C98A05DDF2DE90854CDD7638B"
+ + "&key=test_key1", 1280, 720, PLAY_TIME_MS);
+
+ }
+
+ public void testAvcHigh40() throws Exception {
+ if (!supports(AVC_MIME, CodecProfileLevel.AVCProfileHigh,
+ CodecProfileLevel.AVCLevel4)) {
+ Log.i(TAG, "AvcHigh40 not supported");
+ return;
+ }
+ playVideoWithRetries("http://redirector.c.youtube.com/videoplayback?id=271de9756065677e"
+ + "&itag=37&source=youtube&user=android-device-test"
+ + "&sparams=ip,ipbits,expire,id,itag,source,user"
+ + "&ip=0.0.0.0&ipbits=0&expire=999999999999999999"
+ + "&signature=7C3BBFB2F493E1BC396B6D31DDAF2E1367624487."
+ + "64197F3BB46039669E912297DCD68D1FB2811D9F"
+ + "&key=test_key1", 1920, 1080, PLAY_TIME_MS);
+ }
+
+ private boolean supports(String mimeType, int profile, int level) {
+ int numCodecs = MediaCodecList.getCodecCount();
+ for (int i = 0; i < numCodecs; i++) {
+ MediaCodecInfo codecInfo = MediaCodecList.getCodecInfoAt(i);
+ if (codecInfo.isEncoder()) {
+ continue;
+ }
+
+ CodecCapabilities capabilities = codecInfo.getCapabilitiesForType(mimeType);
+ for (CodecProfileLevel profileLevel : capabilities.profileLevels) {
+ if (profileLevel.profile == profile
+ && profileLevel.level >= level) {
+ return true;
+ }
+ }
+ }
+
+ return false;
+ }
+
+}