Merge "Enable vehicle HAL to be controlled via ADB"

This commit is contained in:
Steve Paik
2017-02-09 00:27:32 +00:00
committed by Android (Google) Code Review
5 changed files with 773 additions and 190 deletions

View File

@@ -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)

View File

@@ -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

View File

@@ -14,12 +14,17 @@
* limitations under the License.
*/
#include "DefaultVehicleHal.h"
#define LOG_TAG "DefaultVehicleHal"
#include <algorithm>
#define LOG_TAG "default_vehicle"
#include <android/log.h>
#include <netinet/in.h>
#include <sys/socket.h>
#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<VehiclePropConfig> configs = listProperties();
emulator::VehiclePropGet getProp = rxMsg.prop(0);
VehiclePropValuePtr v;
auto property = static_cast<VehicleProperty>(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<VehicleProperty>(propValue.prop);
const auto& v = propValue.value;
void DefaultVehicleHal::doGetConfigAll(emulator::EmulatorMessage& /* rxMsg */,
emulator::EmulatorMessage& respMsg) {
std::vector<VehiclePropConfig> 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<VehicleHvacFanDirection>(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<std::mutex> 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<std::mutex> 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<uint8_t> tmp(protoVal.bytes_value().begin(), protoVal.bytes_value().end());
val.value.bytes = tmp;
}
if (protoVal.int32_values_size() > 0) {
std::vector<int32_t> int32Values = std::vector<int32_t>(protoVal.int32_values_size());
for (int i=0; i<protoVal.int32_values_size(); i++) {
int32Values[i] = protoVal.int32_values(i);
}
val.value.int32Values = int32Values;
}
if (protoVal.int64_values_size() > 0) {
std::vector<int64_t> int64Values = std::vector<int64_t>(protoVal.int64_values_size());
for (int i=0; i<protoVal.int64_values_size(); i++) {
int64Values[i] = protoVal.int64_values(i);
}
val.value.int64Values = int64Values;
}
if (protoVal.float_values_size() > 0) {
std::vector<float> floatValues = std::vector<float>(protoVal.float_values_size());
for (int i=0; i<protoVal.float_values_size(); i++) {
floatValues[i] = protoVal.float_values(i);
}
val.value.floatValues = floatValues;
}
if (updateProperty(val) == StatusCode::OK) {
// Send property up to VehicleHalManager via callback
auto& pool = *getValuePool();
VehiclePropValuePtr v = pool.obtain(val);
doHalEvent(std::move(v));
respMsg.set_status(emulator::RESULT_OK);
} else {
respMsg.set_status(emulator::ERROR_INVALID_PROPERTY);
}
}
// This function should only be called while mPropsMutex is locked.
VehiclePropValue* DefaultVehicleHal::getVehiclePropValueLocked(int32_t propId, int32_t areaId) {
if (getPropArea(propId) == VehicleArea::GLOBAL) {
// In VehicleHal, global properties have areaId = -1. We use 0.
areaId = 0;
}
for (auto& prop : mProps) {
if ((prop->prop == 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<uint8_t>& msg) {
emulator::EmulatorMessage rxMsg;
emulator::EmulatorMessage respMsg;
std::string str(reinterpret_cast<const char*>(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<uint8_t> msg = std::vector<uint8_t>(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<struct sockaddr*>(&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<struct sockaddr*>(&cliAddr), &cliLen);
if (cSocket >= 0) {
{
std::lock_guard<std::mutex> 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<std::mutex> 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<char*>(&msgLen), 4);
// Send the message
{
std::lock_guard<std::mutex> 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<std::mutex> 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<std::mutex> 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<VehiclePropConfig> 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<VehiclePropValue> 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) {

View File

@@ -21,8 +21,12 @@
#include <VehicleHal.h>
#include <impl/DefaultConfig.h>
#include <vehicle_hal_manager/Obd2SensorStore.h>
#include <sys/socket.h>
#include <thread>
#include <utils/SystemClock.h>
#include <vehicle_hal_manager/Obd2SensorStore.h>
#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<std::mutex> lock(mTxMutex);
if (mCurSocket != -1) {
close(mCurSocket);
mCurSocket = -1;
}
}
mThread.join();
}
std::vector<VehiclePropConfig> listProperties() override {
return std::vector<VehiclePropConfig>(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<uint8_t>& 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<std::unique_ptr<VehiclePropValue>> mProps;
std::atomic<int> mCurSocket;
std::atomic<int> mExit;
std::unique_ptr<Obd2SensorStore> mObd2SensorStore{nullptr};
std::mutex mPropsMutex;
int mSocket;
std::mutex mTxMutex;
std::thread mThread;
};
} // impl

View File

@@ -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;
};