Loading automotive/vehicle/2.0/default/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ cc_library_static { defaults: ["vhal_v2_0_defaults"], srcs: [ "impl/vhal_v2_0/CommConn.cpp", "impl/vhal_v2_0/EmulatedVehicleConnector.cpp", "impl/vhal_v2_0/EmulatedVehicleHal.cpp", "impl/vhal_v2_0/VehicleEmulator.cpp", "impl/vhal_v2_0/PipeComm.cpp", Loading automotive/vehicle/2.0/default/VehicleService.cpp +5 −2 Original line number Diff line number Diff line Loading @@ -20,8 +20,9 @@ #include <iostream> #include <vhal_v2_0/VehicleHalManager.h> #include <vhal_v2_0/EmulatedVehicleConnector.h> #include <vhal_v2_0/EmulatedVehicleHal.h> #include <vhal_v2_0/VehicleHalManager.h> using namespace android; using namespace android::hardware; Loading @@ -29,9 +30,11 @@ using namespace android::hardware::automotive::vehicle::V2_0; int main(int /* argc */, char* /* argv */ []) { auto store = std::make_unique<VehiclePropertyStore>(); auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get()); auto connector = impl::makeEmulatedPassthroughConnector(); auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get()); auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get()); auto service = std::make_unique<VehicleHalManager>(hal.get()); connector->setValuePool(hal->getValuePool()); configureRpcThreadpool(4, true /* callerWillJoin */); Loading automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp 0 → 100644 +254 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 <android-base/logging.h> #include <utils/SystemClock.h> #include "DefaultConfig.h" #include "EmulatedVehicleConnector.h" #include "JsonFakeValueGenerator.h" #include "LinearFakeValueGenerator.h" #include "Obd2SensorStore.h" namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace V2_0 { namespace impl { void EmulatedVehicleClient::onPropertyValue(const VehiclePropValue& value) { if (!mPropCallback) { LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!"; return; } return mPropCallback(value); } void EmulatedVehicleClient::registerPropertyValueCallback(PropertyCallBackType&& callback) { if (mPropCallback) { LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!"; return; } mPropCallback = std::move(callback); } GeneratorHub* EmulatedVehicleServer::getGenerator() { return &mGeneratorHub; } VehiclePropValuePool* EmulatedVehicleServer::getValuePool() const { if (!mValuePool) { LOG(WARNING) << __func__ << ": Value pool not set!"; } return mValuePool; } void EmulatedVehicleServer::setValuePool(VehiclePropValuePool* valuePool) { if (!valuePool) { LOG(WARNING) << __func__ << ": Setting value pool to nullptr!"; } mValuePool = valuePool; } void EmulatedVehicleServer::onFakeValueGenerated(const VehiclePropValue& value) { LOG(DEBUG) << __func__ << ": " << toString(value); auto updatedPropValue = getValuePool()->obtain(value); if (updatedPropValue) { updatedPropValue->timestamp = value.timestamp; updatedPropValue->status = VehiclePropertyStatus::AVAILABLE; onPropertyValueFromCar(*updatedPropValue); } } std::vector<VehiclePropConfig> EmulatedVehicleServer::onGetAllPropertyConfig() const { std::vector<VehiclePropConfig> vehiclePropConfigs; constexpr size_t numOfVehiclePropConfigs = sizeof(kVehicleProperties) / sizeof(kVehicleProperties[0]); vehiclePropConfigs.reserve(numOfVehiclePropConfigs); for (auto& it : kVehicleProperties) { vehiclePropConfigs.emplace_back(it.config); } return vehiclePropConfigs; } StatusCode EmulatedVehicleServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) { LOG(INFO) << __func__; const auto& v = request.value; if (!v.int32Values.size()) { LOG(ERROR) << __func__ << ": expected at least \"command\" field in int32Values"; return StatusCode::INVALID_ARG; } FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]); switch (command) { case FakeDataCommand::StartLinear: { LOG(INFO) << __func__ << ", FakeDataCommand::StartLinear"; if (v.int32Values.size() < 2) { LOG(ERROR) << __func__ << ": expected property ID in int32Values"; return StatusCode::INVALID_ARG; } if (!v.int64Values.size()) { LOG(ERROR) << __func__ << ": interval is not provided in int64Values"; return StatusCode::INVALID_ARG; } if (v.floatValues.size() < 3) { LOG(ERROR) << __func__ << ": expected at least 3 elements in floatValues, got: " << v.floatValues.size(); return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; getGenerator()->registerGenerator(cookie, std::make_unique<LinearFakeValueGenerator>(request)); break; } case FakeDataCommand::StartJson: { LOG(INFO) << __func__ << ", FakeDataCommand::StartJson"; if (v.stringValue.empty()) { LOG(ERROR) << __func__ << ": path to JSON file is missing"; return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); getGenerator()->registerGenerator(cookie, std::make_unique<JsonFakeValueGenerator>(request)); break; } case FakeDataCommand::StopLinear: { LOG(INFO) << __func__ << ", FakeDataCommand::StopLinear"; if (v.int32Values.size() < 2) { LOG(ERROR) << __func__ << ": expected property ID in int32Values"; return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; getGenerator()->unregisterGenerator(cookie); break; } case FakeDataCommand::StopJson: { LOG(INFO) << __func__ << ", FakeDataCommand::StopJson"; if (v.stringValue.empty()) { LOG(ERROR) << __func__ << ": path to JSON file is missing"; return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); getGenerator()->unregisterGenerator(cookie); break; } case FakeDataCommand::KeyPress: { LOG(INFO) << __func__ << ", FakeDataCommand::KeyPress"; int32_t keyCode = request.value.int32Values[2]; int32_t display = request.value.int32Values[3]; // Send back to HAL onPropertyValueFromCar( *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); onPropertyValueFromCar( *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); break; } default: { LOG(ERROR) << __func__ << ": unexpected command: " << toInt(command); return StatusCode::INVALID_ARG; } } return StatusCode::OK; } VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createApPowerStateReq( VehicleApPowerStateReq state, int32_t param) { auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2); req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); req->areaId = 0; req->timestamp = elapsedRealtimeNano(); req->status = VehiclePropertyStatus::AVAILABLE; req->value.int32Values[0] = toInt(state); req->value.int32Values[1] = param; return req; } VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createHwInputKeyProp( VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) { auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3); keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT); keyEvent->areaId = 0; keyEvent->timestamp = elapsedRealtimeNano(); keyEvent->status = VehiclePropertyStatus::AVAILABLE; keyEvent->value.int32Values[0] = toInt(action); keyEvent->value.int32Values[1] = keyCode; keyEvent->value.int32Values[2] = targetDisplay; return keyEvent; } StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value) { // Some properties need to be treated non-trivially switch (value.prop) { case AP_POWER_STATE_REPORT: switch (value.value.int32Values[0]) { case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT): case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED): case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL): // CPMS is in WAIT_FOR_VHAL state, simply move to ON // Send back to HAL onPropertyValueFromCar( *createApPowerStateReq(VehicleApPowerStateReq::ON, 0)); break; case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY): case toInt(VehicleApPowerStateReport::SHUTDOWN_START): // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command // Send back to HAL onPropertyValueFromCar( *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0)); break; case toInt(VehicleApPowerStateReport::ON): case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE): case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE): // Do nothing break; default: // Unknown state break; } break; default: break; } // In the real vhal, the value will be sent to Car ECU. // We just pretend it is done here. return StatusCode::OK; } StatusCode EmulatedVehicleServer::onSetPropertyFromVehicle(const VehiclePropValue& value) { if (value.prop == kGenerateFakeDataControllingProperty) { auto status = handleGenerateFakeDataRequest(value); return status; } else { // Send back to HAL onPropertyValueFromCar(value); return StatusCode::OK; } } EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() { return std::make_unique<EmulatedPassthroughConnector>(); } } // namespace impl } // namespace V2_0 } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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. */ #ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ #define android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ #include <vhal_v2_0/VehicleConnector.h> #include <vhal_v2_0/VehicleHal.h> #include "GeneratorHub.h" namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace V2_0 { namespace impl { // Extension of the client/server interfaces for emulated vehicle class EmulatedVehicleClient : public IVehicleClient { public: // Type of callback function for handling the new property values using PropertyCallBackType = std::function<void(const VehiclePropValue&)>; // Method from IVehicleClient void onPropertyValue(const VehiclePropValue& value) override; // Request to change the value on the VEHICLE side (for testing) virtual StatusCode setPropertyFromVehicle(const VehiclePropValue& value) = 0; void registerPropertyValueCallback(PropertyCallBackType&& callback); private: PropertyCallBackType mPropCallback; }; class EmulatedVehicleServer : public IVehicleServer { public: // Methods from IVehicleServer std::vector<VehiclePropConfig> onGetAllPropertyConfig() const override; StatusCode onSetProperty(const VehiclePropValue& value) override; // Process the request to change the value on the VEHICLE side (for testing) StatusCode onSetPropertyFromVehicle(const VehiclePropValue& value); // Set the Property Value Pool used in this server void setValuePool(VehiclePropValuePool* valuePool); private: GeneratorHub* getGenerator(); VehiclePropValuePool* getValuePool() const; void onFakeValueGenerated(const VehiclePropValue& value); StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request); VehicleHal::VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param); VehicleHal::VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay); // private data members GeneratorHub mGeneratorHub{ std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)}; VehiclePropValuePool* mValuePool{nullptr}; }; class EmulatedPassthroughConnector : public IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer> { public: StatusCode setPropertyFromVehicle(const VehiclePropValue& value) override { return this->onSetPropertyFromVehicle(value); } }; // Helper functions using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>; EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector(); } // namespace impl } // namespace V2_0 } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android #endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp +20 −159 Original line number Diff line number Diff line Loading @@ -87,17 +87,19 @@ static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorInt return sensorStore; } EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore) EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, EmulatedVehicleClient* client) : mPropStore(propStore), mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)), mRecurrentTimer( std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)), mGeneratorHub( std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1)) { mVehicleClient(client) { 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)); } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get( Loading Loading @@ -162,7 +164,8 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { } if (propValue.prop == kGenerateFakeDataControllingProperty) { StatusCode status = handleGenerateFakeDataRequest(propValue); // send the generator controlling request to the server auto status = mVehicleClient->setPropertyFromVehicle(propValue); if (status != StatusCode::OK) { return status; } Loading @@ -186,29 +189,6 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { // 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; case AP_POWER_STATE_REPORT: switch (propValue.value.int32Values[0]) { case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT): case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED): case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL): // CPMS is in WAIT_FOR_VHAL state, simply move to ON doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::ON, 0)); break; case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY): case toInt(VehicleApPowerStateReport::SHUTDOWN_START): // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0)); break; case toInt(VehicleApPowerStateReport::ON): case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE): case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE): // Do nothing break; default: // Unknown state break; } break; } } Loading @@ -231,10 +211,16 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { /** * After checking all conditions, such as the property is available, a real vhal will * sent the events to Car ECU to take actions. * Google HAL will just add a timestamp for the value and triggle the callback to android. */ VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(propValue); updatedPropValue->timestamp = elapsedRealtimeNano(); // Send the value to the vehicle server, the server will talk to the (real or emulated) car auto setValueStatus = mVehicleClient->setProperty(*updatedPropValue); if (setValueStatus != StatusCode::OK) { return setValueStatus; } if (!mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) { return StatusCode::INTERNAL_ERROR; } Loading Loading @@ -361,146 +347,21 @@ bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const { } bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) { static constexpr bool shouldUpdateStatus = true; if (propValue.prop == kGenerateFakeDataControllingProperty) { StatusCode status = handleGenerateFakeDataRequest(propValue); if (status != StatusCode::OK) { return false; } } if (mPropStore->writeValue(propValue, shouldUpdateStatus)) { doHalEvent(getValuePool()->obtain(propValue)); return true; } else { return false; } return mVehicleClient->setPropertyFromVehicle(propValue) == StatusCode::OK; } std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const { return mPropStore->readAllValues(); } StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) { ALOGI("%s", __func__); const auto& v = request.value; if (!v.int32Values.size()) { ALOGE("%s: expected at least \"command\" field in int32Values", __func__); return StatusCode::INVALID_ARG; } FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]); switch (command) { case FakeDataCommand::StartLinear: { ALOGI("%s, FakeDataCommand::StartLinear", __func__); if (v.int32Values.size() < 2) { ALOGE("%s: expected property ID in int32Values", __func__); return StatusCode::INVALID_ARG; } if (!v.int64Values.size()) { ALOGE("%s: interval is not provided in int64Values", __func__); return StatusCode::INVALID_ARG; } if (v.floatValues.size() < 3) { ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__, v.floatValues.size()); return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; mGeneratorHub.registerGenerator(cookie, std::make_unique<LinearFakeValueGenerator>(request)); break; } case FakeDataCommand::StartJson: { ALOGI("%s, FakeDataCommand::StartJson", __func__); if (v.stringValue.empty()) { ALOGE("%s: path to JSON file is missing", __func__); return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); mGeneratorHub.registerGenerator(cookie, std::make_unique<JsonFakeValueGenerator>(request)); break; } case FakeDataCommand::StopLinear: { ALOGI("%s, FakeDataCommand::StopLinear", __func__); if (v.int32Values.size() < 2) { ALOGE("%s: expected property ID in int32Values", __func__); return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; mGeneratorHub.unregisterGenerator(cookie); break; } case FakeDataCommand::StopJson: { ALOGI("%s, FakeDataCommand::StopJson", __func__); if (v.stringValue.empty()) { ALOGE("%s: path to JSON file is missing", __func__); return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); mGeneratorHub.unregisterGenerator(cookie); break; } case FakeDataCommand::KeyPress: { ALOGI("%s, FakeDataCommand::KeyPress", __func__); int32_t keyCode = request.value.int32Values[2]; int32_t display = request.value.int32Values[3]; doHalEvent( createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); doHalEvent(createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); break; } default: { ALOGE("%s: unexpected command: %d", __func__, command); return StatusCode::INVALID_ARG; } } return StatusCode::OK; } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createApPowerStateReq( VehicleApPowerStateReq state, int32_t param) { auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2); req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); req->areaId = 0; req->timestamp = elapsedRealtimeNano(); req->status = VehiclePropertyStatus::AVAILABLE; req->value.int32Values[0] = toInt(state); req->value.int32Values[1] = param; return req; } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createHwInputKeyProp( VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) { auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3); keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT); keyEvent->areaId = 0; keyEvent->timestamp = elapsedRealtimeNano(); keyEvent->status = VehiclePropertyStatus::AVAILABLE; keyEvent->value.int32Values[0] = toInt(action); keyEvent->value.int32Values[1] = keyCode; keyEvent->value.int32Values[2] = targetDisplay; return keyEvent; } void EmulatedVehicleHal::onFakeValueGenerated(const VehiclePropValue& value) { ALOGD("%s: %s", __func__, toString(value).c_str()); static constexpr bool shouldUpdateStatus = false; void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value) { static constexpr bool shouldUpdateStatus = true; VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value); if (updatedPropValue) { updatedPropValue->timestamp = value.timestamp; updatedPropValue->status = VehiclePropertyStatus::AVAILABLE; mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus); auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode; if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) { if (mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) { doHalEvent(std::move(updatedPropValue)); } } } void EmulatedVehicleHal::initStaticConfig() { for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) { Loading Loading
automotive/vehicle/2.0/default/Android.bp +1 −0 Original line number Diff line number Diff line Loading @@ -58,6 +58,7 @@ cc_library_static { defaults: ["vhal_v2_0_defaults"], srcs: [ "impl/vhal_v2_0/CommConn.cpp", "impl/vhal_v2_0/EmulatedVehicleConnector.cpp", "impl/vhal_v2_0/EmulatedVehicleHal.cpp", "impl/vhal_v2_0/VehicleEmulator.cpp", "impl/vhal_v2_0/PipeComm.cpp", Loading
automotive/vehicle/2.0/default/VehicleService.cpp +5 −2 Original line number Diff line number Diff line Loading @@ -20,8 +20,9 @@ #include <iostream> #include <vhal_v2_0/VehicleHalManager.h> #include <vhal_v2_0/EmulatedVehicleConnector.h> #include <vhal_v2_0/EmulatedVehicleHal.h> #include <vhal_v2_0/VehicleHalManager.h> using namespace android; using namespace android::hardware; Loading @@ -29,9 +30,11 @@ using namespace android::hardware::automotive::vehicle::V2_0; int main(int /* argc */, char* /* argv */ []) { auto store = std::make_unique<VehiclePropertyStore>(); auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get()); auto connector = impl::makeEmulatedPassthroughConnector(); auto hal = std::make_unique<impl::EmulatedVehicleHal>(store.get(), connector.get()); auto emulator = std::make_unique<impl::VehicleEmulator>(hal.get()); auto service = std::make_unique<VehicleHalManager>(hal.get()); connector->setValuePool(hal->getValuePool()); configureRpcThreadpool(4, true /* callerWillJoin */); Loading
automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.cpp 0 → 100644 +254 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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 <android-base/logging.h> #include <utils/SystemClock.h> #include "DefaultConfig.h" #include "EmulatedVehicleConnector.h" #include "JsonFakeValueGenerator.h" #include "LinearFakeValueGenerator.h" #include "Obd2SensorStore.h" namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace V2_0 { namespace impl { void EmulatedVehicleClient::onPropertyValue(const VehiclePropValue& value) { if (!mPropCallback) { LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!"; return; } return mPropCallback(value); } void EmulatedVehicleClient::registerPropertyValueCallback(PropertyCallBackType&& callback) { if (mPropCallback) { LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!"; return; } mPropCallback = std::move(callback); } GeneratorHub* EmulatedVehicleServer::getGenerator() { return &mGeneratorHub; } VehiclePropValuePool* EmulatedVehicleServer::getValuePool() const { if (!mValuePool) { LOG(WARNING) << __func__ << ": Value pool not set!"; } return mValuePool; } void EmulatedVehicleServer::setValuePool(VehiclePropValuePool* valuePool) { if (!valuePool) { LOG(WARNING) << __func__ << ": Setting value pool to nullptr!"; } mValuePool = valuePool; } void EmulatedVehicleServer::onFakeValueGenerated(const VehiclePropValue& value) { LOG(DEBUG) << __func__ << ": " << toString(value); auto updatedPropValue = getValuePool()->obtain(value); if (updatedPropValue) { updatedPropValue->timestamp = value.timestamp; updatedPropValue->status = VehiclePropertyStatus::AVAILABLE; onPropertyValueFromCar(*updatedPropValue); } } std::vector<VehiclePropConfig> EmulatedVehicleServer::onGetAllPropertyConfig() const { std::vector<VehiclePropConfig> vehiclePropConfigs; constexpr size_t numOfVehiclePropConfigs = sizeof(kVehicleProperties) / sizeof(kVehicleProperties[0]); vehiclePropConfigs.reserve(numOfVehiclePropConfigs); for (auto& it : kVehicleProperties) { vehiclePropConfigs.emplace_back(it.config); } return vehiclePropConfigs; } StatusCode EmulatedVehicleServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) { LOG(INFO) << __func__; const auto& v = request.value; if (!v.int32Values.size()) { LOG(ERROR) << __func__ << ": expected at least \"command\" field in int32Values"; return StatusCode::INVALID_ARG; } FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]); switch (command) { case FakeDataCommand::StartLinear: { LOG(INFO) << __func__ << ", FakeDataCommand::StartLinear"; if (v.int32Values.size() < 2) { LOG(ERROR) << __func__ << ": expected property ID in int32Values"; return StatusCode::INVALID_ARG; } if (!v.int64Values.size()) { LOG(ERROR) << __func__ << ": interval is not provided in int64Values"; return StatusCode::INVALID_ARG; } if (v.floatValues.size() < 3) { LOG(ERROR) << __func__ << ": expected at least 3 elements in floatValues, got: " << v.floatValues.size(); return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; getGenerator()->registerGenerator(cookie, std::make_unique<LinearFakeValueGenerator>(request)); break; } case FakeDataCommand::StartJson: { LOG(INFO) << __func__ << ", FakeDataCommand::StartJson"; if (v.stringValue.empty()) { LOG(ERROR) << __func__ << ": path to JSON file is missing"; return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); getGenerator()->registerGenerator(cookie, std::make_unique<JsonFakeValueGenerator>(request)); break; } case FakeDataCommand::StopLinear: { LOG(INFO) << __func__ << ", FakeDataCommand::StopLinear"; if (v.int32Values.size() < 2) { LOG(ERROR) << __func__ << ": expected property ID in int32Values"; return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; getGenerator()->unregisterGenerator(cookie); break; } case FakeDataCommand::StopJson: { LOG(INFO) << __func__ << ", FakeDataCommand::StopJson"; if (v.stringValue.empty()) { LOG(ERROR) << __func__ << ": path to JSON file is missing"; return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); getGenerator()->unregisterGenerator(cookie); break; } case FakeDataCommand::KeyPress: { LOG(INFO) << __func__ << ", FakeDataCommand::KeyPress"; int32_t keyCode = request.value.int32Values[2]; int32_t display = request.value.int32Values[3]; // Send back to HAL onPropertyValueFromCar( *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); onPropertyValueFromCar( *createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); break; } default: { LOG(ERROR) << __func__ << ": unexpected command: " << toInt(command); return StatusCode::INVALID_ARG; } } return StatusCode::OK; } VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createApPowerStateReq( VehicleApPowerStateReq state, int32_t param) { auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2); req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); req->areaId = 0; req->timestamp = elapsedRealtimeNano(); req->status = VehiclePropertyStatus::AVAILABLE; req->value.int32Values[0] = toInt(state); req->value.int32Values[1] = param; return req; } VehicleHal::VehiclePropValuePtr EmulatedVehicleServer::createHwInputKeyProp( VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) { auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3); keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT); keyEvent->areaId = 0; keyEvent->timestamp = elapsedRealtimeNano(); keyEvent->status = VehiclePropertyStatus::AVAILABLE; keyEvent->value.int32Values[0] = toInt(action); keyEvent->value.int32Values[1] = keyCode; keyEvent->value.int32Values[2] = targetDisplay; return keyEvent; } StatusCode EmulatedVehicleServer::onSetProperty(const VehiclePropValue& value) { // Some properties need to be treated non-trivially switch (value.prop) { case AP_POWER_STATE_REPORT: switch (value.value.int32Values[0]) { case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT): case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED): case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL): // CPMS is in WAIT_FOR_VHAL state, simply move to ON // Send back to HAL onPropertyValueFromCar( *createApPowerStateReq(VehicleApPowerStateReq::ON, 0)); break; case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY): case toInt(VehicleApPowerStateReport::SHUTDOWN_START): // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command // Send back to HAL onPropertyValueFromCar( *createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0)); break; case toInt(VehicleApPowerStateReport::ON): case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE): case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE): // Do nothing break; default: // Unknown state break; } break; default: break; } // In the real vhal, the value will be sent to Car ECU. // We just pretend it is done here. return StatusCode::OK; } StatusCode EmulatedVehicleServer::onSetPropertyFromVehicle(const VehiclePropValue& value) { if (value.prop == kGenerateFakeDataControllingProperty) { auto status = handleGenerateFakeDataRequest(value); return status; } else { // Send back to HAL onPropertyValueFromCar(value); return StatusCode::OK; } } EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() { return std::make_unique<EmulatedPassthroughConnector>(); } } // namespace impl } // namespace V2_0 } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android
automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleConnector.h 0 → 100644 +110 −0 Original line number Diff line number Diff line /* * Copyright (C) 2019 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. */ #ifndef android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ #define android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_ #include <vhal_v2_0/VehicleConnector.h> #include <vhal_v2_0/VehicleHal.h> #include "GeneratorHub.h" namespace android { namespace hardware { namespace automotive { namespace vehicle { namespace V2_0 { namespace impl { // Extension of the client/server interfaces for emulated vehicle class EmulatedVehicleClient : public IVehicleClient { public: // Type of callback function for handling the new property values using PropertyCallBackType = std::function<void(const VehiclePropValue&)>; // Method from IVehicleClient void onPropertyValue(const VehiclePropValue& value) override; // Request to change the value on the VEHICLE side (for testing) virtual StatusCode setPropertyFromVehicle(const VehiclePropValue& value) = 0; void registerPropertyValueCallback(PropertyCallBackType&& callback); private: PropertyCallBackType mPropCallback; }; class EmulatedVehicleServer : public IVehicleServer { public: // Methods from IVehicleServer std::vector<VehiclePropConfig> onGetAllPropertyConfig() const override; StatusCode onSetProperty(const VehiclePropValue& value) override; // Process the request to change the value on the VEHICLE side (for testing) StatusCode onSetPropertyFromVehicle(const VehiclePropValue& value); // Set the Property Value Pool used in this server void setValuePool(VehiclePropValuePool* valuePool); private: GeneratorHub* getGenerator(); VehiclePropValuePool* getValuePool() const; void onFakeValueGenerated(const VehiclePropValue& value); StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request); VehicleHal::VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param); VehicleHal::VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay); // private data members GeneratorHub mGeneratorHub{ std::bind(&EmulatedVehicleServer::onFakeValueGenerated, this, std::placeholders::_1)}; VehiclePropValuePool* mValuePool{nullptr}; }; class EmulatedPassthroughConnector : public IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer> { public: StatusCode setPropertyFromVehicle(const VehiclePropValue& value) override { return this->onSetPropertyFromVehicle(value); } }; // Helper functions using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>; EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector(); } // namespace impl } // namespace V2_0 } // namespace vehicle } // namespace automotive } // namespace hardware } // namespace android #endif // android_hardware_automotive_vehicle_V2_0_impl_EmulatedVehicleConnector_H_
automotive/vehicle/2.0/default/impl/vhal_v2_0/EmulatedVehicleHal.cpp +20 −159 Original line number Diff line number Diff line Loading @@ -87,17 +87,19 @@ static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorInt return sensorStore; } EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore) EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, EmulatedVehicleClient* client) : mPropStore(propStore), mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)), mRecurrentTimer( std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)), mGeneratorHub( std::bind(&EmulatedVehicleHal::onFakeValueGenerated, this, std::placeholders::_1)) { mVehicleClient(client) { 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)); } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::get( Loading Loading @@ -162,7 +164,8 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { } if (propValue.prop == kGenerateFakeDataControllingProperty) { StatusCode status = handleGenerateFakeDataRequest(propValue); // send the generator controlling request to the server auto status = mVehicleClient->setPropertyFromVehicle(propValue); if (status != StatusCode::OK) { return status; } Loading @@ -186,29 +189,6 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { // 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; case AP_POWER_STATE_REPORT: switch (propValue.value.int32Values[0]) { case toInt(VehicleApPowerStateReport::DEEP_SLEEP_EXIT): case toInt(VehicleApPowerStateReport::SHUTDOWN_CANCELLED): case toInt(VehicleApPowerStateReport::WAIT_FOR_VHAL): // CPMS is in WAIT_FOR_VHAL state, simply move to ON doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::ON, 0)); break; case toInt(VehicleApPowerStateReport::DEEP_SLEEP_ENTRY): case toInt(VehicleApPowerStateReport::SHUTDOWN_START): // CPMS is in WAIT_FOR_FINISH state, send the FINISHED command doHalEvent(createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0)); break; case toInt(VehicleApPowerStateReport::ON): case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE): case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE): // Do nothing break; default: // Unknown state break; } break; } } Loading @@ -231,10 +211,16 @@ StatusCode EmulatedVehicleHal::set(const VehiclePropValue& propValue) { /** * After checking all conditions, such as the property is available, a real vhal will * sent the events to Car ECU to take actions. * Google HAL will just add a timestamp for the value and triggle the callback to android. */ VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(propValue); updatedPropValue->timestamp = elapsedRealtimeNano(); // Send the value to the vehicle server, the server will talk to the (real or emulated) car auto setValueStatus = mVehicleClient->setProperty(*updatedPropValue); if (setValueStatus != StatusCode::OK) { return setValueStatus; } if (!mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) { return StatusCode::INTERNAL_ERROR; } Loading Loading @@ -361,146 +347,21 @@ bool EmulatedVehicleHal::isContinuousProperty(int32_t propId) const { } bool EmulatedVehicleHal::setPropertyFromVehicle(const VehiclePropValue& propValue) { static constexpr bool shouldUpdateStatus = true; if (propValue.prop == kGenerateFakeDataControllingProperty) { StatusCode status = handleGenerateFakeDataRequest(propValue); if (status != StatusCode::OK) { return false; } } if (mPropStore->writeValue(propValue, shouldUpdateStatus)) { doHalEvent(getValuePool()->obtain(propValue)); return true; } else { return false; } return mVehicleClient->setPropertyFromVehicle(propValue) == StatusCode::OK; } std::vector<VehiclePropValue> EmulatedVehicleHal::getAllProperties() const { return mPropStore->readAllValues(); } StatusCode EmulatedVehicleHal::handleGenerateFakeDataRequest(const VehiclePropValue& request) { ALOGI("%s", __func__); const auto& v = request.value; if (!v.int32Values.size()) { ALOGE("%s: expected at least \"command\" field in int32Values", __func__); return StatusCode::INVALID_ARG; } FakeDataCommand command = static_cast<FakeDataCommand>(v.int32Values[0]); switch (command) { case FakeDataCommand::StartLinear: { ALOGI("%s, FakeDataCommand::StartLinear", __func__); if (v.int32Values.size() < 2) { ALOGE("%s: expected property ID in int32Values", __func__); return StatusCode::INVALID_ARG; } if (!v.int64Values.size()) { ALOGE("%s: interval is not provided in int64Values", __func__); return StatusCode::INVALID_ARG; } if (v.floatValues.size() < 3) { ALOGE("%s: expected at least 3 elements in floatValues, got: %zu", __func__, v.floatValues.size()); return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; mGeneratorHub.registerGenerator(cookie, std::make_unique<LinearFakeValueGenerator>(request)); break; } case FakeDataCommand::StartJson: { ALOGI("%s, FakeDataCommand::StartJson", __func__); if (v.stringValue.empty()) { ALOGE("%s: path to JSON file is missing", __func__); return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); mGeneratorHub.registerGenerator(cookie, std::make_unique<JsonFakeValueGenerator>(request)); break; } case FakeDataCommand::StopLinear: { ALOGI("%s, FakeDataCommand::StopLinear", __func__); if (v.int32Values.size() < 2) { ALOGE("%s: expected property ID in int32Values", __func__); return StatusCode::INVALID_ARG; } int32_t cookie = v.int32Values[1]; mGeneratorHub.unregisterGenerator(cookie); break; } case FakeDataCommand::StopJson: { ALOGI("%s, FakeDataCommand::StopJson", __func__); if (v.stringValue.empty()) { ALOGE("%s: path to JSON file is missing", __func__); return StatusCode::INVALID_ARG; } int32_t cookie = std::hash<std::string>()(v.stringValue); mGeneratorHub.unregisterGenerator(cookie); break; } case FakeDataCommand::KeyPress: { ALOGI("%s, FakeDataCommand::KeyPress", __func__); int32_t keyCode = request.value.int32Values[2]; int32_t display = request.value.int32Values[3]; doHalEvent( createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_DOWN, keyCode, display)); doHalEvent(createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display)); break; } default: { ALOGE("%s: unexpected command: %d", __func__, command); return StatusCode::INVALID_ARG; } } return StatusCode::OK; } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createApPowerStateReq( VehicleApPowerStateReq state, int32_t param) { auto req = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 2); req->prop = toInt(VehicleProperty::AP_POWER_STATE_REQ); req->areaId = 0; req->timestamp = elapsedRealtimeNano(); req->status = VehiclePropertyStatus::AVAILABLE; req->value.int32Values[0] = toInt(state); req->value.int32Values[1] = param; return req; } VehicleHal::VehiclePropValuePtr EmulatedVehicleHal::createHwInputKeyProp( VehicleHwKeyInputAction action, int32_t keyCode, int32_t targetDisplay) { auto keyEvent = getValuePool()->obtain(VehiclePropertyType::INT32_VEC, 3); keyEvent->prop = toInt(VehicleProperty::HW_KEY_INPUT); keyEvent->areaId = 0; keyEvent->timestamp = elapsedRealtimeNano(); keyEvent->status = VehiclePropertyStatus::AVAILABLE; keyEvent->value.int32Values[0] = toInt(action); keyEvent->value.int32Values[1] = keyCode; keyEvent->value.int32Values[2] = targetDisplay; return keyEvent; } void EmulatedVehicleHal::onFakeValueGenerated(const VehiclePropValue& value) { ALOGD("%s: %s", __func__, toString(value).c_str()); static constexpr bool shouldUpdateStatus = false; void EmulatedVehicleHal::onPropertyValue(const VehiclePropValue& value) { static constexpr bool shouldUpdateStatus = true; VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value); if (updatedPropValue) { updatedPropValue->timestamp = value.timestamp; updatedPropValue->status = VehiclePropertyStatus::AVAILABLE; mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus); auto changeMode = mPropStore->getConfigOrDie(value.prop)->changeMode; if (VehiclePropertyChangeMode::ON_CHANGE == changeMode) { if (mPropStore->writeValue(*updatedPropValue, shouldUpdateStatus)) { doHalEvent(std::move(updatedPropValue)); } } } void EmulatedVehicleHal::initStaticConfig() { for (auto&& it = std::begin(kVehicleProperties); it != std::end(kVehicleProperties); ++it) { Loading