HID dynamic sensor: add support to three types of sensor

Implemented HID dynamic sensor using linux hidraw driver and
HidUtils. Support the following cases:

1) HID Sensor spec defined accelerometer, gyroscope, magnetometer
   and orientation (quaternion).
2) Android defined type sensors (HID sensor custom type): ambient
   temperature, barometer, light and heart rate sensor.
3) Android custom type sensor (based on HID sensor custom type).

Test: run tests/examples in test/
Test: test with a USB HID hardware (launchpad sensor) and marlin
Bug: 37482951
Bug: 31026607
Change-Id: I9d679fb34d15324a9df1cf19647ea638fd1a0e68
diff --git a/modules/sensors/dynamic_sensor/Android.mk b/modules/sensors/dynamic_sensor/Android.mk
index cdc1929..4608508 100644
--- a/modules/sensors/dynamic_sensor/Android.mk
+++ b/modules/sensors/dynamic_sensor/Android.mk
@@ -15,6 +15,24 @@
 LOCAL_PATH := $(call my-dir)
 COMMON_CFLAGS := -Wall -Werror -Wextra
 
+dynamic_sensor_src := \
+    BaseDynamicSensorDaemon.cpp \
+    BaseSensorObject.cpp \
+    ConnectionDetector.cpp \
+    DummyDynamicAccelDaemon.cpp \
+    DynamicSensorManager.cpp \
+    HidRawDevice.cpp \
+    HidRawSensor.cpp \
+    HidRawSensorDaemon.cpp \
+    HidRawSensorDevice.cpp \
+    RingBuffer.cpp
+
+dynamic_sensor_shared_lib := \
+    libbase \
+    libcutils \
+    libhidparser \
+    liblog \
+    libutils
 #
 # There are two ways to utilize the dynamic sensor module:
 #   1. Use as an extension in an existing hal: declare dependency on libdynamic_sensor_ext shared
@@ -25,7 +43,6 @@
 #
 # Please take only one of these two options to avoid conflict over hardware resource.
 #
-
 #
 # Option 1: sensor extension module
 #
@@ -37,18 +54,9 @@
 
 LOCAL_CFLAGS += $(COMMON_CFLAGS) -DLOG_TAG=\"DynamicSensorExt\"
 
-LOCAL_SRC_FILES := \
-    BaseDynamicSensorDaemon.cpp \
-    BaseSensorObject.cpp \
-    ConnectionDetector.cpp \
-    DummyDynamicAccelDaemon.cpp \
-    DynamicSensorManager.cpp \
-    RingBuffer.cpp
+LOCAL_SRC_FILES := $(dynamic_sensor_src)
 
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libutils \
-    liblog
+LOCAL_SHARED_LIBRARIES := $(dynamic_sensor_shared_lib)
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
 
@@ -66,23 +74,53 @@
 
 LOCAL_CFLAGS += $(COMMON_CFLAGS) -DLOG_TAG=\"DynamicSensorHal\"
 
-LOCAL_SRC_FILES := \
-    BaseDynamicSensorDaemon.cpp \
-    BaseSensorObject.cpp \
-    ConnectionDetector.cpp \
-    DummyDynamicAccelDaemon.cpp \
-    DynamicSensorManager.cpp \
-    RingBuffer.cpp \
-    sensors.cpp
+LOCAL_SRC_FILES := $(dynamic_sensor_src) sensors.cpp
 
-LOCAL_SHARED_LIBRARIES := \
-    libcutils \
-    libutils \
-    liblog
+LOCAL_SHARED_LIBRARIES := $(dynamic_sensor_shared_lib)
 
 LOCAL_EXPORT_C_INCLUDE_DIRS := $(LOCAL_PATH)
 
 include $(BUILD_SHARED_LIBRARY)
 
-include $(LOCAL_PATH)/HidUtils/Android.mk
+#
+# Host test for HidRawSensor. Test with dummy test HID descriptors.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := hidrawsensor_host_test
+LOCAL_MODULE_TAGS := optional
 
