Sensor direct report HardwareBuffer channel CTS test
Complete test case for sensor direct report Java API and NDK API with
HardwareBuffer/AHardwareBuffer (shared memory allocated by gralloc)
direct channel.
Bug: 30985702
Test: cts-tradefed run cts --module CtsSensorTestCases
--test android.hardware.cts.SensorDirectReportTest
Test: cts-tradefed run cts --module CtsSensorTestCases
--test android.hardware.cts.SensorNativeTest
Change-Id: Ic3806f0886220403318be7355f8a1c1d54cc9b5e
diff --git a/tests/sensor/Android.mk b/tests/sensor/Android.mk
index 294e817..d275b1f 100644
--- a/tests/sensor/Android.mk
+++ b/tests/sensor/Android.mk
@@ -49,7 +49,8 @@
LOCAL_SRC_FILES := \
jni/SensorTest.cpp \
jni/SensorTestCases.cpp \
- jni/android_view_cts_SensorNativeTest.cpp \
+ jni/android_hardware_cts_SensorDirectReportTest.cpp \
+ jni/android_hardware_cts_SensorNativeTest.cpp \
jni/nativeTestHelper.cpp
LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
diff --git a/tests/sensor/jni/SensorTest.cpp b/tests/sensor/jni/SensorTest.cpp
index 69cbdd6..eb2bfe8 100644
--- a/tests/sensor/jni/SensorTest.cpp
+++ b/tests/sensor/jni/SensorTest.cpp
@@ -158,6 +158,30 @@
success = true;
break;
}
+ case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER: {
+ AHardwareBuffer_Desc desc = {
+ .width = static_cast<uint32_t>(size),
+ .height = 1,
+ .layers = 1,
+ .usage0 = AHARDWAREBUFFER_USAGE0_SENSOR_DIRECT_DATA
+ | AHARDWAREBUFFER_USAGE0_CPU_READ_OFTEN,
+ .usage1 = 0,
+ .format = AHARDWAREBUFFER_FORMAT_BLOB
+ };
+
+ // allocate
+ if (AHardwareBuffer_allocate(&desc, &mHardwareBuffer) == 0) {
+ // lock
+ if (AHardwareBuffer_lock(mHardwareBuffer, AHARDWAREBUFFER_USAGE0_CPU_READ,
+ -1, nullptr, reinterpret_cast<void **>(&mBuffer)) == 0) {
+ if (mBuffer != nullptr) {
+ mSize = size;
+ success = true;
+ }
+ }
+ }
+ break;
+ }
default:
break;
}
@@ -185,10 +209,23 @@
mSize = 0;
break;
}
+ case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER: {
+ if (mHardwareBuffer != nullptr) {
+ if (mBuffer != nullptr) {
+ int32_t fence = -1;
+ AHardwareBuffer_unlock(mHardwareBuffer, &fence);
+ mBuffer = nullptr;
+ }
+ AHardwareBuffer_release(mHardwareBuffer);
+ mHardwareBuffer = nullptr;
+ }
+ mSize = 0;
+ break;
+ }
default:
break;
}
- if (mSharedMemoryFd > 0 || mSize != 0 || mBuffer != nullptr) {
+ if (mSharedMemoryFd > 0 || mSize != 0 || mBuffer != nullptr || mHardwareBuffer != nullptr) {
ALOGE("TestSharedMemory %p not properly destructed: "
"type %d, shared_memory_fd %d, hardware_buffer %p, size %zu, buffer %p",
this, static_cast<int>(mType), mSharedMemoryFd, mHardwareBuffer, mSize, mBuffer);
diff --git a/tests/sensor/jni/SensorTest.h b/tests/sensor/jni/SensorTest.h
index 2488db8..7abd1e9 100644
--- a/tests/sensor/jni/SensorTest.h
+++ b/tests/sensor/jni/SensorTest.h
@@ -43,7 +43,8 @@
// tests
void testInitialized(JNIEnv *env);
void testInvalidParameter(JNIEnv *env);
- void testGyroscopeSharedMemoryDirectReport(JNIEnv *env);
+ void testDirectReport(JNIEnv *env, int32_t sensorType, int32_t channelType, int32_t rateLevel);
+
private:
std::unique_ptr<TestSensorManager> mManager;
};
diff --git a/tests/sensor/jni/SensorTestCases.cpp b/tests/sensor/jni/SensorTestCases.cpp
index 9f935f6..713eabf 100644
--- a/tests/sensor/jni/SensorTestCases.cpp
+++ b/tests/sensor/jni/SensorTestCases.cpp
@@ -131,26 +131,40 @@
ASSERT_EQ(ASensor_getHighestDirectReportRateLevel(nullptr), ASENSOR_DIRECT_RATE_STOP);
}
-// Test direct report of gyroscope at normal rate level through ashmem direct channel
-void SensorTest::testGyroscopeSharedMemoryDirectReport(JNIEnv* env) {
- constexpr int type = ASENSOR_TYPE_GYROSCOPE;
+// Test sensor direct report functionality
+void SensorTest::testDirectReport(JNIEnv* env, int32_t sensorType, int32_t channelType, int32_t rateLevel) {
constexpr size_t kEventSize = sizeof(ASensorEvent);
constexpr size_t kNEvent = 500;
constexpr size_t kMemSize = kEventSize * kNEvent;
- TestSensor sensor = mManager->getDefaultSensor(type);
+ // value check criterion
+ constexpr float GRAVITY_MIN = 9.81f - 0.5f;
+ constexpr float GRAVITY_MAX = 9.81f + 0.5f;
+ constexpr float GYRO_MAX = 0.1f; // ~5 dps
- if (sensor.getHighestDirectReportRateLevel() == ASENSOR_DIRECT_RATE_STOP
- || !sensor.isDirectChannelTypeSupported(ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY)) {
- // does not declare support
+ constexpr float RATE_NORMAL_NOMINAL = 50;
+ constexpr float RATE_FAST_NOMINAL = 200;
+ constexpr float RATE_VERY_FAST_NOMINAL = 800;
+
+ TestSensor sensor = mManager->getDefaultSensor(sensorType);
+ if (!sensor.isValid()
+ || sensor.getHighestDirectReportRateLevel() < rateLevel
+ || !sensor.isDirectChannelTypeSupported(channelType)) {
+ // no sensor of type sensorType or it does not declare support of channelType or rateLevel
return;
}
- std::unique_ptr<TestSharedMemory>
- mem(TestSharedMemory::create(ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, kMemSize));
+ std::unique_ptr<TestSharedMemory> mem(TestSharedMemory::create(channelType, kMemSize));
ASSERT_NE(mem, nullptr);
ASSERT_NE(mem->getBuffer(), nullptr);
- ASSERT_GT(mem->getSharedMemoryFd(), 0);
+ switch (channelType) {
+ case ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY:
+ ASSERT_GT(mem->getSharedMemoryFd(), 0);
+ break;
+ case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER:
+ ASSERT_NOT_NULL(mem->getHardwareBuffer());
+ break;
+ }
char* buffer = mem->getBuffer();
// fill memory with data
@@ -168,24 +182,56 @@
}
int32_t eventToken;
- eventToken = mManager->configureDirectReport(sensor, channel, ASENSOR_DIRECT_RATE_NORMAL);
+ eventToken = mManager->configureDirectReport(sensor, channel, rateLevel);
usleep(1500000); // sleep 1 sec for data, plus 0.5 sec for initialization
auto events = mem->parseEvents();
- // allowed to be between 55% and 220% of nominal freq (50Hz)
- ASSERT_GT(events.size(), 50 / 2);
- ASSERT_LT(events.size(), static_cast<size_t>(110*1.5));
+ // find norminal rate
+ float nominalFreq = 0.f;
+ float nominalTestTimeSec = 1.f;
+ float maxTestTimeSec = 1.5f;
+ switch (rateLevel) {
+ case ASENSOR_DIRECT_RATE_NORMAL:
+ nominalFreq = RATE_NORMAL_NOMINAL;
+ break;
+ case ASENSOR_DIRECT_RATE_FAST:
+ nominalFreq = RATE_FAST_NOMINAL;
+ break;
+ case ASENSOR_DIRECT_RATE_VERY_FAST:
+ nominalFreq = RATE_VERY_FAST_NOMINAL;
+ break;
+ }
+
+ // allowed to be between 55% and 220% of nominal freq
+ ASSERT_GT(events.size(), static_cast<size_t>(nominalFreq * 0.55f * nominalTestTimeSec));
+ ASSERT_LT(events.size(), static_cast<size_t>(nominalFreq * 2.2f * maxTestTimeSec));
int64_t lastTimestamp = 0;
for (auto &e : events) {
- ASSERT_EQ(e.type, type);
+ ASSERT_EQ(e.type, sensorType);
ASSERT_EQ(e.sensor, eventToken);
ASSERT_GT(e.timestamp, lastTimestamp);
- ASensorVector &gyro = e.vector;
- double gyroNorm = std::sqrt(gyro.x * gyro.x + gyro.y * gyro.y + gyro.z * gyro.z);
- // assert not drifting
- ASSERT_TRUE(gyroNorm < 0.1); // < ~5 degree/sa
+ // type specific value check
+ switch(sensorType) {
+ case ASENSOR_TYPE_ACCELEROMETER: {
+ ASensorVector &acc = e.vector;
+ double accNorm = std::sqrt(acc.x * acc.x + acc.y * acc.y + acc.z * acc.z);
+ if (accNorm > GRAVITY_MAX || accNorm < GRAVITY_MIN) {
+ ALOGE("Gravity norm = %f", accNorm);
+ }
+ ASSERT_GE(accNorm, GRAVITY_MIN);
+ ASSERT_LE(accNorm, GRAVITY_MAX);
+ break;
+ }
+ case ASENSOR_TYPE_GYROSCOPE: {
+ ASensorVector &gyro = e.vector;
+ double gyroNorm = std::sqrt(gyro.x * gyro.x + gyro.y * gyro.y + gyro.z * gyro.z);
+ // assert not drifting
+ ASSERT_LE(gyroNorm, GYRO_MAX); // < ~2.5 degree/s
+ break;
+ }
+ }
lastTimestamp = e.timestamp;
}
diff --git a/tests/sensor/jni/android_hardware_cts_SensorDirectReportTest.cpp b/tests/sensor/jni/android_hardware_cts_SensorDirectReportTest.cpp
new file mode 100644
index 0000000..2616ff9
--- /dev/null
+++ b/tests/sensor/jni/android_hardware_cts_SensorDirectReportTest.cpp
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"nclude
+ * <android/hardware_buffer_jni.h>);
+ * 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.
+ *
+ */
+
+#include "nativeTestHelper.h"
+#include "SensorTest.h"
+
+#include <android/hardware_buffer_jni.h>
+
+namespace {
+jboolean readHardwareBuffer(JNIEnv* env, jclass,
+ jobject hardwareBufferObj, jbyteArray buffer, jint srcOffset, jint destOffset, jint count) {
+ if (hardwareBufferObj == nullptr || buffer == nullptr ||
+ srcOffset < 0 || destOffset < 0 || count <= 0) {
+ return false;
+ }
+
+ if (env->GetArrayLength(buffer) < destOffset + count) {
+ ALOGE("Byte array is not large enough.");
+ return false;
+ }
+
+ AHardwareBuffer *hardwareBuffer = AHardwareBuffer_fromHardwareBuffer(env, hardwareBufferObj);
+ if (hardwareBuffer == nullptr) {
+ ALOGE("Cannot get AHardwareBuffer from HardwareBuffer");
+ return false;
+ }
+
+ void *address;
+ int32_t fence = -1;
+ jboolean ret = false;
+ if (AHardwareBuffer_lock(hardwareBuffer, AHARDWAREBUFFER_USAGE0_CPU_READ,
+ fence, nullptr, &address) == 0) {
+ if (address != nullptr) {
+ env->SetByteArrayRegion(
+ buffer, destOffset, count, reinterpret_cast<const jbyte *>(address));
+ ret = true;
+ } else {
+ ALOGE("AHardwareBuffer locked but address is invalid");
+ }
+ AHardwareBuffer_unlock(hardwareBuffer, &fence);
+ }
+ return ret;
+}
+
+JNINativeMethod gMethods[] = {
+ { "nativeReadHardwareBuffer", "(Landroid/hardware/HardwareBuffer;[BIII)Z",
+ (void *) readHardwareBuffer},
+};
+} // unamed namespace
+
+int register_android_hardware_cts_SensorDirectReportTest(JNIEnv* env) {
+ jclass clazz = env->FindClass("android/hardware/cts/SensorDirectReportTest");
+ return env->RegisterNatives(clazz, gMethods,
+ sizeof(gMethods) / sizeof(JNINativeMethod));
+}
diff --git a/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp b/tests/sensor/jni/android_hardware_cts_SensorNativeTest.cpp
similarity index 62%
rename from tests/sensor/jni/android_view_cts_SensorNativeTest.cpp
rename to tests/sensor/jni/android_hardware_cts_SensorNativeTest.cpp
index e138ab5..40a5ff7 100644
--- a/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp
+++ b/tests/sensor/jni/android_hardware_cts_SensorNativeTest.cpp
@@ -21,6 +21,8 @@
namespace {
using android::SensorTest::SensorTest;
+#define RETURN_ON_EXCEPTION() do { if (env->ExceptionCheck()) { return;} } while(false)
+
jlong setUp(JNIEnv*, jclass) {
SensorTest *test = new SensorTest();
if (test != nullptr) {
@@ -35,16 +37,34 @@
void test(JNIEnv* env, jclass, jlong instance) {
SensorTest *test = reinterpret_cast<SensorTest *>(instance);
- ASSERT_NE(test, nullptr);
+ ASSERT_NOT_NULL(test);
// test if SensorTest is intialized
+ ALOGI("testInitialized");
test->testInitialized(env);
+ RETURN_ON_EXCEPTION();
// test if SensorTest is intialized
+ ALOGI("testInvalidParameter");
test->testInvalidParameter(env);
+ RETURN_ON_EXCEPTION();
- // test gyro direct report using shared memory buffer
- test->testGyroscopeSharedMemoryDirectReport(env);
+ // test sensor direct report
+ std::vector<int32_t> sensorTypes ={ASENSOR_TYPE_ACCELEROMETER, ASENSOR_TYPE_GYROSCOPE};
+ std::vector<int32_t> rates = {
+ ASENSOR_DIRECT_RATE_NORMAL, ASENSOR_DIRECT_RATE_FAST, ASENSOR_DIRECT_RATE_VERY_FAST};
+ std::vector<int32_t> channelTypes =
+ {ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY};
+ for (auto s : sensorTypes) {
+ for (auto c : channelTypes) {
+ for (auto r : rates) {
+ ALOGI("testDirectReport: sensorType = %d, channelType = %d, ratelevel = %d",
+ s, c, r);
+ test->testDirectReport(env, s, c, r);
+ RETURN_ON_EXCEPTION();
+ }
+ }
+ }
}
JNINativeMethod gMethods[] = {
@@ -57,8 +77,7 @@
};
} // unamed namespace
-int register_android_view_cts_SensorNativeTest(JNIEnv* env)
-{
+int register_android_hardware_cts_SensorNativeTest(JNIEnv* env) {
jclass clazz = env->FindClass("android/hardware/cts/SensorNativeTest");
return env->RegisterNatives(clazz, gMethods,
sizeof(gMethods) / sizeof(JNINativeMethod));
diff --git a/tests/sensor/jni/nativeTestHelper.cpp b/tests/sensor/jni/nativeTestHelper.cpp
index 7c1b378..3c7df9a 100644
--- a/tests/sensor/jni/nativeTestHelper.cpp
+++ b/tests/sensor/jni/nativeTestHelper.cpp
@@ -19,7 +19,8 @@
#include <cstdlib>
#include <cstring>
-extern int register_android_view_cts_SensorNativeTest(JNIEnv* env);
+extern int register_android_hardware_cts_SensorNativeTest(JNIEnv* env);
+extern int register_android_hardware_cts_SensorDirectReportTest(JNIEnv* env);
void fail(JNIEnv* env, const char* format, ...) {
va_list args;
@@ -41,7 +42,10 @@
if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
return JNI_ERR;
}
- if (register_android_view_cts_SensorNativeTest(env)) {
+ if (register_android_hardware_cts_SensorNativeTest(env)) {
+ return JNI_ERR;
+ }
+ if (register_android_hardware_cts_SensorDirectReportTest(env)) {
return JNI_ERR;
}
return JNI_VERSION_1_4;
diff --git a/tests/sensor/jni/nativeTestHelper.h b/tests/sensor/jni/nativeTestHelper.h
index f9e6106..e465667 100644
--- a/tests/sensor/jni/nativeTestHelper.h
+++ b/tests/sensor/jni/nativeTestHelper.h
@@ -44,6 +44,8 @@
ASSERT((a) < (b), "assert failed on (" #a " < " #b ") at " __FILE__ ":%d", __LINE__)
#define ASSERT_LE(a, b) \
ASSERT((a) <= (b), "assert failed on (" #a " <= " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NOT_NULL(a) \
+ ASSERT((a) != nullptr, "assert failed on isNotNull(" #a ") at " __FILE__ ":%d", __LINE__)
#define ASSERT_EMPTY_CSTR(a) do { \
const char *tmp = a; \
ASSERT(tmp != nullptr, \
diff --git a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
index edc71aa..547f44c 100644
--- a/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorDirectReportTest.java
@@ -57,9 +57,9 @@
private static final float FREQ_UPPER_BOUND = 2.2f;
// sensor reading assumption
- private static final float GRAVITY_MIN = 9.81f - 1.f;
- private static final float GRAVITY_MAX = 9.81f + 1.f;
- private static final float GYRO_NORM_MAX = 0.05f;
+ private static final float GRAVITY_MIN = 9.81f - 0.5f;
+ private static final float GRAVITY_MAX = 9.81f + 0.5f;
+ private static final float GYRO_NORM_MAX = 0.1f;
// test constants
private static final int REST_PERIOD_BEFORE_TEST_MILLISEC = 3000;
@@ -69,6 +69,9 @@
private static final int SHARED_MEMORY_SIZE = 2000 * SENSORS_EVENT_SIZE;
private static final float MERCY_FACTOR = 0.1f;
+ private static native boolean nativeReadHardwareBuffer(HardwareBuffer hardwareBuffer,
+ byte[] buffer, int srcOffset, int destOffset, int count);
+
private boolean mNeedMemoryFile;
private MemoryFile mMemoryFile;
private boolean mNeedHardwareBuffer;
@@ -78,6 +81,10 @@
private SensorManager mSensorManager;
private SensorDirectChannel mChannel;
+ static {
+ System.loadLibrary("cts-sensors-ndk-jni");
+ }
+
@Override
protected void setUp() throws Exception {
mSensorManager = (SensorManager) getContext().getSystemService(Context.SENSOR_SERVICE);
@@ -323,11 +330,9 @@
private HardwareBuffer allocateHardwareBuffer() {
HardwareBuffer hardwareBuffer;
- // TODO: remove this after BLOB constant is added into HardwareBuffer.
- int BLOB = 6;
hardwareBuffer = HardwareBuffer.create(
- SHARED_MEMORY_SIZE, 1 /* height */, BLOB, 1 /* layer */,
- HardwareBuffer.USAGE0_CPU_READ | HardwareBuffer.USAGE0_GPU_DATA_BUFFER
+ SHARED_MEMORY_SIZE, 1 /* height */, HardwareBuffer.BLOB, 1 /* layer */,
+ HardwareBuffer.USAGE0_CPU_READ_OFTEN | HardwareBuffer.USAGE0_GPU_DATA_BUFFER
| HardwareBuffer.USAGE0_SENSOR_DIRECT_DATA);
return hardwareBuffer;
}
@@ -345,11 +350,14 @@
private boolean isSharedMemoryFormatted(int memType) {
if (memType == SensorDirectChannel.TYPE_ASHMEM) {
if (!readMemoryFileContent()) {
+ Log.e(TAG, "Read MemoryFile content fail");
return false;
}
} else {
- // Android API only support memory file read back
- return true;
+ if (!readHardwareBufferContent()) {
+ Log.e(TAG, "Read HardwareBuffer content fail");
+ return false;
+ }
}
for (byte b : mBuffer) {
@@ -364,8 +372,7 @@
if (memType == SensorDirectChannel.TYPE_ASHMEM) {
assertTrue("read MemoryFile content failed", readMemoryFileContent());
} else {
- // Android API only support memory file read back
- return;
+ assertTrue("read HardwareBuffer content failed", readHardwareBufferContent());
}
int offset = 0;
@@ -455,6 +462,10 @@
return true;
}
+ private boolean readHardwareBufferContent() {
+ return nativeReadHardwareBuffer(mHardwareBuffer, mBuffer, 0, 0, SHARED_MEMORY_SIZE);
+ }
+
private class DirectReportSensorEvent {
int size;
int token;