Add native test for sensor direct report NDK API

Add native test for the following NDK functions:
* ASensorManager_configureDirectReport
* ASensorManager_createAshmemDirectChannel
* ASensorManager_destroyDirectChannel
* ASensor_getHighestDirectReportRateLevel
* ASensor_isDirectChannelTypeSupported

Also tested the following NDK API as well as passing created shared
memory across process boundary (app to service).
* ASharedMemory_create
* ASharedMemory_getSize

Test:  cts-tradefed run cts --module CtsSensorTestCases \
    --test android.hardware.cts.SensorNativeTest

Change-Id: Ie225c42dda34cda3fcaf5ca405d82efecbdfaa80
diff --git a/tests/sensor/Android.mk b/tests/sensor/Android.mk
index d3e7ce2..a231c3c 100644
--- a/tests/sensor/Android.mk
+++ b/tests/sensor/Android.mk
@@ -14,7 +14,9 @@
 
 LOCAL_PATH:= $(call my-dir)
 
+#
 # Reusable Sensor test classes and helpers
+#
 include $(CLEAR_VARS)
 
 LOCAL_MODULE := cts-sensors-tests
@@ -30,10 +32,38 @@
 LOCAL_SDK_VERSION := current
 
 LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
 include $(BUILD_STATIC_JAVA_LIBRARY)
 
+#
+# JNI components for testing NDK
+#
+include $(CLEAR_VARS)
 
+LOCAL_MODULE := libcts-sensors-ndk-jni
+
+LOCAL_CFLAGS += -Werror -Wall -Wextra
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := \
+    jni/SensorTest.cpp \
+    jni/android_view_cts_SensorNativeTest.cpp \
+    jni/nativeTestHelper.cpp \
+
+LOCAL_C_INCLUDES := $(JNI_H_INCLUDE)
+
+LOCAL_SHARED_LIBRARIES := libandroid liblog
+
+LOCAL_SDK_VERSION := current
+
+LOCAL_NDK_STL_VARIANT := c++_shared
+
+include $(BUILD_SHARED_LIBRARY)
+
+#
 # CtsSensorTestCases package
+#
 include $(CLEAR_VARS)
 
 LOCAL_MODULE_TAGS := tests
@@ -47,9 +77,14 @@
     ctstestrunner \
     cts-sensors-tests \
 
+LOCAL_JNI_SHARED_LIBRARIES := libcts-sensors-ndk-jni
+
 LOCAL_PACKAGE_NAME := CtsSensorTestCases
 
 LOCAL_SDK_VERSION := current
+
 LOCAL_JAVA_LIBRARIES := android.test.runner
 
+LOCAL_NDK_STL_VARIANT := c++_shared
+
 include $(BUILD_CTS_PACKAGE)