+LOCAL_CFLAGS += $(COMMON_CFLAGS)
+
+LOCAL_SRC_FILES := \
+    HidRawSensor.cpp \
+    BaseSensorObject.cpp \
+    HidUtils/test/TestHidDescriptor.cpp \
+    test/HidRawSensorTest.cpp
+
+LOCAL_SHARED_LIBRARIES := libhidparser_host
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/test $(LOCAL_PATH)/HidUtils/test
+include $(BUILD_HOST_EXECUTABLE)
+
+#
+# Host test for HidRawDevice and HidRawSensor. Test with hidraw
+# device node.
+#
+include $(CLEAR_VARS)
+LOCAL_MODULE := hidrawdevice_host_test
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_CFLAGS += $(COMMON_CFLAGS)
+
+LOCAL_SRC_FILES := \
+    HidRawDevice.cpp \
+    HidRawSensor.cpp \
+    BaseSensorObject.cpp \
+    test/HidRawDeviceTest.cpp
+
+LOCAL_SHARED_LIBRARIES := libhidparser_host
+
+LOCAL_C_INCLUDES += $(LOCAL_PATH)/test $(LOCAL_PATH)/HidUtils/test
+include $(BUILD_HOST_EXECUTABLE)
+
+include $(LOCAL_PATH)/HidUtils/Android.mk
diff --git a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
index f5f642c..7e9f217 100644
--- a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
+++ b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
@@ -23,14 +23,23 @@
 
 bool BaseDynamicSensorDaemon::onConnectionChange(const std::string &deviceKey, bool connected) {
     bool ret = false;
-    auto i = mDevices.find(deviceKey);
+    auto i = mDeviceKeySensorMap.find(deviceKey);
     if (connected) {
-        if (i == mDevices.end()) {
+        if (i == mDeviceKeySensorMap.end()) {
             ALOGV("device %s is connected", deviceKey.c_str());
-            BaseSensorObject* s = createSensor(deviceKey);
-            if (s) {
-                mDevices.emplace(deviceKey, sp<BaseSensorObject>(s));
-                mManager.registerSensor(s);
+            // get sensors from implementation
+            BaseSensorVector sensors = createSensor(deviceKey);
+            if (sensors.empty()) {
+                ALOGI("no valid sensor is defined in device %s, ignore", deviceKey.c_str());
+            } else {
+                ALOGV("discovered %zu sensors from device", sensors.size());
+                // build internal table first
+                auto result = mDeviceKeySensorMap.emplace(deviceKey, std::move(sensors));
+                // then register sensor to dynamic sensor manager, result.first is the iterator
+                // of key-value pair; result.first->second is the value, which is s.
+                for (auto &i : result.first->second) {
+                    mManager.registerSensor(i);
+                }
                 ALOGV("device %s is registered", deviceKey.c_str());
                 ret = true;
             }
@@ -39,13 +48,18 @@
         }
     } else {
         ALOGV("device %s is disconnected", deviceKey.c_str());
-        if (i != mDevices.end()) {
-            mManager.unregisterSensor(i->second.get());
-            mDevices.erase(i);
+        if (i != mDeviceKeySensorMap.end()) {
+            BaseSensorVector sensors = i->second;
+            for (auto &sensor : sensors) {
+                mManager.unregisterSensor(sensor);
+            }
+            mDeviceKeySensorMap.erase(i);
+            // notify implementation
+            removeSensor(deviceKey);
             ALOGV("device %s is unregistered", deviceKey.c_str());
             ret = true;
         } else {
-            ALOGD("device not found in registry");
+            ALOGV("device not found in registry");
         }
     }
 
diff --git a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
index b698b7d..0cec965 100644
--- a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
+++ b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
@@ -28,6 +28,8 @@
 
 class DynamicSensorManager;
 
+typedef std::vector<sp<BaseSensorObject>> BaseSensorVector;
+
 class BaseDynamicSensorDaemon : public RefBase {
 public:
     BaseDynamicSensorDaemon(DynamicSensorManager& manager) : mManager(manager) {}
@@ -35,10 +37,11 @@
 
     virtual bool onConnectionChange(const std::string &deviceKey, bool connected);
 protected:
-    virtual BaseSensorObject * createSensor(const std::string &deviceKey) = 0;
+    virtual BaseSensorVector createSensor(const std::string &deviceKey) = 0;
+    virtual void removeSensor(const std::string &/*deviceKey*/) {};
 
     DynamicSensorManager &mManager;
-    std::unordered_map<std::string, sp<BaseSensorObject> > mDevices;
+    std::unordered_map<std::string, BaseSensorVector> mDeviceKeySensorMap;
 };
 
 } // namespace SensorHalExt
diff --git a/modules/sensors/dynamic_sensor/BaseSensorObject.cpp b/modules/sensors/dynamic_sensor/BaseSensorObject.cpp
index 189e87f..4ec76b2 100644
--- a/modules/sensors/dynamic_sensor/BaseSensorObject.cpp
+++ b/modules/sensors/dynamic_sensor/BaseSensorObject.cpp
@@ -16,8 +16,8 @@
 
 #include "BaseSensorObject.h"
 #include "SensorEventCallback.h"
+#include "Utils.h"
 
-#include <utils/Log.h>
 #include <cstring>
 
 namespace android {
@@ -28,7 +28,6 @@
 
 bool BaseSensorObject::setEventCallback(SensorEventCallback* callback) {
     if (mCallback != nullptr) {
-        ALOGE("callback is already assigned, cannot change.");
         return false;
     }
     mCallback = callback;
@@ -51,7 +50,7 @@
 
 void BaseSensorObject::generateEvent(const sensors_event_t &e) {
     if (mCallback) {
-        mCallback->submitEvent(this, e);
+        mCallback->submitEvent(SP_THIS, e);
     }
 }
 
diff --git a/modules/sensors/dynamic_sensor/BaseSensorObject.h b/modules/sensors/dynamic_sensor/BaseSensorObject.h
index 326e138..e8d1a5d 100644
--- a/modules/sensors/dynamic_sensor/BaseSensorObject.h
+++ b/modules/sensors/dynamic_sensor/BaseSensorObject.h
@@ -17,8 +17,7 @@
 #ifndef ANDROID_SENSORHAL_BASE_SENSOR_OBJECT_H
 #define ANDROID_SENSORHAL_BASE_SENSOR_OBJECT_H
 
-#include <utils/RefBase.h>
-#include <utils/Timers.h> // for nsecs_t
+#include "Utils.h"
 #include <cstdint>
 
 struct sensor_t;
@@ -29,7 +28,7 @@
 
 class SensorEventCallback;
 
-class BaseSensorObject : virtual public RefBase {
+class BaseSensorObject : virtual public REF_BASE(BaseSensorObject) {
 public:
     BaseSensorObject();
     virtual ~BaseSensorObject() = default;
@@ -48,7 +47,8 @@
     virtual int enable(bool enable) = 0;
 
     // set sample period and batching period of sensor.
-    virtual int batch(nsecs_t samplePeriod, nsecs_t batchPeriod) = 0;
+    // both sample period and batch period are in nano-seconds.
+    virtual int batch(int64_t samplePeriod, int64_t batchPeriod) = 0;
 
     // flush sensor, default implementation will send a flush complete event back.
     virtual int flush();
diff --git a/modules/sensors/dynamic_sensor/ConnectionDetector.cpp b/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
index 6099493..67a6f27 100644
--- a/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
+++ b/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
@@ -31,7 +31,7 @@
 
 // SocketConnectionDetector functions
 SocketConnectionDetector::SocketConnectionDetector(BaseDynamicSensorDaemon *d, int port)
-        : ConnectionDetector(d) {
+        : ConnectionDetector(d), Thread(false /*canCallJava*/) {
     // initialize socket that accept connection to localhost:port
     mListenFd = ::socket(AF_INET, SOCK_STREAM, 0);
     if (mListenFd < 0) {
@@ -105,7 +105,7 @@
 // FileConnectionDetector functions
 FileConnectionDetector::FileConnectionDetector (
         BaseDynamicSensorDaemon *d, const std::string &path, const std::string &regex)
-            : ConnectionDetector(d), mPath(path), mRegex(regex) {
+            : ConnectionDetector(d), Thread(false /*callCallJava*/), mPath(path), mRegex(regex) {
     mInotifyFd = ::inotify_init1(IN_NONBLOCK);
     if (mInotifyFd < 0) {
         ALOGE("Cannot init inotify");
diff --git a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
index 1977967..a1a47e8 100644
--- a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
+++ b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
@@ -51,23 +51,30 @@
     }
 }
 
-BaseSensorObject * DummyDynamicAccelDaemon::createSensor(const std::string &deviceKey) {
+BaseSensorVector DummyDynamicAccelDaemon::createSensor(const std::string &deviceKey) {
+    BaseSensorVector ret;
     if (deviceKey.compare(0, 1, "/") == 0) {
         // file detector result, deviceKey is file absolute path
-        size_t start = std::max(static_cast<size_t>(0),
-                                deviceKey.length() - (::strlen(FILE_NAME_BASE) + 1));
-        return new DummySensor(deviceKey.substr(start));
-
+        const size_t len = ::strlen(FILE_NAME_BASE) + 1; // +1 for number
+        if (deviceKey.length() < len) {
+            ALOGE("illegal file device key %s", deviceKey.c_str());
+        } else {
+            size_t start = deviceKey.length() - len;
+            ret.emplace_back(new DummySensor(deviceKey.substr(start)));
+        }
     } else if (deviceKey.compare(0, ::strlen("socket:"), "socket:") == 0) {
-        return new DummySensor(deviceKey);
+        ret.emplace_back(new DummySensor(deviceKey));
     } else {
         // unknown deviceKey
-        return nullptr;
+        ALOGE("unknown deviceKey: %s", deviceKey.c_str());
     }
+    return ret;
 }
 
-DummyDynamicAccelDaemon::DummySensor::DummySensor(const std::string &name) : mRunState(false) {
+DummyDynamicAccelDaemon::DummySensor::DummySensor(const std::string &name)
+        : Thread(false /*canCallJava*/), mRunState(false) {
     mSensorName = "Dummy Accel - " + name;
+    // fake sensor information for dummy sensor
     mSensor = (struct sensor_t) {
         mSensorName.c_str(),
         "DemoSense, Inc.",
@@ -120,7 +127,8 @@
     return 0;
 }
 
-int DummyDynamicAccelDaemon::DummySensor::batch(nsecs_t, nsecs_t) {
+int DummyDynamicAccelDaemon::DummySensor::batch(int64_t /*samplePeriod*/, int64_t /*batchPeriod*/) {
+    // Dummy sensor does not support changing rate and batching. But return successful anyway.
     return 0;
 }
 
diff --git a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
index 0bffbee..95a5730 100644
--- a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
+++ b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
@@ -61,7 +61,7 @@
         bool mRunState;
     };
     // implement BaseDynamicSensorDaemon function
-    BaseSensorObject * createSensor(const std::string &deviceKey) override;
+    virtual BaseSensorVector createSensor(const std::string &deviceKey) override;
 
     sp<ConnectionDetector> mFileDetector;
     sp<ConnectionDetector> mSocketDetector;
diff --git a/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp b/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp
index eb4903d..b634052 100644
--- a/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp
+++ b/modules/sensors/dynamic_sensor/DynamicSensorManager.cpp
@@ -17,6 +17,7 @@
 #include "BaseDynamicSensorDaemon.h"
 #include "BaseSensorObject.h"
 #include "DummyDynamicAccelDaemon.h"
+#include "HidRawSensorDaemon.h"
 #include "DynamicSensorManager.h"
 
 #include <utils/Log.h>
@@ -31,6 +32,7 @@
         int handleBase, int handleCount, SensorEventCallback *callback) {
     auto m = new DynamicSensorManager(handleBase, handleBase + handleCount - 1, callback);
     m->mDaemonVector.push_back(new DummyDynamicAccelDaemon(*m));
+    m->mDaemonVector.push_back(new HidRawSensorDaemon(*m));
     return m;
 }
 
@@ -107,7 +109,7 @@
 
 int DynamicSensorManager::flush(int handle) {
     if (handle == mHandleRange.first) {
-        // TODO: submit a flush complete here
+        // submit a flush complete here
         static const sensors_event_t event = {
             .type = SENSOR_TYPE_META_DATA,
             .sensor = mHandleRange.first,
diff --git a/modules/sensors/dynamic_sensor/DynamicSensorManager.h b/modules/sensors/dynamic_sensor/DynamicSensorManager.h
index b8fd472..b6f39da 100644
--- a/modules/sensors/dynamic_sensor/DynamicSensorManager.h
+++ b/modules/sensors/dynamic_sensor/DynamicSensorManager.h
@@ -123,7 +123,7 @@
     // mapping between handle and SensorObjects
     mutable std::mutex mLock;
     int mNextHandle;
-    std::unordered_map<int, wp<BaseSensorObject> > mMap;
+    std::unordered_map<int, wp<BaseSensorObject>> mMap;
     std::unordered_map<void *, int> mReverseMap;
     mutable std::unordered_map<int, ConnectionReport> mPendingReport;
 
diff --git a/modules/sensors/dynamic_sensor/HidDevice.h b/modules/sensors/dynamic_sensor/HidDevice.h
new file mode 100644
index 0000000..7083593
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidDevice.h
@@ -0,0 +1,57 @@
+/*
+ * 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 ANDROID_SENSORHAL_EXT_HID_DEVICE_H
+#define ANDROID_SENSORHAL_EXT_HID_DEVICE_H
+#include "Utils.h"
+#include <string>
+#include <vector>
+#include <unordered_set>
+
+namespace android {
+namespace SensorHalExt {
+
+class HidDevice : virtual public REF_BASE(HidDevice) {
+public:
+    virtual ~HidDevice() = default;
+
+    struct HidDeviceInfo {
+        std::string name;
+        std::string physicalPath;
+        std::string busType;
+        uint16_t vendorId;
+        uint16_t productId;
+        std::vector<uint8_t> descriptor;
+    };
+
+    virtual const HidDeviceInfo& getDeviceInfo() = 0;
+
+    // get feature from device
+    virtual bool getFeature(uint8_t id, std::vector<uint8_t> *out) = 0;
+
+    // write feature to device
+    virtual bool setFeature(uint8_t id, const std::vector<uint8_t> &in) = 0;
+
+    // send report to default output endpoint
+    virtual bool sendReport(uint8_t id, std::vector<uint8_t> &data) = 0;
+
+    // receive from default input endpoint
+    virtual bool receiveReport(uint8_t *id, std::vector<uint8_t> *data) = 0;
+};
+
+} // namespace SensorHalExt
+} // namespace android
+#endif // ANDROID_SENSORHAL_EXT_HID_DEVICE_H
diff --git a/modules/sensors/dynamic_sensor/HidRawDevice.cpp b/modules/sensors/dynamic_sensor/HidRawDevice.cpp
new file mode 100644
index 0000000..2588483
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawDevice.cpp
@@ -0,0 +1,332 @@
+/*
+ * 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 "HidRawDevice.h"
+#include "HidLog.h"
+#include "Utils.h"
+
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/hidraw.h>
+#include <linux/hiddev.h>  // HID_STRING_SIZE
+#include <sys/ioctl.h>
+#include <unistd.h>
+
+#include <set>
+
+namespace android {
+namespace SensorHalExt {
+
+using HidUtil::HidItem;
+
+HidRawDevice::HidRawDevice(
+        const std::string &devName, const std::unordered_set<unsigned int> &usageSet)
+        : mDevFd(-1), mMultiIdDevice(false), mValid(false) {
+    // open device
+    mDevFd = ::open(devName.c_str(), O_RDWR); // read write?
+    if (mDevFd < 0) {
+        LOG_E << "Error in open device node: " << errno << " (" << ::strerror(errno) << ")"
+              << LOG_ENDL;
+        return;
+    }
+
+    // get device information, including hid descriptor
+    if (!populateDeviceInfo()) {
+        LOG_E << "Error obtaining HidRaw device information" << LOG_ENDL;
+        return;
+    }
+
+    if (!generateDigest(usageSet)) {
+        LOG_E << "Cannot parse hid descriptor" << LOG_ENDL;
+        return;
+    }
+
+    // digest error checking
+    std::unordered_set<unsigned int> reportIdSet;
+    for (auto const &digest : mDigestVector) {
+        for (auto const &packet : digest.packets) {
+            if (mReportTypeIdMap.emplace(
+                        std::make_pair(packet.type, packet.id), &packet).second == false) {
+                LOG_E << "Same type - report id pair (" << packet.type << ", " << packet.id << ")"
+                      << "is used by more than one usage collection" << LOG_ENDL;
+                return;
+            }
+            reportIdSet.insert(packet.id);
+        }
+    }
+    if (mReportTypeIdMap.empty()) {
+        return;
+    }
+
+    if (reportIdSet.size() > 1) {
+        if (reportIdSet.find(0) != reportIdSet.end()) {
+            LOG_E << "Default report id 0 is not expected when more than one report id is found."
+                  << LOG_ENDL;
+            return;
+        }
+        mMultiIdDevice = true;
+    } else { // reportIdSet.size() == 1
+        mMultiIdDevice = !(reportIdSet.find(0) != reportIdSet.end());
+    }
+    mValid = true;
+}
+
+HidRawDevice::~HidRawDevice() {
+    if (mDevFd > 0) {
+        ::close(mDevFd);
+        mDevFd = -1;
+    }
+}
+
+bool HidRawDevice::populateDeviceInfo() {
+    HidDeviceInfo info;
+    char buffer[HID_STRING_SIZE + 1];
+
+    if (mDevFd < 0) {
+        return false;
+    }
+
+    // name
+    if (ioctl(mDevFd, HIDIOCGRAWNAME(sizeof(buffer) - 1), buffer) < 0) {
+        return false;
+    }
+    buffer[sizeof(buffer) - 1] = '\0';
+    info.name = buffer;
+
+    // physical path
+    if (ioctl(mDevFd, HIDIOCGRAWPHYS(sizeof(buffer) - 1), buffer) < 0) {
+        return false;
+    }
+    buffer[sizeof(buffer) - 1] = '\0';
+    info.physicalPath = buffer;
+
+    // raw device info
+    hidraw_devinfo devInfo;
+    if (ioctl(mDevFd, HIDIOCGRAWINFO, &devInfo) < 0) {
+        return false;
+    }
+
+    switch (devInfo.bustype) {
+    case BUS_USB:
+        info.busType = "USB";
+        break;
+    case BUS_HIL:
+        info.busType = "HIL";
+        break;
+    case BUS_BLUETOOTH:
+        info.busType = "Bluetooth";
+        break;
+    case BUS_VIRTUAL:
+        info.busType = "Virtual";
+        break;
+    default:
+        info.busType = "Other";
+        break;
+    }
+
+    info.vendorId = devInfo.vendor;
+    info.productId = devInfo.vendor;
+
+    uint32_t descriptorSize;
+    /* Get Report Descriptor Size */
+    if (ioctl(mDevFd, HIDIOCGRDESCSIZE, &descriptorSize) < 0) {
+        return false;
+    }
+
+    struct hidraw_report_descriptor reportDescriptor;
+    memset(&reportDescriptor, 0, sizeof(reportDescriptor));
+    info.descriptor.resize(descriptorSize);
+    reportDescriptor.size = descriptorSize;
+    if (ioctl(mDevFd, HIDIOCGRDESC, &reportDescriptor) < 0) {
+        return false;
+    }
+    std::copy(reportDescriptor.value, reportDescriptor.value + descriptorSize,
+              info.descriptor.begin());
+    mDeviceInfo = info;
+    return true;
+}
+
+bool HidRawDevice::generateDigest(const std::unordered_set<unsigned int> &usage) {
+    if (mDeviceInfo.descriptor.empty()) {
+        return false;
+    }
+
+    std::vector<HidItem> tokens = HidItem::tokenize(mDeviceInfo.descriptor);
+    HidParser parser;
+    if (!parser.parse(tokens)) {
+        return false;
+    }
+
+    parser.filterTree();
+    mDigestVector = parser.generateDigest(usage);
+
+    return mDigestVector.size() > 0;
+}
+
+bool HidRawDevice::isValid() {
+    return mValid;
+}
+
+bool HidRawDevice::getFeature(uint8_t id, std::vector<uint8_t> *out) {
+    if (mDevFd < 0) {
+        return false;
+    }
+
+    if (out == nullptr) {
+        return false;
+    }
+
+    const HidParser::ReportPacket *packet = getReportPacket(HidParser::REPORT_TYPE_FEATURE, id);
+    if (packet == nullptr) {
+        LOG_E << "HidRawDevice::getFeature: unknown feature " << id << LOG_ENDL;
+        return false;
+    }
+
+    size_t size = packet->getByteSize() + 1; // report id size
+
+    std::lock_guard<std::mutex> l(mIoBufferLock);
+    if (mIoBuffer.size() < size) {
+        mIoBuffer.resize(size);
+    }
+    mIoBuffer[0] = id;
+    int res = ::ioctl(mDevFd, HIDIOCGFEATURE(size), mIoBuffer.data());
+    if (res < 0) {
+        LOG_E << "HidRawDevice::getFeature: feature " << static_cast<int>(id)
+              << " ioctl returns " << res << " (" << ::strerror(res) << ")" << LOG_ENDL;
+        return false;
+    }
+
+    if (static_cast<size_t>(res) != size) {
+        LOG_E << "HidRawDevice::getFeature: get feature " << static_cast<int>(id)
+              << " returned " << res << " bytes, does not match expected " << size << LOG_ENDL;
+        return false;
+    }
+    if (mIoBuffer.front() != id) {
+        LOG_E << "HidRawDevice::getFeature: get feature " << static_cast<int>(id)
+              << " result has header " << mIoBuffer.front() << LOG_ENDL;
+    }
+    out->resize(size - 1);
+    std::copy(mIoBuffer.begin() + 1, mIoBuffer.begin() + size, out->begin());
+    return true;
+}
+
+bool HidRawDevice::setFeature(uint8_t id, const std::vector<uint8_t> &in) {
+    if (mDevFd < 0) {
+        return false;
+    }
+
+    const HidParser::ReportPacket *packet = getReportPacket(HidParser::REPORT_TYPE_FEATURE, id);
+    if (packet == nullptr) {
+        LOG_E << "HidRawDevice::setFeature: Unknown feature " << id << LOG_ENDL;
+        return false;
+    }
+
+    size_t size = packet->getByteSize();
+    if (size != in.size()) {
+        LOG_E << "HidRawDevice::setFeature: set feature " << id << " size mismatch, need "
+              << size << " bytes, have " << in.size() << " bytes" << LOG_ENDL;
+        return false;
+    }
+
+    ++size; // report id byte
+    std::lock_guard<std::mutex> l(mIoBufferLock);
+    if (mIoBuffer.size() < size) {
+        mIoBuffer.resize(size);
+    }
+    mIoBuffer[0] = id;
+    std::copy(in.begin(), in.end(), &mIoBuffer[1]);
+    int res = ::ioctl(mDevFd, HIDIOCSFEATURE(size), mIoBuffer.data());
+    if (res < 0) {
+        LOG_E << "HidRawDevice::setFeature: feature " << id << " ioctl returns " << res
+              << " (" << ::strerror(res) << ")" << LOG_ENDL;
+        return false;
+    }
+    return true;
+}
+
+bool HidRawDevice::sendReport(uint8_t id, std::vector<uint8_t> &data) {
+    if (mDevFd < 0) {
+        return false;
+    }
+
+    const HidParser::ReportPacket *packet = getReportPacket(HidParser::REPORT_TYPE_OUTPUT, id);
+    if (packet == nullptr) {
+        LOG_E << "HidRawDevice::sendReport: unknown output " << id << LOG_ENDL;
+        return false;
+    }
+
+    size_t size = packet->getByteSize();
+    if (size != data.size()) {
+        LOG_E << "HidRawDevice::sendReport: send report " << id << " size mismatch, need "
+              << size << " bytes, have " << data.size() << " bytes" << LOG_ENDL;
+        return false;
+    }
+    int res;
+    if (mMultiIdDevice) {
+        std::lock_guard<std::mutex> l(mIoBufferLock);
+        ++size;
+        if (mIoBuffer.size() < size) {
+            mIoBuffer.resize(size);
+        }
+        mIoBuffer[0] = id;
+        std::copy(mIoBuffer.begin() + 1, mIoBuffer.end(), data.begin());
+        res = ::write(mDevFd, mIoBuffer.data(), size);
+    } else {
+        res = ::write(mDevFd, data.data(), size);
+    }
+    if (res < 0) {
+        LOG_E << "HidRawDevice::sendReport: output " << id << " write returns " << res
+              << " (" << ::strerror(res) << ")" << LOG_ENDL;
+        return false;
+    }
+    return true;
+}
+
+bool HidRawDevice::receiveReport(uint8_t *id, std::vector<uint8_t> *data) {
+    if (mDevFd < 0) {
+        return false;
+    }
+
+    uint8_t buffer[256];
+    int res = ::read(mDevFd, buffer, 256);
+    if (res < 0) {
+        LOG_E << "HidRawDevice::receiveReport: read returns " << res
+              << " (" << ::strerror(res) << ")" << LOG_ENDL;
+        return false;
+    }
+
+    if (mMultiIdDevice) {
+        if (!(res > 1)) {
+            LOG_E << "read hidraw returns data too short, len: " << res << LOG_ENDL;
+            return false;
+        }
+        data->resize(static_cast<size_t>(res - 1));
+        std::copy(buffer + 1, buffer + res, data->begin());
+        *id = buffer[0];
+    } else {
+        data->resize(static_cast<size_t>(res));
+        std::copy(buffer, buffer + res, data->begin());
+        *id = 0;
+    }
+    return true;
+}
+
+const HidParser::ReportPacket *HidRawDevice::getReportPacket(unsigned int type, unsigned int id) {
+    auto i = mReportTypeIdMap.find(std::make_pair(type, id));
+    return (i == mReportTypeIdMap.end()) ? nullptr : i->second;
+}
+
+} // namespace SensorHalExt
+} // namespace android
diff --git a/modules/sensors/dynamic_sensor/HidRawDevice.h b/modules/sensors/dynamic_sensor/HidRawDevice.h
new file mode 100644
index 0000000..707dfff
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawDevice.h
@@ -0,0 +1,85 @@
+/*
+ * 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 ANDROID_SENSORHAL_EXT_HIDRAW_DEVICE_H
+#define ANDROID_SENSORHAL_EXT_HIDRAW_DEVICE_H
+
+#include "HidDevice.h"
+
+#include <HidParser.h>
+#include <string>
+#include <vector>
+#include <unordered_set>
+#include <unordered_map>
+
+namespace android {
+namespace SensorHalExt {
+
+using HidUtil::HidParser;
+using HidUtil::HidReport;
+
+class HidRawDevice : public HidDevice {
+    friend class HidRawDeviceTest;
+public:
+    HidRawDevice(const std::string &devName, const std::unordered_set<unsigned int> &usageSet);
+    virtual ~HidRawDevice();
+
+    // test if the device initialized successfully
+    bool isValid();
+
+    // implement HidDevice pure virtuals
+    virtual HidDeviceInfo& getDeviceInfo() override { return mDeviceInfo; }
+    virtual bool getFeature(uint8_t id, std::vector<uint8_t> *out) override;
+    virtual bool setFeature(uint8_t id, const std::vector<uint8_t> &in) override;
+    virtual bool sendReport(uint8_t id, std::vector<uint8_t> &data) override;
+    virtual bool receiveReport(uint8_t *id, std::vector<uint8_t> *data) override;
+
+protected:
+    bool populateDeviceInfo();
+    size_t getReportSize(int type, uint8_t id);
+    bool generateDigest(const std::unordered_set<uint32_t> &usage);
+    size_t calculateReportBitSize(const std::vector<HidReport> &reportItems);
+    const HidParser::ReportPacket *getReportPacket(unsigned int type, unsigned int id);
+
+    typedef std::pair<unsigned int, unsigned int> ReportTypeIdPair;
+    struct UnsignedIntPairHash {
+        size_t operator()(const ReportTypeIdPair& v) const {
+            std::hash<unsigned int> hash;
+            return hash(v.first) ^ hash(v.second);
+        }
+    };
+
+    std::unordered_map<ReportTypeIdPair, const HidParser::ReportPacket *, UnsignedIntPairHash>
+            mReportTypeIdMap;
+
+    HidParser::DigestVector mDigestVector;
+private:
+    std::mutex mIoBufferLock;
+    std::vector<uint8_t> mIoBuffer;
+
+    int mDevFd;
+    HidDeviceInfo mDeviceInfo;
+    bool mMultiIdDevice;
+    int mValid;
+
+    HidRawDevice(const HidRawDevice &) = delete;
+    void operator=(const HidRawDevice &) = delete;
+};
+
+} // namespace SensorHalExt
+} // namespace android
+
+#endif // ANDROID_SENSORHAL_EXT_HIDRAW_DEVICE_H
+
diff --git a/modules/sensors/dynamic_sensor/HidRawSensor.cpp b/modules/sensors/dynamic_sensor/HidRawSensor.cpp
new file mode 100644
index 0000000..a64d7a6
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawSensor.cpp
@@ -0,0 +1,1044 @@
+/*
+ * 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 "HidRawSensor.h"
+#include "HidSensorDef.h"
+
+#include <utils/Errors.h>
+#include "HidLog.h"
+
+#include <algorithm>
+#include <cfloat>
+#include <codecvt>
+#include <iomanip>
+#include <sstream>
+
+namespace android {
+namespace SensorHalExt {
+
+namespace {
+const std::string CUSTOM_TYPE_PREFIX("com.google.hardware.sensor.hid_dynamic.");
+}
+
+HidRawSensor::HidRawSensor(
+        SP(HidDevice) device, uint32_t usage, const std::vector<HidParser::ReportPacket> &packets)
+        : mReportingStateId(-1), mPowerStateId(-1), mReportIntervalId(-1), mInputReportId(-1),
+        mEnabled(false), mSamplingPeriod(1000ll*1000*1000), mBatchingPeriod(0),
+        mDevice(device), mValid(false) {
+    if (device == nullptr) {
+        return;
+    }
+    memset(&mSensor, 0, sizeof(mSensor));
+
+    const HidDevice::HidDeviceInfo &info =  device->getDeviceInfo();
+    initFeatureValueFromHidDeviceInfo(&mFeatureInfo, info);
+
+    if (!populateFeatureValueFromFeatureReport(&mFeatureInfo, packets)) {
+        LOG_E << "populate feature from feature report failed" << LOG_ENDL;
+        return;
+    }
+
+    if (!findSensorControlUsage(packets)) {
+        LOG_E << "finding sensor control usage failed" << LOG_ENDL;
+        return;
+    }
+
+    // build translation table
+    bool translationTableValid = false;
+    switch (usage) {
+        using namespace Hid::Sensor::SensorTypeUsage;
+        using namespace Hid::Sensor::ReportUsage;
+        case ACCELEROMETER_3D:
+            // Hid unit default g
+            // Android unit m/s^2
+            // 1g = 9.81 m/s^2
+            mFeatureInfo.typeString = SENSOR_STRING_TYPE_ACCELEROMETER;
+            mFeatureInfo.type = SENSOR_TYPE_ACCELEROMETER;
+            mFeatureInfo.isWakeUp = false;
+
+            translationTableValid = processTriAxisUsage(packets,
+                                         ACCELERATION_X_AXIS,
+                                         ACCELERATION_Y_AXIS,
+                                         ACCELERATION_Z_AXIS, 9.81);
+            break;
+        case GYROMETER_3D:
+            // Hid unit default degree/s
+            // Android unit rad/s
+            // 1 degree/s = pi/180 rad/s
+            mFeatureInfo.typeString = SENSOR_STRING_TYPE_GYROSCOPE;
+            mFeatureInfo.type = SENSOR_TYPE_GYROSCOPE;
+            mFeatureInfo.isWakeUp = false;
+
+            translationTableValid = processTriAxisUsage(packets,
+                                         ANGULAR_VELOCITY_X_AXIS,
+                                         ANGULAR_VELOCITY_Y_AXIS,
+                                         ANGULAR_VELOCITY_Z_AXIS, M_PI/180);
+            break;
+        case COMPASS_3D: {
+            // Hid unit default mGauss
+            // Android unit uT
+            // 1uT  = 0.1 nGauss
+            mFeatureInfo.typeString = SENSOR_STRING_TYPE_MAGNETIC_FIELD;
+            mFeatureInfo.type = SENSOR_TYPE_MAGNETIC_FIELD;
+
+            if (!processTriAxisUsage(packets,
+                                     MAGNETIC_FLUX_X_AXIS,
+                                     MAGNETIC_FLUX_Y_AXIS,
+                                     MAGNETIC_FLUX_Z_AXIS, 0.1)) {
+                break;
+            }
+            const HidParser::ReportItem *pReportAccuracy = find(packets,
+                                                                  MAGNETOMETER_ACCURACY,
+                                                                  HidParser::REPORT_TYPE_INPUT,
+                                                                  mInputReportId);
+
+            if (pReportAccuracy == nullptr) {
+                LOG_E << "Cannot find accuracy field in usage "
+                      << std::hex << usage << std::dec << LOG_ENDL;
+                break;
+            }
+            if (!pReportAccuracy->isByteAligned()) {
+                LOG_E << "Accuracy field must align to byte" << LOG_ENDL;
+                break;
+            }
+            if (pReportAccuracy->minRaw != 0 || pReportAccuracy->maxRaw != 2) {
+                LOG_E << "Accuracy field value range must be [0, 2]" << LOG_ENDL;
+                break;
+            }
+            ReportTranslateRecord accuracyRecord = {
+                .type = TYPE_ACCURACY,
+                .maxValue = 2,
+                .minValue = 0,
+                .byteOffset = pReportAccuracy->bitOffset / 8,
+                .byteSize = pReportAccuracy->bitSize / 8,
+                .a = 1,
+                .b = 1};
+            mTranslateTable.push_back(accuracyRecord);
+            translationTableValid = true;
+            break;
+        }
+        case DEVICE_ORIENTATION:
+            translationTableValid = processQuaternionUsage(packets);
+            break;
+        case CUSTOM: {
+            if (!mFeatureInfo.isAndroidCustom) {
+                LOG_E << "Invalid android custom sensor" << LOG_ENDL;
+                break;
+            }
+            const HidParser::ReportPacket *pPacket = nullptr;
+            const uint32_t usages[] = {
+                CUSTOM_VALUE_1, CUSTOM_VALUE_2, CUSTOM_VALUE_3,
+                CUSTOM_VALUE_4, CUSTOM_VALUE_5, CUSTOM_VALUE_6
+            };
+            for (const auto &packet : packets) {
+                if (packet.type == HidParser::REPORT_TYPE_INPUT && std::any_of(
+                        packet.reports.begin(), packet.reports.end(),
+                        [&usages] (const HidParser::ReportItem &d) {
+                               return std::find(std::begin(usages), std::end(usages), d.usage)
+                                       != std::end(usages);
+                        })) {
+                    pPacket = &packet;
+                    break;
+                }
+            }
+
+            if (pPacket == nullptr) {
+                LOG_E << "Cannot find CUSTOM_VALUE_X in custom sensor" << LOG_ENDL;
+                break;
+            }
+
+            double range = 0;
+            double resolution = 1;
+
+            for (const auto &digest : pPacket->reports) {
+                if (digest.minRaw >= digest.maxRaw) {
+                    LOG_E << "Custome usage " << digest.usage << ", min must < max" << LOG_ENDL;
+                    return;
+                }
+
+                if (!digest.isByteAligned()
+                        || (digest.bitSize != 8 && digest.bitSize != 16 && digest.bitSize != 32)) {
+                    LOG_E << "Custome usage " << std::hex << digest.usage << std::hex
+                          << ", each input must be 8/16/32 bits and must align to byte boundary"
+                          << LOG_ENDL;
+                    return;
+                }
+
+                ReportTranslateRecord record = {
+                    .minValue = digest.minRaw,
+                    .maxValue = digest.maxRaw,
+                    .byteOffset = digest.bitOffset / 8,
+                    .byteSize = digest.bitSize / 8,
+                    .a = digest.a,
+                    .b = digest.b,
+                    .type = TYPE_FLOAT
+                };
+                // keep track of range and resolution
+                range = std::max(std::max(std::abs((digest.maxRaw + digest.b) * digest.a),
+                                          std::abs((digest.minRaw + digest.b) * digest.a)),
+                                 range);
+                resolution = std::min(digest.a, resolution);
+
+                for (size_t i = 0; i < digest.count; ++i) {
+                    if (mTranslateTable.size() == 16) {
+                        LOG_I << "Custom usage has more than 16 inputs, ignore the rest" << LOG_ENDL;
+                        break;
+                    }
+                    record.index = mTranslateTable.size();
+                    mTranslateTable.push_back(record);
+                    record.byteOffset += digest.bitSize / 8;
+                }
+                if (mTranslateTable.size() == 16) {
+                    break;
+                }
+            }
+            mFeatureInfo.maxRange = range;
+            mFeatureInfo.resolution = resolution;
+            mInputReportId = pPacket->id;
+            translationTableValid = !mTranslateTable.empty();
+            break;
+        }
+        default:
+            LOG_I << "unsupported sensor usage " << usage << LOG_ENDL;
+    }
+
+    bool sensorValid = validateFeatureValueAndBuildSensor();
+    mValid = translationTableValid && sensorValid;
+    LOG_V << "HidRawSensor init, translationTableValid: " << translationTableValid
+          << ", sensorValid: " << sensorValid << LOG_ENDL;
+}
+
+bool HidRawSensor::processQuaternionUsage(const std::vector<HidParser::ReportPacket> &packets) {
+    const HidParser::ReportItem *pReportQuaternion
+            = find(packets,
+                   Hid::Sensor::ReportUsage::ORIENTATION_QUATERNION,
+                   HidParser::REPORT_TYPE_INPUT);
+
+    if (pReportQuaternion == nullptr) {
+        return false;
+    }
+
+    const HidParser::ReportItem &quat = *pReportQuaternion;
+    if ((quat.bitSize != 16 && quat.bitSize != 32) || !quat.isByteAligned()) {
+        LOG_E << "Quaternion usage input must be 16 or 32 bits and aligned at byte boundary" << LOG_ENDL;
+        return false;
+    }
+
+    double min, max;
+    quat.decode(quat.mask(quat.minRaw), &min);
+    quat.decode(quat.mask(quat.maxRaw), &max);
+    if (quat.count != 4 || min > -1 || max < 1) {
+        LOG_E << "Quaternion usage need 4 inputs with range [-1, 1]" << LOG_ENDL;
+        return false;
+    }
+
+    if (quat.minRaw > quat.maxRaw) {
+        LOG_E << "Quaternion usage min must <= max" << LOG_ENDL;
+        return false;
+    }
+
+    ReportTranslateRecord record = {
+        .minValue = quat.minRaw,
+        .maxValue = quat.maxRaw,
+        .byteOffset = quat.bitOffset / 8,
+        .byteSize = quat.bitSize / 8,
+        .b = quat.b,
+        .type = TYPE_FLOAT,
+    };
+
+    // Android X Y Z maps to HID X -Z Y
+    // Android order xyzw, HID order wxyz
+    // X
+    record.index = 0;
+    record.a = quat.a;
+    record.byteOffset = (quat.bitOffset + quat.bitSize) / 8;
+    mTranslateTable.push_back(record);
+    // Y
+    record.index = 1;
+    record.a = -quat.a;
+    record.byteOffset = (quat.bitOffset + 3 * quat.bitSize) / 8;
+    mTranslateTable.push_back(record);
+    // Z
+    record.index = 2;
+    record.a = quat.a;
+    record.byteOffset = (quat.bitOffset + 2 * quat.bitSize) / 8;
+    mTranslateTable.push_back(record);
+    // W
+    record.index = 3;
+    record.a = quat.a;
+    record.byteOffset = quat.bitOffset / 8;
+    mTranslateTable.push_back(record);
+
+    mFeatureInfo.typeString = SENSOR_STRING_TYPE_ROTATION_VECTOR;
+    mFeatureInfo.type = SENSOR_TYPE_ROTATION_VECTOR;
+    mFeatureInfo.maxRange = 1;
+    mFeatureInfo.resolution = quat.a;
+    mFeatureInfo.reportModeFlag = SENSOR_FLAG_CONTINUOUS_MODE;
+
+    mInputReportId = quat.id;
+
+    return true;
+}
+
+bool HidRawSensor::processTriAxisUsage(const std::vector<HidParser::ReportPacket> &packets,
+        uint32_t usageX, uint32_t usageY, uint32_t usageZ, double defaultScaling) {
+    const HidParser::ReportItem *pReportX = find(packets, usageX, HidParser::REPORT_TYPE_INPUT);
+    const HidParser::ReportItem *pReportY = find(packets, usageY, HidParser::REPORT_TYPE_INPUT);
+    const HidParser::ReportItem *pReportZ = find(packets, usageZ, HidParser::REPORT_TYPE_INPUT);
+
+    if (pReportX == nullptr || pReportY == nullptr|| pReportZ == nullptr) {
+        LOG_E << "Three axis sensor does not find all 3 axis" << LOG_ENDL;
+        return false;
+    }
+
+    const HidParser::ReportItem &reportX = *pReportX;
+    const HidParser::ReportItem &reportY = *pReportY;
+    const HidParser::ReportItem &reportZ = *pReportZ;
+    if (reportX.id != reportY.id || reportY.id != reportZ.id) {
+        LOG_E << "All 3 axis should be in the same report" << LOG_ENDL;
+        return false;
+    }
+    if (reportX.minRaw >= reportX.maxRaw
+            || reportX.minRaw != reportY.minRaw
+            || reportX.maxRaw != reportY.maxRaw
+            || reportY.minRaw != reportZ.minRaw
+            || reportY.maxRaw != reportZ.maxRaw) {
+        LOG_E << "All 3 axis should have same min and max value and min must < max" << LOG_ENDL;
+        return false;
+    }
+    if (reportX.a != reportY.a || reportY.a != reportY.a) {
+        LOG_E << "All 3 axis should have same resolution" << LOG_ENDL;
+        return false;
+    }
+    if (reportX.count != 1 || reportY.count != 1 || reportZ.count != 1
+            || (reportX.bitSize != 16 && reportX.bitSize != 32)
+            || reportX.bitSize != reportY.bitSize || reportY.bitSize != reportZ.bitSize
+            || !reportX.isByteAligned()
+            || !reportY.isByteAligned()
+            || !reportZ.isByteAligned() ) {
+        LOG_E << "All 3 axis should have count == 1, same size == 16 or 32 "
+              "and align at byte boundary" << LOG_ENDL;
+        return false;
+    }
+
+    if (reportX.unit != 0 || reportY.unit != 0 || reportZ.unit != 0) {
+        LOG_E << "Specified unit for usage is not supported" << LOG_ENDL;
+        return false;
+    }
+
+    if (reportX.a != reportY.a || reportY.a != reportZ.a
+        || reportX.b != reportY.b || reportY.b != reportZ.b) {
+        LOG_W << "Scaling for 3 axis are different. It is recommended to keep them the same" << LOG_ENDL;
+    }
+
+    // set features
+    mFeatureInfo.maxRange = std::max(
+        std::abs((reportX.maxRaw + reportX.b) * reportX.a),
+        std::abs((reportX.minRaw + reportX.b) * reportX.a));
+    mFeatureInfo.resolution = reportX.a * defaultScaling;
+    mFeatureInfo.reportModeFlag = SENSOR_FLAG_CONTINUOUS_MODE;
+
+    ReportTranslateRecord record = {
+        .minValue = reportX.minRaw,
+        .maxValue = reportX.maxRaw,
+        .byteSize = reportX.bitSize / 8,
+        .type = TYPE_FLOAT
+    };
+
+    // Reorder and swap axis
+    //
+    // HID class devices are encouraged, where possible, to use a right-handed
+    // coordinate system. If a user is facing a device, report values should increase as
+    // controls are moved from left to right (X), from far to near (Y) and from high to
+    // low (Z).
+    //
+
+    // Android X axis = Hid X axis
+    record.index = 0;
+    record.a = reportX.a * defaultScaling;
+    record.b = reportX.b;
+    record.byteOffset = reportX.bitOffset / 8;
+    mTranslateTable.push_back(record);
+
+    // Android Y axis = - Hid Z axis
+    record.index = 1;
+    record.a = -reportZ.a * defaultScaling;
+    record.b = reportZ.b;
+    record.byteOffset = reportZ.bitOffset / 8;
+    mTranslateTable.push_back(record);
+
+    // Android Z axis = Hid Y axis
+    record.index = 2;
+    record.a = reportY.a * defaultScaling;
+    record.b = reportY.b;
+    record.byteOffset = reportY.bitOffset / 8;
+    mTranslateTable.push_back(record);
+
+    mInputReportId = reportX.id;
+    return true;
+}
+
+const HidParser::ReportItem *HidRawSensor::find(
+        const std::vector<HidParser::ReportPacket> &packets,
+        unsigned int usage, int type, int id) {
+    for (const auto &packet : packets) {
+        if (packet.type != type) {
+            continue;
+        }
+        auto i = std::find_if(
+                packet.reports.begin(), packet.reports.end(),
+                [usage, id](const HidParser::ReportItem &p) {
+                    return p.usage == usage
+                            && (id == -1 || p.id == static_cast<unsigned int>(id));
+                });
+        if (i != packet.reports.end()) {
+            return &(*i);
+        }
+    }
+    return nullptr;
+};
+
+void HidRawSensor::initFeatureValueFromHidDeviceInfo(
+        FeatureValue *featureValue, const HidDevice::HidDeviceInfo &info) {
+    featureValue->name = info.name;
+
+    std::ostringstream ss;
+    ss << info.busType << " "
+       << std::hex << std::setfill('0') << std::setw(4) << info.vendorId
+       << ":" << std::setw(4) << info.productId;
+    featureValue->vendor = ss.str();
+
+    featureValue->permission = "";
+    featureValue->typeString = "";
+    featureValue->type = -1; // invalid type
+    featureValue->version = 1;
+
+    featureValue->maxRange = -1.f;
+    featureValue->resolution = FLT_MAX;
+    featureValue->power = 1.f; // default value, does not have a valid source yet
+
+    featureValue->minDelay = 0;
+    featureValue->maxDelay = 0;
+
+    featureValue->fifoSize = 0;
+    featureValue->fifoMaxSize = 0;
+
+    featureValue->reportModeFlag = SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+    featureValue->isWakeUp = false;
+    memset(featureValue->uuid, 0, sizeof(featureValue->uuid));
+    featureValue->isAndroidCustom = false;
+}
+
+bool HidRawSensor::populateFeatureValueFromFeatureReport(
+        FeatureValue *featureValue, const std::vector<HidParser::ReportPacket> &packets) {
+    SP(HidDevice) device = PROMOTE(mDevice);
+    if (device == nullptr) {
+        return false;
+    }
+
+    std::vector<uint8_t> buffer;
+    for (const auto &packet : packets) {
+        if (packet.type != HidParser::REPORT_TYPE_FEATURE) {
+            continue;
+        }
+
+        if (!device->getFeature(packet.id, &buffer)) {
+            continue;
+        }
+
+        std::string str;
+        using namespace Hid::Sensor::PropertyUsage;
+        for (const auto & r : packet.reports) {
+            switch (r.usage) {
+                case FRIENDLY_NAME:
+                    if (!r.isByteAligned() || r.bitSize != 16 || r.count < 1) {
+                        // invalid friendly name
+                        break;
+                    }
+                    if (decodeString(r, buffer, &str) && !str.empty()) {
+                        featureValue->name = str;
+                    }
+                    break;
+                case SENSOR_MANUFACTURER:
+                    if (!r.isByteAligned() || r.bitSize != 16 || r.count < 1) {
+                        // invalid manufacturer
+                        break;
+                    }
+                    if (decodeString(r, buffer, &str) && !str.empty()) {
+                        featureValue->vendor = str;
+                    }
+                    break;
+                case PERSISTENT_UNIQUE_ID:
+                    if (!r.isByteAligned() || r.bitSize != 16 || r.count < 1) {
+                        // invalid unique id string
+                        break;
+                    }
+                    if (decodeString(r, buffer, &str) && !str.empty()) {
+                        featureValue->uniqueId = str;
+                    }
+                    break;
+                case SENSOR_DESCRIPTION:
+                    if (!r.isByteAligned() || r.bitSize != 16 || r.count < 1
+                            || (r.bitOffset / 8 + r.count * 2) > buffer.size() ) {
+                        // invalid description
+                        break;
+                    }
+                    if (decodeString(r, buffer, &str)) {
+                        mFeatureInfo.isAndroidCustom = detectAndroidCustomSensor(str);
+                    }
+                    break;
+                default:
+                    // do not care about others
+                    break;
+            }
+        }
+    }
+    return true;
+}
+
+bool HidRawSensor::validateFeatureValueAndBuildSensor() {
+    if (mFeatureInfo.name.empty() || mFeatureInfo.vendor.empty() || mFeatureInfo.typeString.empty()
+            || mFeatureInfo.type <= 0 || mFeatureInfo.maxRange <= 0
+            || mFeatureInfo.resolution <= 0) {
+        return false;
+    }
+
+    switch (mFeatureInfo.reportModeFlag) {
+        case SENSOR_FLAG_CONTINUOUS_MODE:
+        case SENSOR_FLAG_ON_CHANGE_MODE:
+            if (mFeatureInfo.minDelay < 0) {
+                return false;
+            }
+            if (mFeatureInfo.maxDelay != 0 && mFeatureInfo.maxDelay < mFeatureInfo.minDelay) {
+                return false;
+            }
+            break;
+        case SENSOR_FLAG_ONE_SHOT_MODE:
+            if (mFeatureInfo.minDelay != -1 && mFeatureInfo.maxDelay != 0) {
+                return false;
+            }
+            break;
+        case SENSOR_FLAG_SPECIAL_REPORTING_MODE:
+            if (mFeatureInfo.minDelay != -1 && mFeatureInfo.maxDelay != 0) {
+                return false;
+            }
+            break;
+        default:
+            break;
+    }
+
+    if (mFeatureInfo.fifoMaxSize < mFeatureInfo.fifoSize) {
+        return false;
+    }
+
+    // initialize uuid field, use name, vendor and uniqueId
+    if (mFeatureInfo.name.size() >= 4
+            && mFeatureInfo.vendor.size() >= 4
+            && mFeatureInfo.typeString.size() >= 4
+            && mFeatureInfo.uniqueId.size() >= 4) {
+        uint32_t tmp[4], h;
+        std::hash<std::string> stringHash;
+        h = stringHash(mFeatureInfo.uniqueId);
+        tmp[0] = stringHash(mFeatureInfo.name) ^ h;
+        tmp[1] = stringHash(mFeatureInfo.vendor) ^ h;
+        tmp[2] = stringHash(mFeatureInfo.typeString) ^ h;
+        tmp[3] = tmp[0] ^ tmp[1] ^ tmp[2];
+        memcpy(mFeatureInfo.uuid, tmp, sizeof(mFeatureInfo.uuid));
+    }
+
+    mSensor = (sensor_t) {
+        mFeatureInfo.name.c_str(),                 // name
+        mFeatureInfo.vendor.c_str(),               // vendor
+        mFeatureInfo.version,                      // version
+        -1,                                        // handle, dummy number here
+        mFeatureInfo.type,
+        mFeatureInfo.maxRange,                     // maxRange
+        mFeatureInfo.resolution,                   // resolution
+        mFeatureInfo.power,                        // power
+        mFeatureInfo.minDelay,                     // minDelay
+        (uint32_t)mFeatureInfo.fifoSize,           // fifoReservedEventCount
+        (uint32_t)mFeatureInfo.fifoMaxSize,        // fifoMaxEventCount
+        mFeatureInfo.typeString.c_str(),           // type string
+        mFeatureInfo.permission.c_str(),           // requiredPermission
+        (long)mFeatureInfo.maxDelay,               // maxDelay
+        mFeatureInfo.reportModeFlag | (mFeatureInfo.isWakeUp ? 1 : 0),
+        { NULL, NULL }
+    };
+    return true;
+}
+
+bool HidRawSensor::decodeString(
+        const HidParser::ReportItem &report, const std::vector<uint8_t> &buffer, std::string *d) {
+    if (!report.isByteAligned() || report.bitSize != 16 || report.count < 1) {
+        return false;
+    }
+
+    size_t offset = report.bitOffset / 8;
+    if (offset + report.count * 2 > buffer.size()) {
+        return false;
+    }
+
+    std::vector<uint16_t> data(report.count);
+    auto i = data.begin();
+    auto j = buffer.begin() + offset;
+    for ( ; i != data.end(); ++i, j += sizeof(uint16_t)) {
+        // hid specified little endian
+        *i = *j + (*(j + 1) << 8);
+    }
+    std::wstring wstr(data.begin(), data.end());
+
+    std::wstring_convert<std::codecvt_utf8<wchar_t>, wchar_t> converter;
+    *d = converter.to_bytes(wstr);
+    return true;
+}
+
+std::vector<std::string> split(const std::string &text, char sep) {
+    std::vector<std::string> tokens;
+    size_t start = 0, end = 0;
+    while ((end = text.find(sep, start)) != std::string::npos) {
+        if (end != start) {
+            tokens.push_back(text.substr(start, end - start));
+        }
+        start = end + 1;
+    }
+    if (end != start) {
+        tokens.push_back(text.substr(start));
+    }
+    return tokens;
+}
+
+bool HidRawSensor::detectAndroidCustomSensor(const std::string &description) {
+    size_t nullPosition = description.find('\0');
+    if (nullPosition == std::string::npos) {
+        return false;
+    }
+    const std::string prefix("#ANDROID#");
+    if (description.find(prefix, nullPosition + 1) != nullPosition + 1) {
+        return false;
+    }
+
+    std::string str(description.c_str() + nullPosition + 1 + prefix.size());
+
+    // Format for predefined sensor types:
+    // #ANDROID#nn,[C|X|T|S],[B|0],[W|N]
+    // Format for vendor type sensor
+    // #ANDROID#xxx.yyy.zzz,[C|X|T|S],[B|0],[W|N]
+    //
+    // C: continuous
+    // X: on-change
+    // T: one-shot
+    // S: special trigger
+    //
+    // B: body permission
+    // 0: no permission required
+    std::vector<std::string> segments;
+    size_t start = 0, end = 0;
+    while ((end = str.find(',', start)) != std::string::npos) {
+        if (end != start) {
+            segments.push_back(str.substr(start, end - start));
+        }
+        start = end + 1;
+    }
+    if (end != start) {
+        segments.push_back(str.substr(start));
+    }
+
+    if (segments.size() < 4) {
+        LOG_E << "Not enough segments in android custom description" << LOG_ENDL;
+        return false;
+    }
+
+    // type
+    bool typeParsed = false;
+    if (!segments[0].empty()) {
+        if (::isdigit(segments[0][0])) {
+            int type = ::atoi(segments[0].c_str());
+            // all supported types here
+            switch (type) {
+                case SENSOR_TYPE_HEART_RATE:
+                    mFeatureInfo.type = SENSOR_TYPE_HEART_RATE;
+                    mFeatureInfo.typeString = SENSOR_STRING_TYPE_HEART_RATE;
+                    typeParsed = true;
+                    break;
+                case SENSOR_TYPE_AMBIENT_TEMPERATURE:
+                    mFeatureInfo.type = SENSOR_TYPE_AMBIENT_TEMPERATURE;
+                    mFeatureInfo.typeString = SENSOR_STRING_TYPE_AMBIENT_TEMPERATURE;
+                    typeParsed = true;
+                case SENSOR_TYPE_LIGHT:
+                    mFeatureInfo.type = SENSOR_TYPE_LIGHT;
+                    mFeatureInfo.typeString = SENSOR_STRING_TYPE_LIGHT;
+                    typeParsed = true;
+                    break;
+                case SENSOR_TYPE_PRESSURE:
+                    mFeatureInfo.type = SENSOR_TYPE_PRESSURE;
+                    mFeatureInfo.typeString = SENSOR_STRING_TYPE_PRESSURE;
+                    typeParsed = true;
+                    break;
+                default:
+                    LOG_W << "Android type " << type << " has not been supported yet" << LOG_ENDL;
+                    break;
+            }
+        } else {
+            // assume a xxx.yyy.zzz format
+            std::ostringstream s;
+            bool lastIsDot = true;
+            for (auto c : segments[0]) {
+                if (::isalpha(c)) {
+                    s << static_cast<char>(c);
+                    lastIsDot = false;
+                } else if (!lastIsDot && c == '.') {
+                    s << static_cast<char>(c);
+                    lastIsDot = true;
+                } else {
+                    break;
+                }
+            }
+            if (s.str() == segments[0]) {
+                mFeatureInfo.type = SENSOR_TYPE_DEVICE_PRIVATE_BASE;
+                mFeatureInfo.typeString = CUSTOM_TYPE_PREFIX + s.str();
+                typeParsed = true;
+            }
+        }
+    }
+
+    // reporting type
+    bool reportingModeParsed = false;
+    if (segments[1].size() == 1) {
+        switch (segments[1][0]) {
+            case 'C':
+                mFeatureInfo.reportModeFlag = SENSOR_FLAG_CONTINUOUS_MODE;
+                reportingModeParsed = true;
+                break;
+            case 'X':
+                mFeatureInfo.reportModeFlag = SENSOR_FLAG_ON_CHANGE_MODE;
+                reportingModeParsed = true;
+                break;
+            case 'T':
+                mFeatureInfo.reportModeFlag = SENSOR_FLAG_ONE_SHOT_MODE;
+                reportingModeParsed = true;
+                break;
+            case 'S':
+                mFeatureInfo.reportModeFlag = SENSOR_FLAG_SPECIAL_REPORTING_MODE;
+                reportingModeParsed = true;
+                break;
+            default:
+                LOG_E << "Undefined reporting mode designation " << segments[1] << LOG_ENDL;
+        }
+    }
+
+    // permission parsed
+    bool permissionParsed = false;
+    if (segments[2].size() == 1) {
+        switch (segments[2][0]) {
+            case 'B':
+                mFeatureInfo.permission = SENSOR_PERMISSION_BODY_SENSORS;
+                permissionParsed = true;
+                break;
+            case '0':
+                mFeatureInfo.permission = "";
+                permissionParsed = true;
+                break;
+            default:
+                LOG_E << "Undefined permission designation " << segments[2] << LOG_ENDL;
+        }
+    }
+
+    // wake up
+    bool wakeUpParsed = false;
+    if (segments[3].size() == 1) {
+        switch (segments[3][0]) {
+            case 'W':
+                mFeatureInfo.isWakeUp = true;
+                wakeUpParsed = true;
+                break;
+            case 'N':
+                mFeatureInfo.isWakeUp = false;
+                wakeUpParsed = true;
+                break;
+            default:
+                LOG_E << "Undefined wake up designation " << segments[3] << LOG_ENDL;
+        }
+    }
+
+    int ret = typeParsed && reportingModeParsed && permissionParsed && wakeUpParsed;
+    if (!ret) {
+        LOG_D << "detectAndroidCustomSensor typeParsed: " << typeParsed
+              << " reportingModeParsed: "  << reportingModeParsed
+              << " permissionParsed: " << permissionParsed
+              << " wakeUpParsed: " << wakeUpParsed << LOG_ENDL;
+    }
+    return ret;
+}
+
+bool HidRawSensor::findSensorControlUsage(const std::vector<HidParser::ReportPacket> &packets) {
+    using namespace Hid::Sensor::PropertyUsage;
+    using namespace Hid::Sensor::RawMinMax;
+
+    //REPORTING_STATE
+    const HidParser::ReportItem *reportingState
+            = find(packets, REPORTING_STATE, HidParser::REPORT_TYPE_FEATURE);
+
+    if (reportingState == nullptr
+            || !reportingState->isByteAligned()
+            || reportingState->bitSize != 8
+            || reportingState->minRaw != REPORTING_STATE_MIN
+            || reportingState->maxRaw != REPORTING_STATE_MAX) {
+        LOG_W << "Cannot find valid reporting state feature" << LOG_ENDL;
+    } else {
+        mReportingStateId = reportingState->id;
+        mReportingStateOffset = reportingState->bitOffset / 8;
+    }
+
+    //POWER_STATE
+    const HidParser::ReportItem *powerState
+            = find(packets, POWER_STATE, HidParser::REPORT_TYPE_FEATURE);
+    if (powerState == nullptr
+            || !powerState->isByteAligned()
+            || powerState->bitSize != 8
+            || powerState->minRaw != POWER_STATE_MIN
+            || powerState->maxRaw != POWER_STATE_MAX) {
+        LOG_W << "Cannot find valid power state feature" << LOG_ENDL;
+    } else {
+        mPowerStateId = powerState->id;
+        mPowerStateOffset = powerState->bitOffset / 8;
+    }
+
+    //REPORT_INTERVAL
+    const HidParser::ReportItem *reportInterval
+            = find(packets, REPORT_INTERVAL, HidParser::REPORT_TYPE_FEATURE);
+    if (reportInterval == nullptr
+            || !reportInterval->isByteAligned()
+            || reportInterval->minRaw < 0
+            || (reportInterval->bitSize != 16 && reportInterval->bitSize != 32)) {
+        LOG_W << "Cannot find valid report interval feature" << LOG_ENDL;
+    } else {
+        mReportIntervalId = reportInterval->id;
+        mReportIntervalOffset = reportInterval->bitOffset / 8;
+        mReportIntervalSize = reportInterval->bitSize / 8;
+
+        mFeatureInfo.minDelay = std::max(static_cast<int64_t>(1), reportInterval->minRaw) * 1000;
+        mFeatureInfo.maxDelay = std::min(static_cast<int64_t>(1000000),
+                                    reportInterval->maxRaw) * 1000; // maximum 1000 second
+    }
+    return true;
+    return (mPowerStateId >= 0 || mReportingStateId >= 0) && mReportIntervalId >= 0;
+}
+
+const sensor_t* HidRawSensor::getSensor() const {
+    return &mSensor;
+}
+
+void HidRawSensor::getUuid(uint8_t* uuid) const {
+    memcpy(uuid, mFeatureInfo.uuid, sizeof(mFeatureInfo.uuid));
+}
+
+int HidRawSensor::enable(bool enable) {
+    using namespace Hid::Sensor::StateValue;
+    SP(HidDevice) device = PROMOTE(mDevice);
+
+    if (device == nullptr) {
+        return NO_INIT;
+    }
+
+    if (enable == mEnabled) {
+        return NO_ERROR;
+    }
+
+    std::vector<uint8_t> buffer;
+    bool setPowerOk = true;
+    if (mPowerStateId >= 0) {
+        setPowerOk = false;
+        uint8_t id = static_cast<uint8_t>(mPowerStateId);
+        if (device->getFeature(id, &buffer)
+                && buffer.size() > mPowerStateOffset) {
+            buffer[mPowerStateOffset] = enable ? POWER_STATE_FULL_POWER : POWER_STATE_POWER_OFF;
+            setPowerOk = device->setFeature(id, buffer);
+        } else {
+            LOG_E << "enable: changing POWER STATE failed" << LOG_ENDL;
+        }
+    }
+
+    bool setReportingOk = true;
+    if (mReportingStateId >= 0) {
+        setReportingOk = false;
+        uint8_t id = static_cast<uint8_t>(mReportingStateId);
+        if (device->getFeature(id, &buffer)
+                && buffer.size() > mReportingStateOffset) {
+            buffer[mReportingStateOffset]
+                    = enable ? REPORTING_STATE_ALL_EVENT : REPORTING_STATE_NO_EVENT;
+            setReportingOk = device->setFeature(id, buffer);
+        } else {
+            LOG_E << "enable: changing REPORTING STATE failed" << LOG_ENDL;
+        }
+    }
+
+    if (setPowerOk && setReportingOk) {
+        mEnabled = enable;
+        return NO_ERROR;
+    } else {
+        return INVALID_OPERATION;
+    }
+}
+
+int HidRawSensor::batch(int64_t samplingPeriod, int64_t batchingPeriod) {
+    SP(HidDevice) device = PROMOTE(mDevice);
+    if (device == nullptr) {
+        return NO_INIT;
+    }
+
+    if (samplingPeriod < 0 || batchingPeriod < 0) {
+        return BAD_VALUE;
+    }
+
+    bool needRefresh = mSamplingPeriod != samplingPeriod || mBatchingPeriod != batchingPeriod;
+    std::vector<uint8_t> buffer;
+
+    bool ok = true;
+    if (needRefresh && mReportIntervalId >= 0) {
+        ok = false;
+        uint8_t id = static_cast<uint8_t>(mReportIntervalId);
+        if (device->getFeature(id, &buffer)
+                && buffer.size() >= mReportIntervalOffset + mReportIntervalSize) {
+            int64_t periodMs = samplingPeriod / 1000000; //ns -> ms
+            switch (mReportIntervalSize) {
+                case sizeof(uint16_t):
+                    periodMs = std::min(periodMs, static_cast<int64_t>(UINT16_MAX));
+                    buffer[mReportIntervalOffset] = periodMs & 0xFF;
+                    buffer[mReportIntervalOffset + 1] = (periodMs >> 8) & 0xFF;
+                case sizeof(uint32_t):
+                    periodMs = std::min(periodMs, static_cast<int64_t>(UINT32_MAX));
+                    buffer[mReportIntervalOffset] = periodMs & 0xFF;
+                    buffer[mReportIntervalOffset + 1] = (periodMs >> 8) & 0xFF;
+                    buffer[mReportIntervalOffset + 2] = (periodMs >> 16) & 0xFF;
+                    buffer[mReportIntervalOffset + 3] = (periodMs >> 24) & 0xFF;
+            }
+            ok = device->setFeature(id, buffer);
+        }
+    }
+
+    if (ok) {
+        mSamplingPeriod = samplingPeriod;
+        mBatchingPeriod = batchingPeriod;
+        return NO_ERROR;
+    } else {
+        return INVALID_OPERATION;
+    }
+}
+
+void HidRawSensor::handleInput(uint8_t id, const std::vector<uint8_t> &message) {
+    if (id != mInputReportId || mEnabled == false) {
+        return;
+    }
+    sensors_event_t event = {
+        .version = sizeof(event),
+        .sensor = -1,
+        .type = mSensor.type
+    };
+    bool valid = true;
+    for (const auto &rec : mTranslateTable) {
+        int64_t v = (message[rec.byteOffset + rec.byteSize - 1] & 0x80) ? -1 : 0;
+        for (int i = static_cast<int>(rec.byteSize) - 1; i >= 0; --i) {
+            v = (v << 8) | message[rec.byteOffset + i]; // HID is little endian
+        }
+
+        switch (rec.type) {
+            case TYPE_FLOAT:
+                if (v > rec.maxValue || v < rec.minValue) {
+                    valid = false;
+                }
+                event.data[rec.index] = rec.a * (v + rec.b);
+                break;
+            case TYPE_INT64:
+                if (v > rec.maxValue || v < rec.minValue) {
+                    valid = false;
+                }
+                event.u64.data[rec.index] = v + rec.b;
+                break;
+            case TYPE_ACCURACY:
+                event.magnetic.status = (v & 0xFF) + rec.b;
+                break;
+        }
+    }
+    if (!valid) {
+        LOG_V << "Range error observed in decoding, discard" << LOG_ENDL;
+    }
+    event.timestamp = -1;
+    generateEvent(event);
+}
+
+std::string HidRawSensor::dump() const {
+    std::ostringstream ss;
+    ss << "Feature Values " << LOG_ENDL
+          << "  name: " << mFeatureInfo.name << LOG_ENDL
+          << "  vendor: " << mFeatureInfo.vendor << LOG_ENDL
+          << "  permission: " << mFeatureInfo.permission << LOG_ENDL
+          << "  typeString: " << mFeatureInfo.typeString << LOG_ENDL
+          << "  type: " << mFeatureInfo.type << LOG_ENDL
+          << "  maxRange: " << mFeatureInfo.maxRange << LOG_ENDL
+          << "  resolution: " << mFeatureInfo.resolution << LOG_ENDL
+          << "  power: " << mFeatureInfo.power << LOG_ENDL
+          << "  minDelay: " << mFeatureInfo.minDelay << LOG_ENDL
+          << "  maxDelay: " << mFeatureInfo.maxDelay << LOG_ENDL
+          << "  fifoSize: " << mFeatureInfo.fifoSize << LOG_ENDL
+          << "  fifoMaxSize: " << mFeatureInfo.fifoMaxSize << LOG_ENDL
+          << "  reportModeFlag: " << mFeatureInfo.reportModeFlag << LOG_ENDL
+          << "  isWakeUp: " << (mFeatureInfo.isWakeUp ? "true" : "false") << LOG_ENDL
+          << "  uniqueId: " << mFeatureInfo.uniqueId << LOG_ENDL
+          << "  uuid: ";
+
+    ss << std::hex << std::setfill('0');
+    for (auto d : mFeatureInfo.uuid) {
+          ss << std::setw(2) << static_cast<int>(d) << " ";
+    }
+    ss << std::dec << std::setfill(' ') << LOG_ENDL;
+
+    ss << "Input report id: " << mInputReportId << LOG_ENDL;
+    for (const auto &t : mTranslateTable) {
+        ss << "  type, index: " << t.type << ", " << t.index
+              << "; min,max: " << t.minValue << ", " << t.maxValue
+              << "; byte-offset,size: " << t.byteOffset << ", " << t.byteSize
+              << "; scaling,bias: " << t.a << ", " << t.b << LOG_ENDL;
+    }
+
+    ss << "Control features: " << LOG_ENDL;
+    ss << "  Power state ";
+    if (mPowerStateId >= 0) {
+        ss << "found, id: " << mPowerStateId
+              << " offset: " << mPowerStateOffset << LOG_ENDL;
+    } else {
+        ss << "not found" << LOG_ENDL;
+    }
+
+    ss << "  Reporting state ";
+    if (mReportingStateId >= 0) {
+        ss << "found, id: " << mReportingStateId
+              << " offset: " << mReportingStateOffset << LOG_ENDL;
+    } else {
+        ss << "not found" << LOG_ENDL;
+    }
+
+    ss << "  Report interval ";
+    if (mReportIntervalId >= 0) {
+        ss << "found, id: " << mReportIntervalId
+              << " offset: " << mReportIntervalOffset
+              << " size: " << mReportIntervalSize << LOG_ENDL;
+    } else {
+        ss << "not found" << LOG_ENDL;
+    }
+    return ss.str();
+}
+
+} // namespace SensorHalExt
+} // namespace android
diff --git a/modules/sensors/dynamic_sensor/HidRawSensor.h b/modules/sensors/dynamic_sensor/HidRawSensor.h
new file mode 100644
index 0000000..2dd32b6
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawSensor.h
@@ -0,0 +1,168 @@
+/*
+ * 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 ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_H
+#define ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_H
+
+#include "BaseSensorObject.h"
+#include "HidDevice.h"
+#include "Utils.h"
+
+#include <HidParser.h>
+#include <hardware/sensors.h>
+
+namespace android {
+namespace SensorHalExt {
+
+using HidUtil::HidParser;
+using ReportPacket = HidParser::ReportPacket;
+using ReportItem = HidParser::ReportItem;
+
+class HidRawSensor : public BaseSensorObject {
+    friend class HidRawSensorTest;
+    friend class HidRawDeviceTest;
+public:
+    HidRawSensor(SP(HidDevice) device, uint32_t usage,
+                 const std::vector<HidParser::ReportPacket> &report);
+
+    // implements BaseSensorObject
+    virtual const sensor_t* getSensor() const;
+    virtual void getUuid(uint8_t* uuid) const;
+    virtual int enable(bool enable);
+    virtual int batch(int64_t samplePeriod, int64_t batchPeriod); // unit nano-seconds
+
+    // handle input report received
+    void handleInput(uint8_t id, const std::vector<uint8_t> &message);
+
+    // indicate if the HidRawSensor is a valid one
+    bool isValid() const { return mValid; };
+
+private:
+
+    // structure used for holding descriptor parse result for each report field
+    enum {
+        TYPE_FLOAT,
+        TYPE_INT64,
+        TYPE_ACCURACY
+    };
+    struct ReportTranslateRecord {
+        int type;
+        int index;
+        int64_t maxValue;
+        int64_t minValue;
+        size_t byteOffset;
+        size_t byteSize;
+        double a;
+        int64_t b;
+    };
+
+    // sensor related information parsed from HID descriptor
+    struct FeatureValue {
+        // information needed to furnish sensor_t structure (see hardware/sensors.h)
+        std::string name;
+        std::string vendor;
+        std::string permission;
+        std::string typeString;
+        int32_t type;
+        int version;
+        float maxRange;
+        float resolution;
+        float power;
+        int32_t minDelay;
+        int64_t maxDelay;
+        size_t fifoSize;
+        size_t fifoMaxSize;
+        uint32_t reportModeFlag;
+        bool isWakeUp;
+
+        // dynamic sensor specific
+        std::string uniqueId;
+        uint8_t uuid[16];
+
+        // if the device is custom sensor HID device that furnished android specific descriptors
+        bool isAndroidCustom;
+    };
+
+    // helper function to find the first report item with specified usage, type and id.
+    // if parameter id is omitted, this function looks for usage with all ids.
+    // return nullptr if nothing is found.
+    static const HidParser::ReportItem* find
+            (const std::vector<HidParser::ReportPacket> &packets,
+            unsigned int usage, int type, int id = -1);
+
+    // helper function to decode std::string from HID feature report buffer.
+    static bool decodeString(
+            const HidParser::ReportItem &report,
+            const std::vector<uint8_t> &buffer, std::string *d);
+
+    // initialize default feature values default based on hid device info
+    static void initFeatureValueFromHidDeviceInfo(
+            FeatureValue *featureValue, const HidDevice::HidDeviceInfo &info);
+
+    // populates feature values from descripitors and hid feature reports
+    bool populateFeatureValueFromFeatureReport(
+            FeatureValue *featureValue, const std::vector<HidParser::ReportPacket> &packets);
+
+    // validate feature values and construct sensor_t structure if values are ok.
+    bool validateFeatureValueAndBuildSensor();
+
+    // helper function to find sensor control feature usage from packets
+    bool findSensorControlUsage(const std::vector<HidParser::ReportPacket> &packets);
+
+    // try to parse sensor description feature value to see if it matches
+    // android specified custom sensor definition.
+    bool detectAndroidCustomSensor(const std::string &description);
+
+    // process HID sensor spec defined three axis sensors usages: accel, gyro, mag.
+    bool processTriAxisUsage(const std::vector<HidParser::ReportPacket> &packets,
+            uint32_t usageX, uint32_t usageY, uint32_t usageZ, double defaultScaling = 1);
+
+    // process HID snesor spec defined orientation(quaternion) sensor usages.
+    bool processQuaternionUsage(const std::vector<HidParser::ReportPacket> &packets);
+
+    // dump data for test/debug purpose
+    std::string dump() const;
+
+    // Features for control sensor
+    int mReportingStateId;
+    unsigned int mReportingStateOffset;
+
+    int mPowerStateId;
+    unsigned int mPowerStateOffset;
+
+    int mReportIntervalId;
+    unsigned int mReportIntervalOffset;
+    unsigned int mReportIntervalSize;
+
+    // Input report translate table
+    std::vector<ReportTranslateRecord> mTranslateTable;
+    unsigned mInputReportId;
+
+    FeatureValue mFeatureInfo;
+    sensor_t mSensor;
+
+    // runtime states variable
+    bool mEnabled;
+    int64_t mSamplingPeriod;    // ns
+    int64_t mBatchingPeriod;    // ns
+
+    WP(HidDevice) mDevice;
+    bool mValid;
+};
+
+} // namespace SensorHalExt
+} // namespace android
+#endif // ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_H
+
diff --git a/modules/sensors/dynamic_sensor/HidRawSensorDaemon.cpp b/modules/sensors/dynamic_sensor/HidRawSensorDaemon.cpp
new file mode 100644
index 0000000..6bf34bc
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawSensorDaemon.cpp
@@ -0,0 +1,71 @@
+/*
+ * 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 "HidRawSensorDaemon.h"
+#include "ConnectionDetector.h"
+#include "DynamicSensorManager.h"
+#include "HidRawSensorDevice.h"
+
+#include <utils/Log.h>
+#include <utils/SystemClock.h>
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+
+#include <iomanip>
+#include <sstream>
+
+#define DEV_PATH                "/dev/"
+#define DEV_NAME_REGEX          "^hidraw[0-9]+$"
+
+namespace android {
+namespace SensorHalExt {
+
+HidRawSensorDaemon::HidRawSensorDaemon(DynamicSensorManager& manager)
+        : BaseDynamicSensorDaemon(manager) {
+    mDetector = new FileConnectionDetector(
+            this, std::string(DEV_PATH), std::string(DEV_NAME_REGEX));
+}
+
+BaseSensorVector HidRawSensorDaemon::createSensor(const std::string &deviceKey) {
+    BaseSensorVector ret;
+    sp<HidRawSensorDevice> device(HidRawSensorDevice::create(deviceKey));
+
+    if (device != nullptr) {
+        ALOGV("created HidRawSensorDevice(%p) successfully on device %s contains %zu sensors",
+              device.get(), deviceKey.c_str(), device->getSensors().size());
+
+        // convert type
+        for (auto &i : device->getSensors()) {
+            ret.push_back(i);
+        }
+        mHidRawSensorDevices.emplace(deviceKey, device);
+    } else {
+        ALOGE("failed to create HidRawSensorDevice object");
+    }
+
+    ALOGE("return %zu sensors", ret.size());
+    return ret;
+}
+
+void HidRawSensorDaemon::removeSensor(const std::string &deviceKey) {
+    mHidRawSensorDevices.erase(deviceKey);
+}
+
+} // namespace SensorHalExt
+} // namespace android
+
diff --git a/modules/sensors/dynamic_sensor/HidRawSensorDaemon.h b/modules/sensors/dynamic_sensor/HidRawSensorDaemon.h
new file mode 100644
index 0000000..fc4b2a2
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawSensorDaemon.h
@@ -0,0 +1,63 @@
+/*
+ * 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 ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_DAEMON_H
+#define ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_DAEMON_H
+
+#include "BaseDynamicSensorDaemon.h"
+
+#include <HidParser.h>
+#include <hardware/sensors.h>
+#include <utils/Thread.h>
+
+#include <memory>
+#include <string>
+#include <unordered_map>
+#include <unordered_set>
+#include <vector>
+
+
+using HidUtil::HidParser;
+using HidUtil::HidReport;
+using HidUtil::HidItem;
+
+namespace android {
+namespace SensorHalExt {
+
+class HidRawSensorDevice;
+class ConnectionDetector;
+
+class HidRawSensorDaemon : public BaseDynamicSensorDaemon {
+    friend class HidRawSensorDaemonTest;
+public:
+    HidRawSensorDaemon(DynamicSensorManager& manager);
+    virtual ~HidRawSensorDaemon() = default;
+private:
+    virtual BaseSensorVector createSensor(const std::string &deviceKey);
+    virtual void removeSensor(const std::string &deviceKey);
+
+    class HidRawSensor;
+    void registerExisting();
+
+    sp<ConnectionDetector> mDetector;
+    std::unordered_map<std::string, sp<HidRawSensorDevice>> mHidRawSensorDevices;
+};
+
+} // namespace SensorHalExt
+} // namespace android
+
+#endif // ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_DAEMON_H
+
diff --git a/modules/sensors/dynamic_sensor/HidRawSensorDevice.cpp b/modules/sensors/dynamic_sensor/HidRawSensorDevice.cpp
new file mode 100644
index 0000000..16e9060
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawSensorDevice.cpp
@@ -0,0 +1,123 @@
+/*
+ * 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 "HidRawSensorDevice.h"
+#include "HidRawSensor.h"
+#include "HidSensorDef.h"
+
+#include <utils/Log.h>
+#include <fcntl.h>
+#include <linux/input.h>
+#include <linux/hidraw.h>
+#include <sys/ioctl.h>
+
+#include <set>
+
+namespace android {
+namespace SensorHalExt {
+
+using namespace Hid::Sensor::SensorTypeUsage;
+using namespace Hid::Sensor::PropertyUsage;
+
+const std::unordered_set<unsigned int> HidRawSensorDevice::sInterested{
+        ACCELEROMETER_3D, GYROMETER_3D, COMPASS_3D, CUSTOM};
+
+sp<HidRawSensorDevice> HidRawSensorDevice::create(const std::string &devName) {
+    sp<HidRawSensorDevice> device(new HidRawSensorDevice(devName));
+    // remove +1 strong count added by constructor
+    device->decStrong(device.get());
+
+    if (device->mValid) {
+        return device;
+    } else {
+        return nullptr;
+    }
+}
+
+HidRawSensorDevice::HidRawSensorDevice(const std::string &devName)
+        : RefBase(), HidRawDevice(devName, sInterested),
+          Thread(false /*canCallJava*/), mValid(false) {
+    if (!HidRawDevice::isValid()) {
+        return;
+    }
+    // create HidRawSensor objects from digest
+    // HidRawSensor object will take sp<HidRawSensorDevice> as parameter, so increment strong count
+    // to prevent "this" being destructed.
+    this->incStrong(this);
+    for (const auto &digest : mDigestVector) { // for each usage - vec<ReportPacket> pair
+        uint32_t usage = static_cast<uint32_t>(digest.fullUsage);
+        sp<HidRawSensor> s(new HidRawSensor(this, usage, digest.packets));
+        if (s->isValid()) {
+            for (const auto &packet : digest.packets) {
+                if (packet.type == HidParser::REPORT_TYPE_INPUT) { // only used for input mapping
+                    mSensors.emplace(packet.id/* report id*/, s);
+                }
+            }
+        }
+    }
+    if (mSensors.size() == 0) {
+        return;
+    }
+
+    run("HidRawSensor");
+    mValid = true;
+}
+
+HidRawSensorDevice::~HidRawSensorDevice() {
+    ALOGV("~HidRawSensorDevice %p", this);
+    requestExitAndWait();
+    ALOGV("~HidRawSensorDevice %p, thread exited", this);
+}
+
+bool HidRawSensorDevice::threadLoop() {
+    ALOGV("Hid Raw Device thread started %p", this);
+    std::vector<uint8_t> buffer;
+    bool ret;
+    uint8_t usageId;
+
+    while(!Thread::exitPending()) {
+        ret = receiveReport(&usageId, &buffer);
+        if (!ret) {
+            break;
+        }
+
+        auto i = mSensors.find(usageId);
+        if (i == mSensors.end()) {
+            ALOGW("Input of unknow usage id %u received", usageId);
+            continue;
+        }
+
+        i->second->handleInput(usageId, buffer);
+    }
+
+    ALOGI("Hid Raw Device thread ended for %p", this);
+    return false;
+}
+
+BaseSensorVector HidRawSensorDevice::getSensors() const {
+    BaseSensorVector ret;
+    std::set<sp<BaseSensorObject>> set;
+    for (const auto &s : mSensors) {
+        if (set.find(s.second) == set.end()) {
+            ret.push_back(s.second);
+            set.insert(s.second);
+        }
+    }
+    return ret;
+}
+
+} // namespace SensorHalExt
+} // namespace android
diff --git a/modules/sensors/dynamic_sensor/HidRawSensorDevice.h b/modules/sensors/dynamic_sensor/HidRawSensorDevice.h
new file mode 100644
index 0000000..06d435e
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidRawSensorDevice.h
@@ -0,0 +1,54 @@
+/*
+ * 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 ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_DEVICE_H
+#define ANDROID_SENSORHAL_EXT_HIDRAW_SENSOR_DEVICE_H
+
+#include "BaseSensorObject.h"
+#include "BaseDynamicSensorDaemon.h" // BaseSensorVector
+#include "HidRawDevice.h"
+#include "HidRawSensor.h"
+
+#include <HidParser.h>
+#include <utils/Thread.h>
+#include <string>
+#include <vector>
+
+namespace android {
+namespace SensorHalExt {
+
+class HidRawSensorDevice : public HidRawDevice, public Thread {
+public:
+    static sp<HidRawSensorDevice> create(const std::string &devName);
+    virtual ~HidRawSensorDevice();
+
+    // get a list of sensors associated with this device
+    BaseSensorVector getSensors() const;
+private:
+    static const std::unordered_set<unsigned int> sInterested;
+
+    // constructor will result in +1 strong count
+    explicit HidRawSensorDevice(const std::string &devName);
+    // implement function of Thread
+    virtual bool threadLoop() override;
+    std::unordered_map<unsigned int/*reportId*/, sp<HidRawSensor>> mSensors;
+    bool mValid;
+};
+
+} // namespace SensorHalExt
+} // namespace android
+
+#endif // ANDROID_SENSORHAL_EXT_HIDRAW_DEVICE_H
+
diff --git a/modules/sensors/dynamic_sensor/HidSensorDef.h b/modules/sensors/dynamic_sensor/HidSensorDef.h
new file mode 100644
index 0000000..2728b28
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/HidSensorDef.h
@@ -0,0 +1,101 @@
+/*
+ * 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 HID_SENSOR_DEF_H_
+#define HID_SENSOR_DEF_H_
+namespace Hid {
+namespace Sensor {
+namespace GeneralUsage {
+enum {
+    STATE = 0x200201,
+    EVENT = 0x200202,
+};
+
+} //namespace Usage
+namespace PropertyUsage {
+enum {
+    FRIENDLY_NAME = 0x200301,
+    MINIMUM_REPORT_INTERVAL = 0x200304,
+    PERSISTENT_UNIQUE_ID = 0x200302,
+    POWER_STATE = 0x200319,
+    RANGE_MAXIMUM = 0x200314,
+    RANGE_MINIMUM = 0x200315,
+    REPORTING_STATE = 0x200316,
+    REPORT_INTERVAL = 0x20030E,
+    RESOLUTION = 0x200313,
+    SAMPLING_RATE =0x200317,
+    SENSOR_CONNECTION_TYPE = 0x200309,
+    SENSOR_DESCRIPTION = 0x200308,
+    SENSOR_MANUFACTURER = 0x200305,
+    SENSOR_MODEL = 0x200306,
+    SENSOR_SERIAL_NUMBER = 0x200307,
+    SENSOR_STATUS = 0x200303,
+};
+} // nsmespace PropertyUsage
+
+namespace SensorTypeUsage {
+enum {
+    ACCELEROMETER_3D = 0x200073,
+    COMPASS_3D = 0x200083,
+    CUSTOM = 0x2000E1,
+    DEVICE_ORIENTATION = 0x20008A,
+    GYROMETER_3D = 0x200076,
+};
+} // namespace SensorTypeUsage
+
+namespace ReportUsage {
+enum {
+    ACCELERATION_X_AXIS = 0x200453,
+    ACCELERATION_Y_AXIS = 0x200454,
+    ACCELERATION_Z_AXIS = 0x200455,
+    ANGULAR_VELOCITY_X_AXIS = 0x200457,
+    ANGULAR_VELOCITY_Y_AXIS = 0x200458,
+    ANGULAR_VELOCITY_Z_AXIS = 0x200459,
+    CUSTOM_VALUE_1 = 0x200544,
+    CUSTOM_VALUE_2 = 0x200545,
+    CUSTOM_VALUE_3 = 0x200546,
+    CUSTOM_VALUE_4 = 0x200547,
+    CUSTOM_VALUE_5 = 0x200548,
+    CUSTOM_VALUE_6 = 0x200549,
+    MAGNETIC_FLUX_X_AXIS = 0x200485,
+    MAGNETIC_FLUX_Y_AXIS = 0x200486,
+    MAGNETIC_FLUX_Z_AXIS = 0x200487,
+    MAGNETOMETER_ACCURACY = 0x200488,
+    ORIENTATION_QUATERNION = 0x200483,
+};
+} // namespace ReportUsage
+
+namespace RawMinMax {
+enum {
+    REPORTING_STATE_MIN = 0,
+    REPORTING_STATE_MAX = 5,
+    POWER_STATE_MIN = 0,
+    POWER_STATE_MAX = 5,
+};
+} // namespace RawMinMax
+
+namespace StateValue {
+enum {
+    POWER_STATE_FULL_POWER = 1,
+    POWER_STATE_POWER_OFF = 5,
+
+    REPORTING_STATE_ALL_EVENT = 1,
+    REPORTING_STATE_NO_EVENT = 0,
+};
+} // StateValue
+} // namespace Sensor
+} // namespace Hid
+#endif // HID_SENSOR_DEF_H_
+
diff --git a/modules/sensors/dynamic_sensor/SensorEventCallback.h b/modules/sensors/dynamic_sensor/SensorEventCallback.h
index b98cd7f..2f79529 100644
--- a/modules/sensors/dynamic_sensor/SensorEventCallback.h
+++ b/modules/sensors/dynamic_sensor/SensorEventCallback.h
@@ -17,12 +17,11 @@
 #ifndef ANDROID_SENSORHAL_DSE_SENSOR_EVENT_CALLBACK_H
 #define ANDROID_SENSORHAL_DSE_SENSOR_EVENT_CALLBACK_H
 
+#include "Utils.h"
 #include <hardware/sensors.h>
-#include <utils/RefBase.h>
 
 namespace android {
 namespace SensorHalExt {
-
 class BaseSensorObject;
 
 // if timestamp in sensors_event_t has this value, it will be filled at dispatcher.
@@ -30,7 +29,7 @@
 
 class SensorEventCallback {
 public:
-    virtual int submitEvent(sp<BaseSensorObject> sensor, const sensors_event_t &e) = 0;
+    virtual int submitEvent(SP(BaseSensorObject) sensor, const sensors_event_t &e) = 0;
     virtual ~SensorEventCallback() = default;
 };
 
diff --git a/modules/sensors/dynamic_sensor/Utils.h b/modules/sensors/dynamic_sensor/Utils.h
new file mode 100644
index 0000000..c96c147
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/Utils.h
@@ -0,0 +1,36 @@
+/*
+ * 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 ANDROID_SENSORHAL_EXT_UTILS_H
+#define ANDROID_SENSORHAL_EXT_UTILS_H
+
+// Host build does not have RefBase
+#ifdef __ANDROID__
+#include <utils/RefBase.h>
+#define REF_BASE(a) ::android::RefBase
+#define SP(a) sp<a>
+#define WP(a) wp<a>
+#define SP_THIS this
+#define PROMOTE(a) (a).promote()
+#else
+#include <memory>
+#define REF_BASE(a) std::enable_shared_from_this<a>
+#define SP(a) std::shared_ptr<a>
+#define WP(a) std::weak_ptr<a>
+#define SP_THIS shared_from_this()
+#define PROMOTE(a) (a).lock()
+#endif
+
+#endif // ANDROID_SENSORHAL_EXT_UTILS_H
diff --git a/modules/sensors/dynamic_sensor/test/HidRawDeviceTest.cpp b/modules/sensors/dynamic_sensor/test/HidRawDeviceTest.cpp
new file mode 100644
index 0000000..2a68e39
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/test/HidRawDeviceTest.cpp
@@ -0,0 +1,125 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "HidRawDeviceTest"
+
+#include "HidRawDevice.h"
+#include "HidRawSensor.h"
+#include "HidSensorDef.h"
+#include "SensorEventCallback.h"
+#include "Utils.h"
+#include "HidLog.h"
+#include "StreamIoUtil.h"
+
+namespace android {
+namespace SensorHalExt {
+
+/*
+ * Host test that verifies HidRawDevice and HidRawSensor works correctly.
+ */
+class HidRawDeviceTest {
+public:
+    static void test(const char *devicePath) {
+        using namespace Hid::Sensor::SensorTypeUsage;
+        using HidUtil::hexdumpToStream;
+
+        std::unordered_set<unsigned int> interestedUsage{
+                ACCELEROMETER_3D, GYROMETER_3D, COMPASS_3D, CUSTOM};
+
+        SP(HidRawDevice) device =
+                std::make_shared<HidRawDevice>(std::string(devicePath), interestedUsage);
+        const HidDevice::HidDeviceInfo &info = device->getDeviceInfo();
+
+        LOG_V << "Sizeof descriptor: " << info.descriptor.size() << LOG_ENDL;
+        LOG_V << "Descriptor: " << LOG_ENDL;
+        hexdumpToStream(LOG_V, info.descriptor.begin(), info.descriptor.end());
+
+        if (!device->isValid()) {
+            LOG_E << "invalid device" << LOG_ENDL;
+            return;
+        }
+
+        LOG_V << "Digest: " << LOG_ENDL;
+        LOG_V << device->mDigestVector;
+
+        std::vector<uint8_t> buffer;
+        // Dump first few feature ID to help debugging.
+        // If device does not implement all these features, it will show error messages.
+        for (int featureId = 0; featureId <= 5; ++featureId) {
+            if (!device->getFeature(featureId, &buffer)) {
+                LOG_E << "cannot get feature " << featureId << LOG_ENDL;
+            } else {
+                LOG_V << "Dump of feature " << featureId << LOG_ENDL;
+                hexdumpToStream(LOG_V, buffer.begin(), buffer.end());
+            }
+        }
+        //
+        // use HidRawSensor to operate the device, pick first digest
+        //
+        auto &reportDigest = device->mDigestVector[0];
+        SP(HidRawSensor) sensor = std::make_shared<HidRawSensor>(
+                device, reportDigest.fullUsage, reportDigest.packets);
+
+        if (!sensor->isValid()) {
+            LOG_E << "Sensor is not valid " << LOG_ENDL;
+            return;
+        }
+
+        const sensor_t *s = sensor->getSensor();
+        LOG_V << "Sensor name: " << s->name << ", vendor: " << s->vendor << LOG_ENDL;
+        LOG_V << sensor->dump() << LOG_ENDL;
+
+        class Callback : public SensorEventCallback {
+            virtual int submitEvent(SP(BaseSensorObject) /*sensor*/, const sensors_event_t &e) {
+                LOG_V << "sensor: " << e.sensor << ", type: " << e.type << ", ts: " << e.timestamp
+                      << ", values (" << e.data[0] << ", " << e.data[1] << ", " << e.data[2] << ")"
+                      << LOG_ENDL;
+                return 1;
+            }
+        };
+        Callback callback;
+        sensor->setEventCallback(&callback);
+
+        // Request sensor samples at to 10Hz (100ms)
+        sensor->batch(100LL*1000*1000 /*ns*/, 0);
+        sensor->enable(true);
+
+        // get a couple of events
+        for (size_t i = 0; i < 100; ++i) {
+            uint8_t id;
+            if (!device->receiveReport(&id, &buffer)) {
+                LOG_E << "Receive report error" << LOG_ENDL;
+                continue;
+            }
+            sensor->handleInput(id, buffer);
+        }
+
+        // clean up
+        sensor->enable(false);
+
+        LOG_V << "Done!" << LOG_ENDL;
+    }
+};
+} //namespace SensorHalExt
+} //namespace android
+
+int main(int argc, char* argv[]) {
+    if (argc != 2) {
+        LOG_E << "Usage: " << argv[0] << " hidraw-dev-path" << LOG_ENDL;
+        return -1;
+    }
+    android::SensorHalExt::HidRawDeviceTest::test(argv[1]);
+    return 0;
+}
diff --git a/modules/sensors/dynamic_sensor/test/HidRawSensorTest.cpp b/modules/sensors/dynamic_sensor/test/HidRawSensorTest.cpp
new file mode 100644
index 0000000..66a747a
--- /dev/null
+++ b/modules/sensors/dynamic_sensor/test/HidRawSensorTest.cpp
@@ -0,0 +1,162 @@
+/*
+ * 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.
+ */
+#define LOG_TAG "HidRawSensorTest"
+
+#include "HidDevice.h"
+#include "HidLog.h"
+#include "HidLog.h"
+#include "HidParser.h"
+#include "HidRawSensor.h"
+#include "HidSensorDef.h"
+#include "StreamIoUtil.h"
+#include "TestHidDescriptor.h"
+#include "Utils.h"
+
+#include <deque>
+#include <unordered_map>
+
+namespace android {
+namespace SensorHalExt {
+
+class HidRawDummyDevice : public HidDevice {
+public:
+    struct DataPair {
+        uint8_t id;
+        std::vector<uint8_t> data;
+    };
+
+    HidRawDummyDevice() {
+        // dummy values
+        mInfo = {
+          .name = "Test sensor name",
+          .physicalPath = "/physical/path",
+          .busType = "USB",
+          .vendorId = 0x1234,
+          .productId = 0x5678,
+          .descriptor = {0}
+      };
+    }
+
+    virtual const HidDeviceInfo& getDeviceInfo() {
+        return mInfo;
+    }
+
+    // get feature from device
+    virtual bool getFeature(uint8_t id, std::vector<uint8_t> *out) {
+        auto i = mFeature.find(id);
+        if (i == mFeature.end()) {
+            return false;
+        }
+        *out = i->second;
+        return true;
+    }
+
+    // write feature to device
+    virtual bool setFeature(uint8_t id, const std::vector<uint8_t> &in) {
+        auto i = mFeature.find(id);
+        if (i == mFeature.end() || in.size() != i->second.size()) {
+            return false;
+        }
+        i->second = in;
+        return true;
+    }
+
+    // send report to default output endpoint
+    virtual bool sendReport(uint8_t id, std::vector<uint8_t> &data) {
+        DataPair pair = {
+            .id = id,
+            .data = data
+        };
+        mOutput.push_back(pair);
+        return true;
+    }
+
+    // receive from default input endpoint
+    virtual bool receiveReport(uint8_t * /*id*/, std::vector<uint8_t> * /*data*/) {
+        // not necessary, as input report can be directly feed to HidRawSensor for testing purpose
+        return false;
+    }
+
+    bool dequeuOutputReport(DataPair *pair) {
+        if (!mOutput.empty()) {
+            return false;
+        }
+        *pair = mOutput.front();
+        mOutput.pop_front();
+        return true;
+    }
+
+private:
+    HidDeviceInfo mInfo;
+    std::deque<DataPair> mOutput;
+    std::unordered_map<uint8_t, std::vector<uint8_t>> mFeature;
+};
+
+class HidRawSensorTest {
+public:
+    static bool test() {
+        bool ret = true;
+        using namespace Hid::Sensor::SensorTypeUsage;
+        std::unordered_set<unsigned int> interestedUsage{
+                ACCELEROMETER_3D, GYROMETER_3D, COMPASS_3D, CUSTOM};
+        SP(HidDevice) device(new HidRawDummyDevice());
+
+        HidParser hidParser;
+        for (const TestHidDescriptor *p = gDescriptorArray; ; ++p) {
+            if (p->data == nullptr || p->len == 0) {
+                break;
+            }
+            const char *name = p->name != nullptr ? p->name : "unnamed";
+            if (!hidParser.parse(p->data, p->len)) {
+                LOG_E << name << " parsing error!" << LOG_ENDL;
+                ret = false;
+                continue;
+            }
+
+            hidParser.filterTree();
+            LOG_V << name << "  digest: " << LOG_ENDL;
+            auto digestVector = hidParser.generateDigest(interestedUsage);
+            LOG_V << digestVector;
+
+            if (digestVector.empty()) {
+                LOG_V << name << " does not contain interested usage" << LOG_ENDL;
+                continue;
+            }
+
+            LOG_V << name << "  sensor: " << LOG_ENDL;
+            for (const auto &digest : digestVector) {
+                LOG_I << "Sensor usage " << std::hex << digest.fullUsage << std::dec << LOG_ENDL;
+                auto *s = new HidRawSensor(device, digest.fullUsage, digest.packets);
+                if (s->mValid) {
+                    LOG_V << "Usage " << std::hex << digest.fullUsage << std::dec << LOG_ENDL;
+                    LOG_V << s->dump();
+                } else {
+                    LOG_V << "Sensor of usage " << std::hex << digest.fullUsage << std::dec
+                          << " not valid!" << LOG_ENDL;
+                }
+            }
+            LOG_V << LOG_ENDL;
+        }
+        return ret;
+    }
+};
+
+}// namespace SensorHalExt
+}// namespace android
+
+int main() {
+    return android::SensorHalExt::HidRawSensorTest::test() ? 0 : 1;
+}