mirror of
https://github.com/Evolution-X/hardware_interfaces
synced 2026-02-02 05:56:34 +00:00
Merge changes from topics "presubmit-am-5b7a95f212ff478f89a6c087d9e90e0c", "presubmit-am-cc636b1b7be5428c86072a21533d351e" into tm-dev
* changes: Support '--user-hal' in FakeVehicleHardware. Make FakeVehicleHardware async.
This commit is contained in:
@@ -17,6 +17,7 @@
|
||||
#ifndef android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
|
||||
#define android_hardware_automotive_vehicle_aidl_impl_fake_impl_hardware_include_FakeVehicleHardware_H_
|
||||
|
||||
#include <ConcurrentQueue.h>
|
||||
#include <DefaultConfig.h>
|
||||
#include <FakeObd2Frame.h>
|
||||
#include <FakeUserHal.h>
|
||||
@@ -48,6 +49,8 @@ class FakeVehicleHardware : public IVehicleHardware {
|
||||
|
||||
explicit FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> valuePool);
|
||||
|
||||
~FakeVehicleHardware();
|
||||
|
||||
// Get all the property configs.
|
||||
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>
|
||||
getAllPropertyConfigs() const override;
|
||||
@@ -102,6 +105,29 @@ class FakeVehicleHardware : public IVehicleHardware {
|
||||
// Expose private methods to unit test.
|
||||
friend class FakeVehicleHardwareTestHelper;
|
||||
|
||||
template <class CallbackType, class RequestType>
|
||||
struct RequestWithCallback {
|
||||
RequestType request;
|
||||
std::shared_ptr<const CallbackType> callback;
|
||||
};
|
||||
|
||||
template <class CallbackType, class RequestType>
|
||||
class PendingRequestHandler {
|
||||
public:
|
||||
PendingRequestHandler(FakeVehicleHardware* hardware);
|
||||
|
||||
void addRequest(RequestType request, std::shared_ptr<const CallbackType> callback);
|
||||
|
||||
void stop();
|
||||
|
||||
private:
|
||||
FakeVehicleHardware* mHardware;
|
||||
std::thread mThread;
|
||||
ConcurrentQueue<RequestWithCallback<CallbackType, RequestType>> mRequests;
|
||||
|
||||
void handleRequestsOnce();
|
||||
};
|
||||
|
||||
const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
|
||||
const std::unique_ptr<FakeUserHal> mFakeUserHal;
|
||||
// RecurrentTimer is thread-safe.
|
||||
@@ -111,6 +137,13 @@ class FakeVehicleHardware : public IVehicleHardware {
|
||||
std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback GUARDED_BY(mLock);
|
||||
std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
|
||||
mRecurrentActions GUARDED_BY(mLock);
|
||||
// PendingRequestHandler is thread-safe.
|
||||
mutable PendingRequestHandler<GetValuesCallback,
|
||||
aidl::android::hardware::automotive::vehicle::GetValueRequest>
|
||||
mPendingGetValueRequests;
|
||||
mutable PendingRequestHandler<SetValuesCallback,
|
||||
aidl::android::hardware::automotive::vehicle::SetValueRequest>
|
||||
mPendingSetValueRequests;
|
||||
|
||||
void init();
|
||||
// Stores the initial value to property store.
|
||||
@@ -170,6 +203,10 @@ class FakeVehicleHardware : public IVehicleHardware {
|
||||
|
||||
android::base::Result<void> checkArgumentsSize(const std::vector<std::string>& options,
|
||||
size_t minSize);
|
||||
aidl::android::hardware::automotive::vehicle::GetValueResult handleGetValueRequest(
|
||||
const aidl::android::hardware::automotive::vehicle::GetValueRequest& request);
|
||||
aidl::android::hardware::automotive::vehicle::SetValueResult handleSetValueRequest(
|
||||
const aidl::android::hardware::automotive::vehicle::SetValueRequest& request);
|
||||
};
|
||||
|
||||
} // namespace fake
|
||||
|
||||
@@ -139,10 +139,17 @@ FakeVehicleHardware::FakeVehicleHardware(std::unique_ptr<VehiclePropValuePool> v
|
||||
mServerSidePropStore(new VehiclePropertyStore(mValuePool)),
|
||||
mFakeObd2Frame(new obd2frame::FakeObd2Frame(mServerSidePropStore)),
|
||||
mFakeUserHal(new FakeUserHal(mValuePool)),
|
||||
mRecurrentTimer(new RecurrentTimer()) {
|
||||
mRecurrentTimer(new RecurrentTimer()),
|
||||
mPendingGetValueRequests(this),
|
||||
mPendingSetValueRequests(this) {
|
||||
init();
|
||||
}
|
||||
|
||||
FakeVehicleHardware::~FakeVehicleHardware() {
|
||||
mPendingGetValueRequests.stop();
|
||||
mPendingSetValueRequests.stop();
|
||||
}
|
||||
|
||||
void FakeVehicleHardware::init() {
|
||||
for (auto& it : defaultconfig::getDefaultConfigs()) {
|
||||
VehiclePropConfig cfg = it.config;
|
||||
@@ -427,37 +434,25 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
|
||||
|
||||
StatusCode FakeVehicleHardware::setValues(std::shared_ptr<const SetValuesCallback> callback,
|
||||
const std::vector<SetValueRequest>& requests) {
|
||||
std::vector<SetValueResult> results;
|
||||
for (auto& request : requests) {
|
||||
const VehiclePropValue& value = request.value;
|
||||
int propId = value.prop;
|
||||
|
||||
if (FAKE_VEHICLEHARDWARE_DEBUG) {
|
||||
ALOGD("Set value for property ID: %d", propId);
|
||||
ALOGD("Set value for property ID: %d", request.value.prop);
|
||||
}
|
||||
|
||||
SetValueResult setValueResult;
|
||||
setValueResult.requestId = request.requestId;
|
||||
|
||||
if (auto result = setValue(value); !result.ok()) {
|
||||
ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(),
|
||||
getIntErrorCode(result));
|
||||
setValueResult.status = getErrorCode(result);
|
||||
} else {
|
||||
setValueResult.status = StatusCode::OK;
|
||||
}
|
||||
|
||||
results.push_back(std::move(setValueResult));
|
||||
// In a real VHAL implementation, you could either send the setValue request to vehicle bus
|
||||
// here in the binder thread, or you could send the request in setValue which runs in
|
||||
// the handler thread. If you decide to send the setValue request here, you should not
|
||||
// wait for the response here and the handler thread should handle the setValue response.
|
||||
mPendingSetValueRequests.addRequest(request, callback);
|
||||
}
|
||||
|
||||
// In the real vhal, the values will be sent to Car ECU. We just pretend it is done here and
|
||||
// send back the updated property values to client.
|
||||
(*callback)(std::move(results));
|
||||
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
VhalResult<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) {
|
||||
// In a real VHAL implementation, this will send the request to vehicle bus if not already
|
||||
// sent in setValues, and wait for the response from vehicle bus.
|
||||
// Here we are just updating mValuePool.
|
||||
bool isSpecialValue = false;
|
||||
auto setSpecialValueResult = maybeSetSpecialValue(value, &isSpecialValue);
|
||||
|
||||
@@ -484,41 +479,59 @@ VhalResult<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) {
|
||||
return {};
|
||||
}
|
||||
|
||||
StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback,
|
||||
const std::vector<GetValueRequest>& requests) const {
|
||||
std::vector<GetValueResult> results;
|
||||
for (auto& request : requests) {
|
||||
const VehiclePropValue& value = request.prop;
|
||||
SetValueResult FakeVehicleHardware::handleSetValueRequest(const SetValueRequest& request) {
|
||||
SetValueResult setValueResult;
|
||||
setValueResult.requestId = request.requestId;
|
||||
|
||||
if (FAKE_VEHICLEHARDWARE_DEBUG) {
|
||||
ALOGD("getValues(%d)", value.prop);
|
||||
}
|
||||
|
||||
GetValueResult getValueResult;
|
||||
getValueResult.requestId = request.requestId;
|
||||
|
||||
auto result = getValue(value);
|
||||
if (!result.ok()) {
|
||||
ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(),
|
||||
getIntErrorCode(result));
|
||||
getValueResult.status = getErrorCode(result);
|
||||
} else {
|
||||
getValueResult.status = StatusCode::OK;
|
||||
getValueResult.prop = *result.value();
|
||||
}
|
||||
results.push_back(std::move(getValueResult));
|
||||
if (auto result = setValue(request.value); !result.ok()) {
|
||||
ALOGE("failed to set value, error: %s, code: %d", getErrorMsg(result).c_str(),
|
||||
getIntErrorCode(result));
|
||||
setValueResult.status = getErrorCode(result);
|
||||
} else {
|
||||
setValueResult.status = StatusCode::OK;
|
||||
}
|
||||
|
||||
// In a real VHAL implementation, getValue would be async and we would call the callback after
|
||||
// we actually received the values from vehicle bus. Here we are getting the result
|
||||
// synchronously so we could call the callback here.
|
||||
(*callback)(std::move(results));
|
||||
return setValueResult;
|
||||
}
|
||||
|
||||
StatusCode FakeVehicleHardware::getValues(std::shared_ptr<const GetValuesCallback> callback,
|
||||
const std::vector<GetValueRequest>& requests) const {
|
||||
for (auto& request : requests) {
|
||||
if (FAKE_VEHICLEHARDWARE_DEBUG) {
|
||||
ALOGD("getValues(%d)", request.prop.prop);
|
||||
}
|
||||
|
||||
// In a real VHAL implementation, you could either send the getValue request to vehicle bus
|
||||
// here in the binder thread, or you could send the request in getValue which runs in
|
||||
// the handler thread. If you decide to send the getValue request here, you should not
|
||||
// wait for the response here and the handler thread should handle the getValue response.
|
||||
mPendingGetValueRequests.addRequest(request, callback);
|
||||
}
|
||||
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
GetValueResult FakeVehicleHardware::handleGetValueRequest(const GetValueRequest& request) {
|
||||
GetValueResult getValueResult;
|
||||
getValueResult.requestId = request.requestId;
|
||||
|
||||
auto result = getValue(request.prop);
|
||||
if (!result.ok()) {
|
||||
ALOGE("failed to get value, error: %s, code: %d", getErrorMsg(result).c_str(),
|
||||
getIntErrorCode(result));
|
||||
getValueResult.status = getErrorCode(result);
|
||||
} else {
|
||||
getValueResult.status = StatusCode::OK;
|
||||
getValueResult.prop = *result.value();
|
||||
}
|
||||
return getValueResult;
|
||||
}
|
||||
|
||||
FakeVehicleHardware::ValueResultType FakeVehicleHardware::getValue(
|
||||
const VehiclePropValue& value) const {
|
||||
// In a real VHAL implementation, this will send the request to vehicle bus if not already
|
||||
// sent in getValues, and wait for the response from vehicle bus.
|
||||
// Here we are just reading value from mValuePool.
|
||||
bool isSpecialValue = false;
|
||||
auto result = maybeGetSpecialValue(value, &isSpecialValue);
|
||||
if (isSpecialValue) {
|
||||
@@ -564,6 +577,12 @@ DumpResult FakeVehicleHardware::dump(const std::vector<std::string>& options) {
|
||||
result.buffer = dumpSpecificProperty(options);
|
||||
} else if (EqualsIgnoreCase(option, "--set")) {
|
||||
result.buffer = dumpSetProperties(options);
|
||||
} else if (EqualsIgnoreCase(option, kUserHalDumpOption)) {
|
||||
if (options.size() == 1) {
|
||||
result.buffer = mFakeUserHal->showDumpHelp();
|
||||
} else {
|
||||
result.buffer = mFakeUserHal->dump(options[1]);
|
||||
}
|
||||
} else {
|
||||
result.buffer = StringPrintf("Invalid option: %s\n", option.c_str());
|
||||
}
|
||||
@@ -581,7 +600,9 @@ std::string FakeVehicleHardware::dumpHelp() {
|
||||
"[-b BYTES_VALUE] [-a AREA_ID] : sets the value of property PROP. "
|
||||
"Notice that the string, bytes and area value can be set just once, while the other can"
|
||||
" have multiple values (so they're used in the respective array), "
|
||||
"BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n";
|
||||
"BYTES_VALUE is in the form of 0xXXXX, e.g. 0xdeadbeef.\n\n"
|
||||
"Fake user HAL usage: \n" +
|
||||
mFakeUserHal->showDumpHelp();
|
||||
}
|
||||
|
||||
std::string FakeVehicleHardware::dumpAllProperties() {
|
||||
@@ -971,6 +992,60 @@ Result<std::vector<uint8_t>> FakeVehicleHardware::parseHexString(const std::stri
|
||||
return bytes;
|
||||
}
|
||||
|
||||
template <class CallbackType, class RequestType>
|
||||
FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::PendingRequestHandler(
|
||||
FakeVehicleHardware* hardware)
|
||||
: mHardware(hardware), mThread([this] {
|
||||
while (mRequests.waitForItems()) {
|
||||
handleRequestsOnce();
|
||||
}
|
||||
}) {}
|
||||
|
||||
template <class CallbackType, class RequestType>
|
||||
void FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::addRequest(
|
||||
RequestType request, std::shared_ptr<const CallbackType> callback) {
|
||||
mRequests.push({
|
||||
request,
|
||||
callback,
|
||||
});
|
||||
}
|
||||
|
||||
template <class CallbackType, class RequestType>
|
||||
void FakeVehicleHardware::PendingRequestHandler<CallbackType, RequestType>::stop() {
|
||||
mRequests.deactivate();
|
||||
if (mThread.joinable()) {
|
||||
mThread.join();
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void FakeVehicleHardware::PendingRequestHandler<FakeVehicleHardware::GetValuesCallback,
|
||||
GetValueRequest>::handleRequestsOnce() {
|
||||
std::unordered_map<std::shared_ptr<const GetValuesCallback>, std::vector<GetValueResult>>
|
||||
callbackToResults;
|
||||
for (const auto& rwc : mRequests.flush()) {
|
||||
auto result = mHardware->handleGetValueRequest(rwc.request);
|
||||
callbackToResults[rwc.callback].push_back(std::move(result));
|
||||
}
|
||||
for (const auto& [callback, results] : callbackToResults) {
|
||||
(*callback)(std::move(results));
|
||||
}
|
||||
}
|
||||
|
||||
template <>
|
||||
void FakeVehicleHardware::PendingRequestHandler<FakeVehicleHardware::SetValuesCallback,
|
||||
SetValueRequest>::handleRequestsOnce() {
|
||||
std::unordered_map<std::shared_ptr<const SetValuesCallback>, std::vector<SetValueResult>>
|
||||
callbackToResults;
|
||||
for (const auto& rwc : mRequests.flush()) {
|
||||
auto result = mHardware->handleSetValueRequest(rwc.request);
|
||||
callbackToResults[rwc.callback].push_back(std::move(result));
|
||||
}
|
||||
for (const auto& [callback, results] : callbackToResults) {
|
||||
(*callback)(std::move(results));
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace fake
|
||||
} // namespace vehicle
|
||||
} // namespace automotive
|
||||
|
||||
@@ -34,6 +34,8 @@
|
||||
#include <inttypes.h>
|
||||
#include <chrono>
|
||||
#include <condition_variable>
|
||||
#include <unordered_map>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
namespace android {
|
||||
@@ -99,11 +101,51 @@ class FakeVehicleHardwareTest : public ::testing::Test {
|
||||
FakeVehicleHardware* getHardware() { return &mHardware; }
|
||||
|
||||
StatusCode setValues(const std::vector<SetValueRequest>& requests) {
|
||||
return getHardware()->setValues(mSetValuesCallback, requests);
|
||||
{
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
for (const auto& request : requests) {
|
||||
mPendingSetValueRequests.insert(request.requestId);
|
||||
}
|
||||
}
|
||||
if (StatusCode status = getHardware()->setValues(mSetValuesCallback, requests);
|
||||
status != StatusCode::OK) {
|
||||
return status;
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(mLock);
|
||||
// Wait for the onSetValueResults.
|
||||
bool result = mCv.wait_for(lk, milliseconds(1000), [this] {
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
return mPendingSetValueRequests.size() == 0;
|
||||
});
|
||||
if (!result) {
|
||||
ALOGE("wait for callbacks for setValues timed-out");
|
||||
return StatusCode::INTERNAL_ERROR;
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode getValues(const std::vector<GetValueRequest>& requests) {
|
||||
return getHardware()->getValues(mGetValuesCallback, requests);
|
||||
{
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
for (const auto& request : requests) {
|
||||
mPendingGetValueRequests.insert(request.requestId);
|
||||
}
|
||||
}
|
||||
if (StatusCode status = getHardware()->getValues(mGetValuesCallback, requests);
|
||||
status != StatusCode::OK) {
|
||||
return status;
|
||||
}
|
||||
std::unique_lock<std::mutex> lk(mLock);
|
||||
// Wait for the onGetValueResults.
|
||||
bool result = mCv.wait_for(lk, milliseconds(1000), [this] {
|
||||
ScopedLockAssertion lockAssertion(mLock);
|
||||
return mPendingGetValueRequests.size() == 0;
|
||||
});
|
||||
if (!result) {
|
||||
ALOGE("wait for callbacks for getValues timed-out");
|
||||
return StatusCode::INTERNAL_ERROR;
|
||||
}
|
||||
return StatusCode::OK;
|
||||
}
|
||||
|
||||
StatusCode setValue(const VehiclePropValue& value) {
|
||||
@@ -167,7 +209,9 @@ class FakeVehicleHardwareTest : public ::testing::Test {
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
for (auto& result : results) {
|
||||
mSetValueResults.push_back(result);
|
||||
mPendingSetValueRequests.erase(result.requestId);
|
||||
}
|
||||
mCv.notify_all();
|
||||
}
|
||||
|
||||
const std::vector<SetValueResult>& getSetValueResults() {
|
||||
@@ -179,7 +223,9 @@ class FakeVehicleHardwareTest : public ::testing::Test {
|
||||
std::scoped_lock<std::mutex> lockGuard(mLock);
|
||||
for (auto& result : results) {
|
||||
mGetValueResults.push_back(result);
|
||||
mPendingGetValueRequests.erase(result.requestId);
|
||||
}
|
||||
mCv.notify_all();
|
||||
}
|
||||
|
||||
const std::vector<GetValueResult>& getGetValueResults() {
|
||||
@@ -197,7 +243,7 @@ class FakeVehicleHardwareTest : public ::testing::Test {
|
||||
};
|
||||
mEventCount[propIdAreaId]++;
|
||||
}
|
||||
mCv.notify_one();
|
||||
mCv.notify_all();
|
||||
}
|
||||
|
||||
const std::vector<VehiclePropValue>& getChangedProperties() {
|
||||
@@ -295,6 +341,8 @@ class FakeVehicleHardwareTest : public ::testing::Test {
|
||||
std::vector<SetValueResult> mSetValueResults GUARDED_BY(mLock);
|
||||
std::vector<GetValueResult> mGetValueResults GUARDED_BY(mLock);
|
||||
std::vector<VehiclePropValue> mChangedProperties GUARDED_BY(mLock);
|
||||
std::unordered_set<int64_t> mPendingSetValueRequests GUARDED_BY(mLock);
|
||||
std::unordered_set<int64_t> mPendingGetValueRequests GUARDED_BY(mLock);
|
||||
};
|
||||
|
||||
TEST_F(FakeVehicleHardwareTest, testGetAllPropertyConfigs) {
|
||||
@@ -1357,6 +1405,28 @@ TEST_F(FakeVehicleHardwareTest, testDumpInvalidOptions) {
|
||||
ASSERT_THAT(result.buffer, ContainsRegex("Invalid option: --invalid"));
|
||||
}
|
||||
|
||||
TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHalHelp) {
|
||||
std::vector<std::string> options;
|
||||
options.push_back("--user-hal");
|
||||
|
||||
DumpResult result = getHardware()->dump(options);
|
||||
ASSERT_FALSE(result.callerShouldDumpState);
|
||||
ASSERT_NE(result.buffer, "");
|
||||
ASSERT_THAT(result.buffer, ContainsRegex("dumps state used for user management"));
|
||||
}
|
||||
|
||||
TEST_F(FakeVehicleHardwareTest, testDumpFakeUserHal) {
|
||||
std::vector<std::string> options;
|
||||
options.push_back("--user-hal");
|
||||
// Indent: " ".
|
||||
options.push_back(" ");
|
||||
|
||||
DumpResult result = getHardware()->dump(options);
|
||||
ASSERT_FALSE(result.callerShouldDumpState);
|
||||
ASSERT_NE(result.buffer, "");
|
||||
ASSERT_THAT(result.buffer, ContainsRegex(" No InitialUserInfo response\n"));
|
||||
}
|
||||
|
||||
struct SetPropTestCase {
|
||||
std::string test_name;
|
||||
std::vector<std::string> options;
|
||||
|
||||
Reference in New Issue
Block a user