diff --git a/tests/sensor/jni/SensorTest.cpp b/tests/sensor/jni/SensorTest.cpp
new file mode 100644
index 0000000..3593047
--- /dev/null
+++ b/tests/sensor/jni/SensorTest.cpp
@@ -0,0 +1,280 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#include "SensorTest.h"
+#include <errno.h>
+
+namespace android {
+namespace SensorTest {
+
+// SensorTest container class
+bool SensorTest::SetUp() {
+    if (mManager == nullptr) {
+        mManager.reset(
+              TestSensorManager::getInstanceForPackage("android.hardware.cts.SensorNativeTest"));
+    }
+    return mManager == nullptr;
+}
+
+void SensorTest::TearDown() {
+    if (mManager == nullptr) {
+        mManager.reset(nullptr);
+    }
+}
+
+void SensorTest::testInitialized(JNIEnv *env) {
+    ASSERT_TRUE(mManager->isValid());
+}
+
+TestSensorManager::TestSensorManager(const char *package) {
+    mManager = ASensorManager_getInstanceForPackage(package);
+}
+
+TestSensorManager::~TestSensorManager() {
+    for (int channel : mSensorDirectChannel) {
+        destroyDirectChannel(channel);
+    }
+    mSensorDirectChannel.clear();
+}
+
+TestSensorManager * TestSensorManager::getInstanceForPackage(const char *package) {
+    return new TestSensorManager(package);
+}
+
+TestSensor TestSensorManager::getDefaultSensor(int type) {
+    return TestSensor(ASensorManager_getDefaultSensor(mManager, type));
+}
+
+int TestSensorManager::createDirectChannel(const TestSharedMemory &mem) {
+    if (!isValid()) {
+        return -EINVAL;
+    }
+    switch (mem.getType()) {
+        case ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY:
+            return createSharedMemoryDirectChannel(
+                    mem.getSharedMemoryFd(), mem.getSize());
+        case ASENSOR_DIRECT_CHANNEL_TYPE_HARDWARE_BUFFER:
+            return createHardwareBufferDirectChannel(
+                    mem.getHardwareBuffer(), mem.getSize());
+        default:
+            return -1;
+    }
+}
+
+int TestSensorManager::createSharedMemoryDirectChannel(int fd, size_t size) {
+    int ret = ASensorManager_createSharedMemoryDirectChannel(mManager, fd, size);
+    if (ret > 0) {
+        mSensorDirectChannel.insert(ret);
+    }
+    return ret;
+}
+
+int TestSensorManager::createHardwareBufferDirectChannel(
+        AHardwareBuffer const *buffer, size_t size) {
+    int ret = ASensorManager_createHardwareBufferDirectChannel(mManager, buffer, size);
+    if (ret > 0) {
+        mSensorDirectChannel.insert(ret);
+    }
+    return ret;
+}
+
+void TestSensorManager::destroyDirectChannel(int channel) {
+    if (!isValid()) {
+        return;
+    }
+    ASensorManager_destroyDirectChannel(mManager, channel);
+    mSensorDirectChannel.erase(channel);
+    return;
+}
+
+int TestSensorManager::configureDirectReport(TestSensor sensor, int channel, int rate) {
+    if (!isValid()) {
+        return -EINVAL;
+    }
+    return ASensorManager_configureDirectReport(mManager, sensor, channel, rate);
+}
+
+char * TestSharedMemory::getBuffer() const {
+    return mBuffer;
+}
+
+std::vector<ASensorEvent> TestSharedMemory::parseEvents(int64_t lastCounter, size_t offset) const {
+    constexpr size_t kEventSize = sizeof(ASensorEvent);
+    constexpr size_t kOffsetSize = offsetof(ASensorEvent, version);
+    constexpr size_t kOffsetAtomicCounter = offsetof(ASensorEvent, reserved0);
+
+    std::vector<ASensorEvent> events;
+    while (offset + kEventSize <= mSize) {
+        int64_t atomicCounter = *reinterpret_cast<uint32_t *>(mBuffer + offset + kOffsetAtomicCounter);
+        if (atomicCounter <= lastCounter) {
+            break;
+        }
+
+        int32_t size = *reinterpret_cast<int32_t *>(mBuffer + offset + kOffsetSize);
+        if (size != kEventSize) {
+            // unknown error, events parsed may be wrong, remove all
+            events.clear();
+            break;
+        }
+
+        events.push_back(*reinterpret_cast<ASensorEvent *>(mBuffer + offset));
+        lastCounter = atomicCounter;
+        offset += kEventSize;
+    }
+
+    return events;
+}
+
+TestSharedMemory::TestSharedMemory(int type, size_t size)
+        : mType(type), mSize(0), mBuffer(nullptr),
+            mSharedMemoryFd(-1), mHardwareBuffer(nullptr) {
+    bool success = false;
+    switch(type) {
+        case ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY: {
+            mSharedMemoryFd = ASharedMemory_create("TestSharedMemory", size);
+            if (mSharedMemoryFd < 0
+                    || ASharedMemory_getSize(mSharedMemoryFd) != size) {
+                break;
+            }
+
+            mSize = size;
+            mBuffer = reinterpret_cast<char *>(::mmap(
+                    nullptr, mSize, PROT_READ | PROT_WRITE,
+                    MAP_SHARED, mSharedMemoryFd, 0));
+
+            if (mBuffer == MAP_FAILED) {
+                mBuffer = nullptr;
+                break;
+            }
+            success = true;
+            break;
+        }
+        default:
+            break;
+    }
+
+    if (!success) {
+        release();
+    }
+}
+
+TestSharedMemory::~TestSharedMemory() {
+    release();
+}
+
+void TestSharedMemory::release() {
+    switch(mType) {
+        case ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY: {
+            if (mBuffer != nullptr) {
+                ::munmap(mBuffer, mSize);
+                mBuffer = nullptr;
+            }
+            if (mSharedMemoryFd > 0) {
+                ::close(mSharedMemoryFd);
+                mSharedMemoryFd = -1;
+            }
+            mSize = 0;
+            break;
+        }
+        default:
+            break;
+    }
+    if (mSharedMemoryFd > 0 || mSize != 0 || mBuffer != 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);
+    }
+}
+
+TestSharedMemory* TestSharedMemory::create(int type, size_t size) {
+    constexpr size_t kMaxSize = 128*1024*1024; // sensor test should not need more than 128M
+    if (size == 0 || size >= kMaxSize) {
+        return nullptr;
+    }
+
+    auto m = new TestSharedMemory(type, size);
+    if (m->mSize != size || m->mBuffer == nullptr) {
+        delete m;
+        m = nullptr;
+    }
+    return m;
+}
+
+// Test direct report of gyroscope at normal rate level through ashmem direct channel
+void SensorTest::testGyroscopeSharedMemoryDirectReport(JNIEnv* env) {
+    constexpr int type = ASENSOR_TYPE_GYROSCOPE;
+    constexpr size_t kEventSize = sizeof(ASensorEvent);
+    constexpr size_t kNEvent = 500;
+    constexpr size_t kMemSize = kEventSize * kNEvent;
+
+    TestSensor sensor = mManager->getDefaultSensor(type);
+
+    if (sensor.getHighestDirectReportRateLevel() == ASENSOR_DIRECT_RATE_STOP
+        || !sensor.isDirectChannelTypeSupported(ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY)) {
+        // does not declare support
+        return;
+    }
+
+    std::unique_ptr<TestSharedMemory>
+            mem(TestSharedMemory::create(ASENSOR_DIRECT_CHANNEL_TYPE_SHARED_MEMORY, kMemSize));
+    ASSERT_NE(mem, nullptr);
+    ASSERT_NE(mem->getBuffer(), nullptr);
+    ASSERT_GT(mem->getSharedMemoryFd(), 0);
+
+    char* buffer = mem->getBuffer();
+    // fill memory with data
+    for (size_t i = 0; i < kMemSize; ++i) {
+        buffer[i] = '\xcc';
+    }
+
+    int32_t channel;
+    channel = mManager->createDirectChannel(*mem);
+    ASSERT_GT(channel, 0);
+
+    // check memory is zeroed
+    for (size_t i = 0; i < kMemSize; ++i) {
+        ASSERT_EQ(buffer[i], '\0');
+    }
+
+    int32_t eventToken;
+    eventToken = mManager->configureDirectReport(sensor, channel, ASENSOR_DIRECT_RATE_NORMAL);
+    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));
+
+    int64_t lastTimestamp = 0;
+    for (auto &e : events) {
+        ASSERT_EQ(e.type, type);
+        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
+
+        lastTimestamp = e.timestamp;
+    }
+
+    // stop sensor and unregister channel
+    mManager->configureDirectReport(sensor, channel, ASENSOR_DIRECT_RATE_STOP);
+    mManager->destroyDirectChannel(channel);
+}
+} // namespace SensorTest
+} // namespace android
diff --git a/tests/sensor/jni/SensorTest.h b/tests/sensor/jni/SensorTest.h
new file mode 100644
index 0000000..5f8d703
--- /dev/null
+++ b/tests/sensor/jni/SensorTest.h
@@ -0,0 +1,141 @@
+/*
+ * Copyright (C) 2017 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.
+ */
+
+#ifndef SENSOR_TEST_H
+#define SENSOR_TEST_H
+
+#include "nativeTestHelper.h"
+#include <android/sensor.h>
+#include <android/hardware_buffer.h>
+#include <android/sharedmem.h>
+
+#include <unordered_set>
+#include <vector>
+#include <sys/mman.h>
+#include <unistd.h>
+
+namespace android {
+namespace SensorTest {
+
+class TestSensor;
+class TestSensorManager;
+class TestSharedMemory;
+
+class SensorTest {
+public:
+    virtual bool SetUp();
+    virtual void TearDown();
+    virtual ~SensorTest() = default;
+
+    // tests
+    void testInitialized(JNIEnv *env);
+    void testGyroscopeSharedMemoryDirectReport(JNIEnv *env);
+private:
+    std::unique_ptr<TestSensorManager> mManager;
+};
+
+// NDK ASensorManager wrapper
+class TestSensorManager {
+public:
+    static TestSensorManager * getInstanceForPackage(const char *package);
+    virtual ~TestSensorManager();
+
+    TestSensor getDefaultSensor(int type);
+    int createDirectChannel(const TestSharedMemory &mem);
+    void destroyDirectChannel(int channel);
+    int configureDirectReport(TestSensor sensor, int channel, int rateLevel);
+    bool isValid() const { return mManager != nullptr; }
+private:
+    TestSensorManager(const char *package);
+    int createSharedMemoryDirectChannel(int fd, size_t size);
+    int createHardwareBufferDirectChannel(AHardwareBuffer const *buffer, size_t size);
+
+    ASensorManager *mManager; // singleton, does not need delete
+
+    // book keeping
+    std::unordered_set<int> mSensorDirectChannel;
+};
+
+// NDK ASensor warpper
+class TestSensor {
+public:
+    TestSensor(ASensor const *s) : mSensor(s) { }
+
+    int getType() const {
+        if (!isValid()) {
+            return -1;
+        }
+        return ASensor_getType(mSensor);
+    }
+
+    bool isDirectChannelTypeSupported(int channelType) const {
+        if (!isValid()) {
+            return false;
+        }
+        return ASensor_isDirectChannelTypeSupported(mSensor, channelType);
+    }
+
+    int getHighestDirectReportRateLevel() const {
+        if (!isValid()) {
+            return ASENSOR_DIRECT_RATE_STOP;
+        }
+        return ASensor_getHighestDirectReportRateLevel(mSensor);
+    }
+
+    operator ASensor const * () { return mSensor; }
+
+    bool isValid() const { return mSensor != nullptr; }
+private:
+    ASensor const * mSensor;
+};
+
+// Shared memory wrapper class
+class TestSharedMemory {
+public:
+    static TestSharedMemory* create(int type, size_t size);
+    char * getBuffer() const;
+    std::vector<ASensorEvent> parseEvents(int64_t lastCounter = -1, size_t offset = 0) const;
+    virtual ~TestSharedMemory();
+
+    int getSharedMemoryFd() const {
+        return mSharedMemoryFd;
+    }
+
+    AHardwareBuffer const * getHardwareBuffer() const {
+        return mHardwareBuffer;
+    }
+
+    int getType() const {
+        return mType;
+    }
+
+    size_t getSize() const {
+        return mSize;
+    }
+private:
+    TestSharedMemory(int type, size_t size);
+    void release();
+
+    const int mType;
+    size_t mSize;
+    char* mBuffer;
+    int mSharedMemoryFd;
+    AHardwareBuffer *mHardwareBuffer;
+};
+} // namespace SensorTest
+} // namespace android
+
+#endif // SENSOR_TEST_H
diff --git a/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp b/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp
new file mode 100644
index 0000000..0c67928
--- /dev/null
+++ b/tests/sensor/jni/android_view_cts_SensorNativeTest.cpp
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#include "nativeTestHelper.h"
+#include "SensorTest.h"
+
+namespace {
+using android::SensorTest::SensorTest;
+
+jlong setUp(JNIEnv*, jclass) {
+    SensorTest *test = new SensorTest();
+    if (test != nullptr) {
+        test->SetUp();
+    }
+    return reinterpret_cast<jlong>(test);
+}
+
+void tearDown(JNIEnv*, jclass, jlong instance) {
+    delete reinterpret_cast<SensorTest *>(instance);
+}
+
+void test(JNIEnv* env, jclass, jlong instance) {
+    SensorTest *test = reinterpret_cast<SensorTest *>(instance);
+    ASSERT_NE(test, nullptr);
+
+    // test if SensorTest is intialized
+    test->testInitialized(env);
+
+    // test gyro direct report using shared memory buffer
+    test->testGyroscopeSharedMemoryDirectReport(env);
+}
+
+JNINativeMethod gMethods[] = {
+    {  "nativeSetUp", "()J",
+            (void *) setUp},
+    {  "nativeTearDown", "(J)V",
+            (void *) tearDown},
+    {  "nativeTest", "(J)V",
+            (void *) test},
+};
+} // unamed namespace
+
+int register_android_view_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
new file mode 100644
index 0000000..7c1b378
--- /dev/null
+++ b/tests/sensor/jni/nativeTestHelper.cpp
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#include "nativeTestHelper.h"
+#include <cstdlib>
+#include <cstring>
+
+extern int register_android_view_cts_SensorNativeTest(JNIEnv* env);
+
+void fail(JNIEnv* env, const char* format, ...) {
+    va_list args;
+
+    va_start(args, format);
+    char *msg;
+    vasprintf(&msg, format, args);
+    va_end(args);
+
+    jclass exClass;
+    const char *className = "java/lang/AssertionError";
+    exClass = env->FindClass(className);
+    env->ThrowNew(exClass, msg);
+    free(msg);
+}
+
+jint JNI_OnLoad(JavaVM *vm, void *) {
+    JNIEnv *env = NULL;
+    if (vm->GetEnv((void**)&env, JNI_VERSION_1_4) != JNI_OK) {
+        return JNI_ERR;
+    }
+    if (register_android_view_cts_SensorNativeTest(env)) {
+        return JNI_ERR;
+    }
+    return JNI_VERSION_1_4;
+}
diff --git a/tests/sensor/jni/nativeTestHelper.h b/tests/sensor/jni/nativeTestHelper.h
new file mode 100644
index 0000000..cf7f1bd
--- /dev/null
+++ b/tests/sensor/jni/nativeTestHelper.h
@@ -0,0 +1,48 @@
+/*
+ * Copyright (C) 2017 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.
+ *
+ */
+
+#include <jni.h>
+
+#include <android/log.h>
+#define TAG "SensorNativeTest"
+#define ALOGV(...) __android_log_print(ANDROID_LOG_VERBOSE, TAG, __VA_ARGS__)
+#define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR, TAG, __VA_ARGS__)
+#define ALOGI(...) __android_log_print(ANDROID_LOG_INFO, TAG, __VA_ARGS__)
+
+#define ASSERT(condition, format, args...) \
+        if (!(condition)) { \
+            fail(env, format, ## args); \
+            return; \
+        }
+
+// gtest style assert
+#define ASSERT_TRUE(a) ASSERT((a), "assert failed on (" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_FALSE(a) ASSERT(!(a), "assert failed on (!" #a ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_EQ(a, b) \
+        ASSERT((a) == (b), "assert failed on (" #a " == " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_NE(a, b) \
+        ASSERT((a) != (b), "assert failed on (" #a " != " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_GT(a, b) \
+        ASSERT((a) > (b), "assert failed on (" #a " > " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_GE(a, b) \
+        ASSERT((a) >= (b), "assert failed on (" #a " >= " #b ") at " __FILE__ ":%d", __LINE__)
+#define ASSERT_LT(a, b) \
+        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__)
+
+void fail(JNIEnv* env, const char* format, ...);
diff --git a/tests/sensor/src/android/hardware/cts/SensorNativeTest.java b/tests/sensor/src/android/hardware/cts/SensorNativeTest.java
new file mode 100644
index 0000000..7a05bf5
--- /dev/null
+++ b/tests/sensor/src/android/hardware/cts/SensorNativeTest.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2017 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.content.pm.PackageManager;
+import android.hardware.Sensor;
+import android.hardware.SensorManager;
+
+/**
+ * Check sensor native library funcationality.
+ *
+ * This is the place to implement sensor NDK CTS tests.
+ */
+public class SensorNativeTest extends SensorTestCase {
+    private SensorManager mSensorManager;
+    private boolean mAreHifiSensorsSupported;
+    private boolean mVrHighPerformanceModeSupported;
+
+    protected native long nativeSetUp();
+    protected native void nativeTearDown(long instance);
+    private native void nativeTest(long instance);
+    private long mNativeInstance;
+
+    static {
+        System.loadLibrary("cts-sensors-ndk-jni");
+    }
+
+    @Override
+    public void setUp() throws Exception {
+        super.setUp();
+        mNativeInstance = nativeSetUp();
+        assertTrue("create native instance failed", mNativeInstance != 0);
+    }
+
+    @Override
+    public void tearDown() throws Exception {
+        nativeTearDown(mNativeInstance);
+    }
+
+    public void testNative() throws AssertionError {
+        nativeTest(mNativeInstance);
+    }
+}
diff --git a/tests/sensor/src/android/hardware/cts/SensorSupportTest.java b/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
index 3d1bc09..09e4711 100644
--- a/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
+++ b/tests/sensor/src/android/hardware/cts/SensorSupportTest.java
@@ -20,7 +20,6 @@
 import android.content.pm.PackageManager;
 import android.hardware.Sensor;
 import android.hardware.SensorManager;
-import android.test.AndroidTestCase;
 
 /**
  * Checks if Hifi sensors  or VR High performance mode sensors
@@ -35,7 +34,7 @@
  *     -w android.hardware.cts/android.test.AndroidJUnitRunner
  * </pre>
  */
-public class SensorSupportTest extends AndroidTestCase {
+public class SensorSupportTest extends SensorTestCase {
     private SensorManager mSensorManager;
     private boolean mAreHifiSensorsSupported;
     private boolean mVrHighPerformanceModeSupported;