Merge Android 24Q1 Release (ab/11220357)

Bug: 319669529
Merged-In: I976f80006aeb88bde2ae34ca4f9be72cea727d9e
Change-Id: Id94b25af52bd2e2847b6858697ab21cc6ce27aa3
This commit is contained in:
Xin Li 2024-01-30 10:36:50 -08:00
commit 763c473c3d
1207 changed files with 28993 additions and 5814 deletions

View file

@ -10,3 +10,4 @@ aidl_format = true
aosp_hook_confirmationui = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} confirmationui
aosp_hook_gatekeeper = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} gatekeeper
aosp_hook_keymaster = ${REPO_ROOT}/frameworks/base/tools/aosp/aosp_sha.sh ${PREUPLOAD_COMMIT} keymaster
generate_vehicle_property_enums = ${REPO_ROOT}/hardware/interfaces/automotive/vehicle/tools/generate_annotation_enums.py --android_build_top ${REPO_ROOT} --preupload_files ${PREUPLOAD_FILES} --check_only

View file

@ -96,8 +96,11 @@ cc_library {
shared_libs: [
"android.hardware.bluetooth.audio-impl",
"libaudio_aidl_conversion_common_ndk",
"libaudioutils",
"libbluetooth_audio_session_aidl",
"liblog",
"libmedia_helper",
"libmediautils_vendor",
"libstagefright_foundation",
],
export_shared_lib_headers: [
@ -129,6 +132,7 @@ cc_binary {
"android.hardware.bluetooth.audio-impl",
"libaudio_aidl_conversion_common_ndk",
"libbluetooth_audio_session_aidl",
"liblog",
"libmedia_helper",
"libstagefright_foundation",
],

View file

@ -212,6 +212,11 @@ ndk::ScopedAStatus Module::createStreamContext(
StreamContext::DebugParameters params{mDebug.streamTransientStateDelayMs,
mVendorDebug.forceTransientBurst,
mVendorDebug.forceSynchronousDrain};
std::shared_ptr<ISoundDose> soundDose;
if (!getSoundDose(&soundDose).isOk()) {
LOG(ERROR) << __func__ << ": could not create sound dose instance";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
}
StreamContext temp(
std::make_unique<StreamContext::CommandMQ>(1, true /*configureEventFlagWord*/),
std::make_unique<StreamContext::ReplyMQ>(1, true /*configureEventFlagWord*/),
@ -219,8 +224,7 @@ ndk::ScopedAStatus Module::createStreamContext(
portConfigIt->sampleRate.value().value, flags, nominalLatencyMs,
portConfigIt->ext.get<AudioPortExt::mix>().handle,
std::make_unique<StreamContext::DataMQ>(frameSize * in_bufferSizeFrames),
asyncCallback, outEventCallback,
std::weak_ptr<sounddose::StreamDataProcessorInterface>{}, params);
asyncCallback, outEventCallback, mSoundDose.getInstance(), params);
if (temp.isValid()) {
*out_context = std::move(temp);
} else {

View file

@ -18,7 +18,15 @@
#include "core-impl/SoundDose.h"
#include <aidl/android/hardware/audio/core/sounddose/ISoundDose.h>
#include <android-base/logging.h>
#include <media/AidlConversionCppNdk.h>
#include <utils/Timers.h>
using aidl::android::hardware::audio::core::sounddose::ISoundDose;
using aidl::android::media::audio::common::AudioDevice;
using aidl::android::media::audio::common::AudioDeviceDescription;
using aidl::android::media::audio::common::AudioFormatDescription;
namespace aidl::android::hardware::audio::core::sounddose {
@ -28,11 +36,16 @@ ndk::ScopedAStatus SoundDose::setOutputRs2UpperBound(float in_rs2ValueDbA) {
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
::android::audio_utils::lock_guard l(mMutex);
mRs2Value = in_rs2ValueDbA;
if (mMelProcessor != nullptr) {
mMelProcessor->setOutputRs2UpperBound(in_rs2ValueDbA);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus SoundDose::getOutputRs2UpperBound(float* _aidl_return) {
::android::audio_utils::lock_guard l(mMutex);
*_aidl_return = mRs2Value;
LOG(DEBUG) << __func__ << ": returning " << *_aidl_return;
return ndk::ScopedAStatus::ok();
@ -44,6 +57,8 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback(
LOG(ERROR) << __func__ << ": Callback is nullptr";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
}
::android::audio_utils::lock_guard l(mCbMutex);
if (mCallback != nullptr) {
LOG(ERROR) << __func__ << ": Sound dose callback was already registered";
return ndk::ScopedAStatus::fromExceptionCode(EX_ILLEGAL_STATE);
@ -51,7 +66,81 @@ ndk::ScopedAStatus SoundDose::registerSoundDoseCallback(
mCallback = in_callback;
LOG(DEBUG) << __func__ << ": Registered sound dose callback ";
return ndk::ScopedAStatus::ok();
}
void SoundDose::setAudioDevice(const AudioDevice& audioDevice) {
::android::audio_utils::lock_guard l(mCbMutex);
mAudioDevice = audioDevice;
}
void SoundDose::startDataProcessor(uint32_t sampleRate, uint32_t channelCount,
const AudioFormatDescription& aidlFormat) {
::android::audio_utils::lock_guard l(mMutex);
const auto result = aidl2legacy_AudioFormatDescription_audio_format_t(aidlFormat);
const audio_format_t format = result.value_or(AUDIO_FORMAT_INVALID);
if (mMelProcessor == nullptr) {
// we don't have the deviceId concept on the vendor side so just pass 0
mMelProcessor = ::android::sp<::android::audio_utils::MelProcessor>::make(
sampleRate, channelCount, format, mMelCallback, /*deviceId=*/0, mRs2Value);
} else {
mMelProcessor->updateAudioFormat(sampleRate, channelCount, format);
}
}
void SoundDose::process(const void* buffer, size_t bytes) {
::android::audio_utils::lock_guard l(mMutex);
if (mMelProcessor != nullptr) {
mMelProcessor->process(buffer, bytes);
}
}
void SoundDose::onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
audio_port_handle_t deviceId __attribute__((__unused__))) const {
::android::audio_utils::lock_guard l(mCbMutex);
if (!mAudioDevice.has_value()) {
LOG(WARNING) << __func__ << ": New mel values without a registered device";
return;
}
if (mCallback == nullptr) {
LOG(ERROR) << __func__ << ": New mel values without a registered callback";
return;
}
ISoundDose::IHalSoundDoseCallback::MelRecord melRecord;
melRecord.timestamp = nanoseconds_to_seconds(systemTime());
melRecord.melValues = std::vector<float>(mels.begin() + offset, mels.begin() + offset + length);
mCallback->onNewMelValues(melRecord, mAudioDevice.value());
}
void SoundDose::MelCallback::onNewMelValues(const std::vector<float>& mels, size_t offset,
size_t length,
audio_port_handle_t deviceId
__attribute__((__unused__))) const {
mSoundDose.onNewMelValues(mels, offset, length, deviceId);
}
void SoundDose::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId
__attribute__((__unused__))) const {
::android::audio_utils::lock_guard l(mCbMutex);
if (!mAudioDevice.has_value()) {
LOG(WARNING) << __func__ << ": Momentary exposure without a registered device";
return;
}
if (mCallback == nullptr) {
LOG(ERROR) << __func__ << ": Momentary exposure without a registered callback";
return;
}
mCallback->onMomentaryExposureWarning(currentMel, mAudioDevice.value());
}
void SoundDose::MelCallback::onMomentaryExposure(float currentMel, audio_port_handle_t deviceId
__attribute__((__unused__))) const {
mSoundDose.onMomentaryExposure(currentMel, deviceId);
}
} // namespace aidl::android::hardware::audio::core::sounddose

View file

@ -21,6 +21,8 @@
#include <aidl/android/hardware/audio/core/sounddose/BnSoundDose.h>
#include <aidl/android/media/audio/common/AudioDevice.h>
#include <aidl/android/media/audio/common/AudioFormatDescription.h>
#include <audio_utils/MelProcessor.h>
#include <audio_utils/mutex.h>
namespace aidl::android::hardware::audio::core::sounddose {
@ -37,18 +39,49 @@ class StreamDataProcessorInterface {
virtual void process(const void* buffer, size_t size) = 0;
};
class SoundDose : public BnSoundDose {
class SoundDose final : public BnSoundDose, public StreamDataProcessorInterface {
public:
SoundDose() : mRs2Value(DEFAULT_MAX_RS2){};
SoundDose() : mMelCallback(::android::sp<MelCallback>::make(this)){};
// -------------------------------------- BnSoundDose ------------------------------------------
ndk::ScopedAStatus setOutputRs2UpperBound(float in_rs2ValueDbA) override;
ndk::ScopedAStatus getOutputRs2UpperBound(float* _aidl_return) override;
ndk::ScopedAStatus registerSoundDoseCallback(
const std::shared_ptr<ISoundDose::IHalSoundDoseCallback>& in_callback) override;
// ----------------------------- StreamDataProcessorInterface ----------------------------------
void setAudioDevice(
const ::aidl::android::media::audio::common::AudioDevice& audioDevice) override;
void startDataProcessor(
uint32_t samplerate, uint32_t channelCount,
const ::aidl::android::media::audio::common::AudioFormatDescription& format) override;
void process(const void* buffer, size_t size) override;
private:
std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback;
float mRs2Value;
class MelCallback : public ::android::audio_utils::MelProcessor::MelCallback {
public:
explicit MelCallback(SoundDose* soundDose) : mSoundDose(*soundDose) {}
// ------------------------------------ MelCallback ----------------------------------------
void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
audio_port_handle_t deviceId) const override;
void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const override;
SoundDose& mSoundDose; // must outlive MelCallback, not owning
};
void onNewMelValues(const std::vector<float>& mels, size_t offset, size_t length,
audio_port_handle_t deviceId) const;
void onMomentaryExposure(float currentMel, audio_port_handle_t deviceId) const;
mutable ::android::audio_utils::mutex mCbMutex;
std::shared_ptr<ISoundDose::IHalSoundDoseCallback> mCallback GUARDED_BY(mCbMutex);
std::optional<::aidl::android::media::audio::common::AudioDevice> mAudioDevice
GUARDED_BY(mCbMutex);
mutable ::android::audio_utils::mutex mMutex;
float mRs2Value GUARDED_BY(mMutex) = DEFAULT_MAX_RS2;
::android::sp<::android::audio_utils::MelProcessor> mMelProcessor GUARDED_BY(mMutex);
::android::sp<MelCallback> mMelCallback GUARDED_BY(mMutex);
};
} // namespace aidl::android::hardware::audio::core::sounddose

View file

@ -315,7 +315,7 @@ Effect::Effect(bool isInput, effect_handle_t handle)
Effect::~Effect() {
ATRACE_CALL();
(void)close();
auto [_, handle] = closeImpl();
if (mProcessThread.get()) {
ATRACE_NAME("mProcessThread->join");
status_t status = mProcessThread->join();
@ -328,11 +328,10 @@ Effect::~Effect() {
mInBuffer.clear();
mOutBuffer.clear();
#if MAJOR_VERSION <= 5
int status = EffectRelease(mHandle);
ALOGW_IF(status, "Error releasing effect %p: %s", mHandle, strerror(-status));
int status = EffectRelease(handle);
ALOGW_IF(status, "Error releasing effect %p: %s", handle, strerror(-status));
#endif
EffectMap::getInstance().remove(mHandle);
mHandle = 0;
EffectMap::getInstance().remove(handle);
}
// static
@ -459,7 +458,19 @@ Result Effect::analyzeStatus(const char* funcName, const char* subFuncName,
}
}
void Effect::getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb) {
#define RETURN_IF_EFFECT_CLOSED() \
if (mHandle == kInvalidEffectHandle) { \
return Result::INVALID_STATE; \
}
#define RETURN_RESULT_IF_EFFECT_CLOSED(result) \
if (mHandle == kInvalidEffectHandle) { \
_hidl_cb(Result::INVALID_STATE, result); \
return Void(); \
}
Return<void> Effect::getConfigImpl(int commandCode, const char* commandName,
GetConfigCallback _hidl_cb) {
RETURN_RESULT_IF_EFFECT_CLOSED(EffectConfig());
uint32_t halResultSize = sizeof(effect_config_t);
effect_config_t halConfig{};
status_t status =
@ -468,7 +479,8 @@ void Effect::getConfigImpl(int commandCode, const char* commandName, GetConfigCa
if (status == OK) {
status = EffectUtils::effectConfigFromHal(halConfig, mIsInput, &config);
}
cb(analyzeCommandStatus(commandName, sContextCallToCommand, status), config);
_hidl_cb(analyzeCommandStatus(commandName, sContextCallToCommand, status), config);
return Void();
}
Result Effect::getCurrentConfigImpl(uint32_t featureId, uint32_t configSize,
@ -530,6 +542,7 @@ Result Effect::getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs,
}
Return<void> Effect::prepareForProcessing(prepareForProcessing_cb _hidl_cb) {
RETURN_RESULT_IF_EFFECT_CLOSED(StatusMQ::Descriptor());
status_t status;
// Create message queue.
if (mStatusMQ) {
@ -576,6 +589,7 @@ Return<void> Effect::prepareForProcessing(prepareForProcessing_cb _hidl_cb) {
Return<Result> Effect::setProcessBuffers(const AudioBuffer& inBuffer,
const AudioBuffer& outBuffer) {
RETURN_IF_EFFECT_CLOSED();
AudioBufferManager& manager = AudioBufferManager::getInstance();
sp<AudioBufferWrapper> tempInBuffer, tempOutBuffer;
if (!manager.wrap(inBuffer, &tempInBuffer)) {
@ -600,6 +614,7 @@ Result Effect::sendCommand(int commandCode, const char* commandName) {
}
Result Effect::sendCommand(int commandCode, const char* commandName, uint32_t size, void* data) {
RETURN_IF_EFFECT_CLOSED();
status_t status = (*mHandle)->command(mHandle, commandCode, size, data, 0, NULL);
return analyzeCommandStatus(commandName, sContextCallToCommand, status);
}
@ -611,6 +626,7 @@ Result Effect::sendCommandReturningData(int commandCode, const char* commandName
Result Effect::sendCommandReturningData(int commandCode, const char* commandName, uint32_t size,
void* data, uint32_t* replySize, void* replyData) {
RETURN_IF_EFFECT_CLOSED();
uint32_t expectedReplySize = *replySize;
status_t status = (*mHandle)->command(mHandle, commandCode, size, data, replySize, replyData);
if (status == OK && *replySize != expectedReplySize) {
@ -635,6 +651,7 @@ Result Effect::sendCommandReturningStatusAndData(int commandCode, const char* co
uint32_t size, void* data, uint32_t* replySize,
void* replyData, uint32_t minReplySize,
CommandSuccessCallback onSuccess) {
RETURN_IF_EFFECT_CLOSED();
status_t status = (*mHandle)->command(mHandle, commandCode, size, data, replySize, replyData);
Result retval;
if (status == OK && minReplySize >= sizeof(uint32_t) && *replySize >= minReplySize) {
@ -792,13 +809,11 @@ Return<Result> Effect::setConfigReverse(
}
Return<void> Effect::getConfig(getConfig_cb _hidl_cb) {
getConfigImpl(EFFECT_CMD_GET_CONFIG, "GET_CONFIG", _hidl_cb);
return Void();
return getConfigImpl(EFFECT_CMD_GET_CONFIG, "GET_CONFIG", _hidl_cb);
}
Return<void> Effect::getConfigReverse(getConfigReverse_cb _hidl_cb) {
getConfigImpl(EFFECT_CMD_GET_CONFIG_REVERSE, "GET_CONFIG_REVERSE", _hidl_cb);
return Void();
return getConfigImpl(EFFECT_CMD_GET_CONFIG_REVERSE, "GET_CONFIG_REVERSE", _hidl_cb);
}
Return<void> Effect::getSupportedAuxChannelsConfigs(uint32_t maxConfigs,
@ -845,6 +860,7 @@ Return<Result> Effect::offload(const EffectOffloadParameter& param) {
}
Return<void> Effect::getDescriptor(getDescriptor_cb _hidl_cb) {
RETURN_RESULT_IF_EFFECT_CLOSED(EffectDescriptor());
effect_descriptor_t halDescriptor;
memset(&halDescriptor, 0, sizeof(effect_descriptor_t));
status_t status = (*mHandle)->get_descriptor(mHandle, &halDescriptor);
@ -858,6 +874,10 @@ Return<void> Effect::getDescriptor(getDescriptor_cb _hidl_cb) {
Return<void> Effect::command(uint32_t commandId, const hidl_vec<uint8_t>& data,
uint32_t resultMaxSize, command_cb _hidl_cb) {
if (mHandle == kInvalidEffectHandle) {
_hidl_cb(-ENODATA, hidl_vec<uint8_t>());
return Void();
}
uint32_t halDataSize;
std::unique_ptr<uint8_t[]> halData = hidlVecToHal(data, &halDataSize);
uint32_t halResultSize = resultMaxSize;
@ -942,26 +962,33 @@ Return<Result> Effect::setCurrentConfigForFeature(uint32_t featureId,
halCmd.size(), &halCmd[0]);
}
Return<Result> Effect::close() {
std::tuple<Result, effect_handle_t> Effect::closeImpl() {
if (mStopProcessThread.load(std::memory_order_relaxed)) { // only this thread modifies
return Result::INVALID_STATE;
return {Result::INVALID_STATE, kInvalidEffectHandle};
}
mStopProcessThread.store(true, std::memory_order_release);
if (mEfGroup) {
mEfGroup->wake(static_cast<uint32_t>(MessageQueueFlagBits::REQUEST_QUIT));
}
effect_handle_t handle = mHandle;
mHandle = kInvalidEffectHandle;
#if MAJOR_VERSION <= 5
return Result::OK;
return {Result::OK, handle};
#elif MAJOR_VERSION >= 6
// No need to join the processing thread, it is part of the API contract that the client
// must finish processing before closing the effect.
Result retval =
analyzeStatus("EffectRelease", "", sContextCallFunction, EffectRelease(mHandle));
EffectMap::getInstance().remove(mHandle);
return retval;
Result retval = analyzeStatus("EffectRelease", "", sContextCallFunction, EffectRelease(handle));
EffectMap::getInstance().remove(handle);
return {retval, handle};
#endif
}
Return<Result> Effect::close() {
RETURN_IF_EFFECT_CLOSED();
auto [result, _] = closeImpl();
return result;
}
Return<void> Effect::debug(const hidl_handle& fd, const hidl_vec<hidl_string>& /* options */) {
if (fd.getNativeHandle() != nullptr && fd->numFds == 1) {
uint32_t cmdData = fd->data[0];

View file

@ -23,6 +23,7 @@
#include <atomic>
#include <memory>
#include <tuple>
#include <vector>
#include <fmq/EventFlag.h>
@ -186,6 +187,7 @@ struct Effect : public IEffect {
// Sets the limit on the maximum size of vendor-provided data structures.
static constexpr size_t kMaxDataSize = 1 << 20;
static constexpr effect_handle_t kInvalidEffectHandle = nullptr;
static const char* sContextResultOfCommand;
static const char* sContextCallToCommand;
@ -208,6 +210,7 @@ struct Effect : public IEffect {
static size_t alignedSizeIn(size_t s);
template <typename T>
std::unique_ptr<uint8_t[]> hidlVecToHal(const hidl_vec<T>& vec, uint32_t* halDataSize);
std::tuple<Result, effect_handle_t> closeImpl();
void effectAuxChannelsConfigFromHal(const channel_config_t& halConfig,
EffectAuxChannelsConfig* config);
static void effectAuxChannelsConfigToHal(const EffectAuxChannelsConfig& config,
@ -218,7 +221,8 @@ struct Effect : public IEffect {
const void** valueData, std::vector<uint8_t>* halParamBuffer);
Result analyzeCommandStatus(const char* commandName, const char* context, status_t status);
void getConfigImpl(int commandCode, const char* commandName, GetConfigCallback cb);
Return<void> getConfigImpl(int commandCode, const char* commandName,
GetConfigCallback _hidl_cb);
Result getCurrentConfigImpl(uint32_t featureId, uint32_t configSize,
GetCurrentConfigSuccessCallback onSuccess);
Result getSupportedConfigsImpl(uint32_t featureId, uint32_t maxConfigs, uint32_t configSize,

View file

@ -169,13 +169,15 @@ static const Uuid LOUDNESS_ENHANCER_EFFECT_TYPE = {
0xfe3199be, 0xaed0, 0x413f, 0x87bb,
std::array<uint8_t, 6>{{0x11, 0x26, 0x0e, 0xb6, 0x3c, 0xf1}}};
enum { PARAM_FACTORY_NAME, PARAM_EFFECT_UUID };
using EffectParameter = std::tuple<std::string, Uuid>;
enum { PARAM_FACTORY_NAME, PARAM_EFFECT_UUID, PARAM_USE_AFTER_CLOSE };
using EffectParameter = std::tuple<std::string, Uuid, bool>;
static inline std::string EffectParameterToString(
const ::testing::TestParamInfo<EffectParameter>& info) {
return ::android::hardware::PrintInstanceNameToString(::testing::TestParamInfo<std::string>{
std::get<PARAM_FACTORY_NAME>(info.param), info.index});
std::string prefix = std::get<PARAM_USE_AFTER_CLOSE>(info.param) ? "UseAfterClose_" : "";
return prefix.append(
::android::hardware::PrintInstanceNameToString(::testing::TestParamInfo<std::string>{
std::get<PARAM_FACTORY_NAME>(info.param), info.index}));
}
// The main test class for Audio Effect HIDL HAL.
@ -191,6 +193,13 @@ class AudioEffectHidlTest : public ::testing::TestWithParam<EffectParameter> {
Return<Result> ret = effect->init();
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, ret);
useAfterClose = std::get<PARAM_USE_AFTER_CLOSE>(GetParam());
if (useAfterClose) {
Return<Result> ret = effect->close();
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, ret);
}
}
void TearDown() override {
@ -205,14 +214,34 @@ class AudioEffectHidlTest : public ::testing::TestWithParam<EffectParameter> {
Uuid getEffectType() const { return std::get<PARAM_EFFECT_UUID>(GetParam()); }
void checkResult(const Result& result);
void checkResultForUseAfterClose(const Result& result);
void findAndCreateEffect(const Uuid& type);
void findEffectInstance(const Uuid& type, Uuid* uuid);
void getChannelCount(uint32_t* channelCount);
sp<IEffectsFactory> effectsFactory;
sp<IEffect> effect;
bool useAfterClose;
};
void AudioEffectHidlTest::checkResult(const Result& result) {
if (!useAfterClose) {
ASSERT_EQ(Result::OK, result);
} else {
ASSERT_NO_FATAL_FAILURE(checkResultForUseAfterClose(result));
}
}
void AudioEffectHidlTest::checkResultForUseAfterClose(const Result& result) {
if (useAfterClose) {
// The actual error does not matter. It's important that the effect did not crash
// while executing any command after a call to "close", and that the returned status
// is not OK.
ASSERT_NE(Result::OK, result);
}
}
void AudioEffectHidlTest::findAndCreateEffect(const Uuid& type) {
Uuid effectUuid;
ASSERT_NO_FATAL_FAILURE(findEffectInstance(type, &effectUuid));
@ -257,7 +286,11 @@ void AudioEffectHidlTest::getChannelCount(uint32_t* channelCount) {
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) {
*channelCount = 1;
return;
}
#if MAJOR_VERSION <= 6
ASSERT_TRUE(audio_channel_mask_is_valid(
static_cast<audio_channel_mask_t>(currentConfig.outputCfg.channels)));
@ -276,7 +309,7 @@ TEST_P(AudioEffectHidlTest, Close) {
description("Verify that an effect can be closed");
Return<Result> ret = effect->close();
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
ASSERT_NO_FATAL_FAILURE(checkResult(ret));
}
TEST_P(AudioEffectHidlTest, GetDescriptor) {
@ -290,7 +323,8 @@ TEST_P(AudioEffectHidlTest, GetDescriptor) {
}
});
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) return;
EXPECT_EQ(getEffectType(), actualType);
}
@ -307,7 +341,8 @@ TEST_P(AudioEffectHidlTest, GetSetConfig) {
}
});
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) return;
Return<Result> ret2 = effect->setConfig(currentConfig, nullptr, nullptr);
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, ret2);
@ -336,7 +371,8 @@ TEST_P(AudioEffectHidlTest, SetConfigInvalidArguments) {
}
});
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) return;
for (const auto& invalidInputCfg : generateInvalidConfigs(currentConfig.inputCfg)) {
EffectConfig invalidConfig = currentConfig;
invalidConfig.inputCfg = invalidInputCfg;
@ -356,27 +392,35 @@ TEST_P(AudioEffectHidlTest, SetConfigInvalidArguments) {
TEST_P(AudioEffectHidlTest, GetConfigReverse) {
description("Verify that GetConfigReverse does not crash");
Return<void> ret = effect->getConfigReverse([&](Result, const EffectConfig&) {});
Result retval = Result::OK;
Return<void> ret = effect->getConfigReverse([&](Result r, const EffectConfig&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval));
}
TEST_P(AudioEffectHidlTest, GetSupportedAuxChannelsConfigs) {
description("Verify that GetSupportedAuxChannelsConfigs does not crash");
Result retval = Result::OK;
Return<void> ret = effect->getSupportedAuxChannelsConfigs(
0, [&](Result, const hidl_vec<EffectAuxChannelsConfig>&) {});
0, [&](Result r, const hidl_vec<EffectAuxChannelsConfig>&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval));
}
TEST_P(AudioEffectHidlTest, GetAuxChannelsConfig) {
description("Verify that GetAuxChannelsConfig does not crash");
Return<void> ret = effect->getAuxChannelsConfig([&](Result, const EffectAuxChannelsConfig&) {});
Result retval = Result::OK;
Return<void> ret = effect->getAuxChannelsConfig(
[&](Result r, const EffectAuxChannelsConfig&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval));
}
TEST_P(AudioEffectHidlTest, SetAuxChannelsConfig) {
description("Verify that SetAuxChannelsConfig does not crash");
Return<Result> ret = effect->setAuxChannelsConfig(EffectAuxChannelsConfig());
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
// Not generated automatically because AudioBuffer contains
@ -427,45 +471,56 @@ TEST_P(AudioEffectHidlTest, Reset) {
description("Verify that Reset preserves effect configuration");
Result retval = Result::NOT_INITIALIZED;
EffectConfig originalConfig;
Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
retval = r;
if (r == Result::OK) {
originalConfig = conf;
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
if (!useAfterClose) {
Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
retval = r;
if (r == Result::OK) {
originalConfig = conf;
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
}
Return<Result> ret2 = effect->reset();
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, ret2);
EffectConfig configAfterReset;
ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
retval = r;
if (r == Result::OK) {
configAfterReset = conf;
}
});
EXPECT_EQ(originalConfig, configAfterReset);
EXPECT_NO_FATAL_FAILURE(checkResult(ret2));
if (!useAfterClose) {
EffectConfig configAfterReset;
Return<void> ret = effect->getConfig([&](Result r, const EffectConfig& conf) {
retval = r;
if (r == Result::OK) {
configAfterReset = conf;
}
});
EXPECT_EQ(originalConfig, configAfterReset);
}
}
TEST_P(AudioEffectHidlTest, DisableEnableDisable) {
description("Verify Disable -> Enable -> Disable sequence for an effect");
Return<Result> ret = effect->disable();
EXPECT_TRUE(ret.isOk());
// Note: some legacy effects may return -EINVAL (INVALID_ARGUMENTS),
// more canonical is to return -ENOSYS (NOT_SUPPORTED)
EXPECT_TRUE(ret == Result::NOT_SUPPORTED || ret == Result::INVALID_ARGUMENTS);
if (!useAfterClose) {
// Note: some legacy effects may return -EINVAL (INVALID_ARGUMENTS),
// more canonical is to return -ENOSYS (NOT_SUPPORTED)
EXPECT_TRUE(ret == Result::NOT_SUPPORTED || ret == Result::INVALID_ARGUMENTS);
} else {
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
ret = effect->enable();
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
ret = effect->disable();
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
}
#if MAJOR_VERSION >= 7
TEST_P(AudioEffectHidlTest, SetDeviceInvalidDeviceAddress) {
description("Verify that invalid device address is rejected by SetDevice");
if (useAfterClose) {
GTEST_SKIP() << "Does not make sense for the useAfterClose case";
}
DeviceAddress device{.deviceType = "random_string"};
Return<Result> ret = effect->setDevice(device);
EXPECT_TRUE(ret.isOk());
@ -482,13 +537,13 @@ TEST_P(AudioEffectHidlTest, SetDevice) {
Return<Result> ret = effect->setDevice(device);
#endif
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
}
TEST_P(AudioEffectHidlTest, SetAndGetVolume) {
description("Verify that SetAndGetVolume method works for an effect");
uint32_t channelCount;
getChannelCount(&channelCount);
ASSERT_NO_FATAL_FAILURE(getChannelCount(&channelCount));
hidl_vec<uint32_t> volumes;
volumes.resize(channelCount);
for (uint32_t i = 0; i < channelCount; ++i) {
@ -498,13 +553,13 @@ TEST_P(AudioEffectHidlTest, SetAndGetVolume) {
Return<void> ret =
effect->setAndGetVolume(volumes, [&](Result r, const hidl_vec<uint32_t>&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
}
TEST_P(AudioEffectHidlTest, VolumeChangeNotification) {
description("Verify that effect accepts VolumeChangeNotification");
uint32_t channelCount;
getChannelCount(&channelCount);
ASSERT_NO_FATAL_FAILURE(getChannelCount(&channelCount));
hidl_vec<uint32_t> volumes;
volumes.resize(channelCount);
for (uint32_t i = 0; i < channelCount; ++i) {
@ -512,25 +567,29 @@ TEST_P(AudioEffectHidlTest, VolumeChangeNotification) {
}
Return<Result> ret = effect->volumeChangeNotification(volumes);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
}
TEST_P(AudioEffectHidlTest, SetAudioMode) {
description("Verify that SetAudioMode works for an effect");
Return<Result> ret = effect->setAudioMode(AudioMode::NORMAL);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
}
TEST_P(AudioEffectHidlTest, SetConfigReverse) {
description("Verify that SetConfigReverse does not crash");
Return<Result> ret = effect->setConfigReverse(EffectConfig(), nullptr, nullptr);
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
#if MAJOR_VERSION >= 7
TEST_P(AudioEffectHidlTest, SetInputDeviceInvalidDeviceAddress) {
description("Verify that invalid device address is rejected by SetInputDevice");
if (useAfterClose) {
GTEST_SKIP() << "Does not make sense for the useAfterClose case";
}
DeviceAddress device{.deviceType = "random_string"};
Return<Result> ret = effect->setInputDevice(device);
EXPECT_TRUE(ret.isOk());
@ -548,11 +607,15 @@ TEST_P(AudioEffectHidlTest, SetInputDevice) {
Return<Result> ret = effect->setInputDevice(device);
#endif
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
#if MAJOR_VERSION >= 7
TEST_P(AudioEffectHidlTest, SetInvalidAudioSource) {
description("Verify that an invalid audio source is rejected by SetAudioSource");
if (useAfterClose) {
GTEST_SKIP() << "Does not make sense for the useAfterClose case";
}
Return<Result> ret = effect->setAudioSource("random_string");
ASSERT_TRUE(ret.isOk());
EXPECT_TRUE(ret == Result::INVALID_ARGUMENTS || ret == Result::NOT_SUPPORTED)
@ -568,12 +631,14 @@ TEST_P(AudioEffectHidlTest, SetAudioSource) {
Return<Result> ret = effect->setAudioSource(toString(xsd::AudioSource::AUDIO_SOURCE_MIC));
#endif
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
TEST_P(AudioEffectHidlTest, Offload) {
description("Verify that calling Offload method does not crash");
Return<Result> ret = effect->offload(EffectOffloadParameter{});
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
TEST_P(AudioEffectHidlTest, PrepareForProcessing) {
@ -582,7 +647,7 @@ TEST_P(AudioEffectHidlTest, PrepareForProcessing) {
Return<void> ret = effect->prepareForProcessing(
[&](Result r, const MQDescriptorSync<Result>&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
}
TEST_P(AudioEffectHidlTest, SetProcessBuffers) {
@ -601,7 +666,7 @@ TEST_P(AudioEffectHidlTest, SetProcessBuffers) {
ASSERT_TRUE(success);
Return<Result> ret2 = effect->setProcessBuffers(buffer, buffer);
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, ret2);
EXPECT_NO_FATAL_FAILURE(checkResult(ret2));
}
TEST_P(AudioEffectHidlTest, Command) {
@ -615,6 +680,7 @@ TEST_P(AudioEffectHidlTest, SetParameter) {
description("Verify that SetParameter does not crash");
Return<Result> ret = effect->setParameter(hidl_vec<uint8_t>(), hidl_vec<uint8_t>());
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(ret));
}
TEST_P(AudioEffectHidlTest, GetParameter) {
@ -630,6 +696,9 @@ TEST_P(AudioEffectHidlTest, GetParameterInvalidMaxReplySize) {
if (!isNewDeviceLaunchingOnTPlus) {
GTEST_SKIP() << "The test only applies to devices launching on T or later";
}
if (useAfterClose) {
GTEST_SKIP() << "Does not make sense for the useAfterClose case";
}
// Use a non-empty parameter to avoid being rejected by any earlier checks.
hidl_vec<uint8_t> parameter;
parameter.resize(16);
@ -647,16 +716,20 @@ TEST_P(AudioEffectHidlTest, GetParameterInvalidMaxReplySize) {
TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeature) {
description("Verify that GetSupportedConfigsForFeature does not crash");
Result retval = Result::OK;
Return<void> ret = effect->getSupportedConfigsForFeature(
0, 0, 0, [&](Result, uint32_t, const hidl_vec<uint8_t>&) {});
0, 0, 0, [&](Result r, uint32_t, const hidl_vec<uint8_t>&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval));
}
TEST_P(AudioEffectHidlTest, GetCurrentConfigForFeature) {
description("Verify that GetCurrentConfigForFeature does not crash");
Return<void> ret =
effect->getCurrentConfigForFeature(0, 0, [&](Result, const hidl_vec<uint8_t>&) {});
Result retval = Result::OK;
Return<void> ret = effect->getCurrentConfigForFeature(
0, 0, [&](Result r, const hidl_vec<uint8_t>&) { retval = r; });
EXPECT_TRUE(ret.isOk());
EXPECT_NO_FATAL_FAILURE(checkResultForUseAfterClose(retval));
}
TEST_P(AudioEffectHidlTest, SetCurrentConfigForFeature) {
@ -671,6 +744,9 @@ TEST_P(AudioEffectHidlTest, GetSupportedConfigsForFeatureInvalidConfigSize) {
if (!isNewDeviceLaunchingOnTPlus) {
GTEST_SKIP() << "The test only applies to devices launching on T or later";
}
if (useAfterClose) {
GTEST_SKIP() << "Does not make sense for the useAfterClose case";
}
// Use very large size to ensure that the service does not crash.
const uint32_t veryLargeConfigSize = std::numeric_limits<uint32_t>::max() - 100;
Result retval = Result::OK;
@ -687,6 +763,9 @@ TEST_P(AudioEffectHidlTest, GetCurrentConfigForFeatureInvalidConfigSize) {
if (!isNewDeviceLaunchingOnTPlus) {
GTEST_SKIP() << "The test only applies to devices launching on T or later";
}
if (useAfterClose) {
GTEST_SKIP() << "Does not make sense for the useAfterClose case";
}
// Use very large size to ensure that the service does not crash.
const uint32_t veryLargeConfigSize = std::numeric_limits<uint32_t>::max() - 100;
Result retval = Result::OK;
@ -729,7 +808,8 @@ void EqualizerAudioEffectHidlTest::getNumBands(uint16_t* numBands) {
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) *numBands = 1;
}
void EqualizerAudioEffectHidlTest::getLevelRange(int16_t* minLevel, int16_t* maxLevel) {
@ -742,7 +822,11 @@ void EqualizerAudioEffectHidlTest::getLevelRange(int16_t* minLevel, int16_t* max
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) {
*minLevel = 0;
*maxLevel = 255;
}
}
void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, uint32_t* minFreq,
@ -757,7 +841,7 @@ void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, uint32_t
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
ret = equalizer->getBandCenterFrequency(band, [&](Result r, uint32_t center) {
retval = r;
if (retval == Result::OK) {
@ -765,7 +849,12 @@ void EqualizerAudioEffectHidlTest::getBandFrequencyRange(uint16_t band, uint32_t
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) {
*minFreq = 20;
*centerFreq = 10000;
*maxFreq = 20000;
}
}
void EqualizerAudioEffectHidlTest::getPresetCount(size_t* count) {
@ -777,37 +866,38 @@ void EqualizerAudioEffectHidlTest::getPresetCount(size_t* count) {
}
});
ASSERT_TRUE(ret.isOk());
ASSERT_EQ(Result::OK, retval);
ASSERT_NO_FATAL_FAILURE(checkResult(retval));
if (useAfterClose) *count = 1;
}
TEST_P(EqualizerAudioEffectHidlTest, GetNumBands) {
description("Verify that Equalizer effect reports at least one band");
uint16_t numBands = 0;
getNumBands(&numBands);
ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands));
EXPECT_GT(numBands, 0);
}
TEST_P(EqualizerAudioEffectHidlTest, GetLevelRange) {
description("Verify that Equalizer effect reports adequate band level range");
int16_t minLevel = 0x7fff, maxLevel = 0;
getLevelRange(&minLevel, &maxLevel);
ASSERT_NO_FATAL_FAILURE(getLevelRange(&minLevel, &maxLevel));
EXPECT_GT(maxLevel, minLevel);
}
TEST_P(EqualizerAudioEffectHidlTest, GetSetBandLevel) {
description("Verify that manipulating band levels works for Equalizer effect");
uint16_t numBands = 0;
getNumBands(&numBands);
ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands));
ASSERT_GT(numBands, 0);
int16_t levels[3]{0x7fff, 0, 0};
getLevelRange(&levels[0], &levels[2]);
ASSERT_NO_FATAL_FAILURE(getLevelRange(&levels[0], &levels[2]));
ASSERT_GT(levels[2], levels[0]);
levels[1] = (levels[2] + levels[0]) / 2;
for (uint16_t i = 0; i < numBands; ++i) {
for (size_t j = 0; j < ARRAY_SIZE(levels); ++j) {
Return<Result> ret = equalizer->setBandLevel(i, levels[j]);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
Result retval = Result::NOT_INITIALIZED;
int16_t actualLevel;
Return<void> ret2 = equalizer->getBandLevel(i, [&](Result r, int16_t l) {
@ -817,8 +907,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetBandLevel) {
}
});
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_EQ(levels[j], actualLevel);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (!useAfterClose) {
EXPECT_EQ(levels[j], actualLevel);
}
}
}
}
@ -826,11 +918,11 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetBandLevel) {
TEST_P(EqualizerAudioEffectHidlTest, GetBandCenterFrequencyAndRange) {
description("Verify that Equalizer effect reports adequate band frequency range");
uint16_t numBands = 0;
getNumBands(&numBands);
ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands));
ASSERT_GT(numBands, 0);
for (uint16_t i = 0; i < numBands; ++i) {
uint32_t minFreq = 0xffffffff, centerFreq = 0xffffffff, maxFreq = 0xffffffff;
getBandFrequencyRange(i, &minFreq, &centerFreq, &maxFreq);
ASSERT_NO_FATAL_FAILURE(getBandFrequencyRange(i, &minFreq, &centerFreq, &maxFreq));
// Note: NXP legacy implementation reports "1" as upper bound for last band,
// so this check fails.
EXPECT_GE(maxFreq, centerFreq);
@ -841,7 +933,7 @@ TEST_P(EqualizerAudioEffectHidlTest, GetBandCenterFrequencyAndRange) {
TEST_P(EqualizerAudioEffectHidlTest, GetBandForFrequency) {
description("Verify that Equalizer effect supports GetBandForFrequency correctly");
uint16_t numBands = 0;
getNumBands(&numBands);
ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands));
ASSERT_GT(numBands, 0);
for (uint16_t i = 0; i < numBands; ++i) {
uint32_t freqs[3]{0, 0, 0};
@ -861,8 +953,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetBandForFrequency) {
}
});
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_EQ(i, actualBand) << "Frequency: " << freqs[j];
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (!useAfterClose) {
EXPECT_EQ(i, actualBand) << "Frequency: " << freqs[j];
}
}
}
}
@ -870,19 +964,19 @@ TEST_P(EqualizerAudioEffectHidlTest, GetBandForFrequency) {
TEST_P(EqualizerAudioEffectHidlTest, GetPresetNames) {
description("Verify that Equalizer effect reports at least one preset");
size_t presetCount;
getPresetCount(&presetCount);
ASSERT_NO_FATAL_FAILURE(getPresetCount(&presetCount));
EXPECT_GT(presetCount, 0u);
}
TEST_P(EqualizerAudioEffectHidlTest, GetSetCurrentPreset) {
description("Verify that manipulating the current preset for Equalizer effect");
size_t presetCount;
getPresetCount(&presetCount);
ASSERT_NO_FATAL_FAILURE(getPresetCount(&presetCount));
ASSERT_GT(presetCount, 0u);
for (uint16_t i = 0; i < presetCount; ++i) {
Return<Result> ret = equalizer->setCurrentPreset(i);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
Result retval = Result::NOT_INITIALIZED;
uint16_t actualPreset = 0xffff;
Return<void> ret2 = equalizer->getCurrentPreset([&](Result r, uint16_t p) {
@ -892,8 +986,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetCurrentPreset) {
}
});
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_EQ(i, actualPreset);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (!useAfterClose) {
EXPECT_EQ(i, actualPreset);
}
}
}
@ -904,7 +1000,7 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) {
using AllProperties =
::android::hardware::audio::effect::CPP_VERSION::IEqualizerEffect::AllProperties;
uint16_t numBands = 0;
getNumBands(&numBands);
ASSERT_NO_FATAL_FAILURE(getNumBands(&numBands));
ASSERT_GT(numBands, 0);
AllProperties props;
props.bandLevels.resize(numBands);
@ -919,7 +1015,7 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) {
props.curPreset = -1;
Return<Result> ret = equalizer->setAllProperties(props);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
Return<void> ret2 = equalizer->getAllProperties([&](Result r, AllProperties p) {
retval = r;
if (retval == Result::OK) {
@ -927,14 +1023,16 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) {
}
});
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_EQ(props.bandLevels, actualProps.bandLevels);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (!useAfterClose) {
EXPECT_EQ(props.bandLevels, actualProps.bandLevels);
}
// Verify setting of the current preset via properties.
props.curPreset = 0; // Assuming there is at least one preset.
ret = equalizer->setAllProperties(props);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
ret2 = equalizer->getAllProperties([&](Result r, AllProperties p) {
retval = r;
if (retval == Result::OK) {
@ -942,8 +1040,10 @@ TEST_P(EqualizerAudioEffectHidlTest, GetSetAllProperties) {
}
});
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_EQ(props.curPreset, actualProps.curPreset);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (!useAfterClose) {
EXPECT_EQ(props.curPreset, actualProps.curPreset);
}
}
// The main test class for Equalizer Audio Effect HIDL HAL.
@ -971,7 +1071,7 @@ TEST_P(LoudnessEnhancerAudioEffectHidlTest, GetSetTargetGain) {
const int32_t gain = 100;
Return<Result> ret = enhancer->setTargetGain(gain);
EXPECT_TRUE(ret.isOk());
EXPECT_EQ(Result::OK, ret);
EXPECT_NO_FATAL_FAILURE(checkResult(ret));
int32_t actualGain = 0;
Result retval;
Return<void> ret2 = enhancer->getTargetGain([&](Result r, int32_t g) {
@ -981,8 +1081,10 @@ TEST_P(LoudnessEnhancerAudioEffectHidlTest, GetSetTargetGain) {
}
});
EXPECT_TRUE(ret2.isOk());
EXPECT_EQ(Result::OK, retval);
EXPECT_EQ(gain, actualGain);
EXPECT_NO_FATAL_FAILURE(checkResult(retval));
if (!useAfterClose) {
EXPECT_EQ(gain, actualGain);
}
}
INSTANTIATE_TEST_SUITE_P(EffectsFactory, AudioEffectsFactoryHidlTest,
@ -993,25 +1095,29 @@ INSTANTIATE_TEST_SUITE_P(
Equalizer_IEffect, AudioEffectHidlTest,
::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames(
IEffectsFactory::descriptor)),
::testing::Values(EQUALIZER_EFFECT_TYPE)),
::testing::Values(EQUALIZER_EFFECT_TYPE),
::testing::Values(false, true) /*useAfterClose*/),
EffectParameterToString);
INSTANTIATE_TEST_SUITE_P(
LoudnessEnhancer_IEffect, AudioEffectHidlTest,
::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames(
IEffectsFactory::descriptor)),
::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE)),
::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE),
::testing::Values(false, true) /*useAfterClose*/),
EffectParameterToString);
INSTANTIATE_TEST_SUITE_P(
Equalizer, EqualizerAudioEffectHidlTest,
::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames(
IEffectsFactory::descriptor)),
::testing::Values(EQUALIZER_EFFECT_TYPE)),
::testing::Values(EQUALIZER_EFFECT_TYPE),
::testing::Values(false, true) /*useAfterClose*/),
EffectParameterToString);
INSTANTIATE_TEST_SUITE_P(
LoudnessEnhancer, LoudnessEnhancerAudioEffectHidlTest,
::testing::Combine(::testing::ValuesIn(::android::hardware::getAllHalInstanceNames(
IEffectsFactory::descriptor)),
::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE)),
::testing::Values(LOUDNESS_ENHANCER_EFFECT_TYPE),
::testing::Values(false, true) /*useAfterClose*/),
EffectParameterToString);
// When the VTS test runs on a device lacking the corresponding HAL version the parameter
// list is empty, this isn't a problem.

View file

@ -30,7 +30,7 @@ aidl_interface {
stability: "vintf",
imports: [
"android.hardware.common-V2",
"android.hardware.graphics.common-V4",
"android.hardware.graphics.common-V5",
],
backend: {
java: {
@ -53,14 +53,14 @@ aidl_interface {
version: "1",
imports: [
"android.hardware.common-V2",
"android.hardware.graphics.common-V4",
"android.hardware.graphics.common-V5",
],
},
{
version: "2",
imports: [
"android.hardware.common-V2",
"android.hardware.graphics.common-V4",
"android.hardware.graphics.common-V5",
],
},

View file

@ -21,24 +21,11 @@ package {
default_applicable_licenses: ["hardware_interfaces_license"],
}
cc_binary {
name: "android.hardware.automotive.evs-aidl-default-service",
cc_defaults {
name: "android.hardware.automotive.evs-aidl-default-service-default",
defaults: ["EvsHalDefaults"],
vintf_fragments: ["manifest_evs-default-service.xml"],
init_rc: ["evs-default-service.rc"],
vendor: true,
relative_install_path: "hw",
cflags: [
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
"-Wall",
"-Wextra",
"-Werror",
"-Wthread-safety",
],
srcs: [
":libgui_frame_event_aidl",
"src/*.cpp",
header_libs: [
"libstagefright_headers",
],
shared_libs: [
"android.hardware.graphics.bufferqueue@1.0",
@ -46,28 +33,63 @@ cc_binary {
"android.hidl.token@1.0-utils",
"libEGL",
"libGLESv2",
"libbase",
"libbinder_ndk",
"libbufferqueueconverter",
"libcamera_metadata",
"libhardware_legacy",
"libhidlbase",
"liblog",
"libmediandk",
"libnativewindow",
"libtinyxml2",
"libui",
"libutils",
"libyuv",
],
static_libs: [
"android.frameworks.automotive.display-V1-ndk",
}
cc_library {
name: "android.hardware.automotive.evs-aidl-default-service-lib",
defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
vendor: true,
cflags: [
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
],
srcs: [
":libgui_frame_event_aidl",
"src/*.cpp",
],
exclude_srcs: ["src/service.cpp"],
whole_static_libs: [
"android.frameworks.automotive.display-V2-ndk",
"android.hardware.automotive.evs-V2-ndk",
"android.hardware.common-V2-ndk",
"libaidlcommonsupport",
"libcutils",
],
header_libs: [
"libgui_aidl_headers",
],
local_include_dirs: ["include"],
include_dirs: ["frameworks/native/include/"],
export_include_dirs: ["include"],
}
cc_binary {
name: "android.hardware.automotive.evs-aidl-default-service",
defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
vintf_fragments: ["manifest_evs-default-service.xml"],
init_rc: ["evs-default-service.rc"],
vendor: true,
relative_install_path: "hw",
cflags: [
"-DGL_GLEXT_PROTOTYPES",
"-DEGL_EGLEXT_PROTOTYPES",
],
srcs: ["src/service.cpp"],
static_libs: [
"android.hardware.automotive.evs-aidl-default-service-lib",
],
include_dirs: ["frameworks/native/include/"],
required: ["evs_mock_hal_configuration.xml"],
}
@ -77,3 +99,31 @@ prebuilt_etc {
src: "resources/evs_mock_configuration.xml",
sub_dir: "automotive/evs",
}
cc_test {
name: "android.hardware.automotive.evs-aidl-default-service_cam_buffer_test",
defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
vendor: true,
srcs: ["tests/EvsCameraBufferTest.cpp"],
static_libs: [
"android.hardware.automotive.evs-aidl-default-service-lib",
"libgmock",
],
test_suites: [
"general-tests",
],
}
cc_test {
name: "android.hardware.automotive.evs-aidl-default-service_cam_state_test",
defaults: ["android.hardware.automotive.evs-aidl-default-service-default"],
vendor: true,
srcs: ["tests/EvsCameraStateTest.cpp"],
static_libs: [
"android.hardware.automotive.evs-aidl-default-service-lib",
"libgmock",
],
test_suites: [
"general-tests",
],
}

View file

@ -25,8 +25,10 @@
#include <tinyxml2.h>
#include <limits>
#include <string>
#include <string_view>
#include <type_traits>
#include <unordered_map>
#include <unordered_set>
#include <vector>
@ -54,6 +56,15 @@ class ConfigManager final {
/* Camera device's capabilities and metadata */
class CameraInfo {
public:
enum class DeviceType : std::int32_t {
NONE = 0,
MOCK = 1,
V4L2 = 2,
VIDEO = 3,
UNKNOWN = std::numeric_limits<std::underlying_type_t<DeviceType>>::max(),
};
CameraInfo() : characteristics(nullptr) {}
virtual ~CameraInfo();
@ -69,6 +80,10 @@ class ConfigManager final {
return characteristics != nullptr;
}
static DeviceType deviceTypeFromSV(const std::string_view sv);
DeviceType deviceType{DeviceType::NONE};
/*
* List of supported controls that the primary client can program.
* Paraemters are stored with its valid range

View file

@ -0,0 +1,20 @@
/*
* Copyright (C) 2023 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 "EvsMockCamera.h"
#include "EvsVideoEmulatedCamera.h"

View file

@ -0,0 +1,144 @@
/*
* Copyright (C) 2023 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 "EvsCameraBase.h"
#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
#include <cutils/native_handle.h>
#include <cstddef>
#include <mutex>
#include <utility>
#include <vector>
namespace aidl::android::hardware::automotive::evs::implementation {
class EvsCamera : public EvsCameraBase {
private:
using Base = EvsCameraBase;
using Self = EvsCamera;
public:
using Base::Base;
~EvsCamera() override;
// Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;
ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
int32_t* _aidl_return) override;
ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
ndk::ScopedAStatus startVideoStream(
const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
ndk::ScopedAStatus stopVideoStream() override;
ndk::ScopedAStatus pauseVideoStream() override;
ndk::ScopedAStatus resumeVideoStream() override;
protected:
virtual ::android::status_t allocateOneFrame(buffer_handle_t* handle) = 0;
virtual void freeOneFrame(const buffer_handle_t handle);
virtual bool preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck);
virtual bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) = 0;
virtual bool postVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck);
virtual bool preVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck);
virtual bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) = 0;
virtual bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck);
void shutdown() override;
void closeAllBuffers_unsafe();
// Returns (ID, handle) if succeeds. (kInvalidBufferID, nullptr) otherwise.
[[nodiscard]] std::pair<std::size_t, buffer_handle_t> useBuffer_unsafe();
void returnBuffer_unsafe(const std::size_t id);
bool increaseAvailableFrames_unsafe(const buffer_handle_t handle);
bool decreaseAvailableFrames_unsafe();
bool setAvailableFrames_unsafe(const std::size_t bufferCount);
void swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2);
struct BufferRecord {
BufferRecord() = default;
BufferRecord(const BufferRecord&) = default;
BufferRecord(BufferRecord&&) = default;
BufferRecord& operator=(const BufferRecord&) = default;
BufferRecord& operator=(BufferRecord&&) = default;
~BufferRecord() = default;
explicit BufferRecord(buffer_handle_t h) : handle(h) {}
buffer_handle_t handle{nullptr};
bool inUse{false};
};
enum class StreamState {
STOPPED = 0,
RUNNING = 1,
STOPPING = 2,
DEAD = 3,
};
StreamState mStreamState{StreamState::STOPPED};
std::mutex mMutex;
// Graphics buffers to transfer images, always in the order of:
// In use buffers ... available buffers ... unavailable (unallocated) buffers.
std::vector<BufferRecord> mBuffers;
// Double-mapping between buffer position and ID.
std::vector<std::size_t> mBufferPosToId;
std::vector<std::size_t> mBufferIdToPos;
std::size_t mAvailableFrames{0};
std::size_t mFramesInUse{0};
// We use all 1's as a reserved invalid buffer ID.
static constexpr std::size_t kInvalidBufferID = ~static_cast<std::size_t>(0);
public:
static bool IsBufferIDValid(const std::size_t bufferId) { return ~bufferId; }
};
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 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 <aidl/android/hardware/automotive/evs/BnEvsCamera.h>
namespace aidl::android::hardware::automotive::evs::implementation {
class EvsCameraBase : public evs::BnEvsCamera {
private:
using Base = evs::BnEvsCamera;
using Self = EvsCameraBase;
public:
using Base::Base;
~EvsCameraBase() override = default;
virtual void shutdown() = 0;
protected:
// This is used for the derived classes and it prevents constructors from direct access
// while it allows this class to be instantiated via ndk::SharedRefBase::make<>.
struct Sigil {
explicit Sigil() = default;
};
};
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -17,8 +17,8 @@
#pragma once
#include "ConfigManager.h"
#include "EvsCameraBase.h"
#include "EvsGlDisplay.h"
#include "EvsMockCamera.h"
#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
#include <aidl/android/hardware/automotive/evs/BnEvsEnumerator.h>
@ -27,6 +27,7 @@
#include <aidl/android/hardware/automotive/evs/IEvsCamera.h>
#include <aidl/android/hardware/automotive/evs/IEvsEnumeratorStatusCallback.h>
#include <aidl/android/hardware/automotive/evs/Stream.h>
#include <android-base/thread_annotations.h>
#include <utils/Thread.h>
#include <atomic>
@ -72,7 +73,7 @@ class EvsEnumerator final : public ::aidl::android::hardware::automotive::evs::B
private:
struct CameraRecord {
evs::CameraDesc desc;
std::weak_ptr<EvsMockCamera> activeInstance;
std::weak_ptr<EvsCameraBase> activeInstance;
CameraRecord(const char* cameraId) : desc() { desc.id = cameraId; }
};

View file

@ -23,6 +23,7 @@
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
#include <aidl/android/hardware/automotive/evs/DisplayDesc.h>
#include <aidl/android/hardware/automotive/evs/DisplayState.h>
#include <android-base/thread_annotations.h>
#include <thread>

View file

@ -17,36 +17,36 @@
#pragma once
#include "ConfigManager.h"
#include "EvsCamera.h"
#include <aidl/android/hardware/automotive/evs/BnEvsCamera.h>
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
#include <aidl/android/hardware/automotive/evs/CameraParam.h>
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
#include <aidl/android/hardware/automotive/evs/Stream.h>
// #include <android-base/result.h>
#include <android/hardware_buffer.h>
#include <ui/GraphicBuffer.h>
#include <functional>
#include <cstdint>
#include <memory>
#include <thread>
#include <unordered_map>
#include <vector>
namespace aidl::android::hardware::automotive::evs::implementation {
class EvsMockCamera : public evs::BnEvsCamera {
// This prevents constructors from direct access while it allows this class to
// be instantiated via ndk::SharedRefBase::make<>.
class EvsMockCamera : public EvsCamera {
private:
struct Sigil {
explicit Sigil() = default;
};
using Base = EvsCamera;
public:
EvsMockCamera(Sigil sigil, const char* deviceName,
std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
EvsMockCamera(const EvsMockCamera&) = delete;
EvsMockCamera& operator=(const EvsMockCamera&) = delete;
// Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
ndk::ScopedAStatus doneWithFrame(const std::vector<evs::BufferDesc>& buffers) override;
ndk::ScopedAStatus forcePrimaryClient(
const std::shared_ptr<evs::IEvsDisplay>& display) override;
ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override;
@ -58,47 +58,37 @@ class EvsMockCamera : public evs::BnEvsCamera {
ndk::ScopedAStatus getParameterList(std::vector<evs::CameraParam>* _aidl_return) override;
ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId,
evs::CameraDesc* _aidl_return) override;
ndk::ScopedAStatus importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
int32_t* _aidl_return) override;
ndk::ScopedAStatus pauseVideoStream() override;
ndk::ScopedAStatus resumeVideoStream() override;
ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier,
const std::vector<uint8_t>& opaqueValue) override;
ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value,
std::vector<int32_t>* effectiveValue) override;
ndk::ScopedAStatus setPrimaryClient() override;
ndk::ScopedAStatus setMaxFramesInFlight(int32_t bufferCount) override;
ndk::ScopedAStatus startVideoStream(
const std::shared_ptr<evs::IEvsCameraStream>& receiver) override;
ndk::ScopedAStatus stopVideoStream() override;
ndk::ScopedAStatus unsetPrimaryClient() override;
const evs::CameraDesc& getDesc() { return mDescription; }
static std::shared_ptr<EvsMockCamera> Create(const char* deviceName);
static std::shared_ptr<EvsMockCamera> Create(
const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
const evs::Stream* streamCfg = nullptr);
EvsMockCamera(const EvsMockCamera&) = delete;
EvsMockCamera& operator=(const EvsMockCamera&) = delete;
virtual ~EvsMockCamera() override;
void shutdown();
const evs::CameraDesc& getDesc() { return mDescription; }
// Constructors
EvsMockCamera(Sigil sigil, const char* deviceName,
std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
private:
// These three functions are expected to be called while mAccessLock is held
bool setAvailableFrames_Locked(unsigned bufferCount);
unsigned increaseAvailableFrames_Locked(unsigned numToAdd);
unsigned decreaseAvailableFrames_Locked(unsigned numToRemove);
void generateFrames();
void fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_Desc* pDesc);
void returnBufferLocked(const uint32_t bufferId);
ndk::ScopedAStatus stopVideoStream_impl();
::android::status_t allocateOneFrame(buffer_handle_t* handle) override;
bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override;
bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override;
bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override;
void initializeParameters();
CameraDesc mDescription = {}; // The properties of this camera
@ -119,28 +109,6 @@ class EvsMockCamera : public evs::BnEvsCamera {
// Bytes per line in the buffers
uint32_t mStride = 0;
struct BufferRecord {
buffer_handle_t handle;
bool inUse;
explicit BufferRecord(buffer_handle_t h) : handle(h), inUse(false){};
};
std::vector<BufferRecord> mBuffers; // Graphics buffers to transfer images
unsigned mFramesAllowed; // How many buffers are we currently using
unsigned mFramesInUse; // How many buffers are currently outstanding
enum StreamStateValues {
STOPPED,
RUNNING,
STOPPING,
DEAD,
};
StreamStateValues mStreamState;
// Synchronization necessary to deconflict mCaptureThread from the main service thread
std::mutex mAccessLock;
// Static camera module information
std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
@ -160,7 +128,6 @@ class EvsMockCamera : public evs::BnEvsCamera {
int32_t value;
};
std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
void initializeParameters();
};
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -0,0 +1,161 @@
/*
* Copyright (C) 2023 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 "ConfigManager.h"
#include "EvsCamera.h"
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
#include <aidl/android/hardware/automotive/evs/CameraDesc.h>
#include <aidl/android/hardware/automotive/evs/CameraParam.h>
#include <aidl/android/hardware/automotive/evs/IEvsCameraStream.h>
#include <aidl/android/hardware/automotive/evs/IEvsDisplay.h>
#include <aidl/android/hardware/automotive/evs/ParameterRange.h>
#include <aidl/android/hardware/automotive/evs/Stream.h>
#include <media/NdkMediaExtractor.h>
#include <ui/GraphicBuffer.h>
#include <cstdint>
#include <memory>
#include <thread>
#include <unordered_map>
#include <vector>
namespace aidl::android::hardware::automotive::evs::implementation {
class EvsVideoEmulatedCamera : public EvsCamera {
private:
using Base = EvsCamera;
public:
EvsVideoEmulatedCamera(Sigil sigil, const char* deviceName,
std::unique_ptr<ConfigManager::CameraInfo>& camInfo);
~EvsVideoEmulatedCamera() override = default;
// Methods from ::android::hardware::automotive::evs::IEvsCamera follow.
ndk::ScopedAStatus forcePrimaryClient(
const std::shared_ptr<evs::IEvsDisplay>& display) override;
ndk::ScopedAStatus getCameraInfo(evs::CameraDesc* _aidl_return) override;
ndk::ScopedAStatus getExtendedInfo(int32_t opaqueIdentifier,
std::vector<uint8_t>* value) override;
ndk::ScopedAStatus getIntParameter(evs::CameraParam id, std::vector<int32_t>* value) override;
ndk::ScopedAStatus getIntParameterRange(evs::CameraParam id,
evs::ParameterRange* _aidl_return) override;
ndk::ScopedAStatus getParameterList(std::vector<evs::CameraParam>* _aidl_return) override;
ndk::ScopedAStatus getPhysicalCameraInfo(const std::string& deviceId,
evs::CameraDesc* _aidl_return) override;
ndk::ScopedAStatus setExtendedInfo(int32_t opaqueIdentifier,
const std::vector<uint8_t>& opaqueValue) override;
ndk::ScopedAStatus setIntParameter(evs::CameraParam id, int32_t value,
std::vector<int32_t>* effectiveValue) override;
ndk::ScopedAStatus setPrimaryClient() override;
ndk::ScopedAStatus unsetPrimaryClient() override;
// Methods from EvsCameraBase follow.
void shutdown() override;
const evs::CameraDesc& getDesc() { return mDescription; }
static std::shared_ptr<EvsVideoEmulatedCamera> Create(const char* deviceName);
static std::shared_ptr<EvsVideoEmulatedCamera> Create(
const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
const evs::Stream* streamCfg = nullptr);
private:
// For the camera parameters.
struct CameraParameterDesc {
CameraParameterDesc(int min = 0, int max = 0, int step = 0, int value = 0) {
this->range.min = min;
this->range.max = max;
this->range.step = step;
this->value = value;
}
ParameterRange range;
int32_t value;
};
bool initialize();
void generateFrames();
void renderOneFrame();
void initializeParameters();
void onCodecInputAvailable(const int32_t index);
void onCodecOutputAvailable(const int32_t index, const AMediaCodecBufferInfo& info);
::android::status_t allocateOneFrame(buffer_handle_t* handle) override;
bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override;
bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override;
bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override;
// The properties of this camera.
CameraDesc mDescription = {};
std::thread mCaptureThread;
// The callback used to deliver each frame
std::shared_ptr<evs::IEvsCameraStream> mStream;
std::string mVideoFileName;
// Media decoder resources - Owned by mDecoderThead when thread is running.
int mVideoFd = 0;
struct AMediaExtractorDeleter {
void operator()(AMediaExtractor* extractor) const { AMediaExtractor_delete(extractor); }
};
struct AMediaCodecDeleter {
void operator()(AMediaCodec* codec) const { AMediaCodec_delete(codec); }
};
std::unique_ptr<AMediaExtractor, AMediaExtractorDeleter> mVideoExtractor;
std::unique_ptr<AMediaCodec, AMediaCodecDeleter> mVideoCodec;
// Horizontal pixel count in the buffers
int32_t mWidth = 0;
// Vertical pixel count in the buffers
int32_t mHeight = 0;
// Values from android_pixel_format_t
uint32_t mFormat = 0;
// Values from from Gralloc.h
uint64_t mUsage = 0;
// Bytes per line in the buffers
uint32_t mStride = 0;
// Camera parameters.
std::unordered_map<CameraParam, std::shared_ptr<CameraParameterDesc>> mParams;
// Static camera module information
std::unique_ptr<ConfigManager::CameraInfo>& mCameraInfo;
// For the extended info
std::unordered_map<uint32_t, std::vector<uint8_t>> mExtInfo;
};
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -25,7 +25,7 @@
#include <aidl/android/frameworks/automotive/display/ICarDisplayProxy.h>
#include <aidl/android/hardware/automotive/evs/BufferDesc.h>
#include <android-base/logging.h>
#include <bufferqueueconverter/BufferQueueConverter.h>
#include <cutils/native_handle.h>
namespace aidl::android::hardware::automotive::evs::implementation {
@ -33,7 +33,6 @@ namespace automotivedisplay = ::aidl::android::frameworks::automotive::display;
class GlWrapper {
public:
GlWrapper() : mSurfaceHolder(::android::SurfaceHolderUniquePtr(nullptr, nullptr)) {}
bool initialize(const std::shared_ptr<automotivedisplay::ICarDisplayProxy>& svc,
uint64_t displayId);
void shutdown();
@ -53,9 +52,6 @@ class GlWrapper {
unsigned getHeight() { return mHeight; };
private:
::android::sp<::android::hardware::graphics::bufferqueue::V2_0::IGraphicBufferProducer>
mGfxBufferProducer;
EGLDisplay mDisplay;
EGLSurface mSurface;
EGLContext mContext;
@ -71,9 +67,6 @@ class GlWrapper {
// Opaque handle for a native hardware buffer defined in
// frameworks/native/opengl/include/EGL/eglplatform.h
ANativeWindow* mWindow;
// Pointer to a Surface wrapper.
::android::SurfaceHolderUniquePtr mSurfaceHolder;
};
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -40,6 +40,18 @@ std::string_view ConfigManager::sConfigDefaultPath =
std::string_view ConfigManager::sConfigOverridePath =
"/vendor/etc/automotive/evs/evs_configuration_override.xml";
ConfigManager::CameraInfo::DeviceType ConfigManager::CameraInfo::deviceTypeFromSV(
const std::string_view sv) {
using namespace std::string_view_literals;
static const std::unordered_map<std::string_view, DeviceType> nameToType = {
{"mock"sv, DeviceType::MOCK},
{"v4l2"sv, DeviceType::V4L2},
{"video"sv, DeviceType::VIDEO},
};
const auto search = nameToType.find(sv);
return search == nameToType.end() ? DeviceType::UNKNOWN : search->second;
}
void ConfigManager::printElementNames(const XMLElement* rootElem, const std::string& prefix) const {
const XMLElement* curElem = rootElem;
@ -128,6 +140,10 @@ bool ConfigManager::readCameraDeviceInfo(CameraInfo* aCamera, const XMLElement*
return false;
}
if (const auto typeAttr = aDeviceElem->FindAttribute("type")) {
aCamera->deviceType = CameraInfo::deviceTypeFromSV(typeAttr->Value());
}
/* size information to allocate camera_metadata_t */
size_t totalEntries = 0;
size_t totalDataSize = 0;

View file

@ -0,0 +1,378 @@
/*
* Copyright (C) 2023 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 "EvsCamera.h"
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android/hardware_buffer.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <cstddef>
#include <mutex>
namespace aidl::android::hardware::automotive::evs::implementation {
// Arbitrary limit on number of graphics buffers allowed to be allocated
// Safeguards against unreasonable resource consumption and provides a testable limit
constexpr std::size_t kMaxBuffersInFlight = 100;
// Minimum number of buffers to run a video stream
constexpr int kMinimumBuffersInFlight = 1;
EvsCamera::~EvsCamera() {
shutdown();
}
ndk::ScopedAStatus EvsCamera::doneWithFrame(const std::vector<evs::BufferDesc>& buffers) {
std::lock_guard lck(mMutex);
for (const auto& desc : buffers) {
returnBuffer_unsafe(desc.bufferId);
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsCamera::importExternalBuffers(const std::vector<evs::BufferDesc>& buffers,
int32_t* _aidl_return) {
if (buffers.empty()) {
LOG(DEBUG) << __func__
<< ": Ignoring a request to import external buffers with an empty list.";
return ndk::ScopedAStatus::ok();
}
static auto& mapper = ::android::GraphicBufferMapper::get();
std::lock_guard lck(mMutex);
std::size_t numBuffersToAdd = std::min(buffers.size(), kMaxBuffersInFlight - mAvailableFrames);
if (numBuffersToAdd == 0) {
LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
<< kMaxBuffersInFlight << "). Stop importing.";
return ndk::ScopedAStatus::ok();
} else if (numBuffersToAdd < buffers.size()) {
LOG(WARNING) << "Exceeds the limit on the number of buffers. Only " << numBuffersToAdd
<< " buffers will be imported. " << buffers.size() << " are asked.";
}
const size_t before = mAvailableFrames;
for (std::size_t idx = 0; idx < numBuffersToAdd; ++idx) {
auto& buffer = buffers[idx];
const AHardwareBuffer_Desc* pDesc =
reinterpret_cast<const AHardwareBuffer_Desc*>(&buffer.buffer.description);
buffer_handle_t handleToImport = ::android::dupFromAidl(buffer.buffer.handle);
buffer_handle_t handleToStore = nullptr;
if (handleToImport == nullptr) {
LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer "
<< buffer.bufferId;
continue;
}
::android::status_t result =
mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
if (result != ::android::NO_ERROR || handleToStore == nullptr ||
!increaseAvailableFrames_unsafe(handleToStore)) {
LOG(WARNING) << "Failed to import a buffer " << buffer.bufferId;
}
}
*_aidl_return = mAvailableFrames - before;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsCamera::setMaxFramesInFlight(int32_t bufferCount) {
std::lock_guard lock(mMutex);
if (bufferCount < 1) {
LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::INVALID_ARG));
}
if (!setAvailableFrames_unsafe(bufferCount)) {
LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
}
return ndk::ScopedAStatus::ok();
}
void EvsCamera::freeOneFrame(const buffer_handle_t handle) {
static auto& alloc = ::android::GraphicBufferAllocator::get();
alloc.free(handle);
}
bool EvsCamera::preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& /* lck */) {
if (!receiver) {
LOG(ERROR) << __func__ << ": Null receiver.";
status = ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::INVALID_ARG));
return false;
}
// If we've been displaced by another owner of the camera, then we can't do anything else
if (mStreamState == StreamState::DEAD) {
LOG(ERROR) << __func__ << ": Ignoring when camera has been lost.";
status = ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::OWNERSHIP_LOST));
return false;
}
if (mStreamState != StreamState::STOPPED) {
LOG(ERROR) << __func__ << ": Ignoring when a stream is already running.";
status = ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
return false;
}
// If the client never indicated otherwise, configure ourselves for a single streaming buffer
if (mAvailableFrames < kMinimumBuffersInFlight &&
!setAvailableFrames_unsafe(kMinimumBuffersInFlight)) {
LOG(ERROR) << __func__ << "Failed to because we could not get a graphics buffer.";
status = ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
return false;
}
mStreamState = StreamState::RUNNING;
return true;
}
bool EvsCamera::postVideoStreamStart_locked(
const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
ndk::ScopedAStatus& /* status */, std::unique_lock<std::mutex>& /* lck */) {
return true;
}
bool EvsCamera::preVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& /* lck */) {
if (mStreamState != StreamState::RUNNING) {
// Terminate the stop process because a stream is not running.
status = ndk::ScopedAStatus::ok();
return false;
}
mStreamState = StreamState::STOPPING;
return true;
}
bool EvsCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& /* lck */) {
mStreamState = StreamState::STOPPED;
return true;
}
ndk::ScopedAStatus EvsCamera::startVideoStream(
const std::shared_ptr<evs::IEvsCameraStream>& receiver) {
bool needShutdown = false;
auto status = ndk::ScopedAStatus::ok();
{
std::unique_lock lck(mMutex);
if (!preVideoStreamStart_locked(receiver, status, lck)) {
return status;
}
if ((!startVideoStreamImpl_locked(receiver, status, lck) ||
!postVideoStreamStart_locked(receiver, status, lck)) &&
!status.isOk()) {
needShutdown = true;
}
}
if (needShutdown) {
shutdown();
}
return status;
}
ndk::ScopedAStatus EvsCamera::stopVideoStream() {
bool needShutdown = false;
auto status = ndk::ScopedAStatus::ok();
{
std::unique_lock lck(mMutex);
if ((!preVideoStreamStop_locked(status, lck) || !stopVideoStreamImpl_locked(status, lck) ||
!postVideoStreamStop_locked(status, lck)) &&
!status.isOk()) {
needShutdown = true;
}
}
if (needShutdown) {
shutdown();
}
return status;
}
ndk::ScopedAStatus EvsCamera::pauseVideoStream() {
return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
}
ndk::ScopedAStatus EvsCamera::resumeVideoStream() {
return ndk::ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
}
bool EvsCamera::setAvailableFrames_unsafe(const std::size_t bufferCount) {
if (bufferCount < 1) {
LOG(ERROR) << "Ignoring request to set buffer count to zero.";
return false;
}
if (bufferCount > kMaxBuffersInFlight) {
LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
return false;
}
if (bufferCount > mAvailableFrames) {
bool success = true;
const std::size_t numBufferBeforeAlloc = mAvailableFrames;
for (int numBufferToAllocate = bufferCount - mAvailableFrames;
success && numBufferToAllocate > 0; --numBufferToAllocate) {
buffer_handle_t handle = nullptr;
const auto result = allocateOneFrame(&handle);
if (result != ::android::NO_ERROR || !handle) {
LOG(ERROR) << __func__ << ": Failed to allocate a graphics buffer. Error " << result
<< ", handle: " << handle;
success = false;
break;
}
success &= increaseAvailableFrames_unsafe(handle);
}
if (!success) {
// Rollback when failure.
for (int numBufferToRelease = mAvailableFrames - numBufferBeforeAlloc;
numBufferToRelease > 0; --numBufferToRelease) {
decreaseAvailableFrames_unsafe();
}
return false;
}
} else {
for (int numBufferToRelease = mAvailableFrames - std::max(bufferCount, mFramesInUse);
numBufferToRelease > 0; --numBufferToRelease) {
decreaseAvailableFrames_unsafe();
}
if (mAvailableFrames > bufferCount) {
// This shouldn't happen with a properly behaving client because the client
// should only make this call after returning sufficient outstanding buffers
// to allow a clean resize.
LOG(ERROR) << "Buffer queue shrink failed, asked: " << bufferCount
<< ", actual: " << mAvailableFrames
<< " -- too many buffers currently in use?";
}
}
return true;
}
void EvsCamera::shutdown() {
stopVideoStream();
std::lock_guard lck(mMutex);
closeAllBuffers_unsafe();
mStreamState = StreamState::DEAD;
}
void EvsCamera::closeAllBuffers_unsafe() {
if (mFramesInUse > 0) {
LOG(WARNING) << __func__ << ": Closing while " << mFramesInUse
<< " frame(s) are still in use.";
}
for (auto& buffer : mBuffers) {
freeOneFrame(buffer.handle);
buffer.handle = nullptr;
}
mBuffers.clear();
mBufferPosToId.clear();
mBufferIdToPos.clear();
}
std::pair<std::size_t, buffer_handle_t> EvsCamera::useBuffer_unsafe() {
if (mFramesInUse >= mAvailableFrames) {
DCHECK_EQ(mFramesInUse, mAvailableFrames);
return {kInvalidBufferID, nullptr};
}
const std::size_t pos = mFramesInUse++;
auto& buffer = mBuffers[pos];
DCHECK(!buffer.inUse);
DCHECK(buffer.handle);
buffer.inUse = true;
return {mBufferPosToId[pos], buffer.handle};
}
void EvsCamera::returnBuffer_unsafe(const std::size_t id) {
if (id >= mBuffers.size()) {
LOG(ERROR) << __func__ << ": ID out-of-bound. id: " << id
<< " max: " << mBuffers.size() - 1;
return;
}
const std::size_t pos = mBufferIdToPos[id];
if (!mBuffers[pos].inUse) {
LOG(ERROR) << __func__ << ": Ignoring returning frame " << id << " which is already free.";
return;
}
DCHECK_LT(pos, mFramesInUse);
const std::size_t last_in_use_pos = --mFramesInUse;
swapBufferFrames_unsafe(pos, last_in_use_pos);
mBuffers[last_in_use_pos].inUse = false;
}
bool EvsCamera::increaseAvailableFrames_unsafe(const buffer_handle_t handle) {
if (mAvailableFrames >= kMaxBuffersInFlight) {
LOG(WARNING) << __func__ << ": The number of buffers has hit the upper limit ("
<< kMaxBuffersInFlight << "). Stop increasing.";
return false;
}
const std::size_t pos = mAvailableFrames++;
if (mAvailableFrames > mBuffers.size()) {
const std::size_t oldBufferSize = mBuffers.size();
mBuffers.resize(mAvailableFrames);
mBufferPosToId.resize(mAvailableFrames);
mBufferIdToPos.resize(mAvailableFrames);
// Build position/ID mapping.
for (std::size_t idx = oldBufferSize; idx < mBuffers.size(); ++idx) {
mBufferPosToId[idx] = idx;
mBufferIdToPos[idx] = idx;
}
}
auto& buffer = mBuffers[pos];
DCHECK(!buffer.inUse);
DCHECK(!buffer.handle);
buffer.handle = handle;
return true;
}
bool EvsCamera::decreaseAvailableFrames_unsafe() {
if (mFramesInUse >= mAvailableFrames) {
DCHECK_EQ(mFramesInUse, mAvailableFrames);
return false;
}
const std::size_t pos = --mAvailableFrames;
auto& buffer = mBuffers[pos];
DCHECK(!buffer.inUse);
DCHECK(buffer.handle);
freeOneFrame(buffer.handle);
buffer.handle = nullptr;
return true;
}
void EvsCamera::swapBufferFrames_unsafe(const std::size_t pos1, const std::size_t pos2) {
if (pos1 == pos2) {
return;
}
if (pos1 >= mBuffers.size() || pos2 >= mBuffers.size()) {
LOG(ERROR) << __func__ << ": Index out-of-bound. pos1: " << pos1 << ", pos2: " << pos2
<< ", buffer size: " << mBuffers.size();
return;
}
const std::size_t id1 = mBufferPosToId[pos1];
const std::size_t id2 = mBufferPosToId[pos2];
std::swap(mBufferPosToId[pos1], mBufferPosToId[pos2]);
std::swap(mBufferIdToPos[id1], mBufferIdToPos[id2]);
std::swap(mBuffers[pos1], mBuffers[pos2]);
}
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -17,8 +17,9 @@
#include "EvsEnumerator.h"
#include "ConfigManager.h"
#include "EvsAllCameras.h"
#include "EvsCameraBase.h"
#include "EvsGlDisplay.h"
#include "EvsMockCamera.h"
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidl/android/hardware/graphics/common/BufferUsage.h>
@ -243,7 +244,7 @@ ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg
}
// Has this camera already been instantiated by another caller?
std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
std::shared_ptr<EvsCameraBase> pActiveCamera = pRecord->activeInstance.lock();
if (pActiveCamera) {
LOG(WARNING) << "Killing previous camera because of new caller";
closeCamera(pActiveCamera);
@ -253,12 +254,31 @@ ScopedAStatus EvsEnumerator::openCamera(const std::string& id, const Stream& cfg
if (!sConfigManager) {
pActiveCamera = EvsMockCamera::Create(id.data());
} else {
pActiveCamera = EvsMockCamera::Create(id.data(), sConfigManager->getCameraInfo(id), &cfg);
auto& cameraInfo = sConfigManager->getCameraInfo(id);
switch (cameraInfo->deviceType) {
using DeviceType = ConfigManager::CameraInfo::DeviceType;
// Default to MOCK for backward compatibility.
case DeviceType::NONE:
case DeviceType::MOCK:
pActiveCamera = EvsMockCamera::Create(id.data(), cameraInfo, &cfg);
break;
case DeviceType::VIDEO:
pActiveCamera = EvsVideoEmulatedCamera::Create(id.data(), cameraInfo, &cfg);
break;
default:
LOG(ERROR) << __func__ << ": camera device type "
<< static_cast<std::int32_t>(cameraInfo->deviceType)
<< " is not supported.";
break;
}
}
pRecord->activeInstance = pActiveCamera;
if (!pActiveCamera) {
LOG(ERROR) << "Failed to create new EvsMockCamera object for " << id;
LOG(ERROR) << "Failed to create new EVS camera object for " << id;
return ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::UNDERLYING_SERVICE_ERROR));
}
@ -445,7 +465,7 @@ void EvsEnumerator::closeCamera_impl(const std::shared_ptr<IEvsCamera>& pCamera,
if (!pRecord) {
LOG(ERROR) << "Asked to close a camera whose name isn't recognized";
} else {
std::shared_ptr<EvsMockCamera> pActiveCamera = pRecord->activeInstance.lock();
std::shared_ptr<EvsCameraBase> pActiveCamera = pRecord->activeInstance.lock();
if (!pActiveCamera) {
LOG(WARNING) << "Somehow a camera is being destroyed "
<< "when the enumerator didn't know one existed";

View file

@ -352,8 +352,8 @@ ScopedAStatus EvsGlDisplay::getTargetBuffer(BufferDesc* _aidl_return) {
BufferDesc bufferDescToSend = {
.buffer =
{
.handle = std::move(::android::dupToAidl(mBuffer.handle)),
.description = mBuffer.description,
.handle = std::move(::android::dupToAidl(mBuffer.handle)),
},
.pixelSizeBytes = 4, // RGBA_8888 is 4-byte-per-pixel format
.bufferId = mBuffer.fingerprint,

View file

@ -15,28 +15,25 @@
*/
#include "EvsMockCamera.h"
#include "ConfigManager.h"
#include "EvsEnumerator.h"
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <ui/GraphicBufferAllocator.h>
#include <ui/GraphicBufferMapper.h>
#include <utils/SystemClock.h>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <tuple>
namespace {
using ::aidl::android::hardware::graphics::common::BufferUsage;
using ::ndk::ScopedAStatus;
// Arbitrary limit on number of graphics buffers allowed to be allocated
// Safeguards against unreasonable resource consumption and provides a testable limit
constexpr unsigned kMaxBuffersInFlight = 100;
// Minimum number of buffers to run a video stream
constexpr int kMinimumBuffersInFlight = 1;
// Colors for the colorbar test pattern in ABGR format
constexpr uint32_t kColors[] = {
0xFFFFFFFF, // white
@ -56,7 +53,7 @@ namespace aidl::android::hardware::automotive::evs::implementation {
EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id,
std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
: mFramesAllowed(0), mFramesInUse(0), mStreamState(STOPPED), mCameraInfo(camInfo) {
: mCameraInfo(camInfo) {
LOG(DEBUG) << __FUNCTION__;
/* set a camera id */
@ -73,11 +70,6 @@ EvsMockCamera::EvsMockCamera([[maybe_unused]] Sigil sigil, const char* id,
initializeParameters();
}
EvsMockCamera::~EvsMockCamera() {
LOG(DEBUG) << __FUNCTION__;
shutdown();
}
void EvsMockCamera::initializeParameters() {
mParams.emplace(
CameraParam::BRIGHTNESS,
@ -90,35 +82,6 @@ void EvsMockCamera::initializeParameters() {
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
}
// This gets called if another caller "steals" ownership of the camera
void EvsMockCamera::shutdown() {
LOG(DEBUG) << __FUNCTION__;
// Make sure our output stream is cleaned up
// (It really should be already)
stopVideoStream_impl();
// Claim the lock while we work on internal state
std::lock_guard lock(mAccessLock);
// Drop all the graphics buffers we've been using
if (mBuffers.size() > 0) {
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
for (auto&& rec : mBuffers) {
if (rec.inUse) {
LOG(WARNING) << "WARNING: releasing a buffer remotely owned.";
}
alloc.free(rec.handle);
rec.handle = nullptr;
}
mBuffers.clear();
}
// Put this object into an unrecoverable error state since somebody else
// is going to own the underlying camera now
mStreamState = DEAD;
}
// Methods from ::aidl::android::hardware::automotive::evs::IEvsCamera follow.
ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) {
LOG(DEBUG) << __FUNCTION__;
@ -128,115 +91,6 @@ ScopedAStatus EvsMockCamera::getCameraInfo(CameraDesc* _aidl_return) {
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::setMaxFramesInFlight(int32_t bufferCount) {
LOG(DEBUG) << __FUNCTION__ << ", bufferCount = " << bufferCount;
;
std::lock_guard lock(mAccessLock);
// If we've been displaced by another owner of the camera, then we can't do anything else
if (mStreamState == DEAD) {
LOG(ERROR) << "Ignoring setMaxFramesInFlight call when camera has been lost.";
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
}
// We cannot function without at least one video buffer to send data
if (bufferCount < 1) {
LOG(ERROR) << "Ignoring setMaxFramesInFlight with less than one buffer requested.";
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
}
// Update our internal state
if (!setAvailableFrames_Locked(bufferCount)) {
LOG(ERROR) << "Failed to adjust the maximum number of frames in flight.";
return ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
}
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::startVideoStream(const std::shared_ptr<IEvsCameraStream>& cb) {
LOG(DEBUG) << __FUNCTION__;
if (!cb) {
LOG(ERROR) << "A given stream callback is invalid.";
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::INVALID_ARG));
}
std::lock_guard lock(mAccessLock);
// If we've been displaced by another owner of the camera, then we can't do anything else
if (mStreamState == DEAD) {
LOG(ERROR) << "Ignoring startVideoStream call when camera has been lost.";
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::OWNERSHIP_LOST));
}
if (mStreamState != STOPPED) {
LOG(ERROR) << "Ignoring startVideoStream call when a stream is already running.";
return ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::STREAM_ALREADY_RUNNING));
}
// If the client never indicated otherwise, configure ourselves for a single streaming buffer
if (mFramesAllowed < kMinimumBuffersInFlight &&
!setAvailableFrames_Locked(kMinimumBuffersInFlight)) {
LOG(ERROR) << "Failed to start stream because we couldn't get a graphics buffer";
return ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::BUFFER_NOT_AVAILABLE));
}
// Record the user's callback for use when we have a frame ready
mStream = cb;
// Start the frame generation thread
mStreamState = RUNNING;
mCaptureThread = std::thread([this]() { generateFrames(); });
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::doneWithFrame(const std::vector<BufferDesc>& list) {
std::lock_guard lock(mAccessLock);
for (const auto& desc : list) {
returnBufferLocked(desc.bufferId);
}
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::stopVideoStream() {
LOG(DEBUG) << __FUNCTION__;
return stopVideoStream_impl();
}
ScopedAStatus EvsMockCamera::stopVideoStream_impl() {
std::unique_lock lock(mAccessLock);
if (mStreamState != RUNNING) {
// Safely return here because a stream is not running.
return ScopedAStatus::ok();
}
// Tell the GenerateFrames loop we want it to stop
mStreamState = STOPPING;
// Block outside the mutex until the "stop" flag has been acknowledged
// We won't send any more frames, but the client might still get some already in flight
LOG(DEBUG) << "Waiting for stream thread to end...";
lock.unlock();
if (mCaptureThread.joinable()) {
mCaptureThread.join();
}
lock.lock();
mStreamState = STOPPED;
mStream = nullptr;
LOG(DEBUG) << "Stream marked STOPPED.";
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::getExtendedInfo(int32_t opaqueIdentifier,
std::vector<uint8_t>* opaqueValue) {
const auto it = mExtInfo.find(opaqueIdentifier);
@ -264,14 +118,6 @@ ScopedAStatus EvsMockCamera::getPhysicalCameraInfo([[maybe_unused]] const std::s
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::pauseVideoStream() {
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
}
ScopedAStatus EvsMockCamera::resumeVideoStream() {
return ScopedAStatus::fromServiceSpecificError(static_cast<int>(EvsResult::NOT_SUPPORTED));
}
ScopedAStatus EvsMockCamera::setPrimaryClient() {
/* Because EVS HW module reference implementation expects a single client at
* a time, this returns a success code always.
@ -346,232 +192,27 @@ ScopedAStatus EvsMockCamera::getIntParameter(CameraParam id, std::vector<int32_t
return ScopedAStatus::ok();
}
ScopedAStatus EvsMockCamera::importExternalBuffers(const std::vector<BufferDesc>& buffers,
int32_t* _aidl_return) {
size_t numBuffersToAdd = buffers.size();
if (numBuffersToAdd < 1) {
LOG(DEBUG) << "Ignoring a request to import external buffers with an empty list.";
return ScopedAStatus::ok();
}
std::lock_guard lock(mAccessLock);
if (numBuffersToAdd > (kMaxBuffersInFlight - mFramesAllowed)) {
numBuffersToAdd -= (kMaxBuffersInFlight - mFramesAllowed);
LOG(WARNING) << "Exceed the limit on the number of buffers. " << numBuffersToAdd
<< " buffers will be imported only.";
}
::android::GraphicBufferMapper& mapper = ::android::GraphicBufferMapper::get();
const size_t before = mFramesAllowed;
for (size_t i = 0; i < numBuffersToAdd; ++i) {
auto& b = buffers[i];
const AHardwareBuffer_Desc* pDesc =
reinterpret_cast<const AHardwareBuffer_Desc*>(&b.buffer.description);
buffer_handle_t handleToImport = ::android::dupFromAidl(b.buffer.handle);
buffer_handle_t handleToStore = nullptr;
if (handleToImport == nullptr) {
LOG(WARNING) << "Failed to duplicate a memory handle. Ignoring a buffer " << b.bufferId;
continue;
}
::android::status_t result =
mapper.importBuffer(handleToImport, pDesc->width, pDesc->height, pDesc->layers,
pDesc->format, pDesc->usage, pDesc->stride, &handleToStore);
if (result != ::android::NO_ERROR || handleToStore == nullptr) {
LOG(WARNING) << "Failed to import a buffer " << b.bufferId;
continue;
}
bool stored = false;
for (auto&& rec : mBuffers) {
if (rec.handle != nullptr) {
continue;
}
// Use this existing entry.
rec.handle = handleToStore;
rec.inUse = false;
stored = true;
break;
}
if (!stored) {
// Add a BufferRecord wrapping this handle to our set of available buffers.
mBuffers.push_back(BufferRecord(handleToStore));
}
++mFramesAllowed;
}
*_aidl_return = mFramesAllowed - before;
return ScopedAStatus::ok();
}
bool EvsMockCamera::setAvailableFrames_Locked(unsigned bufferCount) {
if (bufferCount < 1) {
LOG(ERROR) << "Ignoring request to set buffer count to zero";
return false;
}
if (bufferCount > kMaxBuffersInFlight) {
LOG(ERROR) << "Rejecting buffer request in excess of internal limit";
return false;
}
// Is an increase required?
if (mFramesAllowed < bufferCount) {
// An increase is required
auto needed = bufferCount - mFramesAllowed;
LOG(INFO) << "Allocating " << needed << " buffers for camera frames";
auto added = increaseAvailableFrames_Locked(needed);
if (added != needed) {
// If we didn't add all the frames we needed, then roll back to the previous state
LOG(ERROR) << "Rolling back to previous frame queue size";
decreaseAvailableFrames_Locked(added);
return false;
}
} else if (mFramesAllowed > bufferCount) {
// A decrease is required
auto framesToRelease = mFramesAllowed - bufferCount;
LOG(INFO) << "Returning " << framesToRelease << " camera frame buffers";
auto released = decreaseAvailableFrames_Locked(framesToRelease);
if (released != framesToRelease) {
// This shouldn't happen with a properly behaving client because the client
// should only make this call after returning sufficient outstanding buffers
// to allow a clean resize.
LOG(ERROR) << "Buffer queue shrink failed -- too many buffers currently in use?";
}
}
return true;
}
unsigned EvsMockCamera::increaseAvailableFrames_Locked(unsigned numToAdd) {
// Acquire the graphics buffer allocator
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
unsigned added = 0;
while (added < numToAdd) {
unsigned pixelsPerLine = 0;
buffer_handle_t memHandle = nullptr;
auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, &memHandle,
&pixelsPerLine, 0, "EvsMockCamera");
if (result != ::android::NO_ERROR) {
LOG(ERROR) << "Error " << result << " allocating " << mWidth << " x " << mHeight
<< " graphics buffer";
break;
}
if (memHandle == nullptr) {
LOG(ERROR) << "We didn't get a buffer handle back from the allocator";
break;
}
if (mStride > 0) {
if (mStride != pixelsPerLine) {
LOG(ERROR) << "We did not expect to get buffers with different strides!";
}
} else {
// Gralloc defines stride in terms of pixels per line
mStride = pixelsPerLine;
}
// Find a place to store the new buffer
auto stored = false;
for (auto&& rec : mBuffers) {
if (rec.handle == nullptr) {
// Use this existing entry
rec.handle = memHandle;
rec.inUse = false;
stored = true;
break;
}
}
if (!stored) {
// Add a BufferRecord wrapping this handle to our set of available buffers
mBuffers.push_back(BufferRecord(memHandle));
}
++mFramesAllowed;
++added;
}
return added;
}
unsigned EvsMockCamera::decreaseAvailableFrames_Locked(unsigned numToRemove) {
// Acquire the graphics buffer allocator
::android::GraphicBufferAllocator& alloc(::android::GraphicBufferAllocator::get());
unsigned removed = 0;
for (auto&& rec : mBuffers) {
// Is this record not in use, but holding a buffer that we can free?
if ((rec.inUse == false) && (rec.handle != nullptr)) {
// Release buffer and update the record so we can recognize it as "empty"
alloc.free(rec.handle);
rec.handle = nullptr;
--mFramesAllowed;
++removed;
if (removed == numToRemove) {
break;
}
}
}
return removed;
}
// This is the asynchronous frame generation thread that runs in parallel with the
// main serving thread. There is one for each active camera instance.
void EvsMockCamera::generateFrames() {
LOG(DEBUG) << "Frame generation loop started.";
unsigned idx = 0;
while (true) {
bool timeForFrame = false;
const nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC);
// Lock scope for updating shared state
std::size_t bufferId = kInvalidBufferID;
buffer_handle_t bufferHandle = nullptr;
{
std::lock_guard lock(mAccessLock);
if (mStreamState != RUNNING) {
// Break out of our main thread loop
std::lock_guard lock(mMutex);
if (mStreamState != StreamState::RUNNING) {
break;
}
// Are we allowed to issue another buffer?
if (mFramesInUse >= mFramesAllowed) {
// Can't do anything right now -- skip this frame
LOG(WARNING) << "Skipped a frame because too many are in flight.";
} else {
// Identify an available buffer to fill
for (idx = 0; idx < mBuffers.size(); idx++) {
if (!mBuffers[idx].inUse) {
if (mBuffers[idx].handle != nullptr) {
// Found an available record, so stop looking
break;
}
}
}
if (idx >= mBuffers.size()) {
// This shouldn't happen since we already checked mFramesInUse vs mFramesAllowed
ALOGE("Failed to find an available buffer slot\n");
} else {
// We're going to make the frame busy
mBuffers[idx].inUse = true;
mFramesInUse++;
timeForFrame = true;
}
}
std::tie(bufferId, bufferHandle) = useBuffer_unsafe();
}
if (timeForFrame) {
if (bufferHandle != nullptr) {
using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
// Assemble the buffer description we'll transmit below
buffer_handle_t memHandle = mBuffers[idx].handle;
BufferDesc newBuffer = {
.buffer =
{
@ -584,39 +225,31 @@ void EvsMockCamera::generateFrames() {
.usage = static_cast<BufferUsage>(mUsage),
.stride = static_cast<int32_t>(mStride),
},
.handle = ::android::dupToAidl(memHandle),
.handle = ::android::dupToAidl(bufferHandle),
},
.bufferId = static_cast<int32_t>(idx),
.bufferId = static_cast<int32_t>(bufferId),
.deviceId = mDescription.id,
.timestamp = static_cast<int64_t>(::android::elapsedRealtimeNano() *
1e+3), // timestamps is in microseconds
};
// Write test data into the image buffer
fillMockFrame(memHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
&newBuffer.buffer.description));
fillMockFrame(bufferHandle, reinterpret_cast<const AHardwareBuffer_Desc*>(
&newBuffer.buffer.description));
std::vector<BufferDesc> frames;
frames.push_back(std::move(newBuffer));
// Issue the (asynchronous) callback to the client -- can't be holding the lock
auto flag = false;
if (mStream) {
std::vector<BufferDesc> frames;
frames.push_back(std::move(newBuffer));
flag = mStream->deliverFrame(frames).isOk();
}
if (flag) {
LOG(DEBUG) << "Delivered " << memHandle << ", id = " << mBuffers[idx].handle;
if (mStream && mStream->deliverFrame(frames).isOk()) {
LOG(DEBUG) << "Delivered " << bufferHandle << ", id = " << bufferId;
} else {
// This can happen if the client dies and is likely unrecoverable.
// To avoid consuming resources generating failing calls, we stop sending
// frames. Note, however, that the stream remains in the "STREAMING" state
// until cleaned up on the main thread.
LOG(ERROR) << "Frame delivery call failed in the transport layer.";
// Since we didn't actually deliver it, mark the frame as available
std::lock_guard<std::mutex> lock(mAccessLock);
mBuffers[idx].inUse = false;
mFramesInUse--;
doneWithFrame(frames);
}
}
@ -671,34 +304,45 @@ void EvsMockCamera::fillMockFrame(buffer_handle_t handle, const AHardwareBuffer_
mapper.unlock(handle);
}
void EvsMockCamera::returnBufferLocked(const uint32_t bufferId) {
if (bufferId >= mBuffers.size()) {
ALOGE("ignoring doneWithFrame called with invalid bufferId %d (max is %zu)", bufferId,
mBuffers.size() - 1);
return;
::android::status_t EvsMockCamera::allocateOneFrame(buffer_handle_t* handle) {
static auto& alloc = ::android::GraphicBufferAllocator::get();
unsigned pixelsPerLine = 0;
const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine,
0, "EvsMockCamera");
if (mStride < mWidth) {
// Gralloc defines stride in terms of pixels per line
mStride = pixelsPerLine;
} else if (mStride != pixelsPerLine) {
LOG(ERROR) << "We did not expect to get buffers with different strides!";
}
return result;
}
if (!mBuffers[bufferId].inUse) {
ALOGE("ignoring doneWithFrame called on frame %d which is already free", bufferId);
return;
bool EvsMockCamera::startVideoStreamImpl_locked(
const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& /* lck */) {
mStream = receiver;
mCaptureThread = std::thread([this]() { generateFrames(); });
return true;
}
bool EvsMockCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& lck) {
lck.unlock();
if (mCaptureThread.joinable()) {
mCaptureThread.join();
}
lck.lock();
return true;
}
// Mark the frame as available
mBuffers[bufferId].inUse = false;
mFramesInUse--;
// If this frame's index is high in the array, try to move it down
// to improve locality after mFramesAllowed has been reduced.
if (bufferId >= mFramesAllowed) {
// Find an empty slot lower in the array (which should always exist in this case)
for (auto&& rec : mBuffers) {
if (rec.handle == nullptr) {
rec.handle = mBuffers[bufferId].handle;
mBuffers[bufferId].handle = nullptr;
break;
}
}
bool EvsMockCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) {
if (!Base::postVideoStreamStop_locked(status, lck)) {
return false;
}
mStream = nullptr;
return true;
}
std::shared_ptr<EvsMockCamera> EvsMockCamera::Create(const char* deviceName) {

View file

@ -0,0 +1,507 @@
/*
* Copyright (C) 2023 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 "EvsVideoEmulatedCamera.h"
#include <aidl/android/hardware/automotive/evs/EvsResult.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <android-base/logging.h>
#include <android-base/strings.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <ui/GraphicBufferAllocator.h>
#include <utils/SystemClock.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
#include <cstddef>
#include <cstdint>
#include <tuple>
#include <utility>
namespace aidl::android::hardware::automotive::evs::implementation {
namespace {
struct FormatDeleter {
void operator()(AMediaFormat* format) const { AMediaFormat_delete(format); }
};
} // namespace
EvsVideoEmulatedCamera::EvsVideoEmulatedCamera(Sigil, const char* deviceName,
std::unique_ptr<ConfigManager::CameraInfo>& camInfo)
: mVideoFileName(deviceName), mCameraInfo(camInfo) {
mDescription.id = mVideoFileName;
/* set camera metadata */
if (camInfo) {
uint8_t* ptr = reinterpret_cast<uint8_t*>(camInfo->characteristics);
const size_t len = get_camera_metadata_size(camInfo->characteristics);
mDescription.metadata.insert(mDescription.metadata.end(), ptr, ptr + len);
}
initializeParameters();
}
bool EvsVideoEmulatedCamera::initialize() {
// Open file.
mVideoFd = open(mVideoFileName.c_str(), 0, O_RDONLY);
if (mVideoFd < 0) {
PLOG(ERROR) << __func__ << ": Failed to open video file \"" << mVideoFileName << "\".";
return false;
}
// Initialize Media Extractor.
{
mVideoExtractor.reset(AMediaExtractor_new());
off64_t filesize = lseek64(mVideoFd, 0, SEEK_END);
lseek(mVideoFd, 0, SEEK_SET);
const media_status_t status =
AMediaExtractor_setDataSourceFd(mVideoExtractor.get(), mVideoFd, 0, filesize);
if (status != AMEDIA_OK) {
LOG(ERROR) << __func__
<< ": Received error when initializing media extractor. Error code: "
<< status << ".";
return false;
}
}
// Initialize Media Codec and file format.
std::unique_ptr<AMediaFormat, FormatDeleter> format;
const char* mime;
bool selected = false;
int numTracks = AMediaExtractor_getTrackCount(mVideoExtractor.get());
for (int i = 0; i < numTracks; i++) {
format.reset(AMediaExtractor_getTrackFormat(mVideoExtractor.get(), i));
if (!AMediaFormat_getString(format.get(), AMEDIAFORMAT_KEY_MIME, &mime)) {
LOG(ERROR) << __func__ << ": Error in fetching format string";
continue;
}
if (!::android::base::StartsWith(mime, "video/")) {
continue;
}
const media_status_t status = AMediaExtractor_selectTrack(mVideoExtractor.get(), i);
if (status != AMEDIA_OK) {
LOG(ERROR) << __func__
<< ": Media extractor returned error to select track. Error Code: " << status
<< ".";
return false;
}
selected = true;
break;
}
if (!selected) {
LOG(ERROR) << __func__ << ": No video track in video file \"" << mVideoFileName << "\".";
return false;
}
mVideoCodec.reset(AMediaCodec_createDecoderByType(mime));
if (!mVideoCodec) {
LOG(ERROR) << __func__ << ": Unable to create decoder.";
return false;
}
mDescription.vendorFlags = 0xFFFFFFFF; // Arbitrary test value
mUsage = GRALLOC_USAGE_HW_TEXTURE | GRALLOC_USAGE_HW_CAMERA_WRITE |
GRALLOC_USAGE_SW_READ_RARELY | GRALLOC_USAGE_SW_WRITE_RARELY;
mFormat = HAL_PIXEL_FORMAT_YCBCR_420_888;
AMediaFormat_setInt32(format.get(), AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible);
{
const media_status_t status =
AMediaCodec_configure(mVideoCodec.get(), format.get(), nullptr, nullptr, 0);
if (status != AMEDIA_OK) {
LOG(ERROR) << __func__
<< ": Received error in configuring mCodec. Error code: " << status << ".";
return false;
}
}
format.reset(AMediaCodec_getOutputFormat(mVideoCodec.get()));
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_WIDTH, &mWidth);
AMediaFormat_getInt32(format.get(), AMEDIAFORMAT_KEY_HEIGHT, &mHeight);
return true;
}
void EvsVideoEmulatedCamera::generateFrames() {
while (true) {
{
std::lock_guard lock(mMutex);
if (mStreamState != StreamState::RUNNING) {
return;
}
}
renderOneFrame();
}
}
void EvsVideoEmulatedCamera::onCodecInputAvailable(const int32_t index) {
const size_t sampleSize = AMediaExtractor_getSampleSize(mVideoExtractor.get());
const int64_t presentationTime = AMediaExtractor_getSampleTime(mVideoExtractor.get());
size_t bufferSize = 0;
uint8_t* const codecInputBuffer =
AMediaCodec_getInputBuffer(mVideoCodec.get(), index, &bufferSize);
if (sampleSize > bufferSize) {
LOG(ERROR) << __func__ << ": Buffer is not large enough.";
}
if (presentationTime < 0) {
AMediaCodec_queueInputBuffer(mVideoCodec.get(), index, /* offset = */ 0,
/* size = */ 0, presentationTime,
AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM);
LOG(INFO) << __func__ << ": Reaching the end of stream.";
return;
}
const size_t readSize =
AMediaExtractor_readSampleData(mVideoExtractor.get(), codecInputBuffer, sampleSize);
const media_status_t status = AMediaCodec_queueInputBuffer(
mVideoCodec.get(), index, /*offset = */ 0, readSize, presentationTime, /* flags = */ 0);
if (status != AMEDIA_OK) {
LOG(ERROR) << __func__
<< ": Received error in queueing input buffer. Error code: " << status;
}
}
void EvsVideoEmulatedCamera::onCodecOutputAvailable(const int32_t index,
const AMediaCodecBufferInfo& info) {
using std::chrono::duration_cast;
using std::chrono::microseconds;
using std::chrono::nanoseconds;
using AidlPixelFormat = ::aidl::android::hardware::graphics::common::PixelFormat;
using ::aidl::android::hardware::graphics::common::BufferUsage;
size_t decodedOutSize = 0;
uint8_t* const codecOutputBuffer =
AMediaCodec_getOutputBuffer(mVideoCodec.get(), index, &decodedOutSize) + info.offset;
std::size_t renderBufferId = static_cast<std::size_t>(-1);
buffer_handle_t renderBufferHandle = nullptr;
{
std::lock_guard lock(mMutex);
if (mStreamState != StreamState::RUNNING) {
return;
}
std::tie(renderBufferId, renderBufferHandle) = useBuffer_unsafe();
}
if (!renderBufferHandle) {
LOG(ERROR) << __func__ << ": Camera failed to get an available render buffer.";
return;
}
std::vector<BufferDesc> renderBufferDescs;
renderBufferDescs.push_back({
.buffer =
{
.description =
{
.width = static_cast<int32_t>(mWidth),
.height = static_cast<int32_t>(mHeight),
.layers = 1,
.format = static_cast<AidlPixelFormat>(mFormat),
.usage = static_cast<BufferUsage>(mUsage),
.stride = static_cast<int32_t>(mStride),
},
.handle = ::android::dupToAidl(renderBufferHandle),
},
.bufferId = static_cast<int32_t>(renderBufferId),
.deviceId = mDescription.id,
.timestamp = duration_cast<microseconds>(nanoseconds(::android::elapsedRealtimeNano()))
.count(),
});
// Lock our output buffer for writing
uint8_t* pixels = nullptr;
int32_t bytesPerStride = 0;
auto& mapper = ::android::GraphicBufferMapper::get();
mapper.lock(renderBufferHandle, GRALLOC_USAGE_SW_WRITE_OFTEN | GRALLOC_USAGE_SW_READ_NEVER,
::android::Rect(mWidth, mHeight), (void**)&pixels, nullptr, &bytesPerStride);
// If we failed to lock the pixel buffer, we're about to crash, but log it first
if (!pixels) {
LOG(ERROR) << __func__ << ": Camera failed to gain access to image buffer for writing";
return;
}
std::size_t ySize = mHeight * mStride;
std::size_t uvSize = ySize / 4;
std::memcpy(pixels, codecOutputBuffer, ySize);
pixels += ySize;
uint8_t* u_head = codecOutputBuffer + ySize;
uint8_t* v_head = u_head + uvSize;
for (size_t i = 0; i < uvSize; ++i) {
*(pixels++) = *(u_head++);
*(pixels++) = *(v_head++);
}
const auto status =
AMediaCodec_releaseOutputBuffer(mVideoCodec.get(), index, /* render = */ false);
if (status != AMEDIA_OK) {
LOG(ERROR) << __func__
<< ": Received error in releasing output buffer. Error code: " << status;
}
// Release our output buffer
mapper.unlock(renderBufferHandle);
// Issue the (asynchronous) callback to the client -- can't be holding the lock
if (mStream && mStream->deliverFrame(renderBufferDescs).isOk()) {
LOG(DEBUG) << __func__ << ": Delivered " << renderBufferHandle
<< ", id = " << renderBufferId;
} else {
// This can happen if the client dies and is likely unrecoverable.
// To avoid consuming resources generating failing calls, we stop sending
// frames. Note, however, that the stream remains in the "STREAMING" state
// until cleaned up on the main thread.
LOG(ERROR) << __func__ << ": Frame delivery call failed in the transport layer.";
doneWithFrame(renderBufferDescs);
}
}
void EvsVideoEmulatedCamera::renderOneFrame() {
using std::chrono::duration_cast;
using std::chrono::microseconds;
using namespace std::chrono_literals;
// push to codec input
while (true) {
int codecInputBufferIdx =
AMediaCodec_dequeueInputBuffer(mVideoCodec.get(), /* timeoutUs = */ 0);
if (codecInputBufferIdx < 0) {
if (codecInputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
LOG(ERROR) << __func__
<< ": Received error in AMediaCodec_dequeueInputBuffer. Error code: "
<< codecInputBufferIdx;
}
break;
}
onCodecInputAvailable(codecInputBufferIdx);
AMediaExtractor_advance(mVideoExtractor.get());
}
// pop from codec output
AMediaCodecBufferInfo info;
int codecOutputputBufferIdx = AMediaCodec_dequeueOutputBuffer(
mVideoCodec.get(), &info, /* timeoutUs = */ duration_cast<microseconds>(1ms).count());
if (codecOutputputBufferIdx < 0) {
if (codecOutputputBufferIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
LOG(ERROR) << __func__
<< ": Received error in AMediaCodec_dequeueOutputBuffer. Error code: "
<< codecOutputputBufferIdx;
}
return;
}
onCodecOutputAvailable(codecOutputputBufferIdx, info);
}
void EvsVideoEmulatedCamera::initializeParameters() {
mParams.emplace(
CameraParam::BRIGHTNESS,
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
mParams.emplace(
CameraParam::CONTRAST,
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
mParams.emplace(
CameraParam::SHARPNESS,
new CameraParameterDesc(/* min= */ 0, /* max= */ 255, /* step= */ 1, /* value= */ 255));
}
::android::status_t EvsVideoEmulatedCamera::allocateOneFrame(buffer_handle_t* handle) {
static auto& alloc = ::android::GraphicBufferAllocator::get();
unsigned pixelsPerLine = 0;
const auto result = alloc.allocate(mWidth, mHeight, mFormat, 1, mUsage, handle, &pixelsPerLine,
0, "EvsVideoEmulatedCamera");
if (mStride == 0) {
// Gralloc defines stride in terms of pixels per line
mStride = pixelsPerLine;
} else if (mStride != pixelsPerLine) {
LOG(ERROR) << "We did not expect to get buffers with different strides!";
}
return result;
}
bool EvsVideoEmulatedCamera::startVideoStreamImpl_locked(
const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& /* lck */) {
mStream = receiver;
const media_status_t status = AMediaCodec_start(mVideoCodec.get());
if (status != AMEDIA_OK) {
LOG(ERROR) << __func__ << ": Received error in starting decoder. Error code: " << status
<< ".";
return false;
}
mCaptureThread = std::thread([this]() { generateFrames(); });
return true;
}
bool EvsVideoEmulatedCamera::stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& lck) {
const media_status_t status = AMediaCodec_stop(mVideoCodec.get());
lck.unlock();
if (mCaptureThread.joinable()) {
mCaptureThread.join();
}
lck.lock();
return status == AMEDIA_OK;
}
bool EvsVideoEmulatedCamera::postVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) {
if (!Base::postVideoStreamStop_locked(status, lck)) {
return false;
}
mStream = nullptr;
return true;
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::forcePrimaryClient(
const std::shared_ptr<evs::IEvsDisplay>& /* display */) {
/* Because EVS HW module reference implementation expects a single client at
* a time, this returns a success code always.
*/
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::getCameraInfo(evs::CameraDesc* _aidl_return) {
*_aidl_return = mDescription;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::getExtendedInfo(int32_t opaqueIdentifier,
std::vector<uint8_t>* value) {
const auto it = mExtInfo.find(opaqueIdentifier);
if (it == mExtInfo.end()) {
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::INVALID_ARG));
} else {
*value = mExtInfo[opaqueIdentifier];
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::getIntParameter(evs::CameraParam id,
std::vector<int32_t>* value) {
const auto it = mParams.find(id);
if (it == mParams.end()) {
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::NOT_SUPPORTED));
}
value->push_back(it->second->value);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::getIntParameterRange(evs::CameraParam id,
evs::ParameterRange* _aidl_return) {
const auto it = mParams.find(id);
if (it == mParams.end()) {
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::NOT_SUPPORTED));
}
_aidl_return->min = it->second->range.min;
_aidl_return->max = it->second->range.max;
_aidl_return->step = it->second->range.step;
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::getParameterList(
std::vector<evs::CameraParam>* _aidl_return) {
if (mCameraInfo) {
_aidl_return->resize(mCameraInfo->controls.size());
std::size_t idx = 0;
for (const auto& [name, range] : mCameraInfo->controls) {
(*_aidl_return)[idx++] = name;
}
}
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::getPhysicalCameraInfo(const std::string& /* deviceId */,
evs::CameraDesc* _aidl_return) {
return getCameraInfo(_aidl_return);
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::setExtendedInfo(
int32_t opaqueIdentifier, const std::vector<uint8_t>& opaqueValue) {
mExtInfo.insert_or_assign(opaqueIdentifier, opaqueValue);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::setIntParameter(evs::CameraParam id, int32_t value,
std::vector<int32_t>* effectiveValue) {
const auto it = mParams.find(id);
if (it == mParams.end()) {
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::NOT_SUPPORTED));
}
// Rounding down to the closest value.
int32_t candidate = value / it->second->range.step * it->second->range.step;
if (candidate < it->second->range.min || candidate > it->second->range.max) {
return ndk::ScopedAStatus::fromServiceSpecificError(
static_cast<int>(EvsResult::INVALID_ARG));
}
it->second->value = candidate;
effectiveValue->push_back(candidate);
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::setPrimaryClient() {
/* Because EVS HW module reference implementation expects a single client at
* a time, this returns a success code always.
*/
return ndk::ScopedAStatus::ok();
}
ndk::ScopedAStatus EvsVideoEmulatedCamera::unsetPrimaryClient() {
/* Because EVS HW module reference implementation expects a single client at
* a time, there is no chance that this is called by the secondary client and
* therefore returns a success code always.
*/
return ndk::ScopedAStatus::ok();
}
std::shared_ptr<EvsVideoEmulatedCamera> EvsVideoEmulatedCamera::Create(const char* deviceName) {
std::unique_ptr<ConfigManager::CameraInfo> nullCamInfo = nullptr;
return Create(deviceName, nullCamInfo);
}
std::shared_ptr<EvsVideoEmulatedCamera> EvsVideoEmulatedCamera::Create(
const char* deviceName, std::unique_ptr<ConfigManager::CameraInfo>& camInfo,
const evs::Stream* /* streamCfg */) {
std::shared_ptr<EvsVideoEmulatedCamera> c =
ndk::SharedRefBase::make<EvsVideoEmulatedCamera>(Sigil{}, deviceName, camInfo);
if (!c) {
LOG(ERROR) << "Failed to instantiate EvsVideoEmulatedCamera.";
return nullptr;
}
if (!c->initialize()) {
LOG(ERROR) << "Failed to initialize EvsVideoEmulatedCamera.";
return nullptr;
}
return c;
}
void EvsVideoEmulatedCamera::shutdown() {
mVideoCodec.reset();
mVideoExtractor.reset();
close(mVideoFd);
mVideoFd = 0;
Base::shutdown();
}
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -19,6 +19,7 @@
#include <aidl/android/frameworks/automotive/display/DisplayDesc.h>
#include <aidl/android/hardware/graphics/common/HardwareBufferDescription.h>
#include <aidlcommonsupport/NativeHandle.h>
#include <gui/view/Surface.h>
#include <ui/DisplayMode.h>
#include <ui/DisplayState.h>
#include <ui/GraphicBuffer.h>
@ -183,20 +184,6 @@ GLuint buildShaderProgram(const char* vtxSrc, const char* pxlSrc) {
return program;
}
::android::sp<HGraphicBufferProducer> convertNativeHandleToHGBP(const NativeHandle& aidlHandle) {
native_handle_t* handle = ::android::dupFromAidl(aidlHandle);
if (handle->numFds != 0 || handle->numInts < std::ceil(sizeof(size_t) / sizeof(int))) {
LOG(ERROR) << "Invalid native handle";
return nullptr;
}
::android::hardware::hidl_vec<uint8_t> halToken;
halToken.setToExternal(reinterpret_cast<uint8_t*>(const_cast<int*>(&(handle->data[1]))),
handle->data[0]);
::android::sp<HGraphicBufferProducer> hgbp =
HGraphicBufferProducer::castFrom(::android::retrieveHalInterface(halToken));
return std::move(hgbp);
}
} // namespace
namespace aidl::android::hardware::automotive::evs::implementation {
@ -226,30 +213,19 @@ bool GlWrapper::initialize(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy
}
LOG(INFO) << "Display resolution is " << mWidth << "x" << mHeight;
NativeHandle aidlHandle;
status = pWindowProxy->getHGraphicBufferProducer(displayId, &aidlHandle);
aidl::android::view::Surface shimSurface;
status = pWindowProxy->getSurface(displayId, &shimSurface);
if (!status.isOk()) {
LOG(ERROR) << "Failed to get IGraphicBufferProducer from ICarDisplayProxy.";
LOG(ERROR) << "Failed to obtain the surface.";
return false;
}
mGfxBufferProducer = convertNativeHandleToHGBP(aidlHandle);
if (!mGfxBufferProducer) {
LOG(ERROR) << "Failed to convert a NativeHandle to HGBP.";
return false;
}
mSurfaceHolder = getSurfaceFromHGBP(mGfxBufferProducer);
if (mSurfaceHolder == nullptr) {
LOG(ERROR) << "Failed to get a Surface from HGBP.";
return false;
}
mWindow = getNativeWindow(mSurfaceHolder.get());
mWindow = shimSurface.get();
if (mWindow == nullptr) {
LOG(ERROR) << "Failed to get a native window from Surface.";
return false;
}
ANativeWindow_acquire(mWindow);
// Set up our OpenGL ES context associated with the default display
mDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY);
@ -350,7 +326,12 @@ void GlWrapper::shutdown() {
mDisplay = EGL_NO_DISPLAY;
// Release the window
mSurfaceHolder = nullptr;
if (mWindow == nullptr) {
return;
}
ANativeWindow_release(mWindow);
mWindow = nullptr;
}
void GlWrapper::showWindow(const std::shared_ptr<ICarDisplayProxy>& pWindowProxy, uint64_t id) {

View file

@ -0,0 +1,210 @@
/*
* Copyright (C) 2023 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 "EvsCamera.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace aidl::android::hardware::automotive::evs::implementation {
class EvsCameraForTest : public EvsCamera {
public:
using EvsCamera::increaseAvailableFrames_unsafe;
using EvsCamera::returnBuffer_unsafe;
using EvsCamera::useBuffer_unsafe;
~EvsCameraForTest() override { shutdown(); }
::android::status_t allocateOneFrame(buffer_handle_t* handle) override {
static std::intptr_t handle_cnt = 0;
*handle = reinterpret_cast<buffer_handle_t>(++handle_cnt);
return ::android::OK;
}
void freeOneFrame(const buffer_handle_t /* handle */) override {
// Nothing to free because the handles are fake.
}
void checkBufferOrder() {
for (std::size_t idx = 0; idx < mBuffers.size(); ++idx) {
const auto& buffer = mBuffers[idx];
EXPECT_EQ(idx < mFramesInUse, buffer.inUse);
EXPECT_EQ(idx < mAvailableFrames, buffer.handle != nullptr);
EXPECT_LE(mFramesInUse, mAvailableFrames);
}
}
MOCK_METHOD(::ndk::ScopedAStatus, forcePrimaryClient,
(const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>&
in_display),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getCameraInfo,
(::aidl::android::hardware::automotive::evs::CameraDesc * _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getExtendedInfo,
(int32_t in_opaqueIdentifier, std::vector<uint8_t>* _aidl_return), (override));
MOCK_METHOD(::ndk::ScopedAStatus, getIntParameter,
(::aidl::android::hardware::automotive::evs::CameraParam in_id,
std::vector<int32_t>* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getIntParameterRange,
(::aidl::android::hardware::automotive::evs::CameraParam in_id,
::aidl::android::hardware::automotive::evs::ParameterRange* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getParameterList,
(std::vector<::aidl::android::hardware::automotive::evs::CameraParam> *
_aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getPhysicalCameraInfo,
(const std::string& in_deviceId,
::aidl::android::hardware::automotive::evs::CameraDesc* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setExtendedInfo,
(int32_t in_opaqueIdentifier, const std::vector<uint8_t>& in_opaqueValue),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setIntParameter,
(::aidl::android::hardware::automotive::evs::CameraParam in_id, int32_t in_value,
std::vector<int32_t>* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override));
MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override));
MOCK_METHOD(bool, startVideoStreamImpl_locked,
(const std::shared_ptr<evs::IEvsCameraStream>& receiver, ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck),
(override));
MOCK_METHOD(bool, stopVideoStreamImpl_locked,
(ndk::ScopedAStatus & status, std::unique_lock<std::mutex>& lck), (override));
};
TEST(EvsCameraBufferTest, ChangeBufferPoolSize) {
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
EXPECT_TRUE(evsCam->setMaxFramesInFlight(100).isOk());
evsCam->checkBufferOrder();
EXPECT_TRUE(evsCam->setMaxFramesInFlight(50).isOk());
evsCam->checkBufferOrder();
// 2 buffers in use.
const auto [id1, handle1] = evsCam->useBuffer_unsafe();
const auto [id2, handle2] = evsCam->useBuffer_unsafe();
std::ignore = evsCam->useBuffer_unsafe();
// It allows you to set the buffer pool size to 1, but it will keep the space for the in use
// buffers.
EXPECT_TRUE(evsCam->setMaxFramesInFlight(1).isOk());
evsCam->checkBufferOrder();
evsCam->returnBuffer_unsafe(id1);
evsCam->checkBufferOrder();
evsCam->returnBuffer_unsafe(id2);
evsCam->checkBufferOrder();
}
TEST(EvsCameraBufferTest, UseAndReturn) {
constexpr std::size_t kNumOfHandles = 20;
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
// Our "fake handles" of this test case is 1 to kNumOfHandles.
for (std::size_t i = 1; i <= kNumOfHandles; ++i) {
evsCam->increaseAvailableFrames_unsafe(reinterpret_cast<buffer_handle_t>(i));
}
evsCam->checkBufferOrder();
{
std::vector<std::pair<std::size_t, std::intptr_t>> inUseIDHandlePairs;
std::unordered_set<std::size_t> inUseIDs;
std::unordered_set<std::intptr_t> inUseHandles;
for (std::size_t i = 0; i < kNumOfHandles; ++i) {
const auto [id, handle] = evsCam->useBuffer_unsafe();
const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
EXPECT_TRUE(EvsCamera::IsBufferIDValid(id));
EXPECT_NE(handle, nullptr);
EXPECT_LT(id, kNumOfHandles);
// handleInt must be between [1, kNumOfHandles] as we "allocated" above.
EXPECT_LT(0u, handleInt);
EXPECT_LE(handleInt, kNumOfHandles);
inUseIDHandlePairs.push_back({id, handleInt});
EXPECT_TRUE(inUseIDs.insert(id).second);
EXPECT_TRUE(inUseHandles.insert(handleInt).second);
evsCam->checkBufferOrder();
}
// Return buffers in the order of acquiring.
for (const auto [id, handleInt] : inUseIDHandlePairs) {
evsCam->returnBuffer_unsafe(id);
evsCam->checkBufferOrder();
}
}
{
std::vector<std::pair<std::size_t, std::intptr_t>> inUseIDHandlePairs;
std::unordered_set<std::size_t> inUseIDs;
std::unordered_set<std::intptr_t> inUseHandles;
for (std::size_t i = 0; i < kNumOfHandles; ++i) {
const auto [id, handle] = evsCam->useBuffer_unsafe();
const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
EXPECT_TRUE(EvsCamera::IsBufferIDValid(id));
EXPECT_NE(handle, nullptr);
EXPECT_LT(id, kNumOfHandles);
// handleInt must be between [1, kNumOfHandles] as we "allocated" above.
EXPECT_LT(0u, handleInt);
EXPECT_LE(handleInt, kNumOfHandles);
inUseIDHandlePairs.push_back({id, handleInt});
EXPECT_TRUE(inUseIDs.insert(id).second);
EXPECT_TRUE(inUseHandles.insert(handleInt).second);
evsCam->checkBufferOrder();
}
// Return buffers in the reverse order of acquiring.
std::reverse(inUseIDHandlePairs.begin(), inUseIDHandlePairs.end());
for (const auto [id, handleInt] : inUseIDHandlePairs) {
evsCam->returnBuffer_unsafe(id);
evsCam->checkBufferOrder();
}
}
{
// Making sure the handles are still in [1, kNumOfHandles] and IDs are still [0,
// kNumOfHandles). The mapping may be different, though.
std::vector<std::pair<std::size_t, std::intptr_t>> inUseIDHandlePairs;
std::unordered_set<std::size_t> inUseIDs;
std::unordered_set<std::intptr_t> inUseHandles;
for (std::size_t i = 0; i < kNumOfHandles; ++i) {
const auto [id, handle] = evsCam->useBuffer_unsafe();
const std::size_t handleInt = reinterpret_cast<std::size_t>(handle);
EXPECT_TRUE(EvsCamera::IsBufferIDValid(id));
EXPECT_NE(handle, nullptr);
EXPECT_LT(id, kNumOfHandles);
// handleInt must be between [1, kNumOfHandles] as we "allocated" above.
EXPECT_LT(0u, handleInt);
EXPECT_LE(handleInt, kNumOfHandles);
inUseIDHandlePairs.push_back({id, handleInt});
EXPECT_TRUE(inUseIDs.insert(id).second);
EXPECT_TRUE(inUseHandles.insert(handleInt).second);
evsCam->checkBufferOrder();
}
}
}
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -0,0 +1,202 @@
/*
* Copyright (C) 2023 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 "EvsCamera.h"
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <cstdint>
#include <unordered_set>
#include <vector>
namespace aidl::android::hardware::automotive::evs::implementation {
class EvsCameraForTest : public EvsCamera {
private:
using Base = EvsCamera;
public:
using EvsCamera::mStreamState;
using EvsCamera::shutdown;
using EvsCamera::StreamState;
~EvsCameraForTest() override { shutdown(); }
::android::status_t allocateOneFrame(buffer_handle_t* handle) override {
static std::intptr_t handle_cnt = 0;
*handle = reinterpret_cast<buffer_handle_t>(++handle_cnt);
return ::android::OK;
}
void freeOneFrame(const buffer_handle_t /* handle */) override {
// Nothing to free because the handles are fake.
}
bool preVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override {
mPreStartCalled = true;
EXPECT_EQ(mStreamState, StreamState::STOPPED);
EXPECT_FALSE(mStreamStarted);
EXPECT_FALSE(mStreamStopped);
return Base::preVideoStreamStart_locked(receiver, status, lck);
}
bool startVideoStreamImpl_locked(const std::shared_ptr<evs::IEvsCameraStream>& /* receiver */,
ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& /* lck */) override {
EXPECT_EQ(mStreamState, StreamState::RUNNING);
EXPECT_FALSE(mStreamStarted);
EXPECT_FALSE(mStreamStopped);
mStreamStarted = true;
return true;
}
bool postVideoStreamStart_locked(const std::shared_ptr<evs::IEvsCameraStream>& receiver,
ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override {
mPostStartCalled = true;
EXPECT_EQ(mStreamState, StreamState::RUNNING);
EXPECT_TRUE(mStreamStarted);
EXPECT_FALSE(mStreamStopped);
return Base::postVideoStreamStart_locked(receiver, status, lck);
}
bool preVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override {
// Skip the check if stop was called before.
if (!mPreStopCalled) {
mPreStopCalled = true;
EXPECT_EQ(mStreamState, StreamState::RUNNING);
EXPECT_TRUE(mStreamStarted);
EXPECT_FALSE(mStreamStopped);
}
return Base::preVideoStreamStop_locked(status, lck);
}
bool stopVideoStreamImpl_locked(ndk::ScopedAStatus& /* status */,
std::unique_lock<std::mutex>& /* lck */) override {
EXPECT_EQ(mStreamState, StreamState::STOPPING);
EXPECT_TRUE(mStreamStarted);
EXPECT_FALSE(mStreamStopped);
mStreamStopped = true;
return true;
}
bool postVideoStreamStop_locked(ndk::ScopedAStatus& status,
std::unique_lock<std::mutex>& lck) override {
mPostStopCalled = true;
const auto ret = Base::postVideoStreamStop_locked(status, lck);
EXPECT_EQ(mStreamState, StreamState::STOPPED);
EXPECT_TRUE(mStreamStarted);
EXPECT_TRUE(mStreamStopped);
return ret;
}
MOCK_METHOD(::ndk::ScopedAStatus, forcePrimaryClient,
(const std::shared_ptr<::aidl::android::hardware::automotive::evs::IEvsDisplay>&
in_display),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getCameraInfo,
(::aidl::android::hardware::automotive::evs::CameraDesc * _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getExtendedInfo,
(int32_t in_opaqueIdentifier, std::vector<uint8_t>* _aidl_return), (override));
MOCK_METHOD(::ndk::ScopedAStatus, getIntParameter,
(::aidl::android::hardware::automotive::evs::CameraParam in_id,
std::vector<int32_t>* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getIntParameterRange,
(::aidl::android::hardware::automotive::evs::CameraParam in_id,
::aidl::android::hardware::automotive::evs::ParameterRange* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getParameterList,
(std::vector<::aidl::android::hardware::automotive::evs::CameraParam> *
_aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getPhysicalCameraInfo,
(const std::string& in_deviceId,
::aidl::android::hardware::automotive::evs::CameraDesc* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setExtendedInfo,
(int32_t in_opaqueIdentifier, const std::vector<uint8_t>& in_opaqueValue),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setIntParameter,
(::aidl::android::hardware::automotive::evs::CameraParam in_id, int32_t in_value,
std::vector<int32_t>* _aidl_return),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, setPrimaryClient, (), (override));
MOCK_METHOD(::ndk::ScopedAStatus, unsetPrimaryClient, (), (override));
bool mStreamStarted = false;
bool mStreamStopped = false;
bool mPreStartCalled = false;
bool mPostStartCalled = false;
bool mPreStopCalled = false;
bool mPostStopCalled = false;
};
class MockEvsCameraStream : public evs::IEvsCameraStream {
MOCK_METHOD(::ndk::SpAIBinder, asBinder, (), (override));
MOCK_METHOD(bool, isRemote, (), (override));
MOCK_METHOD(
::ndk::ScopedAStatus, deliverFrame,
(const std::vector<::aidl::android::hardware::automotive::evs::BufferDesc>& in_buffer),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, notify,
(const ::aidl::android::hardware::automotive::evs::EvsEventDesc& in_event),
(override));
MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceVersion, (int32_t * _aidl_return), (override));
MOCK_METHOD(::ndk::ScopedAStatus, getInterfaceHash, (std::string * _aidl_return), (override));
};
using StreamState = EvsCameraForTest::StreamState;
TEST(EvsCameraStateTest, StateChangeHooks) {
auto evsCam = ndk::SharedRefBase::make<EvsCameraForTest>();
auto mockStream = ndk::SharedRefBase::make<MockEvsCameraStream>();
EXPECT_FALSE(evsCam->mPreStartCalled);
EXPECT_FALSE(evsCam->mPostStartCalled);
EXPECT_FALSE(evsCam->mPreStopCalled);
EXPECT_FALSE(evsCam->mPostStopCalled);
EXPECT_FALSE(evsCam->mStreamStarted);
EXPECT_FALSE(evsCam->mStreamStopped);
EXPECT_EQ(evsCam->mStreamState, StreamState::STOPPED);
evsCam->startVideoStream(mockStream);
EXPECT_TRUE(evsCam->mPreStartCalled);
EXPECT_TRUE(evsCam->mPostStartCalled);
EXPECT_FALSE(evsCam->mPreStopCalled);
EXPECT_FALSE(evsCam->mPostStopCalled);
EXPECT_TRUE(evsCam->mStreamStarted);
EXPECT_FALSE(evsCam->mStreamStopped);
EXPECT_EQ(evsCam->mStreamState, StreamState::RUNNING);
evsCam->stopVideoStream();
EXPECT_TRUE(evsCam->mPreStartCalled);
EXPECT_TRUE(evsCam->mPostStartCalled);
EXPECT_TRUE(evsCam->mPreStopCalled);
EXPECT_TRUE(evsCam->mPostStopCalled);
EXPECT_TRUE(evsCam->mStreamStarted);
EXPECT_TRUE(evsCam->mStreamStopped);
EXPECT_EQ(evsCam->mStreamState, StreamState::STOPPED);
evsCam->shutdown();
EXPECT_EQ(evsCam->mStreamState, StreamState::DEAD);
}
} // namespace aidl::android::hardware::automotive::evs::implementation

View file

@ -42,6 +42,5 @@ aidl_interface {
imports: [],
},
],
frozen: true,
frozen: false,
}

View file

@ -40,4 +40,10 @@ interface IRemoteAccess {
void setRemoteTaskCallback(android.hardware.automotive.remoteaccess.IRemoteTaskCallback callback);
void clearRemoteTaskCallback();
void notifyApStateChange(in android.hardware.automotive.remoteaccess.ApState state);
boolean isTaskScheduleSupported();
void scheduleTask(in android.hardware.automotive.remoteaccess.ScheduleInfo scheduleInfo);
void unscheduleTask(String clientId, String scheduleId);
void unscheduleAllTasks(String clientId);
boolean isTaskScheduled(String clientId, String scheduleId);
List<android.hardware.automotive.remoteaccess.ScheduleInfo> getAllScheduledTasks(String clientId);
}

View file

@ -0,0 +1,43 @@
/*
* Copyright (C) 2023 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.
*/
///////////////////////////////////////////////////////////////////////////////
// THIS FILE IS IMMUTABLE. DO NOT EDIT IN ANY CASE. //
///////////////////////////////////////////////////////////////////////////////
// This file is a snapshot of an AIDL file. Do not edit it manually. There are
// two cases:
// 1). this is a frozen version file - do not edit this in any case.
// 2). this is a 'current' file. If you make a backwards compatible change to
// the interface (from the latest frozen version), the build system will
// prompt you to update this file with `m <name>-update-api`.
//
// You must not make a backward incompatible change to any AIDL file built
// with the aidl_interface module type with versions property set. The module
// type is used to build AIDL files in a way that they can be used across
// independently updatable components of the system. If a device is shipped
// with such a backward incompatible change, it has a high risk of breaking
// later when a module using the interface is updated, e.g., Mainline modules.
package android.hardware.automotive.remoteaccess;
@JavaDerive(equals=true, toString=true) @VintfStability
parcelable ScheduleInfo {
String clientId;
String scheduleId;
byte[] taskData;
int count;
long startTimeInEpochSeconds;
long periodicInSeconds;
}

View file

@ -18,6 +18,7 @@ package android.hardware.automotive.remoteaccess;
import android.hardware.automotive.remoteaccess.ApState;
import android.hardware.automotive.remoteaccess.IRemoteTaskCallback;
import android.hardware.automotive.remoteaccess.ScheduleInfo;
/**
* Interface representing a remote wakeup client.
@ -96,4 +97,69 @@ interface IRemoteAccess {
* <p>If {@code isWakeupRequired} is false, it must not try to wake up AP.
*/
void notifyApStateChange(in ApState state);
/**
* Returns whether task scheduling is supported.
*
* <p>If this returns {@code true}, user may use {@link scheduleTask} to schedule a task to be
* executed at a later time. If the device is off when the task is scheduled to be executed,
* the device will be woken up to execute the task.
*
* @return {@code true} if serverless remote task scheduling is supported.
*/
boolean isTaskScheduleSupported();
/**
* Schedules a task to be executed later even when the vehicle is off.
*
* <p>If {@link isTaskScheduleSupported} returns {@code false}. This is no-op.
*
* <p>This sends a scheduled task message to a device external to Android so that the device
* can wake up Android and deliver the task through {@link IRemoteTaskCallback}.
*
* <p>Note that the scheduled task execution is on a best-effort basis. Multiple situations
* might cause the task not to execute successfully:
*
* <ul>
* <li>The vehicle is low on battery and the other device decides not to wake up Android.
* <li>User turns off vehicle while the task is executing.
* <li>The task logic itself fails.
*
* <p>Must return {@code EX_ILLEGAL_ARGUMENT} if a pending schedule with the same
* {@code scheduleId} for this client exists.
*/
void scheduleTask(in ScheduleInfo scheduleInfo);
/**
* Unschedules a scheduled task.
*
* <p>If {@link isTaskScheduleSupported} returns {@code false}. This is no-op.
*
* <p>Does nothing if a pending schedule with {@code clientId} and {@code scheduleId} does not
* exist.
*/
void unscheduleTask(String clientId, String scheduleId);
/**
* Unschedules all scheduled tasks for the client.
*
* <p>If {@link isTaskScheduleSupported} returns {@code false}. This is no-op.
*/
void unscheduleAllTasks(String clientId);
/**
* Returns whether the specified task is scheduled.
*
* <p>If {@link isTaskScheduleSupported} returns {@code false}, This must return {@code false}.
*/
boolean isTaskScheduled(String clientId, String scheduleId);
/**
* Gets all pending scheduled tasks for the client.
*
* <p>If {@link isTaskScheduleSupported} returns {@code false}. This must return empty array.
*
* <p>The finished scheduled tasks will not be included.
*/
List<ScheduleInfo> getAllScheduledTasks(String clientId);
}

View file

@ -0,0 +1,62 @@
/*
* Copyright (C) 2023 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.
*/
package android.hardware.automotive.remoteaccess;
@VintfStability
@JavaDerive(equals=true, toString=true)
parcelable ScheduleInfo {
/**
* The ID used to identify the client this schedule is for. This must be one of the
* preconfigured remote access serverless client ID defined in car service resource
* {@code R.xml.remote_access_serverless_client_map}.
*/
String clientId;
/**
* A unique scheduling ID (among the same client). Adding a new schedule info with a duplicate
* scheduleId will return {@code EX_ILLEGAL_ARGUMENT}.
*/
String scheduleId;
/**
* The opaque task data that will be sent back to the remote task client app when the task is
* executed. It is not interpreted/parsed by the Android system.
*/
byte[] taskData;
/**
* How many times this task will be executed. 0 means infinite.
*
* <p>This must be >= 0.
*/
int count;
/**
* The start time in epoch seconds.
*
* <p>The external device issuing remote task must have a clock synced with the
* {@code System.currentTimeMillis()} used in Android system.
*
* <p>Optionally, the VHAL property {@code EPOCH_TIME} can be used to sync the time.
*
* <p>This must be >= 0.
*/
long startTimeInEpochSeconds;
/**
* The interval (in seconds) between scheduled task execution.
*
* <p>This must be >=0. This is not useful when {@code count} is 1. If this is 0,
* The tasks will be delivered multiple times with no interval in between.
*/
long periodicInSeconds;
}

View file

@ -48,17 +48,17 @@ cc_defaults {
}
cc_binary {
name: "android.hardware.automotive.remoteaccess@V1-default-service",
name: "android.hardware.automotive.remoteaccess@V2-default-service",
defaults: ["remote-access-hal-defaults"],
vintf_fragments: ["remoteaccess-default-service.xml"],
init_rc: ["remoteaccess-default-service.rc"],
cflags: [
"-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
"-DGRPC_SERVICE_ADDRESS=\"10.0.2.2:50051\"",
],
}
cc_binary {
name: "android.hardware.automotive.remoteaccess@V1-tcu-test-service",
name: "android.hardware.automotive.remoteaccess@V2-tcu-test-service",
defaults: ["remote-access-hal-defaults"],
vintf_fragments: ["remoteaccess-default-service.xml"],
init_rc: ["remoteaccess-tcu-test-service.rc"],
@ -77,7 +77,7 @@ cc_library {
"src/RemoteAccessService.cpp",
],
whole_static_libs: [
"android.hardware.automotive.remoteaccess-V1-ndk",
"android.hardware.automotive.remoteaccess-V2-ndk",
"wakeup_client_protos",
"libvhalclient",
],
@ -99,7 +99,7 @@ cc_library {
}
cc_fuzz {
name: "android.hardware.automotive.remoteaccess@V1-default-service.aidl_fuzzer",
name: "android.hardware.automotive.remoteaccess@V2-default-service.aidl_fuzzer",
srcs: ["fuzzer/fuzzer.cpp"],
whole_static_libs: [
"RemoteAccessService",

View file

@ -55,6 +55,31 @@ class MockGrpcClientStub : public WakeupClient::StubInterface {
return Status::OK;
}
Status ScheduleTask(ClientContext* context, const ScheduleTaskRequest& request,
ScheduleTaskResponse* response) {
return Status::OK;
}
Status UnscheduleTask(ClientContext* context, const UnscheduleTaskRequest& request,
UnscheduleTaskResponse* response) {
return Status::OK;
}
Status UnscheduleAllTasks(ClientContext* context, const UnscheduleAllTasksRequest& request,
UnscheduleAllTasksResponse* response) {
return Status::OK;
}
Status IsTaskScheduled(ClientContext* context, const IsTaskScheduledRequest& request,
IsTaskScheduledResponse* response) {
return Status::OK;
}
Status GetAllScheduledTasks(ClientContext* context, const GetAllScheduledTasksRequest& request,
GetAllScheduledTasksResponse* response) {
return Status::OK;
}
// Async methods which we do not care.
ClientAsyncReaderInterface<GetRemoteTasksResponse>* AsyncGetRemoteTasksRaw(
[[maybe_unused]] ClientContext* context,
@ -83,6 +108,76 @@ class MockGrpcClientStub : public WakeupClient::StubInterface {
[[maybe_unused]] CompletionQueue* c) {
return nullptr;
}
ClientAsyncResponseReaderInterface<ScheduleTaskResponse>* AsyncScheduleTaskRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const ScheduleTaskRequest& request,
[[maybe_unused]] CompletionQueue* cq) {
return nullptr;
}
ClientAsyncResponseReaderInterface<ScheduleTaskResponse>* PrepareAsyncScheduleTaskRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const ScheduleTaskRequest& request,
[[maybe_unused]] CompletionQueue* c) {
return nullptr;
}
ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>* AsyncUnscheduleTaskRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const UnscheduleTaskRequest& request,
[[maybe_unused]] CompletionQueue* cq) {
return nullptr;
}
ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>* PrepareAsyncUnscheduleTaskRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const UnscheduleTaskRequest& request,
[[maybe_unused]] CompletionQueue* c) {
return nullptr;
}
ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>* AsyncUnscheduleAllTasksRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const UnscheduleAllTasksRequest& request,
[[maybe_unused]] CompletionQueue* cq) {
return nullptr;
}
ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>*
PrepareAsyncUnscheduleAllTasksRaw([[maybe_unused]] ClientContext* context,
[[maybe_unused]] const UnscheduleAllTasksRequest& request,
[[maybe_unused]] CompletionQueue* c) {
return nullptr;
}
ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>* AsyncIsTaskScheduledRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const IsTaskScheduledRequest& request,
[[maybe_unused]] CompletionQueue* cq) {
return nullptr;
}
ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>* PrepareAsyncIsTaskScheduledRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const IsTaskScheduledRequest& request,
[[maybe_unused]] CompletionQueue* c) {
return nullptr;
}
ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>* AsyncGetAllScheduledTasksRaw(
[[maybe_unused]] ClientContext* context,
[[maybe_unused]] const GetAllScheduledTasksRequest& request,
[[maybe_unused]] CompletionQueue* cq) {
return nullptr;
}
ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>*
PrepareAsyncGetAllScheduledTasksRaw([[maybe_unused]] ClientContext* context,
[[maybe_unused]] const GetAllScheduledTasksRequest& request,
[[maybe_unused]] CompletionQueue* c) {
return nullptr;
}
};
} // namespace remoteaccess

View file

@ -21,6 +21,7 @@
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteAccess.h>
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
#include <aidl/android/hardware/automotive/remoteaccess/IRemoteTaskCallback.h>
#include <aidl/android/hardware/automotive/remoteaccess/ScheduleInfo.h>
#include <android-base/thread_annotations.h>
#include <android/binder_auto_utils.h>
#include <utils/SystemClock.h>
@ -78,6 +79,25 @@ class RemoteAccessService
ndk::ScopedAStatus notifyApStateChange(
const aidl::android::hardware::automotive::remoteaccess::ApState& newState) override;
ndk::ScopedAStatus isTaskScheduleSupported(bool* out) override;
ndk::ScopedAStatus scheduleTask(
const aidl::android::hardware::automotive::remoteaccess::ScheduleInfo& scheduleInfo)
override;
ndk::ScopedAStatus unscheduleTask(const std::string& clientId,
const std::string& scheduleId) override;
ndk::ScopedAStatus unscheduleAllTasks(const std::string& clientId) override;
ndk::ScopedAStatus isTaskScheduled(const std::string& clientId, const std::string& scheduleId,
bool* out) override;
ndk::ScopedAStatus getAllScheduledTasks(
const std::string& clientId,
std::vector<aidl::android::hardware::automotive::remoteaccess::ScheduleInfo>* out)
override;
binder_status_t dump(int fd, const char** args, uint32_t numArgs) override;
private:

View file

@ -18,6 +18,12 @@ syntax = "proto3";
package android.hardware.automotive.remoteaccess;
enum ErrorCode {
OK = 0;
UNSPECIFIED = 1;
INVALID_ARG = 2;
}
/**
* Service provided by a wakeup client running on TCU.
*/
@ -50,6 +56,50 @@ service WakeupClient {
* to wake up AP.
*/
rpc NotifyWakeupRequired(NotifyWakeupRequiredRequest) returns (NotifyWakeupRequiredResponse) {}
/**
* Schedules a task to be executed later even when the vehicle is off.
*
* <p>This sends a scheduled task message to a device external to Android so that the device
* can wake up Android and deliver the task through {@link IRemoteTaskCallback}.
*
* <p>Note that the scheduled task execution is on a best-effort basis. Multiple situations
* might cause the task not to execute successfully:
*
* <ul>
* <li>The vehicle is low on battery and the other device decides not to wake up Android.
* <li>User turns off vehicle while the task is executing.
* <li>The task logic itself fails.
*
* <p>Must return a response with error code: {@code INVALID_ARG} if a pending schedule with the
* same {@code scheduleId} for this client exists.
*/
rpc ScheduleTask(ScheduleTaskRequest) returns (ScheduleTaskResponse) {}
/**
* Unschedules a scheduled task.
*
* <p>Does nothing if a pending schedule with {@code clientId} and {@code scheduleId} does not
* exist.
*/
rpc UnscheduleTask(UnscheduleTaskRequest) returns (UnscheduleTaskResponse) {}
/**
* Unschedules all scheduled tasks for the client.
*/
rpc UnscheduleAllTasks(UnscheduleAllTasksRequest) returns (UnscheduleAllTasksResponse) {}
/**
* Returns whether the specified task is scheduled.
*/
rpc IsTaskScheduled(IsTaskScheduledRequest) returns (IsTaskScheduledResponse) {}
/**
* Gets all pending scheduled tasks for the client.
*
* <p>The finished scheduled tasks will not be included.
*/
rpc GetAllScheduledTasks(GetAllScheduledTasksRequest) returns (GetAllScheduledTasksResponse) {}
}
message GetRemoteTasksRequest {}
@ -64,3 +114,50 @@ message NotifyWakeupRequiredRequest {
}
message NotifyWakeupRequiredResponse {}
message ScheduleTaskRequest {
GrpcScheduleInfo scheduleInfo = 1;
}
message ScheduleTaskResponse {
ErrorCode errorCode = 1;
}
message GrpcScheduleInfo {
string clientId = 1;
string scheduleId = 2;
bytes data = 3;
int32 count = 4;
int64 startTimeInEpochSeconds = 5;
int64 periodicInSeconds = 6;
}
message UnscheduleTaskRequest {
string clientId = 1;
string scheduleId = 2;
}
message UnscheduleTaskResponse {}
message UnscheduleAllTasksRequest {
string clientId = 1;
}
message UnscheduleAllTasksResponse {}
message IsTaskScheduledRequest {
string clientId = 1;
string scheduleId = 2;
}
message IsTaskScheduledResponse {
bool isTaskScheduled = 1;
}
message GetAllScheduledTasksRequest {
string clientId = 1;
}
message GetAllScheduledTasksResponse {
repeated GrpcScheduleInfo allScheduledTasks = 1;
}

View file

@ -1,4 +1,4 @@
service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-default-service
service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V2-default-service
class hal
user vehicle_network
group system inet

View file

@ -1,7 +1,7 @@
<manifest version="1.0" type="device">
<hal format="aidl">
<name>android.hardware.automotive.remoteaccess</name>
<version>1</version>
<version>2</version>
<fqname>IRemoteAccess/default</fqname>
</hal>
</manifest>

View file

@ -1,4 +1,4 @@
service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V1-tcu-test-service
service vendor.remoteaccess-default /vendor/bin/hw/android.hardware.automotive.remoteaccess@V2-tcu-test-service
class hal
user vehicle_network
group system inet

View file

@ -30,12 +30,12 @@
constexpr char SERVICE_NAME[] = "android.hardware.automotive.remoteaccess.IRemoteAccess/default";
int main(int /* argc */, char* /* argv */[]) {
LOG(INFO) << "Registering RemoteAccessService as service...";
#ifndef GRPC_SERVICE_ADDRESS
LOG(ERROR) << "GRPC_SERVICE_ADDRESS is not defined, exiting";
exit(1);
#endif
LOG(INFO) << "Registering RemoteAccessService as service, server: " << GRPC_SERVICE_ADDRESS
<< "...";
grpc::ChannelArguments grpcargs = {};
#ifdef GRPC_SERVICE_IFNAME
@ -48,8 +48,7 @@ int main(int /* argc */, char* /* argv */[]) {
android::netdevice::WaitCondition::PRESENT_AND_UP);
LOG(INFO) << "Waiting for interface: " << GRPC_SERVICE_IFNAME << " done";
#endif
auto channel = grpc::CreateCustomChannel(GRPC_SERVICE_ADDRESS,
grpc::InsecureChannelCredentials(), grpcargs);
auto channel = grpc::CreateChannel(GRPC_SERVICE_ADDRESS, grpc::InsecureChannelCredentials());
auto clientStub = android::hardware::automotive::remoteaccess::WakeupClient::NewStub(channel);
auto service = ndk::SharedRefBase::make<
android::hardware::automotive::remoteaccess::RemoteAccessService>(clientStub.get());

View file

@ -39,6 +39,7 @@ namespace {
using ::aidl::android::hardware::automotive::remoteaccess::ApState;
using ::aidl::android::hardware::automotive::remoteaccess::IRemoteTaskCallback;
using ::aidl::android::hardware::automotive::remoteaccess::ScheduleInfo;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::android::base::Error;
using ::android::base::ParseInt;
@ -313,6 +314,109 @@ ScopedAStatus RemoteAccessService::notifyApStateChange(const ApState& newState)
return ScopedAStatus::ok();
}
ScopedAStatus RemoteAccessService::isTaskScheduleSupported(bool* out) {
*out = true;
return ScopedAStatus::ok();
}
ScopedAStatus RemoteAccessService::scheduleTask(const ScheduleInfo& scheduleInfo) {
ClientContext context;
ScheduleTaskRequest request = {};
ScheduleTaskResponse response = {};
request.mutable_scheduleinfo()->set_clientid(scheduleInfo.clientId);
request.mutable_scheduleinfo()->set_scheduleid(scheduleInfo.scheduleId);
request.mutable_scheduleinfo()->set_data(scheduleInfo.taskData.data(),
scheduleInfo.taskData.size());
request.mutable_scheduleinfo()->set_count(scheduleInfo.count);
request.mutable_scheduleinfo()->set_starttimeinepochseconds(
scheduleInfo.startTimeInEpochSeconds);
request.mutable_scheduleinfo()->set_periodicinseconds(scheduleInfo.periodicInSeconds);
Status status = mGrpcStub->ScheduleTask(&context, request, &response);
if (!status.ok()) {
return rpcStatusToScopedAStatus(status, "Failed to call ScheduleTask");
}
int errorCode = response.errorcode();
switch (errorCode) {
case ErrorCode::OK:
return ScopedAStatus::ok();
case ErrorCode::INVALID_ARG:
return ScopedAStatus::fromExceptionCode(EX_ILLEGAL_ARGUMENT);
default:
// Should not happen.
return ScopedAStatus::fromServiceSpecificErrorWithMessage(
-1, ("Got unknown error code: " + ErrorCode_Name(errorCode) +
" from remote access HAL")
.c_str());
}
}
ScopedAStatus RemoteAccessService::unscheduleTask(const std::string& clientId,
const std::string& scheduleId) {
ClientContext context;
UnscheduleTaskRequest request = {};
UnscheduleTaskResponse response = {};
request.set_clientid(clientId);
request.set_scheduleid(scheduleId);
Status status = mGrpcStub->UnscheduleTask(&context, request, &response);
if (!status.ok()) {
return rpcStatusToScopedAStatus(status, "Failed to call UnscheduleTask");
}
return ScopedAStatus::ok();
}
ScopedAStatus RemoteAccessService::unscheduleAllTasks(const std::string& clientId) {
ClientContext context;
UnscheduleAllTasksRequest request = {};
UnscheduleAllTasksResponse response = {};
request.set_clientid(clientId);
Status status = mGrpcStub->UnscheduleAllTasks(&context, request, &response);
if (!status.ok()) {
return rpcStatusToScopedAStatus(status, "Failed to call UnscheduleAllTasks");
}
return ScopedAStatus::ok();
}
ScopedAStatus RemoteAccessService::isTaskScheduled(const std::string& clientId,
const std::string& scheduleId, bool* out) {
ClientContext context;
IsTaskScheduledRequest request = {};
IsTaskScheduledResponse response = {};
request.set_clientid(clientId);
request.set_scheduleid(scheduleId);
Status status = mGrpcStub->IsTaskScheduled(&context, request, &response);
if (!status.ok()) {
return rpcStatusToScopedAStatus(status, "Failed to call isTaskScheduled");
}
*out = response.istaskscheduled();
return ScopedAStatus::ok();
}
ScopedAStatus RemoteAccessService::getAllScheduledTasks(const std::string& clientId,
std::vector<ScheduleInfo>* out) {
ClientContext context;
GetAllScheduledTasksRequest request = {};
GetAllScheduledTasksResponse response = {};
request.set_clientid(clientId);
Status status = mGrpcStub->GetAllScheduledTasks(&context, request, &response);
if (!status.ok()) {
return rpcStatusToScopedAStatus(status, "Failed to call isTaskScheduled");
}
out->clear();
for (int i = 0; i < response.allscheduledtasks_size(); i++) {
const GrpcScheduleInfo& rpcScheduleInfo = response.allscheduledtasks(i);
ScheduleInfo scheduleInfo = {
.clientId = rpcScheduleInfo.clientid(),
.scheduleId = rpcScheduleInfo.scheduleid(),
.taskData = stringToBytes(rpcScheduleInfo.data()),
.count = rpcScheduleInfo.count(),
.startTimeInEpochSeconds = rpcScheduleInfo.starttimeinepochseconds(),
.periodicInSeconds = rpcScheduleInfo.periodicinseconds(),
};
out->push_back(std::move(scheduleInfo));
}
return ScopedAStatus::ok();
}
bool RemoteAccessService::checkDumpPermission() {
uid_t uid = AIBinder_getCallingUid();
return uid == AID_ROOT || uid == AID_SHELL || uid == AID_SYSTEM;

View file

@ -21,6 +21,7 @@
#include <aidl/android/hardware/automotive/remoteaccess/ApState.h>
#include <aidl/android/hardware/automotive/remoteaccess/BnRemoteTaskCallback.h>
#include <aidl/android/hardware/automotive/vehicle/VehiclePropValue.h>
#include <android/binder_status.h>
#include <gmock/gmock.h>
#include <grpcpp/test/mock_stream.h>
#include <gtest/gtest.h>
@ -46,6 +47,7 @@ using ::android::frameworks::automotive::vhal::VhalClientResult;
using ::aidl::android::hardware::automotive::remoteaccess::ApState;
using ::aidl::android::hardware::automotive::remoteaccess::BnRemoteTaskCallback;
using ::aidl::android::hardware::automotive::remoteaccess::ScheduleInfo;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropValue;
using ::grpc::ClientAsyncReaderInterface;
@ -63,6 +65,12 @@ using ::testing::Return;
using ::testing::SetArgPointee;
constexpr char kTestVin[] = "test_VIN";
const std::string kTestClientId = "test client id";
const std::string kTestScheduleId = "test schedule id";
const std::vector<uint8_t> kTestData = {0xde, 0xad, 0xbe, 0xef};
constexpr int32_t kTestCount = 1234;
constexpr int64_t kTestStartTimeInEpochSeconds = 2345;
constexpr int64_t kTestPeriodicInSeconds = 123;
} // namespace
@ -73,6 +81,21 @@ class MockGrpcClientStub : public WakeupClient::StubInterface {
MOCK_METHOD(Status, NotifyWakeupRequired,
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
NotifyWakeupRequiredResponse* response));
MOCK_METHOD(Status, ScheduleTask,
(ClientContext * context, const ScheduleTaskRequest& request,
ScheduleTaskResponse* response));
MOCK_METHOD(Status, UnscheduleTask,
(ClientContext * context, const UnscheduleTaskRequest& request,
UnscheduleTaskResponse* response));
MOCK_METHOD(Status, UnscheduleAllTasks,
(ClientContext * context, const UnscheduleAllTasksRequest& request,
UnscheduleAllTasksResponse* response));
MOCK_METHOD(Status, IsTaskScheduled,
(ClientContext * context, const IsTaskScheduledRequest& request,
IsTaskScheduledResponse* response));
MOCK_METHOD(Status, GetAllScheduledTasks,
(ClientContext * context, const GetAllScheduledTasksRequest& request,
GetAllScheduledTasksResponse* response));
// Async methods which we do not care.
MOCK_METHOD(ClientAsyncReaderInterface<GetRemoteTasksResponse>*, AsyncGetRemoteTasksRaw,
(ClientContext * context, const GetRemoteTasksRequest& request, CompletionQueue* cq,
@ -88,6 +111,42 @@ class MockGrpcClientStub : public WakeupClient::StubInterface {
PrepareAsyncNotifyWakeupRequiredRaw,
(ClientContext * context, const NotifyWakeupRequiredRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<ScheduleTaskResponse>*, AsyncScheduleTaskRaw,
(ClientContext * context, const ScheduleTaskRequest& request, CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<ScheduleTaskResponse>*,
PrepareAsyncScheduleTaskRaw,
(ClientContext * context, const ScheduleTaskRequest& request, CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>*, AsyncUnscheduleTaskRaw,
(ClientContext * context, const UnscheduleTaskRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleTaskResponse>*,
PrepareAsyncUnscheduleTaskRaw,
(ClientContext * context, const UnscheduleTaskRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>*,
AsyncUnscheduleAllTasksRaw,
(ClientContext * context, const UnscheduleAllTasksRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<UnscheduleAllTasksResponse>*,
PrepareAsyncUnscheduleAllTasksRaw,
(ClientContext * context, const UnscheduleAllTasksRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>*,
AsyncIsTaskScheduledRaw,
(ClientContext * context, const IsTaskScheduledRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<IsTaskScheduledResponse>*,
PrepareAsyncIsTaskScheduledRaw,
(ClientContext * context, const IsTaskScheduledRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>*,
AsyncGetAllScheduledTasksRaw,
(ClientContext * context, const GetAllScheduledTasksRequest& request,
CompletionQueue* cq));
MOCK_METHOD(ClientAsyncResponseReaderInterface<GetAllScheduledTasksResponse>*,
PrepareAsyncGetAllScheduledTasksRaw,
(ClientContext * context, const GetAllScheduledTasksRequest& request,
CompletionQueue* cq));
};
class FakeVhalClient final : public android::frameworks::automotive::vhal::IVhalClient {
@ -367,6 +426,174 @@ TEST_F(RemoteAccessServiceUnitTest, testGetVehicleId) {
ASSERT_EQ(vehicleId, kTestVin);
}
TEST_F(RemoteAccessServiceUnitTest, TestIsTaskScheduleSupported) {
bool out = false;
ScopedAStatus status = getService()->isTaskScheduleSupported(&out);
EXPECT_TRUE(status.isOk());
EXPECT_TRUE(out);
}
TEST_F(RemoteAccessServiceUnitTest, TestScheduleTask) {
ScheduleTaskRequest grpcRequest = {};
EXPECT_CALL(*getGrpcWakeupClientStub(), ScheduleTask)
.WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
const ScheduleTaskRequest& request,
[[maybe_unused]] ScheduleTaskResponse* response) {
grpcRequest = request;
return Status();
});
ScheduleInfo scheduleInfo = {
.clientId = kTestClientId,
.scheduleId = kTestScheduleId,
.taskData = kTestData,
.count = kTestCount,
.startTimeInEpochSeconds = kTestStartTimeInEpochSeconds,
.periodicInSeconds = kTestPeriodicInSeconds,
};
ScopedAStatus status = getService()->scheduleTask(scheduleInfo);
ASSERT_TRUE(status.isOk());
EXPECT_EQ(grpcRequest.scheduleinfo().clientid(), kTestClientId);
EXPECT_EQ(grpcRequest.scheduleinfo().scheduleid(), kTestScheduleId);
EXPECT_EQ(grpcRequest.scheduleinfo().data(), std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(grpcRequest.scheduleinfo().count(), kTestCount);
EXPECT_EQ(grpcRequest.scheduleinfo().starttimeinepochseconds(), kTestStartTimeInEpochSeconds);
EXPECT_EQ(grpcRequest.scheduleinfo().periodicinseconds(), kTestPeriodicInSeconds);
}
TEST_F(RemoteAccessServiceUnitTest, TestScheduleTask_InvalidArg) {
EXPECT_CALL(*getGrpcWakeupClientStub(), ScheduleTask)
.WillOnce([]([[maybe_unused]] ClientContext* context,
[[maybe_unused]] const ScheduleTaskRequest& request,
ScheduleTaskResponse* response) {
response->set_errorcode(ErrorCode::INVALID_ARG);
return Status();
});
ScheduleInfo scheduleInfo = {
.clientId = kTestClientId,
.scheduleId = kTestScheduleId,
.taskData = kTestData,
.count = kTestCount,
.startTimeInEpochSeconds = kTestStartTimeInEpochSeconds,
.periodicInSeconds = kTestPeriodicInSeconds,
};
ScopedAStatus status = getService()->scheduleTask(scheduleInfo);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getExceptionCode(), EX_ILLEGAL_ARGUMENT);
}
TEST_F(RemoteAccessServiceUnitTest, TestScheduleTask_UnspecifiedError) {
EXPECT_CALL(*getGrpcWakeupClientStub(), ScheduleTask)
.WillOnce([]([[maybe_unused]] ClientContext* context,
[[maybe_unused]] const ScheduleTaskRequest& request,
ScheduleTaskResponse* response) {
response->set_errorcode(ErrorCode::UNSPECIFIED);
return Status();
});
ScheduleInfo scheduleInfo = {
.clientId = kTestClientId,
.scheduleId = kTestScheduleId,
.taskData = kTestData,
.count = kTestCount,
.startTimeInEpochSeconds = kTestStartTimeInEpochSeconds,
.periodicInSeconds = kTestPeriodicInSeconds,
};
ScopedAStatus status = getService()->scheduleTask(scheduleInfo);
ASSERT_FALSE(status.isOk());
ASSERT_EQ(status.getExceptionCode(), EX_SERVICE_SPECIFIC);
}
TEST_F(RemoteAccessServiceUnitTest, TestUnscheduleTask) {
UnscheduleTaskRequest grpcRequest = {};
EXPECT_CALL(*getGrpcWakeupClientStub(), UnscheduleTask)
.WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
const UnscheduleTaskRequest& request,
[[maybe_unused]] UnscheduleTaskResponse* response) {
grpcRequest = request;
return Status();
});
ScopedAStatus status = getService()->unscheduleTask(kTestClientId, kTestScheduleId);
ASSERT_TRUE(status.isOk());
EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
EXPECT_EQ(grpcRequest.scheduleid(), kTestScheduleId);
}
TEST_F(RemoteAccessServiceUnitTest, TestUnscheduleAllTasks) {
UnscheduleAllTasksRequest grpcRequest = {};
EXPECT_CALL(*getGrpcWakeupClientStub(), UnscheduleAllTasks)
.WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
const UnscheduleAllTasksRequest& request,
[[maybe_unused]] UnscheduleAllTasksResponse* response) {
grpcRequest = request;
return Status();
});
ScopedAStatus status = getService()->unscheduleAllTasks(kTestClientId);
ASSERT_TRUE(status.isOk());
EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
}
TEST_F(RemoteAccessServiceUnitTest, TestIsTaskScheduled) {
bool isTaskScheduled = false;
IsTaskScheduledRequest grpcRequest = {};
EXPECT_CALL(*getGrpcWakeupClientStub(), IsTaskScheduled)
.WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
const IsTaskScheduledRequest& request,
IsTaskScheduledResponse* response) {
grpcRequest = request;
response->set_istaskscheduled(true);
return Status();
});
ScopedAStatus status =
getService()->isTaskScheduled(kTestClientId, kTestScheduleId, &isTaskScheduled);
ASSERT_TRUE(status.isOk());
EXPECT_TRUE(isTaskScheduled);
EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
EXPECT_EQ(grpcRequest.scheduleid(), kTestScheduleId);
}
TEST_F(RemoteAccessServiceUnitTest, testGetAllScheduledTasks) {
std::vector<ScheduleInfo> result;
GetAllScheduledTasksRequest grpcRequest = {};
EXPECT_CALL(*getGrpcWakeupClientStub(), GetAllScheduledTasks)
.WillOnce([&grpcRequest]([[maybe_unused]] ClientContext* context,
const GetAllScheduledTasksRequest& request,
GetAllScheduledTasksResponse* response) {
grpcRequest = request;
GrpcScheduleInfo* newInfo = response->add_allscheduledtasks();
newInfo->set_clientid(kTestClientId);
newInfo->set_scheduleid(kTestScheduleId);
newInfo->set_data(kTestData.data(), kTestData.size());
newInfo->set_count(kTestCount);
newInfo->set_starttimeinepochseconds(kTestStartTimeInEpochSeconds);
newInfo->set_periodicinseconds(kTestPeriodicInSeconds);
return Status();
});
ScopedAStatus status = getService()->getAllScheduledTasks(kTestClientId, &result);
ASSERT_TRUE(status.isOk());
EXPECT_EQ(grpcRequest.clientid(), kTestClientId);
ASSERT_EQ(result.size(), 1u);
ASSERT_EQ(result[0].clientId, kTestClientId);
ASSERT_EQ(result[0].scheduleId, kTestScheduleId);
ASSERT_EQ(result[0].taskData, kTestData);
ASSERT_EQ(result[0].count, kTestCount);
ASSERT_EQ(result[0].startTimeInEpochSeconds, kTestStartTimeInEpochSeconds);
ASSERT_EQ(result[0].periodicInSeconds, kTestPeriodicInSeconds);
}
} // namespace remoteaccess
} // namespace automotive
} // namespace hardware

View file

@ -141,7 +141,7 @@ interface.
* The android lunch target: sdk_car_x86_64-userdebug and
cf_x86_64_auto-userdebug already contains the default remote access HAL. For
other lunch target, you can add the default remote access HAL by adding
'android.hardware.automotive.remoteaccess@V1-default-service' to
'android.hardware.automotive.remoteaccess@V2-default-service' to
'PRODUCT_PACKAGES' variable in mk file, see `device/generic/car/common/car.mk`
as example.

View file

@ -38,6 +38,50 @@ cc_binary {
],
cflags: [
"-Wno-unused-parameter",
"-DGRPC_SERVICE_ADDRESS=\"localhost:50051\"",
"-DGRPC_SERVICE_ADDRESS=\"127.0.0.1:50051\"",
],
}
cc_binary_host {
name: "TestWakeupClientServerHost",
srcs: ["src/*.cpp"],
local_include_dirs: ["include"],
shared_libs: [
"libbase",
"libutils",
"libgrpc++",
"libprotobuf-cpp-full",
],
whole_static_libs: [
"wakeup_client_protos",
],
cflags: [
"-Wno-unused-parameter",
"-DGRPC_SERVICE_ADDRESS=\"127.0.0.1:50051\"",
"-DHOST",
],
}
cc_test_host {
name: "TestWakeupClientServerHostUnitTest",
srcs: [
"test/*.cpp",
"src/TestWakeupClientServiceImpl.cpp",
],
local_include_dirs: ["include"],
shared_libs: [
"libbase",
"libutils",
"libgrpc++",
"libprotobuf-cpp-full",
],
static_libs: [
"libgtest",
],
whole_static_libs: [
"wakeup_client_protos",
],
cflags: [
"-Wno-unused-parameter",
],
}

View file

@ -34,11 +34,9 @@ namespace remoteaccess {
// implementation, the task should come from remote task server. This class is thread-safe.
class FakeTaskGenerator final {
public:
GetRemoteTasksResponse generateTask();
GetRemoteTasksResponse generateTask(const std::string& clientId);
private:
// Simulates the client ID for each task.
std::atomic<int> mCurrentClientId = 0;
constexpr static uint8_t DATA[] = {0xde, 0xad, 0xbe, 0xef};
};
@ -73,38 +71,52 @@ class TaskTimeoutMessageHandler final : public android::MessageHandler {
// TaskQueue is thread-safe.
class TaskQueue final {
public:
TaskQueue();
~TaskQueue();
TaskQueue(android::sp<Looper> looper);
void add(const GetRemoteTasksResponse& response);
std::optional<GetRemoteTasksResponse> maybePopOne();
void waitForTask();
void stopWait();
void handleTaskTimeout();
bool isEmpty();
private:
std::thread mCheckTaskTimeoutThread;
friend class TaskTimeoutMessageHandler;
std::mutex mLock;
std::priority_queue<TaskInfo, std::vector<TaskInfo>, TaskInfoComparator> mTasks
GUARDED_BY(mLock);
// A variable to notify mTasks is not empty.
std::condition_variable mTasksNotEmptyCv;
bool mStopped GUARDED_BY(mLock);
std::atomic<bool> mStopped;
android::sp<Looper> mLooper;
android::sp<TaskTimeoutMessageHandler> mTaskTimeoutMessageHandler;
std::atomic<int> mTaskIdCounter = 0;
void checkForTestTimeoutLoop();
void waitForTaskWithLock(std::unique_lock<std::mutex>& lock);
void loop();
void handleTaskTimeout();
};
class TestWakeupClientServiceImpl final : public WakeupClient::Service {
// forward-declaration
class TestWakeupClientServiceImpl;
class TaskScheduleMsgHandler final : public android::MessageHandler {
public:
TaskScheduleMsgHandler(TestWakeupClientServiceImpl* mImpl);
void handleMessage(const android::Message& message) override;
private:
TestWakeupClientServiceImpl* mImpl;
};
class TestWakeupClientServiceImpl : public WakeupClient::Service {
public:
TestWakeupClientServiceImpl();
~TestWakeupClientServiceImpl();
// Stop the handling for all income requests. Prepare for shutdown.
void stopServer();
grpc::Status GetRemoteTasks(grpc::ServerContext* context, const GetRemoteTasksRequest* request,
grpc::ServerWriter<GetRemoteTasksResponse>* writer) override;
@ -112,25 +124,111 @@ class TestWakeupClientServiceImpl final : public WakeupClient::Service {
const NotifyWakeupRequiredRequest* request,
NotifyWakeupRequiredResponse* response) override;
grpc::Status ScheduleTask(grpc::ServerContext* context, const ScheduleTaskRequest* request,
ScheduleTaskResponse* response) override;
grpc::Status UnscheduleTask(grpc::ServerContext* context, const UnscheduleTaskRequest* request,
UnscheduleTaskResponse* response) override;
grpc::Status UnscheduleAllTasks(grpc::ServerContext* context,
const UnscheduleAllTasksRequest* request,
UnscheduleAllTasksResponse* response) override;
grpc::Status IsTaskScheduled(grpc::ServerContext* context,
const IsTaskScheduledRequest* request,
IsTaskScheduledResponse* response) override;
grpc::Status GetAllScheduledTasks(grpc::ServerContext* context,
const GetAllScheduledTasksRequest* request,
GetAllScheduledTasksResponse* response) override;
/**
* Starts generating fake tasks for the specific client repeatedly.
*
* The fake task will have {0xDE 0xAD 0xBE 0xEF} as payload. A new fake task will be sent
* to the client every 5s.
*/
void startGeneratingFakeTask(const std::string& clientId);
/**
* stops generating fake tasks.
*/
void stopGeneratingFakeTask();
/**
* Returns whether we need to wakeup the target device to send remote tasks.
*/
bool isWakeupRequired();
/**
* Returns whether we have an active connection with the target device.
*/
bool isRemoteTaskConnectionAlive();
/**
* Injects a fake task with taskData to be sent to the specific client.
*/
void injectTask(const std::string& taskData, const std::string& clientId);
/**
* Wakes up the target device.
*
* This must be implemented by child class and contains device specific logic. E.g. this might
* be sending QEMU commands for the emulator device.
*/
virtual void wakeupApplicationProcessor() = 0;
/**
* Cleans up a scheduled task info.
*/
void cleanupScheduledTaskLocked(const std::string& clientId, const std::string& scheduleId)
REQUIRES(mLock);
private:
// This is a thread for communicating with remote wakeup server (via network) and receive tasks
// from it.
std::thread mThread;
friend class TaskScheduleMsgHandler;
struct ScheduleInfo {
std::unique_ptr<GrpcScheduleInfo> grpcScheduleInfo;
// This is a unique ID to represent this schedule. Each repeated tasks will have different
// task ID but will have the same scheduleMsgId so that we can use to unschedule. This has
// to be an int so we cannot use the scheduleId provided by the client.
int scheduleMsgId;
int64_t periodicInSeconds;
int32_t currentCount;
int32_t totalCount;
};
std::atomic<int> mScheduleMsgCounter = 0;
// This is a looper for scheduling tasks to be executed in the future.
android::sp<Looper> mLooper;
android::sp<TaskScheduleMsgHandler> mTaskScheduleMsgHandler;
// This is a thread for generating fake tasks.
std::thread mFakeTaskThread;
// This is a thread for the looper.
std::thread mLooperThread;
// A variable to notify server is stopping.
std::condition_variable mServerStoppedCv;
std::condition_variable mTaskLoopStoppedCv;
// Whether wakeup AP is required for executing tasks.
std::atomic<bool> mWakeupRequired = true;
// Whether we currently have an active long-live connection to deliver remote tasks.
std::atomic<bool> mRemoteTaskConnectionAlive = false;
std::mutex mLock;
bool mServerStopped GUARDED_BY(mLock);
bool mGeneratingFakeTask GUARDED_BY(mLock);
std::atomic<bool> mServerStopped;
std::unordered_map<std::string, std::unordered_map<std::string, ScheduleInfo>>
mInfoByScheduleIdByClientId GUARDED_BY(mLock);
// Thread-safe. For test impl only.
FakeTaskGenerator mFakeTaskGenerator;
// Thread-sfae.
TaskQueue mTaskQueue;
// Thread-safe.
std::unique_ptr<TaskQueue> mTaskQueue;
void fakeTaskGenerateLoop();
void wakeupApplicationProcessor();
void fakeTaskGenerateLoop(const std::string& clientId);
void injectTaskResponse(const GetRemoteTasksResponse& response);
bool getScheduleInfoLocked(int scheduleMsgId, ScheduleInfo** outScheduleInfoPtr)
REQUIRES(mLock);
void handleAddTask(int scheduleMsgId);
void loop();
};
} // namespace remoteaccess

View file

@ -16,8 +16,6 @@
#include "TestWakeupClientServiceImpl.h"
#include "ApPowerControl.h"
#include <android-base/stringprintf.h>
#include <inttypes.h>
#include <utils/Looper.h>
@ -39,17 +37,24 @@ using ::grpc::ServerContext;
using ::grpc::ServerWriter;
using ::grpc::Status;
constexpr int kTaskIntervalInMs = 5'000;
constexpr int64_t KTaskTimeoutInMs = 20'000;
constexpr int64_t kTaskIntervalInMs = 5'000;
constexpr int64_t kTaskTimeoutInMs = 20'000;
int64_t msToNs(int64_t ms) {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::milliseconds(ms))
.count();
}
int64_t sToNs(int64_t s) {
return std::chrono::duration_cast<std::chrono::nanoseconds>(std::chrono::seconds(s)).count();
}
} // namespace
GetRemoteTasksResponse FakeTaskGenerator::generateTask() {
int clientId = mCurrentClientId++;
GetRemoteTasksResponse FakeTaskGenerator::generateTask(const std::string& clientId) {
GetRemoteTasksResponse response;
response.set_data(std::string(reinterpret_cast<const char*>(DATA), sizeof(DATA)));
std::string clientIdStr = StringPrintf("%d", clientId);
response.set_clientid(clientIdStr);
response.set_data(reinterpret_cast<const char*>(DATA), sizeof(DATA));
response.set_clientid(clientId);
return response;
}
@ -60,26 +65,9 @@ void TaskTimeoutMessageHandler::handleMessage(const android::Message& message) {
mTaskQueue->handleTaskTimeout();
}
TaskQueue::TaskQueue() {
TaskQueue::TaskQueue(android::sp<Looper> looper) {
mTaskTimeoutMessageHandler = android::sp<TaskTimeoutMessageHandler>::make(this);
mLooper = Looper::prepare(/*opts=*/0);
mCheckTaskTimeoutThread = std::thread([this] { checkForTestTimeoutLoop(); });
}
TaskQueue::~TaskQueue() {
{
std::lock_guard<std::mutex> lockGuard(mLock);
mStopped = true;
}
while (true) {
// Remove all pending timeout handlers from queue.
if (!maybePopOne().has_value()) {
break;
}
}
if (mCheckTaskTimeoutThread.joinable()) {
mCheckTaskTimeoutThread.join();
}
mLooper = looper;
}
std::optional<GetRemoteTasksResponse> TaskQueue::maybePopOne() {
@ -105,16 +93,12 @@ void TaskQueue::add(const GetRemoteTasksResponse& task) {
.taskData = task,
});
android::Message message(taskId);
mLooper->sendMessageDelayed(KTaskTimeoutInMs * 1000, mTaskTimeoutMessageHandler, message);
mLooper->sendMessageDelayed(msToNs(kTaskTimeoutInMs), mTaskTimeoutMessageHandler, message);
mTasksNotEmptyCv.notify_all();
}
void TaskQueue::waitForTask() {
std::unique_lock<std::mutex> lock(mLock);
waitForTaskWithLock(lock);
}
void TaskQueue::waitForTaskWithLock(std::unique_lock<std::mutex>& lock) {
mTasksNotEmptyCv.wait(lock, [this] {
ScopedLockAssertion lockAssertion(mLock);
return mTasks.size() > 0 || mStopped;
@ -122,9 +106,11 @@ void TaskQueue::waitForTaskWithLock(std::unique_lock<std::mutex>& lock) {
}
void TaskQueue::stopWait() {
std::lock_guard<std::mutex> lockGuard(mLock);
mStopped = true;
mTasksNotEmptyCv.notify_all();
{
std::lock_guard<std::mutex> lockGuard(mLock);
mTasksNotEmptyCv.notify_all();
}
}
bool TaskQueue::isEmpty() {
@ -132,21 +118,6 @@ bool TaskQueue::isEmpty() {
return mTasks.size() == 0 || mStopped;
}
void TaskQueue::checkForTestTimeoutLoop() {
Looper::setForThread(mLooper);
while (true) {
{
std::unique_lock<std::mutex> lock(mLock);
if (mStopped) {
return;
}
}
mLooper->pollAll(/*timeoutMillis=*/-1);
}
}
void TaskQueue::handleTaskTimeout() {
// We know which task timed-out from the taskId in the message. However, there is no easy way
// to remove a specific task with the task ID from the priority_queue, so we just check from
@ -155,48 +126,106 @@ void TaskQueue::handleTaskTimeout() {
int64_t now = uptimeMillis();
while (mTasks.size() > 0) {
const TaskInfo& taskInfo = mTasks.top();
if (taskInfo.timestampInMs + KTaskTimeoutInMs > now) {
if (taskInfo.timestampInMs + kTaskTimeoutInMs > now) {
break;
}
// In real implementation, this should report task failure to remote wakeup server.
printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms",
printf("Task for client ID: %s timed-out, added at %" PRId64 " ms, now %" PRId64 " ms\n",
taskInfo.taskData.clientid().c_str(), taskInfo.timestampInMs, now);
mTasks.pop();
}
}
TestWakeupClientServiceImpl::TestWakeupClientServiceImpl() {
mThread = std::thread([this] { fakeTaskGenerateLoop(); });
mTaskScheduleMsgHandler = android::sp<TaskScheduleMsgHandler>::make(this);
mLooper = android::sp<Looper>::make(/*opts=*/0);
mLooperThread = std::thread([this] { loop(); });
mTaskQueue = std::make_unique<TaskQueue>(mLooper);
}
TestWakeupClientServiceImpl::~TestWakeupClientServiceImpl() {
{
std::lock_guard<std::mutex> lockGuard(mLock);
mServerStopped = true;
mServerStoppedCv.notify_all();
if (mServerStopped) {
return;
}
mTaskQueue.stopWait();
if (mThread.joinable()) {
mThread.join();
stopServer();
}
void TestWakeupClientServiceImpl::stopServer() {
mTaskQueue->stopWait();
stopGeneratingFakeTask();
// Set the flag so that the loop thread will exit.
mServerStopped = true;
mLooper->wake();
if (mLooperThread.joinable()) {
mLooperThread.join();
}
}
void TestWakeupClientServiceImpl::fakeTaskGenerateLoop() {
void TestWakeupClientServiceImpl::loop() {
Looper::setForThread(mLooper);
while (true) {
mLooper->pollAll(/*timeoutMillis=*/-1);
if (mServerStopped) {
return;
}
}
}
void TestWakeupClientServiceImpl::injectTask(const std::string& taskData,
const std::string& clientId) {
GetRemoteTasksResponse response;
response.set_data(taskData);
response.set_clientid(clientId);
injectTaskResponse(response);
}
void TestWakeupClientServiceImpl::injectTaskResponse(const GetRemoteTasksResponse& response) {
printf("Receive a new task\n");
mTaskQueue->add(response);
if (mWakeupRequired) {
wakeupApplicationProcessor();
}
}
void TestWakeupClientServiceImpl::startGeneratingFakeTask(const std::string& clientId) {
std::lock_guard<std::mutex> lockGuard(mLock);
if (mGeneratingFakeTask) {
printf("Fake task is already being generated\n");
return;
}
mGeneratingFakeTask = true;
mFakeTaskThread = std::thread([this, clientId] { fakeTaskGenerateLoop(clientId); });
printf("Started generating fake tasks\n");
}
void TestWakeupClientServiceImpl::stopGeneratingFakeTask() {
{
std::lock_guard<std::mutex> lockGuard(mLock);
if (!mGeneratingFakeTask) {
printf("Fake task is not being generated, do nothing\n");
return;
}
mTaskLoopStoppedCv.notify_all();
mGeneratingFakeTask = false;
}
if (mFakeTaskThread.joinable()) {
mFakeTaskThread.join();
}
printf("Stopped generating fake tasks\n");
}
void TestWakeupClientServiceImpl::fakeTaskGenerateLoop(const std::string& clientId) {
// In actual implementation, this should communicate with the remote server and receives tasks
// from it. Here we simulate receiving one remote task every {kTaskIntervalInMs}ms.
while (true) {
mTaskQueue.add(mFakeTaskGenerator.generateTask());
printf("Received a new task\n");
if (mWakeupRequired) {
wakeupApplicationProcessor();
}
printf("Sleeping for %d seconds until next task\n", kTaskIntervalInMs);
injectTaskResponse(mFakeTaskGenerator.generateTask(clientId));
printf("Sleeping for %" PRId64 " seconds until next task\n", kTaskIntervalInMs);
std::unique_lock lk(mLock);
if (mServerStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
if (mTaskLoopStoppedCv.wait_for(lk, std::chrono::milliseconds(kTaskIntervalInMs), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mServerStopped;
return !mGeneratingFakeTask;
})) {
// If the stopped flag is set, we are quitting, exit the loop.
return;
@ -208,11 +237,18 @@ Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
const GetRemoteTasksRequest* request,
ServerWriter<GetRemoteTasksResponse>* writer) {
printf("GetRemoteTasks called\n");
mRemoteTaskConnectionAlive = true;
while (true) {
mTaskQueue.waitForTask();
mTaskQueue->waitForTask();
if (mServerStopped) {
// Server stopped, exit the loop.
printf("Server stopped exit loop\n");
break;
}
while (true) {
auto maybeTask = mTaskQueue.maybePopOne();
auto maybeTask = mTaskQueue->maybePopOne();
if (!maybeTask.has_value()) {
// No task left, loop again and wait for another task(s).
break;
@ -225,29 +261,238 @@ Status TestWakeupClientServiceImpl::GetRemoteTasks(ServerContext* context,
printf("Failed to deliver remote task to remote access HAL\n");
// The task failed to be sent, add it back to the queue. The order might change, but
// it is okay.
mTaskQueue.add(response);
mTaskQueue->add(response);
mRemoteTaskConnectionAlive = false;
return Status::CANCELLED;
}
}
}
return Status::OK;
// Server stopped, exit the loop.
return Status::CANCELLED;
}
Status TestWakeupClientServiceImpl::NotifyWakeupRequired(ServerContext* context,
const NotifyWakeupRequiredRequest* request,
NotifyWakeupRequiredResponse* response) {
if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue.isEmpty()) {
printf("NotifyWakeupRequired called\n");
if (request->iswakeuprequired() && !mWakeupRequired && !mTaskQueue->isEmpty()) {
// If wakeup is now required and previously not required, this means we have finished
// shutting down the device. If there are still pending tasks, try waking up AP again
// to finish executing those tasks.
wakeupApplicationProcessor();
}
mWakeupRequired = request->iswakeuprequired();
if (mWakeupRequired) {
// We won't know the connection is down unless we try to send a task over. If wakeup is
// required, the connection is very likely already down.
mRemoteTaskConnectionAlive = false;
}
return Status::OK;
}
void TestWakeupClientServiceImpl::wakeupApplicationProcessor() {
wakeupAp();
void TestWakeupClientServiceImpl::cleanupScheduledTaskLocked(const std::string& clientId,
const std::string& scheduleId) {
mInfoByScheduleIdByClientId[clientId].erase(scheduleId);
if (mInfoByScheduleIdByClientId[clientId].size() == 0) {
mInfoByScheduleIdByClientId.erase(clientId);
}
}
TaskScheduleMsgHandler::TaskScheduleMsgHandler(TestWakeupClientServiceImpl* impl) : mImpl(impl) {}
void TaskScheduleMsgHandler::handleMessage(const android::Message& message) {
mImpl->handleAddTask(message.what);
}
Status TestWakeupClientServiceImpl::ScheduleTask(ServerContext* context,
const ScheduleTaskRequest* request,
ScheduleTaskResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const GrpcScheduleInfo& grpcScheduleInfo = request->scheduleinfo();
const std::string& scheduleId = grpcScheduleInfo.scheduleid();
const std::string& clientId = grpcScheduleInfo.clientid();
response->set_errorcode(ErrorCode::OK);
if (mInfoByScheduleIdByClientId.find(clientId) != mInfoByScheduleIdByClientId.end() &&
mInfoByScheduleIdByClientId[clientId].find(scheduleId) !=
mInfoByScheduleIdByClientId[clientId].end()) {
printf("Duplicate schedule Id: %s for client Id: %s\n", scheduleId.c_str(),
clientId.c_str());
response->set_errorcode(ErrorCode::INVALID_ARG);
return Status::OK;
}
int64_t startTimeInEpochSeconds = grpcScheduleInfo.starttimeinepochseconds();
int64_t periodicInSeconds = grpcScheduleInfo.periodicinseconds();
int32_t count = grpcScheduleInfo.count();
int scheduleMsgId = mScheduleMsgCounter++;
mInfoByScheduleIdByClientId[clientId][scheduleId] = {
.grpcScheduleInfo = std::make_unique<GrpcScheduleInfo>(grpcScheduleInfo),
.scheduleMsgId = scheduleMsgId,
.periodicInSeconds = periodicInSeconds,
.currentCount = 0,
.totalCount = count,
};
int64_t delayInSeconds =
startTimeInEpochSeconds - std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
if (delayInSeconds < 0) {
delayInSeconds = 0;
}
printf("ScheduleTask called with client Id: %s, schedule Id: %s, delay: %" PRId64 " s\n",
clientId.c_str(), scheduleId.c_str(), delayInSeconds);
mLooper->sendMessageDelayed(sToNs(delayInSeconds), mTaskScheduleMsgHandler,
android::Message(scheduleMsgId));
return Status::OK;
}
bool TestWakeupClientServiceImpl::getScheduleInfoLocked(int scheduleMsgId,
ScheduleInfo** outScheduleInfoPtr) {
for (auto& [_, infoByScheduleId] : mInfoByScheduleIdByClientId) {
for (auto& [_, scheduleInfo] : infoByScheduleId) {
if (scheduleInfo.scheduleMsgId == scheduleMsgId) {
*outScheduleInfoPtr = &scheduleInfo;
return true;
}
}
}
return false;
}
void TestWakeupClientServiceImpl::handleAddTask(int scheduleMsgId) {
std::lock_guard<std::mutex> lockGuard(mLock);
ScheduleInfo* scheduleInfoPtr;
bool found = getScheduleInfoLocked(scheduleMsgId, &scheduleInfoPtr);
if (!found) {
printf("The schedule msg Id: %d is not found\n", scheduleMsgId);
return;
}
const GrpcScheduleInfo& grpcScheduleInfo = *scheduleInfoPtr->grpcScheduleInfo;
const std::string scheduleId = grpcScheduleInfo.scheduleid();
const std::string clientId = grpcScheduleInfo.clientid();
GetRemoteTasksResponse injectResponse;
injectResponse.set_data(grpcScheduleInfo.data().data(), grpcScheduleInfo.data().size());
injectResponse.set_clientid(clientId);
injectTaskResponse(injectResponse);
scheduleInfoPtr->currentCount++;
printf("Sending scheduled tasks for scheduleId: %s, clientId: %s, taskCount: %d\n",
scheduleId.c_str(), clientId.c_str(), scheduleInfoPtr->currentCount);
if (scheduleInfoPtr->totalCount != 0 &&
scheduleInfoPtr->currentCount == scheduleInfoPtr->totalCount) {
// This schedule is finished.
cleanupScheduledTaskLocked(clientId, scheduleId);
return;
}
// Schedule the task for the next period.
mLooper->sendMessageDelayed(sToNs(scheduleInfoPtr->periodicInSeconds), mTaskScheduleMsgHandler,
android::Message(scheduleMsgId));
}
Status TestWakeupClientServiceImpl::UnscheduleTask(ServerContext* context,
const UnscheduleTaskRequest* request,
UnscheduleTaskResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const std::string& clientId = request->clientid();
const std::string& scheduleId = request->scheduleid();
printf("UnscheduleTask called with client Id: %s, schedule Id: %s\n", clientId.c_str(),
scheduleId.c_str());
if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end() ||
mInfoByScheduleIdByClientId[clientId].find(scheduleId) ==
mInfoByScheduleIdByClientId[clientId].end()) {
printf("UnscheduleTask: no task associated with clientId: %s, scheduleId: %s\n",
clientId.c_str(), scheduleId.c_str());
return Status::OK;
}
mLooper->removeMessages(mTaskScheduleMsgHandler,
mInfoByScheduleIdByClientId[clientId][scheduleId].scheduleMsgId);
cleanupScheduledTaskLocked(clientId, scheduleId);
return Status::OK;
}
Status TestWakeupClientServiceImpl::UnscheduleAllTasks(ServerContext* context,
const UnscheduleAllTasksRequest* request,
UnscheduleAllTasksResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const std::string& clientId = request->clientid();
printf("UnscheduleAllTasks called with client Id: %s\n", clientId.c_str());
if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
printf("UnscheduleTask: no task associated with clientId: %s\n", clientId.c_str());
return Status::OK;
}
const auto& infoByScheduleId = mInfoByScheduleIdByClientId[clientId];
std::vector<int> scheduleMsgIds;
for (const auto& [_, scheduleInfo] : infoByScheduleId) {
mLooper->removeMessages(mTaskScheduleMsgHandler, /*what=*/scheduleInfo.scheduleMsgId);
}
mInfoByScheduleIdByClientId.erase(clientId);
return Status::OK;
}
Status TestWakeupClientServiceImpl::IsTaskScheduled(ServerContext* context,
const IsTaskScheduledRequest* request,
IsTaskScheduledResponse* response) {
std::lock_guard<std::mutex> lockGuard(mLock);
const std::string& clientId = request->clientid();
const std::string& scheduleId = request->scheduleid();
printf("IsTaskScheduled called with client Id: %s, scheduleId: %s\n", clientId.c_str(),
scheduleId.c_str());
if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
response->set_istaskscheduled(false);
return Status::OK;
}
if (mInfoByScheduleIdByClientId[clientId].find(scheduleId) ==
mInfoByScheduleIdByClientId[clientId].end()) {
response->set_istaskscheduled(false);
return Status::OK;
}
response->set_istaskscheduled(true);
return Status::OK;
}
Status TestWakeupClientServiceImpl::GetAllScheduledTasks(ServerContext* context,
const GetAllScheduledTasksRequest* request,
GetAllScheduledTasksResponse* response) {
const std::string& clientId = request->clientid();
printf("GetAllScheduledTasks called with client Id: %s\n", clientId.c_str());
response->clear_allscheduledtasks();
{
std::unique_lock lk(mLock);
if (mInfoByScheduleIdByClientId.find(clientId) == mInfoByScheduleIdByClientId.end()) {
return Status::OK;
}
for (const auto& [_, scheduleInfo] : mInfoByScheduleIdByClientId[clientId]) {
(*response->add_allscheduledtasks()) = *scheduleInfo.grpcScheduleInfo;
}
}
return Status::OK;
}
bool TestWakeupClientServiceImpl::isWakeupRequired() {
return mWakeupRequired;
}
bool TestWakeupClientServiceImpl::isRemoteTaskConnectionAlive() {
return mRemoteTaskConnectionAlive;
}
} // namespace remoteaccess

View file

@ -14,7 +14,17 @@
* limitations under the License.
*/
#include <signal.h>
#include <stdio.h>
#include <sys/wait.h>
#include <iostream>
#include <sstream>
#include <string>
#include <thread>
#ifndef HOST
#include "ApPowerControl.h"
#endif // #ifndef HOST
#include "TestWakeupClientServiceImpl.h"
@ -28,10 +38,18 @@ using ::grpc::Server;
using ::grpc::ServerBuilder;
using ::grpc::ServerWriter;
void RunServer(const std::string& serviceAddr) {
std::shared_ptr<TestWakeupClientServiceImpl> service =
std::make_unique<TestWakeupClientServiceImpl>();
constexpr int SHUTDOWN_REQUEST = 289410889;
constexpr int VEHICLE_IN_USE = 287313738;
const char* COMMAND_RUN_EMU = "source ~/.aae-toolbox/bin/bashrc && aae emulator run";
const char* COMMAND_SET_VHAL_PROP =
"adb -s emulator-5554 wait-for-device && adb -s emulator-5554 root "
"&& sleep 1 && adb -s emulator-5554 wait-for-device && adb -s emulator-5554 shell "
"dumpsys android.hardware.automotive.vehicle.IVehicle/default --set %d -i %d";
pid_t emuPid = 0;
void RunServer(const std::string& serviceAddr,
std::shared_ptr<TestWakeupClientServiceImpl> service) {
ServerBuilder builder;
builder.AddListeningPort(serviceAddr, grpc::InsecureServerCredentials());
builder.RegisterService(service.get());
@ -40,11 +58,228 @@ void RunServer(const std::string& serviceAddr) {
server->Wait();
}
pid_t runCommand(const char* bashCommand) {
pid_t pid = fork();
if (pid == 0) {
// In child process. Put it into a separate process group so we can kill it.
setpgid(0, 0);
execl("/bin/bash", "bash", "-c", bashCommand, /*terminateArg=*/nullptr);
exit(0);
} else {
return pid;
}
}
void updateEmuStatus() {
if (emuPid == 0) {
return;
}
pid_t pid = waitpid(emuPid, nullptr, WNOHANG);
if (pid == emuPid) {
// Emu process already exited. If Emu process is still running, pid will be 0.
emuPid = 0;
}
}
bool powerOnEmu() {
updateEmuStatus();
if (emuPid != 0) {
printf("The emulator is already running\n");
return false;
}
emuPid = runCommand(COMMAND_RUN_EMU);
printf("Emulator started in process: %d\n", emuPid);
return true;
}
bool powerOn() {
#ifdef HOST
return powerOnEmu();
#else
printf("power on is only supported on host\n");
return false;
#endif
}
const char* getSetPropCommand(int propId, int value) {
int size = snprintf(nullptr, 0, COMMAND_SET_VHAL_PROP, propId, value);
char* command = new char[size + 1];
snprintf(command, size + 1, COMMAND_SET_VHAL_PROP, propId, value);
return command;
}
const char* getSetPropCommand(int propId) {
return getSetPropCommand(propId, /*value=*/1);
}
void powerOffEmu() {
updateEmuStatus();
if (emuPid == 0) {
printf("The emulator is not running\n");
return;
}
const char* command = getSetPropCommand(SHUTDOWN_REQUEST);
runCommand(command);
delete[] command;
waitpid(emuPid, nullptr, /*options=*/0);
emuPid = 0;
}
void powerOff() {
#ifdef HOST
powerOffEmu();
#else
printf("power off is only supported on host\n");
#endif
}
void setVehicleInUse(bool vehicleInUse) {
#ifdef HOST
printf("Set vehicleInUse to %d\n", vehicleInUse);
int value = 0;
if (vehicleInUse) {
value = 1;
}
const char* command = getSetPropCommand(VEHICLE_IN_USE, value);
runCommand(command);
delete[] command;
#else
printf("set vehicleInUse is only supported on host\n");
#endif
}
void help() {
std::cout << "Remote Access Host Test Utility" << std::endl
<< "help:\t"
<< "Print out this help info" << std::endl
<< "genFakeTask start [clientID]:\t"
<< "Start generating a fake task every 5s" << std::endl
<< "genFakeTask stop:\t"
<< "Stop the fake task generation" << std::endl
<< "status:\t"
<< "Print current status" << std::endl
<< "power on:\t"
<< "Power on the emulator, simulate user enters vehicle while AP is off"
<< " (only supported on host)" << std::endl
<< "power off:\t"
<< "Power off the emulator, simulate user leaves vehicle"
<< " (only supported on host)" << std::endl
<< "inject task [clientID] [taskData]:\t"
<< "Inject a remote task" << std::endl
<< "set vehicleInUse:\t"
<< "Set vehicle in use, simulate user enter vehicle while boot up for remote task "
<< "(only supported on host)" << std::endl;
}
void parseCommand(const std::string& userInput,
std::shared_ptr<TestWakeupClientServiceImpl> service) {
if (userInput == "") {
// ignore empty line.
} else if (userInput == "help") {
help();
} else if (userInput.rfind("genFakeTask start", 0) == 0) {
std::string clientId;
std::stringstream ss;
ss << userInput;
int i = 0;
while (std::getline(ss, clientId, ' ')) {
i++;
if (i == 3) {
break;
}
}
if (i != 3) {
printf("Missing clientId, see 'help'\n");
return;
}
service->startGeneratingFakeTask(clientId);
} else if (userInput == "genFakeTask stop") {
service->stopGeneratingFakeTask();
} else if (userInput == "status") {
printf("isWakeupRequired: %B, isRemoteTaskConnectionAlive: %B\n",
service->isWakeupRequired(), service->isRemoteTaskConnectionAlive());
} else if (userInput == "power on") {
powerOn();
} else if (userInput == "power off") {
powerOff();
} else if (userInput.rfind("inject task", 0) == 0) {
std::stringstream ss;
ss << userInput;
std::string data;
std::string taskData;
std::string clientId;
int i = 0;
while (std::getline(ss, data, ' ')) {
i++;
if (i == 3) {
clientId = data;
}
if (i == 4) {
taskData = data;
}
}
if (taskData == "" || clientId == "") {
printf("Missing taskData or clientId, see 'help'\n");
return;
}
service->injectTask(taskData, clientId);
printf("Remote task with client ID: %s, data: %s injected\n", clientId.c_str(),
taskData.c_str());
} else if (userInput == "set vehicleInUse") {
setVehicleInUse(true);
} else {
printf("Unknown command, see 'help'\n");
}
}
void saHandler(int signum) {
if (emuPid != 0) {
kill(-emuPid, signum);
waitpid(emuPid, nullptr, /*options=*/0);
// Sleep for 1 seconds to allow emulator to print out logs.
sleep(1);
}
exit(-1);
}
class MyTestWakeupClientServiceImpl final : public TestWakeupClientServiceImpl {
public:
void wakeupApplicationProcessor() override {
#ifdef HOST
if (powerOnEmu()) {
// If we wake up AP to execute remote task, vehicle in use should be false.
setVehicleInUse(false);
}
#else
wakeupAp();
#endif
};
};
int main(int argc, char** argv) {
std::string serviceAddr = GRPC_SERVICE_ADDRESS;
if (argc > 1) {
serviceAddr = argv[1];
}
RunServer(serviceAddr);
// Let the server thread run, we will force kill the server when we exit the program.
std::shared_ptr<TestWakeupClientServiceImpl> service =
std::make_shared<MyTestWakeupClientServiceImpl>();
std::thread serverThread([serviceAddr, service] { RunServer(serviceAddr, service); });
// Register the signal handler for SIGTERM and SIGINT so that we can stop the emulator before
// exit.
struct sigaction sa = {};
sigemptyset(&sa.sa_mask);
sa.sa_handler = saHandler;
sigaction(SIGTERM, &sa, nullptr);
sigaction(SIGINT, &sa, nullptr);
// Start processing the user inputs.
std::string userInput;
while (true) {
std::cout << ">>> ";
std::getline(std::cin, userInput);
parseCommand(userInput, service);
}
return 0;
}

View file

@ -0,0 +1,339 @@
/*
* Copyright (C) 2023 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 "TestWakeupClientServiceImpl.h"
#include <grpcpp/channel.h>
#include <grpcpp/create_channel.h>
#include <grpcpp/security/credentials.h>
#include <grpcpp/security/server_credentials.h>
#include <grpcpp/server.h>
#include <grpcpp/server_builder.h>
#include <gtest/gtest.h>
#include <chrono>
namespace android::hardware::automotive::remoteaccess::test {
using ::android::base::ScopedLockAssertion;
using ::grpc::Channel;
using ::grpc::ClientContext;
using ::grpc::Server;
using ::grpc::ServerBuilder;
using ::grpc::Status;
const std::string kTestClientId = "test client id";
const std::string kTestScheduleId = "test schedule id";
const std::vector<uint8_t> kTestData = {0xde, 0xad, 0xbe, 0xef};
constexpr int32_t kTestCount = 1234;
constexpr int64_t kTestStartTimeInEpochSeconds = 2345;
constexpr int64_t kTestPeriodicInSeconds = 123;
const std::string kTestGrpcAddr = "localhost:50051";
class MyTestWakeupClientServiceImpl final : public TestWakeupClientServiceImpl {
public:
void wakeupApplicationProcessor() override {
// Do nothing.
}
};
class TestWakeupClientServiceImplUnitTest : public ::testing::Test {
public:
virtual void SetUp() override {
mServerThread = std::thread([this] {
{
std::unique_lock<std::mutex> lock(mLock);
mService = std::make_unique<MyTestWakeupClientServiceImpl>();
ServerBuilder builder;
builder.AddListeningPort(kTestGrpcAddr, grpc::InsecureServerCredentials());
builder.RegisterService(mService.get());
mServer = builder.BuildAndStart();
mServerStartCv.notify_one();
}
mServer->Wait();
});
{
std::unique_lock<std::mutex> lock(mLock);
mServerStartCv.wait(lock, [this] {
ScopedLockAssertion lockAssertion(mLock);
return mServer != nullptr;
});
}
mChannel = grpc::CreateChannel(kTestGrpcAddr, grpc::InsecureChannelCredentials());
mStub = WakeupClient::NewStub(mChannel);
}
virtual void TearDown() override {
printf("Start server shutdown\n");
mService->stopServer();
mServer->Shutdown();
printf("Server shutdown complete\n");
mServerThread.join();
printf("Server thread exits\n");
mServer.reset();
mService.reset();
printf("Server and service classes reset\n");
}
WakeupClient::Stub* getStub() { return mStub.get(); }
size_t waitForRemoteTasks(size_t count) {
ClientContext context = {};
GetRemoteTasksResponse response;
auto reader = mStub->GetRemoteTasks(&context, GetRemoteTasksRequest{});
size_t got = 0;
while (reader->Read(&response)) {
got++;
mRemoteTaskResponses.push_back(response);
if (got == count) {
break;
}
}
// If there is more messages to be read in the reader, cancel them all so that we can
// finish.
context.TryCancel();
reader->Finish();
return got;
}
std::vector<GetRemoteTasksResponse> getRemoteTaskResponses() { return mRemoteTaskResponses; }
Status scheduleTask(int32_t count, int64_t startTimeInEpochSeconds, int64_t periodicInSeconds) {
return scheduleTask(kTestScheduleId, count, startTimeInEpochSeconds, periodicInSeconds);
}
Status scheduleTask(const std::string& scheduleId, int32_t count,
int64_t startTimeInEpochSeconds, int64_t periodicInSeconds) {
ClientContext context;
ScheduleTaskRequest request;
ScheduleTaskResponse response;
int64_t now = std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_scheduleid(scheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(count);
request.mutable_scheduleinfo()->set_starttimeinepochseconds(startTimeInEpochSeconds);
request.mutable_scheduleinfo()->set_periodicinseconds(periodicInSeconds);
return getStub()->ScheduleTask(&context, request, &response);
}
int64_t getNow() {
return std::chrono::duration_cast<std::chrono::seconds>(
std::chrono::system_clock::now().time_since_epoch())
.count();
}
private:
std::condition_variable mServerStartCv;
std::mutex mLock;
std::thread mServerThread;
std::unique_ptr<MyTestWakeupClientServiceImpl> mService;
std::unique_ptr<Server> mServer;
std::shared_ptr<Channel> mChannel;
std::unique_ptr<WakeupClient::Stub> mStub;
std::vector<GetRemoteTasksResponse> mRemoteTaskResponses;
};
TEST_F(TestWakeupClientServiceImplUnitTest, TestScheduleTask) {
ClientContext context = {};
ScheduleTaskRequest request = {};
ScheduleTaskResponse response = {};
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(2);
// Schedule the task to be executed 1s later.
request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
request.mutable_scheduleinfo()->set_periodicinseconds(1);
Status status = getStub()->ScheduleTask(&context, request, &response);
ASSERT_TRUE(status.ok());
ASSERT_EQ(response.errorcode(), ErrorCode::OK);
size_t gotTaskCount = waitForRemoteTasks(/*count=*/2);
EXPECT_EQ(gotTaskCount, 2);
auto responses = getRemoteTaskResponses();
for (const auto& response : responses) {
EXPECT_EQ(response.clientid(), kTestClientId);
EXPECT_EQ(response.data(), std::string(kTestData.begin(), kTestData.end()));
}
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestScheduleTask_conflictScheduleId) {
Status status = scheduleTask(/*count=*/2, /*startTimeInEpochSeconds=*/getNow() + 1,
/*periodicInSeconds=*/1);
ASSERT_TRUE(status.ok());
// Schedule the same task again.
ClientContext context = {};
ScheduleTaskRequest request = {};
ScheduleTaskResponse response = {};
request.mutable_scheduleinfo()->set_clientid(kTestClientId);
request.mutable_scheduleinfo()->set_scheduleid(kTestScheduleId);
request.mutable_scheduleinfo()->set_data(kTestData.data(), kTestData.size());
request.mutable_scheduleinfo()->set_count(2);
request.mutable_scheduleinfo()->set_starttimeinepochseconds(getNow() + 1);
request.mutable_scheduleinfo()->set_periodicinseconds(1);
status = getStub()->ScheduleTask(&context, request, &response);
ASSERT_TRUE(status.ok());
ASSERT_EQ(response.errorcode(), ErrorCode::INVALID_ARG);
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestUnscheduleTask) {
Status status = scheduleTask(/*count=*/2, /*startTimeInEpochSeconds=*/getNow() + 1,
/*periodicInSeconds=*/1);
ASSERT_TRUE(status.ok());
ClientContext context;
UnscheduleTaskRequest request;
UnscheduleTaskResponse response;
request.set_clientid(kTestClientId);
request.set_scheduleid(kTestScheduleId);
status = getStub()->UnscheduleTask(&context, request, &response);
ASSERT_TRUE(status.ok());
sleep(2);
// There should be no remote tasks received after 2s because the task was unscheduled.
EXPECT_EQ(getRemoteTaskResponses().size(), 0);
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestIsTaskScheduled) {
int64_t startTimeInEpochSeconds = getNow() + 1;
int64_t periodicInSeconds = 1234;
Status status = scheduleTask(/*count=*/2, startTimeInEpochSeconds, periodicInSeconds);
ASSERT_TRUE(status.ok());
ClientContext context;
IsTaskScheduledRequest request;
IsTaskScheduledResponse response;
request.set_clientid(kTestClientId);
request.set_scheduleid(kTestScheduleId);
status = getStub()->IsTaskScheduled(&context, request, &response);
ASSERT_TRUE(status.ok());
EXPECT_TRUE(response.istaskscheduled());
ClientContext context2;
IsTaskScheduledRequest request2;
IsTaskScheduledResponse response2;
request.set_clientid(kTestClientId);
request.set_scheduleid("invalid id");
status = getStub()->IsTaskScheduled(&context2, request2, &response2);
ASSERT_TRUE(status.ok());
EXPECT_FALSE(response2.istaskscheduled());
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestUnscheduleAllTasks) {
std::string scheduleId1 = "scheduleId1";
std::string scheduleId2 = "scheduleId2";
int64_t time1 = getNow();
int64_t time2 = getNow() + 1;
int64_t periodicInSeconds1 = 1;
int64_t periodicInSeconds2 = 1;
int32_t count1 = 2;
int64_t count2 = 5;
Status status = scheduleTask(scheduleId1, count1, time1, periodicInSeconds1);
ASSERT_TRUE(status.ok());
status = scheduleTask(scheduleId2, count2, time2, periodicInSeconds2);
ASSERT_TRUE(status.ok());
ClientContext context;
UnscheduleAllTasksRequest request;
UnscheduleAllTasksResponse response;
request.set_clientid(kTestClientId);
status = getStub()->UnscheduleAllTasks(&context, request, &response);
ASSERT_TRUE(status.ok());
sleep(2);
// There should be no remote tasks received after 2s because the tasks were unscheduled.
EXPECT_EQ(getRemoteTaskResponses().size(), 0);
}
TEST_F(TestWakeupClientServiceImplUnitTest, TestGetAllScheduledTasks) {
std::string scheduleId1 = "scheduleId1";
std::string scheduleId2 = "scheduleId2";
int64_t time1 = getNow();
int64_t time2 = getNow() + 1;
int64_t periodicInSeconds1 = 1;
int64_t periodicInSeconds2 = 1;
int32_t count1 = 2;
int64_t count2 = 5;
Status status = scheduleTask(scheduleId1, count1, time1, periodicInSeconds1);
ASSERT_TRUE(status.ok());
status = scheduleTask(scheduleId2, count2, time2, periodicInSeconds2);
ASSERT_TRUE(status.ok());
ClientContext context;
GetAllScheduledTasksRequest request;
GetAllScheduledTasksResponse response;
request.set_clientid("invalid client Id");
status = getStub()->GetAllScheduledTasks(&context, request, &response);
ASSERT_TRUE(status.ok());
EXPECT_EQ(response.allscheduledtasks_size(), 0);
ClientContext context2;
GetAllScheduledTasksRequest request2;
GetAllScheduledTasksResponse response2;
request2.set_clientid(kTestClientId);
status = getStub()->GetAllScheduledTasks(&context2, request2, &response2);
ASSERT_TRUE(status.ok());
ASSERT_EQ(response2.allscheduledtasks_size(), 2);
for (int i = 0; i < 2; i++) {
EXPECT_EQ(response2.allscheduledtasks(i).clientid(), kTestClientId);
if (response2.allscheduledtasks(i).scheduleid() == scheduleId1) {
EXPECT_EQ(response2.allscheduledtasks(i).data(),
std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(response2.allscheduledtasks(i).count(), count1);
EXPECT_EQ(response2.allscheduledtasks(i).starttimeinepochseconds(), time1);
EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds1);
} else {
EXPECT_EQ(response2.allscheduledtasks(i).scheduleid(), scheduleId2);
EXPECT_EQ(response2.allscheduledtasks(i).data(),
std::string(kTestData.begin(), kTestData.end()));
EXPECT_EQ(response2.allscheduledtasks(i).count(), count2);
EXPECT_EQ(response2.allscheduledtasks(i).starttimeinepochseconds(), time2);
EXPECT_EQ(response2.allscheduledtasks(i).periodicinseconds(), periodicInSeconds2);
}
}
}
} // namespace android::hardware::automotive::remoteaccess::test
int main(int argc, char** argv) {
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View file

@ -26,7 +26,7 @@ package {
cc_library_shared {
name: "ApPowerControlLib",
vendor: true,
srcs: ["*.cpp"],
srcs: ["ApPowerControl.cpp"],
local_include_dirs: ["."],
export_include_dirs: ["."],
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2021 The Android Open Source Project
* Copyright (C) 2023 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.
@ -14,13 +14,10 @@
* limitations under the License.
*/
package {
default_applicable_licenses: ["Android-Apache-2.0"],
}
#include "ApPowerControl.h"
cc_library_headers {
name: "VehicleHalTestUtilHeaders",
vendor: true,
header_libs: ["VehicleHalUtilHeaders"],
export_include_dirs: ["include"],
#include <cstdio>
void wakeupAp() {
printf("Waking up application processor...\n");
}

View file

@ -2,7 +2,9 @@
"presubmit": [
{
"name": "android.hardware.automotive.vehicle@2.0-manager-unit-tests"
},
}
],
"postsubmit": [
{
"name": "android.hardware.automotive.vehicle@2.0-default-impl-unit-tests"
}

View file

@ -122,21 +122,29 @@ private:
}
std::unique_lock<std::mutex> g(mLock);
// mStopRequested might be set to true after we enter the loop. Must check inside
// the lock to make sure the value will not change before we start the wait.
if (mStopRequested) {
return;
}
mCond.wait_until(g, nextEventTime); // nextEventTime can be nanoseconds::max()
}
}
void stop() {
mStopRequested = true;
{
std::lock_guard<std::mutex> g(mLock);
mCookieToEventsMap.clear();
// Even though this is atomic, this must be set inside the lock to make sure we will
// not change this after we check mStopRequested, but before we start the wait.
mStopRequested = true;
}
mCond.notify_one();
if (mTimerThread.joinable()) {
mTimerThread.join();
}
}
private:
mutable std::mutex mLock;
std::thread mTimerThread;

View file

@ -70,6 +70,16 @@ public:
* example wasn't registered. */
bool writeValue(const VehiclePropValue& propValue, bool updateStatus);
/*
* Stores provided value. Returns true if value was written returns false if config for
* example wasn't registered.
*
* The property value's timestamp will be set to the current ElapsedRealTimeNano.
*/
bool writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr, bool updateStatus);
std::unique_ptr<VehiclePropValue> refreshTimestamp(int32_t propId, int32_t areaId);
void removeValue(const VehiclePropValue& propValue);
void removeValuesForProperty(int32_t propId);
@ -94,6 +104,8 @@ private:
std::unordered_map<int32_t /* VehicleProperty */, RecordConfig> mConfigs;
PropertyMap mPropertyValues; // Sorted map of RecordId : VehiclePropValue.
bool writeValueLocked(const VehiclePropValue& propValue, bool updateStatus);
};
} // namespace V2_0

View file

@ -15,6 +15,7 @@
*/
#define LOG_TAG "VehiclePropertyStore"
#include <log/log.h>
#include <utils/SystemClock.h>
#include <common/include/vhal_v2_0/VehicleUtils.h>
#include "VehiclePropertyStore.h"
@ -41,9 +42,7 @@ void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
mConfigs.insert({ config.prop, RecordConfig { config, tokenFunc } });
}
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
bool updateStatus) {
MuxGuard g(mLock);
bool VehiclePropertyStore::writeValueLocked(const VehiclePropValue& propValue, bool updateStatus) {
if (!mConfigs.count(propValue.prop)) return false;
RecordId recId = getRecordIdLocked(propValue);
@ -68,6 +67,36 @@ bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue,
return true;
}
bool VehiclePropertyStore::writeValue(const VehiclePropValue& propValue, bool updateStatus) {
MuxGuard g(mLock);
return writeValueLocked(propValue, updateStatus);
}
bool VehiclePropertyStore::writeValueWithCurrentTimestamp(VehiclePropValue* propValuePtr,
bool updateStatus) {
MuxGuard g(mLock);
propValuePtr->timestamp = elapsedRealtimeNano();
return writeValueLocked(*propValuePtr, updateStatus);
}
std::unique_ptr<VehiclePropValue> VehiclePropertyStore::refreshTimestamp(int32_t propId,
int32_t areaId) {
MuxGuard g(mLock);
RecordId recId = getRecordIdLocked(VehiclePropValue{
.prop = propId,
.areaId = areaId,
});
auto it = mPropertyValues.find(recId);
if (it == mPropertyValues.end()) {
return nullptr;
}
it->second.timestamp = elapsedRealtimeNano();
return std::make_unique<VehiclePropValue>(it->second);
}
void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
MuxGuard g(mLock);
RecordId recId = getRecordIdLocked(propValue);

View file

@ -0,0 +1,106 @@
/*
* Copyright (C) 2023 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.
*/
// This file contains backported system property definitions and backported enums.
#pragma once
namespace android {
namespace hardware {
namespace automotive {
namespace vehicle {
namespace V2_0 {
namespace backportedproperty {
/**
* Characterization of inputs used for computing location.
*
* This property must indicate what (if any) data and sensor inputs are considered by the system
* when computing the vehicle's location that is shared with Android through the GNSS HAL.
*
* The value must return a collection of bit flags. The bit flags are defined in
* LocationCharacterization. The value must also include exactly one of DEAD_RECKONED or
* RAW_GNSS_ONLY among its collection of bit flags.
*
* When this property is not supported, it is assumed that no additional sensor inputs are fused
* into the GNSS updates provided through the GNSS HAL. That is unless otherwise specified
* through the GNSS HAL interfaces.
*
* @change_mode VehiclePropertyChangeMode.STATIC
* @access VehiclePropertyAccess.READ
*/
constexpr int32_t LOCATION_CHARACTERIZATION = 0x31400C10;
/**
* Used by LOCATION_CHARACTERIZATION to enumerate the supported bit flags.
*
* These flags are used to indicate to what transformations are performed on the
* GNSS data before the location data is sent, so that location processing
* algorithms can take into account prior fusion.
*
* This enum can be extended in future releases to include additional bit flags.
*/
enum class LocationCharacterization : int32_t {
/**
* Prior location samples have been used to refine the raw GNSS data (e.g. a
* Kalman Filter).
*/
PRIOR_LOCATIONS = 0x1,
/**
* Gyroscope data has been used to refine the raw GNSS data.
*/
GYROSCOPE_FUSION = 0x2,
/**
* Accelerometer data has been used to refine the raw GNSS data.
*/
ACCELEROMETER_FUSION = 0x4,
/**
* Compass data has been used to refine the raw GNSS data.
*/
COMPASS_FUSION = 0x8,
/**
* Wheel speed has been used to refine the raw GNSS data.
*/
WHEEL_SPEED_FUSION = 0x10,
/**
* Steering angle has been used to refine the raw GNSS data.
*/
STEERING_ANGLE_FUSION = 0x20,
/**
* Car speed has been used to refine the raw GNSS data.
*/
CAR_SPEED_FUSION = 0x40,
/**
* Some effort is made to dead-reckon location. In particular, this means that
* relative changes in location have meaning when no GNSS satellite is
* available.
*/
DEAD_RECKONED = 0x80,
/**
* Location is based on GNSS satellite signals without sufficient fusion of
* other sensors for complete dead reckoning. This flag should be set when
* relative changes to location cannot be relied on when no GNSS satellite is
* available.
*/
RAW_GNSS_ONLY = 0x100,
};
} // namespace backportedproperty
} // namespace V2_0
} // namespace vehicle
} // namespace automotive
} // namespace hardware
} // namespace android

View file

@ -17,6 +17,7 @@
#ifndef android_hardware_automotive_vehicle_V2_0_impl_DefaultConfig_H_
#define android_hardware_automotive_vehicle_V2_0_impl_DefaultConfig_H_
#include "BackportedPropertyHelper.h"
#include "PropertyUtils.h"
#include <map>
@ -29,6 +30,9 @@ namespace V2_0 {
namespace impl {
using ::android::hardware::automotive::vehicle::V2_0::backportedproperty::LOCATION_CHARACTERIZATION;
using ::android::hardware::automotive::vehicle::V2_0::backportedproperty::LocationCharacterization;
struct ConfigDeclaration {
VehiclePropConfig config;
@ -938,7 +942,10 @@ const ConfigDeclaration kVehicleProperties[]{
(int)VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE,
VENDOR_EXTENSION_FLOAT_PROPERTY,
(int)VehicleVendorPermission::PERMISSION_DEFAULT,
(int)VehicleVendorPermission::PERMISSION_DEFAULT},
(int)VehicleVendorPermission::PERMISSION_DEFAULT,
LOCATION_CHARACTERIZATION,
(int)VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_INFO,
(int)VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE},
},
.initialValue = {.int32Values = {1}}},
@ -1131,6 +1138,15 @@ const ConfigDeclaration kVehicleProperties[]{
// GsrComplianceRequirementType::GSR_COMPLIANCE_REQUIRED_V1
.initialValue = {.int32Values = {1}},
},
{
.config =
{
.prop = LOCATION_CHARACTERIZATION,
.access = VehiclePropertyAccess::READ,
.changeMode = VehiclePropertyChangeMode::STATIC,
},
.initialValue = {.int32Values = {toInt(LocationCharacterization::RAW_GNSS_ONLY)}},
},
#ifdef ENABLE_VENDOR_CLUSTER_PROPERTY_FOR_TESTING
// Vendor propetry for E2E ClusterHomeService testing.
{
@ -1195,29 +1211,131 @@ const ConfigDeclaration kVehicleProperties[]{
},
// All supported property IDs. This list is checked by
// DefaultConfigSupportedPropertyIds_test.
.initialValue =
{.int32Values =
{291504388, 289472773, 291504390, 289472775, 289407240, 289407241,
289472780, 286261505, 286261506, 289407235, 289472779, 291504647,
289408517, 356518832, 356516106, 291504644, 291504649, 291504656,
291504901, 291504903, 287310600, 291504905, 287310602, 287310603,
291504908, 291504904, 392168201, 392168202, 289408514, 289408001,
287310850, 287310851, 287310853, 289408513, 289475088, 289475104,
289475120, 354419984, 320865540, 320865556, 354419975, 354419976,
354419986, 354419973, 354419974, 354419978, 354419977, 356517120,
356517121, 356582673, 356517139, 289408269, 356517131, 358614275,
291570965, 291505923, 289408270, 289408512, 287310855, 289408000,
289408008, 289408009, 289407747, 291504900, 568332561, 371198722,
373295872, 320867268, 322964416, 290521862, 287310858, 287310859,
289475072, 289475073, 289409539, 299896064, 299896065, 299896066,
299896067, 289410560, 289410561, 289410562, 289410563, 289410576,
289410577, 289410578, 289410579, 289476368, 299895808, 639631617,
627048706, 591397123, 554696964, 289410873, 289410874, 287313669,
299896583, 299896584, 299896585, 299896586, 299896587, 286265121,
286265122, 286265123, 290457094, 290459441, 299896626, 290459443,
289410868, 289476405, 299896630, 289410871, 292556600, 557853201,
559950353, 555756049, 554707473, 289410887, 557846324, 557911861,
568332086, 557846327, 560992056, 289476424}},
.initialValue = {.int32Values = {291504388,
289472773,
291504390,
289472775,
289407240,
289407241,
289472780,
286261505,
286261506,
289407235,
289472779,
291504647,
289408517,
356518832,
356516106,
291504644,
291504649,
291504656,
291504901,
291504903,
287310600,
291504905,
287310602,
287310603,
291504908,
291504904,
392168201,
392168202,
289408514,
289408001,
287310850,
287310851,
287310853,
289408513,
289475088,
289475104,
289475120,
354419984,
320865540,
320865556,
354419975,
354419976,
354419986,
354419973,
354419974,
354419978,
354419977,
356517120,
356517121,
356582673,
356517139,
289408269,
356517131,
358614275,
291570965,
291505923,
289408270,
289408512,
287310855,
289408000,
289408008,
289408009,
289407747,
291504900,
568332561,
371198722,
373295872,
320867268,
322964416,
290521862,
287310858,
287310859,
289475072,
289475073,
289409539,
299896064,
299896065,
299896066,
299896067,
289410560,
289410561,
289410562,
289410563,
289410576,
289410577,
289410578,
289410579,
289476368,
299895808,
639631617,
627048706,
591397123,
554696964,
289410873,
289410874,
287313669,
299896583,
299896584,
299896585,
299896586,
299896587,
286265121,
286265122,
286265123,
290457094,
290459441,
299896626,
290459443,
289410868,
289476405,
299896630,
289410871,
292556600,
557853201,
559950353,
555756049,
554707473,
289410887,
557846324,
557911861,
568332086,
557846327,
560992056,
289476424,
LOCATION_CHARACTERIZATION}},
},
#endif // ENABLE_GET_PROP_CONFIGS_BY_MULTIPLE_REQUESTS
};

View file

@ -57,12 +57,6 @@ const VehicleAreaConfig* getAreaConfig(const VehiclePropValue& propValue,
return nullptr;
}
VehicleHal::VehiclePropValuePtr addTimestamp(VehicleHal::VehiclePropValuePtr v) {
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
}
return v;
}
} // namespace
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::createVhalHeartBeatProp() {
@ -102,7 +96,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::getUserHalProp(
*outStatus = StatusCode::INTERNAL_ERROR;
}
}
return addTimestamp(std::move(v));
return v;
}
VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& requestedPropValue,
@ -118,13 +112,13 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
if (propId == OBD2_FREEZE_FRAME) {
v = getValuePool()->obtainComplex();
*outStatus = fillObd2FreezeFrame(mPropStore, requestedPropValue, v.get());
return addTimestamp(std::move(v));
return v;
}
if (propId == OBD2_FREEZE_FRAME_INFO) {
v = getValuePool()->obtainComplex();
*outStatus = fillObd2DtcInfo(mPropStore, v.get());
return addTimestamp(std::move(v));
return v;
}
auto internalPropValue = mPropStore->readValueOrNull(requestedPropValue);
@ -139,7 +133,7 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::get(const VehiclePropValue& r
} else {
*outStatus = StatusCode::TRY_AGAIN;
}
return addTimestamp(std::move(v));
return v;
}
std::vector<VehiclePropConfig> DefaultVehicleHal::listProperties() {
@ -486,26 +480,42 @@ VehicleHal::VehiclePropValuePtr DefaultVehicleHal::doInternalHealthCheck() {
void DefaultVehicleHal::onContinuousPropertyTimer(const std::vector<int32_t>& properties) {
auto& pool = *getValuePool();
for (int32_t property : properties) {
VehiclePropValuePtr v;
std::vector<VehiclePropValuePtr> events;
if (isContinuousProperty(property)) {
auto internalPropValue = mPropStore->readValueOrNull(property);
if (internalPropValue != nullptr) {
v = pool.obtain(*internalPropValue);
const VehiclePropConfig* config = mPropStore->getConfigOrNull(property);
std::vector<int32_t> areaIds;
if (isGlobalProp(property)) {
areaIds.push_back(0);
} else {
for (auto& c : config->areaConfigs) {
areaIds.push_back(c.areaId);
}
}
for (int areaId : areaIds) {
auto v = pool.obtain(*mPropStore->refreshTimestamp(property, areaId));
if (v.get()) {
events.push_back(std::move(v));
}
}
} else if (property == static_cast<int32_t>(VehicleProperty::VHAL_HEARTBEAT)) {
// VHAL_HEARTBEAT is not a continuous value, but it needs to be updated periodically.
// So, the update is done through onContinuousPropertyTimer.
v = doInternalHealthCheck();
auto v = doInternalHealthCheck();
if (!v.get()) {
// Internal health check failed.
continue;
}
mPropStore->writeValueWithCurrentTimestamp(v.get(), /*updateStatus=*/true);
events.push_back(std::move(v));
} else {
ALOGE("Unexpected onContinuousPropertyTimer for property: 0x%x", property);
continue;
}
if (v.get()) {
v->timestamp = elapsedRealtimeNano();
doHalEvent(std::move(v));
for (VehiclePropValuePtr& event : events) {
doHalEvent(std::move(event));
}
}
}
@ -556,7 +566,7 @@ bool DefaultVehicleHal::isContinuousProperty(int32_t propId) const {
void DefaultVehicleHal::onPropertyValue(const VehiclePropValue& value, bool updateStatus) {
VehiclePropValuePtr updatedPropValue = getValuePool()->obtain(value);
if (mPropStore->writeValue(*updatedPropValue, updateStatus)) {
if (mPropStore->writeValueWithCurrentTimestamp(updatedPropValue.get(), updateStatus)) {
doHalEvent(std::move(updatedPropValue));
}
}

View file

@ -81,8 +81,13 @@ using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME;
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_CLEAR;
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_FREEZE_FRAME_INFO;
using ::android::hardware::automotive::vehicle::V2_0::impl::OBD2_LIVE_FRAME;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_LEFT;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_FRONT_RIGHT;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_LEFT;
using ::android::hardware::automotive::vehicle::V2_0::impl::WHEEL_REAR_RIGHT;
using ::testing::HasSubstr;
using ::testing::UnorderedElementsAre;
using VehiclePropValuePtr = recyclable_ptr<VehiclePropValue>;
@ -152,7 +157,7 @@ class DefaultVhalImplTest : public ::testing::Test {
TEST_F(DefaultVhalImplTest, testListProperties) {
std::vector<VehiclePropConfig> configs = mHal->listProperties();
EXPECT_EQ((size_t)124, configs.size());
EXPECT_EQ((size_t)125, configs.size());
}
TEST_F(DefaultVhalImplTest, testGetDefaultPropertyFloat) {
@ -346,6 +351,38 @@ TEST_F(DefaultVhalImplTest, testSubscribe) {
EXPECT_EQ(1.0f, lastEvent->value.floatValues[0]);
}
TEST_F(DefaultVhalImplTest, testSubscribeContinuous_withMultipleAreaIds) {
// Clear existing events.
mEventQueue.flush();
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
auto status = mHal->subscribe(propId, 1);
ASSERT_EQ(StatusCode::OK, status);
std::vector<VehiclePropValuePtr> receivedEvents;
// Wait for 2 updates, each for 4 area IDs.
waitForEvents(&receivedEvents, 4 * 2);
std::vector<int> areasForUpdate1;
std::vector<int> areasForUpdate2;
for (size_t i = 0; i < receivedEvents.size(); i++) {
ASSERT_EQ(receivedEvents[i]->prop, propId);
if (i < 4) {
areasForUpdate1.push_back(receivedEvents[i]->areaId);
} else {
areasForUpdate2.push_back(receivedEvents[i]->areaId);
}
}
ASSERT_THAT(areasForUpdate1, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
ASSERT_THAT(areasForUpdate2, UnorderedElementsAre(WHEEL_FRONT_LEFT, WHEEL_FRONT_RIGHT,
WHEEL_REAR_LEFT, WHEEL_REAR_RIGHT));
}
TEST_F(DefaultVhalImplTest, testSubscribeInvalidProp) {
EXPECT_EQ(StatusCode::INVALID_ARG, mHal->subscribe(toInt(VehicleProperty::INFO_MAKE), 10));
}
@ -1318,7 +1355,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetInt) {
ASSERT_EQ((size_t)1, events.size());
ASSERT_EQ((size_t)1, events[0]->value.int32Values.size());
EXPECT_EQ(2022, events[0]->value.int32Values[0]);
EXPECT_EQ(1000, events[0]->timestamp);
VehiclePropValue value;
StatusCode status;
@ -1352,7 +1388,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetBool) {
ASSERT_EQ((size_t)1, events.size());
EXPECT_EQ(0, events[0]->value.int32Values[0]);
EXPECT_EQ(DOOR_1_LEFT, events[0]->areaId);
EXPECT_EQ(1000, events[0]->timestamp);
VehiclePropValue value;
StatusCode status;
@ -1391,7 +1426,6 @@ TEST_F(DefaultVhalImplTest, testDebugSetFloat) {
ASSERT_EQ((size_t)1, events.size());
ASSERT_EQ((size_t)1, events[0]->value.floatValues.size());
EXPECT_EQ(10.5, events[0]->value.floatValues[0]);
EXPECT_EQ(1000, events[0]->timestamp);
VehiclePropValue value;
StatusCode status;

View file

@ -21,7 +21,7 @@ package {
cc_defaults {
name: "VehicleHalInterfaceDefaults",
static_libs: [
"android.hardware.automotive.vehicle-V2-ndk",
"android.hardware.automotive.vehicle.property-V2-ndk",
"android.hardware.automotive.vehicle-V3-ndk",
"android.hardware.automotive.vehicle.property-V3-ndk",
],
}

View file

@ -43,6 +43,17 @@
"auto-presubmit": [
{
"name": "VtsHalAutomotiveVehicle_TargetTest"
},
{
"name": "CarServiceUnitTest",
"options" : [
{
"include-filter": "com.android.car.hal.fakevhal.FakeVehicleStubUnitTest"
}
]
},
{
"name": "VehicleHalProtoMessageConverterTest"
}
]
}

View file

@ -27,7 +27,7 @@ aidl_interface {
srcs: [
"android/hardware/automotive/vehicle/*.aidl",
],
frozen: true,
frozen: false,
stability: "vintf",
backend: {
cpp: {

View file

@ -37,4 +37,6 @@ parcelable SubscribeOptions {
int propId;
int[] areaIds;
float sampleRate;
float resolution = 0.0f;
boolean enableVariableUpdateRate;
}

View file

@ -42,4 +42,6 @@ parcelable VehicleAreaConfig {
float minFloatValue;
float maxFloatValue;
@nullable long[] supportedEnumValues;
android.hardware.automotive.vehicle.VehiclePropertyAccess access = android.hardware.automotive.vehicle.VehiclePropertyAccess.NONE;
boolean supportVariableUpdateRate;
}

View file

@ -51,8 +51,8 @@ android_test {
":IVehicleGeneratedJavaFiles",
],
static_libs: [
"android.hardware.automotive.vehicle-V2-java",
"android.hardware.automotive.vehicle.property-V2-java",
"android.hardware.automotive.vehicle-V3-java",
"android.hardware.automotive.vehicle.property-V3-java",
"androidx.test.runner",
"truth",
],

View file

@ -39,4 +39,29 @@ parcelable SubscribeOptions {
* This value indicates how many updates per second client wants to receive.
*/
float sampleRate;
/**
* Requested resolution of property updates.
*
* This value indicates the resolution at which continuous property updates should be sent to
* the platform. For example, if resolution is 0.01, the subscribed property value should be
* rounded to two decimal places. If the incoming resolution value is not an integer multiple of
* 10, VHAL should return a StatusCode::INVALID_ARG.
*/
float resolution = 0.0f;
/**
* Whether to enable variable update rate.
*
* This only applies for continuous property. If variable update rate is
* enabled, for each given areaId, if VHAL supports variable update rate for
* the [propId, areaId], VHAL must ignore duplicate property value events
* and only sends changed value events (a.k.a treat continuous as an
* on-change property).
*
* If VHAL does not support variable update rate for the [propId, areaId],
* indicated by 'supportVariableUpdateRate' in 'VehicleAreaConfig', or if
* this property is not a continuous property, this option must be ignored.
*/
boolean enableVariableUpdateRate;
}

View file

@ -16,6 +16,8 @@
package android.hardware.automotive.vehicle;
import android.hardware.automotive.vehicle.VehiclePropertyAccess;
@VintfStability
@JavaDerive(equals=true, toString=true)
parcelable VehicleAreaConfig {
@ -47,4 +49,55 @@ parcelable VehicleAreaConfig {
* assumed all @data_enum values are supported unless specified through another mechanism.
*/
@nullable long[] supportedEnumValues;
/**
* Defines if the area ID for this property is READ, WRITE or READ_WRITE. This only applies if
* the property is defined in the framework as a READ_WRITE property. Access (if set) should be
* equal to, or a superset of, the VehiclePropConfig.access of the property.
*
* For example, if a property is defined as READ_WRITE, but the OEM wants to specify certain
* area Ids as READ-only, the corresponding areaIds should have an access set to READ, while the
* others must be set to READ_WRITE. We do not support setting specific area Ids to WRITE-only
* when the property is READ-WRITE.
*
* Exclusively one of VehiclePropConfig and the VehicleAreaConfigs should be specified for a
* single property. If VehiclePropConfig.access is populated, none of the
* VehicleAreaConfig.access values should be populated. If VehicleAreaConfig.access values are
* populated, VehiclePropConfig.access must not be populated.
*
* VehicleAreaConfigs should not be partially populated with access. If the OEM wants to specify
* access for one area Id, all other configs should be populated with their access levels as
* well.
*/
VehiclePropertyAccess access = VehiclePropertyAccess.NONE;
/**
* Whether variable update rate is supported.
*
* This applies for continuous property only.
*
* It is HIGHLY RECOMMENDED to support variable update rate for all non-heartbeat continuous
* properties for better performance unless the property is large.
*
* If variable update rate is supported and 'enableVariableUpdateRate' is true in subscribe
* options, VHAL must only sends property update event when the property's value changes
* (a.k.a treat continuous as an on-change property).
*
* E.g. if the client is subscribing at 5hz at time 0. If the property's value is 0 initially
* and becomes 1 after 1 second.
* If variable update rate is not enabled, VHAL clients will receive 5 property change events
* with value 0 and 5 events with value 1 after 2 seconds.
*
* If variable update rate is enabled, VHAL clients will receive 1 property change event
* with value 1 at time 1s. VHAL may/may not send a property event for the initial value (e.g.
* a property change event with value 0 at time 0s). VHAL client must not rely on the first
* property event, and must use getValues to fetch the initial value. In fact, car service is
* using getValues to fetch the initial value, convert it to a property event and deliver to
* car service clients.
*
* NOTE: If this is true, car service may cache the property update event for filtering purpose,
* so this should be false if the property is large (e.g. a byte array of 1k in size).
*/
boolean supportVariableUpdateRate;
}

View file

@ -28,6 +28,10 @@ parcelable VehiclePropConfig {
/**
* Defines if the property is read or write or both.
*
* If populating VehicleAreaConfig.access fields for this property, this field should not be
* populated. If the OEM decides to populate this field, none of the VehicleAreaConfig.access
* fields should be populated.
*/
VehiclePropertyAccess access = VehiclePropertyAccess.NONE;

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2023 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.
@ -68,9 +68,11 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::EV_CHARGE_PORT_CONNECTED, VehiclePropertyAccess::READ},
{VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyAccess::READ},
{VehicleProperty::RANGE_REMAINING, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::EV_BATTERY_AVERAGE_TEMPERATURE, VehiclePropertyAccess::READ},
{VehicleProperty::TIRE_PRESSURE, VehiclePropertyAccess::READ},
{VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyAccess::READ},
{VehicleProperty::ENGINE_IDLE_AUTO_STOP_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::IMPACT_DETECTED, VehiclePropertyAccess::READ},
{VehicleProperty::GEAR_SELECTION, VehiclePropertyAccess::READ},
{VehicleProperty::CURRENT_GEAR, VehiclePropertyAccess::READ},
{VehicleProperty::PARKING_BRAKE_ON, VehiclePropertyAccess::READ},
@ -83,6 +85,8 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::ABS_ACTIVE, VehiclePropertyAccess::READ},
{VehicleProperty::TRACTION_CONTROL_ACTIVE, VehiclePropertyAccess::READ},
{VehicleProperty::EV_STOPPING_MODE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::ELECTRONIC_STABILITY_CONTROL_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::ELECTRONIC_STABILITY_CONTROL_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::HVAC_FAN_SPEED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::HVAC_FAN_DIRECTION, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::HVAC_TEMPERATURE_CURRENT, VehiclePropertyAccess::READ},
@ -120,6 +124,7 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::AP_POWER_BOOTUP_REASON, VehiclePropertyAccess::READ},
{VehicleProperty::DISPLAY_BRIGHTNESS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::PER_DISPLAY_BRIGHTNESS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::VALET_MODE_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::HW_KEY_INPUT, VehiclePropertyAccess::READ},
{VehicleProperty::HW_KEY_INPUT_V2, VehiclePropertyAccess::READ},
{VehicleProperty::HW_MOTION_INPUT, VehiclePropertyAccess::READ},
@ -169,11 +174,13 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::SEAT_FOOTWELL_LIGHTS_SWITCH, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_EASY_ACCESS_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_AIRBAG_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_AIRBAGS_DEPLOYED, VehiclePropertyAccess::READ},
{VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_WALK_IN_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::SEAT_BELT_PRETENSIONER_DEPLOYED, VehiclePropertyAccess::READ},
{VehicleProperty::SEAT_OCCUPANCY, VehiclePropertyAccess::READ},
{VehicleProperty::WINDOW_POS, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::WINDOW_MOVE, VehiclePropertyAccess::READ_WRITE},
@ -192,6 +199,11 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::GLOVE_BOX_LOCKED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::VEHICLE_MAP_SERVICE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::LOCATION_CHARACTERIZATION, VehiclePropertyAccess::READ},
{VehicleProperty::ULTRASONICS_SENSOR_POSITION, VehiclePropertyAccess::READ},
{VehicleProperty::ULTRASONICS_SENSOR_ORIENTATION, VehiclePropertyAccess::READ},
{VehicleProperty::ULTRASONICS_SENSOR_FIELD_OF_VIEW, VehiclePropertyAccess::READ},
{VehicleProperty::ULTRASONICS_SENSOR_DETECTION_RANGE, VehiclePropertyAccess::READ},
{VehicleProperty::ULTRASONICS_SENSOR_SUPPORTED_RANGES, VehiclePropertyAccess::READ},
{VehicleProperty::OBD2_LIVE_FRAME, VehiclePropertyAccess::READ},
{VehicleProperty::OBD2_FREEZE_FRAME, VehiclePropertyAccess::READ},
{VehicleProperty::OBD2_FREEZE_FRAME_INFO, VehiclePropertyAccess::READ},
@ -247,6 +259,8 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess::READ},
{VehicleProperty::SHUTDOWN_REQUEST, VehiclePropertyAccess::WRITE},
{VehicleProperty::VEHICLE_IN_USE, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::CLUSTER_HEARTBEAT, VehiclePropertyAccess::WRITE},
{VehicleProperty::VEHICLE_DRIVING_AUTOMATION_CURRENT_LEVEL, VehiclePropertyAccess::READ},
{VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyAccess::READ_WRITE},
@ -272,6 +286,18 @@ std::unordered_map<VehicleProperty, VehiclePropertyAccess> AccessForVehiclePrope
{VehicleProperty::HANDS_ON_DETECTION_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::HANDS_ON_DETECTION_DRIVER_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::HANDS_ON_DETECTION_WARNING, VehiclePropertyAccess::READ},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING, VehiclePropertyAccess::READ},
{VehicleProperty::DRIVER_DISTRACTION_SYSTEM_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::DRIVER_DISTRACTION_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::DRIVER_DISTRACTION_WARNING_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::DRIVER_DISTRACTION_WARNING, VehiclePropertyAccess::READ},
{VehicleProperty::LOW_SPEED_COLLISION_WARNING_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::LOW_SPEED_COLLISION_WARNING_STATE, VehiclePropertyAccess::READ},
{VehicleProperty::CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyAccess::READ_WRITE},
{VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyAccess::READ},
};
} // namespace vehicle

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2023 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.
@ -68,9 +68,11 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::EV_CHARGE_PORT_CONNECTED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyChangeMode::CONTINUOUS},
{VehicleProperty::RANGE_REMAINING, VehiclePropertyChangeMode::CONTINUOUS},
{VehicleProperty::EV_BATTERY_AVERAGE_TEMPERATURE, VehiclePropertyChangeMode::CONTINUOUS},
{VehicleProperty::TIRE_PRESSURE, VehiclePropertyChangeMode::CONTINUOUS},
{VehicleProperty::CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::ENGINE_IDLE_AUTO_STOP_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::IMPACT_DETECTED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::GEAR_SELECTION, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::CURRENT_GEAR, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::PARKING_BRAKE_ON, VehiclePropertyChangeMode::ON_CHANGE},
@ -83,6 +85,8 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::ABS_ACTIVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::TRACTION_CONTROL_ACTIVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::EV_STOPPING_MODE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::ELECTRONIC_STABILITY_CONTROL_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::ELECTRONIC_STABILITY_CONTROL_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HVAC_FAN_SPEED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HVAC_FAN_DIRECTION, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HVAC_TEMPERATURE_CURRENT, VehiclePropertyChangeMode::ON_CHANGE},
@ -120,6 +124,7 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::AP_POWER_BOOTUP_REASON, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::PER_DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::VALET_MODE_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HW_KEY_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HW_KEY_INPUT_V2, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HW_MOTION_INPUT, VehiclePropertyChangeMode::ON_CHANGE},
@ -169,11 +174,13 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::SEAT_FOOTWELL_LIGHTS_SWITCH, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_EASY_ACCESS_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_AIRBAG_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_AIRBAGS_DEPLOYED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_WALK_IN_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_BELT_PRETENSIONER_DEPLOYED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::SEAT_OCCUPANCY, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::WINDOW_POS, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::WINDOW_MOVE, VehiclePropertyChangeMode::ON_CHANGE},
@ -192,6 +199,11 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::GLOVE_BOX_LOCKED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::VEHICLE_MAP_SERVICE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::LOCATION_CHARACTERIZATION, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::ULTRASONICS_SENSOR_POSITION, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::ULTRASONICS_SENSOR_ORIENTATION, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::ULTRASONICS_SENSOR_FIELD_OF_VIEW, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::ULTRASONICS_SENSOR_DETECTION_RANGE, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::ULTRASONICS_SENSOR_SUPPORTED_RANGES, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::OBD2_LIVE_FRAME, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::OBD2_FREEZE_FRAME, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::OBD2_FREEZE_FRAME_INFO, VehiclePropertyChangeMode::ON_CHANGE},
@ -247,6 +259,8 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode::STATIC},
{VehicleProperty::SHUTDOWN_REQUEST, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::VEHICLE_IN_USE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::CLUSTER_HEARTBEAT, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::VEHICLE_DRIVING_AUTOMATION_CURRENT_LEVEL, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
@ -272,6 +286,18 @@ std::unordered_map<VehicleProperty, VehiclePropertyChangeMode> ChangeModeForVehi
{VehicleProperty::HANDS_ON_DETECTION_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HANDS_ON_DETECTION_DRIVER_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::HANDS_ON_DETECTION_WARNING, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DISTRACTION_SYSTEM_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DISTRACTION_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DISTRACTION_WARNING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::DRIVER_DISTRACTION_WARNING, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::LOW_SPEED_COLLISION_WARNING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::LOW_SPEED_COLLISION_WARNING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyChangeMode::ON_CHANGE},
{VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyChangeMode::ON_CHANGE},
};
} // namespace vehicle

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2023 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.
@ -60,9 +60,11 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.EV_CHARGE_PORT_CONNECTED, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.RANGE_REMAINING, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.EV_BATTERY_AVERAGE_TEMPERATURE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.TIRE_PRESSURE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.ENGINE_IDLE_AUTO_STOP_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.IMPACT_DETECTED, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.GEAR_SELECTION, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.CURRENT_GEAR, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.PARKING_BRAKE_ON, VehiclePropertyAccess.READ),
@ -75,6 +77,8 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.ABS_ACTIVE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.TRACTION_CONTROL_ACTIVE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.EV_STOPPING_MODE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.ELECTRONIC_STABILITY_CONTROL_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.ELECTRONIC_STABILITY_CONTROL_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.HVAC_FAN_SPEED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.HVAC_FAN_DIRECTION, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.HVAC_TEMPERATURE_CURRENT, VehiclePropertyAccess.READ),
@ -112,6 +116,7 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.AP_POWER_BOOTUP_REASON, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.DISPLAY_BRIGHTNESS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.PER_DISPLAY_BRIGHTNESS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.VALET_MODE_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.HW_KEY_INPUT, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.HW_KEY_INPUT_V2, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.HW_MOTION_INPUT, VehiclePropertyAccess.READ),
@ -161,11 +166,13 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.SEAT_FOOTWELL_LIGHTS_SWITCH, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_EASY_ACCESS_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_AIRBAG_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_AIRBAGS_DEPLOYED, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_WALK_IN_POS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.SEAT_BELT_PRETENSIONER_DEPLOYED, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.SEAT_OCCUPANCY, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.WINDOW_POS, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.WINDOW_MOVE, VehiclePropertyAccess.READ_WRITE),
@ -184,6 +191,11 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.GLOVE_BOX_LOCKED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.VEHICLE_MAP_SERVICE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.LOCATION_CHARACTERIZATION, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_POSITION, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_ORIENTATION, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_FIELD_OF_VIEW, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_DETECTION_RANGE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_SUPPORTED_RANGES, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.OBD2_LIVE_FRAME, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.OBD2_FREEZE_FRAME, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.OBD2_FREEZE_FRAME_INFO, VehiclePropertyAccess.READ),
@ -239,6 +251,8 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.SHUTDOWN_REQUEST, VehiclePropertyAccess.WRITE),
Map.entry(VehicleProperty.VEHICLE_IN_USE, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.CLUSTER_HEARTBEAT, VehiclePropertyAccess.WRITE),
Map.entry(VehicleProperty.VEHICLE_DRIVING_AUTOMATION_CURRENT_LEVEL, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyAccess.READ_WRITE),
@ -263,7 +277,19 @@ public final class AccessForVehicleProperty {
Map.entry(VehicleProperty.ADAPTIVE_CRUISE_CONTROL_LEAD_VEHICLE_MEASURED_DISTANCE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_DRIVER_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_WARNING, VehiclePropertyAccess.READ)
Map.entry(VehicleProperty.HANDS_ON_DETECTION_WARNING, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_WARNING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_WARNING, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_SYSTEM_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_WARNING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_WARNING, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.LOW_SPEED_COLLISION_WARNING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.LOW_SPEED_COLLISION_WARNING_STATE, VehiclePropertyAccess.READ),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyAccess.READ_WRITE),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyAccess.READ)
);
}

View file

@ -1,5 +1,5 @@
/*
* Copyright (C) 2022 The Android Open Source Project
* Copyright (C) 2023 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.
@ -60,9 +60,11 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.EV_CHARGE_PORT_CONNECTED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.EV_BATTERY_INSTANTANEOUS_CHARGE_RATE, VehiclePropertyChangeMode.CONTINUOUS),
Map.entry(VehicleProperty.RANGE_REMAINING, VehiclePropertyChangeMode.CONTINUOUS),
Map.entry(VehicleProperty.EV_BATTERY_AVERAGE_TEMPERATURE, VehiclePropertyChangeMode.CONTINUOUS),
Map.entry(VehicleProperty.TIRE_PRESSURE, VehiclePropertyChangeMode.CONTINUOUS),
Map.entry(VehicleProperty.CRITICALLY_LOW_TIRE_PRESSURE, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.ENGINE_IDLE_AUTO_STOP_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.IMPACT_DETECTED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.GEAR_SELECTION, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.CURRENT_GEAR, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.PARKING_BRAKE_ON, VehiclePropertyChangeMode.ON_CHANGE),
@ -75,6 +77,8 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.ABS_ACTIVE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.TRACTION_CONTROL_ACTIVE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.EV_STOPPING_MODE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.ELECTRONIC_STABILITY_CONTROL_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.ELECTRONIC_STABILITY_CONTROL_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HVAC_FAN_SPEED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HVAC_FAN_DIRECTION, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HVAC_TEMPERATURE_CURRENT, VehiclePropertyChangeMode.ON_CHANGE),
@ -112,6 +116,7 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.AP_POWER_BOOTUP_REASON, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.PER_DISPLAY_BRIGHTNESS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.VALET_MODE_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HW_KEY_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HW_KEY_INPUT_V2, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HW_MOTION_INPUT, VehiclePropertyChangeMode.ON_CHANGE),
@ -161,11 +166,13 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.SEAT_FOOTWELL_LIGHTS_SWITCH, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_EASY_ACCESS_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_AIRBAG_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_AIRBAGS_DEPLOYED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_POS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_CUSHION_SIDE_SUPPORT_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_POS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_LUMBAR_VERTICAL_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_WALK_IN_POS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_BELT_PRETENSIONER_DEPLOYED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.SEAT_OCCUPANCY, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.WINDOW_POS, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.WINDOW_MOVE, VehiclePropertyChangeMode.ON_CHANGE),
@ -184,6 +191,11 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.GLOVE_BOX_LOCKED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.VEHICLE_MAP_SERVICE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.LOCATION_CHARACTERIZATION, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_POSITION, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_ORIENTATION, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_FIELD_OF_VIEW, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_DETECTION_RANGE, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.ULTRASONICS_SENSOR_SUPPORTED_RANGES, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.OBD2_LIVE_FRAME, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.OBD2_FREEZE_FRAME, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.OBD2_FREEZE_FRAME_INFO, VehiclePropertyChangeMode.ON_CHANGE),
@ -239,6 +251,8 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.SUPPORTED_PROPERTY_IDS, VehiclePropertyChangeMode.STATIC),
Map.entry(VehicleProperty.SHUTDOWN_REQUEST, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.VEHICLE_IN_USE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.CLUSTER_HEARTBEAT, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.VEHICLE_DRIVING_AUTOMATION_CURRENT_LEVEL, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.FORWARD_COLLISION_WARNING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
@ -263,7 +277,19 @@ public final class ChangeModeForVehicleProperty {
Map.entry(VehicleProperty.ADAPTIVE_CRUISE_CONTROL_LEAD_VEHICLE_MEASURED_DISTANCE, VehiclePropertyChangeMode.CONTINUOUS),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_DRIVER_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_WARNING, VehiclePropertyChangeMode.ON_CHANGE)
Map.entry(VehicleProperty.HANDS_ON_DETECTION_WARNING, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_WARNING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_WARNING, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_SYSTEM_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_WARNING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_WARNING, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.LOW_SPEED_COLLISION_WARNING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.LOW_SPEED_COLLISION_WARNING_STATE, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_ENABLED, VehiclePropertyChangeMode.ON_CHANGE),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, VehiclePropertyChangeMode.ON_CHANGE)
);
}

View file

@ -0,0 +1,109 @@
/*
* Copyright (C) 2023 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.
*/
/**
* DO NOT EDIT MANUALLY!!!
*
* Generated by tools/generate_annotation_enums.py.
*/
// clang-format off
package android.hardware.automotive.vehicle;
import java.util.List;
import java.util.Map;
public final class EnumForVehicleProperty {
public static final Map<Integer, List<Class<?>>> values = Map.ofEntries(
Map.entry(VehicleProperty.INFO_FUEL_TYPE, List.of(FuelType.class)),
Map.entry(VehicleProperty.INFO_EV_CONNECTOR_TYPE, List.of(EvConnectorType.class)),
Map.entry(VehicleProperty.INFO_FUEL_DOOR_LOCATION, List.of(PortLocationType.class)),
Map.entry(VehicleProperty.INFO_EV_PORT_LOCATION, List.of(PortLocationType.class)),
Map.entry(VehicleProperty.INFO_DRIVER_SEAT, List.of(VehicleAreaSeat.class)),
Map.entry(VehicleProperty.INFO_MULTI_EV_PORT_LOCATIONS, List.of(PortLocationType.class)),
Map.entry(VehicleProperty.ENGINE_OIL_LEVEL, List.of(VehicleOilLevel.class)),
Map.entry(VehicleProperty.IMPACT_DETECTED, List.of(ImpactSensorLocation.class)),
Map.entry(VehicleProperty.GEAR_SELECTION, List.of(VehicleGear.class)),
Map.entry(VehicleProperty.CURRENT_GEAR, List.of(VehicleGear.class)),
Map.entry(VehicleProperty.TURN_SIGNAL_STATE, List.of(VehicleTurnSignal.class)),
Map.entry(VehicleProperty.IGNITION_STATE, List.of(VehicleIgnitionState.class)),
Map.entry(VehicleProperty.EV_STOPPING_MODE, List.of(EvStoppingMode.class)),
Map.entry(VehicleProperty.ELECTRONIC_STABILITY_CONTROL_STATE, List.of(ElectronicStabilityControlState.class, ErrorState.class)),
Map.entry(VehicleProperty.HVAC_FAN_DIRECTION, List.of(VehicleHvacFanDirection.class)),
Map.entry(VehicleProperty.HVAC_TEMPERATURE_DISPLAY_UNITS, List.of(VehicleUnit.class)),
Map.entry(VehicleProperty.HVAC_FAN_DIRECTION_AVAILABLE, List.of(VehicleHvacFanDirection.class)),
Map.entry(VehicleProperty.DISTANCE_DISPLAY_UNITS, List.of(VehicleUnit.class)),
Map.entry(VehicleProperty.FUEL_VOLUME_DISPLAY_UNITS, List.of(VehicleUnit.class)),
Map.entry(VehicleProperty.TIRE_PRESSURE_DISPLAY_UNITS, List.of(VehicleUnit.class)),
Map.entry(VehicleProperty.EV_BATTERY_DISPLAY_UNITS, List.of(VehicleUnit.class)),
Map.entry(VehicleProperty.HW_ROTARY_INPUT, List.of(RotaryInputType.class)),
Map.entry(VehicleProperty.HW_CUSTOM_INPUT, List.of(CustomInputType.class)),
Map.entry(VehicleProperty.SEAT_FOOTWELL_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.SEAT_FOOTWELL_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.SEAT_AIRBAGS_DEPLOYED, List.of(VehicleAirbagLocation.class)),
Map.entry(VehicleProperty.SEAT_OCCUPANCY, List.of(VehicleSeatOccupancyState.class)),
Map.entry(VehicleProperty.WINDSHIELD_WIPERS_STATE, List.of(WindshieldWipersState.class)),
Map.entry(VehicleProperty.WINDSHIELD_WIPERS_SWITCH, List.of(WindshieldWipersSwitch.class)),
Map.entry(VehicleProperty.HEADLIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.FOG_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.HAZARD_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.HEADLIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.HIGH_BEAM_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.FOG_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.HAZARD_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.CABIN_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.CABIN_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.READING_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.READING_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.STEERING_WHEEL_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.STEERING_WHEEL_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_TYPE, List.of(ElectronicTollCollectionCardType.class)),
Map.entry(VehicleProperty.ELECTRONIC_TOLL_COLLECTION_CARD_STATUS, List.of(ElectronicTollCollectionCardStatus.class)),
Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.FRONT_FOG_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.REAR_FOG_LIGHTS_STATE, List.of(VehicleLightState.class)),
Map.entry(VehicleProperty.REAR_FOG_LIGHTS_SWITCH, List.of(VehicleLightSwitch.class)),
Map.entry(VehicleProperty.EV_CHARGE_STATE, List.of(EvChargeState.class)),
Map.entry(VehicleProperty.EV_REGENERATIVE_BRAKING_STATE, List.of(EvRegenerativeBrakingState.class)),
Map.entry(VehicleProperty.TRAILER_PRESENT, List.of(TrailerState.class)),
Map.entry(VehicleProperty.GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT, List.of(GsrComplianceRequirementType.class)),
Map.entry(VehicleProperty.SHUTDOWN_REQUEST, List.of(VehicleApPowerStateShutdownParam.class)),
Map.entry(VehicleProperty.VEHICLE_DRIVING_AUTOMATION_CURRENT_LEVEL, List.of(VehicleAutonomousState.class)),
Map.entry(VehicleProperty.AUTOMATIC_EMERGENCY_BRAKING_STATE, List.of(AutomaticEmergencyBrakingState.class, ErrorState.class)),
Map.entry(VehicleProperty.FORWARD_COLLISION_WARNING_STATE, List.of(ForwardCollisionWarningState.class, ErrorState.class)),
Map.entry(VehicleProperty.BLIND_SPOT_WARNING_STATE, List.of(BlindSpotWarningState.class, ErrorState.class)),
Map.entry(VehicleProperty.LANE_DEPARTURE_WARNING_STATE, List.of(LaneDepartureWarningState.class, ErrorState.class)),
Map.entry(VehicleProperty.LANE_KEEP_ASSIST_STATE, List.of(LaneKeepAssistState.class, ErrorState.class)),
Map.entry(VehicleProperty.LANE_CENTERING_ASSIST_COMMAND, List.of(LaneCenteringAssistCommand.class)),
Map.entry(VehicleProperty.LANE_CENTERING_ASSIST_STATE, List.of(LaneCenteringAssistState.class, ErrorState.class)),
Map.entry(VehicleProperty.EMERGENCY_LANE_KEEP_ASSIST_STATE, List.of(EmergencyLaneKeepAssistState.class, ErrorState.class)),
Map.entry(VehicleProperty.CRUISE_CONTROL_TYPE, List.of(CruiseControlType.class, ErrorState.class)),
Map.entry(VehicleProperty.CRUISE_CONTROL_STATE, List.of(CruiseControlState.class, ErrorState.class)),
Map.entry(VehicleProperty.CRUISE_CONTROL_COMMAND, List.of(CruiseControlCommand.class)),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_DRIVER_STATE, List.of(HandsOnDetectionDriverState.class, ErrorState.class)),
Map.entry(VehicleProperty.HANDS_ON_DETECTION_WARNING, List.of(HandsOnDetectionWarning.class, ErrorState.class)),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_STATE, List.of(DriverDrowsinessAttentionState.class, ErrorState.class)),
Map.entry(VehicleProperty.DRIVER_DROWSINESS_ATTENTION_WARNING, List.of(DriverDrowsinessAttentionWarning.class, ErrorState.class)),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_STATE, List.of(DriverDistractionState.class, ErrorState.class)),
Map.entry(VehicleProperty.DRIVER_DISTRACTION_WARNING, List.of(DriverDistractionWarning.class, ErrorState.class)),
Map.entry(VehicleProperty.LOW_SPEED_COLLISION_WARNING_STATE, List.of(LowSpeedCollisionWarningState.class, ErrorState.class)),
Map.entry(VehicleProperty.CROSS_TRAFFIC_MONITORING_WARNING_STATE, List.of(CrossTrafficMonitoringWarningState.class, ErrorState.class))
);
}

View file

@ -46,7 +46,7 @@ implementations (including reference VHAL). Vendor VHAL implementation could
use this library, along with their own implementation for `IVehicleHardware`
interface.
Also defines a binary `android.hardware.automotive.vehicle@V1-default-service`
Also defines a binary `android.hardware.automotive.vehicle@V3-default-service`
which is the reference VHAL implementation. It implements `IVehicle.aidl`
interface. It uses `DefaultVehicleHal`, along with `FakeVehicleHardware`
(in fake_impl). It simulates the vehicle bus interaction by using an

View file

@ -35,14 +35,17 @@ cc_library {
cc_library {
name: "VehicleHalJsonConfigLoaderEnableTestProperties",
vendor: true,
srcs: ["src/*.cpp"],
srcs: [
"src/*.cpp",
":VhalTestVendorProperties",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
defaults: ["VehicleHalDefaults"],
static_libs: ["VehicleHalUtils"],
header_libs: [
"VehicleHalTestUtilHeaders",
"IVehicleGeneratedHeaders",
"libbinder_headers",
],
cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
shared_libs: ["libjsoncpp"],

View file

@ -21,7 +21,7 @@
#include <PropertyUtils.h>
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
#include <TestPropertyUtils.h>
#include <android/hardware/automotive/vehicle/TestVendorProperty.h>
#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
#include <android-base/strings.h>
@ -38,9 +38,15 @@ using ::aidl::android::hardware::automotive::vehicle::AccessForVehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::AutomaticEmergencyBrakingState;
using ::aidl::android::hardware::automotive::vehicle::BlindSpotWarningState;
using ::aidl::android::hardware::automotive::vehicle::ChangeModeForVehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::CrossTrafficMonitoringWarningState;
using ::aidl::android::hardware::automotive::vehicle::CruiseControlCommand;
using ::aidl::android::hardware::automotive::vehicle::CruiseControlState;
using ::aidl::android::hardware::automotive::vehicle::CruiseControlType;
using ::aidl::android::hardware::automotive::vehicle::DriverDistractionState;
using ::aidl::android::hardware::automotive::vehicle::DriverDistractionWarning;
using ::aidl::android::hardware::automotive::vehicle::DriverDrowsinessAttentionState;
using ::aidl::android::hardware::automotive::vehicle::DriverDrowsinessAttentionWarning;
using ::aidl::android::hardware::automotive::vehicle::ElectronicStabilityControlState;
using ::aidl::android::hardware::automotive::vehicle::EmergencyLaneKeepAssistState;
using ::aidl::android::hardware::automotive::vehicle::ErrorState;
using ::aidl::android::hardware::automotive::vehicle::EvConnectorType;
@ -51,17 +57,21 @@ using ::aidl::android::hardware::automotive::vehicle::FuelType;
using ::aidl::android::hardware::automotive::vehicle::GsrComplianceRequirementType;
using ::aidl::android::hardware::automotive::vehicle::HandsOnDetectionDriverState;
using ::aidl::android::hardware::automotive::vehicle::HandsOnDetectionWarning;
using ::aidl::android::hardware::automotive::vehicle::ImpactSensorLocation;
using ::aidl::android::hardware::automotive::vehicle::LaneCenteringAssistCommand;
using ::aidl::android::hardware::automotive::vehicle::LaneCenteringAssistState;
using ::aidl::android::hardware::automotive::vehicle::LaneDepartureWarningState;
using ::aidl::android::hardware::automotive::vehicle::LaneKeepAssistState;
using ::aidl::android::hardware::automotive::vehicle::LocationCharacterization;
using ::aidl::android::hardware::automotive::vehicle::LowSpeedCollisionWarningState;
using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
using ::aidl::android::hardware::automotive::vehicle::VehicleAirbagLocation;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaMirror;
using ::aidl::android::hardware::automotive::vehicle::VehicleAreaWindow;
using ::aidl::android::hardware::automotive::vehicle::VehicleAutonomousState;
using ::aidl::android::hardware::automotive::vehicle::VehicleGear;
using ::aidl::android::hardware::automotive::vehicle::VehicleHvacFanDirection;
using ::aidl::android::hardware::automotive::vehicle::VehicleIgnitionState;
@ -91,10 +101,6 @@ const std::unordered_map<std::string, int> CONSTANTS_BY_NAME = {
{"HVAC_ALL", HVAC_ALL},
{"HVAC_LEFT", HVAC_LEFT},
{"HVAC_RIGHT", HVAC_RIGHT},
{"VENDOR_EXTENSION_INT_PROPERTY", VENDOR_EXTENSION_INT_PROPERTY},
{"VENDOR_EXTENSION_BOOLEAN_PROPERTY", VENDOR_EXTENSION_BOOLEAN_PROPERTY},
{"VENDOR_EXTENSION_STRING_PROPERTY", VENDOR_EXTENSION_STRING_PROPERTY},
{"VENDOR_EXTENSION_FLOAT_PROPERTY", VENDOR_EXTENSION_FLOAT_PROPERTY},
{"WINDOW_1_LEFT", WINDOW_1_LEFT},
{"WINDOW_1_RIGHT", WINDOW_1_RIGHT},
{"WINDOW_2_LEFT", WINDOW_2_LEFT},
@ -133,24 +139,9 @@ const std::unordered_map<std::string, int> CONSTANTS_BY_NAME = {
{"EV_STOPPING_MODE_HOLD", EV_STOPPING_MODE_HOLD},
{"MIRROR_DRIVER_LEFT_RIGHT",
toInt(VehicleAreaMirror::DRIVER_LEFT) | toInt(VehicleAreaMirror::DRIVER_RIGHT)},
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
// Following are test properties:
{"ECHO_REVERSE_BYTES", ECHO_REVERSE_BYTES},
{"VENDOR_PROPERTY_ID", VENDOR_PROPERTY_ID},
{"kMixedTypePropertyForTest", kMixedTypePropertyForTest},
{"VENDOR_CLUSTER_NAVIGATION_STATE", VENDOR_CLUSTER_NAVIGATION_STATE},
{"VENDOR_CLUSTER_REQUEST_DISPLAY", VENDOR_CLUSTER_REQUEST_DISPLAY},
{"VENDOR_CLUSTER_SWITCH_UI", VENDOR_CLUSTER_SWITCH_UI},
{"VENDOR_CLUSTER_DISPLAY_STATE", VENDOR_CLUSTER_DISPLAY_STATE},
{"VENDOR_CLUSTER_REPORT_STATE", VENDOR_CLUSTER_REPORT_STATE},
{"PLACEHOLDER_PROPERTY_INT", PLACEHOLDER_PROPERTY_INT},
{"PLACEHOLDER_PROPERTY_FLOAT", PLACEHOLDER_PROPERTY_FLOAT},
{"PLACEHOLDER_PROPERTY_BOOLEAN", PLACEHOLDER_PROPERTY_BOOLEAN},
{"PLACEHOLDER_PROPERTY_STRING", PLACEHOLDER_PROPERTY_STRING}
#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
};
// A class to parse constant values for type T.
// A class to parse constant values for type T where T is defined as an enum in NDK AIDL backend.
template <class T>
class ConstantParser final : public ConstantParserInterface {
public:
@ -181,6 +172,33 @@ class ConstantParser final : public ConstantParserInterface {
std::unordered_map<std::string, int> mValueByName;
};
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
// A class to parse constant values for type T where T is defined as an enum in CPP AIDL backend.
template <class T>
class CppConstantParser final : public ConstantParserInterface {
public:
CppConstantParser() {
for (const T& v : android::enum_range<T>()) {
std::string name = android::hardware::automotive::vehicle::toString(v);
mValueByName[name] = toInt(v);
}
}
~CppConstantParser() = default;
Result<int> parseValue(const std::string& name) const override {
auto it = mValueByName.find(name);
if (it == mValueByName.end()) {
return Error() << "Constant name: " << name << " is not defined";
}
return it->second;
}
private:
std::unordered_map<std::string, int> mValueByName;
};
#endif
// A class to parse constant values defined in CONSTANTS_BY_NAME map.
class LocalVariableParser final : public ConstantParserInterface {
public:
@ -232,6 +250,12 @@ JsonValueParser::JsonValueParser() {
std::make_unique<ConstantParser<WindshieldWipersState>>();
mConstantParsersByType["WindshieldWipersSwitch"] =
std::make_unique<ConstantParser<WindshieldWipersSwitch>>();
mConstantParsersByType["VehicleAutonomousState"] =
std::make_unique<ConstantParser<VehicleAutonomousState>>();
mConstantParsersByType["VehicleAirbagLocation"] =
std::make_unique<ConstantParser<VehicleAirbagLocation>>();
mConstantParsersByType["ImpactSensorLocation"] =
std::make_unique<ConstantParser<ImpactSensorLocation>>();
mConstantParsersByType["EmergencyLaneKeepAssistState"] =
std::make_unique<ConstantParser<EmergencyLaneKeepAssistState>>();
mConstantParsersByType["CruiseControlType"] =
@ -244,6 +268,14 @@ JsonValueParser::JsonValueParser() {
std::make_unique<ConstantParser<HandsOnDetectionDriverState>>();
mConstantParsersByType["HandsOnDetectionWarning"] =
std::make_unique<ConstantParser<HandsOnDetectionWarning>>();
mConstantParsersByType["DriverDrowsinessAttentionState"] =
std::make_unique<ConstantParser<DriverDrowsinessAttentionState>>();
mConstantParsersByType["DriverDrowsinessAttentionWarning"] =
std::make_unique<ConstantParser<DriverDrowsinessAttentionWarning>>();
mConstantParsersByType["DriverDistractionState"] =
std::make_unique<ConstantParser<DriverDistractionState>>();
mConstantParsersByType["DriverDistractionWarning"] =
std::make_unique<ConstantParser<DriverDistractionWarning>>();
mConstantParsersByType["ErrorState"] = std::make_unique<ConstantParser<ErrorState>>();
mConstantParsersByType["AutomaticEmergencyBrakingState"] =
std::make_unique<ConstantParser<AutomaticEmergencyBrakingState>>();
@ -259,7 +291,27 @@ JsonValueParser::JsonValueParser() {
std::make_unique<ConstantParser<LaneCenteringAssistCommand>>();
mConstantParsersByType["LaneCenteringAssistState"] =
std::make_unique<ConstantParser<LaneCenteringAssistState>>();
mConstantParsersByType["LowSpeedCollisionWarningState"] =
std::make_unique<ConstantParser<LowSpeedCollisionWarningState>>();
mConstantParsersByType["ElectronicStabilityControlState"] =
std::make_unique<ConstantParser<ElectronicStabilityControlState>>();
mConstantParsersByType["CrossTrafficMonitoringWarningState"] =
std::make_unique<ConstantParser<CrossTrafficMonitoringWarningState>>();
mConstantParsersByType["Constants"] = std::make_unique<LocalVariableParser>();
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
mConstantParsersByType["TestVendorProperty"] =
std::make_unique<CppConstantParser<TestVendorProperty>>();
#endif // ENABLE_VEHICLE_HAL_TEST_PROPERTIES
}
template <>
Result<bool> JsonValueParser::convertValueToType<bool>(const std::string& fieldName,
const Json::Value& value) {
if (!value.isBool()) {
return Error() << "The value: " << value << " for field: " << fieldName
<< " is not in correct type, expect bool";
}
return value.asBool();
}
template <>
@ -519,6 +571,12 @@ void JsonConfigParser::parseAreas(const Json::Value& parentJsonNode, const std::
tryParseJsonValueToVariable(jsonAreaConfig, "maxFloatValue", /*optional=*/true,
&areaConfig.maxFloatValue, errors);
// By default we support variable update rate for all properties except it is explicitly
// disabled.
areaConfig.supportVariableUpdateRate = true;
tryParseJsonValueToVariable(jsonAreaConfig, "supportVariableUpdateRate", /*optional=*/true,
&areaConfig.supportVariableUpdateRate, errors);
std::vector<int64_t> supportedEnumValues;
tryParseJsonArrayToVariable(jsonAreaConfig, "supportedEnumValues", /*optional=*/true,
&supportedEnumValues, errors);
@ -573,6 +631,16 @@ std::optional<ConfigDeclaration> JsonConfigParser::parseEachProperty(
if (errors->size() != initialErrorCount) {
return std::nullopt;
}
// If there is no area config, by default we allow variable update rate, so we have to add
// a global area config.
if (configDecl.config.areaConfigs.size() == 0) {
VehicleAreaConfig areaConfig = {
.areaId = 0,
.supportVariableUpdateRate = true,
};
configDecl.config.areaConfigs.push_back(std::move(areaConfig));
}
return configDecl;
}

View file

@ -1327,6 +1327,75 @@
}
]
},
{
"property": "VehicleProperty::SEAT_AIRBAGS_DEPLOYED",
"defaultValue": {
"int32Values": [
0
]
},
"areas": [
{
"areaId": "Constants::SEAT_1_LEFT",
"supportedEnumValues": [
"VehicleAirbagLocation::FRONT",
"VehicleAirbagLocation::KNEE",
"VehicleAirbagLocation::LEFT_SIDE",
"VehicleAirbagLocation::RIGHT_SIDE",
"VehicleAirbagLocation::CURTAIN"
]
},
{
"areaId": "Constants::SEAT_1_RIGHT",
"supportedEnumValues": [
"VehicleAirbagLocation::FRONT",
"VehicleAirbagLocation::KNEE",
"VehicleAirbagLocation::LEFT_SIDE",
"VehicleAirbagLocation::RIGHT_SIDE",
"VehicleAirbagLocation::CURTAIN"
]
},
{
"areaId": "Constants::SEAT_2_LEFT",
"supportedEnumValues": [
"VehicleAirbagLocation::FRONT",
"VehicleAirbagLocation::CURTAIN"
]
},
{
"areaId": "Constants::SEAT_2_RIGHT",
"supportedEnumValues": [
"VehicleAirbagLocation::FRONT",
"VehicleAirbagLocation::CURTAIN"
]
}
]
},
{
"property": "VehicleProperty::SEAT_BELT_PRETENSIONER_DEPLOYED",
"defaultValue": {
"int32Values": [
0
]
},
"areas": [
{
"areaId": "Constants::SEAT_1_LEFT"
},
{
"areaId": "Constants::SEAT_1_RIGHT"
},
{
"areaId": "Constants::SEAT_2_LEFT"
},
{
"areaId": "Constants::SEAT_2_RIGHT"
},
{
"areaId": "Constants::SEAT_2_CENTER"
}
]
},
{
"property": "VehicleProperty::SEAT_OCCUPANCY",
"defaultValue": {
@ -1559,6 +1628,16 @@
"maxSampleRate": 2.0,
"minSampleRate": 1.0
},
{
"property": "VehicleProperty::EV_BATTERY_AVERAGE_TEMPERATURE",
"defaultValue": {
"floatValues": [
25.0
]
},
"maxSampleRate": 2.0,
"minSampleRate": 1.0
},
{
"property": "VehicleProperty::TIRE_PRESSURE",
"defaultValue": {
@ -1899,8 +1978,16 @@
}
],
"configArray": [
"VehicleProperty::HVAC_ACTUAL_FAN_SPEED_RPM",
"VehicleProperty::HVAC_AC_ON",
"VehicleProperty::HVAC_AUTO_ON",
"VehicleProperty::HVAC_AUTO_RECIRC_ON",
"VehicleProperty::HVAC_FAN_DIRECTION",
"VehicleProperty::HVAC_FAN_SPEED",
"VehicleProperty::HVAC_FAN_DIRECTION"
"VehicleProperty::HVAC_MAX_AC_ON",
"VehicleProperty::HVAC_RECIRC_ON",
"VehicleProperty::HVAC_TEMPERATURE_CURRENT",
"VehicleProperty::HVAC_TEMPERATURE_SET"
]
},
{
@ -2360,9 +2447,9 @@
160,
280,
5,
600,
840,
10
608,
824,
9
]
},
{
@ -2372,7 +2459,7 @@
66.19999694824219,
"VehicleUnit::FAHRENHEIT",
19.0,
66.0
66.2
]
}
},
@ -2494,6 +2581,27 @@
]
}
},
{
"property": "VehicleProperty::IMPACT_DETECTED",
"defaultValue": {
"int32Values": [
0
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ImpactSensorLocation::FRONT",
"ImpactSensorLocation::FRONT_LEFT_DOOR_SIDE",
"ImpactSensorLocation::FRONT_RIGHT_DOOR_SIDE",
"ImpactSensorLocation::REAR_LEFT_DOOR_SIDE",
"ImpactSensorLocation::REAR_RIGHT_DOOR_SIDE",
"ImpactSensorLocation::REAR"
]
}
]
},
{
"property": "VehicleProperty::DOOR_LOCK",
"areas": [
@ -3102,6 +3210,14 @@
}
]
},
{
"property": "VehicleProperty::VALET_MODE_ENABLED",
"defaultValue": {
"int32Values": [
0
]
}
},
{
"property": "VehicleProperty::OBD2_LIVE_FRAME",
"configArray": [
@ -3387,7 +3503,7 @@
"property": "VehicleProperty::CRUISE_CONTROL_TYPE",
"defaultValue": {
"int32Values": [
"CruiseControlType::STANDARD"
"CruiseControlType::ADAPTIVE"
]
},
"areas": [
@ -3537,6 +3653,117 @@
}
]
},
{
"property": "VehicleProperty::DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::DRIVER_DROWSINESS_ATTENTION_STATE",
"defaultValue": {
"int32Values": [
"DriverDrowsinessAttentionState::KSS_RATING_3_ALERT"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_DISABLED",
"DriverDrowsinessAttentionState::KSS_RATING_1_EXTREMELY_ALERT",
"DriverDrowsinessAttentionState::KSS_RATING_2_VERY_ALERT",
"DriverDrowsinessAttentionState::KSS_RATING_3_ALERT",
"DriverDrowsinessAttentionState::KSS_RATING_4_RATHER_ALERT",
"DriverDrowsinessAttentionState::KSS_RATING_5_NEITHER_ALERT_NOR_SLEEPY",
"DriverDrowsinessAttentionState::KSS_RATING_6_SOME_SLEEPINESS",
"DriverDrowsinessAttentionState::KSS_RATING_7_SLEEPY_NO_EFFORT",
"DriverDrowsinessAttentionState::KSS_RATING_8_SLEEPY_SOME_EFFORT",
"DriverDrowsinessAttentionState::KSS_RATING_9_VERY_SLEEPY"
]
}
]
},
{
"property": "VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING",
"defaultValue": {
"int32Values": [
"DriverDrowsinessAttentionWarning::NO_WARNING"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_DISABLED",
"DriverDrowsinessAttentionWarning::NO_WARNING",
"DriverDrowsinessAttentionWarning::WARNING"
]
}
]
},
{
"property": "VehicleProperty::DRIVER_DISTRACTION_SYSTEM_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::DRIVER_DISTRACTION_STATE",
"defaultValue": {
"int32Values": [
"DriverDistractionState::NOT_DISTRACTED"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_DISABLED",
"DriverDistractionState::NOT_DISTRACTED",
"DriverDistractionState::DISTRACTED"
]
}
]
},
{
"property": "VehicleProperty::DRIVER_DISTRACTION_WARNING_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::DRIVER_DISTRACTION_WARNING",
"defaultValue": {
"int32Values": [
"DriverDistractionWarning::NO_WARNING"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_DISABLED",
"DriverDistractionWarning::NO_WARNING",
"DriverDistractionWarning::WARNING"
]
}
]
},
{
"property": "VehicleProperty::INITIAL_USER_INFO"
},
@ -3571,7 +3798,13 @@
"property": "VehicleProperty::WATCHDOG_TERMINATED_PROCESS"
},
{
"property": "VehicleProperty::VHAL_HEARTBEAT"
"property": "VehicleProperty::VHAL_HEARTBEAT",
"areas": [
{
"areaId": 0,
"supportVariableUpdateRate": false
}
]
},
{
"property": "VehicleProperty::CLUSTER_SWITCH_UI",
@ -3620,6 +3853,27 @@
{
"property": "VehicleProperty::CLUSTER_NAVIGATION_STATE"
},
{
"property": "VehicleProperty::CLUSTER_HEARTBEAT",
"configArray": [
0,
0,
0,
0,
0,
2,
0,
0,
16
],
"areas": [
{
"areaId": 0,
"supportVariableUpdateRate": false
}
],
"comment": "configArray specifies it consists of int64[2] and byte[16]."
},
{
"property": "VehicleProperty::GENERAL_SAFETY_REGULATION_COMPLIANCE_REQUIREMENT",
"defaultValue": {
@ -3628,6 +3882,25 @@
]
}
},
{
"property": "VehicleProperty::SHUTDOWN_REQUEST"
},
{
"property": "VehicleProperty::VEHICLE_IN_USE",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::VEHICLE_DRIVING_AUTOMATION_CURRENT_LEVEL",
"defaultValue": {
"int32Values": [
"VehicleAutonomousState::LEVEL_0"
]
}
},
{
"property": "VehicleProperty::AUTOMATIC_EMERGENCY_BRAKING_ENABLED",
"defaultValue": {
@ -3826,6 +4099,98 @@
]
}
]
},
{
"property": "VehicleProperty::LOW_SPEED_COLLISION_WARNING_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::LOW_SPEED_COLLISION_WARNING_STATE",
"defaultValue": {
"int32Values": [
"LowSpeedCollisionWarningState::NO_WARNING"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_SAFETY",
"ErrorState::NOT_AVAILABLE_POOR_VISIBILITY",
"ErrorState::NOT_AVAILABLE_SPEED_HIGH",
"ErrorState::NOT_AVAILABLE_DISABLED",
"LowSpeedCollisionWarningState::NO_WARNING",
"LowSpeedCollisionWarningState::WARNING"
]
}
]
},
{
"property": "VehicleProperty::ELECTRONIC_STABILITY_CONTROL_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::ELECTRONIC_STABILITY_CONTROL_STATE",
"defaultValue": {
"int32Values": [
"ElectronicStabilityControlState::ENABLED"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_SAFETY",
"ErrorState::NOT_AVAILABLE_SPEED_HIGH",
"ErrorState::NOT_AVAILABLE_SPEED_LOW",
"ErrorState::NOT_AVAILABLE_DISABLED",
"ElectronicStabilityControlState::ENABLED",
"ElectronicStabilityControlState::ACTIVATED"
]
}
]
},
{
"property": "VehicleProperty::CROSS_TRAFFIC_MONITORING_ENABLED",
"defaultValue": {
"int32Values": [
1
]
}
},
{
"property": "VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE",
"defaultValue": {
"int32Values": [
"CrossTrafficMonitoringWarningState::NO_WARNING"
]
},
"areas": [
{
"areaId": 0,
"supportedEnumValues": [
"ErrorState::NOT_AVAILABLE_SAFETY",
"ErrorState::NOT_AVAILABLE_POOR_VISIBILITY",
"ErrorState::NOT_AVAILABLE_SPEED_HIGH",
"ErrorState::NOT_AVAILABLE_DISABLED",
"CrossTrafficMonitoringWarningState::NO_WARNING",
"CrossTrafficMonitoringWarningState::WARNING_FRONT_LEFT",
"CrossTrafficMonitoringWarningState::WARNING_FRONT_RIGHT",
"CrossTrafficMonitoringWarningState::WARNING_FRONT_BOTH",
"CrossTrafficMonitoringWarningState::WARNING_REAR_LEFT",
"CrossTrafficMonitoringWarningState::WARNING_REAR_RIGHT",
"CrossTrafficMonitoringWarningState::WARNING_REAR_BOTH"
]
}
]
}
]
}

View file

@ -1,7 +1,7 @@
{
"properties": [
{
"property": "Constants::kMixedTypePropertyForTest",
"property": "TestVendorProperty::MIXED_TYPE_PROPERTY_FOR_TEST",
"defaultValue": {
"floatValues": [
4.5
@ -28,7 +28,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_EXTENSION_BOOLEAN_PROPERTY",
"property": "TestVendorProperty::VENDOR_EXTENSION_BOOLEAN_PROPERTY",
"areas": [
{
"defaultValue": {
@ -67,7 +67,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_EXTENSION_FLOAT_PROPERTY",
"property": "TestVendorProperty::VENDOR_EXTENSION_FLOAT_PROPERTY",
"areas": [
{
"defaultValue": {
@ -94,7 +94,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_EXTENSION_INT_PROPERTY",
"property": "TestVendorProperty::VENDOR_EXTENSION_INT_PROPERTY",
"areas": [
{
"defaultValue": {
@ -131,7 +131,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_EXTENSION_STRING_PROPERTY",
"property": "TestVendorProperty::VENDOR_EXTENSION_STRING_PROPERTY",
"defaultValue": {
"stringValue": "Vendor String Property"
},
@ -139,7 +139,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::PLACEHOLDER_PROPERTY_INT",
"property": "TestVendorProperty::PLACEHOLDER_PROPERTY_INT",
"defaultValue": {
"int32Values": [
0
@ -149,7 +149,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::PLACEHOLDER_PROPERTY_FLOAT",
"property": "TestVendorProperty::PLACEHOLDER_PROPERTY_FLOAT",
"defaultValue": {
"floatValues": [
0.0
@ -159,7 +159,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::PLACEHOLDER_PROPERTY_BOOLEAN",
"property": "TestVendorProperty::PLACEHOLDER_PROPERTY_BOOLEAN",
"defaultValue": {
"int32Values": [
0
@ -169,7 +169,7 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::PLACEHOLDER_PROPERTY_STRING",
"property": "TestVendorProperty::PLACEHOLDER_PROPERTY_STRING",
"defaultValue": {
"stringValue": "Test"
},
@ -177,12 +177,12 @@
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::ECHO_REVERSE_BYTES",
"property": "TestVendorProperty::ECHO_REVERSE_BYTES",
"access": "VehiclePropertyAccess::READ_WRITE",
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_PROPERTY_ID",
"property": "TestVendorProperty::VENDOR_PROPERTY_FOR_ERROR_CODE_TESTING",
"access": "VehiclePropertyAccess::READ_WRITE",
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
@ -194,13 +194,13 @@
]
},
"configArray": [
"Constants::kMixedTypePropertyForTest",
"TestVendorProperty::MIXED_TYPE_PROPERTY_FOR_TEST",
"VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_INFO",
"VehicleVendorPermission::PERMISSION_SET_VENDOR_CATEGORY_INFO",
"Constants::VENDOR_EXTENSION_INT_PROPERTY",
"TestVendorProperty::VENDOR_EXTENSION_INT_PROPERTY",
"VehicleVendorPermission::PERMISSION_GET_VENDOR_CATEGORY_SEAT",
"VehicleVendorPermission::PERMISSION_NOT_ACCESSIBLE",
"Constants::VENDOR_EXTENSION_FLOAT_PROPERTY",
"TestVendorProperty::VENDOR_EXTENSION_FLOAT_PROPERTY",
"VehicleVendorPermission::PERMISSION_DEFAULT",
"VehicleVendorPermission::PERMISSION_DEFAULT"
]

View file

@ -1,17 +1,17 @@
{
"properties": [
{
"property": "Constants::VENDOR_CLUSTER_SWITCH_UI",
"property": "TestVendorProperty::VENDOR_CLUSTER_SWITCH_UI",
"access": "VehiclePropertyAccess::WRITE",
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_CLUSTER_DISPLAY_STATE",
"property": "TestVendorProperty::VENDOR_CLUSTER_DISPLAY_STATE",
"access": "VehiclePropertyAccess::WRITE",
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
},
{
"property": "Constants::VENDOR_CLUSTER_REPORT_STATE",
"property": "TestVendorProperty::VENDOR_CLUSTER_REPORT_STATE",
"defaultValue": {
"int32Values": [
0,
@ -44,7 +44,7 @@
"Value means 0 /* Off */, -1, -1, -1, -1 /* Bounds */, -1, -1, -1, -1 /* Insets */, 0 /* ClusterHome */, -1 /* ClusterNone */"
},
{
"property": "Constants::VENDOR_CLUSTER_REQUEST_DISPLAY",
"property": "TestVendorProperty::VENDOR_CLUSTER_REQUEST_DISPLAY",
"defaultValue": {
"int32Values": [
0
@ -55,7 +55,7 @@
"comment": "0 means ClusterHome"
},
{
"property": "Constants::VENDOR_CLUSTER_NAVIGATION_STATE",
"property": "TestVendorProperty::VENDOR_CLUSTER_NAVIGATION_STATE",
"access": "VehiclePropertyAccess::READ",
"changeMode": "VehiclePropertyChangeMode::ON_CHANGE"
}

View file

@ -21,7 +21,10 @@ package {
cc_library {
name: "FakeVehicleHardware",
vendor: true,
srcs: ["src/*.cpp"],
srcs: [
"src/*.cpp",
":VhalTestVendorProperties",
],
local_include_dirs: ["include"],
export_include_dirs: ["include"],
cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
@ -35,7 +38,7 @@ cc_defaults {
name: "FakeVehicleHardwareDefaults",
header_libs: [
"IVehicleHardware",
"VehicleHalTestUtilHeaders",
"libbinder_headers",
],
export_header_lib_headers: ["IVehicleHardware"],
static_libs: [

View file

@ -36,6 +36,7 @@
#include <memory>
#include <mutex>
#include <unordered_map>
#include <unordered_set>
#include <vector>
namespace android {
@ -90,9 +91,13 @@ class FakeVehicleHardware : public IVehicleHardware {
void registerOnPropertySetErrorEvent(
std::unique_ptr<const PropertySetErrorCallback> callback) override;
// Update the sample rate for the [propId, areaId] pair.
aidl::android::hardware::automotive::vehicle::StatusCode updateSampleRate(
int32_t propId, int32_t areaId, float sampleRate) override;
// Subscribe to a new [propId, areaId] or change the update rate.
aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
aidl::android::hardware::automotive::vehicle::SubscribeOptions options) override;
// Unsubscribe to a [propId, areaId].
aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(int32_t propId,
int32_t areaId) override;
protected:
// mValuePool is also used in mServerSidePropStore.
@ -137,6 +142,16 @@ class FakeVehicleHardware : public IVehicleHardware {
void handleRequestsOnce();
};
struct RefreshInfo {
VehiclePropertyStore::EventMode eventMode;
int64_t intervalInNanos;
};
struct ActionForInterval {
std::unordered_set<PropIdAreaId, PropIdAreaIdHash> propIdAreaIdsToRefresh;
std::shared_ptr<RecurrentTimer::Callback> recurrentAction;
};
const std::unique_ptr<obd2frame::FakeObd2Frame> mFakeObd2Frame;
const std::unique_ptr<FakeUserHal> mFakeUserHal;
// RecurrentTimer is thread-safe.
@ -149,10 +164,12 @@ class FakeVehicleHardware : public IVehicleHardware {
std::unique_ptr<const PropertySetErrorCallback> mOnPropertySetErrorCallback;
std::mutex mLock;
std::unordered_map<PropIdAreaId, std::shared_ptr<RecurrentTimer::Callback>, PropIdAreaIdHash>
mRecurrentActions GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, RefreshInfo, PropIdAreaIdHash> mRefreshInfoByPropIdAreaId
GUARDED_BY(mLock);
std::unordered_map<int64_t, ActionForInterval> mActionByIntervalInNanos GUARDED_BY(mLock);
std::unordered_map<PropIdAreaId, VehiclePropValuePool::RecyclableType, PropIdAreaIdHash>
mSavedProps GUARDED_BY(mLock);
std::unordered_set<PropIdAreaId, PropIdAreaIdHash> mSubOnChangePropIdAreaIds GUARDED_BY(mLock);
// PendingRequestHandler is thread-safe.
mutable PendingRequestHandler<GetValuesCallback,
aidl::android::hardware::automotive::vehicle::GetValueRequest>
@ -161,6 +178,9 @@ class FakeVehicleHardware : public IVehicleHardware {
aidl::android::hardware::automotive::vehicle::SetValueRequest>
mPendingSetValueRequests;
// Set of HVAC properties dependent on HVAC_POWER_ON
std::unordered_set<int32_t> hvacPowerDependentProps;
const bool mForceOverride;
bool mAddExtraTestVendorConfigs;
@ -172,7 +192,12 @@ class FakeVehicleHardware : public IVehicleHardware {
void storePropInitialValue(const ConfigDeclaration& config);
// The callback that would be called when a vehicle property value change happens.
void onValueChangeCallback(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)
EXCLUDES(mLock);
// The callback that would be called when multiple vehicle property value changes happen.
void onValuesChangeCallback(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue> values)
EXCLUDES(mLock);
// Load the config files in format '*.json' from the directory and parse the config files
// into a map from property ID to ConfigDeclarations.
void loadPropConfigsFromDir(const std::string& dirPath,
@ -192,11 +217,14 @@ class FakeVehicleHardware : public IVehicleHardware {
VhalResult<void> maybeSetSpecialValue(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
bool* isSpecialValue);
VhalResult<bool> isCruiseControlTypeStandard() const;
ValueResultType maybeGetSpecialValue(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value,
bool* isSpecialValue) const;
VhalResult<void> setApPowerStateReport(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
VhalResult<void> setApPowerStateReqShutdown(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value);
VehiclePropValuePool::RecyclableType createApPowerStateReq(
aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq state);
VehiclePropValuePool::RecyclableType createAdasStateReq(int32_t propertyId, int32_t areaId,
@ -209,6 +237,9 @@ class FakeVehicleHardware : public IVehicleHardware {
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value) const;
bool isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const;
VhalResult<void> isAdasPropertyAvailable(int32_t adasStatePropertyId) const;
VhalResult<void> synchronizeHvacTemp(int32_t hvacDualOnAreaId,
std::optional<float> newTempC) const;
std::optional<int32_t> getSyncedAreaIdIfHvacDualOn(int32_t hvacTemperatureSetAreaId) const;
std::unordered_map<int32_t, ConfigDeclaration> loadConfigDeclarations();
@ -250,11 +281,21 @@ class FakeVehicleHardware : public IVehicleHardware {
const aidl::android::hardware::automotive::vehicle::SetValueRequest& request);
std::string genFakeDataCommand(const std::vector<std::string>& options);
void sendHvacPropertiesCurrentValues(int32_t areaId);
void sendHvacPropertiesCurrentValues(int32_t areaId, int32_t hvacPowerOnVal);
void sendAdasPropertiesState(int32_t propertyId, int32_t state);
void generateVendorConfigs(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig>&) const;
aidl::android::hardware::automotive::vehicle::StatusCode subscribePropIdAreaIdLocked(
int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
vehiclePropConfig) REQUIRES(mLock);
void registerRefreshLocked(PropIdAreaId propIdAreaId, VehiclePropertyStore::EventMode eventMode,
float sampleRateHz) REQUIRES(mLock);
void unregisterRefreshLocked(PropIdAreaId propIdAreaId) REQUIRES(mLock);
void refreshTimeStampForInterval(int64_t intervalInNanos) EXCLUDES(mLock);
static aidl::android::hardware::automotive::vehicle::VehiclePropValue createHwInputKeyProp(
aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction action,
int32_t keyCode, int32_t targetDisplay);
@ -268,6 +309,10 @@ class FakeVehicleHardware : public IVehicleHardware {
static std::string genFakeDataHelp();
static std::string parseErrMsg(std::string fieldName, std::string value, std::string type);
static bool isVariableUpdateRateSupported(
const aidl::android::hardware::automotive::vehicle::VehiclePropConfig&
vehiclePropConfig,
int32_t areaId);
};
} // namespace fake

View file

@ -24,7 +24,6 @@
#include <JsonFakeValueGenerator.h>
#include <LinearFakeValueGenerator.h>
#include <PropertyUtils.h>
#include <TestPropertyUtils.h>
#include <VehicleHalTypes.h>
#include <VehicleUtils.h>
@ -32,6 +31,7 @@
#include <android-base/parsedouble.h>
#include <android-base/properties.h>
#include <android-base/strings.h>
#include <android/hardware/automotive/vehicle/TestVendorProperty.h>
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include <utils/Trace.h>
@ -51,6 +51,12 @@ namespace fake {
namespace {
using ::aidl::android::hardware::automotive::vehicle::CruiseControlCommand;
using ::aidl::android::hardware::automotive::vehicle::CruiseControlType;
using ::aidl::android::hardware::automotive::vehicle::DriverDistractionState;
using ::aidl::android::hardware::automotive::vehicle::DriverDistractionWarning;
using ::aidl::android::hardware::automotive::vehicle::DriverDrowsinessAttentionState;
using ::aidl::android::hardware::automotive::vehicle::DriverDrowsinessAttentionWarning;
using ::aidl::android::hardware::automotive::vehicle::ErrorState;
using ::aidl::android::hardware::automotive::vehicle::GetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::GetValueResult;
@ -58,12 +64,16 @@ using ::aidl::android::hardware::automotive::vehicle::RawPropValues;
using ::aidl::android::hardware::automotive::vehicle::SetValueRequest;
using ::aidl::android::hardware::automotive::vehicle::SetValueResult;
using ::aidl::android::hardware::automotive::vehicle::StatusCode;
using ::aidl::android::hardware::automotive::vehicle::SubscribeOptions;
using ::aidl::android::hardware::automotive::vehicle::toString;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReport;
using ::aidl::android::hardware::automotive::vehicle::VehicleApPowerStateReq;
using ::aidl::android::hardware::automotive::vehicle::VehicleArea;
using ::aidl::android::hardware::automotive::vehicle::VehicleHwKeyInputAction;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropConfig;
using ::aidl::android::hardware::automotive::vehicle::VehicleProperty;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyAccess;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyChangeMode;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyGroup;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyStatus;
using ::aidl::android::hardware::automotive::vehicle::VehiclePropertyType;
@ -84,14 +94,12 @@ using ::android::base::StringPrintf;
// getPropertiesAsync, and setPropertiesAsync.
// 0x21403000
constexpr int32_t STARTING_VENDOR_CODE_PROPERTIES_FOR_TEST =
0x3000 | toInt(testpropertyutils_impl::VehiclePropertyGroup::VENDOR) |
toInt(testpropertyutils_impl::VehicleArea::GLOBAL) |
toInt(testpropertyutils_impl::VehiclePropertyType::INT32);
0x3000 | toInt(VehiclePropertyGroup::VENDOR) | toInt(VehicleArea::GLOBAL) |
toInt(VehiclePropertyType::INT32);
// 0x21405000
constexpr int32_t ENDING_VENDOR_CODE_PROPERTIES_FOR_TEST =
0x5000 | toInt(testpropertyutils_impl::VehiclePropertyGroup::VENDOR) |
toInt(testpropertyutils_impl::VehicleArea::GLOBAL) |
toInt(testpropertyutils_impl::VehiclePropertyType::INT32);
0x5000 | toInt(VehiclePropertyGroup::VENDOR) | toInt(VehicleArea::GLOBAL) |
toInt(VehiclePropertyType::INT32);
// The directory for default property configuration file.
// For config file format, see impl/default_config/config/README.md.
constexpr char DEFAULT_CONFIG_DIR[] = "/vendor/etc/automotive/vhalconfig/";
@ -102,7 +110,7 @@ constexpr char OVERRIDE_CONFIG_DIR[] = "/vendor/etc/automotive/vhaloverride/";
// overwrite the default configs.
constexpr char OVERRIDE_PROPERTY[] = "persist.vendor.vhal_init_value_override";
constexpr char POWER_STATE_REQ_CONFIG_PROPERTY[] = "ro.vendor.fake_vhal.ap_power_state_req.config";
// The value to be returned if VENDOR_PROPERTY_ID is set as the property
// The value to be returned if VENDOR_PROPERTY_FOR_ERROR_CODE_TESTING is set as the property
constexpr int VENDOR_ERROR_CODE = 0x00ab0005;
// A list of supported options for "--set" command.
const std::unordered_set<std::string> SET_PROP_OPTIONS = {
@ -188,6 +196,56 @@ const std::unordered_map<int32_t, std::vector<int32_t>> mAdasEnabledPropToAdasPr
toInt(VehicleProperty::HANDS_ON_DETECTION_WARNING),
},
},
// Driver Drowsiness and Attention
{
toInt(VehicleProperty::DRIVER_DROWSINESS_ATTENTION_SYSTEM_ENABLED),
{
toInt(VehicleProperty::DRIVER_DROWSINESS_ATTENTION_STATE),
},
},
// Driver Drowsiness and Attention Warning
{
toInt(VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING_ENABLED),
{
toInt(VehicleProperty::DRIVER_DROWSINESS_ATTENTION_WARNING),
},
},
// Driver Distraction
{
toInt(VehicleProperty::DRIVER_DISTRACTION_SYSTEM_ENABLED),
{
toInt(VehicleProperty::DRIVER_DISTRACTION_STATE),
toInt(VehicleProperty::DRIVER_DISTRACTION_WARNING),
},
},
// Driver Distraction Warning
{
toInt(VehicleProperty::DRIVER_DISTRACTION_WARNING_ENABLED),
{
toInt(VehicleProperty::DRIVER_DISTRACTION_WARNING),
},
},
// LSCW
{
toInt(VehicleProperty::LOW_SPEED_COLLISION_WARNING_ENABLED),
{
toInt(VehicleProperty::LOW_SPEED_COLLISION_WARNING_STATE),
},
},
// ESC
{
toInt(VehicleProperty::ELECTRONIC_STABILITY_CONTROL_ENABLED),
{
toInt(VehicleProperty::ELECTRONIC_STABILITY_CONTROL_STATE),
},
},
// CTMW
{
toInt(VehicleProperty::CROSS_TRAFFIC_MONITORING_ENABLED),
{
toInt(VehicleProperty::CROSS_TRAFFIC_MONITORING_WARNING_STATE),
},
},
};
} // namespace
@ -199,6 +257,11 @@ void FakeVehicleHardware::storePropInitialValue(const ConfigDeclaration& config)
bool globalProp = isGlobalProp(propId);
size_t numAreas = globalProp ? 1 : vehiclePropConfig.areaConfigs.size();
if (propId == toInt(VehicleProperty::HVAC_POWER_ON)) {
const auto& configArray = vehiclePropConfig.configArray;
hvacPowerDependentProps.insert(configArray.begin(), configArray.end());
}
for (size_t i = 0; i < numAreas; i++) {
int32_t curArea = globalProp ? 0 : vehiclePropConfig.areaConfigs[i].areaId;
@ -295,17 +358,18 @@ void FakeVehicleHardware::init() {
}
// OBD2_LIVE_FRAME and OBD2_FREEZE_FRAME must be configured in default configs.
auto maybeObd2LiveFrame = mServerSidePropStore->getConfig(OBD2_LIVE_FRAME);
auto maybeObd2LiveFrame = mServerSidePropStore->getPropConfig(OBD2_LIVE_FRAME);
if (maybeObd2LiveFrame.has_value()) {
mFakeObd2Frame->initObd2LiveFrame(*maybeObd2LiveFrame.value());
mFakeObd2Frame->initObd2LiveFrame(maybeObd2LiveFrame.value());
}
auto maybeObd2FreezeFrame = mServerSidePropStore->getConfig(OBD2_FREEZE_FRAME);
auto maybeObd2FreezeFrame = mServerSidePropStore->getPropConfig(OBD2_FREEZE_FRAME);
if (maybeObd2FreezeFrame.has_value()) {
mFakeObd2Frame->initObd2FreezeFrame(*maybeObd2FreezeFrame.value());
mFakeObd2Frame->initObd2FreezeFrame(maybeObd2FreezeFrame.value());
}
mServerSidePropStore->setOnValueChangeCallback(
[this](const VehiclePropValue& value) { return onValueChangeCallback(value); });
mServerSidePropStore->setOnValuesChangeCallback([this](std::vector<VehiclePropValue> values) {
return onValuesChangeCallback(std::move(values));
});
}
std::vector<VehiclePropConfig> FakeVehicleHardware::getAllPropertyConfigs() const {
@ -341,6 +405,25 @@ VehiclePropValuePool::RecyclableType FakeVehicleHardware::createAdasStateReq(int
return req;
}
VhalResult<void> FakeVehicleHardware::setApPowerStateReqShutdown(const VehiclePropValue& value) {
if (value.value.int32Values.size() != 1) {
return StatusError(StatusCode::INVALID_ARG)
<< "Failed to set SHUTDOWN_REQUEST, expect 1 int value: "
<< "VehicleApPowerStateShutdownParam";
}
int powerStateShutdownParam = value.value.int32Values[0];
auto prop = createApPowerStateReq(VehicleApPowerStateReq::SHUTDOWN_PREPARE);
prop->value.int32Values[1] = powerStateShutdownParam;
if (auto writeResult = mServerSidePropStore->writeValue(
std::move(prop), /*updateStatus=*/true, VehiclePropertyStore::EventMode::ALWAYS);
!writeResult.ok()) {
return StatusError(getErrorCode(writeResult))
<< "failed to write AP_POWER_STATE_REQ into property store, error: "
<< getErrorMsg(writeResult);
}
return {};
}
VhalResult<void> FakeVehicleHardware::setApPowerStateReport(const VehiclePropValue& value) {
auto updatedValue = mValuePool->obtain(value);
updatedValue->timestamp = elapsedRealtimeNano();
@ -406,7 +489,7 @@ int FakeVehicleHardware::getHvacTempNumIncrements(int requestedTemp, int minTemp
int increment) {
requestedTemp = std::max(requestedTemp, minTemp);
requestedTemp = std::min(requestedTemp, maxTemp);
int numIncrements = (requestedTemp - minTemp) / increment;
int numIncrements = std::round((requestedTemp - minTemp) / static_cast<float>(increment));
return numIncrements;
}
@ -444,7 +527,7 @@ void FakeVehicleHardware::updateHvacTemperatureValueSuggestionInput(
VhalResult<void> FakeVehicleHardware::setHvacTemperatureValueSuggestion(
const VehiclePropValue& hvacTemperatureValueSuggestion) {
auto hvacTemperatureSetConfigResult =
mServerSidePropStore->getConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
mServerSidePropStore->getPropConfig(toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
if (!hvacTemperatureSetConfigResult.ok()) {
return StatusError(getErrorCode(hvacTemperatureSetConfigResult)) << StringPrintf(
@ -471,7 +554,7 @@ VhalResult<void> FakeVehicleHardware::setHvacTemperatureValueSuggestion(
}
auto updatedValue = mValuePool->obtain(hvacTemperatureValueSuggestion);
const auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfigResult.value()->configArray;
const auto& hvacTemperatureSetConfigArray = hvacTemperatureSetConfigResult.value().configArray;
auto& hvacTemperatureValueSuggestionInput = updatedValue->value.floatValues;
updateHvacTemperatureValueSuggestionInput(hvacTemperatureSetConfigArray,
@ -491,9 +574,7 @@ VhalResult<void> FakeVehicleHardware::setHvacTemperatureValueSuggestion(
}
bool FakeVehicleHardware::isHvacPropAndHvacNotAvailable(int32_t propId, int32_t areaId) const {
std::unordered_set<int32_t> powerProps(std::begin(HVAC_POWER_PROPERTIES),
std::end(HVAC_POWER_PROPERTIES));
if (powerProps.count(propId)) {
if (hvacPowerDependentProps.count(propId)) {
auto hvacPowerOnResults =
mServerSidePropStore->readValuesForProperty(toInt(VehicleProperty::HVAC_POWER_ON));
if (!hvacPowerOnResults.ok()) {
@ -575,6 +656,65 @@ VhalResult<void> FakeVehicleHardware::setUserHalProp(const VehiclePropValue& val
return {};
}
VhalResult<void> FakeVehicleHardware::synchronizeHvacTemp(int32_t hvacDualOnAreaId,
std::optional<float> newTempC) const {
auto hvacTemperatureSetResults = mServerSidePropStore->readValuesForProperty(
toInt(VehicleProperty::HVAC_TEMPERATURE_SET));
if (!hvacTemperatureSetResults.ok()) {
return StatusError(StatusCode::NOT_AVAILABLE)
<< "Failed to get HVAC_TEMPERATURE_SET, error: "
<< getErrorMsg(hvacTemperatureSetResults);
}
auto& hvacTemperatureSetValues = hvacTemperatureSetResults.value();
std::optional<float> tempCToSynchronize = newTempC;
for (size_t i = 0; i < hvacTemperatureSetValues.size(); i++) {
int32_t areaId = hvacTemperatureSetValues[i]->areaId;
if ((hvacDualOnAreaId & areaId) != areaId) {
continue;
}
if (hvacTemperatureSetValues[i]->status != VehiclePropertyStatus::AVAILABLE) {
continue;
}
// When HVAC_DUAL_ON is initially enabled, synchronize all area IDs
// to the temperature of the first area ID, which is the driver's.
if (!tempCToSynchronize.has_value()) {
tempCToSynchronize = hvacTemperatureSetValues[i]->value.floatValues[0];
continue;
}
auto updatedValue = std::move(hvacTemperatureSetValues[i]);
updatedValue->value.floatValues[0] = tempCToSynchronize.value();
updatedValue->timestamp = elapsedRealtimeNano();
// This will trigger a property change event for the current hvac property value.
auto writeResult =
mServerSidePropStore->writeValue(std::move(updatedValue), /*updateStatus=*/true,
VehiclePropertyStore::EventMode::ALWAYS);
if (!writeResult.ok()) {
return StatusError(getErrorCode(writeResult))
<< "Failed to write value into property store, error: "
<< getErrorMsg(writeResult);
}
}
return {};
}
std::optional<int32_t> FakeVehicleHardware::getSyncedAreaIdIfHvacDualOn(
int32_t hvacTemperatureSetAreaId) const {
auto hvacDualOnResults =
mServerSidePropStore->readValuesForProperty(toInt(VehicleProperty::HVAC_DUAL_ON));
if (!hvacDualOnResults.ok()) {
return std::nullopt;
}
auto& hvacDualOnValues = hvacDualOnResults.value();
for (size_t i = 0; i < hvacDualOnValues.size(); i++) {
if ((hvacDualOnValues[i]->areaId & hvacTemperatureSetAreaId) == hvacTemperatureSetAreaId &&
hvacDualOnValues[i]->value.int32Values.size() == 1 &&
hvacDualOnValues[i]->value.int32Values[0] == 1) {
return hvacDualOnValues[i]->areaId;
}
}
return std::nullopt;
}
FakeVehicleHardware::ValueResultType FakeVehicleHardware::getUserHalProp(
const VehiclePropValue& value) const {
auto propId = value.prop;
@ -596,6 +736,18 @@ FakeVehicleHardware::ValueResultType FakeVehicleHardware::getUserHalProp(
}
}
VhalResult<bool> FakeVehicleHardware::isCruiseControlTypeStandard() const {
auto isCruiseControlTypeAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::CRUISE_CONTROL_TYPE));
if (!isCruiseControlTypeAvailableResult.ok()) {
return isCruiseControlTypeAvailableResult.error();
}
auto cruiseControlTypeValue =
mServerSidePropStore->readValue(toInt(VehicleProperty::CRUISE_CONTROL_TYPE));
return cruiseControlTypeValue.value()->value.int32Values[0] ==
toInt(CruiseControlType::STANDARD);
}
FakeVehicleHardware::ValueResultType FakeVehicleHardware::maybeGetSpecialValue(
const VehiclePropValue& value, bool* isSpecialValue) const {
*isSpecialValue = false;
@ -623,6 +775,7 @@ FakeVehicleHardware::ValueResultType FakeVehicleHardware::maybeGetSpecialValue(
return StatusError(StatusCode::NOT_AVAILABLE_DISABLED) << "hvac not available";
}
VhalResult<void> isAdasPropertyAvailableResult;
switch (propId) {
case OBD2_FREEZE_FRAME:
*isSpecialValue = true;
@ -638,24 +791,41 @@ FakeVehicleHardware::ValueResultType FakeVehicleHardware::maybeGetSpecialValue(
result.value()->timestamp = elapsedRealtimeNano();
}
return result;
case ECHO_REVERSE_BYTES:
case toInt(TestVendorProperty::ECHO_REVERSE_BYTES):
*isSpecialValue = true;
return getEchoReverseBytes(value);
case VENDOR_PROPERTY_ID:
case toInt(TestVendorProperty::VENDOR_PROPERTY_FOR_ERROR_CODE_TESTING):
*isSpecialValue = true;
return StatusError((StatusCode)VENDOR_ERROR_CODE);
case toInt(VehicleProperty::CRUISE_CONTROL_TARGET_SPEED):
[[fallthrough]];
case toInt(VehicleProperty::ADAPTIVE_CRUISE_CONTROL_TARGET_TIME_GAP):
[[fallthrough]];
case toInt(VehicleProperty::ADAPTIVE_CRUISE_CONTROL_LEAD_VEHICLE_MEASURED_DISTANCE): {
auto isAdasPropertyAvailableResult =
isAdasPropertyAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::CRUISE_CONTROL_STATE));
if (!isAdasPropertyAvailableResult.ok()) {
*isSpecialValue = true;
return isAdasPropertyAvailableResult.error();
}
return nullptr;
case toInt(VehicleProperty::ADAPTIVE_CRUISE_CONTROL_TARGET_TIME_GAP):
[[fallthrough]];
case toInt(VehicleProperty::ADAPTIVE_CRUISE_CONTROL_LEAD_VEHICLE_MEASURED_DISTANCE): {
isAdasPropertyAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::CRUISE_CONTROL_STATE));
if (!isAdasPropertyAvailableResult.ok()) {
*isSpecialValue = true;
return isAdasPropertyAvailableResult.error();
}
auto isCruiseControlTypeStandardResult = isCruiseControlTypeStandard();
if (!isCruiseControlTypeStandardResult.ok()) {
*isSpecialValue = true;
return isCruiseControlTypeStandardResult.error();
}
if (isCruiseControlTypeStandardResult.value()) {
*isSpecialValue = true;
return StatusError(StatusCode::NOT_AVAILABLE_DISABLED)
<< "tried to get target time gap or lead vehicle measured distance value "
<< "while on a standard CC setting";
}
return nullptr;
}
default:
// Do nothing.
@ -681,9 +851,8 @@ FakeVehicleHardware::ValueResultType FakeVehicleHardware::getEchoReverseBytes(
return std::move(gotValue);
}
void FakeVehicleHardware::sendHvacPropertiesCurrentValues(int32_t areaId) {
for (size_t i = 0; i < sizeof(HVAC_POWER_PROPERTIES) / sizeof(int32_t); i++) {
int powerPropId = HVAC_POWER_PROPERTIES[i];
void FakeVehicleHardware::sendHvacPropertiesCurrentValues(int32_t areaId, int32_t hvacPowerOnVal) {
for (auto& powerPropId : hvacPowerDependentProps) {
auto powerPropResults = mServerSidePropStore->readValuesForProperty(powerPropId);
if (!powerPropResults.ok()) {
ALOGW("failed to get power prop 0x%x, error: %s", powerPropId,
@ -694,7 +863,8 @@ void FakeVehicleHardware::sendHvacPropertiesCurrentValues(int32_t areaId) {
for (size_t j = 0; j < powerPropValues.size(); j++) {
auto powerPropValue = std::move(powerPropValues[j]);
if ((powerPropValue->areaId & areaId) == powerPropValue->areaId) {
powerPropValue->status = VehiclePropertyStatus::AVAILABLE;
powerPropValue->status = hvacPowerOnVal ? VehiclePropertyStatus::AVAILABLE
: VehiclePropertyStatus::UNAVAILABLE;
powerPropValue->timestamp = elapsedRealtimeNano();
// This will trigger a property change event for the current hvac property value.
mServerSidePropStore->writeValue(std::move(powerPropValue), /*updateStatus=*/true,
@ -707,15 +877,21 @@ void FakeVehicleHardware::sendHvacPropertiesCurrentValues(int32_t areaId) {
void FakeVehicleHardware::sendAdasPropertiesState(int32_t propertyId, int32_t state) {
auto& adasDependentPropIds = mAdasEnabledPropToAdasPropWithErrorState.find(propertyId)->second;
for (auto dependentPropId : adasDependentPropIds) {
auto dependentPropConfigResult = mServerSidePropStore->getConfig(dependentPropId);
auto dependentPropConfigResult = mServerSidePropStore->getPropConfig(dependentPropId);
if (!dependentPropConfigResult.ok()) {
ALOGW("Failed to get config for ADAS property 0x%x, error: %s", dependentPropId,
getErrorMsg(dependentPropConfigResult).c_str());
continue;
}
auto& dependentPropConfig = dependentPropConfigResult.value();
for (auto& areaConfig : dependentPropConfig->areaConfigs) {
auto propValue = createAdasStateReq(dependentPropId, areaConfig.areaId, state);
for (auto& areaConfig : dependentPropConfig.areaConfigs) {
int32_t hardcoded_state = state;
// TODO: restore old/initial values here instead of hardcoded value (b/295542701)
if (state == 1 && dependentPropId == toInt(VehicleProperty::CRUISE_CONTROL_TYPE)) {
hardcoded_state = toInt(CruiseControlType::ADAPTIVE);
}
auto propValue =
createAdasStateReq(dependentPropId, areaConfig.areaId, hardcoded_state);
// This will trigger a property change event for the current ADAS property value.
mServerSidePropStore->writeValue(std::move(propValue), /*updateStatus=*/true,
VehiclePropertyStore::EventMode::ALWAYS);
@ -740,13 +916,6 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
return setUserHalProp(value);
}
if (propId == toInt(VehicleProperty::HVAC_POWER_ON) && value.value.int32Values.size() == 1 &&
value.value.int32Values[0] == 1) {
// If we are turning HVAC power on, send current hvac property values through on change
// event.
sendHvacPropertiesCurrentValues(value.areaId);
}
if (isHvacPropAndHvacNotAvailable(propId, value.areaId)) {
*isSpecialValue = true;
return StatusError(StatusCode::NOT_AVAILABLE_DISABLED) << "hvac not available";
@ -762,10 +931,18 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
}
}
VhalResult<void> isAdasPropertyAvailableResult;
VhalResult<bool> isCruiseControlTypeStandardResult;
switch (propId) {
case toInt(VehicleProperty::AP_POWER_STATE_REPORT):
*isSpecialValue = true;
return setApPowerStateReport(value);
case toInt(VehicleProperty::SHUTDOWN_REQUEST):
// If we receive SHUTDOWN_REQUEST, we should send this to an external component which
// should shutdown Android system via sending an AP_POWER_STATE_REQ event. Here we have
// no external components to notify, so we just send the event.
*isSpecialValue = true;
return setApPowerStateReqShutdown(value);
case toInt(VehicleProperty::VEHICLE_MAP_SERVICE):
// Placeholder for future implementation of VMS property in the default hal. For
// now, just returns OK; otherwise, hal clients crash with property not supported.
@ -774,14 +951,46 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
case OBD2_FREEZE_FRAME_CLEAR:
*isSpecialValue = true;
return mFakeObd2Frame->clearObd2FreezeFrames(value);
case VENDOR_PROPERTY_ID:
case toInt(TestVendorProperty::VENDOR_PROPERTY_FOR_ERROR_CODE_TESTING):
*isSpecialValue = true;
return StatusError((StatusCode)VENDOR_ERROR_CODE);
case toInt(VehicleProperty::HVAC_POWER_ON):
if (value.value.int32Values.size() != 1) {
*isSpecialValue = true;
return StatusError(StatusCode::INVALID_ARG)
<< "HVAC_POWER_ON requires only one int32 value";
}
// When changing HVAC power state, send current hvac property values
// through on change event.
sendHvacPropertiesCurrentValues(value.areaId, value.value.int32Values[0]);
return {};
case toInt(VehicleProperty::HVAC_TEMPERATURE_VALUE_SUGGESTION):
*isSpecialValue = true;
return setHvacTemperatureValueSuggestion(value);
case toInt(VehicleProperty::HVAC_TEMPERATURE_SET):
if (value.value.floatValues.size() != 1) {
*isSpecialValue = true;
return StatusError(StatusCode::INVALID_ARG)
<< "HVAC_DUAL_ON requires only one float value";
}
if (auto hvacDualOnAreaId = getSyncedAreaIdIfHvacDualOn(value.areaId);
hvacDualOnAreaId.has_value()) {
*isSpecialValue = true;
return synchronizeHvacTemp(hvacDualOnAreaId.value(), value.value.floatValues[0]);
}
return {};
case toInt(VehicleProperty::HVAC_DUAL_ON):
if (value.value.int32Values.size() != 1) {
*isSpecialValue = true;
return StatusError(StatusCode::INVALID_ARG)
<< "HVAC_DUAL_ON requires only one int32 value";
}
if (value.value.int32Values[0] == 1) {
synchronizeHvacTemp(value.areaId, std::nullopt);
}
return {};
case toInt(VehicleProperty::LANE_CENTERING_ASSIST_COMMAND): {
auto isAdasPropertyAvailableResult =
isAdasPropertyAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::LANE_CENTERING_ASSIST_STATE));
if (!isAdasPropertyAvailableResult.ok()) {
*isSpecialValue = true;
@ -789,14 +998,47 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
return isAdasPropertyAvailableResult;
}
case toInt(VehicleProperty::CRUISE_CONTROL_COMMAND):
[[fallthrough]];
case toInt(VehicleProperty::ADAPTIVE_CRUISE_CONTROL_TARGET_TIME_GAP): {
auto isAdasPropertyAvailableResult =
isAdasPropertyAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::CRUISE_CONTROL_STATE));
if (!isAdasPropertyAvailableResult.ok()) {
*isSpecialValue = true;
return isAdasPropertyAvailableResult;
}
return isAdasPropertyAvailableResult;
isCruiseControlTypeStandardResult = isCruiseControlTypeStandard();
if (!isCruiseControlTypeStandardResult.ok()) {
*isSpecialValue = true;
return isCruiseControlTypeStandardResult.error();
}
if (isCruiseControlTypeStandardResult.value() &&
(value.value.int32Values[0] ==
toInt(CruiseControlCommand::INCREASE_TARGET_TIME_GAP) ||
value.value.int32Values[0] ==
toInt(CruiseControlCommand::DECREASE_TARGET_TIME_GAP))) {
*isSpecialValue = true;
return StatusError(StatusCode::NOT_AVAILABLE_DISABLED)
<< "tried to use a change target time gap command while on a standard CC "
<< "setting";
}
return {};
case toInt(VehicleProperty::ADAPTIVE_CRUISE_CONTROL_TARGET_TIME_GAP): {
isAdasPropertyAvailableResult =
isAdasPropertyAvailable(toInt(VehicleProperty::CRUISE_CONTROL_STATE));
if (!isAdasPropertyAvailableResult.ok()) {
*isSpecialValue = true;
return isAdasPropertyAvailableResult;
}
isCruiseControlTypeStandardResult = isCruiseControlTypeStandard();
if (!isCruiseControlTypeStandardResult.ok()) {
*isSpecialValue = true;
return isCruiseControlTypeStandardResult.error();
}
if (isCruiseControlTypeStandardResult.value()) {
*isSpecialValue = true;
return StatusError(StatusCode::NOT_AVAILABLE_DISABLED)
<< "tried to set target time gap or lead vehicle measured distance value "
<< "while on a standard CC setting";
}
return {};
}
#ifdef ENABLE_VEHICLE_HAL_TEST_PROPERTIES
@ -806,9 +1048,9 @@ VhalResult<void> FakeVehicleHardware::maybeSetSpecialValue(const VehiclePropValu
[[fallthrough]];
case toInt(VehicleProperty::CLUSTER_NAVIGATION_STATE):
[[fallthrough]];
case VENDOR_CLUSTER_SWITCH_UI:
case toInt(TestVendorProperty::VENDOR_CLUSTER_SWITCH_UI):
[[fallthrough]];
case VENDOR_CLUSTER_DISPLAY_STATE:
case toInt(TestVendorProperty::VENDOR_CLUSTER_DISPLAY_STATE):
*isSpecialValue = true;
updatedValue = mValuePool->obtain(getPropType(value.prop));
updatedValue->prop = value.prop & ~toInt(VehiclePropertyGroup::MASK);
@ -868,10 +1110,11 @@ VhalResult<void> FakeVehicleHardware::setValue(const VehiclePropValue& value) {
}
auto updatedValue = mValuePool->obtain(value);
int64_t timestamp = elapsedRealtimeNano();
updatedValue->timestamp = timestamp;
auto writeResult = mServerSidePropStore->writeValue(std::move(updatedValue));
auto writeResult = mServerSidePropStore->writeValue(
std::move(updatedValue),
/*updateStatus=*/false, /*mode=*/VehiclePropertyStore::EventMode::ON_VALUE_CHANGE,
/*useCurrentTimestamp=*/true);
if (!writeResult.ok()) {
return StatusError(getErrorCode(writeResult))
<< StringPrintf("failed to write value into property store, error: %s",
@ -1514,12 +1757,12 @@ std::string FakeVehicleHardware::dumpSpecificProperty(const std::vector<std::str
continue;
}
int32_t prop = propResult.value();
auto result = mServerSidePropStore->getConfig(prop);
auto result = mServerSidePropStore->getPropConfig(prop);
if (!result.ok()) {
msg += StringPrintf("No property %d\n", prop);
continue;
}
msg += dumpOnePropertyByConfig(rowNumber++, *result.value());
msg += dumpOnePropertyByConfig(rowNumber++, result.value());
}
return msg;
}
@ -1823,54 +2066,196 @@ void FakeVehicleHardware::registerOnPropertySetErrorEvent(
mOnPropertySetErrorCallback = std::move(callback);
}
StatusCode FakeVehicleHardware::updateSampleRate(int32_t propId, int32_t areaId, float sampleRate) {
// DefaultVehicleHal makes sure that sampleRate must be within minSampleRate and maxSampleRate.
// For fake implementation, we would write the same value with a new timestamp into propStore
// at sample rate.
std::scoped_lock<std::mutex> lockGuard(mLock);
StatusCode FakeVehicleHardware::subscribe(SubscribeOptions options) {
int32_t propId = options.propId;
auto configResult = mServerSidePropStore->getPropConfig(propId);
if (!configResult.ok()) {
ALOGE("subscribe: property: %" PRId32 " is not supported", propId);
return StatusCode::INVALID_ARG;
}
std::scoped_lock<std::mutex> lockGuard(mLock);
for (int areaId : options.areaIds) {
if (StatusCode status = subscribePropIdAreaIdLocked(propId, areaId, options.sampleRate,
options.enableVariableUpdateRate,
configResult.value());
status != StatusCode::OK) {
return status;
}
}
return StatusCode::OK;
}
bool FakeVehicleHardware::isVariableUpdateRateSupported(const VehiclePropConfig& vehiclePropConfig,
int32_t areaId) {
for (size_t i = 0; i < vehiclePropConfig.areaConfigs.size(); i++) {
const auto& areaConfig = vehiclePropConfig.areaConfigs[i];
if (areaConfig.areaId != areaId) {
continue;
}
if (areaConfig.supportVariableUpdateRate) {
return true;
}
break;
}
return false;
}
void FakeVehicleHardware::refreshTimeStampForInterval(int64_t intervalInNanos) {
std::unordered_map<PropIdAreaId, VehiclePropertyStore::EventMode, PropIdAreaIdHash>
eventModeByPropIdAreaId;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
if (mActionByIntervalInNanos.find(intervalInNanos) == mActionByIntervalInNanos.end()) {
ALOGE("No actions scheduled for the interval: %" PRId64 ", ignore the refresh request",
intervalInNanos);
return;
}
ActionForInterval actionForInterval = mActionByIntervalInNanos[intervalInNanos];
// Make a copy so that we don't hold the lock while trying to refresh the timestamp.
// Refreshing the timestamp will inovke onValueChangeCallback which also requires lock, so
// we must not hold lock.
for (const PropIdAreaId& propIdAreaId : actionForInterval.propIdAreaIdsToRefresh) {
const RefreshInfo& refreshInfo = mRefreshInfoByPropIdAreaId[propIdAreaId];
eventModeByPropIdAreaId[propIdAreaId] = refreshInfo.eventMode;
}
}
mServerSidePropStore->refreshTimestamps(eventModeByPropIdAreaId);
}
void FakeVehicleHardware::registerRefreshLocked(PropIdAreaId propIdAreaId,
VehiclePropertyStore::EventMode eventMode,
float sampleRateHz) {
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) != mRefreshInfoByPropIdAreaId.end()) {
unregisterRefreshLocked(propIdAreaId);
}
int64_t intervalInNanos = static_cast<int64_t>(1'000'000'000. / sampleRateHz);
RefreshInfo refreshInfo = {
.eventMode = eventMode,
.intervalInNanos = intervalInNanos,
};
mRefreshInfoByPropIdAreaId[propIdAreaId] = refreshInfo;
if (mActionByIntervalInNanos.find(intervalInNanos) != mActionByIntervalInNanos.end()) {
// If we have already registered for this interval, then add the action info to the
// actions list.
mActionByIntervalInNanos[intervalInNanos].propIdAreaIdsToRefresh.insert(propIdAreaId);
return;
}
// This is the first action for the interval, register a timer callback for that interval.
auto action = std::make_shared<RecurrentTimer::Callback>(
[this, intervalInNanos] { refreshTimeStampForInterval(intervalInNanos); });
mActionByIntervalInNanos[intervalInNanos] = ActionForInterval{
.propIdAreaIdsToRefresh = {propIdAreaId},
.recurrentAction = action,
};
mRecurrentTimer->registerTimerCallback(intervalInNanos, action);
}
void FakeVehicleHardware::unregisterRefreshLocked(PropIdAreaId propIdAreaId) {
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) == mRefreshInfoByPropIdAreaId.end()) {
ALOGW("PropId: %" PRId32 ", areaId: %" PRId32 " was not registered for refresh, ignore",
propIdAreaId.propId, propIdAreaId.areaId);
return;
}
int64_t intervalInNanos = mRefreshInfoByPropIdAreaId[propIdAreaId].intervalInNanos;
auto& actionForInterval = mActionByIntervalInNanos[intervalInNanos];
actionForInterval.propIdAreaIdsToRefresh.erase(propIdAreaId);
if (actionForInterval.propIdAreaIdsToRefresh.empty()) {
mRecurrentTimer->unregisterTimerCallback(actionForInterval.recurrentAction);
mActionByIntervalInNanos.erase(intervalInNanos);
}
mRefreshInfoByPropIdAreaId.erase(propIdAreaId);
}
StatusCode FakeVehicleHardware::subscribePropIdAreaIdLocked(
int32_t propId, int32_t areaId, float sampleRateHz, bool enableVariableUpdateRate,
const VehiclePropConfig& vehiclePropConfig) {
PropIdAreaId propIdAreaId{
.propId = propId,
.areaId = areaId,
};
if (mRecurrentActions.find(propIdAreaId) != mRecurrentActions.end()) {
mRecurrentTimer->unregisterTimerCallback(mRecurrentActions[propIdAreaId]);
switch (vehiclePropConfig.changeMode) {
case VehiclePropertyChangeMode::STATIC:
ALOGW("subscribe to a static property, do nothing.");
return StatusCode::OK;
case VehiclePropertyChangeMode::ON_CHANGE:
mSubOnChangePropIdAreaIds.insert(std::move(propIdAreaId));
return StatusCode::OK;
case VehiclePropertyChangeMode::CONTINUOUS:
if (sampleRateHz == 0.f) {
ALOGE("Must not use sample rate 0 for a continuous property");
return StatusCode::INTERNAL_ERROR;
}
// For continuous properties, we must generate a new onPropertyChange event
// periodically according to the sample rate.
auto eventMode = VehiclePropertyStore::EventMode::ALWAYS;
if (isVariableUpdateRateSupported(vehiclePropConfig, areaId) &&
enableVariableUpdateRate) {
eventMode = VehiclePropertyStore::EventMode::ON_VALUE_CHANGE;
}
registerRefreshLocked(propIdAreaId, eventMode, sampleRateHz);
return StatusCode::OK;
}
if (sampleRate == 0) {
return StatusCode::OK;
}
StatusCode FakeVehicleHardware::unsubscribe(int32_t propId, int32_t areaId) {
std::scoped_lock<std::mutex> lockGuard(mLock);
PropIdAreaId propIdAreaId{
.propId = propId,
.areaId = areaId,
};
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) != mRefreshInfoByPropIdAreaId.end()) {
unregisterRefreshLocked(propIdAreaId);
}
int64_t interval = static_cast<int64_t>(1'000'000'000. / sampleRate);
auto action = std::make_shared<RecurrentTimer::Callback>([this, propId, areaId] {
// Refresh the property value. In real implementation, this should poll the latest value
// from vehicle bus. Here, we are just refreshing the existing value with a new timestamp.
auto result = getValue(VehiclePropValue{
.areaId = areaId,
.prop = propId,
.value = {},
});
if (!result.ok()) {
// Failed to read current value, skip refreshing.
return;
}
result.value()->timestamp = elapsedRealtimeNano();
// For continuous properties, we must generate a new onPropertyChange event periodically
// according to the sample rate.
mServerSidePropStore->writeValue(std::move(result.value()), /*updateStatus=*/true,
VehiclePropertyStore::EventMode::ALWAYS);
});
mRecurrentTimer->registerTimerCallback(interval, action);
mRecurrentActions[propIdAreaId] = action;
mSubOnChangePropIdAreaIds.erase(propIdAreaId);
return StatusCode::OK;
}
void FakeVehicleHardware::onValueChangeCallback(const VehiclePropValue& value) {
if (mOnPropertyChangeCallback == nullptr) {
return;
ATRACE_CALL();
onValuesChangeCallback({value});
}
void FakeVehicleHardware::onValuesChangeCallback(std::vector<VehiclePropValue> values) {
ATRACE_CALL();
std::vector<VehiclePropValue> subscribedUpdatedValues;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
if (mOnPropertyChangeCallback == nullptr) {
return;
}
for (const auto& value : values) {
PropIdAreaId propIdAreaId{
.propId = value.prop,
.areaId = value.areaId,
};
if (mRefreshInfoByPropIdAreaId.find(propIdAreaId) == mRefreshInfoByPropIdAreaId.end() &&
mSubOnChangePropIdAreaIds.find(propIdAreaId) == mSubOnChangePropIdAreaIds.end()) {
if (FAKE_VEHICLEHARDWARE_DEBUG) {
ALOGD("The updated property value: %s is not subscribed, ignore",
value.toString().c_str());
}
continue;
}
subscribedUpdatedValues.push_back(value);
}
}
std::vector<VehiclePropValue> updatedValues;
updatedValues.push_back(value);
(*mOnPropertyChangeCallback)(std::move(updatedValues));
(*mOnPropertyChangeCallback)(std::move(subscribedUpdatedValues));
}
void FakeVehicleHardware::loadPropConfigsFromDir(

View file

@ -21,11 +21,14 @@ package {
cc_test {
name: "FakeVehicleHardwareTest",
vendor: true,
srcs: ["*.cpp"],
srcs: [
"*.cpp",
":VhalTestVendorProperties",
],
cflags: ["-DENABLE_VEHICLE_HAL_TEST_PROPERTIES"],
header_libs: [
"IVehicleHardware",
"VehicleHalTestUtilHeaders",
"libbinder_headers",
],
static_libs: [
"VehicleHalJsonConfigLoaderEnableTestProperties",
@ -47,7 +50,9 @@ cc_test {
":FakeVehicleHardwareTestOverrideJson",
":FakeVehicleHardwareTestPropJson",
],
defaults: ["VehicleHalDefaults"],
defaults: [
"VehicleHalDefaults",
],
test_suites: ["device-tests"],
}

View file

@ -67,12 +67,19 @@ void aidlToProto(const aidl_vehicle::VehiclePropConfig& in, proto::VehiclePropCo
for (auto& areaConfig : in.areaConfigs) {
auto* protoACfg = out->add_area_configs();
protoACfg->set_area_id(areaConfig.areaId);
protoACfg->set_access(static_cast<proto::VehiclePropertyAccess>(toInt(areaConfig.access)));
protoACfg->set_min_int64_value(areaConfig.minInt64Value);
protoACfg->set_max_int64_value(areaConfig.maxInt64Value);
protoACfg->set_min_float_value(areaConfig.minFloatValue);
protoACfg->set_max_float_value(areaConfig.maxFloatValue);
protoACfg->set_min_int32_value(areaConfig.minInt32Value);
protoACfg->set_max_int32_value(areaConfig.maxInt32Value);
if (areaConfig.supportedEnumValues.has_value()) {
for (auto& supportedEnumValue : areaConfig.supportedEnumValues.value()) {
protoACfg->add_supported_enum_values(supportedEnumValue);
}
}
protoACfg->set_support_variable_update_rate(areaConfig.supportVariableUpdateRate);
}
}
@ -87,15 +94,24 @@ void protoToAidl(const proto::VehiclePropConfig& in, aidl_vehicle::VehiclePropCo
COPY_PROTOBUF_VEC_TO_VHAL_TYPE(in, config_array, out, configArray);
auto cast_to_acfg = [](const proto::VehicleAreaConfig& protoAcfg) {
return aidl_vehicle::VehicleAreaConfig{
auto vehicleAreaConfig = aidl_vehicle::VehicleAreaConfig{
.areaId = protoAcfg.area_id(),
.access = static_cast<aidl_vehicle::VehiclePropertyAccess>(protoAcfg.access()),
.minInt32Value = protoAcfg.min_int32_value(),
.maxInt32Value = protoAcfg.max_int32_value(),
.minInt64Value = protoAcfg.min_int64_value(),
.maxInt64Value = protoAcfg.max_int64_value(),
.minFloatValue = protoAcfg.min_float_value(),
.maxFloatValue = protoAcfg.max_float_value(),
.supportVariableUpdateRate = protoAcfg.support_variable_update_rate(),
};
if (protoAcfg.supported_enum_values().size() != 0) {
vehicleAreaConfig.supportedEnumValues = std::vector<int64_t>();
COPY_PROTOBUF_VEC_TO_VHAL_TYPE(protoAcfg, supported_enum_values, (&vehicleAreaConfig),
supportedEnumValues.value());
}
return vehicleAreaConfig;
};
CAST_COPY_PROTOBUF_VEC_TO_VHAL_TYPE(in, area_configs, out, areaConfigs, cast_to_acfg);
}

View file

@ -82,6 +82,117 @@ class IVehicleHardware {
const std::vector<aidl::android::hardware::automotive::vehicle::GetValueRequest>&
requests) const = 0;
// Dump debug information in the server.
virtual DumpResult dump(const std::vector<std::string>& options) = 0;
// Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
virtual aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() = 0;
// Register a callback that would be called when there is a property change event from vehicle.
// This function must only be called once during initialization.
virtual void registerOnPropertyChangeEvent(
std::unique_ptr<const PropertyChangeCallback> callback) = 0;
// Register a callback that would be called when there is a property set error event from
// vehicle. Must only be called once during initialization.
virtual void registerOnPropertySetErrorEvent(
std::unique_ptr<const PropertySetErrorCallback> callback) = 0;
// Gets the batching window used by DefaultVehicleHal for property change events.
//
// In DefaultVehicleHal, all the property change events generated within the batching window
// will be delivered through one callback to the VHAL client. This affects the maximum supported
// subscription rate. For example, if this returns 10ms, then only one callback for property
// change events will be called per 10ms, meaining that the max subscription rate for all
// continuous properties would be 100hz.
//
// A higher batching window means less callbacks to the VHAL client, causing a better
// performance. However, it also means a longer average latency for every property change
// events.
//
// 0 means no batching should be enabled in DefaultVehicleHal. In this case, batching can
// be optionally implemented in IVehicleHardware layer.
virtual std::chrono::nanoseconds getPropertyOnChangeEventBatchingWindow() {
// By default batching is disabled.
return std::chrono::nanoseconds(0);
}
// A [propId, areaId] is newly subscribed or the subscribe options are changed.
//
// The subscribe options contain sample rate in Hz or enable/disable variable update rate.
//
// For continuous properties:
//
// The sample rate is never 0 and indicates the desired polling rate for this property. The
// sample rate is guaranteed to be within supported {@code minSampleRate} and
// {@code maxSampleRate} as specified in {@code VehiclePropConfig}.
//
// If the specified sample rate is not supported, e.g. vehicle bus only supports 5hz and 10hz
// polling rate but the sample rate is 8hz, impl must choose the higher polling rate (10hz).
//
// Whether variable update rate is enabled is specified by {@code enableVariableUpdateRate} in
// {@code SubscribeOptions}. If variable update rate is not supported for the
// [propId, areaId], impl must ignore this option and always treat it as disabled.
//
// If variable update rate is disabled/not supported, impl must report all the property events
// for this [propId, areaId] through {@code propertyChangeCallback} according to the sample
// rate. E.g. a sample rate of 10hz must generate at least 10 property change events per second.
//
// If variable update rate is enabled AND supported, impl must only report property events
// when the [propId, areaId]'s value or status changes (a.k.a same as on-change property).
// The sample rate still guides the polling rate, but duplicate property events must be dropped
// and not reported via {@code propertyChangeCallback}.
//
// Async property set error events are not affected by variable update rate and must always
// be reported.
//
// If the impl is always polling at {@code maxSampleRate} for all continuous [propId, areaId]s,
// and do not support variable update rate for any [propId, areaId], then this function can be a
// no-op.
//
// For on-change properties:
//
// The sample rate is always 0 and must be ignored. If the impl is always subscribing to all
// on-change properties, then this function can be no-op.
//
// For all properties:
//
// It is recommended to only deliver the subscribed property events to DefaultVehicleHal to
// improve performance. However, even if unsubscribed property events are delivered, they
// will be filtered out by DefaultVehicleHal.
//
// A subscription from VHAL client might not necessarily trigger this function.
// DefaultVehicleHal will aggregate all the subscriptions from all the clients and notify
// IVehicleHardware if new subscriptions are required or subscribe options are updated.
//
// For example:
// 1. VHAL initially have no subscriber for speed.
// 2. A new subscriber is subscribing speed for 10 times/s, 'subscribe' is called
// with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
// 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
// times/sec, 'subscribe' is not called.
// 4. The initial subscriber is removed, 'subscribe' is called with sampleRate as
// 5, because now it only needs to report event 5times/sec. The impl can now poll vehicle
// speed 5 times/s. If the impl is still polling at 10 times/s, that is okay as long as
// the polling rate is larger than 5times/s. DefaultVehicleHal would ignore the additional
// events.
// 5. The second subscriber is removed, 'unsubscribe' is called.
// The impl can optionally disable the polling for vehicle speed.
//
virtual aidl::android::hardware::automotive::vehicle::StatusCode subscribe(
[[maybe_unused]] aidl::android::hardware::automotive::vehicle::SubscribeOptions
options) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
}
// A [propId, areaId] is unsubscribed. This applies for both continuous or on-change property.
virtual aidl::android::hardware::automotive::vehicle::StatusCode unsubscribe(
[[maybe_unused]] int32_t propId, [[maybe_unused]] int32_t areaId) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
}
// This function is deprecated, subscribe/unsubscribe should be used instead.
//
// Update the sampling rate for the specified property and the specified areaId (0 for global
// property) if server supports it. The property must be a continuous property.
// {@code sampleRate} means that for this specific property, the server must generate at least
@ -91,7 +202,7 @@ class IVehicleHardware {
// This would be called if sample rate is updated for a subscriber, a new subscriber is added
// or an existing subscriber is removed. For example:
// 1. We have no subscriber for speed.
// 2. A new subscriber is subscribing speed for 10 times/s, updsateSampleRate would be called
// 2. A new subscriber is subscribing speed for 10 times/s, updateSampleRate would be called
// with sampleRate as 10. The impl is now polling vehicle speed from bus 10 times/s.
// 3. A new subscriber is subscribing speed for 5 times/s, because it is less than 10
// times/sec, updateSampleRate would not be called.
@ -110,22 +221,6 @@ class IVehicleHardware {
[[maybe_unused]] float sampleRate) {
return aidl::android::hardware::automotive::vehicle::StatusCode::OK;
}
// Dump debug information in the server.
virtual DumpResult dump(const std::vector<std::string>& options) = 0;
// Check whether the system is healthy, return {@code StatusCode::OK} for healthy.
virtual aidl::android::hardware::automotive::vehicle::StatusCode checkHealth() = 0;
// Register a callback that would be called when there is a property change event from vehicle.
// Must only be called once during initialization.
virtual void registerOnPropertyChangeEvent(
std::unique_ptr<const PropertyChangeCallback> callback) = 0;
// Register a callback that would be called when there is a property set error event from
// vehicle. Must only be called once during initialization.
virtual void registerOnPropertySetErrorEvent(
std::unique_ptr<const PropertySetErrorCallback> callback) = 0;
};
} // namespace vehicle

View file

@ -38,6 +38,37 @@ enum StatusCode {
/* Something unexpected has happened in Vehicle HAL */
INTERNAL_ERROR = 5;
/**
* For features that are not available because the underlying feature is
* disabled.
*/
NOT_AVAILABLE_DISABLED = 6;
/**
* For features that are not available because the vehicle speed is too low.
*/
NOT_AVAILABLE_SPEED_LOW = 7;
/**
* For features that are not available because the vehicle speed is too
* high.
*/
NOT_AVAILABLE_SPEED_HIGH = 8;
/**
* For features that are not available because of bad camera or sensor
* visibility. Examples might be bird poop blocking the camera or a bumper
* cover blocking an ultrasonic sensor.
*/
NOT_AVAILABLE_POOR_VISIBILITY = 9;
/**
* The feature cannot be accessed due to safety reasons. Eg. System could be
* in a faulty state, an object or person could be blocking the requested
* operation such as closing a trunk door, etc.
*/
NOT_AVAILABLE_SAFETY = 10;
};
message VehicleHalCallStatus {

View file

@ -18,6 +18,8 @@ syntax = "proto3";
package android.hardware.automotive.vehicle.proto;
import "android/hardware/automotive/vehicle/VehiclePropertyAccess.proto";
/* Must be in sync with VehicleAreaConfig.aidl. */
message VehicleAreaConfig {
/* Area id is ignored for VehiclePropertyGroup:GLOBAL properties. */
@ -36,4 +38,13 @@ message VehicleAreaConfig {
float min_float_value = 6;
float max_float_value = 7;
/**
* If the property has a @data_enum, then it is possible to specify a supported subset of the
* @data_enum. If the property has a @data_enum and supported_enum_values is null, then it is
* assumed all @data_enum values are supported unless specified through another mechanism.
*/
repeated int64 supported_enum_values = 8;
VehiclePropertyAccess access = 9;
bool support_variable_update_rate = 10;
};

View file

@ -57,6 +57,6 @@ delete and lookup.
Defines many useful utility functions.
## test
## test_vendor_properties
Defines utility libraries for test only.
Contains vendor properties used for testing purpose in reference VHAL.

View file

@ -69,6 +69,19 @@ class ConcurrentQueue {
mCond.notify_one();
}
void push(std::vector<T>&& items) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
if (!mIsActive) {
return;
}
for (T& item : items) {
mQueue.push(std::move(item));
}
}
mCond.notify_one();
}
// Deactivates the queue, thus no one can push items to it, also notifies all waiting thread.
// The items already in the queue could still be flushed even after the queue is deactivated.
void deactivate() {
@ -92,6 +105,69 @@ class ConcurrentQueue {
std::queue<T> mQueue GUARDED_BY(mLock);
};
template <typename T>
class BatchingConsumer {
private:
enum class State {
INIT = 0,
RUNNING = 1,
STOP_REQUESTED = 2,
STOPPED = 3,
};
public:
BatchingConsumer() : mState(State::INIT) {}
BatchingConsumer(const BatchingConsumer&) = delete;
BatchingConsumer& operator=(const BatchingConsumer&) = delete;
using OnBatchReceivedFunc = std::function<void(std::vector<T> vec)>;
void run(ConcurrentQueue<T>* queue, std::chrono::nanoseconds batchInterval,
const OnBatchReceivedFunc& func) {
mQueue = queue;
mBatchInterval = batchInterval;
mWorkerThread = std::thread(&BatchingConsumer<T>::runInternal, this, func);
}
void requestStop() { mState = State::STOP_REQUESTED; }
void waitStopped() {
if (mWorkerThread.joinable()) {
mWorkerThread.join();
}
}
private:
void runInternal(const OnBatchReceivedFunc& onBatchReceived) {
if (mState.exchange(State::RUNNING) == State::INIT) {
while (State::RUNNING == mState) {
mQueue->waitForItems();
if (State::STOP_REQUESTED == mState) break;
std::this_thread::sleep_for(mBatchInterval);
if (State::STOP_REQUESTED == mState) break;
std::vector<T> items = mQueue->flush();
if (items.size() > 0) {
onBatchReceived(std::move(items));
}
}
}
mState = State::STOPPED;
}
private:
std::thread mWorkerThread;
std::atomic<State> mState;
std::chrono::nanoseconds mBatchInterval;
ConcurrentQueue<T>* mQueue;
};
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View file

@ -77,22 +77,6 @@ constexpr int SEAT_1_RIGHT = toInt(propertyutils_impl::VehicleAreaSeat::ROW_1_RI
constexpr int SEAT_2_LEFT = toInt(propertyutils_impl::VehicleAreaSeat::ROW_2_LEFT);
constexpr int SEAT_2_RIGHT = toInt(propertyutils_impl::VehicleAreaSeat::ROW_2_RIGHT);
constexpr int SEAT_2_CENTER = toInt(propertyutils_impl::VehicleAreaSeat::ROW_2_CENTER);
constexpr int VENDOR_EXTENSION_BOOLEAN_PROPERTY =
0x101 | toInt(propertyutils_impl::VehiclePropertyGroup::VENDOR) |
toInt(propertyutils_impl::VehiclePropertyType::BOOLEAN) |
toInt(propertyutils_impl::VehicleArea::DOOR);
constexpr int VENDOR_EXTENSION_FLOAT_PROPERTY =
0x102 | toInt(propertyutils_impl::VehiclePropertyGroup::VENDOR) |
toInt(propertyutils_impl::VehiclePropertyType::FLOAT) |
toInt(propertyutils_impl::VehicleArea::SEAT);
constexpr int VENDOR_EXTENSION_INT_PROPERTY =
0x103 | toInt(propertyutils_impl::VehiclePropertyGroup::VENDOR) |
toInt(propertyutils_impl::VehiclePropertyType::INT32) |
toInt(propertyutils_impl::VehicleArea::WINDOW);
constexpr int VENDOR_EXTENSION_STRING_PROPERTY =
0x104 | toInt(propertyutils_impl::VehiclePropertyGroup::VENDOR) |
toInt(propertyutils_impl::VehiclePropertyType::STRING) |
toInt(propertyutils_impl::VehicleArea::GLOBAL);
constexpr int FUEL_DOOR_REAR_LEFT = toInt(propertyutils_impl::PortLocationType::REAR_LEFT);
constexpr int CHARGE_PORT_FRONT_LEFT = toInt(propertyutils_impl::PortLocationType::FRONT_LEFT);
constexpr int CHARGE_PORT_REAR_LEFT = toInt(propertyutils_impl::PortLocationType::REAR_LEFT);
@ -114,11 +98,6 @@ constexpr int HVAC_LEFT = SEAT_1_LEFT | SEAT_2_LEFT | SEAT_2_CENTER;
constexpr int HVAC_RIGHT = SEAT_1_RIGHT | SEAT_2_RIGHT;
constexpr int HVAC_ALL = HVAC_LEFT | HVAC_RIGHT;
const int32_t HVAC_POWER_PROPERTIES[] = {
toInt(propertyutils_impl::VehicleProperty::HVAC_FAN_SPEED),
toInt(propertyutils_impl::VehicleProperty::HVAC_FAN_DIRECTION),
};
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View file

@ -19,6 +19,8 @@
#include <android-base/thread_annotations.h>
#include <utils/Looper.h>
#include <atomic>
#include <memory>
#include <mutex>
#include <queue>
@ -31,6 +33,9 @@ namespace hardware {
namespace automotive {
namespace vehicle {
// Forward declaration
class RecurrentMessageHandler;
// A thread-safe recurrent timer.
class RecurrentTimer final {
public:
@ -43,49 +48,44 @@ class RecurrentTimer final {
// Registers a recurrent callback for a given interval.
// Registering the same callback twice will override the interval provided before.
void registerTimerCallback(int64_t intervalInNano, std::shared_ptr<Callback> callback);
void registerTimerCallback(int64_t intervalInNanos, std::shared_ptr<Callback> callback);
// Unregisters a previously registered recurrent callback.
void unregisterTimerCallback(std::shared_ptr<Callback> callback);
private:
// friend class for unit testing.
friend class RecurrentMessageHandler;
// For unit test
friend class RecurrentTimerTest;
struct CallbackInfo {
std::shared_ptr<Callback> callback;
int64_t interval;
int64_t nextTime;
// A flag to indicate whether this CallbackInfo is already outdated and should be ignored.
// The reason we need this flag is because we cannot easily remove an element from a heap.
bool outdated = false;
static bool cmp(const std::unique_ptr<CallbackInfo>& lhs,
const std::unique_ptr<CallbackInfo>& rhs);
int64_t intervalInNanos;
int64_t nextTimeInNanos;
};
android::sp<Looper> mLooper;
android::sp<RecurrentMessageHandler> mHandler;
std::atomic<bool> mStopRequested = false;
std::atomic<int> mCallbackId = 0;
std::mutex mLock;
std::thread mThread;
std::condition_variable mCond;
bool mStopRequested GUARDED_BY(mLock) = false;
// A map to map each callback to its current active CallbackInfo in the mCallbackQueue.
std::unordered_map<std::shared_ptr<Callback>, CallbackInfo*> mCallbacks GUARDED_BY(mLock);
// A min-heap sorted by nextTime. Note that because we cannot remove arbitrary element from the
// heap, a single Callback can have multiple entries in this queue, all but one should be valid.
// The rest should be mark as outdated. The valid one is one stored in mCallbacks.
std::vector<std::unique_ptr<CallbackInfo>> mCallbackQueue GUARDED_BY(mLock);
std::unordered_map<std::shared_ptr<Callback>, int> mIdByCallback GUARDED_BY(mLock);
std::unordered_map<int, std::unique_ptr<CallbackInfo>> mCallbackInfoById GUARDED_BY(mLock);
void loop();
void handleMessage(const android::Message& message) EXCLUDES(mLock);
int getCallbackIdLocked(std::shared_ptr<Callback> callback) REQUIRES(mLock);
};
// Mark the callbackInfo as outdated and should be ignored when popped from the heap.
void markOutdatedLocked(CallbackInfo* callback) REQUIRES(mLock);
// Remove all outdated callbackInfos from the top of the heap. This function must be called
// each time we might introduce outdated elements to the top. We must make sure the heap is
// always valid from the top.
void removeInvalidCallbackLocked() REQUIRES(mLock);
// Gets the next calblack to run (must be valid) from the heap, update its nextTime and put
// it back to the heap.
std::shared_ptr<Callback> getNextCallbackLocked(int64_t now) REQUIRES(mLock);
class RecurrentMessageHandler final : public android::MessageHandler {
public:
RecurrentMessageHandler(RecurrentTimer* timer) { mTimer = timer; }
void handleMessage(const android::Message& message) override;
private:
RecurrentTimer* mTimer;
};
} // namespace vehicle

View file

@ -19,11 +19,17 @@
#include <aidl/android/hardware/automotive/vehicle/AutomaticEmergencyBrakingState.h>
#include <aidl/android/hardware/automotive/vehicle/BlindSpotWarningState.h>
#include <aidl/android/hardware/automotive/vehicle/CrossTrafficMonitoringWarningState.h>
#include <aidl/android/hardware/automotive/vehicle/CruiseControlCommand.h>
#include <aidl/android/hardware/automotive/vehicle/CruiseControlState.h>
#include <aidl/android/hardware/automotive/vehicle/CruiseControlType.h>
#include <aidl/android/hardware/automotive/vehicle/DiagnosticFloatSensorIndex.h>
#include <aidl/android/hardware/automotive/vehicle/DiagnosticIntegerSensorIndex.h>
#include <aidl/android/hardware/automotive/vehicle/DriverDistractionState.h>
#include <aidl/android/hardware/automotive/vehicle/DriverDistractionWarning.h>
#include <aidl/android/hardware/automotive/vehicle/DriverDrowsinessAttentionState.h>
#include <aidl/android/hardware/automotive/vehicle/DriverDrowsinessAttentionWarning.h>
#include <aidl/android/hardware/automotive/vehicle/ElectronicStabilityControlState.h>
#include <aidl/android/hardware/automotive/vehicle/EmergencyLaneKeepAssistState.h>
#include <aidl/android/hardware/automotive/vehicle/ErrorState.h>
#include <aidl/android/hardware/automotive/vehicle/EvConnectorType.h>
@ -38,11 +44,13 @@
#include <aidl/android/hardware/automotive/vehicle/GsrComplianceRequirementType.h>
#include <aidl/android/hardware/automotive/vehicle/HandsOnDetectionDriverState.h>
#include <aidl/android/hardware/automotive/vehicle/HandsOnDetectionWarning.h>
#include <aidl/android/hardware/automotive/vehicle/ImpactSensorLocation.h>
#include <aidl/android/hardware/automotive/vehicle/LaneCenteringAssistCommand.h>
#include <aidl/android/hardware/automotive/vehicle/LaneCenteringAssistState.h>
#include <aidl/android/hardware/automotive/vehicle/LaneDepartureWarningState.h>
#include <aidl/android/hardware/automotive/vehicle/LaneKeepAssistState.h>
#include <aidl/android/hardware/automotive/vehicle/LocationCharacterization.h>
#include <aidl/android/hardware/automotive/vehicle/LowSpeedCollisionWarningState.h>
#include <aidl/android/hardware/automotive/vehicle/Obd2CommonIgnitionMonitors.h>
#include <aidl/android/hardware/automotive/vehicle/Obd2FuelSystemStatus.h>
#include <aidl/android/hardware/automotive/vehicle/Obd2FuelType.h>
@ -55,6 +63,7 @@
#include <aidl/android/hardware/automotive/vehicle/SetValueResults.h>
#include <aidl/android/hardware/automotive/vehicle/StatusCode.h>
#include <aidl/android/hardware/automotive/vehicle/SubscribeOptions.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleAirbagLocation.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReport.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleApPowerStateReq.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleArea.h>
@ -63,6 +72,7 @@
#include <aidl/android/hardware/automotive/vehicle/VehicleAreaSeat.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleAreaWheel.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleAreaWindow.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleAutonomousState.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleGear.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleHvacFanDirection.h>
#include <aidl/android/hardware/automotive/vehicle/VehicleIgnitionState.h>

View file

@ -48,21 +48,21 @@ class VehiclePropertyStore final {
enum class EventMode : uint8_t {
/**
* Only invoke OnValueChangeCallback if the new property value (ignoring timestamp) is
* different than the existing value.
* Only invoke OnValueChangeCallback or OnValuesChangeCallback if the new property value
* (ignoring timestamp) is different than the existing value.
*
* This should be used for regular cases.
*/
ON_VALUE_CHANGE,
/**
* Always invoke OnValueChangeCallback.
* Always invoke OnValueChangeCallback or OnValuesChangeCallback.
*
* This should be used for the special properties that are used for delivering event, e.g.
* HW_KEY_INPUT.
*/
ALWAYS,
/**
* Never invoke OnValueChangeCallback.
* Never invoke OnValueChangeCallback or OnValuesChangeCalblack.
*
* This should be used for continuous property subscription when the sample rate for the
* subscription is smaller than the refresh rate for the property. E.g., the vehicle speed
@ -82,6 +82,10 @@ class VehiclePropertyStore final {
using OnValueChangeCallback = std::function<void(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue&)>;
// Callback when one or more property values have been updated or new values added.
using OnValuesChangeCallback = std::function<void(
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropValue>)>;
// Function that used to calculate unique token for given VehiclePropValue.
using TokenFunction = std::function<int64_t(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& value)>;
@ -92,54 +96,83 @@ class VehiclePropertyStore final {
// used as the key.
void registerProperty(
const aidl::android::hardware::automotive::vehicle::VehiclePropConfig& config,
TokenFunction tokenFunc = nullptr);
TokenFunction tokenFunc = nullptr) EXCLUDES(mLock);
// Stores provided value. Returns error if config wasn't registered. If 'updateStatus' is
// true, the 'status' in 'propValue' would be stored. Otherwise, if this is a new value,
// 'status' would be initialized to {@code VehiclePropertyStatus::AVAILABLE}, if this is to
// override an existing value, the status for the existing value would be used for the
// overridden value.
// 'EventMode' controls whether the 'OnValueChangeCallback' will be called for this operation.
// 'EventMode' controls whether the 'OnValueChangeCallback' or 'OnValuesChangeCallback' will be
// called for this operation.
// If 'useCurrentTimestamp' is true, the property value will be set to the current timestamp.
VhalResult<void> writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus = false,
EventMode mode = EventMode::ON_VALUE_CHANGE);
EventMode mode = EventMode::ON_VALUE_CHANGE,
bool useCurrentTimestamp = false) EXCLUDES(mLock);
// Refresh the timestamp for the stored property value for [propId, areaId]. If eventMode is
// always, generates the property update event, otherwise, only update the stored timestamp
// without generating event. This operation is atomic with other writeValue operations.
void refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) EXCLUDES(mLock);
// Refresh the timestamp for multiple [propId, areaId]s.
void refreshTimestamps(
std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId)
EXCLUDES(mLock);
// Remove a given property value from the property store. The 'propValue' would be used to
// generate the key for the value to remove.
void removeValue(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue);
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& propValue)
EXCLUDES(mLock);
// Remove all the values for the property.
void removeValuesForProperty(int32_t propId);
void removeValuesForProperty(int32_t propId) EXCLUDES(mLock);
// Read all the stored values.
std::vector<VehiclePropValuePool::RecyclableType> readAllValues() const;
std::vector<VehiclePropValuePool::RecyclableType> readAllValues() const EXCLUDES(mLock);
// Read all the values for the property.
ValuesResultType readValuesForProperty(int32_t propId) const;
ValuesResultType readValuesForProperty(int32_t propId) const EXCLUDES(mLock);
// Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
// value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
// not configured.
ValueResultType readValue(
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const;
const aidl::android::hardware::automotive::vehicle::VehiclePropValue& request) const
EXCLUDES(mLock);
// Read the value for the requested property. Returns {@code StatusCode::NOT_AVAILABLE} if the
// value has not been set yet. Returns {@code StatusCode::INVALID_ARG} if the property is
// not configured.
ValueResultType readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const;
ValueResultType readValue(int32_t prop, int32_t area = 0, int64_t token = 0) const
EXCLUDES(mLock);
// Get all property configs.
std::vector<aidl::android::hardware::automotive::vehicle::VehiclePropConfig> getAllConfigs()
const;
const EXCLUDES(mLock);
// Get the property config for the requested property.
// Deprecated, use getPropConfig instead. This is unsafe to use if registerProperty overwrites
// an existing config.
android::base::Result<const aidl::android::hardware::automotive::vehicle::VehiclePropConfig*,
VhalError>
getConfig(int32_t propId) const;
getConfig(int32_t propId) const EXCLUDES(mLock);
// Get the property config for the requested property.
android::base::Result<aidl::android::hardware::automotive::vehicle::VehiclePropConfig,
VhalError>
getPropConfig(int32_t propId) const EXCLUDES(mLock);
// Set a callback that would be called when a property value has been updated.
void setOnValueChangeCallback(const OnValueChangeCallback& callback);
void setOnValueChangeCallback(const OnValueChangeCallback& callback) EXCLUDES(mLock);
// Set a callback that would be called when one or more property values have been updated.
// For backward compatibility, this is optional. If this is not set, then multiple property
// updates will be delivered through multiple OnValueChangeCallback instead.
// It is recommended to set this and batch the property update events for better performance.
// If this is set, then OnValueChangeCallback will not be used.
void setOnValuesChangeCallback(const OnValuesChangeCallback& callback) EXCLUDES(mLock);
inline std::shared_ptr<VehiclePropValuePool> getValuePool() { return mValuePool; }
@ -168,6 +201,7 @@ class VehiclePropertyStore final {
mutable std::mutex mLock;
std::unordered_map<int32_t, Record> mRecordsByPropId GUARDED_BY(mLock);
OnValueChangeCallback mOnValueChangeCallback GUARDED_BY(mLock);
OnValuesChangeCallback mOnValuesChangeCallback GUARDED_BY(mLock);
const Record* getRecordLocked(int32_t propId) const;

View file

@ -329,6 +329,11 @@ struct PropIdAreaIdHash {
}
};
inline std::string propIdToString(int32_t propId) {
return toString(
static_cast<aidl::android::hardware::automotive::vehicle::VehicleProperty>(propId));
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View file

@ -17,6 +17,7 @@
#include "RecurrentTimer.h"
#include <utils/Log.h>
#include <utils/Looper.h>
#include <utils/SystemClock.h>
#include <inttypes.h>
@ -27,153 +28,119 @@ namespace hardware {
namespace automotive {
namespace vehicle {
namespace {
using ::android::base::ScopedLockAssertion;
constexpr int INVALID_ID = -1;
} // namespace
RecurrentTimer::RecurrentTimer() {
mThread = std::thread(&RecurrentTimer::loop, this);
mHandler = sp<RecurrentMessageHandler>::make(this);
mLooper = sp<Looper>::make(/*allowNonCallbacks=*/false);
mThread = std::thread([this] {
Looper::setForThread(mLooper);
while (!mStopRequested) {
mLooper->pollOnce(/*timeoutMillis=*/-1);
}
});
}
RecurrentTimer::~RecurrentTimer() {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
mStopRequested = true;
}
mCond.notify_one();
mStopRequested = true;
mLooper->removeMessages(mHandler);
mLooper->wake();
if (mThread.joinable()) {
mThread.join();
}
}
void RecurrentTimer::registerTimerCallback(int64_t intervalInNano,
int RecurrentTimer::getCallbackIdLocked(std::shared_ptr<RecurrentTimer::Callback> callback) {
const auto& it = mIdByCallback.find(callback);
if (it != mIdByCallback.end()) {
return it->second;
}
return INVALID_ID;
}
void RecurrentTimer::registerTimerCallback(int64_t intervalInNanos,
std::shared_ptr<RecurrentTimer::Callback> callback) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
int callbackId = getCallbackIdLocked(callback);
if (callbackId == INVALID_ID) {
callbackId = mCallbackId++;
mIdByCallback.insert({callback, callbackId});
} else {
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
" ns, new: %" PRId64 " ns",
mCallbackInfoById[callbackId]->intervalInNanos, intervalInNanos);
mLooper->removeMessages(mHandler, callbackId);
}
// Aligns the nextTime to multiply of interval.
int64_t nextTime = ceil(uptimeNanos() / intervalInNano) * intervalInNano;
int64_t nextTimeInNanos = ceil(uptimeNanos() / intervalInNanos) * intervalInNanos;
std::unique_ptr<CallbackInfo> info = std::make_unique<CallbackInfo>();
info->callback = callback;
info->interval = intervalInNano;
info->nextTime = nextTime;
info->intervalInNanos = intervalInNanos;
info->nextTimeInNanos = nextTimeInNanos;
mCallbackInfoById.insert({callbackId, std::move(info)});
auto it = mCallbacks.find(callback);
if (it != mCallbacks.end()) {
ALOGI("Replacing an existing timer callback with a new interval, current: %" PRId64
" ns, new: %" PRId64 " ns",
it->second->interval, intervalInNano);
markOutdatedLocked(it->second);
}
mCallbacks[callback] = info.get();
mCallbackQueue.push_back(std::move(info));
// Insert the last element into the heap.
std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
mLooper->sendMessageAtTime(nextTimeInNanos, mHandler, Message(callbackId));
}
mCond.notify_one();
}
void RecurrentTimer::unregisterTimerCallback(std::shared_ptr<RecurrentTimer::Callback> callback) {
{
std::scoped_lock<std::mutex> lockGuard(mLock);
auto it = mCallbacks.find(callback);
if (it == mCallbacks.end()) {
int callbackId = getCallbackIdLocked(callback);
if (callbackId == INVALID_ID) {
ALOGE("No event found to unregister");
return;
}
markOutdatedLocked(it->second);
mCallbacks.erase(it);
}
mCond.notify_one();
}
void RecurrentTimer::markOutdatedLocked(RecurrentTimer::CallbackInfo* info) {
info->outdated = true;
info->callback = nullptr;
// Make sure the first element is always valid.
removeInvalidCallbackLocked();
}
void RecurrentTimer::removeInvalidCallbackLocked() {
while (mCallbackQueue.size() != 0 && mCallbackQueue[0]->outdated) {
std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
mCallbackQueue.pop_back();
mLooper->removeMessages(mHandler, callbackId);
mCallbackInfoById.erase(callbackId);
mIdByCallback.erase(callback);
}
}
std::shared_ptr<RecurrentTimer::Callback> RecurrentTimer::getNextCallbackLocked(int64_t now) {
std::pop_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
auto& callbackInfo = mCallbackQueue[mCallbackQueue.size() - 1];
auto nextCallback = callbackInfo->callback;
// intervalCount is the number of interval we have to advance until we pass now.
size_t intervalCount = (now - callbackInfo->nextTime) / callbackInfo->interval + 1;
callbackInfo->nextTime += intervalCount * callbackInfo->interval;
std::push_heap(mCallbackQueue.begin(), mCallbackQueue.end(), CallbackInfo::cmp);
void RecurrentTimer::handleMessage(const Message& message) {
std::shared_ptr<RecurrentTimer::Callback> callback;
{
std::scoped_lock<std::mutex> lockGuard(mLock);
// Make sure the first element is always valid.
removeInvalidCallbackLocked();
int callbackId = message.what;
return nextCallback;
}
void RecurrentTimer::loop() {
std::vector<std::shared_ptr<Callback>> callbacksToRun;
while (true) {
{
std::unique_lock<std::mutex> uniqueLock(mLock);
ScopedLockAssertion lockAssertion(mLock);
// Wait until the timer exits or we have at least one recurrent callback.
mCond.wait(uniqueLock, [this] {
ScopedLockAssertion lockAssertion(mLock);
return mStopRequested || mCallbackQueue.size() != 0;
});
int64_t interval;
if (mStopRequested) {
return;
}
// The first element is the nearest next event.
int64_t nextTime = mCallbackQueue[0]->nextTime;
int64_t now = uptimeNanos();
if (nextTime > now) {
interval = nextTime - now;
} else {
interval = 0;
}
// Wait for the next event or the timer exits.
if (mCond.wait_for(uniqueLock, std::chrono::nanoseconds(interval), [this] {
ScopedLockAssertion lockAssertion(mLock);
return mStopRequested;
})) {
return;
}
now = uptimeNanos();
callbacksToRun.clear();
while (mCallbackQueue.size() > 0) {
int64_t nextTime = mCallbackQueue[0]->nextTime;
if (nextTime > now) {
break;
}
callbacksToRun.push_back(getNextCallbackLocked(now));
}
auto it = mCallbackInfoById.find(callbackId);
if (it == mCallbackInfoById.end()) {
ALOGW("The event for callback ID: %d is outdated, ignore", callbackId);
return;
}
// Do not execute the callback while holding the lock.
for (size_t i = 0; i < callbacksToRun.size(); i++) {
(*callbacksToRun[i])();
}
CallbackInfo* callbackInfo = it->second.get();
callback = callbackInfo->callback;
int64_t nowNanos = uptimeNanos();
// intervalCount is the number of interval we have to advance until we pass now.
size_t intervalCount =
(nowNanos - callbackInfo->nextTimeInNanos) / callbackInfo->intervalInNanos + 1;
callbackInfo->nextTimeInNanos += intervalCount * callbackInfo->intervalInNanos;
mLooper->sendMessageAtTime(callbackInfo->nextTimeInNanos, mHandler, Message(callbackId));
}
(*callback)();
}
bool RecurrentTimer::CallbackInfo::cmp(const std::unique_ptr<RecurrentTimer::CallbackInfo>& lhs,
const std::unique_ptr<RecurrentTimer::CallbackInfo>& rhs) {
return lhs->nextTime > rhs->nextTime;
void RecurrentMessageHandler::handleMessage(const Message& message) {
mTimer->handleMessage(message);
}
} // namespace vehicle

View file

@ -16,6 +16,7 @@
#define LOG_TAG "VehiclePropertyStore"
#include <utils/Log.h>
#include <utils/SystemClock.h>
#include "VehiclePropertyStore.h"
@ -107,56 +108,158 @@ void VehiclePropertyStore::registerProperty(const VehiclePropConfig& config,
VhalResult<void> VehiclePropertyStore::writeValue(VehiclePropValuePool::RecyclableType propValue,
bool updateStatus,
VehiclePropertyStore::EventMode eventMode) {
std::scoped_lock<std::mutex> g(mLock);
int32_t propId = propValue->prop;
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
}
if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
return StatusError(StatusCode::INVALID_ARG)
<< "no config for property: " << propId << " area: " << propValue->areaId;
}
VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
VehiclePropertyStore::EventMode eventMode,
bool useCurrentTimestamp) {
bool valueUpdated = true;
if (auto it = record->values.find(recId); it != record->values.end()) {
const VehiclePropValue* valueToUpdate = it->second.get();
int64_t oldTimestamp = valueToUpdate->timestamp;
VehiclePropertyStatus oldStatus = valueToUpdate->status;
// propValue is outdated and drops it.
if (oldTimestamp > propValue->timestamp) {
return StatusError(StatusCode::INVALID_ARG)
<< "outdated timestamp: " << propValue->timestamp;
}
if (!updateStatus) {
propValue->status = oldStatus;
VehiclePropValue updatedValue;
OnValueChangeCallback onValueChangeCallback = nullptr;
OnValuesChangeCallback onValuesChangeCallback = nullptr;
int32_t propId;
int32_t areaId;
{
std::scoped_lock<std::mutex> g(mLock);
// Must set timestamp inside the lock to make sure no other writeValue will update the
// the timestamp to a newer one while we are writing this value.
if (useCurrentTimestamp) {
propValue->timestamp = elapsedRealtimeNano();
}
valueUpdated = (valueToUpdate->value != propValue->value ||
valueToUpdate->status != propValue->status ||
valueToUpdate->prop != propValue->prop ||
valueToUpdate->areaId != propValue->areaId);
} else if (!updateStatus) {
propValue->status = VehiclePropertyStatus::AVAILABLE;
propId = propValue->prop;
areaId = propValue->areaId;
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
return StatusError(StatusCode::INVALID_ARG)
<< "property: " << propId << " not registered";
}
if (!isGlobalProp(propId) && getAreaConfig(*propValue, record->propConfig) == nullptr) {
return StatusError(StatusCode::INVALID_ARG)
<< "no config for property: " << propId << " area ID: " << propValue->areaId;
}
VehiclePropertyStore::RecordId recId = getRecordIdLocked(*propValue, *record);
if (auto it = record->values.find(recId); it != record->values.end()) {
const VehiclePropValue* valueToUpdate = it->second.get();
int64_t oldTimestampNanos = valueToUpdate->timestamp;
VehiclePropertyStatus oldStatus = valueToUpdate->status;
// propValue is outdated and drops it.
if (oldTimestampNanos > propValue->timestamp) {
return StatusError(StatusCode::INVALID_ARG)
<< "outdated timestampNanos: " << propValue->timestamp;
}
if (!updateStatus) {
propValue->status = oldStatus;
}
valueUpdated = (valueToUpdate->value != propValue->value ||
valueToUpdate->status != propValue->status ||
valueToUpdate->prop != propValue->prop ||
valueToUpdate->areaId != propValue->areaId);
} else if (!updateStatus) {
propValue->status = VehiclePropertyStatus::AVAILABLE;
}
record->values[recId] = std::move(propValue);
if (eventMode == EventMode::NEVER) {
return {};
}
updatedValue = *(record->values[recId]);
onValuesChangeCallback = mOnValuesChangeCallback;
onValueChangeCallback = mOnValueChangeCallback;
}
record->values[recId] = std::move(propValue);
if (eventMode == EventMode::NEVER) {
if (onValuesChangeCallback == nullptr && onValueChangeCallback == nullptr) {
ALOGW("No callback registered, ignoring property update for propId: %" PRId32
", area ID: %" PRId32,
propId, areaId);
return {};
}
if ((eventMode == EventMode::ALWAYS || valueUpdated) && mOnValueChangeCallback != nullptr) {
mOnValueChangeCallback(*(record->values[recId]));
// Invoke the callback outside the lock to prevent dead-lock.
if (eventMode == EventMode::ALWAYS || valueUpdated) {
if (onValuesChangeCallback != nullptr) {
onValuesChangeCallback({updatedValue});
} else {
onValueChangeCallback(updatedValue);
}
}
return {};
}
void VehiclePropertyStore::refreshTimestamp(int32_t propId, int32_t areaId, EventMode eventMode) {
std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId;
PropIdAreaId propIdAreaId = {
.propId = propId,
.areaId = areaId,
};
eventModeByPropIdAreaId[propIdAreaId] = eventMode;
refreshTimestamps(eventModeByPropIdAreaId);
}
void VehiclePropertyStore::refreshTimestamps(
std::unordered_map<PropIdAreaId, EventMode, PropIdAreaIdHash> eventModeByPropIdAreaId) {
std::vector<VehiclePropValue> updatedValues;
OnValuesChangeCallback onValuesChangeCallback = nullptr;
OnValueChangeCallback onValueChangeCallback = nullptr;
{
std::scoped_lock<std::mutex> g(mLock);
onValuesChangeCallback = mOnValuesChangeCallback;
onValueChangeCallback = mOnValueChangeCallback;
for (const auto& [propIdAreaId, eventMode] : eventModeByPropIdAreaId) {
int32_t propId = propIdAreaId.propId;
int32_t areaId = propIdAreaId.areaId;
VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
continue;
}
VehiclePropValue propValue = {
.areaId = areaId,
.prop = propId,
.value = {},
};
VehiclePropertyStore::RecordId recId = getRecordIdLocked(propValue, *record);
if (auto it = record->values.find(recId); it != record->values.end()) {
it->second->timestamp = elapsedRealtimeNano();
if (eventMode == EventMode::ALWAYS) {
updatedValues.push_back(*(it->second));
}
} else {
continue;
}
}
}
// Invoke the callback outside the lock to prevent dead-lock.
if (updatedValues.empty()) {
return;
}
if (!onValuesChangeCallback && !onValueChangeCallback) {
// If no callback is set, then we don't have to do anything.
for (const auto& updateValue : updatedValues) {
ALOGW("No callback registered, ignoring property update for propId: %" PRId32
", area ID: %" PRId32,
updateValue.prop, updateValue.areaId);
}
return;
}
if (onValuesChangeCallback != nullptr) {
onValuesChangeCallback(updatedValues);
} else {
// Fallback to use multiple onValueChangeCallback
for (const auto& updateValue : updatedValues) {
onValueChangeCallback(updateValue);
}
}
}
void VehiclePropertyStore::removeValue(const VehiclePropValue& propValue) {
std::scoped_lock<std::mutex> g(mLock);
@ -263,6 +366,17 @@ VhalResult<const VehiclePropConfig*> VehiclePropertyStore::getConfig(int32_t pro
return &record->propConfig;
}
VhalResult<VehiclePropConfig> VehiclePropertyStore::getPropConfig(int32_t propId) const {
std::scoped_lock<std::mutex> g(mLock);
const VehiclePropertyStore::Record* record = getRecordLocked(propId);
if (record == nullptr) {
return StatusError(StatusCode::INVALID_ARG) << "property: " << propId << " not registered";
}
return record->propConfig;
}
void VehiclePropertyStore::setOnValueChangeCallback(
const VehiclePropertyStore::OnValueChangeCallback& callback) {
std::scoped_lock<std::mutex> g(mLock);
@ -270,6 +384,13 @@ void VehiclePropertyStore::setOnValueChangeCallback(
mOnValueChangeCallback = callback;
}
void VehiclePropertyStore::setOnValuesChangeCallback(
const VehiclePropertyStore::OnValuesChangeCallback& callback) {
std::scoped_lock<std::mutex> g(mLock);
mOnValuesChangeCallback = callback;
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

View file

@ -27,7 +27,6 @@ cc_test {
"libgtest",
"libgmock",
],
header_libs: ["VehicleHalTestUtilHeaders"],
defaults: ["VehicleHalDefaults"],
test_suites: ["device-tests"],
}

View file

@ -60,9 +60,14 @@ class RecurrentTimerTest : public testing::Test {
mCallbacks.clear();
}
size_t countTimerCallbackQueue(RecurrentTimer* timer) {
size_t countCallbackInfoById(RecurrentTimer* timer) {
std::scoped_lock<std::mutex> lockGuard(timer->mLock);
return timer->mCallbackQueue.size();
return timer->mCallbackInfoById.size();
}
size_t countIdByCallback(RecurrentTimer* timer) {
std::scoped_lock<std::mutex> lockGuard(timer->mLock);
return timer->mIdByCallback.size();
}
private:
@ -109,6 +114,9 @@ TEST_F(RecurrentTimerTest, testRegisterUnregisterRegister) {
<< "Not enough callbacks called before timeout";
timer.unregisterTimerCallback(action);
ASSERT_EQ(countCallbackInfoById(&timer), 0u);
ASSERT_EQ(countIdByCallback(&timer), 0u);
}
TEST_F(RecurrentTimerTest, testDestroyTimerWithCallback) {
@ -198,8 +206,8 @@ TEST_F(RecurrentTimerTest, testRegisterSameCallbackMultipleTimes) {
timer.unregisterTimerCallback(action);
// Make sure there is no item in the callback queue.
ASSERT_EQ(countTimerCallbackQueue(&timer), static_cast<size_t>(0));
ASSERT_EQ(countCallbackInfoById(&timer), 0u);
ASSERT_EQ(countIdByCallback(&timer), 0u);
}
TEST_F(RecurrentTimerTest, testRegisterCallbackMultipleTimesNoDeadLock) {

View file

@ -20,6 +20,7 @@
#include <VehicleUtils.h>
#include <gmock/gmock.h>
#include <gtest/gtest.h>
#include <utils/SystemClock.h>
namespace android {
namespace hardware {
@ -101,16 +102,16 @@ TEST_F(VehiclePropertyStoreTest, testGetAllConfigs) {
ASSERT_EQ(configs.size(), static_cast<size_t>(2));
}
TEST_F(VehiclePropertyStoreTest, testGetConfig) {
VhalResult<const VehiclePropConfig*> result =
mStore->getConfig(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
TEST_F(VehiclePropertyStoreTest, testGetPropConfig) {
VhalResult<VehiclePropConfig> result =
mStore->getPropConfig(toInt(VehicleProperty::INFO_FUEL_CAPACITY));
ASSERT_RESULT_OK(result);
ASSERT_EQ(*(result.value()), mConfigFuelCapacity);
ASSERT_EQ(result.value(), mConfigFuelCapacity);
}
TEST_F(VehiclePropertyStoreTest, testGetConfigWithInvalidPropId) {
VhalResult<const VehiclePropConfig*> result = mStore->getConfig(INVALID_PROP_ID);
TEST_F(VehiclePropertyStoreTest, testGetPropConfigWithInvalidPropId) {
VhalResult<VehiclePropConfig> result = mStore->getPropConfig(INVALID_PROP_ID);
EXPECT_FALSE(result.ok()) << "expect error when getting a config for an invalid property ID";
EXPECT_EQ(result.error().code(), StatusCode::INVALID_ARG);
@ -509,6 +510,151 @@ TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackForceNoUpdate) {
ASSERT_EQ(updatedValue.prop, INVALID_PROP_ID);
}
TEST_F(VehiclePropertyStoreTest, testPropertyChangeCallbackUseVehiclePropertyStore_noDeadLock) {
VehiclePropValue fuelCapacity = {
.prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
.value = {.floatValues = {1.0}},
};
std::vector<VehiclePropConfig> configs;
mStore->setOnValueChangeCallback(
[this, &configs]([[maybe_unused]] const VehiclePropValue& value) {
configs = mStore->getAllConfigs();
});
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity), /*updateStatus=*/true,
VehiclePropertyStore::EventMode::ALWAYS));
ASSERT_EQ(configs.size(), static_cast<size_t>(2));
}
TEST_F(VehiclePropertyStoreTest, testOnValuesChangeCallback) {
std::vector<VehiclePropValue> updatedValues;
VehiclePropValue fuelCapacity = {
.prop = toInt(VehicleProperty::INFO_FUEL_CAPACITY),
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
fuelCapacity.value.floatValues[0] = 2.0;
fuelCapacity.timestamp = 1;
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
ASSERT_THAT(updatedValues, ElementsAre(fuelCapacity));
}
TEST_F(VehiclePropertyStoreTest, testRefreshTimestamp) {
std::vector<VehiclePropValue> updatedValues;
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
int64_t now = elapsedRealtimeNano();
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
int areaId = WHEEL_FRONT_LEFT;
VehiclePropValue tirePressure = {
.prop = propId,
.areaId = areaId,
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(tirePressure)));
updatedValues.clear();
mStore->refreshTimestamp(propId, areaId, VehiclePropertyStore::EventMode::ALWAYS);
ASSERT_EQ(updatedValues.size(), 1u);
ASSERT_EQ(updatedValues[0].prop, propId);
ASSERT_EQ(updatedValues[0].areaId, areaId);
ASSERT_EQ(updatedValues[0].value.floatValues[0], 1.0);
int64_t timestamp = updatedValues[0].timestamp;
ASSERT_GE(timestamp, now);
auto result = mStore->readValue(tirePressure);
ASSERT_RESULT_OK(result);
ASSERT_EQ((result.value())->timestamp, timestamp);
}
TEST_F(VehiclePropertyStoreTest, testRefreshTimestamp_eventModeOnValueChange) {
std::vector<VehiclePropValue> updatedValues;
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
int64_t now = elapsedRealtimeNano();
int propId = toInt(VehicleProperty::TIRE_PRESSURE);
int areaId = WHEEL_FRONT_LEFT;
VehiclePropValue tirePressure = {
.prop = propId,
.areaId = areaId,
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(tirePressure)));
updatedValues.clear();
mStore->refreshTimestamp(propId, areaId, VehiclePropertyStore::EventMode::ON_VALUE_CHANGE);
ASSERT_EQ(updatedValues.size(), 0u)
<< "Must generate no property update events if only the timestamp is refreshed";
auto result = mStore->readValue(tirePressure);
ASSERT_RESULT_OK(result);
ASSERT_GE((result.value())->timestamp, now)
<< "Even though event mode is on value change, the store timestamp must be updated";
}
TEST_F(VehiclePropertyStoreTest, testRefreshTimestamps) {
std::vector<VehiclePropValue> updatedValues;
mStore->setOnValuesChangeCallback(
[&updatedValues](std::vector<VehiclePropValue> values) { updatedValues = values; });
int64_t now = elapsedRealtimeNano();
int propId1 = toInt(VehicleProperty::INFO_FUEL_CAPACITY);
int areaId1 = 0;
VehiclePropValue fuelCapacity = {
.prop = propId1,
.areaId = areaId1,
.value = {.floatValues = {1.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(fuelCapacity)));
int propId2 = toInt(VehicleProperty::TIRE_PRESSURE);
int areaId2 = WHEEL_FRONT_LEFT;
VehiclePropValue tirePressure = {
.prop = propId2,
.areaId = areaId2,
.value = {.floatValues = {2.0}},
};
ASSERT_RESULT_OK(mStore->writeValue(mValuePool->obtain(tirePressure)));
updatedValues.clear();
std::unordered_map<PropIdAreaId, VehiclePropertyStore::EventMode, PropIdAreaIdHash>
eventModeByPropIdAreaId;
eventModeByPropIdAreaId[PropIdAreaId{
.propId = propId1,
.areaId = areaId1,
}] = VehiclePropertyStore::EventMode::ALWAYS;
eventModeByPropIdAreaId[PropIdAreaId{
.propId = propId2,
.areaId = areaId2,
}] = VehiclePropertyStore::EventMode::ALWAYS;
mStore->refreshTimestamps(eventModeByPropIdAreaId);
ASSERT_EQ(updatedValues.size(), 2u);
ASSERT_EQ(updatedValues[0].prop, propId1);
ASSERT_EQ(updatedValues[0].areaId, areaId1);
ASSERT_EQ(updatedValues[0].value.floatValues[0], 1.0);
ASSERT_GE(updatedValues[0].timestamp, now);
ASSERT_EQ(updatedValues[1].prop, propId2);
ASSERT_EQ(updatedValues[1].areaId, areaId2);
ASSERT_EQ(updatedValues[1].value.floatValues[0], 2.0);
ASSERT_GE(updatedValues[1].timestamp, now);
}
} // namespace vehicle
} // namespace automotive
} // namespace hardware

Some files were not shown because too many files have changed in this diff Show more