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;