diff options
author | Peng Xu <pengxu@google.com> | 2017-04-04 01:31:54 -0700 |
---|---|---|
committer | Peng Xu <pengxu@google.com> | 2017-04-20 23:23:53 +0000 |
commit | 4715d1734d2c60cc91ef70d409678d8eda382fde (patch) | |
tree | 4a7968a6472431119e43f4ad0ad9520fee541c44 | |
parent | c87b15a2245b6fac68bd2e259cb0f4b4efbcf045 (diff) | |
download | libhardware-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
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 ®ex) - : 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; +} |