/* * Copyright (C) 2016 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 "contexthub.h" #include <cstring> #include <errno.h> #include <vector> #include "apptohostevent.h" #include "log.h" #include "resetreasonevent.h" #include "sensorevent.h" #include "util.h" namespace android { #define UNUSED_PARAM(param) (void) (param) constexpr int kCalibrationTimeoutMs(10000); constexpr int kTestTimeoutMs(10000); constexpr int kBridgeVersionTimeoutMs(500); struct SensorTypeNames { SensorType sensor_type; const char *name_abbrev; }; static const SensorTypeNames sensor_names_[] = { { SensorType::Accel, "accel" }, { SensorType::AnyMotion, "anymo" }, { SensorType::NoMotion, "nomo" }, { SensorType::SignificantMotion, "sigmo" }, { SensorType::Flat, "flat" }, { SensorType::Gyro, "gyro" }, //{ SensorType::GyroUncal, "gyro_uncal" }, { SensorType::Magnetometer, "mag" }, //{ SensorType::MagnetometerUncal, "mag_uncal" }, { SensorType::Barometer, "baro" }, { SensorType::Temperature, "temp" }, { SensorType::AmbientLightSensor, "als" }, { SensorType::Proximity, "prox" }, { SensorType::Orientation, "orien" }, //{ SensorType::HeartRateECG, "ecg" }, //{ SensorType::HeartRatePPG, "ppg" }, { SensorType::Gravity, "gravity" }, { SensorType::LinearAccel, "linear_acc" }, { SensorType::RotationVector, "rotation" }, { SensorType::GeomagneticRotationVector, "geomag" }, { SensorType::GameRotationVector, "game" }, { SensorType::StepCount, "step_cnt" }, { SensorType::StepDetect, "step_det" }, { SensorType::Gesture, "gesture" }, { SensorType::Tilt, "tilt" }, { SensorType::DoubleTwist, "twist" }, { SensorType::DoubleTap, "doubletap" }, { SensorType::WindowOrientation, "win_orien" }, { SensorType::Hall, "hall" }, { SensorType::Activity, "activity" }, { SensorType::Vsync, "vsync" }, { SensorType::WristTilt, "wrist_tilt" }, }; struct SensorTypeAlias { SensorType sensor_type; SensorType sensor_alias; const char *name_abbrev; }; static const SensorTypeAlias sensor_aliases_[] = { { SensorType::Accel, SensorType::CompressedAccel, "compressed_accel" }, }; bool SensorTypeIsAliasOf(SensorType sensor_type, SensorType alias) { for (size_t i = 0; i < ARRAY_LEN(sensor_aliases_); i++) { if (sensor_aliases_[i].sensor_type == sensor_type && sensor_aliases_[i].sensor_alias == alias) { return true; } } return false; } SensorType ContextHub::SensorAbbrevNameToType(const char *sensor_name_abbrev) { for (unsigned int i = 0; i < ARRAY_LEN(sensor_names_); i++) { if (strcmp(sensor_names_[i].name_abbrev, sensor_name_abbrev) == 0) { return sensor_names_[i].sensor_type; } } return SensorType::Invalid_; } SensorType ContextHub::SensorAbbrevNameToType(const std::string& abbrev_name) { return ContextHub::SensorAbbrevNameToType(abbrev_name.c_str()); } std::string ContextHub::SensorTypeToAbbrevName(SensorType sensor_type) { for (unsigned int i = 0; i < ARRAY_LEN(sensor_names_); i++) { if (sensor_names_[i].sensor_type == sensor_type) { return std::string(sensor_names_[i].name_abbrev); } } for (unsigned int i = 0; i < ARRAY_LEN(sensor_aliases_); i++) { if (sensor_aliases_[i].sensor_alias == sensor_type) { return std::string(sensor_aliases_[i].name_abbrev); } } char buffer[24]; snprintf(buffer, sizeof(buffer), "unknown (%d)", static_cast<int>(sensor_type)); return std::string(buffer); } std::string ContextHub::ListAllSensorAbbrevNames() { std::string sensor_list; for (unsigned int i = 0; i < ARRAY_LEN(sensor_names_); i++) { sensor_list += sensor_names_[i].name_abbrev; if (i < ARRAY_LEN(sensor_names_) - 1) { sensor_list += ", "; } } return sensor_list; } bool ContextHub::Flash(const std::string& filename) { FILE *firmware_file = fopen(filename.c_str(), "r"); if (!firmware_file) { LOGE("Failed to open firmware image: %d (%s)", errno, strerror(errno)); return false; } fseek(firmware_file, 0, SEEK_END); long file_size = ftell(firmware_file); fseek(firmware_file, 0, SEEK_SET); auto firmware_data = std::vector<uint8_t>(file_size); size_t bytes_read = fread(firmware_data.data(), sizeof(uint8_t), file_size, firmware_file); fclose(firmware_file); if (bytes_read != static_cast<size_t>(file_size)) { LOGE("Read of firmware file returned %zu, expected %ld", bytes_read, file_size); return false; } return FlashSensorHub(firmware_data); } bool ContextHub::CalibrateSensors(const std::vector<SensorSpec>& sensors) { bool success = ForEachSensor(sensors, [this](const SensorSpec &spec) -> bool { return CalibrateSingleSensor(spec); }); if (success) { success = SaveCalibration(); } return success; } bool ContextHub::TestSensors(const std::vector<SensorSpec>& sensors) { bool success = ForEachSensor(sensors, [this](const SensorSpec &spec) -> bool { return TestSingleSensor(spec); }); return success; } bool ContextHub::EnableSensor(const SensorSpec& spec) { ConfigureSensorRequest req; req.config.event_type = static_cast<uint32_t>(EventType::ConfigureSensor); req.config.sensor_type = static_cast<uint8_t>(spec.sensor_type); req.config.command = static_cast<uint8_t>( ConfigureSensorRequest::CommandType::Enable); if (spec.special_rate != SensorSpecialRate::None) { req.config.rate = static_cast<uint32_t>(spec.special_rate); } else { req.config.rate = ConfigureSensorRequest::FloatRateToFixedPoint( spec.rate_hz); } req.config.latency = spec.latency_ns; LOGI("Enabling sensor %d at rate %.0f Hz (special 0x%x) and latency %.2f ms", spec.sensor_type, spec.rate_hz, spec.special_rate, spec.latency_ns / 1000000.0f); auto result = WriteEvent(req); if (result == TransportResult::Success) { sensor_is_active_[static_cast<int>(spec.sensor_type)] = true; return true; } LOGE("Could not enable sensor %d", spec.sensor_type); return false; } bool ContextHub::EnableSensors(const std::vector<SensorSpec>& sensors) { return ForEachSensor(sensors, [this](const SensorSpec &spec) -> bool { return EnableSensor(spec); }); } bool ContextHub::DisableSensor(SensorType sensor_type) { ConfigureSensorRequest req; req.config.event_type = static_cast<uint32_t>(EventType::ConfigureSensor); req.config.sensor_type = static_cast<uint8_t>(sensor_type); req.config.command = static_cast<uint8_t>( ConfigureSensorRequest::CommandType::Disable); // Note that nanohub treats us as a single client, so if we call enable // twice then disable once, the sensor will be disabled LOGI("Disabling sensor %d", sensor_type); auto result = WriteEvent(req); if (result == TransportResult::Success) { sensor_is_active_[static_cast<int>(sensor_type)] = false; return true; } LOGE("Could not disable sensor %d", sensor_type); return false; } bool ContextHub::DisableSensors(const std::vector<SensorSpec>& sensors) { return ForEachSensor(sensors, [this](const SensorSpec &spec) -> bool { return DisableSensor(spec.sensor_type); }); } bool ContextHub::DisableAllSensors() { bool success = true; for (size_t i = 0; i < ARRAY_LEN(sensor_names_); i++) { success &= DisableSensor(sensor_names_[i].sensor_type); } return success; } bool ContextHub::DisableActiveSensors() { bool success = true; LOGD("Disabling all active sensors"); for (size_t i = 0; i < ARRAY_LEN(sensor_names_); i++) { if (sensor_is_active_[static_cast<int>(sensor_names_[i].sensor_type)]) { success &= DisableSensor(sensor_names_[i].sensor_type); } } return success; } void ContextHub::PrintAllEvents(unsigned int limit) { bool continuous = (limit == 0); auto event_printer = [&limit, continuous](const SensorEvent& event) -> bool { printf("%s", event.ToString().c_str()); return (continuous || --limit > 0); }; ReadSensorEvents(event_printer); } bool ContextHub::PrintBridgeVersion() { BridgeVersionInfoRequest request; TransportResult result = WriteEvent(request); if (result != TransportResult::Success) { LOGE("Failed to send bridge version info request: %d", static_cast<int>(result)); return false; } bool success = false; auto event_handler = [&success](const AppToHostEvent &event) -> bool { bool keep_going = true; auto rsp = reinterpret_cast<const BrHostEventData *>(event.GetDataPtr()); if (event.GetAppId() != kAppIdBridge) { LOGD("Ignored event from unexpected app"); } else if (event.GetDataLen() < sizeof(BrHostEventData)) { LOGE("Got short app to host event from bridge: length %u, expected " "at least %zu", event.GetDataLen(), sizeof(BrHostEventData)); } else if (rsp->msgId != BRIDGE_HOST_EVENT_MSG_VERSION_INFO) { LOGD("Ignored bridge event with unexpected message ID %u", rsp->msgId); } else if (rsp->status) { LOGE("Bridge version info request failed with status %u", rsp->status); keep_going = false; } else if (event.GetDataLen() < (sizeof(BrHostEventData) + sizeof(BrVersionInfoRsp))) { LOGE("Got successful version info response with short payload: " "length %u, expected at least %zu", event.GetDataLen(), (sizeof(BrHostEventData) + sizeof(BrVersionInfoRsp))); keep_going = false; } else { auto ver = reinterpret_cast<const struct BrVersionInfoRsp *>( rsp->payload); printf("Bridge version info:\n" " HW type: 0x%04x\n" " OS version: 0x%04x\n" " Variant version: 0x%08x\n" " Bridge version: 0x%08x\n", ver->hwType, ver->osVer, ver->variantVer, ver->bridgeVer); keep_going = false; success = true; } return keep_going; }; ReadAppEvents(event_handler, kBridgeVersionTimeoutMs); return success; } void ContextHub::PrintSensorEvents(SensorType type, int limit) { bool continuous = (limit == 0); auto event_printer = [type, &limit, continuous](const SensorEvent& event) -> bool { SensorType event_source = event.GetSensorType(); if (event_source == type || SensorTypeIsAliasOf(type, event_source)) { printf("%s", event.ToString().c_str()); limit -= event.GetNumSamples(); } return (continuous || limit > 0); }; ReadSensorEvents(event_printer); } void ContextHub::PrintSensorEvents(const std::vector<SensorSpec>& sensors, int limit) { bool continuous = (limit == 0); auto event_printer = [&sensors, &limit, continuous](const SensorEvent& event) -> bool { SensorType event_source = event.GetSensorType(); for (unsigned int i = 0; i < sensors.size(); i++) { if (sensors[i].sensor_type == event_source || SensorTypeIsAliasOf(sensors[i].sensor_type, event_source)) { printf("%s", event.ToString().c_str()); limit -= event.GetNumSamples(); break; } } return (continuous || limit > 0); }; ReadSensorEvents(event_printer); } // Protected methods ----------------------------------------------------------- bool ContextHub::CalibrateSingleSensor(const SensorSpec& sensor) { ConfigureSensorRequest req; req.config.event_type = static_cast<uint32_t>(EventType::ConfigureSensor); req.config.sensor_type = static_cast<uint8_t>(sensor.sensor_type); req.config.command = static_cast<uint8_t>( ConfigureSensorRequest::CommandType::Calibrate); LOGI("Issuing calibration request to sensor %d (%s)", sensor.sensor_type, ContextHub::SensorTypeToAbbrevName(sensor.sensor_type).c_str()); auto result = WriteEvent(req); if (result != TransportResult::Success) { LOGE("Failed to calibrate sensor %d", sensor.sensor_type); return false; } bool success = false; auto cal_event_handler = [this, &sensor, &success](const AppToHostEvent &event) -> bool { if (event.IsCalibrationEventForSensor(sensor.sensor_type)) { success = HandleCalibrationResult(sensor, event); return false; } return true; }; result = ReadAppEvents(cal_event_handler, kCalibrationTimeoutMs); if (result != TransportResult::Success) { LOGE("Error reading calibration response %d", static_cast<int>(result)); return false; } return success; } bool ContextHub::TestSingleSensor(const SensorSpec& sensor) { ConfigureSensorRequest req; req.config.event_type = static_cast<uint32_t>(EventType::ConfigureSensor); req.config.sensor_type = static_cast<uint8_t>(sensor.sensor_type); req.config.command = static_cast<uint8_t>( ConfigureSensorRequest::CommandType::SelfTest); LOGI("Issuing test request to sensor %d (%s)", sensor.sensor_type, ContextHub::SensorTypeToAbbrevName(sensor.sensor_type).c_str()); auto result = WriteEvent(req); if (result != TransportResult::Success) { LOGE("Failed to test sensor %d", sensor.sensor_type); return false; } bool success = false; auto test_event_handler = [this, &sensor, &success](const AppToHostEvent &event) -> bool { if (event.IsTestEventForSensor(sensor.sensor_type)) { success = HandleTestResult(sensor, event); return false; } return true; }; result = ReadAppEvents(test_event_handler, kTestTimeoutMs); if (result != TransportResult::Success) { LOGE("Error reading test response %d", static_cast<int>(result)); return false; } return success; } bool ContextHub::ForEachSensor(const std::vector<SensorSpec>& sensors, std::function<bool(const SensorSpec&)> callback) { bool success = true; for (unsigned int i = 0; success && i < sensors.size(); i++) { success &= callback(sensors[i]); } return success; } bool ContextHub::HandleCalibrationResult(const SensorSpec& sensor, const AppToHostEvent &event) { auto hdr = reinterpret_cast<const SensorAppEventHeader *>(event.GetDataPtr()); if (hdr->status) { LOGE("Calibration of sensor %d (%s) failed with status %u", sensor.sensor_type, ContextHub::SensorTypeToAbbrevName(sensor.sensor_type).c_str(), hdr->status); return false; } bool success = false; switch (sensor.sensor_type) { case SensorType::Accel: case SensorType::Gyro: { auto result = reinterpret_cast<const TripleAxisCalibrationResult *>( event.GetDataPtr()); success = SetCalibration(sensor.sensor_type, result->xBias, result->yBias, result->zBias); break; } case SensorType::Barometer: { auto result = reinterpret_cast<const FloatCalibrationResult *>( event.GetDataPtr()); if (sensor.have_cal_ref) { success = SetCalibration(sensor.sensor_type, (sensor.cal_ref - result->value)); } break; } case SensorType::Proximity: { auto result = reinterpret_cast<const FourAxisCalibrationResult *>( event.GetDataPtr()); success = SetCalibration(sensor.sensor_type, result->xBias, result->yBias, result->zBias, result->wBias); break; } case SensorType::AmbientLightSensor: { auto result = reinterpret_cast<const FloatCalibrationResult *>( event.GetDataPtr()); if (sensor.have_cal_ref && (result->value != 0.0f)) { success = SetCalibration(sensor.sensor_type, (sensor.cal_ref / result->value)); } break; } default: LOGE("Calibration not supported for sensor type %d", static_cast<int>(sensor.sensor_type)); } return success; } bool ContextHub::HandleTestResult(const SensorSpec& sensor, const AppToHostEvent &event) { auto hdr = reinterpret_cast<const SensorAppEventHeader *>(event.GetDataPtr()); if (!hdr->status) { LOGI("Self-test of sensor %d (%s) succeeded", sensor.sensor_type, ContextHub::SensorTypeToAbbrevName(sensor.sensor_type).c_str()); return true; } else { LOGE("Self-test of sensor %d (%s) failed with status %u", sensor.sensor_type, ContextHub::SensorTypeToAbbrevName(sensor.sensor_type).c_str(), hdr->status); return false; } } ContextHub::TransportResult ContextHub::ReadAppEvents( std::function<bool(const AppToHostEvent&)> callback, int timeout_ms) { using Milliseconds = std::chrono::milliseconds; TransportResult result; bool timeout_required = timeout_ms > 0; bool keep_going = true; while (keep_going) { if (timeout_required && timeout_ms <= 0) { return TransportResult::Timeout; } std::unique_ptr<ReadEventResponse> event; SteadyClock start_time = std::chrono::steady_clock::now(); result = ReadEvent(&event, timeout_ms); SteadyClock end_time = std::chrono::steady_clock::now(); auto delta = end_time - start_time; timeout_ms -= std::chrono::duration_cast<Milliseconds>(delta).count(); if (result == TransportResult::Success && event->IsAppToHostEvent()) { AppToHostEvent *app_event = reinterpret_cast<AppToHostEvent*>( event.get()); keep_going = callback(*app_event); } else { if (result != TransportResult::Success) { LOGE("Error %d while reading", static_cast<int>(result)); if (result != TransportResult::ParseFailure) { return result; } } else { LOGD("Ignoring non-app-to-host event"); } } } return TransportResult::Success; } void ContextHub::ReadSensorEvents(std::function<bool(const SensorEvent&)> callback) { TransportResult result; bool keep_going = true; while (keep_going) { std::unique_ptr<ReadEventResponse> event; result = ReadEvent(&event); if (result == TransportResult::Success && event->IsSensorEvent()) { SensorEvent *sensor_event = reinterpret_cast<SensorEvent*>( event.get()); keep_going = callback(*sensor_event); } else { if (result != TransportResult::Success) { LOGE("Error %d while reading", static_cast<int>(result)); if (result != TransportResult::ParseFailure) { break; } } else { LOGD("Ignoring non-sensor event"); } } } } bool ContextHub::SendCalibrationData(SensorType sensor_type, const std::vector<uint8_t>& cal_data) { ConfigureSensorRequest req; req.config.event_type = static_cast<uint32_t>(EventType::ConfigureSensor); req.config.sensor_type = static_cast<uint8_t>(sensor_type); req.config.command = static_cast<uint8_t>( ConfigureSensorRequest::CommandType::ConfigData); req.SetAdditionalData(cal_data); auto result = WriteEvent(req); return (result == TransportResult::Success); } ContextHub::TransportResult ContextHub::WriteEvent( const WriteEventRequest& request) { return WriteEvent(request.GetBytes()); } ContextHub::TransportResult ContextHub::ReadEvent( std::unique_ptr<ReadEventResponse>* response, int timeout_ms) { std::vector<uint8_t> responseBuf(256); ContextHub::TransportResult result = ReadEvent(responseBuf, timeout_ms); if (result == TransportResult::Success) { *response = ReadEventResponse::FromBytes(responseBuf); if (*response == nullptr) { result = TransportResult::ParseFailure; } } return result; } // Stubs for subclasses that don't implement calibration support bool ContextHub::LoadCalibration() { LOGE("Loading calibration data not implemented"); return false; } bool ContextHub::SetCalibration(SensorType sensor_type, int32_t data) { UNUSED_PARAM(sensor_type); UNUSED_PARAM(data); return false; } bool ContextHub::SetCalibration(SensorType sensor_type, float data) { UNUSED_PARAM(sensor_type); UNUSED_PARAM(data); return false; } bool ContextHub::SetCalibration(SensorType sensor_type, int32_t x, int32_t y, int32_t z) { UNUSED_PARAM(sensor_type); UNUSED_PARAM(x); UNUSED_PARAM(y); UNUSED_PARAM(z); return false; } bool ContextHub::SetCalibration(SensorType sensor_type, int32_t x, int32_t y, int32_t z, int32_t w) { UNUSED_PARAM(sensor_type); UNUSED_PARAM(x); UNUSED_PARAM(y); UNUSED_PARAM(z); UNUSED_PARAM(w); return false; } bool ContextHub::SaveCalibration() { LOGE("Saving calibration data not implemented"); return false; } } // namespace android