Split client and server impl

Some of the code in VHAL client implementation contains Android-specific
code, and some of the server operations only works in the native case.
So we split them up so that the AGL VHAL server can selectivly pick the
parts it needs.

It won't change the logic of native VHAL.

Bug: 148877226

Test: Build

Change-Id: Ie142b19a5c435a0b4252ffd297504bde69eb44b0
This commit is contained in:
Hao Chen 2020-02-14 11:51:26 -08:00
parent 8dfac92fee
commit 7e9e37fa0a
9 changed files with 524 additions and 410 deletions

View file

@ -63,6 +63,8 @@ cc_library_static {
"impl/vhal_v2_0/CommConn.cpp",
"impl/vhal_v2_0/EmulatedVehicleConnector.cpp",
"impl/vhal_v2_0/EmulatedVehicleHal.cpp",
"impl/vhal_v2_0/VehicleHalClient.cpp",
"impl/vhal_v2_0/VehicleHalServer.cpp",
"impl/vhal_v2_0/VehicleEmulator.cpp",
"impl/vhal_v2_0/PipeComm.cpp",
"impl/vhal_v2_0/ProtoMessageConverter.cpp",

View file

@ -35,346 +35,16 @@ namespace V2_0 {
namespace impl {
void EmulatedVehicleClient::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
if (!mPropCallback) {
LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!";
return;
}
return mPropCallback(value, updateStatus);
}
class EmulatedPassthroughConnector : public PassthroughConnector {
public:
bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
void EmulatedVehicleClient::registerPropertyValueCallback(PropertyCallBackType&& callback) {
if (mPropCallback) {
LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!";
return;
}
mPropCallback = std::move(callback);
}
private:
void dumpUserHal(int fd, std::string indent);
};
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) {
constexpr bool updateStatus = true;
LOG(DEBUG) << __func__ << ": " << toString(value);
auto updatedPropValue = getValuePool()->obtain(value);
if (updatedPropValue) {
updatedPropValue->timestamp = value.timestamp;
updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
}
}
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) {
constexpr bool updateStatus = true;
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),
updateStatus);
onPropertyValueFromCar(
*createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display),
updateStatus);
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, bool updateStatus) {
// Some properties need to be treated non-trivially
switch (value.prop) {
case kGenerateFakeDataControllingProperty:
return handleGenerateFakeDataRequest(value);
// set the value from vehicle side, used in end to end test.
case kSetIntPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::INT32, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.int32Values[0] = value.value.int32Values[1];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case kSetFloatPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.floatValues[0] = value.value.floatValues[0];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case kSetBooleanPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1);
updatedPropValue->prop = value.value.int32Values[1];
updatedPropValue->value.int32Values[0] = value.value.int32Values[0];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
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
// ALWAYS update status for generated property value
onPropertyValueFromCar(*createApPowerStateReq(VehicleApPowerStateReq::ON, 0),
true /* updateStatus */);
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
// ALWAYS update status for generated property value
onPropertyValueFromCar(
*createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0),
true /* updateStatus */);
break;
case toInt(VehicleApPowerStateReport::ON):
case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
// Do nothing
break;
default:
// Unknown state
break;
}
break;
case INITIAL_USER_INFO:
return onSetInitialUserInfo(value, updateStatus);
default:
break;
}
// In the real vhal, the value will be sent to Car ECU.
// We just pretend it is done here and send back to HAL
auto updatedPropValue = getValuePool()->obtain(value);
updatedPropValue->timestamp = elapsedRealtimeNano();
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
/**
* INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
* indicating what the initial user should be.
*
* During normal circumstances, the emulator will reply right away, passing a response if
* InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which user
* to boot).
*
* But during development / testing, the behavior can be changed using lshal dump, which must use
* the areaId to indicate what should happen next.
*
* So, the behavior of set(INITIAL_USER_INFO) is:
*
* - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called by
* lshal).
* - else if mInitialUserResponseFromCmd is not set, return a response with the same request id and
* InitialUserInfoResponseAction::DEFAULT
* - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
* - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
* - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can test
* this error scenario)
* - if it's 3, then don't send a property change (so Android can emulate a timeout)
*
*/
StatusCode EmulatedVehicleServer::onSetInitialUserInfo(const VehiclePropValue& value,
bool updateStatus) {
// TODO: LOG calls below might be more suited to be DEBUG, but those are not being logged
// (even when explicitly calling setprop log.tag. As this class should be using ALOG instead of
// LOG, it's not worth investigating why...
if (value.value.int32Values.size() == 0) {
LOG(ERROR) << "set(INITIAL_USER_INFO): no int32values, ignoring it: " << toString(value);
return StatusCode::INVALID_ARG;
}
if (value.areaId != 0) {
LOG(INFO) << "set(INITIAL_USER_INFO) called from lshal; storing it: " << toString(value);
mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
return StatusCode::OK;
}
LOG(INFO) << "set(INITIAL_USER_INFO) called from Android: " << toString(value);
int32_t requestId = value.value.int32Values[0];
// Create the update property and set common values
auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
updatedValue->prop = INITIAL_USER_INFO;
updatedValue->timestamp = elapsedRealtimeNano();
if (mInitialUserResponseFromCmd == nullptr) {
updatedValue->value.int32Values.resize(2);
updatedValue->value.int32Values[0] = requestId;
updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
LOG(INFO) << "no lshal response; returning InitialUserInfoResponseAction::DEFAULT: "
<< toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
// mInitialUserResponseFromCmd is used for just one request
std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);
// TODO(b/138709788): rather than populate the raw values directly, it should use the
// libraries that convert a InitialUserInfoResponse into a VehiclePropValue)
switch (response->areaId) {
case 1:
LOG(INFO) << "returning response with right request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = requestId;
break;
case 2:
LOG(INFO) << "returning response with wrong request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = -requestId;
break;
case 3:
LOG(INFO) << "not generating a property change event because of lshal prop: "
<< toString(*response);
return StatusCode::OK;
default:
LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
return StatusCode::INTERNAL_ERROR;
}
LOG(INFO) << "updating property to: " << toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
bool EmulatedVehicleServer::onDump(const hidl_handle& handle,
const hidl_vec<hidl_string>& options) {
bool EmulatedPassthroughConnector::onDump(const hidl_handle& handle,
const hidl_vec<hidl_string>& options) {
int fd = handle->data[0];
if (options.size() > 0) {
@ -401,7 +71,7 @@ bool EmulatedVehicleServer::onDump(const hidl_handle& handle,
return true;
}
void EmulatedVehicleServer::dumpUserHal(int fd, std::string indent) {
void EmulatedPassthroughConnector::dumpUserHal(int fd, std::string indent) {
if (mInitialUserResponseFromCmd != nullptr) {
dprintf(fd, "%sInitial User Info: %s\n", indent.c_str(),
toString(*mInitialUserResponseFromCmd).c_str());
@ -410,7 +80,7 @@ void EmulatedVehicleServer::dumpUserHal(int fd, std::string indent) {
}
}
EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector() {
PassthroughConnectorPtr makeEmulatedPassthroughConnector() {
return std::make_unique<EmulatedPassthroughConnector>();
}

View file

@ -18,9 +18,9 @@
#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"
#include "VehicleHalClient.h"
#include "VehicleHalServer.h"
namespace android {
namespace hardware {
@ -30,69 +30,10 @@ namespace V2_0 {
namespace impl {
// Extension of the client/server interfaces for emulated vehicle
using PassthroughConnector = IPassThroughConnector<VehicleHalClient, VehicleHalServer>;
using PassthroughConnectorPtr = std::unique_ptr<PassthroughConnector>;
class EmulatedVehicleClient : public IVehicleClient {
public:
// Type of callback function for handling the new property values
using PropertyCallBackType = std::function<void(const VehiclePropValue&, bool updateStatus)>;
// Method from IVehicleClient
void onPropertyValue(const VehiclePropValue& value, bool updateStatus) override;
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, bool updateStatus) override;
bool onDump(const hidl_handle& fd, const hidl_vec<hidl_string>& options) override;
// 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};
// TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus);
void dumpUserHal(int fd, std::string indent);
};
// Helper functions
using EmulatedPassthroughConnector =
IPassThroughConnector<EmulatedVehicleClient, EmulatedVehicleServer>;
using EmulatedPassthroughConnectorPtr = std::unique_ptr<EmulatedPassthroughConnector>;
EmulatedPassthroughConnectorPtr makeEmulatedPassthroughConnector();
PassthroughConnectorPtr makeEmulatedPassthroughConnector();
} // namespace impl

View file

@ -87,12 +87,11 @@ static std::unique_ptr<Obd2SensorStore> fillDefaultObd2Frame(size_t numVendorInt
return sensorStore;
}
EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore,
EmulatedVehicleClient* client)
EmulatedVehicleHal::EmulatedVehicleHal(VehiclePropertyStore* propStore, VehicleHalClient* client)
: mPropStore(propStore),
mHvacPowerProps(std::begin(kHvacPowerProperties), std::end(kHvacPowerProperties)),
mRecurrentTimer(
std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this, std::placeholders::_1)),
mRecurrentTimer(std::bind(&EmulatedVehicleHal::onContinuousPropertyTimer, this,
std::placeholders::_1)),
mVehicleClient(client) {
initStaticConfig();
for (size_t i = 0; i < arraysize(kVehicleProperties); i++) {

View file

@ -46,7 +46,7 @@ namespace impl {
class EmulatedVehicleHal : public EmulatedVehicleHalIface {
public:
EmulatedVehicleHal(VehiclePropertyStore* propStore,
EmulatedVehicleClient* client);
VehicleHalClient* client);
~EmulatedVehicleHal() = default;
// Methods from VehicleHal
@ -85,7 +85,7 @@ private:
VehiclePropertyStore* mPropStore;
std::unordered_set<int32_t> mHvacPowerProps;
RecurrentTimer mRecurrentTimer;
EmulatedVehicleClient* mVehicleClient;
VehicleHalClient* mVehicleClient;
};
} // impl

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2020 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 "VehicleHalClient.h"
#include <android-base/logging.h>
namespace android::hardware::automotive::vehicle::V2_0::impl {
void VehicleHalClient::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
if (!mPropCallback) {
LOG(ERROR) << __func__ << ": PropertyCallBackType is not registered!";
return;
}
return mPropCallback(value, updateStatus);
}
void VehicleHalClient::registerPropertyValueCallback(PropertyCallBackType&& callback) {
if (mPropCallback) {
LOG(ERROR) << __func__ << ": Cannot register multiple callbacks!";
return;
}
mPropCallback = std::move(callback);
}
} // namespace android::hardware::automotive::vehicle::V2_0::impl

View file

@ -0,0 +1,39 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <vhal_v2_0/VehicleClient.h>
namespace android::hardware::automotive::vehicle::V2_0::impl {
// The common client operations that may be used by both native and
// virtualized VHAL clients.
class VehicleHalClient : public IVehicleClient {
public:
// Type of callback function for handling the new property values
using PropertyCallBackType = std::function<void(const VehiclePropValue&, bool updateStatus)>;
// Method from IVehicleClient
void onPropertyValue(const VehiclePropValue& value, bool updateStatus) override;
void registerPropertyValueCallback(PropertyCallBackType&& callback);
private:
PropertyCallBackType mPropCallback;
};
} // namespace android::hardware::automotive::vehicle::V2_0::impl

View file

@ -0,0 +1,353 @@
/*
* Copyright (C) 2020 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 "VehicleHalServer.h"
#include <fstream>
#include <android-base/logging.h>
#include <utils/SystemClock.h>
#include "DefaultConfig.h"
#include "JsonFakeValueGenerator.h"
#include "LinearFakeValueGenerator.h"
#include "Obd2SensorStore.h"
namespace android::hardware::automotive::vehicle::V2_0::impl {
GeneratorHub* VehicleHalServer::getGenerator() {
return &mGeneratorHub;
}
VehiclePropValuePool* VehicleHalServer::getValuePool() const {
if (!mValuePool) {
LOG(WARNING) << __func__ << ": Value pool not set!";
}
return mValuePool;
}
void VehicleHalServer::setValuePool(VehiclePropValuePool* valuePool) {
if (!valuePool) {
LOG(WARNING) << __func__ << ": Setting value pool to nullptr!";
}
mValuePool = valuePool;
}
void VehicleHalServer::onFakeValueGenerated(const VehiclePropValue& value) {
constexpr bool updateStatus = true;
LOG(DEBUG) << __func__ << ": " << toString(value);
auto updatedPropValue = getValuePool()->obtain(value);
if (updatedPropValue) {
updatedPropValue->timestamp = value.timestamp;
updatedPropValue->status = VehiclePropertyStatus::AVAILABLE;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
}
}
std::vector<VehiclePropConfig> VehicleHalServer::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 VehicleHalServer::handleGenerateFakeDataRequest(const VehiclePropValue& request) {
constexpr bool updateStatus = true;
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),
updateStatus);
onPropertyValueFromCar(
*createHwInputKeyProp(VehicleHwKeyInputAction::ACTION_UP, keyCode, display),
updateStatus);
break;
}
default: {
LOG(ERROR) << __func__ << ": unexpected command: " << toInt(command);
return StatusCode::INVALID_ARG;
}
}
return StatusCode::OK;
}
VehicleHalServer::VehiclePropValuePtr VehicleHalServer::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;
}
VehicleHalServer::VehiclePropValuePtr VehicleHalServer::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 VehicleHalServer::onSetProperty(const VehiclePropValue& value, bool updateStatus) {
// Some properties need to be treated non-trivially
switch (value.prop) {
case kGenerateFakeDataControllingProperty:
return handleGenerateFakeDataRequest(value);
// set the value from vehicle side, used in end to end test.
case kSetIntPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::INT32, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.int32Values[0] = value.value.int32Values[1];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case kSetFloatPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::FLOAT, 1);
updatedPropValue->prop = value.value.int32Values[0];
updatedPropValue->value.floatValues[0] = value.value.floatValues[0];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
case kSetBooleanPropertyFromVehicleForTest: {
auto updatedPropValue = createVehiclePropValue(VehiclePropertyType::BOOLEAN, 1);
updatedPropValue->prop = value.value.int32Values[1];
updatedPropValue->value.int32Values[0] = value.value.int32Values[0];
updatedPropValue->timestamp = value.value.int64Values[0];
updatedPropValue->areaId = value.areaId;
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
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
// ALWAYS update status for generated property value
onPropertyValueFromCar(*createApPowerStateReq(VehicleApPowerStateReq::ON, 0),
true /* updateStatus */);
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
// ALWAYS update status for generated property value
onPropertyValueFromCar(
*createApPowerStateReq(VehicleApPowerStateReq::FINISHED, 0),
true /* updateStatus */);
break;
case toInt(VehicleApPowerStateReport::ON):
case toInt(VehicleApPowerStateReport::SHUTDOWN_POSTPONE):
case toInt(VehicleApPowerStateReport::SHUTDOWN_PREPARE):
// Do nothing
break;
default:
// Unknown state
break;
}
break;
case INITIAL_USER_INFO:
return onSetInitialUserInfo(value, updateStatus);
default:
break;
}
// In the real vhal, the value will be sent to Car ECU.
// We just pretend it is done here and send back to HAL
auto updatedPropValue = getValuePool()->obtain(value);
updatedPropValue->timestamp = elapsedRealtimeNano();
onPropertyValueFromCar(*updatedPropValue, updateStatus);
return StatusCode::OK;
}
/**
* INITIAL_USER_INFO is called by Android when it starts, and it's expecting a property change
* indicating what the initial user should be.
*
* During normal circumstances, the emulator will reply right away, passing a response if
* InitialUserInfoResponseAction::DEFAULT (so Android could use its own logic to decide which user
* to boot).
*
* But during development / testing, the behavior can be changed using lshal dump, which must use
* the areaId to indicate what should happen next.
*
* So, the behavior of set(INITIAL_USER_INFO) is:
*
* - if it has an areaId, store the property into mInitialUserResponseFromCmd (as it was called by
* lshal).
* - else if mInitialUserResponseFromCmd is not set, return a response with the same request id and
* InitialUserInfoResponseAction::DEFAULT
* - else the behavior is defined by the areaId on mInitialUserResponseFromCmd:
* - if it's 1, reply with mInitialUserResponseFromCmd and the right request id
* - if it's 2, reply with mInitialUserResponseFromCmd but a wrong request id (so Android can test
* this error scenario)
* - if it's 3, then don't send a property change (so Android can emulate a timeout)
*
*/
StatusCode VehicleHalServer::onSetInitialUserInfo(const VehiclePropValue& value,
bool updateStatus) {
// TODO: LOG calls below might be more suited to be DEBUG, but those are not being logged
// (even when explicitly calling setprop log.tag. As this class should be using ALOG instead of
// LOG, it's not worth investigating why...
if (value.value.int32Values.size() == 0) {
LOG(ERROR) << "set(INITIAL_USER_INFO): no int32values, ignoring it: " << toString(value);
return StatusCode::INVALID_ARG;
}
if (value.areaId != 0) {
LOG(INFO) << "set(INITIAL_USER_INFO) called from lshal; storing it: " << toString(value);
mInitialUserResponseFromCmd.reset(new VehiclePropValue(value));
return StatusCode::OK;
}
LOG(INFO) << "set(INITIAL_USER_INFO) called from Android: " << toString(value);
int32_t requestId = value.value.int32Values[0];
// Create the update property and set common values
auto updatedValue = createVehiclePropValue(VehiclePropertyType::MIXED, 0);
updatedValue->prop = INITIAL_USER_INFO;
updatedValue->timestamp = elapsedRealtimeNano();
if (mInitialUserResponseFromCmd == nullptr) {
updatedValue->value.int32Values.resize(2);
updatedValue->value.int32Values[0] = requestId;
updatedValue->value.int32Values[1] = (int32_t)InitialUserInfoResponseAction::DEFAULT;
LOG(INFO) << "no lshal response; returning InitialUserInfoResponseAction::DEFAULT: "
<< toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
// mInitialUserResponseFromCmd is used for just one request
std::unique_ptr<VehiclePropValue> response = std::move(mInitialUserResponseFromCmd);
// TODO(b/138709788): rather than populate the raw values directly, it should use the
// libraries that convert a InitialUserInfoResponse into a VehiclePropValue)
switch (response->areaId) {
case 1:
LOG(INFO) << "returning response with right request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = requestId;
break;
case 2:
LOG(INFO) << "returning response with wrong request id";
*updatedValue = *response;
updatedValue->areaId = 0;
updatedValue->value.int32Values[0] = -requestId;
break;
case 3:
LOG(INFO) << "not generating a property change event because of lshal prop: "
<< toString(*response);
return StatusCode::OK;
default:
LOG(ERROR) << "invalid action on lshal response: " << toString(*response);
return StatusCode::INTERNAL_ERROR;
}
LOG(INFO) << "updating property to: " << toString(*updatedValue);
onPropertyValueFromCar(*updatedValue, updateStatus);
return StatusCode::OK;
}
} // namespace android::hardware::automotive::vehicle::V2_0::impl

View file

@ -0,0 +1,71 @@
/*
* Copyright (C) 2020 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.
*/
#pragma once
#include <vhal_v2_0/VehicleObjectPool.h>
#include <vhal_v2_0/VehicleServer.h>
#include "GeneratorHub.h"
namespace android::hardware::automotive::vehicle::V2_0::impl {
// This contains the common server operations that will be used by
// both native and virtualized VHAL server. Notice that in the virtualized
// scenario, the server may be run on a different OS than Android.
class VehicleHalServer : public IVehicleServer {
public:
// Methods from IVehicleServer
std::vector<VehiclePropConfig> onGetAllPropertyConfig() const override;
StatusCode onSetProperty(const VehiclePropValue& value, bool updateStatus) override;
// Set the Property Value Pool used in this server
void setValuePool(VehiclePropValuePool* valuePool);
private:
using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
GeneratorHub* getGenerator();
VehiclePropValuePool* getValuePool() const;
void onFakeValueGenerated(const VehiclePropValue& value);
StatusCode handleGenerateFakeDataRequest(const VehiclePropValue& request);
VehiclePropValuePtr createApPowerStateReq(VehicleApPowerStateReq req, int32_t param);
VehiclePropValuePtr createHwInputKeyProp(VehicleHwKeyInputAction action, int32_t keyCode,
int32_t targetDisplay);
StatusCode onSetInitialUserInfo(const VehiclePropValue& value, bool updateStatus);
// data members
protected:
// TODO(b/146207078): it might be clearer to move members below to an EmulatedUserHal class
std::unique_ptr<VehiclePropValue> mInitialUserResponseFromCmd;
private:
GeneratorHub mGeneratorHub{
std::bind(&VehicleHalServer::onFakeValueGenerated, this, std::placeholders::_1)};
VehiclePropValuePool* mValuePool{nullptr};
};
} // namespace android::hardware::automotive::vehicle::V2_0::impl