diff --git a/automotive/vehicle/2.0/default/Android.mk b/automotive/vehicle/2.0/default/Android.mk index a346507d0a..e82204477b 100644 --- a/automotive/vehicle/2.0/default/Android.mk +++ b/automotive/vehicle/2.0/default/Android.mk @@ -40,6 +40,28 @@ LOCAL_SHARED_LIBRARIES := \ include $(BUILD_STATIC_LIBRARY) +############################################################################### +# Vehicle HAL Protobuf library +############################################################################### +include $(CLEAR_VARS) +LOCAL_SRC_FILES := $(call all-proto-files-under, impl/proto) + +LOCAL_PROTOC_OPTIMIZE_TYPE := nano + +LOCAL_MODULE := $(module_prefix)-libproto-native +LOCAL_MODULE_CLASS := STATIC_LIBRARIES + +LOCAL_MODULE_TAGS := optional + +LOCAL_STRIP_MODULE := keep_symbols + +generated_sources_dir := $(call local-generated-sources-dir) +LOCAL_EXPORT_C_INCLUDE_DIRS := \ + $(generated_sources_dir)/proto/$(LOCAL_PATH)/impl/proto + +include $(BUILD_STATIC_LIBRARY) + + ############################################################################### # Vehicle default VehicleHAL implementation ############################################################################### @@ -55,9 +77,13 @@ LOCAL_SHARED_LIBRARIES := \ libhidltransport \ libhwbinder \ liblog \ + libprotobuf-cpp-lite \ libutils \ $(module_prefix) \ +LOCAL_STATIC_LIBRARIES := \ + $(module_prefix)-libproto-native + include $(BUILD_STATIC_LIBRARY) @@ -114,7 +140,11 @@ LOCAL_SHARED_LIBRARIES := \ libhidltransport \ libhwbinder \ liblog \ + libprotobuf-cpp-lite \ libutils \ $(module_prefix) \ +LOCAL_STATIC_LIBRARIES := \ + $(module_prefix)-libproto-native + include $(BUILD_EXECUTABLE) diff --git a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rc b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rc index 344a9844f3..1ee4d47c37 100644 --- a/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rc +++ b/automotive/vehicle/2.0/default/android.hardware.automotive.vehicle@2.0-service.rc @@ -1,4 +1,4 @@ service vehicle-hal-2.0 /system/bin/hw/android.hardware.automotive.vehicle@2.0-service class hal user vehicle_network - group system + group system inet diff --git a/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.cpp b/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.cpp index 1c26759a3a..88717a935e 100644 --- a/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.cpp +++ b/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.cpp @@ -14,12 +14,17 @@ * limitations under the License. */ -#include "DefaultVehicleHal.h" +#define LOG_TAG "DefaultVehicleHal" #include - -#define LOG_TAG "default_vehicle" #include +#include +#include + +#include "DefaultVehicleHal.h" +#include "VehicleHalProto.pb.h" + +#define DEBUG_SOCKET (33452) namespace android { namespace hardware { @@ -29,144 +34,157 @@ namespace V2_0 { namespace impl { -VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get( - const VehiclePropValue& requestedPropValue, StatusCode* outStatus) { - *outStatus = StatusCode::OK; +void DefaultVehicleHal::doGetConfig(emulator::EmulatorMessage& rxMsg, + emulator::EmulatorMessage& respMsg) { + std::vector configs = listProperties(); + emulator::VehiclePropGet getProp = rxMsg.prop(0); - VehiclePropValuePtr v; - auto property = static_cast(requestedPropValue.prop); - int32_t areaId = requestedPropValue.areaId; - auto& pool = *getValuePool(); + respMsg.set_msg_type(emulator::GET_CONFIG_RESP); + respMsg.set_status(emulator::ERROR_INVALID_PROPERTY); - switch (property) { - case VehicleProperty::INFO_MAKE: - v = pool.obtainString("Default Car"); + for (auto& config : configs) { + // Find the config we are looking for + if (config.prop == getProp.prop()) { + emulator::VehiclePropConfig* protoCfg = respMsg.add_config(); + populateProtoVehicleConfig(protoCfg, config); + respMsg.set_status(emulator::RESULT_OK); break; - case VehicleProperty::HVAC_FAN_SPEED: - v = pool.obtainInt32(mFanSpeed); - break; - case VehicleProperty::HVAC_POWER_ON: - v = pool.obtainBoolean(mHvacPowerOn); - break; - case VehicleProperty::HVAC_RECIRC_ON: - v = pool.obtainBoolean(mHvacRecircOn); - break; - case VehicleProperty::HVAC_AC_ON: - v = pool.obtainBoolean(mHvacAcOn); - break; - case VehicleProperty::HVAC_AUTO_ON: - v = pool.obtainBoolean(mHvacAutoOn); - break; - case VehicleProperty::HVAC_FAN_DIRECTION: - v = pool.obtainInt32(toInt(mFanDirection)); - break; - case VehicleProperty::HVAC_DEFROSTER: - bool defroster; - *outStatus = getHvacDefroster(areaId, &defroster); - if (StatusCode::OK == *outStatus) { - v = pool.obtainBoolean(defroster); - } - break; - case VehicleProperty::HVAC_TEMPERATURE_SET: - float value; - *outStatus = getHvacTemperature(requestedPropValue.areaId, - &value); - if (StatusCode::OK == *outStatus) { - v = pool.obtainFloat(value); - } - break; - case VehicleProperty::INFO_FUEL_CAPACITY: - v = pool.obtainFloat(0.75f); - break; - case VehicleProperty::DISPLAY_BRIGHTNESS: - v = pool.obtainInt32(mBrightness); - break; - case VehicleProperty::NIGHT_MODE: - v = pool.obtainBoolean(false); - break; - case VehicleProperty::GEAR_SELECTION: - v = pool.obtainInt32(toInt(VehicleGear::GEAR_PARK)); - break; - case VehicleProperty::DRIVING_STATUS: - v = pool.obtainInt32(toInt(VehicleDrivingStatus::UNRESTRICTED)); - break; - case VehicleProperty::IGNITION_STATE: - v = pool.obtainInt32(toInt(VehicleIgnitionState::ACC)); - break; - case VehicleProperty::OBD2_LIVE_FRAME: - v = pool.obtainComplex(); - *outStatus = fillObd2LiveFrame(&v); - break; - case VehicleProperty::OBD2_FREEZE_FRAME: - v = pool.obtainComplex(); - *outStatus = fillObd2FreezeFrame(&v); - break; - default: - *outStatus = StatusCode::INVALID_ARG; + } } - - if (StatusCode::OK == *outStatus && v.get() != nullptr) { - v->prop = toInt(property); - v->areaId = areaId; - v->timestamp = elapsedRealtimeNano(); - } - - return v; } -StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) { - auto property = static_cast(propValue.prop); - const auto& v = propValue.value; +void DefaultVehicleHal::doGetConfigAll(emulator::EmulatorMessage& /* rxMsg */, + emulator::EmulatorMessage& respMsg) { + std::vector configs = listProperties(); - StatusCode status = StatusCode::OK; + respMsg.set_msg_type(emulator::GET_CONFIG_ALL_RESP); + respMsg.set_status(emulator::RESULT_OK); - switch (property) { - case VehicleProperty::HVAC_POWER_ON: - mHvacPowerOn = v.int32Values[0] == 1; - break; - case VehicleProperty::HVAC_RECIRC_ON: - mHvacRecircOn = v.int32Values[0] == 1; - break; - case VehicleProperty::HVAC_AC_ON: - mHvacAcOn = v.int32Values[0] == 1; - break; - case VehicleProperty::HVAC_AUTO_ON: - mHvacAutoOn = v.int32Values[0] == 1; - break; - case VehicleProperty::HVAC_DEFROSTER: - status = setHvacDefroster(propValue.areaId, v.int32Values[0] == 1); - break; - case VehicleProperty::HVAC_FAN_DIRECTION: - mFanDirection = - static_cast(v.int32Values[0]); - break; - case VehicleProperty::HVAC_FAN_SPEED: - mFanSpeed = v.int32Values[0]; - break; - case VehicleProperty::HVAC_TEMPERATURE_SET: - status = setHvacTemperature(propValue.areaId, v.floatValues[0]); - break; - case VehicleProperty::DISPLAY_BRIGHTNESS: - mBrightness = v.int32Values[0]; - break; - default: - status = StatusCode::INVALID_ARG; + for (auto& config : configs) { + emulator::VehiclePropConfig* protoCfg = respMsg.add_config(); + populateProtoVehicleConfig(protoCfg, config); } - - return status; } -void DefaultVehicleHal::onCreate() { - const auto& propConfigs(listProperties()); - auto obd2LiveFramePropConfig = std::find_if( - propConfigs.begin(), - propConfigs.end(), - [] (VehiclePropConfig config) -> bool { - return (config.prop == toInt(VehicleProperty::OBD2_LIVE_FRAME)); - }); +void DefaultVehicleHal::doGetProperty(emulator::EmulatorMessage& rxMsg, + emulator::EmulatorMessage& respMsg) { + int32_t areaId = 0; + emulator::VehiclePropGet getProp = rxMsg.prop(0); + int32_t propId = getProp.prop(); + emulator::Status status = emulator::ERROR_INVALID_PROPERTY; + VehiclePropValue* val; + + respMsg.set_msg_type(emulator::GET_PROPERTY_RESP); + + if (getProp.has_area_id()) { + areaId = getProp.area_id(); + } + + { + std::lock_guard lock(mPropsMutex); + + val = getVehiclePropValueLocked(propId, areaId); + if (val != nullptr) { + emulator::VehiclePropValue* protoVal = respMsg.add_value(); + populateProtoVehiclePropValue(protoVal, val); + status = emulator::RESULT_OK; + } + } + + respMsg.set_status(status); +} + +void DefaultVehicleHal::doGetPropertyAll(emulator::EmulatorMessage& /* rxMsg */, + emulator::EmulatorMessage& respMsg) { + respMsg.set_msg_type(emulator::GET_PROPERTY_ALL_RESP); + respMsg.set_status(emulator::RESULT_OK); + + { + std::lock_guard lock(mPropsMutex); + + for (auto& propVal : mProps) { + emulator::VehiclePropValue* protoVal = respMsg.add_value(); + populateProtoVehiclePropValue(protoVal, propVal.get()); + } + } +} + +void DefaultVehicleHal::doSetProperty(emulator::EmulatorMessage& rxMsg, + emulator::EmulatorMessage& respMsg) { + emulator::VehiclePropValue protoVal = rxMsg.value(0); + VehiclePropValue val; + + respMsg.set_msg_type(emulator::SET_PROPERTY_RESP); + + val.prop = protoVal.prop(); + val.areaId = protoVal.area_id(); + + // Copy value data if it is set. This automatically handles complex data types if needed. + if (protoVal.has_string_value()) { + val.value.stringValue = protoVal.string_value().c_str(); + } + + if (protoVal.has_bytes_value()) { + std::vector tmp(protoVal.bytes_value().begin(), protoVal.bytes_value().end()); + val.value.bytes = tmp; + } + + if (protoVal.int32_values_size() > 0) { + std::vector int32Values = std::vector(protoVal.int32_values_size()); + for (int i=0; i 0) { + std::vector int64Values = std::vector(protoVal.int64_values_size()); + for (int i=0; i 0) { + std::vector floatValues = std::vector(protoVal.float_values_size()); + for (int i=0; iprop == propId) && (prop->areaId == areaId)) { + return prop.get(); + } + } + ALOGW("%s: Property not found: propId = 0x%x, areaId = 0x%x", __FUNCTION__, propId, areaId); + return nullptr; +} + +void DefaultVehicleHal::initObd2LiveFrame(VehiclePropConfig& obd2LiveFramePropConfig) { mObd2SensorStore.reset(new Obd2SensorStore( - obd2LiveFramePropConfig->configArray[0], - obd2LiveFramePropConfig->configArray[1])); + obd2LiveFramePropConfig.configArray[0], + obd2LiveFramePropConfig.configArray[1])); // precalculate OBD2 sensor values mObd2SensorStore->setIntegerSensor( Obd2IntegerSensorIndex::FUEL_SYSTEM_STATUS, @@ -246,56 +264,461 @@ void DefaultVehicleHal::onCreate() { Obd2FloatSensorIndex::COMMANDED_THROTTLE_ACTUATOR, 0.024); } -StatusCode DefaultVehicleHal::getHvacTemperature(int32_t areaId, - float* outValue) { - if (areaId == toInt(VehicleAreaZone::ROW_1_LEFT)) { - *outValue = mRow1LeftHvacTemperatureSet; - } else if (areaId == toInt(VehicleAreaZone::ROW_1_RIGHT)) { - *outValue = mRow1RightHvacTemperatureSet; - } else { - return StatusCode::INVALID_ARG; +void DefaultVehicleHal::parseRxProtoBuf(std::vector& msg) { + emulator::EmulatorMessage rxMsg; + emulator::EmulatorMessage respMsg; + std::string str(reinterpret_cast(msg.data()), msg.size()); + + rxMsg.ParseFromString(str); + + switch (rxMsg.msg_type()) { + case emulator::GET_CONFIG_CMD: + doGetConfig(rxMsg, respMsg); + break; + case emulator::GET_CONFIG_ALL_CMD: + doGetConfigAll(rxMsg, respMsg); + break; + case emulator::GET_PROPERTY_CMD: + doGetProperty(rxMsg, respMsg); + break; + case emulator::GET_PROPERTY_ALL_CMD: + doGetPropertyAll(rxMsg, respMsg); + break; + case emulator::SET_PROPERTY_CMD: + doSetProperty(rxMsg, respMsg); + break; + default: + ALOGW("%s: Unknown message received, type = %d", __FUNCTION__, rxMsg.msg_type()); + respMsg.set_status(emulator::ERROR_UNIMPLEMENTED_CMD); + break; } - return StatusCode::OK; + + // Send the reply + txMsg(respMsg); } -StatusCode DefaultVehicleHal::setHvacTemperature( - int32_t areaId, float value) { - if (areaId == toInt(VehicleAreaZone::ROW_1_LEFT)) { - mRow1LeftHvacTemperatureSet = value; - } else if (areaId == toInt(VehicleAreaZone::ROW_1_RIGHT)) { - mRow1RightHvacTemperatureSet = value; - } else { - return StatusCode::INVALID_ARG; +// Copies internal VehiclePropConfig data structure to protobuf VehiclePropConfig +void DefaultVehicleHal::populateProtoVehicleConfig(emulator::VehiclePropConfig* protoCfg, + const VehiclePropConfig& cfg) { + protoCfg->set_prop(cfg.prop); + protoCfg->set_access(toInt(cfg.access)); + protoCfg->set_change_mode(toInt(cfg.changeMode)); + protoCfg->set_value_type(toInt(getPropType(cfg.prop))); + + if (!isGlobalProp(cfg.prop)) { + protoCfg->set_supported_areas(cfg.supportedAreas); } - return StatusCode::OK; + + for (auto& configElement : cfg.configArray) { + protoCfg->add_config_array(configElement); + } + + if (cfg.configString.size() > 0) { + protoCfg->set_config_string(cfg.configString.c_str(), cfg.configString.size()); + } + + // Populate the min/max values based on property type + switch (getPropType(cfg.prop)) { + case VehiclePropertyType::STRING: + case VehiclePropertyType::BOOLEAN: + case VehiclePropertyType::INT32_VEC: + case VehiclePropertyType::FLOAT_VEC: + case VehiclePropertyType::BYTES: + case VehiclePropertyType::COMPLEX: + // Do nothing. These types don't have min/max values + break; + case VehiclePropertyType::INT64: + if (cfg.areaConfigs.size() > 0) { + emulator::VehicleAreaConfig* aCfg = protoCfg->add_area_configs(); + aCfg->set_min_int64_value(cfg.areaConfigs[0].minInt64Value); + aCfg->set_max_int64_value(cfg.areaConfigs[0].maxInt64Value); + } + break; + case VehiclePropertyType::FLOAT: + if (cfg.areaConfigs.size() > 0) { + emulator::VehicleAreaConfig* aCfg = protoCfg->add_area_configs(); + aCfg->set_min_float_value(cfg.areaConfigs[0].minFloatValue); + aCfg->set_max_float_value(cfg.areaConfigs[0].maxFloatValue); + } + break; + case VehiclePropertyType::INT32: + if (cfg.areaConfigs.size() > 0) { + emulator::VehicleAreaConfig* aCfg = protoCfg->add_area_configs(); + aCfg->set_min_int32_value(cfg.areaConfigs[0].minInt32Value); + aCfg->set_max_int32_value(cfg.areaConfigs[0].maxInt32Value); + } + break; + default: + ALOGW("%s: Unknown property type: 0x%x", __FUNCTION__, toInt(getPropType(cfg.prop))); + break; + } + + protoCfg->set_min_sample_rate(cfg.minSampleRate); + protoCfg->set_max_sample_rate(cfg.maxSampleRate); } -StatusCode DefaultVehicleHal::getHvacDefroster(int32_t areaId, - bool* outValue) { - ALOGI("Getting Hvac defroster for area: 0x%x", areaId); +// Copies internal VehiclePropValue data structure to protobuf VehiclePropValue +void DefaultVehicleHal::populateProtoVehiclePropValue(emulator::VehiclePropValue* protoVal, + const VehiclePropValue* val) { + protoVal->set_prop(val->prop); + protoVal->set_value_type(toInt(getPropType(val->prop))); + protoVal->set_timestamp(val->timestamp); + protoVal->set_area_id(val->areaId); - if (areaId == toInt(VehicleAreaWindow::FRONT_WINDSHIELD)) { - *outValue = mFrontDefroster; - } else if (areaId == toInt(VehicleAreaWindow::REAR_WINDSHIELD)) { - *outValue = mRearDefroster; - } else { - ALOGE("Unable to get hvac defroster for area: 0x%x", areaId); - return StatusCode::INVALID_ARG; + // Copy value data if it is set. + // - for bytes and strings, this is indicated by size > 0 + // - for int32, int64, and float, copy the values if vectors have data + if (val->value.stringValue.size() > 0) { + protoVal->set_string_value(val->value.stringValue.c_str(), val->value.stringValue.size()); } - ALOGI("Getting Hvac defroster for area: 0x%x, OK", areaId); - return StatusCode::OK; + if (val->value.bytes.size() > 0) { + protoVal->set_bytes_value(val->value.bytes.data(), val->value.bytes.size()); + } + + for (auto& int32Value : val->value.int32Values) { + protoVal->add_int32_values(int32Value); + } + + for (auto& int64Value : val->value.int64Values) { + protoVal->add_int64_values(int64Value); + } + + for (auto& floatValue : val->value.floatValues) { + protoVal->add_float_values(floatValue); + } } -StatusCode DefaultVehicleHal::setHvacDefroster(int32_t areaId, bool value) { - if (areaId == toInt(VehicleAreaWindow::FRONT_WINDSHIELD)) { - mFrontDefroster = value; - } else if (areaId == toInt(VehicleAreaWindow::REAR_WINDSHIELD)) { - mRearDefroster = value; - } else { - return StatusCode::INVALID_ARG; +void DefaultVehicleHal::rxMsg(void) { + int numBytes = 0; + int32_t msgSize; + do { + // This is a variable length message. + // Read the number of bytes to rx over the socket + numBytes = read(mCurSocket, &msgSize, sizeof(msgSize)); + + if (numBytes != sizeof(msgSize)) { + // This happens when connection is closed + ALOGD("%s: numBytes=%d, expected=4", __FUNCTION__, numBytes); + break; + } + + std::vector msg = std::vector(msgSize); + + numBytes = read(mCurSocket, msg.data(), msgSize); + + if ((numBytes == msgSize) && (msgSize > 0)) { + // Received a message. + parseRxProtoBuf(msg); + } else { + // This happens when connection is closed + ALOGD("%s: numBytes=%d, msgSize=%d", __FUNCTION__, numBytes, msgSize); + break; + } + } while (mExit == 0); +} + +void DefaultVehicleHal::rxThread(void) { + // Initialize the socket + { + int retVal; + struct sockaddr_in servAddr; + + mSocket = socket(AF_INET, SOCK_STREAM, 0); + if (mSocket < 0) { + ALOGE("%s: socket() failed, mSocket=%d, errno=%d", __FUNCTION__, mSocket, errno); + mSocket = -1; + return; + } + + bzero(&servAddr, sizeof(servAddr)); + servAddr.sin_family = AF_INET; + servAddr.sin_addr.s_addr = INADDR_ANY; + servAddr.sin_port = htons(DEBUG_SOCKET); + + retVal = bind(mSocket, reinterpret_cast(&servAddr), sizeof(servAddr)); + if(retVal < 0) { + ALOGE("%s: Error on binding: retVal=%d, errno=%d", __FUNCTION__, retVal, errno); + close(mSocket); + mSocket = -1; + return; + } + + listen(mSocket, 1); + + // Set the socket to be non-blocking so we can poll it continouously + fcntl(mSocket, F_SETFL, O_NONBLOCK); } - return StatusCode::OK; + + while (mExit == 0) { + struct sockaddr_in cliAddr; + socklen_t cliLen = sizeof(cliAddr); + int cSocket = accept(mSocket, reinterpret_cast(&cliAddr), &cliLen); + + if (cSocket >= 0) { + { + std::lock_guard lock(mTxMutex); + mCurSocket = cSocket; + } + ALOGD("%s: Incoming connection received on socket %d", __FUNCTION__, cSocket); + rxMsg(); + ALOGD("%s: Connection terminated on socket %d", __FUNCTION__, cSocket); + { + std::lock_guard lock(mTxMutex); + mCurSocket = -1; + } + } + + // TODO: Use a blocking socket? + // Check every 100ms for a new socket connection + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // Shutdown the socket + close(mSocket); + mSocket = -1; +} + +// This function sets the default value of a property if we are interested in setting it. +// TODO: Co-locate the default values with the configuration structure, to make it easier to +// add new properties and their defaults. +void DefaultVehicleHal::setDefaultValue(VehiclePropValue* prop) { + switch (prop->prop) { + case toInt(VehicleProperty::INFO_MAKE): + prop->value.stringValue = "Default Car"; + break; + case toInt(VehicleProperty::HVAC_POWER_ON): + prop->value.int32Values[0] = 1; + break; + case toInt(VehicleProperty::HVAC_DEFROSTER): + prop->value.int32Values[0] = 0; + break; + case toInt(VehicleProperty::HVAC_RECIRC_ON): + prop->value.int32Values[0] = 1; + break; + case toInt(VehicleProperty::HVAC_AC_ON): + prop->value.int32Values[0] = 1; + break; + case toInt(VehicleProperty::HVAC_AUTO_ON): + prop->value.int32Values[0] = 1; + break; + case toInt(VehicleProperty::HVAC_FAN_SPEED): + prop->value.int32Values[0] = 3; + break; + case toInt(VehicleProperty::HVAC_FAN_DIRECTION): + prop->value.int32Values[0] = toInt(VehicleHvacFanDirection::FACE); + break; + case toInt(VehicleProperty::HVAC_TEMPERATURE_SET): + prop->value.floatValues[0] = 16; + break; + case toInt(VehicleProperty::NIGHT_MODE): + prop->value.int32Values[0] = 0; + break; + case toInt(VehicleProperty::DRIVING_STATUS): + prop->value.int32Values[0] = toInt(VehicleDrivingStatus::UNRESTRICTED); + break; + case toInt(VehicleProperty::GEAR_SELECTION): + prop->value.int32Values[0] = toInt(VehicleGear::GEAR_PARK); + break; + case toInt(VehicleProperty::INFO_FUEL_CAPACITY): + prop->value.floatValues[0] = 0.75f; + break; + case toInt(VehicleProperty::DISPLAY_BRIGHTNESS): + prop->value.int32Values[0] = 7; + break; + case toInt(VehicleProperty::IGNITION_STATE): + prop->value.int32Values[0] = toInt(VehicleIgnitionState::ON); + break; + case toInt(VehicleProperty::OBD2_LIVE_FRAME): + // OBD2 is handled separately + break; + case toInt(VehicleProperty::OBD2_FREEZE_FRAME): + // OBD2 is handled separately + break; + default: + ALOGW("%s: propId=0x%x not found", __FUNCTION__, prop->prop); + break; + } +} + +// Transmit a reply back to the emulator +void DefaultVehicleHal::txMsg(emulator::EmulatorMessage& txMsg) { + std::string msgString; + + if (txMsg.SerializeToString(&msgString)) { + int32_t msgLen = msgString.length(); + int retVal = 0; + + // TODO: Prepend the message length to the string without a copy + msgString.insert(0, reinterpret_cast(&msgLen), 4); + + // Send the message + { + std::lock_guard lock(mTxMutex); + if (mCurSocket != -1) { + retVal = write(mCurSocket, msgString.data(), msgString.size()); + } + } + + if (retVal < 0) { + ALOGE("%s: Failed to tx message: retval=%d, errno=%d", __FUNCTION__, retVal, errno); + } + } else { + ALOGE("%s: SerializeToString failed!", __FUNCTION__); + } +} + +// Updates the property value held in the HAL +StatusCode DefaultVehicleHal::updateProperty(const VehiclePropValue& propValue) { + auto propId = propValue.prop; + auto areaId = propValue.areaId; + StatusCode status = StatusCode::INVALID_ARG; + + { + std::lock_guard lock(mPropsMutex); + + VehiclePropValue* internalPropValue = getVehiclePropValueLocked(propId, areaId); + if (internalPropValue != nullptr) { + internalPropValue->value = propValue.value; + internalPropValue->timestamp = elapsedRealtimeNano(); + status = StatusCode::OK; + } + } + return status; +} + +VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get( + const VehiclePropValue& requestedPropValue, StatusCode* outStatus) { + auto areaId = requestedPropValue.areaId; + auto& pool = *getValuePool(); + auto propId = requestedPropValue.prop; + StatusCode status; + VehiclePropValuePtr v = nullptr; + + switch (propId) { + case toInt(VehicleProperty::OBD2_LIVE_FRAME): + v = pool.obtainComplex(); + status = fillObd2LiveFrame(&v); + break; + case toInt(VehicleProperty::OBD2_FREEZE_FRAME): + v = pool.obtainComplex(); + status = fillObd2FreezeFrame(&v); + break; + default: + { + std::lock_guard lock(mPropsMutex); + + VehiclePropValue *internalPropValue = getVehiclePropValueLocked(propId, areaId); + if (internalPropValue != nullptr) { + v = pool.obtain(*internalPropValue); + } + } + + if (v != nullptr) { + status = StatusCode::OK; + } else { + status = StatusCode::INVALID_ARG; + } + break; + } + + *outStatus = status; + return v; +} + +StatusCode DefaultVehicleHal::set(const VehiclePropValue& propValue) { + StatusCode status = updateProperty(propValue); + + if (status == StatusCode::OK) { + // Send property update to emulator + emulator::EmulatorMessage msg; + emulator::VehiclePropValue *val = msg.add_value(); + populateProtoVehiclePropValue(val, &propValue); + msg.set_status(emulator::RESULT_OK); + msg.set_msg_type(emulator::SET_PROPERTY_ASYNC); + txMsg(msg); + } + + return status; +} + +// Parse supported properties list and generate vector of property values to hold current values. +void DefaultVehicleHal::onCreate() { + // Initialize member variables + mCurSocket = -1; + mExit = 0; + mSocket = -1; + + // Get the list of configurations supported by this HAL + std::vector configs = listProperties(); + + for (auto& cfg : configs) { + VehiclePropertyType propType = getPropType(cfg.prop); + int32_t supportedAreas = cfg.supportedAreas; + int32_t vecSize; + + // Set the vector size based on property type + switch (propType) { + case VehiclePropertyType::BOOLEAN: + case VehiclePropertyType::INT32: + case VehiclePropertyType::INT64: + case VehiclePropertyType::FLOAT: + vecSize = 1; + break; + case VehiclePropertyType::INT32_VEC: + case VehiclePropertyType::FLOAT_VEC: + case VehiclePropertyType::BYTES: + // TODO: Add proper support for these types + vecSize = 1; + break; + case VehiclePropertyType::STRING: + // Require individual handling + vecSize = 0; + break; + case VehiclePropertyType::COMPLEX: + switch (cfg.prop) { + case toInt(VehicleProperty::OBD2_LIVE_FRAME): + initObd2LiveFrame(cfg); + break; + default: + // Need to handle each complex property separately + break; + } + continue; + break; + case VehiclePropertyType::MASK: + default: + ALOGW("%s: propType=0x%x not found", __FUNCTION__, propType); + vecSize = 0; + break; + } + + // A global property will have supportedAreas = 0 + if (getPropArea(cfg.prop) == VehicleArea::GLOBAL) { + supportedAreas = 0; + } + + // This loop is a do-while so it executes at least once to handle global properties + do { + int32_t curArea = supportedAreas; + + // Clear the right-most bit of supportedAreas + supportedAreas &= supportedAreas - 1; + + // Set curArea to the previously cleared bit + curArea ^= supportedAreas; + + // Create a separate instance for each individual zone + std::unique_ptr prop = createVehiclePropValue(propType, vecSize); + prop->areaId = curArea; + prop->prop = cfg.prop; + setDefaultValue(prop.get()); + mProps.push_back(std::move(prop)); + } while (supportedAreas != 0); + } + + // Start rx thread + mThread = std::thread(&DefaultVehicleHal::rxThread, this); } StatusCode DefaultVehicleHal::fillObd2LiveFrame(VehiclePropValuePtr* v) { diff --git a/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.h b/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.h index ccedeeb3a5..edfc224914 100644 --- a/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.h +++ b/automotive/vehicle/2.0/default/impl/DefaultVehicleHal.h @@ -21,8 +21,12 @@ #include #include -#include +#include +#include #include +#include +#include "VehicleHalProto.pb.h" + namespace android { namespace hardware { @@ -34,6 +38,23 @@ namespace impl { class DefaultVehicleHal : public VehicleHal { public: + DefaultVehicleHal() : mThread() {} + ~DefaultVehicleHal() override { + // Notify thread to finish and wait for it to terminate + mExit = 1; + + // Close emulator socket if it is open + { + std::lock_guard lock(mTxMutex); + if (mCurSocket != -1) { + close(mCurSocket); + mCurSocket = -1; + } + } + + mThread.join(); + } + std::vector listProperties() override { return std::vector(std::begin(kVehicleProperties), std::end(kVehicleProperties)); @@ -46,38 +67,47 @@ public: StatusCode set(const VehiclePropValue& propValue) override; - StatusCode subscribe(int32_t /*property*/, - int32_t /*areas*/, - float /*sampleRate*/) override { - // TODO(pavelm): implement + StatusCode subscribe(int32_t property, int32_t areas, float sampleRate) { + ALOGD("%s: not implemented: prop=0x%x, areas=0x%x, rate=%f", __FUNCTION__, property, + areas, sampleRate); return StatusCode::OK; } - StatusCode unsubscribe(int32_t /*property*/) override { - // TODO(pavelm): implement + StatusCode unsubscribe(int32_t property) { + ALOGD("%s: not implemented: prop=0x%x", __FUNCTION__, property); return StatusCode::OK; } private: - StatusCode getHvacTemperature(int32_t areaId, float* outValue); - StatusCode setHvacTemperature(int32_t areaId, float value); - StatusCode getHvacDefroster(int32_t areaId, bool* outValue); - StatusCode setHvacDefroster(int32_t areaId, bool value); + void doGetConfig(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg); + void doGetConfigAll(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg); + void doGetProperty(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg); + void doGetPropertyAll(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg); + void doSetProperty(emulator::EmulatorMessage& rxMsg, emulator::EmulatorMessage& respMsg); + VehiclePropValue* getVehiclePropValueLocked(int32_t propId, int32_t areaId); + void initObd2LiveFrame(VehiclePropConfig& obd2LiveFramePropConfig); + void parseRxProtoBuf(std::vector& msg); + void populateProtoVehicleConfig(emulator::VehiclePropConfig* protoCfg, + const VehiclePropConfig& cfg); + void populateProtoVehiclePropValue(emulator::VehiclePropValue* protoVal, + const VehiclePropValue* val); + void setDefaultValue(VehiclePropValue* prop); + void rxMsg(void); + void rxThread(void); + void txMsg(emulator::EmulatorMessage& txMsg); + StatusCode updateProperty(const VehiclePropValue& propValue); StatusCode fillObd2LiveFrame(VehiclePropValuePtr* v); StatusCode fillObd2FreezeFrame(VehiclePropValuePtr* v); private: - int32_t mFanSpeed = 3; - int32_t mBrightness = 7; - float mRow1LeftHvacTemperatureSet = 16; - float mRow1RightHvacTemperatureSet = 22; - bool mFrontDefroster = false; - bool mRearDefroster = false; - bool mHvacPowerOn = true; - bool mHvacRecircOn = true; - bool mHvacAcOn = true; - bool mHvacAutoOn = true; - VehicleHvacFanDirection mFanDirection = VehicleHvacFanDirection::FACE; + // TODO: Use a hashtable to support indexing props + std::vector> mProps; + std::atomic mCurSocket; + std::atomic mExit; std::unique_ptr mObd2SensorStore{nullptr}; + std::mutex mPropsMutex; + int mSocket; + std::mutex mTxMutex; + std::thread mThread; }; } // impl diff --git a/automotive/vehicle/2.0/default/impl/proto/VehicleHalProto.proto b/automotive/vehicle/2.0/default/impl/proto/VehicleHalProto.proto new file mode 100644 index 0000000000..86433f50f9 --- /dev/null +++ b/automotive/vehicle/2.0/default/impl/proto/VehicleHalProto.proto @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2015 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. + */ + +syntax = "proto2"; +option optimize_for = LITE_RUNTIME; + +package emulator; + +// CMD messages are from workstation --> VHAL +// RESP messages are from VHAL --> workstation +enum MsgType { + GET_CONFIG_CMD = 0; + GET_CONFIG_RESP = 1; + GET_CONFIG_ALL_CMD = 2; + GET_CONFIG_ALL_RESP = 3; + GET_PROPERTY_CMD = 4; + GET_PROPERTY_RESP = 5; + GET_PROPERTY_ALL_CMD = 6; + GET_PROPERTY_ALL_RESP = 7; + SET_PROPERTY_CMD = 8; + SET_PROPERTY_RESP = 9; + SET_PROPERTY_ASYNC = 10; +} +enum Status { + RESULT_OK = 0; + ERROR_UNKNOWN = 1; + ERROR_UNIMPLEMENTED_CMD = 2; + ERROR_INVALID_PROPERTY = 3; + ERROR_INVALID_AREA_ID = 4; + ERROR_PROPERTY_UNINITIALIZED = 5; + ERROR_WRITE_ONLY_PROPERTY = 6; + ERROR_MEMORY_ALLOC_FAILED = 7; + ERROR_INVALID_OPERATION = 8; +} + +message VehicleAreaConfig { + required int32 area_id = 1; + optional sint32 min_int32_value = 2; + optional sint32 max_int32_value = 3; + optional sint64 min_int64_value = 4; + optional sint64 max_int64_value = 5; + optional float min_float_value = 6; + optional float max_float_value = 7; +} + +message VehiclePropConfig { + required int32 prop = 1; + optional int32 access = 2; + optional int32 change_mode = 3; + optional int32 value_type = 4; + optional int32 supported_areas = 5; + repeated VehicleAreaConfig area_configs = 6; + optional int32 config_flags = 7; + repeated int32 config_array = 8; + optional string config_string = 9; + optional float min_sample_rate = 10; + optional float max_sample_rate = 11; +}; + +message VehiclePropValue { + // common data + required int32 prop = 1; + optional int32 value_type = 2; + optional int64 timestamp = 3; // required for valid data from HAL, skipped for set + + // values + optional int32 area_id = 4; + repeated sint32 int32_values = 5; // this also covers boolean value. + repeated sint64 int64_values = 6; + repeated float float_values = 7; + optional string string_value = 8; + optional bytes bytes_value = 9; +}; + +// This structure is used to notify what values to get from the Vehicle HAL +message VehiclePropGet { + required int32 prop = 1; + optional int32 area_id = 2; +}; + +message EmulatorMessage { + required MsgType msg_type = 1; + optional Status status = 2; // Only for RESP messages + repeated VehiclePropGet prop = 3; // Provided for getConfig, getProperty commands + repeated VehiclePropConfig config = 4; + repeated VehiclePropValue value = 5; +};