blob: 3ef42f1683bdb68c43a3c6322d3440e37f4c5020 [file] [log] [blame]
/*
* 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.
*/
#define LOG_TAG "DefaultVehicleHal_v2_0"
#include <android-base/macros.h>
#include <android-base/properties.h>
#include <android/log.h>
#include <dirent.h>
#include <sys/system_properties.h>
#include <fstream>
#include <regex>
#include "EmulatedVehicleHal.h"
#include "JsonFakeValueGenerator.h"
#include "LinearFakeValueGenerator.h"
#include "Obd2SensorStore.h"
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace impl {
static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorIntegerSensors,
size_t numVendorFloatSensors) {
std::unique_ptr<Obd2SensorStore> sensorStore(
new Obd2SensorStore(numVendorIntegerSensors, numVendorFloatSensors));
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_SYSTEM_STATUS,
toInt(Obd2FuelSystemStatus::CLOSED_LOOP));
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MALFUNCTION_INDICATOR_LIGHT_ON, 0);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_MONITORS_SUPPORTED,
toInt(Obd2IgnitionMonitorKind::SPARK));
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::IGNITION_SPECIFIC_MONITORS,
Obd2CommonIgnitionMonitors::COMPONENTS_AVAILABLE |
Obd2CommonIgnitionMonitors::MISFIRE_AVAILABLE |
Obd2SparkIgnitionMonitors::AC_REFRIGERANT_AVAILABLE |
Obd2SparkIgnitionMonitors::EVAPORATIVE_SYSTEM_AVAILABLE);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::INTAKE_AIR_TEMPERATURE, 35);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::COMMANDED_SECONDARY_AIR_STATUS,
toInt(Obd2SecondaryAirStatus::FROM_OUTSIDE_OR_OFF));
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::NUM_OXYGEN_SENSORS_PRESENT, 1);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::RUNTIME_SINCE_ENGINE_START, 500);
sensorStore->setIntegerSensor(
DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_WITH_MALFUNCTION_INDICATOR_LIGHT_ON, 0);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::WARMUPS_SINCE_CODES_CLEARED, 51);
sensorStore->setIntegerSensor(
DiagnosticIntegerSensorIndex::DISTANCE_TRAVELED_SINCE_CODES_CLEARED, 365);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::ABSOLUTE_BAROMETRIC_PRESSURE, 30);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::CONTROL_MODULE_VOLTAGE, 12);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::AMBIENT_AIR_TEMPERATURE, 18);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::MAX_FUEL_AIR_EQUIVALENCE_RATIO, 1);
sensorStore->setIntegerSensor(DiagnosticIntegerSensorIndex::FUEL_TYPE,
toInt(Obd2FuelType::GASOLINE));
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CALCULATED_ENGINE_LOAD, 0.153);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK1, -0.16);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK1, -0.16);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::SHORT_TERM_FUEL_TRIM_BANK2, -0.16);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::LONG_TERM_FUEL_TRIM_BANK2, -0.16);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::INTAKE_MANIFOLD_ABSOLUTE_PRESSURE, 7.5);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ENGINE_RPM, 1250.);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::VEHICLE_SPEED, 40.);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::TIMING_ADVANCE, 2.5);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::THROTTLE_POSITION, 19.75);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::OXYGEN_SENSOR1_VOLTAGE, 0.265);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::FUEL_TANK_LEVEL_INPUT, 0.824);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::EVAPORATION_SYSTEM_VAPOR_PRESSURE,
-0.373);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::CATALYST_TEMPERATURE_BANK1_SENSOR1,
190.);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::RELATIVE_THROTTLE_POSITION, 3.);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ABSOLUTE_THROTTLE_POSITION_B, 0.306);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_D, 0.188);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::ACCELERATOR_PEDAL_POSITION_E, 0.094);
sensorStore->setFloatSensor(DiagnosticFloatSensorIndex::COMMANDED_THROTTLE_ACTUATOR, 0.024);
return sensorStore;
}
EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client,
EmulatedUserHal* emulatedUserHal)
: mPropStore(propStore),
mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
std::placeholders::_1)),
mVehicleClient(client),
mEmulatedUserHal(emulatedUserHal) {
initStaticConfig();
for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {
mPropStore->registerProperty(kVehicleProperties[i].config);
}
mVehicleClient->registerPropertyValueCallback(std::bind(&EmulatedVehicleHal::onPropertyValue,
this, std::placeholders::_1,
std::placeholders::_2));
mInitVhalValueOverride =
android::base::GetBoolProperty("persist.vendor.vhal_init_value_override", false);
if (mInitVhalValueOverride) {
getAllPropertiesOverride();
}
}
void EmulatedVehicleHal::getAllPropertiesOverride() {
if (auto dir = opendir("/vendor/etc/vhaloverride/")) {
std::regex reg_json(".*[.]json", std::regex::icase);
while (auto f = readdir(dir)) {
if (!regex_match(f->d_name, reg_json)) {
continue;
}
std::string file = "/vendor/etc/vhaloverride/" + std::string(f->d_name);
JsonFakeValueGenerator tmpGenerator(file);
std::vector<VehiclePropValue> propvalues = tmpGenerator.getAllEvents();
mVehiclePropertiesOverride.insert(std::end(mVehiclePropertiesOverride),
std::begin(propvalues), std::end(propvalues));
}
closedir(dir);
}
}
VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get(
const VehiclePropValue& requestedPropValue, StatusCode* outStatus) {
auto propId = requestedPropValue.prop;
ALOGV("get(%d)", propId);
auto& pool = *getValuePool();
VehiclePropValuePtr v = nullptr;
switch (propId) {
case OBD2_FREEZE_FRAME:
v = pool.obtainComplex();
*outStatus = fillObd2FreezeFrame(requestedPropValue, v.get());
break;
case OBD2_FREEZE_FRAME_INFO:
v = pool.obtainComplex();
*outStatus = fillObd2DtcInfo(v.get());
break;
default:
if (mEmulatedUserHal != nullptr && mEmulatedUserHal->isSupported(propId)) {
ALOGI("get(): getting value for prop %d from User HAL", propId);
const auto& ret = mEmulatedUserHal->onGetProperty(requestedPropValue);
if (!ret.ok()) {
ALOGE("get(): User HAL returned error: %s", ret.error().message().c_str());
*outStatus = StatusCode(ret.error().code());
} else {
auto value = ret.value().get();
if (value != nullptr) {
ALOGI("get(): User HAL returned value: %s", toString(*value).c_str());
v = getValuePool()->obtain(*value);
*outStatus = StatusCode::OK;
} else {
ALOGE("get(): User HAL returned null value");
*outStatus = StatusCode::INTERNAL_ERROR;
}
}
break;
}
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
if (internalPropValue != nullptr) {
v = getValuePool()->obtain(*internalPropValue);
}
*outStatus = v != nullptr ? StatusCode::OK : StatusCode::INVALID_ARG;
break;
}
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
}
return v;
}
bool EmulatedVehicleHal::dump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) {
return mVehicleClient->dump(fd, options);
}
StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) {
constexpr bool updateStatus = false;
if (propValue.prop == kGenerateFakeDataControllingProperty) {
// Send the generator controlling request to the server.
// 'updateStatus' flag is only for the value sent by setProperty (propValue in this case)
// instead of the generated values triggered by it. 'propValue' works as a control signal
// here, since we never send the control signal back, the value of 'updateStatus' flag
// does not matter here.
auto status = mVehicleClient->setProperty(propValue, updateStatus);
return status;
} else if (mHvacPowerProps.count(propValue.prop)) {
auto hvacPowerOn = mPropStore->readValueOrNull(
toInt(VehicleProperty::HVAC_POWER_ON),
(VehicleAreaSeat::ROW_1_LEFT | VehicleAreaSeat::ROW_1_RIGHT |
VehicleAreaSeat::ROW_2_LEFT | VehicleAreaSeat::ROW_2_CENTER |
VehicleAreaSeat::ROW_2_RIGHT));
if (hvacPowerOn && hvacPowerOn->value.int32Values.size() == 1
&& hvacPowerOn->value.int32Values[0] == 0) {
return StatusCode::NOT_AVAILABLE;
}
} else {
// Handle property specific code
switch (propValue.prop) {
case OBD2_FREEZE_FRAME_CLEAR:
return clearObd2FreezeFrames(propValue);
case VEHICLE_MAP_SERVICE:
// Placeholder for future implementation of VMS property in the default hal. For
// now, just returns OK; otherwise, hal clients crash with property not supported.
return StatusCode::OK;
}
}
if (propValue.status != VehiclePropertyStatus::AVAILABLE) {
// Android side cannot set property status - this value is the
// purview of the HAL implementation to reflect the state of
// its underlying hardware
return StatusCode::INVALID_ARG;
}
auto currentPropValue = mPropStore->readValueOrNull(propValue);
if (currentPropValue == nullptr) {
return StatusCode::INVALID_ARG;
}
if (currentPropValue->status != VehiclePropertyStatus::AVAILABLE) {
// do not allow Android side to set() a disabled/error property
return StatusCode::NOT_AVAILABLE;
}
if (mInEmulator && propValue.prop == toInt(VehicleProperty::DISPLAY_BRIGHTNESS)) {
// Emulator does not support remote brightness control, b/139959479
// do not send it down so that it does not bring unnecessary property change event
// return other error code, such NOT_AVAILABLE, causes Emulator to be freezing
// TODO: return StatusCode::NOT_AVAILABLE once the above issue is fixed
return StatusCode::OK;
}
/**
* After checking all conditions, such as the property is available, a real vhal will
* sent the events to Car ECU to take actions.
*/
// Send the value to the vehicle server, the server will talk to the (real or emulated) car
auto setValueStatus = mVehicleClient->setProperty(propValue, updateStatus);
if (setValueStatus != StatusCode::OK) {
return setValueStatus;
}
return StatusCode::OK;
}
static bool isDiagnosticProperty(VehiclePropConfig propConfig) {
switch (propConfig.prop) {
case OBD2_LIVE_FRAME:
case OBD2_FREEZE_FRAME:
case OBD2_FREEZE_FRAME_CLEAR:
case OBD2_FREEZE_FRAME_INFO:
return true;
}
return false;
}
// Parse supported properties list and generate vector of property values to hold current values.
void EmulatedVehicleHal::onCreate() {
static constexpr bool shouldUpdateStatus = true;
for (auto& it : kVehicleProperties) {
VehiclePropConfig cfg = it.config;
int32_t numAreas = cfg.areaConfigs.size();
if (isDiagnosticProperty(cfg)) {
// do not write an initial empty value for the diagnostic properties
// as we will initialize those separately.
continue;
}
// A global property will have only a single area
if (isGlobalProp(cfg.prop)) {
numAreas = 1;
}
for (int i = 0; i < numAreas; i++) {
int32_t curArea;
if (isGlobalProp(cfg.prop)) {
curArea = 0;
} else {
curArea = cfg.areaConfigs[i].areaId;
}
// Create a separate instance for each individual zone
VehiclePropValue prop = {
.areaId = curArea,
.prop = cfg.prop,
};
if (it.initialAreaValues.size() > 0) {
auto valueForAreaIt = it.initialAreaValues.find(curArea);
if (valueForAreaIt != it.initialAreaValues.end()) {
prop.value = valueForAreaIt->second;
} else {
ALOGW("%s failed to get default value for prop 0x%x area 0x%x",
__func__, cfg.prop, curArea);
}
} else {
prop.value = it.initialValue;
if (mInitVhalValueOverride) {
for (auto& itOverride : mVehiclePropertiesOverride) {
if (itOverride.prop == cfg.prop) {
prop.value = itOverride.value;
}
}
}
}
mPropStore->writeValue(prop, shouldUpdateStatus);
}
}
initObd2LiveFrame(*mPropStore->getConfigOrDie(OBD2_LIVE_FRAME));
initObd2FreezeFrame(*mPropStore->getConfigOrDie(OBD2_FREEZE_FRAME));
mInEmulator = isInEmulator();
ALOGD("mInEmulator=%s", mInEmulator ? "true" : "false");
}
std::vector<VehiclePropConfig> EmulatedVehicleHal::listProperties() {
return mPropStore->getAllConfigs();
}
void EmulatedVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
VehiclePropValuePtr v;
auto& pool = *getValuePool();
for (int32_t property : properties) {
if (isContinuousProperty(property)) {
auto internalPropValue = mPropStore->readValueOrNull(property);
if (internalPropValue != nullptr) {
v = pool.obtain(*internalPropValue);
}
} else {
ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
}
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
doHalEvent(std::move(v));
}
}
}
StatusCode EmulatedVehicleHal::subscribe(int32_t property, float sampleRate) {
ALOGI("%s propId: 0x%x, sampleRate: %f", __func__, property, sampleRate);
if (isContinuousProperty(property)) {
mRecurrentTimer.registerRecurrentEvent(hertzToNanoseconds(sampleRate), property);
}
return StatusCode::OK;
}
StatusCode EmulatedVehicleHal::unsubscribe(int32_t property) {
ALOGI("%s propId: 0x%x", __func__, property);
if (isContinuousProperty(property)) {
mRecurrentTimer.unregisterRecurrentEvent(property);
}
return StatusCode::OK;
}
bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const {
const VehiclePropConfig* config = mPropStore->getConfigOrNull(propId);
if (config == nullptr) {
ALOGW("Config not found for property: 0x%x", propId);
return false;
}
return config->changeMode == VehiclePropertyChangeMode::CONTINUOUS;
}
bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) {
constexpr bool updateStatus = true;
return mVehicleClient->setProperty(propValue, updateStatus) == StatusCode::OK;
}
std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const {
return mPropStore->readAllValues();
}
void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
getEmulatorOrDie()->doSetValueFromClient(*updatedPropValue);
doHalEvent(std::move(updatedPropValue));
}
}
void EmulatedVehicleHal::initStaticConfig() {
for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) {
const auto& cfg = it->config;
VehiclePropertyStore::TokenFunction tokenFunction = nullptr;
switch (cfg.prop) {
case OBD2_FREEZE_FRAME: {
tokenFunction = [](const VehiclePropValue& propValue) {
return propValue.timestamp;
};
break;
}
default:
break;
}
mPropStore->registerProperty(cfg, tokenFunction);
}
}
void EmulatedVehicleHal::initObd2LiveFrame(const VehiclePropConfig& propConfig) {
static constexpr bool shouldUpdateStatus = true;
auto liveObd2Frame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
static_cast<size_t>(propConfig.configArray[1]));
sensorStore->fillPropValue("", liveObd2Frame.get());
liveObd2Frame->prop = OBD2_LIVE_FRAME;
mPropStore->writeValue(*liveObd2Frame, shouldUpdateStatus);
}
void EmulatedVehicleHal::initObd2FreezeFrame(const VehiclePropConfig& propConfig) {
static constexpr bool shouldUpdateStatus = true;
auto sensorStore = fillDefaultObd2Frame(static_cast<size_t>(propConfig.configArray[0]),
static_cast<size_t>(propConfig.configArray[1]));
static std::vector<std::string> sampleDtcs = {"P0070",
"P0102"
"P0123"};
for (auto&& dtc : sampleDtcs) {
auto freezeFrame = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
sensorStore->fillPropValue(dtc, freezeFrame.get());
freezeFrame->prop = OBD2_FREEZE_FRAME;
mPropStore->writeValue(*freezeFrame, shouldUpdateStatus);
}
}
StatusCode EmulatedVehicleHal::fillObd2FreezeFrame(const VehiclePropValue& requestedPropValue,
VehiclePropValue* outValue) {
if (requestedPropValue.value.int64Values.size() != 1) {
ALOGE("asked for OBD2_FREEZE_FRAME without valid timestamp");
return StatusCode::INVALID_ARG;
}
auto timestamp = requestedPropValue.value.int64Values[0];
auto freezeFrame = mPropStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
if (freezeFrame == nullptr) {
ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
return StatusCode::INVALID_ARG;
}
outValue->prop = OBD2_FREEZE_FRAME;
outValue->value.int32Values = freezeFrame->value.int32Values;
outValue->value.floatValues = freezeFrame->value.floatValues;
outValue->value.bytes = freezeFrame->value.bytes;
outValue->value.stringValue = freezeFrame->value.stringValue;
outValue->timestamp = freezeFrame->timestamp;
return StatusCode::OK;
}
StatusCode EmulatedVehicleHal::clearObd2FreezeFrames(const VehiclePropValue& propValue) {
if (propValue.value.int64Values.size() == 0) {
mPropStore->removeValuesForProperty(OBD2_FREEZE_FRAME);
return StatusCode::OK;
} else {
for (int64_t timestamp : propValue.value.int64Values) {
auto freezeFrame = mPropStore->readValueOrNull(OBD2_FREEZE_FRAME, 0, timestamp);
if (freezeFrame == nullptr) {
ALOGE("asked for OBD2_FREEZE_FRAME at invalid timestamp");
return StatusCode::INVALID_ARG;
}
mPropStore->removeValue(*freezeFrame);
}
}
return StatusCode::OK;
}
StatusCode EmulatedVehicleHal::fillObd2DtcInfo(VehiclePropValue* outValue) {
std::vector<int64_t> timestamps;
for (const auto& freezeFrame : mPropStore->readValuesForProperty(OBD2_FREEZE_FRAME)) {
timestamps.push_back(freezeFrame.timestamp);
}
outValue->value.int64Values = timestamps;
outValue->prop = OBD2_FREEZE_FRAME_INFO;
return StatusCode::OK;
}
} // impl
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android