| /* |
| * 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 <HidUtils.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 = { |
| .type = TYPE_FLOAT, |
| .maxValue = digest.maxRaw, |
| .minValue = digest.minRaw, |
| .byteOffset = digest.bitOffset / 8, |
| .byteSize = digest.bitSize / 8, |
| .a = digest.a, |
| .b = digest.b, |
| }; |
| // 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 = { |
| .type = TYPE_FLOAT, |
| .maxValue = quat.maxRaw, |
| .minValue = quat.minRaw, |
| .byteOffset = quat.bitOffset / 8, |
| .byteSize = quat.bitSize / 8, |
| .b = quat.b, |
| }; |
| |
| // 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 = { |
| .type = TYPE_FLOAT, |
| .maxValue = reportX.maxRaw, |
| .minValue = reportX.minRaw, |
| .byteSize = reportX.bitSize / 8, |
| }; |
| |
| // 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 (decodeString(r, buffer, &str)) { |
| detectSensorFromDescription(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 != 8 && report.bitSize != 16) || report.count < 1) { |
| return false; |
| } |
| |
| size_t charSize = report.bitSize / 8; |
| size_t offset = report.bitOffset / 8; |
| if (offset + report.count * charSize > buffer.size()) { |
| return false; |
| } |
| |
| if (charSize == 1) { |
| *d = std::string(buffer.begin() + offset, |
| buffer.begin() + offset + report.count); |
| } else { |
| 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; |
| } |
| |
| void HidRawSensor::detectSensorFromDescription(const std::string &description) { |
| if (detectAndroidHeadTrackerSensor(description) || |
| detectAndroidCustomSensor(description)) { |
| mFeatureInfo.isAndroidCustom = true; |
| } |
| } |
| |
| bool HidRawSensor::detectAndroidHeadTrackerSensor( |
| const std::string &description) { |
| if (description.find("#AndroidHeadTracker#1.") != 0) { |
| return false; |
| } |
| |
| mFeatureInfo.type = SENSOR_TYPE_DEVICE_PRIVATE_BASE; |
| mFeatureInfo.typeString = CUSTOM_TYPE_PREFIX + "headtracker"; |
| mFeatureInfo.reportModeFlag = SENSOR_FLAG_CONTINUOUS_MODE; |
| mFeatureInfo.permission = ""; |
| mFeatureInfo.isWakeUp = false; |
| |
| return true; |
| } |
| |
| 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; |
| break; |
| 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::PowerStateUsage; |
| using namespace Hid::Sensor::PropertyUsage; |
| using namespace Hid::Sensor::ReportingStateUsage; |
| |
| //REPORTING_STATE |
| const HidParser::ReportItem *reportingState |
| = find(packets, REPORTING_STATE, HidParser::REPORT_TYPE_FEATURE); |
| |
| if (reportingState == nullptr) { |
| LOG_W << "Cannot find valid reporting state feature" << LOG_ENDL; |
| } else { |
| mReportingStateId = reportingState->id; |
| mReportingStateBitOffset = reportingState->bitOffset; |
| mReportingStateBitSize = reportingState->bitSize; |
| |
| mReportingStateDisableIndex = -1; |
| mReportingStateEnableIndex = -1; |
| for (unsigned i = 0; i < reportingState->usageVector.size(); ++i) { |
| if (reportingState->usageVector[i] == REPORTING_STATE_NO_EVENTS) { |
| mReportingStateDisableIndex = i; |
| } |
| if (reportingState->usageVector[i] == REPORTING_STATE_ALL_EVENTS) { |
| mReportingStateEnableIndex = i; |
| } |
| } |
| if (mReportingStateDisableIndex < 0) { |
| LOG_W << "Cannot find reporting state to disable sensor" |
| << LOG_ENDL; |
| mReportingStateId = -1; |
| } |
| if (mReportingStateEnableIndex < 0) { |
| LOG_W << "Cannot find reporting state to enable sensor" << LOG_ENDL; |
| mReportingStateId = -1; |
| } |
| } |
| |
| //POWER_STATE |
| const HidParser::ReportItem *powerState |
| = find(packets, POWER_STATE, HidParser::REPORT_TYPE_FEATURE); |
| if (powerState == nullptr) { |
| LOG_W << "Cannot find valid power state feature" << LOG_ENDL; |
| } else { |
| mPowerStateId = powerState->id; |
| mPowerStateBitOffset = powerState->bitOffset; |
| mPowerStateBitSize = powerState->bitSize; |
| |
| mPowerStateOffIndex = -1; |
| mPowerStateOnIndex = -1; |
| for (unsigned i = 0; i < powerState->usageVector.size(); ++i) { |
| if (powerState->usageVector[i] == POWER_STATE_D4_POWER_OFF) { |
| mPowerStateOffIndex = i; |
| } |
| if (powerState->usageVector[i] == POWER_STATE_D0_FULL_POWER) { |
| mPowerStateOnIndex = i; |
| } |
| } |
| if (mPowerStateOffIndex < 0) { |
| LOG_W << "Cannot find power state to power off sensor" |
| << LOG_ENDL; |
| mPowerStateId = -1; |
| } |
| if (mPowerStateOnIndex < 0) { |
| LOG_W << "Cannot find power state to power on sensor" << LOG_ENDL; |
| mPowerStateId = -1; |
| } |
| } |
| |
| //REPORT_INTERVAL |
| const HidParser::ReportItem *reportInterval |
| = find(packets, REPORT_INTERVAL, HidParser::REPORT_TYPE_FEATURE); |
| if (reportInterval == nullptr |
| || reportInterval->minRaw < 0) { |
| LOG_W << "Cannot find valid report interval feature" << LOG_ENDL; |
| } else { |
| mReportIntervalId = reportInterval->id; |
| mReportIntervalBitOffset = reportInterval->bitOffset; |
| mReportIntervalBitSize = reportInterval->bitSize; |
| |
| 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) { |
| 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) |
| && (8 * buffer.size()) >= |
| (mPowerStateBitOffset + mPowerStateBitSize)) { |
| uint8_t index = enable ? mPowerStateOnIndex : mPowerStateOffIndex; |
| HidUtil::copyBits(&index, &(buffer[0]), buffer.size(), |
| 0, mPowerStateBitOffset, mPowerStateBitSize); |
| 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) |
| && (8 * buffer.size()) > |
| (mReportingStateBitOffset + mReportingStateBitSize)) { |
| uint8_t index = enable ? mReportingStateEnableIndex : |
| mReportingStateDisableIndex; |
| HidUtil::copyBits(&index, &(buffer[0]), buffer.size(),0, |
| mReportingStateBitOffset, mReportingStateBitSize); |
| 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) |
| && (8 * buffer.size()) >= |
| (mReportIntervalBitOffset + mReportIntervalBitSize)) { |
| int64_t periodMs = samplingPeriod / 1000000; //ns -> ms |
| int64_t maxPeriodMs = |
| (1LL << std::min(mReportIntervalBitSize, 63U)) - 1; |
| periodMs = std::min(periodMs, maxPeriodMs); |
| HidUtil::copyBits(&periodMs, &(buffer[0]), buffer.size(), |
| 0, mReportIntervalBitOffset, |
| mReportIntervalBitSize); |
| 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 |
| << " bit offset: " << mPowerStateBitOffset |
| << " bit size: " << mPowerStateBitSize |
| << " power off index: " << mPowerStateOffIndex |
| << " power on index: " << mPowerStateOnIndex |
| << LOG_ENDL; |
| } else { |
| ss << "not found" << LOG_ENDL; |
| } |
| |
| ss << " Reporting state "; |
| if (mReportingStateId >= 0) { |
| ss << "found, id: " << mReportingStateId |
| << " bit offset: " << mReportingStateBitOffset |
| << " bit size: " << mReportingStateBitSize |
| << " disable index: " << mReportingStateDisableIndex |
| << " enable index: " << mReportingStateEnableIndex |
| << LOG_ENDL; |
| } else { |
| ss << "not found" << LOG_ENDL; |
| } |
| |
| ss << " Report interval "; |
| if (mReportIntervalId >= 0) { |
| ss << "found, id: " << mReportIntervalId |
| << " bit offset: " << mReportIntervalBitOffset |
| << " bit size: " << mReportIntervalBitSize << LOG_ENDL; |
| } else { |
| ss << "not found" << LOG_ENDL; |
| } |
| return ss.str(); |
| } |
| |
| } // namespace SensorHalExt |
| } // namespace android |