Merge Android 24Q1 Release (ab/11220357)
Bug: 319669529 Merged-In: I976f80006aeb88bde2ae34ca4f9be72cea727d9e Change-Id: Id94b25af52bd2e2847b6858697ab21cc6ce27aa3
This commit is contained in:
commit
763c473c3d
1207 changed files with 28993 additions and 5814 deletions
|
@ -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
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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];
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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, ¢erFreq, &maxFreq);
|
||||
ASSERT_NO_FATAL_FAILURE(getBandFrequencyRange(i, &minFreq, ¢erFreq, &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.
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
},
|
||||
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
20
automotive/evs/aidl/impl/default/include/EvsAllCameras.h
Normal file
20
automotive/evs/aidl/impl/default/include/EvsAllCameras.h
Normal 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"
|
144
automotive/evs/aidl/impl/default/include/EvsCamera.h
Normal file
144
automotive/evs/aidl/impl/default/include/EvsCamera.h
Normal 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
|
43
automotive/evs/aidl/impl/default/include/EvsCameraBase.h
Normal file
43
automotive/evs/aidl/impl/default/include/EvsCameraBase.h
Normal 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
|
|
@ -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; }
|
||||
};
|
||||
|
|
|
@ -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>
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
378
automotive/evs/aidl/impl/default/src/EvsCamera.cpp
Normal file
378
automotive/evs/aidl/impl/default/src/EvsCamera.cpp
Normal 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
|
|
@ -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";
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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) {
|
||||
|
|
507
automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
Normal file
507
automotive/evs/aidl/impl/default/src/EvsVideoEmulatedCamera.cpp
Normal 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
|
|
@ -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) {
|
||||
|
|
210
automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
Normal file
210
automotive/evs/aidl/impl/default/tests/EvsCameraBufferTest.cpp
Normal 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
|
202
automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
Normal file
202
automotive/evs/aidl/impl/default/tests/EvsCameraStateTest.cpp
Normal 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
|
|
@ -42,6 +42,5 @@ aidl_interface {
|
|||
imports: [],
|
||||
},
|
||||
],
|
||||
frozen: true,
|
||||
|
||||
frozen: false,
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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",
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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());
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.
|
||||
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
|
@ -26,7 +26,7 @@ package {
|
|||
cc_library_shared {
|
||||
name: "ApPowerControlLib",
|
||||
vendor: true,
|
||||
srcs: ["*.cpp"],
|
||||
srcs: ["ApPowerControl.cpp"],
|
||||
local_include_dirs: ["."],
|
||||
export_include_dirs: ["."],
|
||||
}
|
||||
|
|
|
@ -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");
|
||||
}
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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
|
|
@ -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
|
||||
};
|
||||
|
|
|
@ -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));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
}
|
||||
|
|
|
@ -43,6 +43,17 @@
|
|||
"auto-presubmit": [
|
||||
{
|
||||
"name": "VtsHalAutomotiveVehicle_TargetTest"
|
||||
},
|
||||
{
|
||||
"name": "CarServiceUnitTest",
|
||||
"options" : [
|
||||
{
|
||||
"include-filter": "com.android.car.hal.fakevhal.FakeVehicleStubUnitTest"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "VehicleHalProtoMessageConverterTest"
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -27,7 +27,7 @@ aidl_interface {
|
|||
srcs: [
|
||||
"android/hardware/automotive/vehicle/*.aidl",
|
||||
],
|
||||
frozen: true,
|
||||
frozen: false,
|
||||
stability: "vintf",
|
||||
backend: {
|
||||
cpp: {
|
||||
|
|
|
@ -37,4 +37,6 @@ parcelable SubscribeOptions {
|
|||
int propId;
|
||||
int[] areaIds;
|
||||
float sampleRate;
|
||||
float resolution = 0.0f;
|
||||
boolean enableVariableUpdateRate;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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",
|
||||
],
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
);
|
||||
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
);
|
||||
|
||||
}
|
|
@ -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
|
||||
|
|
|
@ -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"],
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
|
|
@ -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"
|
||||
]
|
||||
|
|
|
@ -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"
|
||||
}
|
||||
|
|
|
@ -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: [
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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"],
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -27,7 +27,6 @@ cc_test {
|
|||
"libgtest",
|
||||
"libgmock",
|
||||
],
|
||||
header_libs: ["VehicleHalTestUtilHeaders"],
|
||||
defaults: ["VehicleHalDefaults"],
|
||||
test_suites: ["device-tests"],
|
||||
}
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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
Loading…
Reference in a new issue