summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPeng Xu <pengxu@google.com>2017-04-04 01:31:54 -0700
committerPeng Xu <pengxu@google.com>2017-04-20 23:23:53 +0000
commit4715d1734d2c60cc91ef70d409678d8eda382fde (patch)
tree4a7968a6472431119e43f4ad0ad9520fee541c44
parentc87b15a2245b6fac68bd2e259cb0f4b4efbcf045 (diff)
downloadlibhardware-4715d1734d2c60cc91ef70d409678d8eda382fde.tar.gz
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
-rw-r--r--modules/sensors/dynamic_sensor/Android.mk88
-rw-r--r--modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp34
-rw-r--r--modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h7
-rw-r--r--modules/sensors/dynamic_sensor/BaseSensorObject.cpp5
-rw-r--r--modules/sensors/dynamic_sensor/BaseSensorObject.h8
-rw-r--r--modules/sensors/dynamic_sensor/ConnectionDetector.cpp4
-rw-r--r--modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp26
-rw-r--r--modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h2
-rw-r--r--modules/sensors/dynamic_sensor/DynamicSensorManager.cpp4
-rw-r--r--modules/sensors/dynamic_sensor/DynamicSensorManager.h2
-rw-r--r--modules/sensors/dynamic_sensor/HidDevice.h57
-rw-r--r--modules/sensors/dynamic_sensor/HidRawDevice.cpp332
-rw-r--r--modules/sensors/dynamic_sensor/HidRawDevice.h85
-rw-r--r--modules/sensors/dynamic_sensor/HidRawSensor.cpp1044
-rw-r--r--modules/sensors/dynamic_sensor/HidRawSensor.h168
-rw-r--r--modules/sensors/dynamic_sensor/HidRawSensorDaemon.cpp71
-rw-r--r--modules/sensors/dynamic_sensor/HidRawSensorDaemon.h63
-rw-r--r--modules/sensors/dynamic_sensor/HidRawSensorDevice.cpp123
-rw-r--r--modules/sensors/dynamic_sensor/HidRawSensorDevice.h54
-rw-r--r--modules/sensors/dynamic_sensor/HidSensorDef.h101
-rw-r--r--modules/sensors/dynamic_sensor/SensorEventCallback.h5
-rw-r--r--modules/sensors/dynamic_sensor/Utils.h36
-rw-r--r--modules/sensors/dynamic_sensor/test/HidRawDeviceTest.cpp125
-rw-r--r--modules/sensors/dynamic_sensor/test/HidRawSensorTest.cpp162
24 files changed, 2545 insertions, 61 deletions
diff --git a/modules/sensors/dynamic_sensor/Android.mk b/modules/sensors/dynamic_sensor/Android.mk
index cdc1929a..46085085 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 @@ COMMON_CFLAGS := -Wall -Werror -Wextra
#
# Please take only one of these two options to avoid conflict over hardware resource.
#
-
#
# Option 1: sensor extension module
#
@@ -37,18 +54,9 @@ LOCAL_PROPRIETARY_MODULE := true
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_PROPRIETARY_MODULE := true
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 f5f642ce..7e9f2173 100644
--- a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
+++ b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.cpp
@@ -23,14 +23,23 @@ namespace SensorHalExt {
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 @@ bool BaseDynamicSensorDaemon::onConnectionChange(const std::string &deviceKey, b
}
} 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 b698b7d5..0cec9651 100644
--- a/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
+++ b/modules/sensors/dynamic_sensor/BaseDynamicSensorDaemon.h
@@ -28,6 +28,8 @@ namespace SensorHalExt {
class DynamicSensorManager;
+typedef std::vector<sp<BaseSensorObject>> BaseSensorVector;
+
class BaseDynamicSensorDaemon : public RefBase {
public:
BaseDynamicSensorDaemon(DynamicSensorManager& manager) : mManager(manager) {}
@@ -35,10 +37,11 @@ public:
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 189e87fa..4ec76b27 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 @@ BaseSensorObject::BaseSensorObject() : mCallback(nullptr) {
bool BaseSensorObject::setEventCallback(SensorEventCallback* callback) {
if (mCallback != nullptr) {
- ALOGE("callback is already assigned, cannot change.");
return false;
}
mCallback = callback;
@@ -51,7 +50,7 @@ int BaseSensorObject::flush() {
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 326e138d..e8d1a5d1 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 @@ namespace SensorHalExt {
class SensorEventCallback;
-class BaseSensorObject : virtual public RefBase {
+class BaseSensorObject : virtual public REF_BASE(BaseSensorObject) {
public:
BaseSensorObject();
virtual ~BaseSensorObject() = default;
@@ -48,7 +47,8 @@ public:
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 60994930..67a6f272 100644
--- a/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
+++ b/modules/sensors/dynamic_sensor/ConnectionDetector.cpp
@@ -31,7 +31,7 @@ namespace SensorHalExt {
// 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 @@ bool SocketConnectionDetector::threadLoop() {
// 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 19779678..a1a47e81 100644
--- a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
+++ b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.cpp
@@ -51,23 +51,30 @@ DummyDynamicAccelDaemon::DummyDynamicAccelDaemon(DynamicSensorManager& manager)
}
}
-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 @@ int DummyDynamicAccelDaemon::DummySensor::enable(bool enable) {
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 0bffbee3..95a57309 100644
--- a/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
+++ b/modules/sensors/dynamic_sensor/DummyDynamicAccelDaemon.h
@@ -61,7 +61,7 @@ private:
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 eb4903d2..b6340524 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 @@ DynamicSensorManager* DynamicSensorManager::createInstance(
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::setDelay(int handle, nsecs_t sample_period) {
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 b8fd4721..b6f39da6 100644
--- a/modules/sensors/dynamic_sensor/DynamicSensorManager.h
+++ b/modules/sensors/dynamic_sensor/DynamicSensorManager.h
@@ -123,7 +123,7 @@ private:
// 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 00000000..7083593b
--- /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 00000000..2588483a
--- /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 00000000..707dfffb
--- /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 00000000..a64d7a68
--- /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 00000000..2dd32b61
--- /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 00000000..6bf34bc8
--- /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 00000000..fc4b2a26
--- /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 00000000..16e9060a
--- /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 00000000..06d435e7
--- /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 00000000..2728b28a
--- /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 b98cd7f7..2f79529a 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 @@ constexpr int64_t TIMESTAMP_AUTO_FILL = -1;
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 00000000..c96c147a
--- /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 00000000..2a68e39a
--- /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 00000000..66a747ac
--- /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;
+}